Compare commits
494 Commits
temp-noise
...
hair_guide
Author | SHA1 | Date | |
---|---|---|---|
b8ab411185 | |||
dc2d841b7c | |||
209686f1c8 | |||
27b28e437d | |||
f854fefa17 | |||
b60dcd85f9 | |||
58979947bc | |||
1dba28221d | |||
b1e839d9e3 | |||
3bff67be73 | |||
4a92042916 | |||
31f371b200 | |||
a89bcac14b | |||
a9be42dfa2 | |||
8915d9ed0d | |||
47912ef922 | |||
51500addaa | |||
8a8b9d7ea9 | |||
a754d222ba | |||
ff8df843c3 | |||
08836b73fe | |||
54aaceaabc | |||
287968377f | |||
d09822c876 | |||
7ad71174fe | |||
0042eb1077 | |||
6751627797 | |||
487775dd87 | |||
142112faec | |||
9b06c23dc3 | |||
0875d8b569 | |||
aa85cbee00 | |||
7cd45e1713 | |||
e4cc8ecd07 | |||
614868b571 | |||
dc766bfffc | |||
f52a7a0a4c | |||
5aee89a70f | |||
c8d3b62e46 | |||
a765b89694 | |||
2d9a5607a7 | |||
ff31741748 | |||
82e5e246ba | |||
220d3692e7 | |||
7ee1de4218 | |||
a25a334d45 | |||
e027e10f5d | |||
2469a6871e | |||
dfe61958a7 | |||
54a556b71b | |||
abac69b89c | |||
7d48a4629e | |||
9ce92878d0 | |||
a190407e91 | |||
6af06e3c91 | |||
11367d968c | |||
839aa5bd02 | |||
bd2bc66158 | |||
fec39ff862 | |||
56adfbe36d | |||
9c02682f23 | |||
94f3cf23ec | |||
c307b13be1 | |||
a927dbd17a | |||
446cc6f3b6 | |||
cc66a466e2 | |||
cd71dd6382 | |||
0fe4cae57b | |||
dec92e6ba1 | |||
a483e45dc9 | |||
995f5ee581 | |||
c891621d73 | |||
131c5d46d9 | |||
3eb658e7f2 | |||
a730fcfc61 | |||
43fa4a3f70 | |||
114a2171c0 | |||
fca537d3ef | |||
4843d144a5 | |||
98e808d7c2 | |||
28f11d30dd | |||
ebb27f77cb | |||
d1b0843a44 | |||
08cdc98b54 | |||
f4e6ced192 | |||
2d8c39c97c | |||
229f0c4b80 | |||
28c4d6ef05 | |||
0d0f4c1634 | |||
6fcb9374ea | |||
34d61bfd83 | |||
2af2dbc6ae | |||
b7b6f72cb5 | |||
4f2532e88c | |||
bccafbd84b | |||
0c475345e9 | |||
082da58685 | |||
2a5067d07c | |||
bb0fae907f | |||
37a383a4ae | |||
7d5297305a | |||
7372ead5ed | |||
80b2b28ffa | |||
4e71eb53d1 | |||
aab5e7bd16 | |||
1aae5ac1a8 | |||
b28c35daef | |||
e44b3bf7b8 | |||
945e1e98ba | |||
a1cd012549 | |||
3f678e37c6 | |||
606f8b2576 | |||
489d5e998a | |||
6c98748c70 | |||
6afb7546d3 | |||
e4d342b542 | |||
99c738045a | |||
d210d5de1a | |||
e163beb273 | |||
a751c7b2d1 | |||
db5c496b26 | |||
a6fffa09aa | |||
5d5397ea00 | |||
142338991b | |||
4c2d7767cf | |||
7b359d1ae5 | |||
38c28972c1 | |||
d7977c7667 | |||
97178ee43c | |||
542bbf3282 | |||
3d195d0763 | |||
3f99451465 | |||
fff9ba9b95 | |||
271948a630 | |||
31b153a717 | |||
069234f38d | |||
bc4bd70bf0 | |||
e288e3d368 | |||
a6dbdd5282 | |||
5f73faf620 | |||
1e96bd87db | |||
614f76592a | |||
4e05dd1dc5 | |||
ba3a650dde | |||
e46e8eafb7 | |||
1dca9cb77c | |||
de83bb5957 | |||
d2dfa4791f | |||
5a75ada839 | |||
2a22e31e79 | |||
6885b90564 | |||
5cdfa31e27 | |||
fe5b74c36c | |||
9036a6e60b | |||
3cef615645 | |||
23cb7e81b0 | |||
0fd34d2201 | |||
103189d9aa | |||
085f043447 | |||
d0f4b62586 | |||
ba6ce197bb | |||
0d71bffe18 | |||
8555279aa7 | |||
cd14565c0d | |||
16d631f5f0 | |||
86a3ac25bc | |||
33eaf7f495 | |||
4fd377990b | |||
671ff29220 | |||
64e3744e8b | |||
93177bce3e | |||
d3c5d74382 | |||
9c9ea7c75f | |||
8a3861e5d9 | |||
c88e551229 | |||
b5fa901697 | |||
64f109026b | |||
6797d4f776 | |||
52d6f25d82 | |||
b2db8c07b9 | |||
3ba96b7c4d | |||
8a6b26cf5d | |||
28b17a7fb6 | |||
029ac8dc15 | |||
0ac09ed0da | |||
25a83cb3cb | |||
ec4ec90934 | |||
383c6bc172 | |||
b44af40eec | |||
c155f3bc95 | |||
c67adabdba | |||
e7992b1279 | |||
d6e5d9a578 | |||
1447fecbbf | |||
99b9636bf0 | |||
298fd140f2 | |||
58a70542e0 | |||
dcd9cb72f5 | |||
974e55e3e6 | |||
e9cf9776f8 | |||
25bb3669cc | |||
7e1832c8d5 | |||
c0c8df3f2c | |||
8fa55d3885 | |||
471b410e43 | |||
9e56f6357f | |||
49c19ca679 | |||
e15dd419d4 | |||
4443f052f6 | |||
57b3e8a137 | |||
1e8139d912 | |||
95d8653c08 | |||
7f2a7d97d6 | |||
75f9c888f3 | |||
bbe8311948 | |||
3ee9d04db3 | |||
1d26d50a46 | |||
22e7d5cbaf | |||
fb5d222217 | |||
2228585912 | |||
82b1143afa | |||
bf919ab830 | |||
0206d49960 | |||
dc34c0df3d | |||
36fac1e27c | |||
2a7dfcd927 | |||
be6965011b | |||
8f46ae5ec9 | |||
359bef03df | |||
5ad90a874f | |||
5d17484d92 | |||
1d92d84bb1 | |||
ff381505fa | |||
a881edff7a | |||
a7f6ddba07 | |||
160696a21d | |||
207d442496 | |||
afdcd07b32 | |||
71e731ef1e | |||
ed27d3a398 | |||
b3c4ee5266 | |||
69bebcf8b5 | |||
067c890b36 | |||
00dc4813c3 | |||
1b971b0058 | |||
fe72fea762 | |||
c673982b22 | |||
d1258f92f8 | |||
c7414140a4 | |||
66f0df6907 | |||
d70547b666 | |||
acfdf08ea2 | |||
f70b6c96c5 | |||
9660a2e2dd | |||
61fae8440d | |||
b3c59d51e0 | |||
da9fb8496c | |||
02ae16fd6e | |||
e266d9d2b5 | |||
5665c1b411 | |||
5f41014a4c | |||
1014b23cea | |||
f6c5948c5c | |||
6c28125efe | |||
eba518c040 | |||
43e840b947 | |||
d2a75fb6dc | |||
3fbbef51c8 | |||
4e0d7349e8 | |||
56c3dbb656 | |||
12200ce0b1 | |||
91864d9bbe | |||
e9a4bb925c | |||
7bdbc7e6f3 | |||
7a8d3e623d | |||
047c05ed68 | |||
267f0e521e | |||
c53c6e99b3 | |||
56c1388ee4 | |||
5be3403a1d | |||
08b8b4465d | |||
6855c5dab6 | |||
21c913f957 | |||
2d24189369 | |||
f47f7383ce | |||
b4b07115cc | |||
cf3fce96b1 | |||
ae84236ac3 | |||
01968f95ed | |||
593fee0e7d | |||
c6ddb6fdd9 | |||
c8ef6904d1 | |||
a19f945476 | |||
4276d302f6 | |||
d7ba494283 | |||
78428043d6 | |||
a5d69d81e8 | |||
b81ce32a59 | |||
22c5c78c02 | |||
a007de04b1 | |||
cf11953996 | |||
e421ba7144 | |||
1ddbde24ff | |||
2d4694678a | |||
45f30a9fdb | |||
3aab7537e8 | |||
9f74b66ab7 | |||
5e1888965b | |||
fb7c003e99 | |||
caded470a6 | |||
d9a4945bb2 | |||
188874af2f | |||
e3033693f9 | |||
ef67e13bdb | |||
9e8a41aadf | |||
8b607d77e5 | |||
0d4fa23414 | |||
e3b80d6202 | |||
ead00baab1 | |||
45f0f3dc04 | |||
0d67f8d5c4 | |||
438b3f95a1 | |||
1b837226ce | |||
6faa4d77c1 | |||
b0717ad91e | |||
91fd0a487d | |||
e7df5f8528 | |||
6fa838f6d3 | |||
41e8bd9337 | |||
77802b21a6 | |||
8abe6745a4 | |||
53eb011e3d | |||
801c20cebc | |||
9feec51214 | |||
e34ba9fb7a | |||
c92457a87a | |||
bf0f058954 | |||
e35f24fb28 | |||
e8ff620325 | |||
3a438c675f | |||
57cbaa15b6 | |||
679113fbd5 | |||
65d2374c80 | |||
d3349e5e3f | |||
5a7efaf287 | |||
f3bc942370 | |||
07cffae7ea | |||
1b431c3cfe | |||
dd1211ecb8 | |||
1d28579daf | |||
48fc8b76b2 | |||
bd4ca21b54 | |||
28270f0953 | |||
2f596756b7 | |||
56ad4520cd | |||
4e95617769 | |||
548dfdbd31 | |||
73140ca283 | |||
32455e230a | |||
af594b7b85 | |||
5f67ac83bf | |||
b2cb4c448e | |||
216aacbcba | |||
ec51e83798 | |||
9a20f68295 | |||
a85c9b241a | |||
07221e980b | |||
56d8228713 | |||
892529029f | |||
d820eb1265 | |||
3aaca08aad | |||
5f6d9e301e | |||
05281abca1 | |||
38d9301674 | |||
fbc70fa51c | |||
d52bdb6d3d | |||
16b6d6c676 | |||
bf7e8b42fa | |||
20e960215a | |||
9cafd3ae56 | |||
0dccffb7aa | |||
7719a6365a | |||
3a3327dcd5 | |||
8ffcd1f434 | |||
ed1dc43657 | |||
5b9b779cab | |||
4798464e8b | |||
299859e40b | |||
5e42aff397 | |||
3f539c987f | |||
6146b90312 | |||
8b2ec99f57 | |||
410998cdc6 | |||
b6f4e0932d | |||
62fa5c4a84 | |||
2cddc3cda8 | |||
9120df6cb3 | |||
48a86af388 | |||
eacc24ccf1 | |||
ac54ded29b | |||
f7511b3d01 | |||
208ddcde2c | |||
f3b22c5769 | |||
b70c815ac2 | |||
d0a1fc8bb0 | |||
2d1d909817 | |||
e9737da5f2 | |||
1e047231c6 | |||
142ef0b2e9 | |||
56a09434c5 | |||
8672304e30 | |||
f95dde244e | |||
79e649a3bf | |||
e5b25e3c07 | |||
a4664ef88d | |||
be312b1399 | |||
3edc512888 | |||
b2ea8c1022 | |||
339a8b7521 | |||
77da317cb1 | |||
2c0616b034 | |||
a9fbd3b23b | |||
687b5a1ec0 | |||
59efe23830 | |||
d16599ea04 | |||
37d3a3331e | |||
5254df30ba | |||
2590231315 | |||
83b20c7ddf | |||
0e06a60168 | |||
121677d7c8 | |||
1615da133f | |||
7ae6492c85 | |||
7779166313 | |||
833a11f891 | |||
79959152e6 | |||
4fd6a7a696 | |||
98b15d9a7c | |||
a889a1458c | |||
7b6332f634 | |||
0a27e97fb7 | |||
eb5257002b | |||
23b284348b | |||
2d79994e39 | |||
fd34e3efbc | |||
dc43df89cd | |||
4e47239de2 | |||
991ee8a570 | |||
e43ef55e7f | |||
1afd5be044 | |||
d550a29558 | |||
5f44f4a2ff | |||
b0a9e48a19 | |||
afb947c00c | |||
f1e4d35489 | |||
1c7053f7a0 | |||
7bb90a06e1 | |||
a9001adbb4 | |||
dd69a3dbcf | |||
c65f1b2e15 | |||
09a7dbc29a | |||
6078e79cea | |||
6783b15453 | |||
82132fc5b0 | |||
4b4f24607e | |||
fe48c353f1 | |||
308af3bfb3 | |||
3f7d66ece4 | |||
a952800ffc | |||
71e34dad2b | |||
24b77359ae | |||
62aabdd550 | |||
2204bb092b | |||
8374e08894 | |||
822949a3e2 | |||
a86869af8f | |||
ca31ce144e | |||
f61f7e2d57 | |||
3036cedd90 | |||
cc5b3c2ff7 | |||
a30f9ffa87 | |||
3361788b1c | |||
21ab9ac9e3 | |||
18c6bb2436 | |||
a93841e3d4 | |||
728b6ed8e6 | |||
fa0163b8c7 | |||
7112a531b2 | |||
60cde6ca4a | |||
717db95210 | |||
a944cf8450 | |||
a1dd14c764 | |||
47d024c2b0 | |||
30e02b9a19 |
@@ -616,6 +616,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
|
||||
bf_editor_datafiles
|
||||
bf_editor_mask
|
||||
bf_editor_io
|
||||
bf_editor_groom
|
||||
|
||||
bf_render
|
||||
bf_python
|
||||
|
@@ -110,95 +110,361 @@ 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;
|
||||
|
||||
if(b_ob->type() == BL::Object::type_GROOM) {
|
||||
BL::Groom b_groom(b_ob->data());
|
||||
BL::HairSystem b_hsys = b_groom.hair_system();
|
||||
|
||||
int shader = clamp(b_groom.material_index() - 1, 0, mesh->used_shaders.size()-1);
|
||||
|
||||
BL::Mesh b_scalp(b_groom.scalp_object().data());
|
||||
if (b_scalp) {
|
||||
ObtainCacheDataFromHairSystem(b_ob,
|
||||
&b_hsys,
|
||||
&b_scalp,
|
||||
shader,
|
||||
background,
|
||||
CData,
|
||||
&curvenum,
|
||||
&keyno);
|
||||
}
|
||||
}
|
||||
|
||||
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 +472,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 +486,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 +498,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 +512,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 +1107,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 +1143,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 +1168,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 +1176,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 +1205,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 +1218,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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -81,7 +81,8 @@ bool BlenderSync::object_is_mesh(BL::Object& b_ob)
|
||||
else {
|
||||
return (b_ob_data.is_a(&RNA_Mesh) ||
|
||||
b_ob_data.is_a(&RNA_Curve) ||
|
||||
b_ob_data.is_a(&RNA_MetaBall));
|
||||
b_ob_data.is_a(&RNA_MetaBall) ||
|
||||
b_ob_data.is_a(&RNA_Groom));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -35,6 +35,7 @@ _modules = [
|
||||
"properties_data_curve",
|
||||
"properties_data_empty",
|
||||
"properties_data_gpencil",
|
||||
"properties_data_groom",
|
||||
"properties_data_light",
|
||||
"properties_data_lattice",
|
||||
"properties_data_mesh",
|
||||
@@ -44,6 +45,7 @@ _modules = [
|
||||
"properties_data_lightprobe",
|
||||
"properties_data_speaker",
|
||||
"properties_data_workspace",
|
||||
"properties_hair_common",
|
||||
"properties_mask_common",
|
||||
"properties_material",
|
||||
"properties_material_gpencil",
|
||||
|
178
release/scripts/startup/bl_ui/properties_data_groom.py
Normal file
178
release/scripts/startup/bl_ui/properties_data_groom.py
Normal file
@@ -0,0 +1,178 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Menu, Panel
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
|
||||
class GROOM_UL_regions(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
|
||||
groom = data
|
||||
region = item
|
||||
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
row = layout.row(align=True)
|
||||
if not region.is_bound:
|
||||
row.label(icon='ERROR')
|
||||
row.label(region.scalp_facemap)
|
||||
|
||||
elif self.layout_type == 'GRID':
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
class DataButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.groom
|
||||
|
||||
|
||||
class DATA_PT_context_groom(DataButtonsPanel, Panel):
|
||||
bl_label = ""
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
ob = context.object
|
||||
groom = context.groom
|
||||
space = context.space_data
|
||||
|
||||
split = layout.split(percentage=0.65)
|
||||
|
||||
if ob:
|
||||
split.template_ID(ob, "data")
|
||||
elif groom:
|
||||
split.template_ID(space, "pin_id")
|
||||
|
||||
|
||||
class DATA_PT_groom(DataButtonsPanel, Panel):
|
||||
bl_label = "Groom"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
groom = context.groom
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
col.alert = (groom.scalp_object is None)
|
||||
col.label("Scalp Object:")
|
||||
col.prop(groom, "scalp_object", "")
|
||||
|
||||
col = split.column()
|
||||
col.label("Curves:")
|
||||
col.prop(groom, "curve_resolution", "Resolution")
|
||||
|
||||
|
||||
class DATA_PT_groom_regions(DataButtonsPanel, Panel):
|
||||
bl_label = "Regions"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
groom = context.groom
|
||||
region = context.groom.regions.active
|
||||
|
||||
row = layout.row()
|
||||
|
||||
row.template_list("GROOM_UL_regions", "regions",
|
||||
groom, "regions",
|
||||
groom.regions, "active_index")
|
||||
|
||||
col = row.column(align=True)
|
||||
col.operator("groom.region_add", icon='ZOOMIN', text="")
|
||||
col.operator("groom.region_remove", icon='ZOOMOUT', text="")
|
||||
|
||||
if region:
|
||||
col = layout.column()
|
||||
if groom.scalp_object:
|
||||
col.prop_search(region, "scalp_facemap", groom.scalp_object, "face_maps", text="")
|
||||
else:
|
||||
col.prop(region, "scalp_facemap", text="")
|
||||
|
||||
col.prop(region.bundle, "guides_count")
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.prop(region, "taper_length")
|
||||
sub.prop(region, "taper_thickness")
|
||||
|
||||
|
||||
class DATA_PT_groom_hair(DataButtonsPanel, Panel):
|
||||
bl_label = "Hair"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
groom = context.groom
|
||||
|
||||
layout.operator("groom.hair_distribute")
|
||||
|
||||
|
||||
class DATA_PT_groom_draw_settings(DataButtonsPanel, Panel):
|
||||
bl_label = "Draw Settings"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
groom = context.groom
|
||||
ds = groom.hair_draw_settings
|
||||
|
||||
layout.prop(groom, "material_slot")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label("Follicles:")
|
||||
col.prop(ds, "follicle_mode", expand=True)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label("Guide Curves:")
|
||||
col.prop(ds, "guide_mode", expand=True)
|
||||
|
||||
layout.prop(ds, "shape")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(ds, "root_radius")
|
||||
col.prop(ds, "tip_radius")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(ds, "radius_scale")
|
||||
col.prop(ds, "use_close_tip")
|
||||
|
||||
|
||||
class DATA_PT_custom_props_groom(DataButtonsPanel, PropertyPanel, Panel):
|
||||
_context_path = "object.data"
|
||||
_property_type = bpy.types.Groom
|
||||
|
||||
|
||||
classes = (
|
||||
GROOM_UL_regions,
|
||||
DATA_PT_context_groom,
|
||||
DATA_PT_groom,
|
||||
DATA_PT_groom_regions,
|
||||
DATA_PT_groom_hair,
|
||||
DATA_PT_groom_draw_settings,
|
||||
DATA_PT_custom_props_groom,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
@@ -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)
|
||||
|
61
release/scripts/startup/bl_ui/properties_hair_common.py
Normal file
61
release/scripts/startup/bl_ui/properties_hair_common.py
Normal 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)
|
@@ -57,7 +57,10 @@ class VIEW3D_HT_header(Header):
|
||||
# object_mode = obj.mode
|
||||
|
||||
# Particle edit
|
||||
if object_mode == 'PARTICLE_EDIT':
|
||||
if object_mode == 'EDIT' and obj.type == 'GROOM':
|
||||
row = layout.row()
|
||||
row.prop(tool_settings.groom_edit_settings, "mode", text="", expand=True)
|
||||
elif object_mode == 'PARTICLE_EDIT':
|
||||
row = layout.row()
|
||||
row.prop(tool_settings.particle_edit, "select_mode", text="", expand=True)
|
||||
|
||||
@@ -285,6 +288,8 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
layout.menu("INFO_MT_surface_add", text="Add")
|
||||
elif mode_string == 'EDIT_METABALL':
|
||||
layout.menu("INFO_MT_metaball_add", text="Add")
|
||||
elif mode_string == 'EDIT_GROOM':
|
||||
layout.menu("INFO_MT_groom_add", text="Add")
|
||||
elif mode_string == 'EDIT_ARMATURE':
|
||||
layout.menu("INFO_MT_edit_armature_add", text="Add")
|
||||
|
||||
@@ -1390,6 +1395,17 @@ class INFO_MT_metaball_add(Menu):
|
||||
layout.operator_enum("object.metaball_add", "type")
|
||||
|
||||
|
||||
class INFO_MT_groom_add(Menu):
|
||||
bl_idname = "INFO_MT_groom_add"
|
||||
bl_label = "Groom"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
layout.operator("object.groom_add")
|
||||
|
||||
|
||||
class INFO_MT_edit_curve_add(Menu):
|
||||
bl_idname = "INFO_MT_edit_curve_add"
|
||||
bl_label = "Add"
|
||||
@@ -1480,6 +1496,7 @@ class INFO_MT_add(Menu):
|
||||
# layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE')
|
||||
layout.menu("INFO_MT_surface_add", icon='OUTLINER_OB_SURFACE')
|
||||
layout.menu("INFO_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META')
|
||||
layout.menu("INFO_MT_groom_add", text="Groom", icon='OUTLINER_OB_GROOM')
|
||||
layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT')
|
||||
layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
|
||||
layout.separator()
|
||||
@@ -3420,6 +3437,28 @@ class VIEW3D_MT_edit_lattice(Menu):
|
||||
layout.operator("object.vertex_parent_set")
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_groom(Menu):
|
||||
bl_label = "Groom"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
edit_object = context.edit_object
|
||||
groom = edit_object.data
|
||||
|
||||
layout.menu("VIEW3D_MT_undo_redo")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_transform")
|
||||
layout.menu("VIEW3D_MT_mirror")
|
||||
layout.menu("VIEW3D_MT_snap")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("groom.region_add")
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_armature(Menu):
|
||||
bl_label = "Armature"
|
||||
|
||||
@@ -5025,6 +5064,7 @@ classes = (
|
||||
INFO_MT_curve_add,
|
||||
INFO_MT_surface_add,
|
||||
INFO_MT_metaball_add,
|
||||
INFO_MT_groom_add,
|
||||
INFO_MT_edit_curve_add,
|
||||
INFO_MT_edit_armature_add,
|
||||
INFO_MT_armature_add,
|
||||
@@ -5111,6 +5151,7 @@ classes = (
|
||||
VIEW3D_MT_edit_meta,
|
||||
VIEW3D_MT_edit_meta_showhide,
|
||||
VIEW3D_MT_edit_lattice,
|
||||
VIEW3D_MT_edit_groom,
|
||||
VIEW3D_MT_edit_armature,
|
||||
VIEW3D_MT_armature_specials,
|
||||
VIEW3D_MT_edit_armature_parent,
|
||||
|
@@ -78,7 +78,6 @@ def is_not_gpencil_edit_mode(context):
|
||||
)
|
||||
return not is_gpmode
|
||||
|
||||
|
||||
# ********** default tools for editmode_mesh ****************
|
||||
|
||||
|
||||
|
@@ -48,7 +48,9 @@ set(SRC_DNA_INC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h
|
||||
${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_groom_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_group_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_hair_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h
|
||||
|
@@ -111,6 +111,7 @@ enum {
|
||||
CTX_MODE_EDIT_ARMATURE,
|
||||
CTX_MODE_EDIT_METABALL,
|
||||
CTX_MODE_EDIT_LATTICE,
|
||||
CTX_MODE_EDIT_GROOM,
|
||||
CTX_MODE_POSE,
|
||||
CTX_MODE_SCULPT,
|
||||
CTX_MODE_PAINT_WEIGHT,
|
||||
|
149
source/blender/blenkernel/BKE_groom.h
Normal file
149
source/blender/blenkernel/BKE_groom.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BKE_GROOM_H__
|
||||
#define __BKE_GROOM_H__
|
||||
|
||||
/** \file BKE_groom.h
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
struct BoundBox;
|
||||
struct Depsgraph;
|
||||
struct Groom;
|
||||
struct GroomBundle;
|
||||
struct GroomRegion;
|
||||
struct Main;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
|
||||
|
||||
/* === Groom Datablock === */
|
||||
|
||||
void BKE_groom_init(struct Groom *groom);
|
||||
void *BKE_groom_add(struct Main *bmain, const char *name);
|
||||
|
||||
void BKE_groom_free(struct Groom *groom);
|
||||
void BKE_groom_bundle_curve_cache_clear(struct GroomBundle *bundle);
|
||||
|
||||
void BKE_groom_copy_data(struct Main *bmain, struct Groom *groom_dst, const struct Groom *groom_src, const int flag);
|
||||
struct Groom *BKE_groom_copy(struct Main *bmain, const struct Groom *groom);
|
||||
|
||||
void BKE_groom_make_local(struct Main *bmain, struct Groom *groom, const bool lib_local);
|
||||
|
||||
bool BKE_groom_minmax(struct Groom *groom, float min[3], float max[3]);
|
||||
void BKE_groom_boundbox_calc(struct Groom *groom);
|
||||
struct BoundBox *BKE_groom_boundbox_get(struct Object *ob);
|
||||
|
||||
|
||||
/* === Curve cache === */
|
||||
|
||||
void BKE_groom_curve_cache_update(const struct Depsgraph *depsgraph, struct Groom *groom);
|
||||
void BKE_groom_curve_cache_clear(struct Groom *groom);
|
||||
|
||||
|
||||
/* === Scalp regions === */
|
||||
|
||||
struct GroomRegion* BKE_groom_region_add(struct Groom *groom);
|
||||
void BKE_groom_region_remove(struct Groom *groom, struct GroomRegion *region);
|
||||
|
||||
struct Mesh* BKE_groom_get_scalp(const struct Depsgraph *depsgraph, const struct Groom *groom);
|
||||
|
||||
/* Set the region's facemap name.
|
||||
* Returns false if no facemap of that name can be found in the scalp object.
|
||||
*/
|
||||
bool BKE_groom_set_region_scalp_facemap(struct Groom *groom, struct GroomRegion *region, const char *facemap_name);
|
||||
|
||||
/* Try to bind bundles to their scalp regions */
|
||||
void BKE_groom_bind_scalp_regions(const struct Depsgraph *depsgraph, struct Groom *groom, bool force_rebind);
|
||||
|
||||
bool BKE_groom_region_bind(const struct Depsgraph *depsgraph, struct Groom *groom, struct GroomRegion *region, bool force_rebind);
|
||||
void BKE_groom_region_unbind(struct GroomRegion *region);
|
||||
|
||||
bool BKE_groom_region_reset_shape(const struct Depsgraph *depsgraph, const struct Groom *groom, struct GroomRegion *region);
|
||||
|
||||
/* Calculates the scalp orientation at the root of the region */
|
||||
bool BKE_groom_calc_region_transform_on_scalp(const struct GroomRegion *region, const struct Mesh *scalp, float r_loc[3], float r_rot[3][3]);
|
||||
|
||||
|
||||
/* === Constraints === */
|
||||
|
||||
/* Apply constraints on groom geometry */
|
||||
void BKE_groom_apply_constraints(const struct Depsgraph *depsgraph, struct Groom *groom);
|
||||
|
||||
|
||||
/* === Hair System === */
|
||||
|
||||
/* Create follicles on the scalp surface for hair fiber rendering */
|
||||
void BKE_groom_hair_distribute(const struct Depsgraph *depsgraph, struct Groom *groom, unsigned int seed, int hair_count);
|
||||
|
||||
/* Calculate guide curve shapes based on groom bundle deformation */
|
||||
void BKE_groom_hair_update_guide_curves(const struct Depsgraph *depsgraph, struct Groom *groom);
|
||||
|
||||
|
||||
/* === Depsgraph evaluation === */
|
||||
|
||||
void BKE_groom_eval_geometry(const struct Depsgraph *depsgraph, struct Groom *groom);
|
||||
|
||||
|
||||
/* === Draw Cache === */
|
||||
|
||||
enum {
|
||||
BKE_GROOM_BATCH_DIRTY_ALL = 0,
|
||||
BKE_GROOM_BATCH_DIRTY_SELECT,
|
||||
};
|
||||
void BKE_groom_batch_cache_dirty(struct Groom *groom, int mode);
|
||||
void BKE_groom_batch_cache_free(struct Groom *groom);
|
||||
|
||||
/* === Iterators === */
|
||||
|
||||
/* Utility class for iterating over groom elements */
|
||||
typedef struct GroomIterator
|
||||
{
|
||||
int isection; /* section index */
|
||||
struct GroomSection *section; /* section data pointer */
|
||||
|
||||
int ivertex; /* vertex index */
|
||||
int isectionvertex; /* vertex index for the inner loop */
|
||||
struct GroomSectionVertex *vertex; /* vertex data pointer */
|
||||
} GroomIterator;
|
||||
|
||||
#define GROOM_ITER_SECTIONS(iter, bundle) \
|
||||
for (iter.isection = 0, iter.section = (bundle)->sections; \
|
||||
iter.isection < (bundle)->totsections; \
|
||||
++iter.isection, ++iter.section)
|
||||
|
||||
#define GROOM_ITER_SECTION_LOOPS(iter, sectionvar, vertexvar, bundle) \
|
||||
for (iter.isection = 0, iter.section = (bundle)->sections, iter.ivertex = 0, iter.vertex = (bundle)->verts; \
|
||||
iter.isection < (bundle)->totsections; \
|
||||
++iter.isection, ++iter.section) \
|
||||
for (iter.isectionvertex = 0; \
|
||||
iter.isectionvertex < (bundle)->numloopverts; \
|
||||
++iter.isectionvertex, ++iter.vertex)
|
||||
|
||||
#endif /* __BKE_GROOM_H__ */
|
244
source/blender/blenkernel/BKE_hair.h
Normal file
244
source/blender/blenkernel/BKE_hair.h
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* ***** 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
|
||||
* \param totverts Number of guide curve vertices to allocate
|
||||
*/
|
||||
void BKE_hair_guide_curves_alloc(struct HairSystem *hsys, int totcurves, int totverts);
|
||||
|
||||
/* Allocate buffers for defining guide curves
|
||||
* \param totcurves Number of guide 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
|
@@ -167,7 +167,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma
|
||||
|
||||
struct ListBase *which_libbase(struct Main *mainlib, short type);
|
||||
|
||||
#define MAX_LIBARRAY 37
|
||||
#define MAX_LIBARRAY 38
|
||||
int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
|
||||
|
||||
/* Main API */
|
||||
|
@@ -127,6 +127,7 @@ typedef struct Main {
|
||||
ListBase linestyle;
|
||||
ListBase cachefiles;
|
||||
ListBase workspaces;
|
||||
ListBase grooms;
|
||||
|
||||
/* Must be generated, used and freed by same code - never assume this is valid data unless you know
|
||||
* when, who and how it was created.
|
||||
|
116
source/blender/blenkernel/BKE_mesh_sample.h
Normal file
116
source/blender/blenkernel/BKE_mesh_sample.h
Normal 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__ */
|
@@ -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);
|
||||
|
@@ -118,6 +118,9 @@ set(SRC
|
||||
intern/freestyle.c
|
||||
intern/gpencil.c
|
||||
intern/gpencil_modifier.c
|
||||
intern/groom.c
|
||||
intern/hair.c
|
||||
intern/hair_draw.c
|
||||
intern/icons.c
|
||||
intern/icons_rasterize.c
|
||||
intern/idcode.c
|
||||
@@ -149,6 +152,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 +268,8 @@ set(SRC
|
||||
BKE_global.h
|
||||
BKE_gpencil.h
|
||||
BKE_gpencil_modifier.h
|
||||
BKE_groom.h
|
||||
BKE_hair.h
|
||||
BKE_icons.h
|
||||
BKE_idcode.h
|
||||
BKE_idprop.h
|
||||
@@ -288,6 +294,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
|
||||
|
@@ -1003,6 +1003,8 @@ int CTX_data_mode_enum_ex(const Object *obedit, const Object *ob, const eObjectM
|
||||
return CTX_MODE_EDIT_METABALL;
|
||||
case OB_LATTICE:
|
||||
return CTX_MODE_EDIT_LATTICE;
|
||||
case OB_GROOM:
|
||||
return CTX_MODE_EDIT_GROOM;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1041,6 +1043,7 @@ static const char *data_mode_strings[] = {
|
||||
"armature_edit",
|
||||
"mball_edit",
|
||||
"lattice_edit",
|
||||
"groom_edit",
|
||||
"posemode",
|
||||
"sculpt_mode",
|
||||
"weightpaint",
|
||||
|
1653
source/blender/blenkernel/intern/groom.c
Normal file
1653
source/blender/blenkernel/intern/groom.c
Normal file
File diff suppressed because it is too large
Load Diff
721
source/blender/blenkernel/intern/hair.c
Normal file
721
source/blender/blenkernel/intern/hair.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
314
source/blender/blenkernel/intern/hair_draw.c
Normal file
314
source/blender/blenkernel/intern/hair_draw.c
Normal 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);
|
||||
}
|
||||
}
|
@@ -64,6 +64,7 @@ static IDType idtypes[] = {
|
||||
{ ID_GR, "Collection", "collections", BLT_I18NCONTEXT_ID_COLLECTION, IDTYPE_FLAGS_ISLINKABLE },
|
||||
{ ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE },
|
||||
{ ID_GD, "GPencil", "grease_pencil", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */
|
||||
{ ID_GM, "Groom", "grooms", BLT_I18NCONTEXT_ID_GROOM, IDTYPE_FLAGS_ISLINKABLE },
|
||||
{ ID_IM, "Image", "images", BLT_I18NCONTEXT_ID_IMAGE, IDTYPE_FLAGS_ISLINKABLE },
|
||||
{ ID_IP, "Ipo", "ipos", "", IDTYPE_FLAGS_ISLINKABLE }, /* deprecated */
|
||||
{ ID_KE, "Key", "shape_keys", BLT_I18NCONTEXT_ID_SHAPEKEY, 0 },
|
||||
@@ -281,6 +282,7 @@ int BKE_idcode_to_index(const short idcode)
|
||||
CASE_IDINDEX(CF);
|
||||
CASE_IDINDEX(CU);
|
||||
CASE_IDINDEX(GD);
|
||||
CASE_IDINDEX(GM);
|
||||
CASE_IDINDEX(GR);
|
||||
CASE_IDINDEX(IM);
|
||||
CASE_IDINDEX(KE);
|
||||
|
@@ -47,8 +47,9 @@
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_ipo_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -97,6 +98,7 @@
|
||||
#include "BKE_font.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_idcode.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_image.h"
|
||||
@@ -475,6 +477,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local)
|
||||
case ID_CF:
|
||||
if (!test) BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local);
|
||||
return true;
|
||||
case ID_GM:
|
||||
if (!test) BKE_groom_make_local(bmain, (Groom *)id, lib_local);
|
||||
return true;
|
||||
case ID_WS:
|
||||
case ID_SCR:
|
||||
/* A bit special: can be appended but not linked. Return false
|
||||
@@ -659,6 +664,9 @@ bool BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag, con
|
||||
case ID_VF:
|
||||
BKE_vfont_copy_data(bmain, (VFont *)*r_newid, (VFont *)id, flag);
|
||||
break;
|
||||
case ID_GM:
|
||||
BKE_groom_copy_data(bmain, (Groom *)*r_newid, (Groom *)id, flag);
|
||||
break;
|
||||
case ID_LI:
|
||||
case ID_SCR:
|
||||
case ID_WM:
|
||||
@@ -744,6 +752,7 @@ void BKE_id_swap(Main *bmain, ID *id_a, ID *id_b)
|
||||
CASE_SWAP(ID_PAL, Palette);
|
||||
CASE_SWAP(ID_PC, PaintCurve);
|
||||
CASE_SWAP(ID_CF, CacheFile);
|
||||
CASE_SWAP(ID_GM, Groom);
|
||||
case ID_IP:
|
||||
break; /* Deprecated. */
|
||||
}
|
||||
@@ -965,6 +974,8 @@ ListBase *which_libbase(Main *mainlib, short type)
|
||||
return &(mainlib->cachefiles);
|
||||
case ID_WS:
|
||||
return &(mainlib->workspaces);
|
||||
case ID_GM:
|
||||
return &(mainlib->grooms);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -1057,6 +1068,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
|
||||
@@ -1091,6 +1104,7 @@ int set_listbasepointers(Main *main, ListBase **lb)
|
||||
lb[INDEX_ID_ME] = &(main->mesh);
|
||||
lb[INDEX_ID_CU] = &(main->curve);
|
||||
lb[INDEX_ID_MB] = &(main->mball);
|
||||
lb[INDEX_ID_GM] = &(main->grooms);
|
||||
|
||||
lb[INDEX_ID_LT] = &(main->latt);
|
||||
lb[INDEX_ID_LA] = &(main->lamp);
|
||||
@@ -1182,6 +1196,7 @@ size_t BKE_libblock_get_alloc_info(short type, const char **name)
|
||||
CASE_RETURN(ID_PC, PaintCurve);
|
||||
CASE_RETURN(ID_CF, CacheFile);
|
||||
CASE_RETURN(ID_WS, WorkSpace);
|
||||
CASE_RETURN(ID_GM, Groom);
|
||||
}
|
||||
return 0;
|
||||
#undef CASE_RETURN
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
@@ -633,6 +634,16 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_GM:
|
||||
{
|
||||
Groom *groom = (Groom *) id;
|
||||
CALLBACK_INVOKE(groom->scalp_object, IDWALK_CB_NOP);
|
||||
for (i = 0; i < groom->totcol; i++) {
|
||||
CALLBACK_INVOKE(groom->mat[i], IDWALK_CB_USER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_MA:
|
||||
{
|
||||
Material *material = (Material *) id;
|
||||
@@ -1108,6 +1119,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
|
||||
return ELEM(id_type_used, ID_IM);
|
||||
case ID_GD:
|
||||
return ELEM(id_type_used, ID_MA);
|
||||
case ID_GM:
|
||||
return true;
|
||||
case ID_WS:
|
||||
return ELEM(id_type_used, ID_SCR, ID_SCE);
|
||||
case ID_IM:
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_ipo_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -78,6 +79,7 @@
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_font.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_ipo.h"
|
||||
@@ -870,6 +872,9 @@ void BKE_libblock_free_datablock(ID *id, const int UNUSED(flag))
|
||||
case ID_WS:
|
||||
BKE_workspace_free((WorkSpace *)id);
|
||||
break;
|
||||
case ID_GM:
|
||||
BKE_groom_free((Groom *)id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
@@ -277,6 +278,10 @@ Material ***give_matarar(Object *ob)
|
||||
gpd = ob->data;
|
||||
return &(gpd->mat);
|
||||
}
|
||||
else if (ob->type == OB_GROOM) {
|
||||
Groom *groom = ob->data;
|
||||
return &(groom->mat);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -303,6 +308,10 @@ short *give_totcolp(Object *ob)
|
||||
gpd = ob->data;
|
||||
return &(gpd->totcol);
|
||||
}
|
||||
else if (ob->type == OB_GROOM) {
|
||||
Groom *groom = ob->data;
|
||||
return &(groom->totcol);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -321,6 +330,8 @@ Material ***give_matarar_id(ID *id)
|
||||
return &(((MetaBall *)id)->mat);
|
||||
case ID_GD:
|
||||
return &(((bGPdata *)id)->mat);
|
||||
case ID_GM:
|
||||
return &(((Groom *)id)->mat);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -341,6 +352,8 @@ short *give_totcolp_id(ID *id)
|
||||
return &(((MetaBall *)id)->totcol);
|
||||
case ID_GD:
|
||||
return &(((bGPdata *)id)->totcol);
|
||||
case ID_GM:
|
||||
return &(((Groom *)id)->totcol);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
1673
source/blender/blenkernel/intern/mesh_sample.c
Normal file
1673
source/blender/blenkernel/intern/mesh_sample.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,7 @@
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_gpencil_modifier_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
@@ -75,6 +76,7 @@
|
||||
#include "BKE_pbvh.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_action.h"
|
||||
@@ -362,6 +364,13 @@ void BKE_object_free_derived_caches(Object *ob)
|
||||
atomic_fetch_and_or_int32(&cu->bb->flag, BOUNDBOX_DIRTY);
|
||||
}
|
||||
}
|
||||
else if (ELEM(ob->type, OB_GROOM)) {
|
||||
Groom *groom = ob->data;
|
||||
|
||||
if (groom && groom->bb) {
|
||||
atomic_fetch_and_or_int32(&groom->bb->flag, BOUNDBOX_DIRTY);
|
||||
}
|
||||
}
|
||||
|
||||
if (ob->bb) {
|
||||
MEM_freeN(ob->bb);
|
||||
@@ -554,6 +563,11 @@ bool BKE_object_is_in_editmode(const Object *ob)
|
||||
if (cu->editnurb)
|
||||
return true;
|
||||
}
|
||||
else if (ob->type == OB_GROOM) {
|
||||
Groom *groom = ob->data;
|
||||
if (groom->editgroom)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -689,6 +703,7 @@ static const char *get_obdata_defname(int type)
|
||||
case OB_SURF: return DATA_("Surf");
|
||||
case OB_FONT: return DATA_("Text");
|
||||
case OB_MBALL: return DATA_("Mball");
|
||||
case OB_GROOM: return DATA_("Groom");
|
||||
case OB_CAMERA: return DATA_("Camera");
|
||||
case OB_LAMP: return DATA_("Light");
|
||||
case OB_LATTICE: return DATA_("Lattice");
|
||||
@@ -721,6 +736,7 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name)
|
||||
case OB_SPEAKER: return BKE_speaker_add(bmain, name);
|
||||
case OB_LIGHTPROBE:return BKE_lightprobe_add(bmain, name);
|
||||
case OB_GPENCIL: return BKE_gpencil_data_addnew(bmain, name);
|
||||
case OB_GROOM: return BKE_groom_add(bmain, name);
|
||||
case OB_EMPTY: return NULL;
|
||||
default:
|
||||
printf("%s: Internal error, bad type: %d\n", __func__, type);
|
||||
@@ -2444,6 +2460,9 @@ BoundBox *BKE_object_boundbox_get(Object *ob)
|
||||
else if (ob->type == OB_ARMATURE) {
|
||||
bb = BKE_armature_boundbox_get(ob);
|
||||
}
|
||||
else if (ob->type == OB_GROOM) {
|
||||
bb = BKE_groom_boundbox_get(ob);
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
@@ -63,6 +63,7 @@
|
||||
#include "BKE_pointcache.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_groom.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
@@ -328,6 +329,9 @@ void BKE_object_eval_uber_data(Depsgraph *depsgraph,
|
||||
case OB_GPENCIL:
|
||||
BKE_gpencil_batch_cache_dirty(ob->data);
|
||||
break;
|
||||
case OB_GROOM:
|
||||
BKE_groom_batch_cache_dirty(ob->data, BKE_GROOM_BATCH_DIRTY_ALL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,6 +383,10 @@ void BKE_object_data_select_update(Depsgraph *depsgraph, ID *object_data)
|
||||
BKE_lattice_batch_cache_dirty((struct Lattice *)object_data,
|
||||
BKE_CURVE_BATCH_DIRTY_SELECT);
|
||||
break;
|
||||
case ID_GM:
|
||||
BKE_groom_batch_cache_dirty((struct Groom *)object_data,
|
||||
BKE_GROOM_BATCH_DIRTY_SELECT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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]);
|
||||
|
||||
|
@@ -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])
|
||||
{
|
||||
|
@@ -70,9 +70,11 @@
|
||||
#include "DNA_effect_types.h"
|
||||
#include "DNA_fileglobal_types.h"
|
||||
#include "DNA_genfile.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_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 +5097,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 +5439,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8367,6 +8398,61 @@ static void direct_link_linestyle(FileData *fd, FreestyleLineStyle *linestyle)
|
||||
}
|
||||
}
|
||||
|
||||
/* ************ READ GROOM *************** */
|
||||
|
||||
static void lib_link_groom(FileData *fd, Main *bmain)
|
||||
{
|
||||
for (Groom *groom = bmain->grooms.first; groom; groom = groom->id.next) {
|
||||
ID *id = (ID *)groom;
|
||||
|
||||
if ((id->tag & LIB_TAG_NEED_LINK) == 0) {
|
||||
continue;
|
||||
}
|
||||
IDP_LibLinkProperty(id->properties, fd);
|
||||
id_us_ensure_real(id);
|
||||
|
||||
groom->scalp_object = newlibadr(fd, id->lib, groom->scalp_object);
|
||||
|
||||
for (int a = 0; a < groom->totcol; a++) {
|
||||
groom->mat[a] = newlibadr_us(fd, groom->id.lib, groom->mat[a]);
|
||||
}
|
||||
|
||||
id->tag &= ~LIB_TAG_NEED_LINK;
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_groom(FileData *fd, Groom *groom)
|
||||
{
|
||||
groom->adt= newdataadr(fd, groom->adt);
|
||||
direct_link_animdata(fd, groom->adt);
|
||||
|
||||
link_list(fd, &groom->regions);
|
||||
for (GroomRegion *region = groom->regions.first; region; region = region->next)
|
||||
{
|
||||
region->scalp_samples = newdataadr(fd, region->scalp_samples);
|
||||
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
bundle->sections = newdataadr(fd, bundle->sections);
|
||||
bundle->verts = newdataadr(fd, bundle->verts);
|
||||
bundle->guides = newdataadr(fd, bundle->guides);
|
||||
bundle->guide_shape_weights = newdataadr(fd, bundle->guide_shape_weights);
|
||||
bundle->curvecache = NULL;
|
||||
bundle->curvesize = 0;
|
||||
bundle->totcurvecache = 0;
|
||||
}
|
||||
|
||||
groom->hair_system = newdataadr(fd, groom->hair_system);
|
||||
direct_link_hair(fd, groom->hair_system);
|
||||
groom->hair_draw_settings = newdataadr(fd, groom->hair_draw_settings);
|
||||
|
||||
groom->mat = newdataadr(fd, groom->mat);
|
||||
test_pointer_array(fd, (void **)&groom->mat);
|
||||
|
||||
groom->bb = NULL;
|
||||
groom->editgroom = NULL;
|
||||
groom->batch_cache = NULL;
|
||||
}
|
||||
|
||||
/* ************** GENERAL & MAIN ******************** */
|
||||
|
||||
|
||||
@@ -8664,6 +8750,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short
|
||||
case ID_WS:
|
||||
direct_link_workspace(fd, (WorkSpace *)id, main);
|
||||
break;
|
||||
case ID_GM:
|
||||
direct_link_groom(fd, (Groom *)id);
|
||||
break;
|
||||
}
|
||||
|
||||
oldnewmap_free_unused(fd->datamap);
|
||||
@@ -8851,6 +8940,7 @@ static void lib_link_all(FileData *fd, Main *main)
|
||||
lib_link_gpencil(fd, main);
|
||||
lib_link_cachefiles(fd, main);
|
||||
lib_link_workspaces(fd, main);
|
||||
lib_link_groom(fd, main);
|
||||
|
||||
lib_link_library(fd, main); /* only init users */
|
||||
}
|
||||
@@ -9506,6 +9596,15 @@ static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSetting
|
||||
}
|
||||
}
|
||||
|
||||
static void expand_groom(FileData *fd, Main *mainvar, Groom *groom)
|
||||
{
|
||||
expand_doit(fd, mainvar, groom->scalp_object);
|
||||
|
||||
for (int a = 0; a < groom->totcol; a++) {
|
||||
expand_doit(fd, mainvar, groom->mat[a]);
|
||||
}
|
||||
}
|
||||
|
||||
static void expand_collection(FileData *fd, Main *mainvar, Collection *collection)
|
||||
{
|
||||
for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
|
||||
@@ -10182,6 +10281,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
|
||||
case ID_AC:
|
||||
expand_action(fd, mainvar, (bAction *)id); // XXX deprecated - old animation system
|
||||
break;
|
||||
case ID_GM:
|
||||
expand_groom(fd, mainvar, (Groom *)id);
|
||||
break;
|
||||
case ID_GR:
|
||||
expand_collection(fd, mainvar, (Collection *)id);
|
||||
break;
|
||||
|
@@ -116,11 +116,13 @@
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_dynamicpaint_types.h"
|
||||
#include "DNA_genfile.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_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 +1619,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 +1802,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3680,6 +3706,38 @@ static void write_workspace(WriteData *wd, WorkSpace *workspace)
|
||||
}
|
||||
}
|
||||
|
||||
static void write_groom(WriteData *wd, Groom *groom)
|
||||
{
|
||||
writestruct(wd, ID_GM, Groom, 1, groom);
|
||||
write_iddata(wd, &groom->id);
|
||||
if (groom->adt) {
|
||||
write_animdata(wd, groom->adt);
|
||||
}
|
||||
|
||||
writelist(wd, DATA, GroomRegion, &groom->regions);
|
||||
for (GroomRegion *region = groom->regions.first; region; region = region->next)
|
||||
{
|
||||
writestruct(wd, DATA, MeshSample, region->numverts + 1, region->scalp_samples);
|
||||
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
writestruct(wd, DATA, GroomSection, bundle->totsections, bundle->sections);
|
||||
writestruct(wd, DATA, GroomSectionVertex, bundle->totverts, bundle->verts);
|
||||
writestruct(wd, DATA, GroomHairGuide, bundle->totguides, bundle->guides);
|
||||
writedata(wd, DATA, sizeof(float) * bundle->totguides * region->numverts, bundle->guide_shape_weights);
|
||||
}
|
||||
|
||||
if (groom->hair_system) {
|
||||
writestruct(wd, DATA, HairSystem, 1, groom->hair_system);
|
||||
write_hair(wd, groom->hair_system);
|
||||
}
|
||||
if (groom->hair_draw_settings)
|
||||
{
|
||||
writestruct(wd, DATA, HairDrawSettings, 1, groom->hair_draw_settings);
|
||||
}
|
||||
|
||||
writedata(wd, DATA, sizeof(void *) * groom->totcol, groom->mat);
|
||||
}
|
||||
|
||||
/* Keep it last of write_foodata functions. */
|
||||
static void write_libraries(WriteData *wd, Main *main)
|
||||
{
|
||||
@@ -3978,6 +4036,9 @@ static bool write_file_handle(
|
||||
case ID_CF:
|
||||
write_cachefile(wd, (CacheFile *)id);
|
||||
break;
|
||||
case ID_GM:
|
||||
write_groom(wd, (Groom *)id);
|
||||
break;
|
||||
case ID_LI:
|
||||
/* Do nothing, handled below - and should never be reached. */
|
||||
BLI_assert(0);
|
||||
|
@@ -154,6 +154,7 @@ bool BLT_lang_is_ime_supported(void);
|
||||
#define BLT_I18NCONTEXT_ID_WINDOWMANAGER "WindowManager"
|
||||
#define BLT_I18NCONTEXT_ID_MOVIECLIP "MovieClip"
|
||||
#define BLT_I18NCONTEXT_ID_MASK "Mask"
|
||||
#define BLT_I18NCONTEXT_ID_GROOM "Groom"
|
||||
|
||||
/* Helper for bpy.app.i18n object... */
|
||||
typedef struct {
|
||||
@@ -179,6 +180,7 @@ typedef struct {
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GROOM, "id_groom"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ID, "id_id"), \
|
||||
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_IMAGE, "id_image"), \
|
||||
/*BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_IPO, "id_ipo"),*/ \
|
||||
|
@@ -54,6 +54,7 @@ set(SRC
|
||||
operators/bmo_dupe.c
|
||||
operators/bmo_edgenet.c
|
||||
operators/bmo_extrude.c
|
||||
operators/bmo_face_island.c
|
||||
operators/bmo_fill_attribute.c
|
||||
operators/bmo_fill_edgeloop.c
|
||||
operators/bmo_fill_grid.c
|
||||
|
@@ -2022,6 +2022,26 @@ static BMOpDefine bmo_symmetrize_def = {
|
||||
BMO_OPTYPE_FLAG_SELECT_VALIDATE),
|
||||
};
|
||||
|
||||
/*
|
||||
* Face island boundary.
|
||||
*/
|
||||
static BMOpDefine bmo_face_island_boundary_def = {
|
||||
"face_island_boundary",
|
||||
/* slots_in */
|
||||
{{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}},
|
||||
{{'\0'}},
|
||||
},
|
||||
/* slots_out */
|
||||
{{"boundary", BMO_OP_SLOT_ELEMENT_BUF, {BM_LOOP}},
|
||||
{{'\0'}},
|
||||
},
|
||||
bmo_face_island_boundary_exec,
|
||||
(BMO_OPTYPE_FLAG_UNTAN_MULTIRES |
|
||||
BMO_OPTYPE_FLAG_NORMALS_CALC |
|
||||
BMO_OPTYPE_FLAG_SELECT_FLUSH |
|
||||
BMO_OPTYPE_FLAG_SELECT_VALIDATE),
|
||||
};
|
||||
|
||||
const BMOpDefine *bmo_opdefines[] = {
|
||||
&bmo_automerge_def,
|
||||
&bmo_average_vert_facedata_def,
|
||||
@@ -2057,6 +2077,7 @@ const BMOpDefine *bmo_opdefines[] = {
|
||||
&bmo_duplicate_def,
|
||||
&bmo_holes_fill_def,
|
||||
&bmo_face_attribute_fill_def,
|
||||
&bmo_face_island_boundary_def,
|
||||
&bmo_offset_edgeloops_def,
|
||||
&bmo_edgeloop_fill_def,
|
||||
&bmo_edgenet_fill_def,
|
||||
|
@@ -63,6 +63,7 @@ void bmo_dissolve_degenerate_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_duplicate_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_edgeloop_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_face_attribute_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_face_island_boundary_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_holes_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_edgenet_fill_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_edgenet_prepare_exec(BMesh *bm, BMOperator *op);
|
||||
|
@@ -698,8 +698,6 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker)
|
||||
{
|
||||
BMwIslandboundWalker *iwalk, owalk;
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
BMLoop *l;
|
||||
/* int found = 0; */
|
||||
|
||||
@@ -708,31 +706,25 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker)
|
||||
iwalk = &owalk;
|
||||
|
||||
l = iwalk->curloop;
|
||||
e = l->e;
|
||||
|
||||
v = BM_edge_other_vert(e, iwalk->lastv);
|
||||
v = BM_edge_other_vert(l->e, iwalk->lastv);
|
||||
|
||||
/* pop off current state */
|
||||
BMW_state_remove(walker);
|
||||
|
||||
f = l->f;
|
||||
|
||||
while (1) {
|
||||
l = BM_loop_other_edge_loop(l, v);
|
||||
if (BM_loop_is_manifold(l)) {
|
||||
l = l->radial_next;
|
||||
f = l->f;
|
||||
e = l->e;
|
||||
|
||||
if (!bmw_mask_check_face(walker, f)) {
|
||||
if (!bmw_mask_check_face(walker, l->f)) {
|
||||
l = l->radial_next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* treat non-manifold edges as boundaries */
|
||||
f = l->f;
|
||||
e = l->e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
239
source/blender/bmesh/operators/bmo_face_island.c
Normal file
239
source/blender/bmesh/operators/bmo_face_island.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/operators/bmo_face_island.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Face island search.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "intern/bmesh_operators_private.h"
|
||||
|
||||
|
||||
#define FACE_MARK 1
|
||||
|
||||
static BMLoop* bmo_face_island_find_start_loop(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMFace *f;
|
||||
BMOIter oiter;
|
||||
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE)
|
||||
{
|
||||
BMLoop *l;
|
||||
BMIter l_iter;
|
||||
BM_ITER_ELEM(l, &l_iter, f, BM_LOOPS_OF_FACE)
|
||||
{
|
||||
if (!BM_loop_is_manifold(l)) {
|
||||
/* treat non-manifold edges as boundaries */
|
||||
return l;
|
||||
}
|
||||
|
||||
BMLoop *lr = l;
|
||||
do
|
||||
{
|
||||
if (!BMO_face_flag_test(bm, lr->f, FACE_MARK))
|
||||
{
|
||||
return l;
|
||||
}
|
||||
lr = lr->radial_next;
|
||||
}
|
||||
while (lr != l);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bmo_face_island_boundary_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_MARK);
|
||||
|
||||
BMLoop *l_start = bmo_face_island_find_start_loop(bm, op);
|
||||
if (!l_start)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BMLoop **boundary = NULL;
|
||||
BLI_array_declare(boundary);
|
||||
|
||||
BMWalker walker;
|
||||
BMW_init(&walker, bm, BMW_ISLANDBOUND,
|
||||
BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
|
||||
BMW_FLAG_NOP, /* no need to check BMW_FLAG_TEST_HIDDEN, faces are already marked by the bmo */
|
||||
BMW_NIL_LAY);
|
||||
|
||||
for (BMLoop *l_iter = BMW_begin(&walker, l_start); l_iter; l_iter = BMW_step(&walker))
|
||||
{
|
||||
BLI_array_append(boundary, l_iter);
|
||||
}
|
||||
BMW_end(&walker);
|
||||
|
||||
{
|
||||
BMOpSlot *slot = BMO_slot_get(op->slots_out, "boundary");
|
||||
BMO_slot_buffer_from_array(op, slot, (BMHeader **)boundary, BLI_array_len(boundary));
|
||||
BLI_array_free(boundary);
|
||||
}
|
||||
|
||||
#if 0
|
||||
BMOIter oiter;
|
||||
BMFace *f;
|
||||
BMFace ***regions = NULL;
|
||||
BMFace **faces = NULL;
|
||||
BLI_array_declare(regions);
|
||||
BLI_array_declare(faces);
|
||||
BMFace *act_face = bm->act_face;
|
||||
BMWalker regwalker;
|
||||
int i;
|
||||
|
||||
const bool use_verts = BMO_slot_bool_get(op->slots_in, "use_verts");
|
||||
|
||||
if (use_verts) {
|
||||
/* tag verts that start out with only 2 edges,
|
||||
* don't remove these later */
|
||||
BMIter viter;
|
||||
BMVert *v;
|
||||
|
||||
BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
|
||||
BMO_vert_flag_set(bm, v, VERT_MARK, !BM_vert_is_edge_pair(v));
|
||||
}
|
||||
}
|
||||
|
||||
BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_MARK | FACE_TAG);
|
||||
|
||||
/* collect region */
|
||||
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
|
||||
BMFace *f_iter;
|
||||
if (!BMO_face_flag_test(bm, f, FACE_TAG)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BLI_array_empty(faces);
|
||||
faces = NULL; /* forces different allocatio */
|
||||
|
||||
BMW_init(®walker, bm, BMW_ISLAND_MANIFOLD,
|
||||
BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
|
||||
BMW_FLAG_NOP, /* no need to check BMW_FLAG_TEST_HIDDEN, faces are already marked by the bmo */
|
||||
BMW_NIL_LAY);
|
||||
|
||||
for (f_iter = BMW_begin(®walker, f); f_iter; f_iter = BMW_step(®walker)) {
|
||||
BLI_array_append(faces, f_iter);
|
||||
}
|
||||
BMW_end(®walker);
|
||||
|
||||
for (i = 0; i < BLI_array_count(faces); i++) {
|
||||
f_iter = faces[i];
|
||||
BMO_face_flag_disable(bm, f_iter, FACE_TAG);
|
||||
BMO_face_flag_enable(bm, f_iter, FACE_ORIG);
|
||||
}
|
||||
|
||||
if (BMO_error_occurred(bm)) {
|
||||
BMO_error_clear(bm);
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
BLI_array_append(faces, NULL);
|
||||
BLI_array_append(regions, faces);
|
||||
}
|
||||
|
||||
/* track how many faces we should end up with */
|
||||
int totface_target = bm->totface;
|
||||
|
||||
for (i = 0; i < BLI_array_count(regions); i++) {
|
||||
BMFace *f_new;
|
||||
int tot = 0;
|
||||
|
||||
faces = regions[i];
|
||||
if (!faces[0]) {
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
|
||||
"Could not find boundary of dissolve region");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
while (faces[tot])
|
||||
tot++;
|
||||
|
||||
f_new = BM_faces_join(bm, faces, tot, true);
|
||||
|
||||
if (f_new) {
|
||||
/* maintain active face */
|
||||
if (act_face && bm->act_face == NULL) {
|
||||
bm->act_face = f_new;
|
||||
}
|
||||
totface_target -= tot - 1;
|
||||
}
|
||||
else {
|
||||
BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
|
||||
"Could not create merged face");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* if making the new face failed (e.g. overlapping test)
|
||||
* unmark the original faces for deletion */
|
||||
BMO_face_flag_disable(bm, f_new, FACE_ORIG);
|
||||
BMO_face_flag_enable(bm, f_new, FACE_NEW);
|
||||
}
|
||||
|
||||
/* Typically no faces need to be deleted */
|
||||
if (totface_target != bm->totface) {
|
||||
BMO_op_callf(bm, op->flag, "delete geom=%ff context=%i", FACE_ORIG, DEL_FACES);
|
||||
}
|
||||
|
||||
if (use_verts) {
|
||||
BMIter viter;
|
||||
BMVert *v, *v_next;
|
||||
|
||||
BM_ITER_MESH_MUTABLE (v, v_next, &viter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
|
||||
if (BM_vert_is_edge_pair(v)) {
|
||||
BM_vert_collapse_edge(bm, v->e, v, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BMO_error_occurred(bm)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "region.out", BM_FACE, FACE_NEW);
|
||||
|
||||
cleanup:
|
||||
/* free/cleanup */
|
||||
for (i = 0; i < BLI_array_count(regions); i++) {
|
||||
if (regions[i]) MEM_freeN(regions[i]);
|
||||
}
|
||||
|
||||
BLI_array_free(regions);
|
||||
#endif
|
||||
}
|
@@ -51,6 +51,7 @@ extern "C" {
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_effect_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -80,6 +81,7 @@ extern "C" {
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_gpencil_modifier.h"
|
||||
#include "BKE_idcode.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_library.h"
|
||||
@@ -604,6 +606,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object)
|
||||
case OB_MBALL:
|
||||
case OB_LATTICE:
|
||||
case OB_GPENCIL:
|
||||
case OB_GROOM:
|
||||
build_object_data_geometry(object);
|
||||
/* TODO(sergey): Only for until we support granular
|
||||
* update of curves.
|
||||
@@ -1239,6 +1242,18 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
|
||||
op_node->set_as_entry();
|
||||
break;
|
||||
}
|
||||
case ID_GM:
|
||||
{
|
||||
/* Groom evaluation operations. */
|
||||
op_node = add_operation_node(obdata, DEG_NODE_TYPE_GEOMETRY,
|
||||
function_bind(BKE_groom_eval_geometry,
|
||||
_1,
|
||||
(Groom *)obdata_cow),
|
||||
DEG_OPCODE_PLACEHOLDER,
|
||||
"Geometry Eval");
|
||||
op_node->set_as_entry();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert(!"Should not happen");
|
||||
break;
|
||||
|
@@ -47,6 +47,7 @@ struct ID;
|
||||
struct Image;
|
||||
struct FCurve;
|
||||
struct Collection;
|
||||
struct Groom;
|
||||
struct Key;
|
||||
struct Lamp;
|
||||
struct LayerCollection;
|
||||
|
@@ -51,6 +51,7 @@ extern "C" {
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_effect_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -79,6 +80,7 @@ extern "C" {
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_collision.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
@@ -637,6 +639,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object)
|
||||
case OB_MBALL:
|
||||
case OB_LATTICE:
|
||||
case OB_GPENCIL:
|
||||
case OB_GROOM:
|
||||
{
|
||||
build_object_data_geometry(object);
|
||||
break;
|
||||
@@ -2021,6 +2024,18 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ID_GM: /* Groom */
|
||||
{
|
||||
Groom *groom = (Groom *)obdata;
|
||||
ComponentKey geometry_key(&groom->id, DEG_NODE_TYPE_GEOMETRY);
|
||||
|
||||
if (groom->scalp_object)
|
||||
{
|
||||
ID *scalp_id = &groom->scalp_object->id;
|
||||
add_relation(ComponentKey(scalp_id, DEG_NODE_TYPE_GEOMETRY), geometry_key, "Scalp Object -> Groom");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert(!"Should not happen");
|
||||
break;
|
||||
|
@@ -81,6 +81,7 @@ struct ViewLayer;
|
||||
struct Tex;
|
||||
struct World;
|
||||
struct EffectorWeights;
|
||||
struct Groom;
|
||||
|
||||
struct PropertyRNA;
|
||||
|
||||
|
@@ -101,6 +101,7 @@ void depsgraph_geometry_tag_to_component(const ID *id,
|
||||
case OB_LATTICE:
|
||||
case OB_MBALL:
|
||||
case OB_GPENCIL:
|
||||
case OB_GROOM:
|
||||
*component_type = DEG_NODE_TYPE_GEOMETRY;
|
||||
break;
|
||||
case OB_ARMATURE:
|
||||
|
@@ -65,6 +65,7 @@ extern "C" {
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
@@ -470,6 +471,14 @@ void update_mesh_edit_mode_pointers(const Depsgraph *depsgraph,
|
||||
mesh_cow->edit_btmesh->derivedCage = NULL;
|
||||
}
|
||||
|
||||
void update_groom_edit_mode_pointers(const Depsgraph *UNUSED(depsgraph),
|
||||
const ID *id_orig, ID *id_cow)
|
||||
{
|
||||
const Groom *groom_orig = (const Groom *)id_orig;
|
||||
Groom *groom_cow = (Groom *)id_cow;
|
||||
groom_cow->editgroom = groom_orig->editgroom;
|
||||
}
|
||||
|
||||
/* Edit data is stored and owned by original datablocks, copied ones
|
||||
* are simply referencing to them.
|
||||
*/
|
||||
@@ -493,6 +502,9 @@ void update_edit_mode_pointers(const Depsgraph *depsgraph,
|
||||
case ID_LT:
|
||||
update_lattice_edit_mode_pointers(depsgraph, id_orig, id_cow);
|
||||
break;
|
||||
case ID_GM:
|
||||
update_groom_edit_mode_pointers(depsgraph, id_orig, id_cow);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -928,6 +940,12 @@ void discard_mesh_edit_mode_pointers(ID *id_cow)
|
||||
mesh_cow->edit_btmesh = NULL;
|
||||
}
|
||||
|
||||
void discard_groom_edit_mode_pointers(ID *id_cow)
|
||||
{
|
||||
Groom *groom_cow = (Groom *)id_cow;
|
||||
groom_cow->editgroom = NULL;
|
||||
}
|
||||
|
||||
void discard_scene_pointers(ID *id_cow)
|
||||
{
|
||||
Scene *scene_cow = (Scene *)id_cow;
|
||||
@@ -950,6 +968,9 @@ void discard_edit_mode_pointers(ID *id_cow)
|
||||
case ID_CU:
|
||||
discard_curve_edit_mode_pointers(id_cow);
|
||||
break;
|
||||
case ID_GM:
|
||||
discard_groom_edit_mode_pointers(id_cow);
|
||||
break;
|
||||
case ID_MB:
|
||||
discard_mball_edit_mode_pointers(id_cow);
|
||||
break;
|
||||
|
@@ -58,6 +58,8 @@ set(SRC
|
||||
intern/draw_cache.c
|
||||
intern/draw_cache_impl_curve.c
|
||||
intern/draw_cache_impl_displist.c
|
||||
intern/draw_cache_impl_groom.c
|
||||
intern/draw_cache_impl_hair.c
|
||||
intern/draw_cache_impl_lattice.c
|
||||
intern/draw_cache_impl_mesh.c
|
||||
intern/draw_cache_impl_metaball.c
|
||||
@@ -76,6 +78,7 @@ set(SRC
|
||||
intern/draw_view.c
|
||||
modes/edit_armature_mode.c
|
||||
modes/edit_curve_mode.c
|
||||
modes/edit_groom_mode.c
|
||||
modes/edit_lattice_mode.c
|
||||
modes/edit_mesh_mode.c
|
||||
modes/edit_mesh_mode_text.c
|
||||
@@ -275,6 +278,8 @@ data_to_c_simple(modes/shaders/edit_mesh_overlay_facefill_frag.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_curve_overlay_frag.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_curve_overlay_handle_geom.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_curve_overlay_loosevert_vert.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_groom_overlay_frag.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_groom_overlay_loosevert_vert.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_lattice_overlay_frag.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_lattice_overlay_loosevert_vert.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_normals_vert.glsl SRC)
|
||||
|
@@ -136,7 +136,7 @@ void EEVEE_cache_populate(void *vedata, Object *ob)
|
||||
}
|
||||
|
||||
if (DRW_check_object_visible_within_active_context(ob)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_GROOM)) {
|
||||
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
else if (!USE_SCENE_LIGHT(draw_ctx->v3d)) {
|
||||
|
@@ -31,6 +31,8 @@
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_hair.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_pbvh.h"
|
||||
@@ -39,6 +41,8 @@
|
||||
#include "DNA_world_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
|
||||
#include "GPU_material.h"
|
||||
|
||||
@@ -812,6 +816,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 +867,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 +901,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 +1282,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 +1425,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 = ½
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_FAILED:
|
||||
default:
|
||||
color_p = error_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
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 = ½
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_FAILED:
|
||||
default:
|
||||
color_p = error_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
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,115 +1827,35 @@ 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 = ½
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_FAILED:
|
||||
default:
|
||||
color_p = error_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ob->type == OB_GROOM) {
|
||||
Groom *groom = ob->data;
|
||||
|
||||
Material *material = give_current_material(ob, groom->material_index);
|
||||
|
||||
struct Mesh *scalp = BKE_groom_get_scalp(draw_ctx->depsgraph, groom);
|
||||
material_hair(vedata, sldata, ob, groom->hair_system, groom->hair_draw_settings, material, scalp);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_materials_cache_finish(EEVEE_Data *vedata)
|
||||
|
@@ -164,7 +164,7 @@ void EEVEE_render_cache(
|
||||
}
|
||||
|
||||
if (DRW_check_object_visible_within_active_context(ob)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_GROOM)) {
|
||||
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
|
||||
}
|
||||
else if (ob->type == OB_LIGHTPROBE) {
|
||||
|
@@ -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
|
||||
|
@@ -32,10 +32,12 @@
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_rand.h"
|
||||
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_particle.h"
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
@@ -633,7 +635,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);
|
||||
@@ -644,6 +646,34 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o
|
||||
}
|
||||
}
|
||||
|
||||
static void workbench_cache_populate_groom(WORKBENCH_Data *vedata, Object *ob)
|
||||
{
|
||||
const Groom *groom = ob->data;
|
||||
const struct HairDrawSettings *draw_set = groom->hair_draw_settings;
|
||||
WORKBENCH_StorageList *stl = vedata->stl;
|
||||
WORKBENCH_PassList *psl = vedata->psl;
|
||||
WORKBENCH_PrivateData *wpd = stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
struct Mesh *scalp = BKE_groom_get_scalp(draw_ctx->depsgraph, groom);
|
||||
|
||||
Image *image = NULL;
|
||||
Material *mat = give_current_material(ob, groom->material_index);
|
||||
ED_object_get_active_image(ob, groom->material_index, &image, NULL, NULL, NULL);
|
||||
int color_type = workbench_material_determine_color_type(wpd, image);
|
||||
WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type);
|
||||
|
||||
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
|
||||
? wpd->prepass_solid_hair_fibers_sh
|
||||
: wpd->prepass_texture_hair_fibers_sh;
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_fibers_create(
|
||||
draw_ctx->scene, ob, groom->hair_system, scalp, draw_set,
|
||||
psl->prepass_hair_pass,
|
||||
shader);
|
||||
DRW_shgroup_stencil_mask(shgrp, 0xFF);
|
||||
DRW_shgroup_uniform_int(shgrp, "object_id", &material->object_id, 1);
|
||||
workbench_material_shgroup_uniform(wpd, shgrp, material);
|
||||
}
|
||||
|
||||
void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
|
||||
{
|
||||
WORKBENCH_StorageList *stl = vedata->stl;
|
||||
@@ -658,6 +688,9 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
|
||||
if (ob->type == OB_MESH) {
|
||||
workbench_cache_populate_particles(vedata, ob);
|
||||
}
|
||||
if (ob->type == OB_GROOM) {
|
||||
workbench_cache_populate_groom(vedata, ob);
|
||||
}
|
||||
|
||||
ModifierData *md;
|
||||
if (((ob->base_flag & BASE_FROMDUPLI) == 0) &&
|
||||
@@ -674,7 +707,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
|
||||
}
|
||||
|
||||
WORKBENCH_MaterialData *material;
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_GROOM)) {
|
||||
const bool is_active = (ob == draw_ctx->obact);
|
||||
const bool is_sculpt_mode = is_active && (draw_ctx->object_mode & OB_MODE_SCULPT) != 0;
|
||||
bool is_drawn = false;
|
||||
|
@@ -31,10 +31,12 @@
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_modifier.h"
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
@@ -425,7 +427,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 +446,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);
|
||||
@@ -452,6 +454,55 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
|
||||
}
|
||||
}
|
||||
|
||||
static void workbench_forward_cache_populate_groom(WORKBENCH_Data *vedata, Object *ob)
|
||||
{
|
||||
const Groom *groom = ob->data;
|
||||
const struct HairDrawSettings *draw_set = groom->hair_draw_settings;
|
||||
WORKBENCH_StorageList *stl = vedata->stl;
|
||||
WORKBENCH_PassList *psl = vedata->psl;
|
||||
WORKBENCH_PrivateData *wpd = stl->g_data;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
struct Mesh *scalp = BKE_groom_get_scalp(draw_ctx->depsgraph, groom);
|
||||
|
||||
Image *image = NULL;
|
||||
Material *mat = give_current_material(ob, groom->material_index);
|
||||
ED_object_get_active_image(ob, groom->material_index, &image, NULL, NULL, NULL);
|
||||
int color_type = workbench_material_determine_color_type(wpd, image);
|
||||
WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type);
|
||||
|
||||
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
|
||||
? wpd->transparent_accum_hair_fibers_sh
|
||||
: wpd->transparent_accum_texture_hair_fibers_sh;
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_fibers_create(
|
||||
draw_ctx->scene, ob, groom->hair_system, scalp, draw_set,
|
||||
psl->transparent_accum_pass,
|
||||
shader);
|
||||
workbench_material_set_normal_world_matrix(shgrp, wpd, e_data.normal_world_matrix);
|
||||
DRW_shgroup_uniform_block(shgrp, "world_block", wpd->world_ubo);
|
||||
workbench_material_shgroup_uniform(wpd, shgrp, material);
|
||||
DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)wpd->viewvecs, 3);
|
||||
/* Hairs have lots of layer and can rapidly become the most prominent surface.
|
||||
* So lower their alpha artificially. */
|
||||
float hair_alpha = wpd->shading.xray_alpha * 0.33f;
|
||||
DRW_shgroup_uniform_float_copy(shgrp, "alpha", hair_alpha);
|
||||
if (image) {
|
||||
GPUTexture *tex = GPU_texture_from_blender(image, NULL, GL_TEXTURE_2D, false, 0.0f);
|
||||
DRW_shgroup_uniform_texture(shgrp, "image", tex);
|
||||
}
|
||||
if (STUDIOLIGHT_ORIENTATION_VIEWNORMAL_ENABLED(wpd)) {
|
||||
BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE);
|
||||
DRW_shgroup_uniform_texture(shgrp, "matcapImage", wpd->studio_light->equirectangular_radiance_gputexture);
|
||||
}
|
||||
if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
|
||||
DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
|
||||
}
|
||||
shgrp = DRW_shgroup_hair_fibers_create(
|
||||
draw_ctx->scene, ob, groom->hair_system, scalp,
|
||||
draw_set, vedata->psl->object_outline_pass,
|
||||
e_data.object_outline_hair_fibers_sh);
|
||||
DRW_shgroup_uniform_int(shgrp, "object_id", &material->object_id, 1);
|
||||
}
|
||||
|
||||
void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
|
||||
{
|
||||
WORKBENCH_StorageList *stl = vedata->stl;
|
||||
@@ -465,6 +516,9 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
|
||||
if (ob->type == OB_MESH) {
|
||||
workbench_forward_cache_populate_particles(vedata, ob);
|
||||
}
|
||||
if (ob->type == OB_GROOM) {
|
||||
workbench_forward_cache_populate_groom(vedata, ob);
|
||||
}
|
||||
|
||||
ModifierData *md;
|
||||
if (((ob->base_flag & BASE_FROMDUPLI) == 0) &&
|
||||
@@ -481,7 +535,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
|
||||
}
|
||||
|
||||
WORKBENCH_MaterialData *material;
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
|
||||
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_GROOM)) {
|
||||
const bool is_active = (ob == draw_ctx->obact);
|
||||
const bool is_sculpt_mode = is_active && (draw_ctx->object_mode & OB_MODE_SCULPT) != 0;
|
||||
bool is_drawn = false;
|
||||
|
@@ -677,6 +677,8 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob)
|
||||
return DRW_cache_text_surface_get(ob);
|
||||
case OB_MBALL:
|
||||
return DRW_cache_mball_surface_get(ob);
|
||||
case OB_GROOM:
|
||||
return DRW_cache_groom_surface_get(ob);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -704,6 +706,8 @@ GPUBatch **DRW_cache_object_surface_material_get(
|
||||
return DRW_cache_text_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
|
||||
case OB_MBALL:
|
||||
return DRW_cache_mball_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
|
||||
case OB_GROOM:
|
||||
return DRW_cache_groom_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -3194,6 +3198,54 @@ GPUBatch *DRW_cache_lattice_vert_overlay_get(Object *ob)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Groom
|
||||
* \{ */
|
||||
|
||||
Gwn_Batch *DRW_cache_groom_verts_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_GROOM);
|
||||
|
||||
struct Groom *groom = ob->data;
|
||||
return DRW_groom_batch_cache_get_all_verts(groom);
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_cache_groom_wire_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_GROOM);
|
||||
|
||||
struct Groom *groom = ob->data;
|
||||
return DRW_groom_batch_cache_get_all_edges(groom);
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_cache_groom_surface_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_GROOM);
|
||||
|
||||
struct Groom *groom = ob->data;
|
||||
return DRW_groom_batch_cache_get_all_triangles(groom);
|
||||
}
|
||||
|
||||
Gwn_Batch **DRW_cache_groom_surface_shaded_get(
|
||||
Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len)
|
||||
{
|
||||
BLI_assert(ob->type == OB_GROOM);
|
||||
|
||||
struct Groom *groom = ob->data;
|
||||
return DRW_groom_batch_cache_get_surface_shaded(groom, gpumat_array, gpumat_array_len);
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_cache_groom_vert_overlay_get(Object *ob, int mode)
|
||||
{
|
||||
BLI_assert(ob->type == OB_GROOM);
|
||||
|
||||
struct Groom *groom = ob->data;
|
||||
return DRW_groom_batch_cache_get_overlay_verts(groom, mode);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Particles
|
||||
* \{ */
|
||||
|
||||
@@ -3366,6 +3418,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)
|
||||
{
|
||||
|
@@ -31,6 +31,9 @@ struct GPUMaterial;
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
struct PTCacheEdit;
|
||||
struct Groom;
|
||||
struct HairSystem;
|
||||
struct HairExportCache;
|
||||
|
||||
void DRW_shape_cache_free(void);
|
||||
void DRW_shape_cache_reset(void);
|
||||
@@ -180,6 +183,14 @@ struct GPUBatch *DRW_cache_lattice_verts_get(struct Object *ob);
|
||||
struct GPUBatch *DRW_cache_lattice_wire_get(struct Object *ob, bool use_weight);
|
||||
struct GPUBatch *DRW_cache_lattice_vert_overlay_get(struct Object *ob);
|
||||
|
||||
/* Groom */
|
||||
struct Gwn_Batch *DRW_cache_groom_verts_get(struct Object *ob);
|
||||
struct Gwn_Batch *DRW_cache_groom_wire_get(struct Object *ob);
|
||||
struct Gwn_Batch *DRW_cache_groom_surface_get(struct Object *ob);
|
||||
struct Gwn_Batch **DRW_cache_groom_surface_shaded_get(
|
||||
struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len);
|
||||
struct Gwn_Batch *DRW_cache_groom_vert_overlay_get(struct Object *ob, int mode);
|
||||
|
||||
/* Particles */
|
||||
struct GPUBatch *DRW_cache_particles_get_hair(
|
||||
struct Object *object, struct ParticleSystem *psys, struct ModifierData *md);
|
||||
@@ -193,6 +204,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);
|
||||
|
@@ -36,6 +36,9 @@ struct ListBase;
|
||||
struct ModifierData;
|
||||
struct ParticleSystem;
|
||||
struct PTCacheEdit;
|
||||
struct Groom;
|
||||
struct HairSystem;
|
||||
struct HairExportCache;
|
||||
|
||||
struct Curve;
|
||||
struct Lattice;
|
||||
@@ -62,6 +65,12 @@ 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_groom_batch_cache_dirty(struct Groom *groom, int mode);
|
||||
void DRW_groom_batch_cache_free(struct Groom *groom);
|
||||
|
||||
void DRW_hair_batch_cache_dirty(struct HairSystem *hsys, int mode);
|
||||
void DRW_hair_batch_cache_free(struct HairSystem *hsys);
|
||||
|
||||
/* Curve */
|
||||
struct 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(
|
||||
@@ -96,6 +105,13 @@ struct GPUBatch *DRW_lattice_batch_cache_get_all_edges(struct Lattice *lt, bool
|
||||
struct GPUBatch *DRW_lattice_batch_cache_get_all_verts(struct Lattice *lt);
|
||||
struct GPUBatch *DRW_lattice_batch_cache_get_overlay_verts(struct Lattice *lt);
|
||||
|
||||
/* Groom */
|
||||
struct Gwn_Batch *DRW_groom_batch_cache_get_all_edges(struct Groom *groom);
|
||||
struct Gwn_Batch *DRW_groom_batch_cache_get_all_triangles(struct Groom *groom);
|
||||
struct Gwn_Batch **DRW_groom_batch_cache_get_surface_shaded(struct Groom *groom, struct GPUMaterial **gpumat_array, uint gpumat_array_len);
|
||||
struct Gwn_Batch *DRW_groom_batch_cache_get_all_verts(struct Groom *groom);
|
||||
struct Gwn_Batch *DRW_groom_batch_cache_get_overlay_verts(struct Groom *groom, int mode);
|
||||
|
||||
/* Mesh */
|
||||
|
||||
struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(
|
||||
@@ -146,4 +162,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__ */
|
||||
|
987
source/blender/draw/intern/draw_cache_impl_groom.c
Normal file
987
source/blender/draw/intern/draw_cache_impl_groom.c
Normal file
@@ -0,0 +1,987 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file draw_cache_impl_groom.c
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Groom API for render engines
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "draw_cache_impl.h" /* own include */
|
||||
|
||||
#define SELECT 1
|
||||
|
||||
static void groom_batch_cache_clear(Groom *groom);
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Groom Gwn_Batch Cache */
|
||||
|
||||
typedef struct GroomBatchCache {
|
||||
Gwn_VertBuf *pos;
|
||||
Gwn_IndexBuf *edges;
|
||||
Gwn_IndexBuf *faces;
|
||||
|
||||
Gwn_Batch *all_verts;
|
||||
Gwn_Batch *all_edges;
|
||||
Gwn_Batch *all_triangles;
|
||||
Gwn_Batch **shaded_triangles;
|
||||
int mat_len;
|
||||
|
||||
Gwn_Batch *overlay_verts;
|
||||
|
||||
/* settings to determine if cache is invalid */
|
||||
bool is_dirty;
|
||||
|
||||
bool is_editmode;
|
||||
} GroomBatchCache;
|
||||
|
||||
/* Gwn_Batch cache management. */
|
||||
|
||||
static bool groom_batch_cache_valid(Groom *groom)
|
||||
{
|
||||
GroomBatchCache *cache = groom->batch_cache;
|
||||
|
||||
if (cache == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_editmode != (groom->editgroom != NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_dirty == false) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (cache->is_editmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void groom_batch_cache_init(Groom *groom)
|
||||
{
|
||||
GroomBatchCache *cache = groom->batch_cache;
|
||||
|
||||
if (!cache) {
|
||||
cache = groom->batch_cache = MEM_callocN(sizeof(*cache), __func__);
|
||||
}
|
||||
else {
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
cache->is_editmode = (groom->editgroom != NULL);
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
static GroomBatchCache *groom_batch_cache_get(Groom *groom)
|
||||
{
|
||||
if (!groom_batch_cache_valid(groom)) {
|
||||
groom_batch_cache_clear(groom);
|
||||
groom_batch_cache_init(groom);
|
||||
}
|
||||
return groom->batch_cache;
|
||||
}
|
||||
|
||||
void DRW_groom_batch_cache_dirty(Groom *groom, int mode)
|
||||
{
|
||||
GroomBatchCache *cache = groom->batch_cache;
|
||||
if (cache == NULL) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_GROOM_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
case BKE_GROOM_BATCH_DIRTY_SELECT:
|
||||
/* TODO Separate Flag vbo */
|
||||
GWN_BATCH_DISCARD_SAFE(cache->overlay_verts);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void groom_batch_cache_clear(Groom *groom)
|
||||
{
|
||||
GroomBatchCache *cache = groom->batch_cache;
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
GWN_BATCH_DISCARD_SAFE(cache->all_verts);
|
||||
GWN_BATCH_DISCARD_SAFE(cache->all_edges);
|
||||
GWN_BATCH_DISCARD_SAFE(cache->all_triangles);
|
||||
GWN_BATCH_DISCARD_SAFE(cache->overlay_verts);
|
||||
/* Note: shaded_triangles[0] is already freed by cache->all_triangles */
|
||||
MEM_SAFE_FREE(cache->shaded_triangles);
|
||||
cache->mat_len = 0;
|
||||
|
||||
GWN_VERTBUF_DISCARD_SAFE(cache->pos);
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->edges);
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->faces);
|
||||
}
|
||||
|
||||
void DRW_groom_batch_cache_free(Groom *groom)
|
||||
{
|
||||
groom_batch_cache_clear(groom);
|
||||
MEM_SAFE_FREE(groom->batch_cache);
|
||||
}
|
||||
|
||||
enum {
|
||||
VFLAG_VERTEX_SELECTED = 1 << 0,
|
||||
VFLAG_VERTEX_ACTIVE = 1 << 1,
|
||||
};
|
||||
|
||||
BLI_INLINE char make_vertex_flag(bool active, bool selected)
|
||||
{
|
||||
char vflag = 0;
|
||||
if (active)
|
||||
{
|
||||
vflag |= VFLAG_VERTEX_ACTIVE;
|
||||
}
|
||||
if (selected)
|
||||
{
|
||||
vflag |= VFLAG_VERTEX_SELECTED;
|
||||
}
|
||||
return vflag;
|
||||
}
|
||||
|
||||
/* Parts of the groom object to render */
|
||||
typedef enum GroomRenderPart
|
||||
{
|
||||
GM_RENDER_REGIONS = (1 << 0), /* Draw scalp regions */
|
||||
GM_RENDER_CENTER_CURVES = (1 << 1), /* Draw center curves of bundles */
|
||||
GM_RENDER_SECTIONS = (1 << 2), /* Draw section curves */
|
||||
GM_RENDER_HULL = (1 << 3), /* Draw hull faces */
|
||||
|
||||
GM_RENDER_ALL = GM_RENDER_REGIONS | GM_RENDER_CENTER_CURVES | GM_RENDER_SECTIONS | GM_RENDER_HULL,
|
||||
} GroomRenderPart;
|
||||
|
||||
static int groom_count_verts_regions(const ListBase *regions)
|
||||
{
|
||||
// TODO
|
||||
UNUSED_VARS(regions);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int groom_count_verts_center_curves(const ListBase *regions, bool use_curve_cache)
|
||||
{
|
||||
int vert_len = 0;
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (use_curve_cache)
|
||||
{
|
||||
vert_len += bundle->curvesize;
|
||||
}
|
||||
else
|
||||
{
|
||||
vert_len += bundle->totsections;
|
||||
}
|
||||
}
|
||||
return vert_len;
|
||||
}
|
||||
|
||||
static int groom_count_verts_sections(const ListBase *regions)
|
||||
{
|
||||
int vert_len = 0;
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
vert_len += bundle->totverts;
|
||||
}
|
||||
return vert_len;
|
||||
}
|
||||
|
||||
static int groom_count_verts_hull(const ListBase *regions, bool use_curve_cache)
|
||||
{
|
||||
int vert_len = 0;
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (use_curve_cache)
|
||||
{
|
||||
vert_len += bundle->curvesize * region->numverts;
|
||||
}
|
||||
else
|
||||
{
|
||||
vert_len += bundle->totverts;
|
||||
}
|
||||
}
|
||||
return vert_len;
|
||||
}
|
||||
|
||||
static int groom_count_verts(const ListBase *regions, int parts, bool use_curve_cache)
|
||||
{
|
||||
int vert_len = 0;
|
||||
|
||||
if (parts & GM_RENDER_REGIONS)
|
||||
{
|
||||
vert_len += groom_count_verts_regions(regions);
|
||||
}
|
||||
if (parts & GM_RENDER_CENTER_CURVES)
|
||||
{
|
||||
vert_len += groom_count_verts_center_curves(regions, use_curve_cache);
|
||||
}
|
||||
if (parts & GM_RENDER_SECTIONS)
|
||||
{
|
||||
vert_len += groom_count_verts_sections(regions);
|
||||
}
|
||||
if (parts & GM_RENDER_HULL)
|
||||
{
|
||||
vert_len += groom_count_verts_hull(regions, use_curve_cache);
|
||||
}
|
||||
|
||||
return vert_len;
|
||||
}
|
||||
|
||||
static int groom_count_edges(const ListBase *regions, int parts, bool use_curve_cache)
|
||||
{
|
||||
int edge_len = 0;
|
||||
|
||||
if (parts & GM_RENDER_REGIONS)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
if (parts & GM_RENDER_CENTER_CURVES)
|
||||
{
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (use_curve_cache)
|
||||
{
|
||||
if (bundle->curvesize > 0)
|
||||
{
|
||||
edge_len += bundle->curvesize - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bundle->totsections > 0)
|
||||
{
|
||||
edge_len += bundle->totsections - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parts & GM_RENDER_SECTIONS)
|
||||
{
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
// Closed edge loop, 1 edge per vertex
|
||||
edge_len += bundle->totverts;
|
||||
}
|
||||
}
|
||||
|
||||
return edge_len;
|
||||
}
|
||||
|
||||
static int groom_count_faces(const ListBase *regions, int parts, bool use_curve_cache)
|
||||
{
|
||||
int face_len = 0;
|
||||
|
||||
if (parts & GM_RENDER_REGIONS)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
if (parts & GM_RENDER_SECTIONS)
|
||||
{
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
/* Polygon on each section, except the first */
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (region->numverts > 2)
|
||||
{
|
||||
int numpolys = bundle->totsections - 1;
|
||||
face_len += poly_to_tri_count(numpolys, region->numverts * numpolys);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parts & GM_RENDER_HULL)
|
||||
{
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
/* Closed triangle strip around each section */
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (use_curve_cache)
|
||||
{
|
||||
if (bundle->curvesize > 0)
|
||||
{
|
||||
face_len += (bundle->curvesize - 1) * region->numverts * 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bundle->totsections > 0)
|
||||
{
|
||||
face_len += (bundle->totsections - 1) * region->numverts * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return face_len;
|
||||
}
|
||||
|
||||
typedef struct GroomRenderData
|
||||
{
|
||||
const struct ListBase *regions;
|
||||
int curve_res;
|
||||
|
||||
int tri_len; /* Total mlooptri array length */
|
||||
struct MLoopTri *mlooptri; /* Triangulation data for sections */
|
||||
} GroomRenderData;
|
||||
|
||||
static GroomRenderData* groom_render_data_create(Groom *groom)
|
||||
{
|
||||
GroomRenderData *rdata = MEM_callocN(sizeof(GroomRenderData), "groom render data");
|
||||
EditGroom *edit = groom->editgroom;
|
||||
const ListBase *regions = edit ? &edit->regions : &groom->regions;
|
||||
|
||||
rdata->regions = regions;
|
||||
rdata->curve_res = groom->curve_res;
|
||||
|
||||
{
|
||||
int totpoly = 0;
|
||||
int totvert = 0;
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
/* Polygon on each section */
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (region->numverts > 2)
|
||||
{
|
||||
int numpolys = bundle->totsections;
|
||||
totpoly += numpolys;
|
||||
totvert += region->numverts * numpolys;
|
||||
|
||||
/* Polygons are unconnected, no shared vertices,
|
||||
* same vertex number for each section polygon.
|
||||
*/
|
||||
int section_tri_len = poly_to_tri_count(1, region->numverts);
|
||||
rdata->tri_len += section_tri_len * numpolys;
|
||||
}
|
||||
}
|
||||
|
||||
/* Temporary mesh buffers for calculating tesselation */
|
||||
MPoly *mpolys = MEM_mallocN(sizeof(*mpolys) * totpoly, __func__);
|
||||
MVert *mverts = MEM_mallocN(sizeof(*mverts) * totvert, __func__);
|
||||
MLoop *mloops = MEM_mallocN(sizeof(*mloops) * totvert, __func__);
|
||||
|
||||
{
|
||||
int ipoly = 0;
|
||||
int ivert = 0;
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
/* Polygon on each section, except the first */
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (region->numverts > 2)
|
||||
{
|
||||
const GroomSectionVertex *vert = bundle->verts;
|
||||
for (int i = 0; i < bundle->totsections; ++i)
|
||||
{
|
||||
mpolys[ipoly].loopstart = ivert;
|
||||
mpolys[ipoly].totloop = region->numverts;
|
||||
|
||||
for (int j = 0; j < region->numverts; ++j)
|
||||
{
|
||||
/* No need to calculate final 3D positions,
|
||||
* because the tesselation in local 2D space is just the same.
|
||||
*/
|
||||
copy_v2_v2(mverts[ivert].co, vert->co);
|
||||
mverts[ivert].co[2] = 0.0f;
|
||||
mloops[ivert].v = ivert;
|
||||
|
||||
++vert;
|
||||
++ivert;
|
||||
}
|
||||
|
||||
++ipoly;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rdata->mlooptri = MEM_mallocN(sizeof(*rdata->mlooptri) * rdata->tri_len, __func__);
|
||||
BKE_mesh_recalc_looptri(mloops, mpolys, mverts, totvert, totpoly, rdata->mlooptri);
|
||||
|
||||
MEM_freeN(mverts);
|
||||
MEM_freeN(mpolys);
|
||||
MEM_freeN(mloops);
|
||||
}
|
||||
|
||||
return rdata;
|
||||
}
|
||||
|
||||
static void groom_render_data_free(GroomRenderData *rdata)
|
||||
{
|
||||
if (rdata->mlooptri)
|
||||
{
|
||||
MEM_freeN(rdata->mlooptri);
|
||||
}
|
||||
|
||||
MEM_freeN(rdata);
|
||||
}
|
||||
|
||||
#define GM_ATTR_ID_UNUSED 0xFFFFFFFF
|
||||
|
||||
static void groom_get_verts(
|
||||
GroomRenderData *rdata,
|
||||
int parts,
|
||||
bool use_curve_cache,
|
||||
Gwn_VertBuf *vbo,
|
||||
uint id_pos,
|
||||
uint id_nor,
|
||||
uint id_flag)
|
||||
{
|
||||
int vert_len = groom_count_verts(rdata->regions, parts, use_curve_cache);
|
||||
|
||||
GWN_vertbuf_data_alloc(vbo, vert_len);
|
||||
|
||||
uint idx = 0;
|
||||
if (parts & GM_RENDER_REGIONS)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
if (parts & GM_RENDER_CENTER_CURVES)
|
||||
{
|
||||
for (GroomRegion *region = rdata->regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (use_curve_cache)
|
||||
{
|
||||
/* curvecache has numverts+1 curves, the last one is the center curve */
|
||||
GroomCurveCache *cache = bundle->curvecache + bundle->curvesize * region->numverts;
|
||||
for (int i = 0; i < bundle->curvesize; ++i, ++cache)
|
||||
{
|
||||
if (id_pos != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
GWN_vertbuf_attr_set(vbo, id_pos, idx, cache->co);
|
||||
}
|
||||
if (id_flag != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
char vflag = make_vertex_flag(false, false);
|
||||
GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag);
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool active = region->flag & GM_REGION_SELECT;
|
||||
GroomSection *section = bundle->sections;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
if (id_pos != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
GWN_vertbuf_attr_set(vbo, id_pos, idx, section->center);
|
||||
}
|
||||
if (id_flag != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
char vflag = make_vertex_flag(active, section->flag & GM_SECTION_SELECT);
|
||||
GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag);
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parts & GM_RENDER_SECTIONS)
|
||||
{
|
||||
for (GroomRegion *region = rdata->regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSection *section = bundle->sections;
|
||||
GroomSectionVertex *vertex = bundle->verts;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
const bool active = (region->flag & GM_REGION_SELECT) && (section->flag & GM_SECTION_SELECT);
|
||||
|
||||
for (int j = 0; j < region->numverts; ++j, ++vertex)
|
||||
{
|
||||
if (id_pos != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
float co[3] = {vertex->co[0], vertex->co[1], 0.0f};
|
||||
mul_m3_v3(section->mat, co);
|
||||
add_v3_v3(co, section->center);
|
||||
GWN_vertbuf_attr_set(vbo, id_pos, idx, co);
|
||||
}
|
||||
if (id_nor != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
GWN_vertbuf_attr_set(vbo, id_nor, idx, section->mat[2]);
|
||||
}
|
||||
if (id_flag != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
char vflag = make_vertex_flag(active, vertex->flag & GM_VERTEX_SELECT);
|
||||
GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag);
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parts & GM_RENDER_HULL)
|
||||
{
|
||||
for (GroomRegion *region = rdata->regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
int numshapeverts = region->numverts;
|
||||
if (use_curve_cache)
|
||||
{
|
||||
GroomCurveCache *cache = bundle->curvecache;
|
||||
for (int i = 0; i < numshapeverts; ++i)
|
||||
{
|
||||
const int curvesize = bundle->curvesize;
|
||||
for (int j = 0; j < curvesize; ++j)
|
||||
{
|
||||
const int i0 = i > 0 ? i - 1 : numshapeverts - 1;
|
||||
const int i1 = i;
|
||||
const int i2 = i < numshapeverts - 1 ? i + 1 : 0;
|
||||
const int j0 = j - 1;
|
||||
const int j1 = j;
|
||||
const int j2 = j + 1;
|
||||
const GroomCurveCache *c00 = cache + i0 * curvesize + j0;
|
||||
const GroomCurveCache *c10 = cache + i1 * curvesize + j0;
|
||||
const GroomCurveCache *c20 = cache + i2 * curvesize + j0;
|
||||
const GroomCurveCache *c01 = cache + i0 * curvesize + j1;
|
||||
const GroomCurveCache *c11 = cache + i1 * curvesize + j1;
|
||||
const GroomCurveCache *c21 = cache + i2 * curvesize + j1;
|
||||
const GroomCurveCache *c02 = cache + i0 * curvesize + j2;
|
||||
const GroomCurveCache *c12 = cache + i1 * curvesize + j2;
|
||||
const GroomCurveCache *c22 = cache + i2 * curvesize + j2;
|
||||
if (id_pos != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
GWN_vertbuf_attr_set(vbo, id_pos, idx, c11->co);
|
||||
}
|
||||
if (id_nor != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
float fnor[3];
|
||||
float vnor[3] = {0.0f, 0.0f, 0.0f};
|
||||
float div = 1.0f;
|
||||
if (j > 0)
|
||||
{
|
||||
normal_quad_v3(fnor, c00->co, c10->co, c11->co, c01->co);
|
||||
add_v3_v3(vnor, fnor);
|
||||
normal_quad_v3(fnor, c10->co, c20->co, c21->co, c11->co);
|
||||
add_v3_v3(vnor, fnor);
|
||||
div *= 0.5f;
|
||||
}
|
||||
if (j < curvesize-1)
|
||||
{
|
||||
normal_quad_v3(fnor, c01->co, c11->co, c12->co, c02->co);
|
||||
add_v3_v3(vnor, fnor);
|
||||
normal_quad_v3(fnor, c11->co, c21->co, c22->co, c12->co);
|
||||
add_v3_v3(vnor, fnor);
|
||||
div *= 0.5f;
|
||||
}
|
||||
mul_v3_fl(vnor, div);
|
||||
|
||||
GWN_vertbuf_attr_set(vbo, id_nor, idx, vnor);
|
||||
}
|
||||
if (id_flag != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
char vflag = make_vertex_flag(false, false);
|
||||
GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag);
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GroomSection *section = bundle->sections;
|
||||
GroomSectionVertex *vertex = bundle->verts;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
for (int j = 0; j < numshapeverts; ++j, ++vertex)
|
||||
{
|
||||
if (id_pos != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
float co[3] = {vertex->co[0], vertex->co[1], 0.0f};
|
||||
mul_m3_v3(section->mat, co);
|
||||
add_v3_v3(co, section->center);
|
||||
GWN_vertbuf_attr_set(vbo, id_pos, idx, co);
|
||||
}
|
||||
if (id_flag != GM_ATTR_ID_UNUSED)
|
||||
{
|
||||
char vflag = make_vertex_flag(false, false);
|
||||
GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag);
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void groom_get_edges(
|
||||
GroomRenderData *rdata,
|
||||
int parts,
|
||||
bool use_curve_cache,
|
||||
Gwn_IndexBuf **ibo)
|
||||
{
|
||||
Gwn_IndexBufBuilder elb;
|
||||
|
||||
int vert_len = groom_count_verts(rdata->regions, parts, use_curve_cache);
|
||||
int edge_len = groom_count_edges(rdata->regions, parts, use_curve_cache);
|
||||
|
||||
GWN_indexbuf_init(&elb, GWN_PRIM_LINES, edge_len, vert_len);
|
||||
|
||||
uint idx = 0;
|
||||
if (parts & GM_RENDER_REGIONS)
|
||||
{
|
||||
// TODO
|
||||
idx += groom_count_verts_regions(rdata->regions);
|
||||
}
|
||||
if (parts & GM_RENDER_CENTER_CURVES)
|
||||
{
|
||||
for (GroomRegion *region = rdata->regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (use_curve_cache)
|
||||
{
|
||||
for (int i = 0; i < bundle->curvesize - 1; ++i)
|
||||
{
|
||||
GWN_indexbuf_add_line_verts(&elb, idx + i, idx + i + 1);
|
||||
}
|
||||
idx += bundle->curvesize;
|
||||
}
|
||||
else
|
||||
{
|
||||
GroomSection *section = bundle->sections;
|
||||
for (int i = 0; i < bundle->totsections - 1; ++i, ++section)
|
||||
{
|
||||
GWN_indexbuf_add_line_verts(&elb, idx + i, idx + i + 1);
|
||||
}
|
||||
idx += bundle->totsections;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parts & GM_RENDER_SECTIONS)
|
||||
{
|
||||
for (GroomRegion *region = rdata->regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
const int numshapeverts = region->numverts;
|
||||
if (numshapeverts > 1)
|
||||
{
|
||||
/* Skip the first section */
|
||||
for (int i = 1; i < bundle->totsections; ++i)
|
||||
{
|
||||
uint idx0 = idx + i * numshapeverts;
|
||||
for (int j = 0; j < numshapeverts - 1; ++j)
|
||||
{
|
||||
GWN_indexbuf_add_line_verts(&elb, idx0 + j, idx0 + j + 1);
|
||||
}
|
||||
// close the loop
|
||||
GWN_indexbuf_add_line_verts(&elb, idx0 + (numshapeverts-1), idx0);
|
||||
}
|
||||
|
||||
idx += bundle->totverts;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parts & GM_RENDER_HULL)
|
||||
{
|
||||
idx += groom_count_verts_hull(rdata->regions, use_curve_cache);
|
||||
}
|
||||
|
||||
*ibo = GWN_indexbuf_build(&elb);
|
||||
}
|
||||
|
||||
static void groom_get_faces(
|
||||
GroomRenderData *rdata,
|
||||
int parts,
|
||||
bool use_curve_cache,
|
||||
Gwn_IndexBuf **ibo)
|
||||
{
|
||||
Gwn_IndexBufBuilder elb;
|
||||
|
||||
int vert_len = groom_count_verts(rdata->regions, parts, use_curve_cache);
|
||||
int face_len = groom_count_faces(rdata->regions, parts, use_curve_cache);
|
||||
|
||||
GWN_indexbuf_init_ex(&elb, GWN_PRIM_TRIS, face_len * 3, vert_len, true);
|
||||
|
||||
uint idx = 0;
|
||||
if (parts & GM_RENDER_REGIONS)
|
||||
{
|
||||
// TODO
|
||||
idx += groom_count_verts_regions(rdata->regions);
|
||||
}
|
||||
if (parts & GM_RENDER_CENTER_CURVES)
|
||||
{
|
||||
idx += groom_count_verts_center_curves(rdata->regions, use_curve_cache);
|
||||
}
|
||||
if (parts & GM_RENDER_SECTIONS)
|
||||
{
|
||||
const MLoopTri *mtri = rdata->mlooptri;
|
||||
for (GroomRegion *region = rdata->regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
const int numshapeverts = region->numverts;
|
||||
if (numshapeverts > 1)
|
||||
{
|
||||
int section_tri_len = poly_to_tri_count(1, region->numverts);
|
||||
/* Skip the first section */
|
||||
mtri += section_tri_len;
|
||||
for (int i = 1; i < bundle->totsections; ++i)
|
||||
{
|
||||
for (int j = 0; j < section_tri_len; ++j, ++mtri)
|
||||
{
|
||||
GWN_indexbuf_add_tri_verts(
|
||||
&elb,
|
||||
idx + mtri->tri[0],
|
||||
idx + mtri->tri[1],
|
||||
idx + mtri->tri[2]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
idx += groom_count_verts_sections(rdata->regions);
|
||||
}
|
||||
if (parts & GM_RENDER_HULL)
|
||||
{
|
||||
for (GroomRegion *region = rdata->regions->first; region; region = region->next)
|
||||
{
|
||||
/* Closed triangle strip around each section */
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
const int numshapeverts = region->numverts;
|
||||
if (numshapeverts > 1)
|
||||
{
|
||||
if (use_curve_cache)
|
||||
{
|
||||
for (int i = 0; i < numshapeverts; ++i)
|
||||
{
|
||||
const int idx0 = idx + bundle->curvesize * i;
|
||||
const int idx1 = idx + bundle->curvesize * ((i + 1) % numshapeverts);
|
||||
for (int j = 0; j < bundle->curvesize - 1; ++j)
|
||||
{
|
||||
GWN_indexbuf_add_tri_verts(&elb, idx0 + j, idx1 + j, idx1 + j + 1);
|
||||
GWN_indexbuf_add_tri_verts(&elb, idx0 + j, idx1 + j + 1, idx0 + j + 1);
|
||||
}
|
||||
}
|
||||
idx += bundle->curvesize * numshapeverts;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numshapeverts; ++i)
|
||||
{
|
||||
const int idx0 = idx + bundle->totsections * i;
|
||||
const int idx1 = idx + bundle->totsections * ((i + 1) % numshapeverts);
|
||||
for (int j = 0; j < bundle->totsections - 1; ++j)
|
||||
{
|
||||
GWN_indexbuf_add_tri_verts(&elb, idx0 + j, idx1 + j, idx1 + j + 1);
|
||||
GWN_indexbuf_add_tri_verts(&elb, idx0 + j, idx1 + j + 1, idx0 + j + 1);
|
||||
}
|
||||
}
|
||||
idx += bundle->totverts;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ibo = GWN_indexbuf_build(&elb);
|
||||
}
|
||||
|
||||
/* Gwn_Batch cache usage. */
|
||||
static Gwn_VertBuf *groom_batch_cache_get_pos(GroomRenderData *rdata, GroomBatchCache *cache, int parts)
|
||||
{
|
||||
if (cache->pos == NULL) {
|
||||
static Gwn_VertFormat format = { 0 };
|
||||
static struct { uint pos, nor; } attr_id;
|
||||
|
||||
GWN_vertformat_clear(&format);
|
||||
|
||||
/* initialize vertex format */
|
||||
attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
|
||||
attr_id.nor = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
|
||||
|
||||
cache->pos = GWN_vertbuf_create_with_format(&format);
|
||||
|
||||
groom_get_verts(rdata, parts, true, cache->pos, attr_id.pos, attr_id.nor, GM_ATTR_ID_UNUSED);
|
||||
}
|
||||
|
||||
return cache->pos;
|
||||
}
|
||||
|
||||
static Gwn_IndexBuf *groom_batch_cache_get_edges(GroomRenderData *rdata, GroomBatchCache *cache, int parts)
|
||||
{
|
||||
if (cache->edges == NULL) {
|
||||
groom_get_edges(rdata, parts, true, &cache->edges);
|
||||
}
|
||||
|
||||
return cache->edges;
|
||||
}
|
||||
|
||||
static Gwn_IndexBuf *groom_batch_cache_get_faces(GroomRenderData *rdata, GroomBatchCache *cache, int parts)
|
||||
{
|
||||
if (cache->faces == NULL) {
|
||||
groom_get_faces(rdata, parts, true, &cache->faces);
|
||||
}
|
||||
|
||||
return cache->faces;
|
||||
}
|
||||
|
||||
static void groom_batch_cache_create_overlay_batches(GroomRenderData *rdata, GroomBatchCache *cache, int parts)
|
||||
{
|
||||
if (cache->overlay_verts == NULL) {
|
||||
static Gwn_VertFormat format = { 0 };
|
||||
static struct { uint pos, nor, data; } attr_id;
|
||||
if (format.attr_len == 0) {
|
||||
/* initialize vertex format */
|
||||
attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
|
||||
attr_id.nor = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
|
||||
attr_id.data = GWN_vertformat_attr_add(&format, "data", GWN_COMP_U8, 1, GWN_FETCH_INT);
|
||||
}
|
||||
|
||||
Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
|
||||
|
||||
groom_get_verts(rdata, parts, false, vbo, attr_id.pos, attr_id.nor, attr_id.data);
|
||||
|
||||
cache->overlay_verts = GWN_batch_create_ex(GWN_PRIM_POINTS, vbo, NULL, GWN_BATCH_OWNS_VBO);
|
||||
}
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_groom_batch_cache_get_all_edges(Groom *groom)
|
||||
{
|
||||
GroomBatchCache *cache = groom_batch_cache_get(groom);
|
||||
|
||||
if (cache->all_edges == NULL) {
|
||||
GroomRenderData *rdata = groom_render_data_create(groom);
|
||||
|
||||
cache->all_edges = GWN_batch_create(
|
||||
GWN_PRIM_LINES,
|
||||
groom_batch_cache_get_pos(rdata, cache, GM_RENDER_ALL),
|
||||
groom_batch_cache_get_edges(rdata, cache, GM_RENDER_ALL));
|
||||
|
||||
groom_render_data_free(rdata);
|
||||
}
|
||||
|
||||
return cache->all_edges;
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_groom_batch_cache_get_all_triangles(Groom *groom)
|
||||
{
|
||||
GroomBatchCache *cache = groom_batch_cache_get(groom);
|
||||
|
||||
if (cache->all_triangles == NULL) {
|
||||
GroomRenderData *rdata = groom_render_data_create(groom);
|
||||
|
||||
cache->all_triangles = GWN_batch_create(
|
||||
GWN_PRIM_TRIS,
|
||||
groom_batch_cache_get_pos(rdata, cache, GM_RENDER_ALL),
|
||||
groom_batch_cache_get_faces(rdata, cache, GM_RENDER_ALL));
|
||||
|
||||
groom_render_data_free(rdata);
|
||||
}
|
||||
|
||||
return cache->all_triangles;
|
||||
}
|
||||
|
||||
Gwn_Batch **DRW_groom_batch_cache_get_surface_shaded(Groom *groom, struct GPUMaterial **UNUSED(gpumat_array), uint gpumat_array_len)
|
||||
{
|
||||
GroomBatchCache *cache = groom_batch_cache_get(groom);
|
||||
|
||||
if (cache->shaded_triangles == NULL) {
|
||||
cache->mat_len = gpumat_array_len;
|
||||
cache->shaded_triangles = MEM_callocN(sizeof(*cache->shaded_triangles) * cache->mat_len, __func__);
|
||||
cache->shaded_triangles[0] = DRW_groom_batch_cache_get_all_triangles(groom);
|
||||
for (int i = 1; i < cache->mat_len; ++i) {
|
||||
cache->shaded_triangles[i] = NULL;
|
||||
}
|
||||
}
|
||||
return cache->shaded_triangles;
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_groom_batch_cache_get_all_verts(Groom *groom)
|
||||
{
|
||||
GroomBatchCache *cache = groom_batch_cache_get(groom);
|
||||
|
||||
if (cache->all_verts == NULL) {
|
||||
GroomRenderData *rdata = groom_render_data_create(groom);
|
||||
|
||||
cache->all_verts = GWN_batch_create(
|
||||
GWN_PRIM_POINTS,
|
||||
groom_batch_cache_get_pos(rdata, cache, GM_RENDER_ALL),
|
||||
NULL);
|
||||
|
||||
groom_render_data_free(rdata);
|
||||
}
|
||||
|
||||
return cache->all_verts;
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_groom_batch_cache_get_overlay_verts(Groom *groom, int mode)
|
||||
{
|
||||
GroomBatchCache *cache = groom_batch_cache_get(groom);
|
||||
|
||||
if (cache->overlay_verts == NULL) {
|
||||
GroomRenderData *rdata = groom_render_data_create(groom);
|
||||
|
||||
GroomRenderPart parts = 0;
|
||||
switch ((GroomEditMode)mode)
|
||||
{
|
||||
case GM_EDIT_MODE_REGIONS: parts |= GM_RENDER_REGIONS; break;
|
||||
case GM_EDIT_MODE_CURVES: parts |= GM_RENDER_CENTER_CURVES; break;
|
||||
case GM_EDIT_MODE_SECTIONS: parts |= GM_RENDER_SECTIONS; break;
|
||||
}
|
||||
|
||||
groom_batch_cache_create_overlay_batches(rdata, cache, parts);
|
||||
|
||||
groom_render_data_free(rdata);
|
||||
}
|
||||
|
||||
return cache->overlay_verts;
|
||||
}
|
556
source/blender/draw/intern/draw_cache_impl_hair.c
Normal file
556
source/blender/draw/intern/draw_cache_impl_hair.c
Normal 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;
|
||||
}
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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__ */
|
||||
|
@@ -1209,6 +1209,9 @@ static void drw_engines_enable_from_mode(int mode)
|
||||
case CTX_MODE_EDIT_LATTICE:
|
||||
use_drw_engine(&draw_engine_edit_lattice_type);
|
||||
break;
|
||||
case CTX_MODE_EDIT_GROOM:
|
||||
use_drw_engine(&draw_engine_edit_groom_type);
|
||||
break;
|
||||
case CTX_MODE_POSE:
|
||||
use_drw_engine(&draw_engine_pose_type);
|
||||
break;
|
||||
@@ -2429,6 +2432,7 @@ void DRW_engines_register(void)
|
||||
DRW_engine_register(&draw_engine_object_type);
|
||||
DRW_engine_register(&draw_engine_edit_armature_type);
|
||||
DRW_engine_register(&draw_engine_edit_curve_type);
|
||||
DRW_engine_register(&draw_engine_edit_groom_type);
|
||||
DRW_engine_register(&draw_engine_edit_lattice_type);
|
||||
DRW_engine_register(&draw_engine_edit_mesh_type);
|
||||
DRW_engine_register(&draw_engine_edit_metaball_type);
|
||||
@@ -2464,6 +2468,12 @@ 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: groom.c */
|
||||
extern void *BKE_groom_batch_cache_dirty_cb;
|
||||
extern void *BKE_groom_batch_cache_free_cb;
|
||||
/* BKE: hair.c */
|
||||
extern void *BKE_hair_batch_cache_dirty_cb;
|
||||
extern void *BKE_hair_batch_cache_free_cb;
|
||||
|
||||
BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty;
|
||||
BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
|
||||
@@ -2480,8 +2490,14 @@ void DRW_engines_register(void)
|
||||
BKE_particle_batch_cache_dirty_cb = DRW_particle_batch_cache_dirty;
|
||||
BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free;
|
||||
|
||||
BKE_groom_batch_cache_dirty_cb = DRW_groom_batch_cache_dirty;
|
||||
BKE_groom_batch_cache_free_cb = DRW_groom_batch_cache_free;
|
||||
|
||||
BKE_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -29,6 +29,7 @@
|
||||
extern DrawEngineType draw_engine_object_type;
|
||||
extern DrawEngineType draw_engine_edit_armature_type;
|
||||
extern DrawEngineType draw_engine_edit_curve_type;
|
||||
extern DrawEngineType draw_engine_edit_groom_type;
|
||||
extern DrawEngineType draw_engine_edit_lattice_type;
|
||||
extern DrawEngineType draw_engine_edit_mesh_type;
|
||||
extern DrawEngineType draw_engine_edit_metaball_type;
|
||||
|
304
source/blender/draw/modes/edit_groom_mode.c
Normal file
304
source/blender/draw/modes/edit_groom_mode.c
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/draw/modes/edit_groom_mode.c
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
/* If builtin shaders are needed */
|
||||
#include "GPU_shader.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
|
||||
#include "draw_mode_engines.h"
|
||||
|
||||
/* If needed, contains all global/Theme colors
|
||||
* Add needed theme colors / values to DRW_globals_update() and update UBO
|
||||
* Not needed for constant color. */
|
||||
extern struct GPUUniformBuffer *globals_ubo; /* draw_common.c */
|
||||
extern struct GlobalsUboStorage ts; /* draw_common.c */
|
||||
|
||||
extern char datatoc_common_globals_lib_glsl[];
|
||||
extern char datatoc_edit_groom_overlay_loosevert_vert_glsl[];
|
||||
extern char datatoc_edit_groom_overlay_frag_glsl[];
|
||||
|
||||
extern char datatoc_gpu_shader_3D_vert_glsl[];
|
||||
extern char datatoc_gpu_shader_uniform_color_frag_glsl[];
|
||||
extern char datatoc_gpu_shader_point_uniform_color_frag_glsl[];
|
||||
|
||||
/* *********** LISTS *********** */
|
||||
/* All lists are per viewport specific datas.
|
||||
* They are all free when viewport changes engines
|
||||
* or is free itself. Use EDIT_GROOM_engine_init() to
|
||||
* initialize most of them and EDIT_GROOM_cache_init()
|
||||
* for EDIT_GROOM_PassList */
|
||||
|
||||
typedef struct EDIT_GROOM_PassList {
|
||||
/* Declare all passes here and init them in
|
||||
* EDIT_GROOM_cache_init().
|
||||
* Only contains (DRWPass *) */
|
||||
struct DRWPass *wire_pass;
|
||||
struct DRWPass *vert_pass;
|
||||
} EDIT_GROOM_PassList;
|
||||
|
||||
typedef struct EDIT_GROOM_FramebufferList {
|
||||
/* Contains all framebuffer objects needed by this engine.
|
||||
* Only contains (GPUFrameBuffer *) */
|
||||
struct GPUFrameBuffer *fb;
|
||||
} EDIT_GROOM_FramebufferList;
|
||||
|
||||
typedef struct EDIT_GROOM_TextureList {
|
||||
/* Contains all framebuffer textures / utility textures
|
||||
* needed by this engine. Only viewport specific textures
|
||||
* (not per object). Only contains (GPUTexture *) */
|
||||
struct GPUTexture *texture;
|
||||
} EDIT_GROOM_TextureList;
|
||||
|
||||
typedef struct EDIT_GROOM_StorageList {
|
||||
/* Contains any other memory block that the engine needs.
|
||||
* Only directly MEM_(m/c)allocN'ed blocks because they are
|
||||
* free with MEM_freeN() when viewport is freed.
|
||||
* (not per object) */
|
||||
struct CustomStruct *block;
|
||||
struct EDIT_GROOM_PrivateData *g_data;
|
||||
} EDIT_GROOM_StorageList;
|
||||
|
||||
typedef struct EDIT_GROOM_Data {
|
||||
/* Struct returned by DRW_viewport_engine_data_ensure.
|
||||
* If you don't use one of these, just make it a (void *) */
|
||||
// void *fbl;
|
||||
void *engine_type; /* Required */
|
||||
EDIT_GROOM_FramebufferList *fbl;
|
||||
EDIT_GROOM_TextureList *txl;
|
||||
EDIT_GROOM_PassList *psl;
|
||||
EDIT_GROOM_StorageList *stl;
|
||||
} EDIT_GROOM_Data;
|
||||
|
||||
/* *********** STATIC *********** */
|
||||
|
||||
static struct {
|
||||
/* Custom shaders :
|
||||
* Add sources to source/blender/draw/modes/shaders
|
||||
* init in EDIT_GROOM_engine_init();
|
||||
* free in EDIT_GROOM_engine_free(); */
|
||||
GPUShader *wire_sh;
|
||||
|
||||
GPUShader *overlay_vert_sh;
|
||||
|
||||
} e_data = {NULL}; /* Engine data */
|
||||
|
||||
typedef struct EDIT_GROOM_PrivateData {
|
||||
/* This keeps the references of the shading groups for
|
||||
* easy access in EDIT_GROOM_cache_populate() */
|
||||
DRWShadingGroup *wire_shgrp;
|
||||
DRWShadingGroup *vert_shgrp;
|
||||
} EDIT_GROOM_PrivateData; /* Transient data */
|
||||
|
||||
/* *********** FUNCTIONS *********** */
|
||||
|
||||
/* Init Textures, Framebuffers, Storage and Shaders.
|
||||
* It is called for every frames.
|
||||
* (Optional) */
|
||||
static void EDIT_GROOM_engine_init(void *vedata)
|
||||
{
|
||||
EDIT_GROOM_TextureList *txl = ((EDIT_GROOM_Data *)vedata)->txl;
|
||||
EDIT_GROOM_FramebufferList *fbl = ((EDIT_GROOM_Data *)vedata)->fbl;
|
||||
EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl;
|
||||
|
||||
UNUSED_VARS(txl, fbl, stl);
|
||||
|
||||
/* Init Framebuffers like this: order is attachment order (for color texs) */
|
||||
/*
|
||||
* DRWFboTexture tex[2] = {{&txl->depth, DRW_TEX_DEPTH_24, 0},
|
||||
* {&txl->color, DRW_TEX_RGBA_8, DRW_TEX_FILTER}};
|
||||
*/
|
||||
|
||||
/* DRW_framebuffer_init takes care of checking if
|
||||
* the framebuffer is valid and has the right size*/
|
||||
/*
|
||||
* float *viewport_size = DRW_viewport_size_get();
|
||||
* DRW_framebuffer_init(&fbl->occlude_wire_fb,
|
||||
* (int)viewport_size[0], (int)viewport_size[1],
|
||||
* tex, 2);
|
||||
*/
|
||||
|
||||
if (!e_data.wire_sh) {
|
||||
e_data.wire_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR);
|
||||
}
|
||||
|
||||
if (!e_data.overlay_vert_sh) {
|
||||
e_data.overlay_vert_sh = DRW_shader_create_with_lib(
|
||||
datatoc_edit_groom_overlay_loosevert_vert_glsl, NULL,
|
||||
datatoc_edit_groom_overlay_frag_glsl,
|
||||
datatoc_common_globals_lib_glsl, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Here init all passes and shading groups
|
||||
* Assume that all Passes are NULL */
|
||||
static void EDIT_GROOM_cache_init(void *vedata)
|
||||
{
|
||||
EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl;
|
||||
EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl;
|
||||
|
||||
if (!stl->g_data) {
|
||||
/* Alloc transient pointers */
|
||||
stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
|
||||
}
|
||||
|
||||
{
|
||||
psl->wire_pass = DRW_pass_create(
|
||||
"Groom Wire",
|
||||
DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_WIRE);
|
||||
stl->g_data->wire_shgrp = DRW_shgroup_create(e_data.wire_sh, psl->wire_pass);
|
||||
|
||||
psl->vert_pass = DRW_pass_create(
|
||||
"Groom Verts",
|
||||
DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_POINT);
|
||||
stl->g_data->vert_shgrp = DRW_shgroup_create(e_data.overlay_vert_sh, psl->vert_pass);
|
||||
|
||||
DRW_shgroup_uniform_block(stl->g_data->vert_shgrp, "globalsBlock", globals_ubo);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add geometry to shadingGroups. Execute for each objects */
|
||||
static void EDIT_GROOM_cache_populate(void *vedata, Object *ob)
|
||||
{
|
||||
EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl;
|
||||
EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
Object *obedit = draw_ctx->object_edit;
|
||||
GroomEditSettings *editsettings = &scene->toolsettings->groom_edit_settings;
|
||||
|
||||
UNUSED_VARS(psl);
|
||||
|
||||
if (ob->type == OB_GROOM) {
|
||||
if (ob == obedit) {
|
||||
/* Get geometry cache */
|
||||
struct Gwn_Batch *geom;
|
||||
|
||||
geom = DRW_cache_groom_wire_get(ob);
|
||||
DRW_shgroup_call_add(stl->g_data->wire_shgrp, geom, ob->obmat);
|
||||
|
||||
geom = DRW_cache_groom_vert_overlay_get(ob, editsettings->mode);
|
||||
DRW_shgroup_call_add(stl->g_data->vert_shgrp, geom, ob->obmat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional: Post-cache_populate callback */
|
||||
static void EDIT_GROOM_cache_finish(void *vedata)
|
||||
{
|
||||
EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl;
|
||||
EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl;
|
||||
|
||||
/* Do something here! dependant on the objects gathered */
|
||||
UNUSED_VARS(psl, stl);
|
||||
}
|
||||
|
||||
/* Draw time ! Control rendering pipeline from here */
|
||||
static void EDIT_GROOM_draw_scene(void *vedata)
|
||||
{
|
||||
EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl;
|
||||
EDIT_GROOM_FramebufferList *fbl = ((EDIT_GROOM_Data *)vedata)->fbl;
|
||||
|
||||
/* Default framebuffer and texture */
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
|
||||
UNUSED_VARS(fbl, dtxl);
|
||||
|
||||
MULTISAMPLE_SYNC_ENABLE(dfbl, dtxl)
|
||||
|
||||
/* Show / hide entire passes, swap framebuffers ... whatever you fancy */
|
||||
/*
|
||||
* DRW_framebuffer_texture_detach(dtxl->depth);
|
||||
* DRW_framebuffer_bind(fbl->custom_fb);
|
||||
* DRW_draw_pass(psl->pass);
|
||||
* DRW_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0);
|
||||
* DRW_framebuffer_bind(dfbl->default_fb);
|
||||
*/
|
||||
|
||||
/* ... or just render passes on default framebuffer. */
|
||||
DRW_draw_pass(psl->wire_pass);
|
||||
DRW_draw_pass(psl->vert_pass);
|
||||
|
||||
MULTISAMPLE_SYNC_DISABLE(dfbl, dtxl)
|
||||
|
||||
/* If you changed framebuffer, double check you rebind
|
||||
* the default one with its textures attached before finishing */
|
||||
}
|
||||
|
||||
/* Cleanup when destroying the engine.
|
||||
* This is not per viewport ! only when quitting blender.
|
||||
* Mostly used for freeing shaders */
|
||||
static void EDIT_GROOM_engine_free(void)
|
||||
{
|
||||
// Currently built-in, dont free
|
||||
DRW_SHADER_FREE_SAFE(e_data.overlay_vert_sh);
|
||||
}
|
||||
|
||||
/* Create collection settings here.
|
||||
*
|
||||
* Be sure to add this function there :
|
||||
* source/blender/draw/DRW_engine.h
|
||||
* source/blender/blenkernel/intern/layer.c
|
||||
* source/blenderplayer/bad_level_call_stubs/stubs.c
|
||||
*
|
||||
* And relevant collection settings to :
|
||||
* source/blender/makesrna/intern/rna_scene.c
|
||||
* source/blender/blenkernel/intern/layer.c
|
||||
*/
|
||||
#if 0
|
||||
void EDIT_GROOM_collection_settings_create(CollectionEngineSettings *ces)
|
||||
{
|
||||
BLI_assert(ces);
|
||||
// BKE_collection_engine_property_add_int(ces, "my_bool_prop", false);
|
||||
// BKE_collection_engine_property_add_int(ces, "my_int_prop", 0);
|
||||
// BKE_collection_engine_property_add_float(ces, "my_float_prop", 0.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const DrawEngineDataSize EDIT_GROOM_data_size = DRW_VIEWPORT_DATA_SIZE(EDIT_GROOM_Data);
|
||||
|
||||
DrawEngineType draw_engine_edit_groom_type = {
|
||||
NULL, NULL,
|
||||
N_("EditGroomMode"),
|
||||
&EDIT_GROOM_data_size,
|
||||
&EDIT_GROOM_engine_init,
|
||||
&EDIT_GROOM_engine_free,
|
||||
&EDIT_GROOM_cache_init,
|
||||
&EDIT_GROOM_cache_populate,
|
||||
&EDIT_GROOM_cache_finish,
|
||||
NULL, /* draw_background but not needed by mode engines */
|
||||
&EDIT_GROOM_draw_scene,
|
||||
NULL,
|
||||
};
|
@@ -30,6 +30,7 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
@@ -49,6 +50,7 @@
|
||||
#include "BKE_camera.h"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_mball.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_modifier.h"
|
||||
@@ -114,6 +116,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 +250,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 +1356,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(
|
||||
@@ -2442,6 +2462,25 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OB_GROOM:
|
||||
{
|
||||
if (ob != draw_ctx->object_edit) {
|
||||
Groom *groom = ob->data;
|
||||
{
|
||||
struct Gwn_Batch *geom = DRW_cache_groom_wire_get(ob);
|
||||
if (theme_id == TH_UNDEFINED) {
|
||||
theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL);
|
||||
}
|
||||
DRWShadingGroup *shgroup = shgroup_theme_id_to_wire_or(stl, theme_id, stl->g_data->wire);
|
||||
DRW_shgroup_call_add(shgroup, geom, ob->obmat);
|
||||
}
|
||||
|
||||
Mesh *scalp = BKE_groom_get_scalp(draw_ctx->depsgraph, groom);
|
||||
DRW_shgroup_hair(ob, groom->hair_system, groom->hair_draw_settings, scalp,
|
||||
stl->g_data->hair_verts, stl->g_data->hair_edges);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OB_LAMP:
|
||||
if (hide_object_extra) {
|
||||
break;
|
||||
@@ -2623,6 +2662,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()) {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
|
||||
flat in int vertFlag;
|
||||
|
||||
#define VERTEX_SELECTED (1 << 0)
|
||||
#define VERTEX_ACTIVE (1 << 1)
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
/* TODO: vertex size */
|
||||
|
||||
if ((vertFlag & VERTEX_SELECTED) != 0) {
|
||||
FragColor = colorVertexSelect;
|
||||
}
|
||||
else if ((vertFlag & VERTEX_ACTIVE) != 0) {
|
||||
FragColor = colorEditMeshActive;
|
||||
}
|
||||
else {
|
||||
FragColor = colorVertex;
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
|
||||
/* Draw Groom Vertices */
|
||||
|
||||
uniform mat4 ModelViewProjectionMatrix;
|
||||
uniform vec2 viewportSize;
|
||||
|
||||
in vec3 pos;
|
||||
in int data;
|
||||
|
||||
/* these are the same for all vertices
|
||||
* and does not need interpolation */
|
||||
flat out int vertFlag;
|
||||
flat out int clipCase;
|
||||
|
||||
/* See fragment shader */
|
||||
noperspective out vec4 eData1;
|
||||
flat out vec4 eData2;
|
||||
|
||||
/* project to screen space */
|
||||
vec2 proj(vec4 pos)
|
||||
{
|
||||
return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
clipCase = 0;
|
||||
|
||||
vec4 pPos = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
|
||||
/* only vertex position 0 is used */
|
||||
eData1 = eData2 = vec4(1e10);
|
||||
eData2.zw = proj(pPos);
|
||||
|
||||
vertFlag = data;
|
||||
|
||||
gl_PointSize = sizeVertex;
|
||||
gl_Position = pPos;
|
||||
}
|
@@ -27,6 +27,7 @@ if(WITH_BLENDER)
|
||||
add_subdirectory(armature)
|
||||
add_subdirectory(curve)
|
||||
add_subdirectory(gpencil)
|
||||
add_subdirectory(groom)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(lattice)
|
||||
|
50
source/blender/editors/groom/CMakeLists.txt
Normal file
50
source/blender/editors/groom/CMakeLists.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# Contributor(s): Lukas Toenne.
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
set(INC
|
||||
../include
|
||||
../../blenkernel
|
||||
../../blenlib
|
||||
../../blentranslation
|
||||
../../depsgraph
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
../../../../intern/guardedalloc
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
groom_hair.c
|
||||
groom_ops.c
|
||||
editgroom.c
|
||||
editgroom_region.c
|
||||
editgroom_select.c
|
||||
|
||||
groom_intern.h
|
||||
)
|
||||
|
||||
if(WITH_INTERNATIONAL)
|
||||
add_definitions(-DWITH_INTERNATIONAL)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_editor_groom "${SRC}" "${INC}" "${INC_SYS}")
|
171
source/blender/editors/groom/editgroom.c
Normal file
171
source/blender/editors/groom/editgroom.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/groom/editgroom.c
|
||||
* \ingroup edgroom
|
||||
*/
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array_utils.h"
|
||||
#include "BLI_blenlib.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "DEG_depsgraph_build.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_groom.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_types.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "groom_intern.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
/********************** Load/Make/Free ********************/
|
||||
|
||||
static void groom_regions_free(ListBase *regions)
|
||||
{
|
||||
for (GroomRegion *region = regions->first; region; region = region->next)
|
||||
{
|
||||
if (region->scalp_samples)
|
||||
{
|
||||
MEM_freeN(region->scalp_samples);
|
||||
}
|
||||
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
BKE_groom_bundle_curve_cache_clear(bundle);
|
||||
|
||||
if (bundle->verts)
|
||||
{
|
||||
MEM_freeN(bundle->verts);
|
||||
}
|
||||
if (bundle->sections)
|
||||
{
|
||||
MEM_freeN(bundle->sections);
|
||||
}
|
||||
if (bundle->guides)
|
||||
{
|
||||
MEM_freeN(bundle->guides);
|
||||
}
|
||||
if (bundle->guide_shape_weights)
|
||||
{
|
||||
MEM_freeN(bundle->guide_shape_weights);
|
||||
}
|
||||
}
|
||||
BLI_freelistN(regions);
|
||||
}
|
||||
|
||||
static void groom_regions_copy(ListBase *regions_dst, ListBase *regions_src)
|
||||
{
|
||||
BLI_duplicatelist(regions_dst, regions_src);
|
||||
for (GroomRegion *region = regions_dst->first; region; region = region->next)
|
||||
{
|
||||
if (region->scalp_samples)
|
||||
{
|
||||
region->scalp_samples = MEM_dupallocN(region->scalp_samples);
|
||||
}
|
||||
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
if (bundle->curvecache)
|
||||
{
|
||||
bundle->curvecache = MEM_dupallocN(bundle->curvecache);
|
||||
}
|
||||
if (bundle->sections)
|
||||
{
|
||||
bundle->sections = MEM_dupallocN(bundle->sections);
|
||||
}
|
||||
if (bundle->verts)
|
||||
{
|
||||
bundle->verts = MEM_dupallocN(bundle->verts);
|
||||
}
|
||||
if (bundle->guides)
|
||||
{
|
||||
bundle->guides = MEM_dupallocN(bundle->guides);
|
||||
}
|
||||
if (bundle->guide_shape_weights)
|
||||
{
|
||||
bundle->guide_shape_weights = MEM_dupallocN(bundle->guide_shape_weights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ED_groom_editgroom_make(Object *obedit)
|
||||
{
|
||||
Groom *groom = obedit->data;
|
||||
|
||||
ED_groom_editgroom_free(obedit);
|
||||
|
||||
groom->editgroom = MEM_callocN(sizeof(EditGroom), "editgroom");
|
||||
groom_regions_copy(&groom->editgroom->regions, &groom->regions);
|
||||
}
|
||||
|
||||
void ED_groom_editgroom_load(Object *obedit)
|
||||
{
|
||||
Groom *groom = obedit->data;
|
||||
|
||||
groom_regions_free(&groom->regions);
|
||||
groom_regions_copy(&groom->regions, &groom->editgroom->regions);
|
||||
}
|
||||
|
||||
void ED_groom_editgroom_free(Object *ob)
|
||||
{
|
||||
Groom *groom = ob->data;
|
||||
|
||||
if (groom->editgroom) {
|
||||
groom_regions_free(&groom->editgroom->regions);
|
||||
|
||||
MEM_freeN(groom->editgroom);
|
||||
groom->editgroom = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int ED_groom_object_poll(bContext *C)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
return ob && ob->type == OB_GROOM;
|
||||
}
|
399
source/blender/editors/groom/editgroom_region.c
Normal file
399
source/blender/editors/groom/editgroom_region.c
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/groom/editgroom_region.c
|
||||
* \ingroup edgroom
|
||||
*/
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_object_facemap.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_groom.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_transform.h"
|
||||
#include "ED_util.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "UI_resources.h"
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "groom_intern.h"
|
||||
|
||||
/* GROOM_OT_region_add */
|
||||
|
||||
static void region_add_set_bundle_curve(GroomRegion *region, const float loc[3], const float rot[3][3], float length)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
|
||||
bundle->totsections = 2;
|
||||
bundle->sections = MEM_callocN(sizeof(GroomSection) * bundle->totsections, "groom bundle sections");
|
||||
|
||||
madd_v3_v3v3fl(bundle->sections[0].center, loc, rot[2], 0.0f);
|
||||
madd_v3_v3v3fl(bundle->sections[1].center, loc, rot[2], length);
|
||||
}
|
||||
|
||||
static int region_add_poll(bContext *C)
|
||||
{
|
||||
if (!ED_groom_object_poll(C))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We want a scalp object to make this useful */
|
||||
Object *ob = ED_object_context(C);
|
||||
Groom *groom = ob->data;
|
||||
return groom->scalp_object != NULL;
|
||||
}
|
||||
|
||||
|
||||
static const EnumPropertyItem *region_add_facemap_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
{
|
||||
if (C == NULL) {
|
||||
return DummyRNA_NULL_items;
|
||||
}
|
||||
Object *ob = ED_object_context(C);
|
||||
if (!ob || ob->type != OB_GROOM)
|
||||
{
|
||||
return DummyRNA_NULL_items;
|
||||
}
|
||||
Groom *groom = ob->data;
|
||||
Object *scalp_ob = groom->scalp_object;
|
||||
if (!scalp_ob)
|
||||
{
|
||||
return DummyRNA_NULL_items;
|
||||
}
|
||||
|
||||
EnumPropertyItem *item = NULL, item_tmp = {0};
|
||||
int totitem = 0;
|
||||
|
||||
int i = 0;
|
||||
for (bFaceMap *fmap = scalp_ob->fmaps.first; fmap; fmap = fmap->next, ++i)
|
||||
{
|
||||
item_tmp.identifier = fmap->name;
|
||||
item_tmp.name = fmap->name;
|
||||
item_tmp.description = "";
|
||||
item_tmp.value = i;
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static int region_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const Depsgraph *depsgraph = CTX_data_depsgraph(C);
|
||||
Object *ob = ED_object_context(C);
|
||||
Groom *groom = ob->data;
|
||||
if (!groom->scalp_object)
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
char scalp_facemap_name[MAX_VGROUP_NAME];
|
||||
int fmap_index = RNA_enum_get(op->ptr, "scalp_facemap");
|
||||
bFaceMap *fmap = BLI_findlink(&groom->scalp_object->fmaps, fmap_index);
|
||||
if (!fmap)
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
BLI_strncpy(scalp_facemap_name, fmap->name, sizeof(scalp_facemap_name));
|
||||
|
||||
GroomRegion *region = BKE_groom_region_add(groom);
|
||||
|
||||
float scalp_loc[3];
|
||||
float scalp_rot[3][3];
|
||||
zero_v3(scalp_loc);
|
||||
unit_m3(scalp_rot);
|
||||
|
||||
if (BKE_groom_set_region_scalp_facemap(groom, region, scalp_facemap_name))
|
||||
{
|
||||
const struct Mesh *scalp = BKE_groom_get_scalp(depsgraph, groom);
|
||||
BLI_assert(scalp != NULL);
|
||||
|
||||
if (BKE_groom_region_bind(depsgraph, groom, region, true))
|
||||
{
|
||||
BKE_groom_calc_region_transform_on_scalp(region, scalp, scalp_loc, scalp_rot);
|
||||
}
|
||||
}
|
||||
|
||||
region_add_set_bundle_curve(region, scalp_loc, scalp_rot, 1.0f);
|
||||
BKE_groom_region_reset_shape(depsgraph, groom, region);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GROOM_OT_region_add(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Add Region";
|
||||
ot->description = "Add a new region to the groom object";
|
||||
ot->idname = "GROOM_OT_region_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = region_add_exec;
|
||||
ot->poll = region_add_poll;
|
||||
ot->invoke = WM_enum_search_invoke;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
prop = RNA_def_enum(ot->srna, "scalp_facemap", DummyRNA_NULL_items, 0, "Scalp Facemap", "Facemap to which to bind the new region");
|
||||
RNA_def_enum_funcs(prop, region_add_facemap_itemf);
|
||||
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
|
||||
ot->prop = prop;
|
||||
}
|
||||
|
||||
/* GROOM_OT_region_remove */
|
||||
|
||||
static int region_remove_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
Groom *groom = ob->data;
|
||||
|
||||
ListBase *regions = (groom->editgroom ? &groom->editgroom->regions : &groom->regions);
|
||||
GroomRegion *region = CTX_data_pointer_get_type(C, "groom_region", &RNA_GroomRegion).data;
|
||||
if (region == NULL)
|
||||
{
|
||||
region = BLI_findlink(regions, groom->active_region);
|
||||
if (region == NULL)
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_groom_region_remove(groom, region);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GROOM_OT_region_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Region";
|
||||
ot->description = "Remove a region from the groom object";
|
||||
ot->idname = "GROOM_OT_region_remove";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = region_remove_exec;
|
||||
ot->poll = ED_groom_object_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* GROOM_OT_region_bind */
|
||||
|
||||
static int region_bind_poll(bContext *C)
|
||||
{
|
||||
if (!ED_operator_scene_editable(C))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Object *ob = ED_object_context(C);
|
||||
Groom *groom = ob->data;
|
||||
if (groom->editgroom)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int region_bind_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const Depsgraph *depsgraph = CTX_data_depsgraph(C);
|
||||
Object *ob = ED_object_context(C);
|
||||
Groom *groom = ob->data;
|
||||
const bool force_rebind = RNA_boolean_get(op->ptr, "force_rebind");
|
||||
|
||||
GroomRegion *region = CTX_data_pointer_get_type(C, "groom_region", &RNA_GroomRegion).data;
|
||||
if (!region)
|
||||
{
|
||||
region = BLI_findlink(&groom->regions, groom->active_region);
|
||||
if (!region)
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_groom_region_bind(depsgraph, groom, region, force_rebind);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GROOM_OT_region_bind(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Bind Region";
|
||||
ot->description = "Bind a groom bundle to its scalp region";
|
||||
ot->idname = "GROOM_OT_region_bind";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = region_bind_exec;
|
||||
ot->poll = region_bind_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(ot->srna, "force_rebind", true, "Force Rebind",
|
||||
"Force rebinding of the groom region even if a binding already exists");
|
||||
}
|
||||
|
||||
/* GROOM_OT_extrude_bundle */
|
||||
|
||||
static void groom_bundle_extrude(const Depsgraph *depsgraph, const Groom *groom, GroomRegion *region)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
const int numverts = region->numverts;
|
||||
|
||||
bundle->totsections += 1;
|
||||
bundle->sections = MEM_reallocN_id(bundle->sections, sizeof(GroomSection) * bundle->totsections, "groom bundle sections");
|
||||
|
||||
GroomSection *new_section = &bundle->sections[bundle->totsections - 1];
|
||||
if (bundle->totsections > 1)
|
||||
{
|
||||
/* Initialize by copying from the last section */
|
||||
const GroomSection *prev_section = &bundle->sections[bundle->totsections - 2];
|
||||
memcpy(new_section, prev_section, sizeof(GroomSection));
|
||||
|
||||
bundle->totverts += numverts;
|
||||
bundle->verts = MEM_reallocN_id(bundle->verts, sizeof(GroomSectionVertex) * bundle->totverts, "groom bundle vertices");
|
||||
|
||||
GroomSectionVertex *new_verts = &bundle->verts[bundle->totverts - numverts];
|
||||
const GroomSectionVertex *prev_verts = &bundle->verts[bundle->totverts - 2*numverts];
|
||||
memcpy(new_verts, prev_verts, sizeof(GroomSectionVertex) * numverts);
|
||||
}
|
||||
else
|
||||
{
|
||||
const struct Mesh *scalp = BKE_groom_get_scalp(depsgraph, groom);
|
||||
BKE_groom_calc_region_transform_on_scalp(region, scalp, new_section->center, new_section->mat);
|
||||
|
||||
BKE_groom_region_reset_shape(depsgraph, groom, region);
|
||||
}
|
||||
|
||||
{
|
||||
/* Select the last section */
|
||||
GroomSection *section = region->bundle.sections;
|
||||
for (int i = 0; i < bundle->totsections - 1; ++i, ++section)
|
||||
{
|
||||
section->flag &= ~GM_SECTION_SELECT;
|
||||
}
|
||||
bundle->sections[bundle->totsections - 1].flag |= GM_SECTION_SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
static int groom_extrude_bundle_poll(bContext *C)
|
||||
{
|
||||
if (!ED_operator_editgroom(C))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
if (scene->toolsettings->groom_edit_settings.mode != GM_EDIT_MODE_CURVES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int groom_extrude_bundle_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
const Depsgraph *depsgraph = CTX_data_depsgraph(C);
|
||||
Object *ob = ED_object_context(C);
|
||||
Groom *groom = ob->data;
|
||||
EditGroom *edit = groom->editgroom;
|
||||
|
||||
for (GroomRegion *region = edit->regions.first; region; region = region->next)
|
||||
{
|
||||
if (region->flag & GM_REGION_SELECT)
|
||||
{
|
||||
groom_bundle_extrude(depsgraph, groom, region);
|
||||
}
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GROOM_OT_extrude_bundle(struct wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Extrude Bundle";
|
||||
ot->idname = "GROOM_OT_extrude_bundle";
|
||||
ot->description = "Extrude hair bundle";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = groom_extrude_bundle_exec;
|
||||
ot->poll = groom_extrude_bundle_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
Transform_Properties(ot, P_NO_DEFAULTS);
|
||||
}
|
448
source/blender/editors/groom/editgroom_select.c
Normal file
448
source/blender/editors/groom/editgroom_select.c
Normal file
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/groom/editgroom_select.c
|
||||
* \ingroup edgroom
|
||||
*/
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_screen.h"
|
||||
#include "ED_types.h"
|
||||
#include "ED_view3d.h"
|
||||
#include "ED_groom.h"
|
||||
|
||||
#include "groom_intern.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
bool ED_groom_select_check_regions(const EditGroom *edit)
|
||||
{
|
||||
for (GroomRegion* region = edit->regions.first; region; region = region->next)
|
||||
{
|
||||
if (region->flag & GM_REGION_SELECT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_groom_select_check_curves(const EditGroom *edit)
|
||||
{
|
||||
for (GroomRegion* region = edit->regions.first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSection *section = bundle->sections;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
if (section->flag & GM_SECTION_SELECT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_groom_select_check_sections(const EditGroom *edit)
|
||||
{
|
||||
for (GroomRegion* region = edit->regions.first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSectionVertex *vertex = bundle->verts;
|
||||
for (int i = 0; i < bundle->totverts; ++i, ++vertex)
|
||||
{
|
||||
if (vertex->flag & GM_VERTEX_SELECT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ED_groom_select_regions(EditGroom *edit, EditGroomSelectCb select_cb, void *userdata)
|
||||
{
|
||||
for (GroomRegion* region = edit->regions.first; region; region = region->next)
|
||||
{
|
||||
const bool select = select_cb(userdata, region->flag & GM_REGION_SELECT);
|
||||
if (select)
|
||||
{
|
||||
region->flag |= GM_REGION_SELECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
region->flag &= ~GM_REGION_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ED_groom_select_curves(EditGroom *edit, EditGroomSelectCb select_cb, void *userdata)
|
||||
{
|
||||
for (GroomRegion* region = edit->regions.first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSection *section = bundle->sections;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
const bool select = select_cb(userdata, section->flag & GM_SECTION_SELECT);
|
||||
if (select)
|
||||
{
|
||||
section->flag |= GM_SECTION_SELECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
section->flag &= ~GM_SECTION_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ED_groom_select_sections(EditGroom *edit, EditGroomSelectCb select_cb, void *userdata)
|
||||
{
|
||||
for (GroomRegion* region = edit->regions.first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSectionVertex *vertex = bundle->verts;
|
||||
for (int i = 0; i < bundle->totverts; ++i, ++vertex)
|
||||
{
|
||||
const bool select = select_cb(userdata, vertex->flag & GM_VERTEX_SELECT);
|
||||
if (select)
|
||||
{
|
||||
vertex->flag |= GM_VERTEX_SELECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex->flag &= ~GM_VERTEX_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool groom_select_all_cb(void *UNUSED(userdata), bool UNUSED(is_selected))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool groom_deselect_all_cb(void *UNUSED(userdata), bool UNUSED(is_selected))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool groom_select_swap_cb(void *UNUSED(userdata), bool is_selected)
|
||||
{
|
||||
return !is_selected;
|
||||
}
|
||||
|
||||
static bool groom_has_selected(EditGroom *edit, GroomEditMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GM_EDIT_MODE_REGIONS:
|
||||
return ED_groom_select_check_regions(edit);
|
||||
case GM_EDIT_MODE_CURVES:
|
||||
return ED_groom_select_check_curves(edit);
|
||||
case GM_EDIT_MODE_SECTIONS:
|
||||
return ED_groom_select_check_sections(edit);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int de_select_all_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
GroomEditMode mode = CTX_data_tool_settings(C)->groom_edit_settings.mode;
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
Groom *groom = obedit->data;
|
||||
int action = RNA_enum_get(op->ptr, "action");
|
||||
|
||||
EditGroomSelectCb cb;
|
||||
switch (action) {
|
||||
case SEL_SELECT:
|
||||
cb = groom_select_all_cb;
|
||||
break;
|
||||
case SEL_DESELECT:
|
||||
cb = groom_deselect_all_cb;
|
||||
break;
|
||||
case SEL_INVERT:
|
||||
cb = groom_select_swap_cb;
|
||||
break;
|
||||
case SEL_TOGGLE:
|
||||
{
|
||||
if (groom_has_selected(groom->editgroom, mode)) {
|
||||
cb = groom_deselect_all_cb;
|
||||
}
|
||||
else
|
||||
{
|
||||
cb = groom_select_all_cb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case GM_EDIT_MODE_REGIONS:
|
||||
ED_groom_select_regions(groom->editgroom, cb, NULL);
|
||||
break;
|
||||
case GM_EDIT_MODE_CURVES:
|
||||
ED_groom_select_curves(groom->editgroom, cb, NULL);
|
||||
break;
|
||||
case GM_EDIT_MODE_SECTIONS:
|
||||
ED_groom_select_sections(groom->editgroom, cb, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GROOM_OT_select_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "(De)select All";
|
||||
ot->idname = "GROOM_OT_select_all";
|
||||
ot->description = "(De)select all control points";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = de_select_all_exec;
|
||||
ot->poll = ED_operator_editgroom;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
WM_operator_properties_select_all(ot);
|
||||
}
|
||||
|
||||
/****************************** Mouse Selection *************************/
|
||||
|
||||
static void select_pick_findnearest_cb(
|
||||
void *userdata,
|
||||
GroomRegion *region,
|
||||
GroomSection *section,
|
||||
GroomSectionVertex *vertex,
|
||||
const float screen_co[2])
|
||||
{
|
||||
struct
|
||||
{
|
||||
GroomRegion *region;
|
||||
GroomSection *section;
|
||||
GroomSectionVertex *vertex;
|
||||
float dist;
|
||||
bool select;
|
||||
float mval_fl[2];
|
||||
} *data = userdata;
|
||||
|
||||
float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
|
||||
|
||||
/* bias towards unselected items */
|
||||
if (data->select &&
|
||||
((vertex && vertex->flag & GM_VERTEX_SELECT) ||
|
||||
(section && section->flag & GM_SECTION_SELECT) ||
|
||||
(region && region->flag & GM_REGION_SELECT)))
|
||||
{
|
||||
dist_test += 5.0f;
|
||||
}
|
||||
|
||||
if (dist_test < data->dist) {
|
||||
data->dist = dist_test;
|
||||
data->region = region;
|
||||
data->section = section;
|
||||
data->vertex = vertex;
|
||||
}
|
||||
}
|
||||
|
||||
static void groom_set_region_select_flags(Groom *groom, int flag)
|
||||
{
|
||||
for (GroomRegion* region = groom->editgroom->regions.first; region; region = region->next)
|
||||
{
|
||||
region->flag = (region->flag & ~GM_REGION_SELECT) | (flag & GM_REGION_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
static void groom_set_curve_select_flags(Groom *groom, int flag)
|
||||
{
|
||||
for (GroomRegion* region = groom->editgroom->regions.first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSection *section = bundle->sections;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
section->flag = (section->flag & ~GM_SECTION_SELECT) | (flag & GM_SECTION_SELECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void groom_set_section_select_flags(Groom *groom, int flag)
|
||||
{
|
||||
for (GroomRegion* region = groom->editgroom->regions.first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSectionVertex *vertex = bundle->verts;
|
||||
for (int i = 0; i < bundle->totverts; ++i, ++vertex)
|
||||
{
|
||||
vertex->flag = (vertex->flag & ~GM_VERTEX_SELECT) | (flag & GM_VERTEX_SELECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ED_groom_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
|
||||
{
|
||||
ViewContext vc;
|
||||
ED_view3d_viewcontext_init(C, &vc);
|
||||
Groom *groom = vc.obedit->data;
|
||||
|
||||
struct
|
||||
{
|
||||
GroomRegion *region;
|
||||
GroomSection *section;
|
||||
GroomSectionVertex *vertex;
|
||||
float dist;
|
||||
bool select;
|
||||
float mval_fl[2];
|
||||
} data = {NULL};
|
||||
|
||||
data.dist = ED_view3d_select_dist_px();
|
||||
data.select = true;
|
||||
data.mval_fl[0] = mval[0];
|
||||
data.mval_fl[1] = mval[1];
|
||||
|
||||
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
|
||||
groom_foreachScreenVert(&vc, select_pick_findnearest_cb, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
|
||||
|
||||
bool found = false;
|
||||
if (data.vertex)
|
||||
{
|
||||
if (extend)
|
||||
{
|
||||
data.vertex->flag |= GM_VERTEX_SELECT;
|
||||
}
|
||||
else if (deselect)
|
||||
{
|
||||
data.vertex->flag &= ~GM_VERTEX_SELECT;
|
||||
}
|
||||
else if (toggle)
|
||||
{
|
||||
data.vertex->flag ^= GM_VERTEX_SELECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* deselect all other verts */
|
||||
groom_set_section_select_flags(groom, 0);
|
||||
data.vertex->flag |= GM_VERTEX_SELECT;
|
||||
}
|
||||
|
||||
if (data.vertex->flag & GM_VERTEX_SELECT)
|
||||
{
|
||||
/* set active section */
|
||||
groom_set_region_select_flags(groom, 0);
|
||||
groom_set_curve_select_flags(groom, 0);
|
||||
data.section->flag |= GM_SECTION_SELECT;
|
||||
data.region->flag |= GM_REGION_SELECT;
|
||||
}
|
||||
|
||||
found = true;
|
||||
}
|
||||
else if (data.section)
|
||||
{
|
||||
if (extend)
|
||||
{
|
||||
data.section->flag |= GM_SECTION_SELECT;
|
||||
}
|
||||
else if (deselect)
|
||||
{
|
||||
data.section->flag &= ~GM_SECTION_SELECT;
|
||||
}
|
||||
else if (toggle)
|
||||
{
|
||||
data.section->flag ^= GM_SECTION_SELECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* deselect all other sections */
|
||||
groom_set_curve_select_flags(groom, 0);
|
||||
data.section->flag |= GM_SECTION_SELECT;
|
||||
}
|
||||
|
||||
if (data.section->flag & GM_SECTION_SELECT)
|
||||
{
|
||||
/* set active region */
|
||||
groom_set_region_select_flags(groom, 0);
|
||||
data.region->flag |= GM_REGION_SELECT;
|
||||
}
|
||||
|
||||
found = true;
|
||||
}
|
||||
else if (data.region)
|
||||
{
|
||||
if (extend)
|
||||
{
|
||||
data.region->flag |= GM_REGION_SELECT;
|
||||
}
|
||||
else if (deselect)
|
||||
{
|
||||
data.region->flag &= ~GM_REGION_SELECT;
|
||||
}
|
||||
else if (toggle)
|
||||
{
|
||||
data.region->flag ^= GM_REGION_SELECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* deselect all other regions */
|
||||
groom_set_region_select_flags(groom, 0);
|
||||
data.region->flag |= GM_REGION_SELECT;
|
||||
}
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
106
source/blender/editors/groom/groom_hair.c
Normal file
106
source/blender/editors/groom/groom_hair.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/groom/groom_hair.c
|
||||
* \ingroup groom
|
||||
*/
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_util.h"
|
||||
#include "ED_view3d.h"
|
||||
#include "ED_groom.h"
|
||||
|
||||
#include "groom_intern.h"
|
||||
|
||||
/* GROOM_OT_hair_distribute */
|
||||
|
||||
static int hair_distribute_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const Depsgraph *depsgraph = CTX_data_depsgraph(C);
|
||||
Object *ob = ED_object_context(C);
|
||||
Groom *groom = ob->data;
|
||||
int hair_count = RNA_int_get(op->ptr, "hair_count");
|
||||
unsigned int seed = (unsigned int)RNA_int_get(op->ptr, "seed");
|
||||
|
||||
if (!groom->scalp_object)
|
||||
{
|
||||
BKE_reportf(op->reports, RPT_ERROR, "Scalp object needed for creating hair follicles");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BKE_groom_hair_distribute(depsgraph, groom, seed, hair_count);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GROOM_OT_hair_distribute(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Distribute Hair";
|
||||
ot->description = "Distribute hair follicles and guide curves on the scalp";
|
||||
ot->idname = "GROOM_OT_hair_distribute";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = WM_operator_props_popup_confirm;
|
||||
ot->exec = hair_distribute_exec;
|
||||
ot->poll = ED_groom_object_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_int(ot->srna, "hair_count", 1000, 0, INT_MAX,
|
||||
"Hair Count", "Number of hairs to generate", 1, 1e6);
|
||||
RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX,
|
||||
"Seed", "Seed value for randomized follicle distribution", 0, INT_MAX);
|
||||
}
|
51
source/blender/editors/groom/groom_intern.h
Normal file
51
source/blender/editors/groom/groom_intern.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/groom/groom_intern.h
|
||||
* \ingroup edgroom
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GROOM_INTERN_H__
|
||||
#define __GROOM_INTERN_H__
|
||||
|
||||
struct wmOperatorType;
|
||||
|
||||
/* editgroom_region.c */
|
||||
void GROOM_OT_region_add(struct wmOperatorType *ot);
|
||||
void GROOM_OT_region_remove(struct wmOperatorType *ot);
|
||||
void GROOM_OT_region_bind(struct wmOperatorType *ot);
|
||||
|
||||
void GROOM_OT_extrude_bundle(struct wmOperatorType *ot);
|
||||
|
||||
/* editgroom_select.c */
|
||||
void GROOM_OT_select_all(struct wmOperatorType *ot);
|
||||
|
||||
/* groom_hair.c */
|
||||
void GROOM_OT_hair_distribute(struct wmOperatorType *ot);
|
||||
|
||||
#endif /* __GROOM_INTERN_H__ */
|
96
source/blender/editors/groom/groom_ops.c
Normal file
96
source/blender/editors/groom/groom_ops.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/groom/groom_ops.c
|
||||
* \ingroup edgroom
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_groom.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_transform.h"
|
||||
|
||||
#include "groom_intern.h"
|
||||
|
||||
/************************* registration ****************************/
|
||||
|
||||
void ED_operatortypes_groom(void)
|
||||
{
|
||||
WM_operatortype_append(GROOM_OT_region_add);
|
||||
WM_operatortype_append(GROOM_OT_region_remove);
|
||||
WM_operatortype_append(GROOM_OT_region_bind);
|
||||
|
||||
WM_operatortype_append(GROOM_OT_extrude_bundle);
|
||||
|
||||
WM_operatortype_append(GROOM_OT_select_all);
|
||||
|
||||
WM_operatortype_append(GROOM_OT_hair_distribute);
|
||||
}
|
||||
|
||||
void ED_operatormacros_groom(void)
|
||||
{
|
||||
wmOperatorType *ot;
|
||||
wmOperatorTypeMacro *otmacro;
|
||||
|
||||
ot = WM_operatortype_append_macro("GROOM_OT_extrude_bundle_move", "Extrude Bundle and Move",
|
||||
"Extrude bundle and move result", OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
otmacro = WM_operatortype_macro_define(ot, "GROOM_OT_extrude_bundle");
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
||||
|
||||
UNUSED_VARS(otmacro);
|
||||
}
|
||||
|
||||
void ED_keymap_groom(wmKeyConfig *keyconf)
|
||||
{
|
||||
wmKeyMap *keymap;
|
||||
wmKeyMapItem *kmi;
|
||||
|
||||
keymap = WM_keymap_find(keyconf, "Groom", 0, 0);
|
||||
keymap->poll = ED_operator_editgroom;
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "GROOM_OT_select_all", AKEY, KM_PRESS, 0, 0);
|
||||
RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
|
||||
kmi = WM_keymap_add_item(keymap, "GROOM_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
|
||||
RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "GROOM_OT_extrude_bundle_move", EKEY, KM_PRESS, 0, 0);
|
||||
|
||||
ED_keymap_proportional_cycle(keyconf, keymap);
|
||||
ED_keymap_proportional_editmode(keyconf, keymap, true);
|
||||
}
|
68
source/blender/editors/include/ED_groom.h
Normal file
68
source/blender/editors/include/ED_groom.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file ED_groom.h
|
||||
* \ingroup editors
|
||||
*/
|
||||
|
||||
#ifndef __ED_GROOM_H__
|
||||
#define __ED_GROOM_H__
|
||||
|
||||
struct bContext;
|
||||
struct Groom;
|
||||
struct Object;
|
||||
struct wmOperator;
|
||||
struct wmKeyConfig;
|
||||
struct EditGroom;
|
||||
|
||||
/* groom_ops.c */
|
||||
void ED_operatortypes_groom(void);
|
||||
void ED_operatormacros_groom(void);
|
||||
void ED_keymap_groom(struct wmKeyConfig *keyconf);
|
||||
|
||||
/* editgroom.c */
|
||||
void undo_push_groom(struct bContext *C, const char *name);
|
||||
|
||||
void ED_groom_editgroom_load(struct Object *obedit);
|
||||
void ED_groom_editgroom_make(struct Object *obedit);
|
||||
void ED_groom_editgroom_free(struct Object *obedit);
|
||||
|
||||
int ED_groom_object_poll(struct bContext *C);
|
||||
|
||||
/* editgroom_select.c */
|
||||
bool ED_groom_select_check_regions(const struct EditGroom *edit);
|
||||
bool ED_groom_select_check_curves(const struct EditGroom *edit);
|
||||
bool ED_groom_select_check_sections(const struct EditGroom *edit);
|
||||
|
||||
typedef bool (*EditGroomSelectCb)(void *userdata, bool is_selected);
|
||||
void ED_groom_select_regions(struct EditGroom *edit, EditGroomSelectCb select_cb, void *userdata);
|
||||
void ED_groom_select_curves(struct EditGroom *edit, EditGroomSelectCb select_cb, void *userdata);
|
||||
void ED_groom_select_sections(struct EditGroom *edit, EditGroomSelectCb select_cb, void *userdata);
|
||||
|
||||
bool ED_groom_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
|
||||
|
||||
#endif /* __ED_GROOM_H__ */
|
@@ -296,6 +296,7 @@ bool ED_operator_editsurfcurve_region_view3d(struct bContext *C);
|
||||
bool ED_operator_editfont(struct bContext *C);
|
||||
bool ED_operator_editlattice(struct bContext *C);
|
||||
bool ED_operator_editmball(struct bContext *C);
|
||||
bool ED_operator_editgroom(struct bContext *C);
|
||||
bool ED_operator_uvedit(struct bContext *C);
|
||||
bool ED_operator_uvedit_space_image(struct bContext *C);
|
||||
bool ED_operator_uvmap(struct bContext *C);
|
||||
|
@@ -43,6 +43,9 @@ struct BoundBox;
|
||||
struct Camera;
|
||||
struct Depsgraph;
|
||||
struct EditBone;
|
||||
struct GroomRegion;
|
||||
struct GroomSection;
|
||||
struct GroomSectionVertex;
|
||||
struct ImBuf;
|
||||
struct MVert;
|
||||
struct Main;
|
||||
@@ -219,6 +222,15 @@ void pose_foreachScreenBone(
|
||||
void (*func)(void *userData, struct bPoseChannel *pchan,
|
||||
const float screen_co_a[2], const float screen_co_b[2]),
|
||||
void *userData, const eV3DProjTest clip_flag);
|
||||
void groom_foreachScreenVert(
|
||||
struct ViewContext *vc,
|
||||
void (*func)(
|
||||
void *userData,
|
||||
struct GroomRegion *region,
|
||||
struct GroomSection *section,
|
||||
struct GroomSectionVertex *vert,
|
||||
const float screen_co[2]),
|
||||
void *userData, const eV3DProjTest clip_flag);
|
||||
/* *** end iterators *** */
|
||||
|
||||
|
||||
|
@@ -260,8 +260,8 @@ DEF_ICON(GROUP_BONE)
|
||||
DEF_ICON(GROUP_VERTEX)
|
||||
DEF_ICON(GROUP_VCOL)
|
||||
DEF_ICON(GROUP_UVS)
|
||||
DEF_ICON(GROOM_DATA)
|
||||
#ifndef DEF_ICON_BLANK_SKIP
|
||||
DEF_ICON(BLANK089)
|
||||
DEF_ICON(BLANK090)
|
||||
#endif
|
||||
DEF_ICON(RNA)
|
||||
@@ -313,6 +313,7 @@ DEF_ICON(OUTLINER_OB_SPEAKER)
|
||||
DEF_ICON(OUTLINER_OB_FORCE_FIELD)
|
||||
DEF_ICON(OUTLINER_OB_GROUP_INSTANCE)
|
||||
DEF_ICON(OUTLINER_OB_GREASEPENCIL)
|
||||
DEF_ICON(OUTLINER_OB_GROOM)
|
||||
DEF_ICON(OUTLINER_OB_LIGHTPROBE)
|
||||
#ifndef DEF_ICON_BLANK_SKIP
|
||||
DEF_ICON(BLANK124)
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
@@ -72,6 +73,7 @@
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_font.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_lamp.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_layer.h"
|
||||
@@ -767,6 +769,41 @@ void OBJECT_OT_metaball_add(wmOperatorType *ot)
|
||||
ED_object_add_generic_props(ot, true);
|
||||
}
|
||||
|
||||
/********************* Add Groom Operator ********************/
|
||||
|
||||
static int object_groom_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bool enter_editmode;
|
||||
unsigned int layer;
|
||||
float loc[3], rot[3];
|
||||
if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL))
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
Object *ob = ED_object_add_type(C, OB_GROOM, NULL, loc, rot, false, layer);
|
||||
|
||||
Groom *groom = ob->data;
|
||||
UNUSED_VARS(groom);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_groom_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Groom";
|
||||
ot->description = "Add a groom object to the scene";
|
||||
ot->idname = "OBJECT_OT_groom_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = object_groom_add_exec;
|
||||
ot->poll = ED_operator_objectmode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
ED_object_add_generic_props(ot, true);
|
||||
}
|
||||
|
||||
/********************* Add Text Operator ********************/
|
||||
|
||||
static int object_add_text_exec(bContext *C, wmOperator *op)
|
||||
|
@@ -48,6 +48,7 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
@@ -92,6 +93,7 @@
|
||||
|
||||
#include "ED_armature.h"
|
||||
#include "ED_curve.h"
|
||||
#include "ED_groom.h"
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_mball.h"
|
||||
#include "ED_lattice.h"
|
||||
@@ -492,6 +494,14 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
|
||||
ED_mball_editmball_free(obedit);
|
||||
}
|
||||
}
|
||||
else if (obedit->type == OB_GROOM) {
|
||||
const Groom *groom = obedit->data;
|
||||
if (groom->editgroom == NULL) {
|
||||
return false;
|
||||
}
|
||||
ED_groom_editgroom_load(obedit);
|
||||
if (freedata) ED_groom_editgroom_free(obedit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -630,6 +640,12 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag
|
||||
|
||||
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene);
|
||||
}
|
||||
else if (ob->type == OB_GROOM) {
|
||||
ok = 1;
|
||||
ED_groom_editgroom_make(ob);
|
||||
|
||||
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_GROOM, scene);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
@@ -121,6 +121,7 @@ void OBJECT_OT_effector_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_camera_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_speaker_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_collection_instance_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_groom_add(struct wmOperatorType *ot);
|
||||
|
||||
void OBJECT_OT_duplicates_make_real(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_duplicate(struct wmOperatorType *ot);
|
||||
@@ -175,6 +176,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);
|
||||
|
@@ -105,6 +105,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
|
||||
case OB_SURF:
|
||||
case OB_FONT:
|
||||
case OB_MBALL:
|
||||
case OB_GROOM:
|
||||
if (mode & (OB_MODE_EDIT))
|
||||
return true;
|
||||
break;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -119,6 +119,7 @@ void ED_operatortypes_object(void)
|
||||
WM_operatortype_append(OBJECT_OT_light_add);
|
||||
WM_operatortype_append(OBJECT_OT_camera_add);
|
||||
WM_operatortype_append(OBJECT_OT_speaker_add);
|
||||
WM_operatortype_append(OBJECT_OT_groom_add);
|
||||
WM_operatortype_append(OBJECT_OT_add);
|
||||
WM_operatortype_append(OBJECT_OT_add_named);
|
||||
WM_operatortype_append(OBJECT_OT_effector_add);
|
||||
@@ -263,6 +264,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);
|
||||
|
@@ -38,6 +38,7 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
@@ -71,6 +72,7 @@
|
||||
#include "BKE_displist.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_lamp.h"
|
||||
@@ -1794,6 +1796,9 @@ static void single_obdata_users(Main *bmain, Scene *scene, ViewLayer *view_layer
|
||||
case OB_SPEAKER:
|
||||
ob->data = ID_NEW_SET(ob->data, BKE_speaker_copy(bmain, ob->data));
|
||||
break;
|
||||
case OB_GROOM:
|
||||
ob->data = ID_NEW_SET(ob->data, BKE_groom_copy(bmain, ob->data));
|
||||
break;
|
||||
case OB_LIGHTPROBE:
|
||||
ob->data = ID_NEW_SET(ob->data, BKE_lightprobe_copy(bmain, ob->data));
|
||||
break;
|
||||
@@ -1896,6 +1901,10 @@ static void single_mat_users_expand(Main *bmain)
|
||||
for (gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next)
|
||||
if (gpd->id.tag & LIB_TAG_NEW)
|
||||
new_id_matar(bmain, gpd->mat, gpd->totcol);
|
||||
|
||||
for (Groom *groom = bmain->grooms.first; groom; groom = groom->id.next)
|
||||
if (groom->id.tag & LIB_TAG_NEW)
|
||||
new_id_matar(bmain, groom->mat, groom->totcol);
|
||||
}
|
||||
|
||||
/* used for copying scenes */
|
||||
|
@@ -45,6 +45,7 @@
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
@@ -541,6 +542,14 @@ bool ED_operator_editmball(bContext *C)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ED_operator_editgroom(bContext *C)
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
if (obedit && obedit->type == OB_GROOM)
|
||||
return NULL != ((Groom *)obedit->data)->editgroom;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ED_operator_mask(bContext *C)
|
||||
{
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
|
@@ -48,6 +48,7 @@
|
||||
#include "ED_curve.h"
|
||||
#include "ED_fileselect.h"
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_groom.h"
|
||||
#include "ED_markers.h"
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_node.h"
|
||||
@@ -123,6 +124,7 @@ void ED_spacetypes_init(void)
|
||||
ED_operatortypes_render();
|
||||
ED_operatortypes_mask();
|
||||
ED_operatortypes_io();
|
||||
ED_operatortypes_groom();
|
||||
|
||||
ED_operatortypes_view2d();
|
||||
ED_operatortypes_ui();
|
||||
@@ -174,6 +176,7 @@ void ED_spacemacros_init(void)
|
||||
ED_operatormacros_sequencer();
|
||||
ED_operatormacros_paint();
|
||||
ED_operatormacros_gpencil();
|
||||
ED_operatormacros_groom();
|
||||
|
||||
/* register dropboxes (can use macros) */
|
||||
spacetypes = BKE_spacetypes_list();
|
||||
@@ -207,6 +210,7 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf)
|
||||
ED_keymap_paint(keyconf);
|
||||
ED_keymap_mask(keyconf);
|
||||
ED_keymap_marker(keyconf);
|
||||
ED_keymap_groom(keyconf);
|
||||
|
||||
ED_keymap_view2d(keyconf);
|
||||
ED_keymap_ui(keyconf);
|
||||
|
@@ -238,6 +238,7 @@ static int buttons_context_path_data(ButsContextPath *path, int type)
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) return 1;
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) return 1;
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) return 1;
|
||||
else if (RNA_struct_is_a(ptr->type, &RNA_Groom) && (type == -1 || type == OB_GROOM)) return 1;
|
||||
/* try to get an object in the path, no pinning supported here */
|
||||
else if (buttons_context_path_object(path)) {
|
||||
ob = path->ptr[path->len - 1].data;
|
||||
@@ -717,7 +718,7 @@ const char *buttons_context_dir[] = {
|
||||
"texture", "texture_user", "texture_user_property", "bone", "edit_bone",
|
||||
"pose_bone", "particle_system", "particle_system_editable", "particle_settings",
|
||||
"cloth", "soft_body", "fluid", "smoke", "collision", "brush", "dynamic_paint",
|
||||
"line_style", "collection", NULL
|
||||
"line_style", "collection", "groom", NULL
|
||||
};
|
||||
|
||||
int buttons_context(const bContext *C, const char *member, bContextDataResult *result)
|
||||
@@ -989,6 +990,10 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
|
||||
set_pointer_type(path, result, &RNA_FreestyleLineStyle);
|
||||
return 1;
|
||||
}
|
||||
else if (CTX_data_equals(member, "groom")) {
|
||||
set_pointer_type(path, result, &RNA_Groom);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
@@ -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:
|
||||
@@ -1207,6 +1210,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
|
||||
data.icon = ICON_OUTLINER_OB_CURVE; break;
|
||||
case OB_MBALL:
|
||||
data.icon = ICON_OUTLINER_OB_META; break;
|
||||
case OB_GROOM:
|
||||
tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break;
|
||||
case OB_LATTICE:
|
||||
data.icon = ICON_OUTLINER_OB_LATTICE; break;
|
||||
case OB_ARMATURE:
|
||||
|
@@ -92,7 +92,7 @@ typedef struct TreeElementIcon {
|
||||
#define TREESTORE_ID_TYPE(_id) \
|
||||
(ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \
|
||||
ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS, ID_LP) || \
|
||||
ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL, ID_MC, ID_WS)) /* Only in 'blendfile' mode ... :/ */
|
||||
ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL, ID_MC, ID_WS, ID_GM)) /* Only in 'blendfile' mode ... :/ */
|
||||
|
||||
/* TreeElement->flag */
|
||||
enum {
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
#include "DNA_linestyle_types.h"
|
||||
@@ -180,6 +181,11 @@ static void unlink_material_cb(
|
||||
totcol = mb->totcol;
|
||||
matar = mb->mat;
|
||||
}
|
||||
else if (GS(tsep->id->name) == ID_GM) {
|
||||
Groom *groom = (Groom *)tsep->id;
|
||||
totcol = groom->totcol;
|
||||
matar = groom->mat;
|
||||
}
|
||||
else {
|
||||
BLI_assert(0);
|
||||
}
|
||||
|
@@ -40,6 +40,7 @@
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -554,6 +555,17 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor
|
||||
outliner_add_element(soops, &te->subtree, mb->mat[a], te, 0, a);
|
||||
break;
|
||||
}
|
||||
case ID_GM:
|
||||
{
|
||||
Groom *groom = (Groom *)id;
|
||||
|
||||
if (outliner_animdata_test(groom->adt))
|
||||
outliner_add_element(soops, &te->subtree, groom, te, TSE_ANIM_DATA, 0);
|
||||
|
||||
for (int a = 0; a < groom->totcol; a++)
|
||||
outliner_add_element(soops, &te->subtree, groom->mat[a], te, 0, a);
|
||||
break;
|
||||
}
|
||||
case ID_MA:
|
||||
{
|
||||
Material *ma = (Material *)id;
|
||||
|
@@ -518,6 +518,9 @@ static void view3d_main_region_init(wmWindowManager *wm, ARegion *ar)
|
||||
keymap = WM_keymap_find(wm->defaultconf, "Particle", 0, 0);
|
||||
WM_event_add_keymap_handler(&ar->handlers, keymap);
|
||||
|
||||
keymap = WM_keymap_find(wm->defaultconf, "Groom", 0, 0);
|
||||
WM_event_add_keymap_handler(&ar->handlers, keymap);
|
||||
|
||||
/* editfont keymap swallows all... */
|
||||
keymap = WM_keymap_find(wm->defaultconf, "Font", 0, 0);
|
||||
WM_event_add_keymap_handler(&ar->handlers, keymap);
|
||||
@@ -1034,6 +1037,11 @@ static void view3d_main_region_listener(
|
||||
ED_region_tag_redraw(ar);
|
||||
}
|
||||
break;
|
||||
case NC_GROOM:
|
||||
if (wmn->data == ND_DATA || ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
|
||||
ED_region_tag_redraw(ar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,9 +31,11 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_groom_types.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_armature.h"
|
||||
@@ -42,6 +44,7 @@
|
||||
#include "BKE_displist.h"
|
||||
#include "BKE_editmesh.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_groom.h"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@@ -402,6 +405,77 @@ void lattice_foreachScreenVert(
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
void groom_foreachScreenVert(
|
||||
ViewContext *vc,
|
||||
void (*func)(
|
||||
void *userData,
|
||||
GroomRegion *region,
|
||||
GroomSection *section,
|
||||
GroomSectionVertex *vert,
|
||||
const float screen_co[2]),
|
||||
void *userData, const eV3DProjTest clip_flag)
|
||||
{
|
||||
GroomEditSettings *edit_settings = &vc->scene->toolsettings->groom_edit_settings;
|
||||
Object *obedit = vc->obedit;
|
||||
Groom *groom = obedit->data;
|
||||
ListBase *regions = &groom->editgroom->regions;
|
||||
|
||||
ED_view3d_check_mats_rv3d(vc->rv3d);
|
||||
|
||||
if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
|
||||
ED_view3d_clipping_local(vc->rv3d, obedit->obmat); /* for local clipping lookups */
|
||||
}
|
||||
|
||||
switch (edit_settings->mode)
|
||||
{
|
||||
case GM_EDIT_MODE_REGIONS:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case GM_EDIT_MODE_CURVES:
|
||||
for (GroomRegion* region = regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSection *section = bundle->sections;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
float screen_co[2];
|
||||
if (ED_view3d_project_float_object(vc->ar, section->center, screen_co, clip_flag) == V3D_PROJ_RET_OK)
|
||||
{
|
||||
func(userData, region, section, NULL, screen_co);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GM_EDIT_MODE_SECTIONS:
|
||||
for (GroomRegion* region = regions->first; region; region = region->next)
|
||||
{
|
||||
GroomBundle *bundle = ®ion->bundle;
|
||||
GroomSectionVertex *vertex = bundle->verts;
|
||||
GroomSection *section = bundle->sections;
|
||||
for (int i = 0; i < bundle->totsections; ++i, ++section)
|
||||
{
|
||||
for (int j = 0; j < region->numverts; ++j, ++vertex)
|
||||
{
|
||||
float co[3] = {vertex->co[0], vertex->co[1], 0.0f};
|
||||
mul_m3_v3(section->mat, co);
|
||||
add_v3_v3(co, section->center);
|
||||
|
||||
float screen_co[2];
|
||||
if (ED_view3d_project_float_object(vc->ar, co, screen_co, clip_flag) == V3D_PROJ_RET_OK)
|
||||
{
|
||||
func(userData, region, section, vertex, screen_co);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* ED_view3d_init_mats_rv3d must be called first */
|
||||
void armature_foreachScreenBone(
|
||||
struct ViewContext *vc,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user