Compare commits
381 Commits
tmp-batch-
...
hair_guide
Author | SHA1 | Date | |
---|---|---|---|
dc2d841b7c | |||
27b28e437d | |||
f854fefa17 | |||
b60dcd85f9 | |||
58979947bc | |||
1dba28221d | |||
4a92042916 | |||
31f371b200 | |||
a9be42dfa2 | |||
8915d9ed0d | |||
47912ef922 | |||
51500addaa | |||
8a8b9d7ea9 | |||
a754d222ba | |||
08836b73fe | |||
54aaceaabc | |||
287968377f | |||
d09822c876 | |||
7ad71174fe | |||
0042eb1077 | |||
6751627797 | |||
487775dd87 | |||
142112faec | |||
9b06c23dc3 | |||
0875d8b569 | |||
aa85cbee00 | |||
7cd45e1713 | |||
614868b571 | |||
dc766bfffc | |||
6af06e3c91 | |||
94f3cf23ec | |||
a927dbd17a | |||
cd71dd6382 | |||
dec92e6ba1 | |||
08cdc98b54 | |||
2d8c39c97c | |||
34d61bfd83 | |||
b7b6f72cb5 | |||
4f2532e88c | |||
7d5297305a | |||
4e71eb53d1 | |||
b28c35daef | |||
4c2d7767cf | |||
38c28972c1 | |||
d7977c7667 | |||
e288e3d368 | |||
a6dbdd5282 | |||
4e05dd1dc5 | |||
ba3a650dde | |||
5a75ada839 | |||
6885b90564 | |||
fe5b74c36c | |||
9036a6e60b | |||
3cef615645 | |||
23cb7e81b0 | |||
4fd377990b | |||
671ff29220 | |||
64e3744e8b | |||
93177bce3e | |||
d3c5d74382 | |||
9c9ea7c75f | |||
8a3861e5d9 | |||
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 |
@@ -110,95 +110,342 @@ static void InterpolateKeySegments(int seg,
|
|||||||
curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t);
|
curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ObtainCacheParticleData(Mesh *mesh,
|
static void ObtainCacheDataFromParticleSystem(Mesh *mesh,
|
||||||
BL::Mesh *b_mesh,
|
BL::Object *b_ob,
|
||||||
BL::Object *b_ob,
|
BL::ParticleSystemModifier *b_psmd,
|
||||||
ParticleCurveData *CData,
|
ParticleCurveData *CData,
|
||||||
bool background)
|
bool background,
|
||||||
|
int *curvenum,
|
||||||
|
int *keyno)
|
||||||
{
|
{
|
||||||
int curvenum = 0;
|
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
|
||||||
int keyno = 0;
|
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||||
|
|
||||||
if(!(mesh && b_mesh && b_ob && CData))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Transform tfm = get_transform(b_ob->matrix_world());
|
Transform tfm = get_transform(b_ob->matrix_world());
|
||||||
Transform itfm = transform_quick_inverse(tfm);
|
Transform itfm = transform_quick_inverse(tfm);
|
||||||
|
|
||||||
|
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
|
||||||
|
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int shader = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
|
||||||
|
int draw_step = background ? b_part.render_step() : b_part.draw_step();
|
||||||
|
int totparts = b_psys.particles.length();
|
||||||
|
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||||
|
int totcurves = totchild;
|
||||||
|
|
||||||
|
if(b_part.child_type() == 0 || totchild == 0)
|
||||||
|
totcurves += totparts;
|
||||||
|
|
||||||
|
if(totcurves == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int ren_step = (1 << draw_step) + 1;
|
||||||
|
if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
|
||||||
|
ren_step += b_part.kink_extra_steps();
|
||||||
|
|
||||||
|
CData->psys_firstcurve.push_back_slow(*curvenum);
|
||||||
|
CData->psys_curvenum.push_back_slow(totcurves);
|
||||||
|
CData->psys_shader.push_back_slow(shader);
|
||||||
|
|
||||||
|
float radius = b_part.radius_scale() * 0.5f;
|
||||||
|
|
||||||
|
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
|
||||||
|
CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
|
||||||
|
CData->psys_shape.push_back_slow(b_part.shape());
|
||||||
|
CData->psys_closetip.push_back_slow(b_part.use_close_tip());
|
||||||
|
|
||||||
|
int pa_no = 0;
|
||||||
|
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||||
|
pa_no = totparts;
|
||||||
|
|
||||||
|
int num_add = (totparts+totchild - pa_no);
|
||||||
|
CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
|
||||||
|
CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
|
||||||
|
CData->curve_length.reserve(CData->curve_length.size() + num_add);
|
||||||
|
CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
|
||||||
|
CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
|
||||||
|
|
||||||
|
for(; pa_no < totparts+totchild; pa_no++) {
|
||||||
|
int keynum = 0;
|
||||||
|
CData->curve_firstkey.push_back_slow(*keyno);
|
||||||
|
|
||||||
|
float curve_length = 0.0f;
|
||||||
|
float3 pcKey;
|
||||||
|
for(int step_no = 0; step_no < ren_step; step_no++) {
|
||||||
|
float nco[3];
|
||||||
|
b_psys.co_hair(*b_ob, pa_no, step_no, nco);
|
||||||
|
float3 cKey = make_float3(nco[0], nco[1], nco[2]);
|
||||||
|
cKey = transform_point(&itfm, cKey);
|
||||||
|
if(step_no > 0) {
|
||||||
|
float step_length = len(cKey - pcKey);
|
||||||
|
if(step_length == 0.0f)
|
||||||
|
continue;
|
||||||
|
curve_length += step_length;
|
||||||
|
}
|
||||||
|
CData->curvekey_co.push_back_slow(cKey);
|
||||||
|
CData->curvekey_time.push_back_slow(curve_length);
|
||||||
|
pcKey = cKey;
|
||||||
|
keynum++;
|
||||||
|
}
|
||||||
|
*keyno += keynum;
|
||||||
|
|
||||||
|
CData->curve_keynum.push_back_slow(keynum);
|
||||||
|
CData->curve_length.push_back_slow(curve_length);
|
||||||
|
(*curvenum)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ObtainCacheUVFromParticleSystem(BL::Mesh *b_mesh,
|
||||||
|
BL::ParticleSystemModifier *b_psmd,
|
||||||
|
ParticleCurveData *CData,
|
||||||
|
bool background,
|
||||||
|
int uv_num)
|
||||||
|
{
|
||||||
|
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
|
||||||
|
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||||
|
|
||||||
|
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
|
||||||
|
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totparts = b_psys.particles.length();
|
||||||
|
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||||
|
int totcurves = totchild;
|
||||||
|
|
||||||
|
if(b_part.child_type() == 0 || totchild == 0)
|
||||||
|
totcurves += totparts;
|
||||||
|
|
||||||
|
if(totcurves == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int pa_no = 0;
|
||||||
|
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||||
|
pa_no = totparts;
|
||||||
|
|
||||||
|
int num_add = (totparts+totchild - pa_no);
|
||||||
|
CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
|
||||||
|
|
||||||
|
BL::ParticleSystem::particles_iterator b_pa;
|
||||||
|
b_psys.particles.begin(b_pa);
|
||||||
|
for(; pa_no < totparts+totchild; pa_no++) {
|
||||||
|
/* Add UVs */
|
||||||
|
BL::Mesh::tessface_uv_textures_iterator l;
|
||||||
|
b_mesh->tessface_uv_textures.begin(l);
|
||||||
|
|
||||||
|
float3 uv = make_float3(0.0f, 0.0f, 0.0f);
|
||||||
|
if(b_mesh->tessface_uv_textures.length())
|
||||||
|
b_psys.uv_on_emitter(*b_psmd, *b_pa, pa_no, uv_num, &uv.x);
|
||||||
|
CData->curve_uv.push_back_slow(uv);
|
||||||
|
|
||||||
|
if(pa_no < totparts && b_pa != b_psys.particles.end())
|
||||||
|
++b_pa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ObtainCacheVColFromParticleSystem(BL::Mesh *b_mesh,
|
||||||
|
BL::ParticleSystemModifier *b_psmd,
|
||||||
|
ParticleCurveData *CData,
|
||||||
|
bool background,
|
||||||
|
int vcol_num)
|
||||||
|
{
|
||||||
|
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
|
||||||
|
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||||
|
|
||||||
|
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
|
||||||
|
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totparts = b_psys.particles.length();
|
||||||
|
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||||
|
int totcurves = totchild;
|
||||||
|
|
||||||
|
if(b_part.child_type() == 0 || totchild == 0)
|
||||||
|
totcurves += totparts;
|
||||||
|
|
||||||
|
if(totcurves == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int pa_no = 0;
|
||||||
|
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||||
|
pa_no = totparts;
|
||||||
|
|
||||||
|
int num_add = (totparts+totchild - pa_no);
|
||||||
|
CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
|
||||||
|
|
||||||
|
BL::ParticleSystem::particles_iterator b_pa;
|
||||||
|
b_psys.particles.begin(b_pa);
|
||||||
|
for(; pa_no < totparts+totchild; pa_no++) {
|
||||||
|
/* Add vertex colors */
|
||||||
|
BL::Mesh::tessface_vertex_colors_iterator l;
|
||||||
|
b_mesh->tessface_vertex_colors.begin(l);
|
||||||
|
|
||||||
|
float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
|
||||||
|
if(b_mesh->tessface_vertex_colors.length())
|
||||||
|
b_psys.mcol_on_emitter(*b_psmd, *b_pa, pa_no, vcol_num, &vcol.x);
|
||||||
|
CData->curve_vcol.push_back_slow(vcol);
|
||||||
|
|
||||||
|
if(pa_no < totparts && b_pa != b_psys.particles.end())
|
||||||
|
++b_pa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ObtainCacheDataFromHairSystem(BL::Object *b_ob,
|
||||||
|
BL::HairSystem *b_hsys,
|
||||||
|
BL::Mesh *b_scalp,
|
||||||
|
int shader,
|
||||||
|
bool /*background*/,
|
||||||
|
ParticleCurveData *CData,
|
||||||
|
int *curvenum,
|
||||||
|
int *keyno)
|
||||||
|
{
|
||||||
|
Transform tfm = get_transform(b_ob->matrix_world());
|
||||||
|
Transform itfm = transform_quick_inverse(tfm);
|
||||||
|
|
||||||
|
void *hair_cache = BKE_hair_export_cache_new();
|
||||||
|
BKE_hair_export_cache_update(hair_cache, b_hsys->ptr.data, 0, b_scalp->ptr.data, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
int totcurves, totverts;
|
||||||
|
BKE_hair_render_get_buffer_size(hair_cache, &totcurves, &totverts);
|
||||||
|
|
||||||
|
if(totcurves == 0)
|
||||||
|
{
|
||||||
|
BKE_hair_export_cache_free(hair_cache);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CData->psys_firstcurve.push_back_slow(*curvenum);
|
||||||
|
CData->psys_curvenum.push_back_slow(totcurves);
|
||||||
|
|
||||||
|
// Material
|
||||||
|
CData->psys_shader.push_back_slow(shader);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Cycles settings
|
||||||
|
// PointerRNA cpsys = RNA_pointer_get(&b_hsys->ptr, "cycles");
|
||||||
|
// float radius = get_float(cpsys, "radius_scale") * 0.5f;
|
||||||
|
// CData->psys_rootradius.push_back_slow(radius * get_float(cpsys, "root_width"));
|
||||||
|
// CData->psys_tipradius.push_back_slow(radius * get_float(cpsys, "tip_width"));
|
||||||
|
// CData->psys_shape.push_back_slow(get_float(cpsys, "shape"));
|
||||||
|
// CData->psys_closetip.push_back_slow(get_boolean(cpsys, "use_closetip"));
|
||||||
|
float radius = 0.01f * 0.5f;
|
||||||
|
CData->psys_rootradius.push_back_slow(radius * 1.0f);
|
||||||
|
CData->psys_tipradius.push_back_slow(radius * 0.0f);
|
||||||
|
CData->psys_shape.push_back_slow(0.0f);
|
||||||
|
CData->psys_closetip.push_back_slow(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate buffers
|
||||||
|
int *firstkey_data;
|
||||||
|
int *keynum_data;
|
||||||
|
float *length_data;
|
||||||
|
float3 *co_data;
|
||||||
|
float *time_data;
|
||||||
|
{
|
||||||
|
const size_t firstkey_start = CData->curve_firstkey.size();
|
||||||
|
const size_t keynum_start = CData->curve_keynum.size();
|
||||||
|
const size_t length_start = CData->curve_length.size();
|
||||||
|
const size_t co_start = CData->curvekey_co.size();
|
||||||
|
const size_t time_start = CData->curvekey_time.size();
|
||||||
|
CData->curve_firstkey.resize(firstkey_start + totcurves);
|
||||||
|
CData->curve_keynum.resize(keynum_start + totcurves);
|
||||||
|
CData->curve_length.resize(length_start + totcurves);
|
||||||
|
CData->curvekey_co.resize(co_start + totverts);
|
||||||
|
CData->curvekey_time.resize(time_start + totverts);
|
||||||
|
firstkey_data = CData->curve_firstkey.data() + firstkey_start;
|
||||||
|
keynum_data = CData->curve_keynum.data() + keynum_start;
|
||||||
|
length_data = CData->curve_length.data() + length_start;
|
||||||
|
co_data = CData->curvekey_co.data() + co_start;
|
||||||
|
time_data = CData->curvekey_time.data() + time_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import render curves from hair system
|
||||||
|
BKE_hair_render_fill_buffers(
|
||||||
|
hair_cache,
|
||||||
|
(int)sizeof(float3),
|
||||||
|
firstkey_data,
|
||||||
|
keynum_data,
|
||||||
|
(float*)co_data);
|
||||||
|
|
||||||
|
// Compute curve length and key times
|
||||||
|
for(int c = 0; c < totcurves; ++c) {
|
||||||
|
const int firstkey = firstkey_data[c];
|
||||||
|
const int keynum = keynum_data[c];
|
||||||
|
|
||||||
|
float curve_length = 0.0f;
|
||||||
|
float3 pcKey;
|
||||||
|
for(int v = 0; v < keynum; v++) {
|
||||||
|
float3 cKey = co_data[firstkey + v];
|
||||||
|
cKey = transform_point(&itfm, cKey);
|
||||||
|
if(v > 0) {
|
||||||
|
float step_length = len(cKey - pcKey);
|
||||||
|
if(step_length == 0.0f)
|
||||||
|
continue;
|
||||||
|
curve_length += step_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
co_data[firstkey + v] = cKey;
|
||||||
|
time_data[v] = curve_length;
|
||||||
|
pcKey = cKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstkey_data[c] = *keyno;
|
||||||
|
length_data[c] = curve_length;
|
||||||
|
*keyno += keynum;
|
||||||
|
}
|
||||||
|
*curvenum += totcurves;
|
||||||
|
|
||||||
|
BKE_hair_export_cache_free(hair_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ObtainCacheDataFromObject(Mesh *mesh,
|
||||||
|
BL::Object *b_ob,
|
||||||
|
ParticleCurveData *CData,
|
||||||
|
bool background)
|
||||||
|
{
|
||||||
|
int curvenum = 0;
|
||||||
|
int keyno = 0;
|
||||||
|
|
||||||
|
if(!(mesh && b_ob && CData))
|
||||||
|
return false;
|
||||||
|
|
||||||
BL::Object::modifiers_iterator b_mod;
|
BL::Object::modifiers_iterator b_mod;
|
||||||
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++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())) {
|
if (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);
|
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM)) {
|
||||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
|
||||||
|
ObtainCacheDataFromParticleSystem(mesh,
|
||||||
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
|
b_ob,
|
||||||
int shader = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
|
&b_psmd,
|
||||||
int draw_step = background ? b_part.render_step() : b_part.draw_step();
|
CData,
|
||||||
int totparts = b_psys.particles.length();
|
background,
|
||||||
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
&curvenum,
|
||||||
int totcurves = totchild;
|
&keyno);
|
||||||
|
}
|
||||||
if(b_part.child_type() == 0 || totchild == 0)
|
if((b_mod->type() == b_mod->type_HAIR)) {
|
||||||
totcurves += totparts;
|
BL::HairModifier b_hmd((const PointerRNA)b_mod->ptr);
|
||||||
|
BL::HairSystem b_hsys = b_hmd.hair_system();
|
||||||
if(totcurves == 0)
|
|
||||||
continue;
|
const int material_index = 1; /* TODO */
|
||||||
|
int shader = clamp(material_index - 1, 0, mesh->used_shaders.size()-1);
|
||||||
int ren_step = (1 << draw_step) + 1;
|
|
||||||
if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
|
BL::Mesh b_scalp(b_ob->data());
|
||||||
ren_step += b_part.kink_extra_steps();
|
|
||||||
|
ObtainCacheDataFromHairSystem(b_ob,
|
||||||
CData->psys_firstcurve.push_back_slow(curvenum);
|
&b_hsys,
|
||||||
CData->psys_curvenum.push_back_slow(totcurves);
|
&b_scalp,
|
||||||
CData->psys_shader.push_back_slow(shader);
|
shader,
|
||||||
|
background,
|
||||||
float radius = b_part.radius_scale() * 0.5f;
|
CData,
|
||||||
|
&curvenum,
|
||||||
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
|
&keyno);
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,12 +453,12 @@ static bool ObtainCacheParticleData(Mesh *mesh,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ObtainCacheParticleUV(Mesh *mesh,
|
static bool ObtainCacheUVFromObject(Mesh *mesh,
|
||||||
BL::Mesh *b_mesh,
|
BL::Mesh *b_mesh,
|
||||||
BL::Object *b_ob,
|
BL::Object *b_ob,
|
||||||
ParticleCurveData *CData,
|
ParticleCurveData *CData,
|
||||||
bool background,
|
bool background,
|
||||||
int uv_num)
|
int uv_num)
|
||||||
{
|
{
|
||||||
if(!(mesh && b_mesh && b_ob && CData))
|
if(!(mesh && b_mesh && b_ob && CData))
|
||||||
return false;
|
return false;
|
||||||
@@ -220,44 +467,11 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
|
|||||||
|
|
||||||
BL::Object::modifiers_iterator b_mod;
|
BL::Object::modifiers_iterator b_mod;
|
||||||
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++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())) {
|
if (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);
|
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM)) {
|
||||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
|
||||||
|
ObtainCacheUVFromParticleSystem(b_mesh, &b_psmd, CData, background, uv_num);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,12 +479,12 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ObtainCacheParticleVcol(Mesh *mesh,
|
static bool ObtainCacheVcolFromObject(Mesh *mesh,
|
||||||
BL::Mesh *b_mesh,
|
BL::Mesh *b_mesh,
|
||||||
BL::Object *b_ob,
|
BL::Object *b_ob,
|
||||||
ParticleCurveData *CData,
|
ParticleCurveData *CData,
|
||||||
bool background,
|
bool background,
|
||||||
int vcol_num)
|
int vcol_num)
|
||||||
{
|
{
|
||||||
if(!(mesh && b_mesh && b_ob && CData))
|
if(!(mesh && b_mesh && b_ob && CData))
|
||||||
return false;
|
return false;
|
||||||
@@ -279,44 +493,12 @@ static bool ObtainCacheParticleVcol(Mesh *mesh,
|
|||||||
|
|
||||||
BL::Object::modifiers_iterator b_mod;
|
BL::Object::modifiers_iterator b_mod;
|
||||||
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++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())) {
|
if (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);
|
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM))
|
||||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
{
|
||||||
|
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
|
||||||
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
|
ObtainCacheVColFromParticleSystem(b_mesh, &b_psmd, CData, background, vcol_num);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -906,7 +1088,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
|||||||
|
|
||||||
ParticleCurveData CData;
|
ParticleCurveData CData;
|
||||||
|
|
||||||
ObtainCacheParticleData(mesh, &b_mesh, &b_ob, &CData, !preview);
|
ObtainCacheDataFromObject(mesh, &b_ob, &CData, !preview);
|
||||||
|
|
||||||
/* add hair geometry to mesh */
|
/* add hair geometry to mesh */
|
||||||
if(primitive == CURVE_TRIANGLES) {
|
if(primitive == CURVE_TRIANGLES) {
|
||||||
@@ -942,7 +1124,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
|||||||
|
|
||||||
/* generated coordinates from first key. we should ideally get this from
|
/* generated coordinates from first key. we should ideally get this from
|
||||||
* blender to handle deforming objects */
|
* blender to handle deforming objects */
|
||||||
if(!motion) {
|
if(b_mesh && !motion) {
|
||||||
if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
|
if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
|
||||||
float3 loc, size;
|
float3 loc, size;
|
||||||
mesh_texture_space(b_mesh, loc, size);
|
mesh_texture_space(b_mesh, loc, size);
|
||||||
@@ -967,7 +1149,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* create vertex color attributes */
|
/* create vertex color attributes */
|
||||||
if(!motion) {
|
if(b_mesh && !motion) {
|
||||||
BL::Mesh::tessface_vertex_colors_iterator l;
|
BL::Mesh::tessface_vertex_colors_iterator l;
|
||||||
int vcol_num = 0;
|
int vcol_num = 0;
|
||||||
|
|
||||||
@@ -975,7 +1157,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
|||||||
if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
|
if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
|
||||||
continue;
|
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) {
|
if(primitive == CURVE_TRIANGLES) {
|
||||||
Attribute *attr_vcol = mesh->attributes.add(
|
Attribute *attr_vcol = mesh->attributes.add(
|
||||||
@@ -1004,7 +1186,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* create UV attributes */
|
/* create UV attributes */
|
||||||
if(!motion) {
|
if(b_mesh && !motion) {
|
||||||
BL::Mesh::tessface_uv_textures_iterator l;
|
BL::Mesh::tessface_uv_textures_iterator l;
|
||||||
int uv_num = 0;
|
int uv_num = 0;
|
||||||
|
|
||||||
@@ -1017,7 +1199,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
|||||||
if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
|
if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
|
||||||
Attribute *attr_uv;
|
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(primitive == CURVE_TRIANGLES) {
|
||||||
if(active_render)
|
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());
|
create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(view_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
|
if(view_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
|
||||||
sync_curves(mesh, b_mesh, b_ob, false);
|
sync_curves(mesh, b_mesh, b_ob, false);
|
||||||
|
|
||||||
|
if(b_mesh) {
|
||||||
/* free derived mesh */
|
/* free derived mesh */
|
||||||
b_data.meshes.remove(b_mesh, false, true, false);
|
b_data.meshes.remove(b_mesh, false, true, false);
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
void BKE_image_user_file_path(void *iuser, void *ima, char *path);
|
||||||
unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
|
unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
|
||||||
float *BKE_image_get_float_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
|
CCL_NAMESPACE_BEGIN
|
||||||
|
@@ -44,6 +44,7 @@ _modules = [
|
|||||||
"properties_data_lightprobe",
|
"properties_data_lightprobe",
|
||||||
"properties_data_speaker",
|
"properties_data_speaker",
|
||||||
"properties_data_workspace",
|
"properties_data_workspace",
|
||||||
|
"properties_hair_common",
|
||||||
"properties_mask_common",
|
"properties_mask_common",
|
||||||
"properties_material",
|
"properties_material",
|
||||||
"properties_material_gpencil",
|
"properties_material_gpencil",
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from bpy.app.translations import pgettext_iface as iface_
|
from bpy.app.translations import pgettext_iface as iface_
|
||||||
|
from .properties_hair_common import draw_hair_display_settings
|
||||||
|
|
||||||
class ModifierButtonsPanel:
|
class ModifierButtonsPanel:
|
||||||
bl_space_type = 'PROPERTIES'
|
bl_space_type = 'PROPERTIES'
|
||||||
@@ -1577,6 +1577,27 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
|||||||
if md.rest_source == 'BIND':
|
if md.rest_source == 'BIND':
|
||||||
layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "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):
|
def WEIGHTED_NORMAL(self, layout, ob, md):
|
||||||
layout.label("Weighting Mode:")
|
layout.label("Weighting Mode:")
|
||||||
split = layout.split(align=True)
|
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)
|
@@ -78,7 +78,6 @@ def is_not_gpencil_edit_mode(context):
|
|||||||
)
|
)
|
||||||
return not is_gpmode
|
return not is_gpmode
|
||||||
|
|
||||||
|
|
||||||
# ********** default tools for editmode_mesh ****************
|
# ********** default tools for editmode_mesh ****************
|
||||||
|
|
||||||
|
|
||||||
|
@@ -49,6 +49,7 @@ set(SRC_DNA_INC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_shader_fx_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_gpu_types.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_group_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_image_types.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
|
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h
|
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h
|
||||||
|
238
source/blender/blenkernel/BKE_hair.h
Normal file
238
source/blender/blenkernel/BKE_hair.h
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* The Original Code is Copyright (C) Blender Foundation
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* The Original Code is: all of this file.
|
||||||
|
*
|
||||||
|
* Contributor(s): Lukas Toenne
|
||||||
|
*
|
||||||
|
* ***** END GPL LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BKE_HAIR_H__
|
||||||
|
#define __BKE_HAIR_H__
|
||||||
|
|
||||||
|
/** \file blender/blenkernel/BKE_hair.h
|
||||||
|
* \ingroup bke
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BLI_utildefines.h"
|
||||||
|
|
||||||
|
static const unsigned int HAIR_CURVE_INDEX_NONE = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
struct HairFollicle;
|
||||||
|
struct HairPattern;
|
||||||
|
struct HairSystem;
|
||||||
|
struct HairDrawSettings;
|
||||||
|
struct HairCurveData;
|
||||||
|
struct Mesh;
|
||||||
|
struct MeshSample;
|
||||||
|
struct MLoop;
|
||||||
|
struct Object;
|
||||||
|
|
||||||
|
/* Create a new hair system instance */
|
||||||
|
struct HairSystem* BKE_hair_new(void);
|
||||||
|
/* Copy an existing hair system */
|
||||||
|
struct HairSystem* BKE_hair_copy(struct HairSystem *hsys);
|
||||||
|
/* Delete a hair system */
|
||||||
|
void BKE_hair_free(struct HairSystem *hsys);
|
||||||
|
|
||||||
|
/* === Fiber curves === */
|
||||||
|
|
||||||
|
/* Allocate buffers for defining fiber curves
|
||||||
|
* \param totcurves Number of fiber curves to allocate
|
||||||
|
*/
|
||||||
|
void BKE_hair_fiber_curves_begin(struct HairSystem *hsys, int totcurves);
|
||||||
|
|
||||||
|
/* Set properties of a fiber curve
|
||||||
|
* \param index Index of the fiber curve
|
||||||
|
* \param mesh_sample Origin of the fiber curve on the scalp mesh.
|
||||||
|
* \param numverts Number of vertices in this fiber curve
|
||||||
|
*/
|
||||||
|
void BKE_hair_set_fiber_curve(struct HairSystem *hsys, int index, int numverts,
|
||||||
|
float taper_length, float taper_thickness);
|
||||||
|
|
||||||
|
/* Finalize fiber curve update */
|
||||||
|
void BKE_hair_fiber_curves_end(struct HairSystem *hsys);
|
||||||
|
|
||||||
|
/* Set properties of a fiber curve vertex
|
||||||
|
* \param index Index of the fiber curve vertex.
|
||||||
|
* \param flag Flags to set on the vertex.
|
||||||
|
* \param co Location of the vertex in object space.
|
||||||
|
*/
|
||||||
|
void BKE_hair_set_fiber_vertex(struct HairSystem *hsys, int index, int flag, const float co[3]);
|
||||||
|
|
||||||
|
/* Set the hair fiber curve data used by the hair system.
|
||||||
|
*/
|
||||||
|
void BKE_hair_set_fiber_curves(struct HairSystem *hsys, struct HairCurveData *curves);
|
||||||
|
|
||||||
|
/* Remove all fiber curves.
|
||||||
|
*/
|
||||||
|
void BKE_hair_clear_fiber_curves(struct HairSystem *hsys);
|
||||||
|
|
||||||
|
/* === Follicles === */
|
||||||
|
|
||||||
|
/* Calculate surface area of a scalp mesh */
|
||||||
|
float BKE_hair_calc_surface_area(const struct Mesh *scalp);
|
||||||
|
|
||||||
|
/* Calculate a density value based on surface area and sample count */
|
||||||
|
float BKE_hair_calc_density_from_count(float area, int count);
|
||||||
|
/* Calculate maximum sample count based on surface area and density */
|
||||||
|
int BKE_hair_calc_max_count_from_density(float area, float density);
|
||||||
|
|
||||||
|
/* Calculate a density value based on a minimum distance */
|
||||||
|
float BKE_hair_calc_density_from_min_distance(float min_distance);
|
||||||
|
/* Calculate a minimum distance based on density */
|
||||||
|
float BKE_hair_calc_min_distance_from_density(float density);
|
||||||
|
|
||||||
|
/* Distribute hair follicles on a scalp mesh */
|
||||||
|
void BKE_hair_generate_follicles(
|
||||||
|
struct HairSystem* hsys,
|
||||||
|
struct Mesh *scalp,
|
||||||
|
unsigned int seed,
|
||||||
|
int count);
|
||||||
|
|
||||||
|
/* Distribute hair follicles on a scalp mesh.
|
||||||
|
* Optional per-loop weights control follicle density on the scalp.
|
||||||
|
*/
|
||||||
|
void BKE_hair_generate_follicles_ex(
|
||||||
|
struct HairSystem* hsys,
|
||||||
|
struct Mesh *scalp,
|
||||||
|
unsigned int seed,
|
||||||
|
int count,
|
||||||
|
const float *loop_weights);
|
||||||
|
|
||||||
|
bool BKE_hair_bind_follicles(struct HairSystem *hsys, const struct Mesh *scalp);
|
||||||
|
|
||||||
|
/* === Draw Settings === */
|
||||||
|
|
||||||
|
struct HairDrawSettings* BKE_hair_draw_settings_new(void);
|
||||||
|
struct HairDrawSettings* BKE_hair_draw_settings_copy(struct HairDrawSettings *draw_settings);
|
||||||
|
void BKE_hair_draw_settings_free(struct HairDrawSettings *draw_settings);
|
||||||
|
|
||||||
|
/* === Export === */
|
||||||
|
|
||||||
|
/* Intermediate data for export */
|
||||||
|
typedef struct HairExportCache
|
||||||
|
{
|
||||||
|
/* Per fiber curve data */
|
||||||
|
int totcurves;
|
||||||
|
struct HairFiberCurve *fiber_curves;
|
||||||
|
|
||||||
|
/* Per fiber vertex data */
|
||||||
|
int totverts;
|
||||||
|
struct HairFiberVertex *fiber_verts;
|
||||||
|
float (*fiber_tangents)[3]; /* Tangent vectors on fiber curves */
|
||||||
|
float (*fiber_normals)[3]; /* Normal vectors on fiber curves */
|
||||||
|
|
||||||
|
/* Per follicle data */
|
||||||
|
int totfollicles;
|
||||||
|
float (*follicle_root_position)[3]; /* Root position of each follicle */
|
||||||
|
const struct HairFollicle *follicles;
|
||||||
|
} HairExportCache;
|
||||||
|
|
||||||
|
/* Identifiers for data stored in hair export caches */
|
||||||
|
typedef enum eHairExportCacheUpdateFlags
|
||||||
|
{
|
||||||
|
/* Follicle placement on the scalp mesh */
|
||||||
|
HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS = (1 << 0),
|
||||||
|
/* Follicle curve index */
|
||||||
|
HAIR_EXPORT_FOLLICLE_BINDING = (1 << 1),
|
||||||
|
/* Fiber vertex positions (deform only) */
|
||||||
|
HAIR_EXPORT_FIBER_VERTICES = (1 << 2),
|
||||||
|
/* Fiber curve number and vertex counts (topology changes) */
|
||||||
|
HAIR_EXPORT_FIBER_CURVES = (1 << 3),
|
||||||
|
|
||||||
|
HAIR_EXPORT_ALL =
|
||||||
|
HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS |
|
||||||
|
HAIR_EXPORT_FOLLICLE_BINDING |
|
||||||
|
HAIR_EXPORT_FIBER_VERTICES |
|
||||||
|
HAIR_EXPORT_FIBER_CURVES,
|
||||||
|
HAIR_EXPORT_FIBERS =
|
||||||
|
HAIR_EXPORT_FIBER_VERTICES |
|
||||||
|
HAIR_EXPORT_FIBER_CURVES,
|
||||||
|
HAIR_EXPORT_FOLLICLES =
|
||||||
|
HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS |
|
||||||
|
HAIR_EXPORT_FOLLICLE_BINDING,
|
||||||
|
} eHairExportCacheUpdateFlags;
|
||||||
|
|
||||||
|
/* Create a new export cache.
|
||||||
|
* This can be used to construct full fiber data for rendering.
|
||||||
|
*/
|
||||||
|
struct HairExportCache* BKE_hair_export_cache_new(void);
|
||||||
|
|
||||||
|
/* Update an existing export cache to ensure it contains the requested data.
|
||||||
|
* Returns flags for data that has been updated.
|
||||||
|
*/
|
||||||
|
int BKE_hair_export_cache_update(struct HairExportCache *cache, const struct HairSystem *hsys,
|
||||||
|
int subdiv, struct Mesh *scalp, int requested_data);
|
||||||
|
|
||||||
|
/* Free the given export cache */
|
||||||
|
void BKE_hair_export_cache_free(struct HairExportCache *cache);
|
||||||
|
|
||||||
|
/* Invalidate all data in a hair export cache */
|
||||||
|
void BKE_hair_export_cache_clear(struct HairExportCache *cache);
|
||||||
|
|
||||||
|
/* Invalidate part of the data in a hair export cache.
|
||||||
|
*
|
||||||
|
* Note some parts may get invalidated automatically based on internal dependencies.
|
||||||
|
*/
|
||||||
|
void BKE_hair_export_cache_invalidate(struct HairExportCache *cache, int invalidate);
|
||||||
|
|
||||||
|
/* === Draw Cache === */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BKE_HAIR_BATCH_DIRTY_FIBERS = (1 << 0),
|
||||||
|
BKE_HAIR_BATCH_DIRTY_STRANDS = (1 << 1),
|
||||||
|
BKE_HAIR_BATCH_DIRTY_ALL = 0xFFFF,
|
||||||
|
};
|
||||||
|
void BKE_hair_batch_cache_dirty(struct HairSystem* hsys, int mode);
|
||||||
|
void BKE_hair_batch_cache_free(struct HairSystem* hsys);
|
||||||
|
|
||||||
|
void BKE_hair_get_texture_buffer_size(
|
||||||
|
const struct HairExportCache *cache,
|
||||||
|
int *r_size,
|
||||||
|
int *r_strand_map_start,
|
||||||
|
int *r_strand_vertex_start,
|
||||||
|
int *r_fiber_start);
|
||||||
|
void BKE_hair_get_texture_buffer(
|
||||||
|
const struct HairExportCache *cache,
|
||||||
|
void *texbuffer);
|
||||||
|
|
||||||
|
/* === Render API === */
|
||||||
|
|
||||||
|
/* Calculate required size for render buffers. */
|
||||||
|
void BKE_hair_render_get_buffer_size(
|
||||||
|
const struct HairExportCache* cache,
|
||||||
|
int subdiv,
|
||||||
|
int *r_totcurves,
|
||||||
|
int *r_totverts);
|
||||||
|
|
||||||
|
/* Create render data in existing buffers.
|
||||||
|
* Buffers must be large enough according to BKE_hair_get_render_buffer_size.
|
||||||
|
*/
|
||||||
|
void BKE_hair_render_fill_buffers(
|
||||||
|
const struct HairExportCache* cache,
|
||||||
|
int subdiv,
|
||||||
|
int vertco_stride,
|
||||||
|
int *r_curvestart,
|
||||||
|
int *r_curvelen,
|
||||||
|
float *r_vertco);
|
||||||
|
|
||||||
|
#endif
|
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 Object;
|
||||||
struct Scene;
|
struct Scene;
|
||||||
struct Depsgraph;
|
struct Depsgraph;
|
||||||
|
struct Mesh;
|
||||||
struct ModifierData;
|
struct ModifierData;
|
||||||
struct MTFace;
|
struct MTFace;
|
||||||
struct MCol;
|
struct MCol;
|
||||||
@@ -433,6 +434,7 @@ void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFa
|
|||||||
float orco[3]);
|
float orco[3]);
|
||||||
float psys_particle_value_from_verts(struct Mesh *mesh, short from, struct ParticleData *pa, float *values);
|
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);
|
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 */
|
/* BLI_bvhtree_ray_cast callback */
|
||||||
void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit);
|
void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit);
|
||||||
|
@@ -118,6 +118,8 @@ set(SRC
|
|||||||
intern/freestyle.c
|
intern/freestyle.c
|
||||||
intern/gpencil.c
|
intern/gpencil.c
|
||||||
intern/gpencil_modifier.c
|
intern/gpencil_modifier.c
|
||||||
|
intern/hair.c
|
||||||
|
intern/hair_draw.c
|
||||||
intern/icons.c
|
intern/icons.c
|
||||||
intern/icons_rasterize.c
|
intern/icons_rasterize.c
|
||||||
intern/idcode.c
|
intern/idcode.c
|
||||||
@@ -149,6 +151,7 @@ set(SRC
|
|||||||
intern/mesh_merge.c
|
intern/mesh_merge.c
|
||||||
intern/mesh_remap.c
|
intern/mesh_remap.c
|
||||||
intern/mesh_runtime.c
|
intern/mesh_runtime.c
|
||||||
|
intern/mesh_sample.c
|
||||||
intern/mesh_tangent.c
|
intern/mesh_tangent.c
|
||||||
intern/mesh_validate.c
|
intern/mesh_validate.c
|
||||||
intern/modifier.c
|
intern/modifier.c
|
||||||
@@ -264,6 +267,7 @@ set(SRC
|
|||||||
BKE_global.h
|
BKE_global.h
|
||||||
BKE_gpencil.h
|
BKE_gpencil.h
|
||||||
BKE_gpencil_modifier.h
|
BKE_gpencil_modifier.h
|
||||||
|
BKE_hair.h
|
||||||
BKE_icons.h
|
BKE_icons.h
|
||||||
BKE_idcode.h
|
BKE_idcode.h
|
||||||
BKE_idprop.h
|
BKE_idprop.h
|
||||||
@@ -288,6 +292,7 @@ set(SRC
|
|||||||
BKE_mesh_mapping.h
|
BKE_mesh_mapping.h
|
||||||
BKE_mesh_remap.h
|
BKE_mesh_remap.h
|
||||||
BKE_mesh_runtime.h
|
BKE_mesh_runtime.h
|
||||||
|
BKE_mesh_sample.h
|
||||||
BKE_mesh_tangent.h
|
BKE_mesh_tangent.h
|
||||||
BKE_modifier.h
|
BKE_modifier.h
|
||||||
BKE_movieclip.h
|
BKE_movieclip.h
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -47,8 +47,8 @@
|
|||||||
#include "DNA_brush_types.h"
|
#include "DNA_brush_types.h"
|
||||||
#include "DNA_cachefile_types.h"
|
#include "DNA_cachefile_types.h"
|
||||||
#include "DNA_camera_types.h"
|
#include "DNA_camera_types.h"
|
||||||
#include "DNA_group_types.h"
|
|
||||||
#include "DNA_gpencil_types.h"
|
#include "DNA_gpencil_types.h"
|
||||||
|
#include "DNA_group_types.h"
|
||||||
#include "DNA_ipo_types.h"
|
#include "DNA_ipo_types.h"
|
||||||
#include "DNA_key_types.h"
|
#include "DNA_key_types.h"
|
||||||
#include "DNA_lamp_types.h"
|
#include "DNA_lamp_types.h"
|
||||||
@@ -1057,6 +1057,8 @@ void BKE_main_lib_objects_recalc_all(Main *bmain)
|
|||||||
DEG_id_type_tag(bmain, ID_OB);
|
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,
|
* 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
|
* and returns the number of them as the function result. This is useful for
|
||||||
|
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
@@ -1437,7 +1437,7 @@ int psys_particle_dm_face_lookup(
|
|||||||
return DMCACHE_NOTFOUND;
|
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)
|
if (index < 0)
|
||||||
return 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;
|
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 */
|
/* 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,
|
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],
|
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];
|
float (*orcodata)[3];
|
||||||
int mapindex;
|
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 (vec) { vec[0] = vec[1] = vec[2] = 0.0; }
|
||||||
if (nor) { nor[0] = nor[1] = 0.0; nor[2] = 1.0; }
|
if (nor) { nor[0] = nor[1] = 0.0; nor[2] = 1.0; }
|
||||||
if (orco) { orco[0] = orco[1] = orco[2] = 0.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];
|
float mapfw[4];
|
||||||
int mapindex;
|
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 0.0f;
|
||||||
|
|
||||||
return psys_interpolate_value_from_verts(mesh, from, mapindex, mapfw, values);
|
return psys_interpolate_value_from_verts(mesh, from, mapindex, mapfw, values);
|
||||||
|
@@ -376,6 +376,8 @@ bool clip_segment_v3_plane_n(
|
|||||||
/****************************** Interpolation ********************************/
|
/****************************** 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_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]);
|
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_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]);
|
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 */
|
/* 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])
|
int barycentric_inside_triangle_v2(const float w[3])
|
||||||
{
|
{
|
||||||
|
@@ -73,6 +73,7 @@
|
|||||||
#include "DNA_group_types.h"
|
#include "DNA_group_types.h"
|
||||||
#include "DNA_gpencil_types.h"
|
#include "DNA_gpencil_types.h"
|
||||||
#include "DNA_gpencil_modifier_types.h"
|
#include "DNA_gpencil_modifier_types.h"
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
#include "DNA_shader_fx_types.h"
|
#include "DNA_shader_fx_types.h"
|
||||||
#include "DNA_ipo_types.h"
|
#include "DNA_ipo_types.h"
|
||||||
#include "DNA_key_types.h"
|
#include "DNA_key_types.h"
|
||||||
@@ -5095,6 +5096,25 @@ static void direct_link_pose(FileData *fd, bPose *pose)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void direct_link_hair(FileData *fd, HairSystem* hsys)
|
||||||
|
{
|
||||||
|
if (!hsys) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hsys->pattern = newdataadr(fd, hsys->pattern);
|
||||||
|
if ( hsys->pattern )
|
||||||
|
{
|
||||||
|
hsys->pattern->follicles = newdataadr(fd, hsys->pattern->follicles);
|
||||||
|
}
|
||||||
|
|
||||||
|
hsys->curve_data.curves = newdataadr(fd, hsys->curve_data.curves);
|
||||||
|
hsys->curve_data.verts = newdataadr(fd, hsys->curve_data.verts);
|
||||||
|
|
||||||
|
hsys->draw_batch_cache = NULL;
|
||||||
|
hsys->draw_texture_cache = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
||||||
{
|
{
|
||||||
ModifierData *md;
|
ModifierData *md;
|
||||||
@@ -5418,6 +5438,16 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (md->type == eModifierType_Hair) {
|
||||||
|
HairModifierData *hmd = (HairModifierData *)md;
|
||||||
|
|
||||||
|
hmd->hair_system = newdataadr(fd, hmd->hair_system);
|
||||||
|
direct_link_hair(fd, hmd->hair_system);
|
||||||
|
|
||||||
|
hmd->draw_settings = newdataadr(fd, hmd->draw_settings);
|
||||||
|
|
||||||
|
BLI_listbase_clear(&hmd->fiber_curves); // runtime
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -121,6 +121,7 @@
|
|||||||
#include "DNA_gpencil_modifier_types.h"
|
#include "DNA_gpencil_modifier_types.h"
|
||||||
#include "DNA_shader_fx_types.h"
|
#include "DNA_shader_fx_types.h"
|
||||||
#include "DNA_fileglobal_types.h"
|
#include "DNA_fileglobal_types.h"
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
#include "DNA_key_types.h"
|
#include "DNA_key_types.h"
|
||||||
#include "DNA_lattice_types.h"
|
#include "DNA_lattice_types.h"
|
||||||
#include "DNA_lamp_types.h"
|
#include "DNA_lamp_types.h"
|
||||||
@@ -1617,6 +1618,18 @@ static void write_fmaps(WriteData *wd, ListBase *fbase)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void write_hair(WriteData *wd, HairSystem *hsys)
|
||||||
|
{
|
||||||
|
if ( hsys->pattern )
|
||||||
|
{
|
||||||
|
writestruct(wd, DATA, HairPattern, 1, hsys->pattern);
|
||||||
|
writestruct(wd, DATA, HairFollicle, hsys->pattern->num_follicles, hsys->pattern->follicles);
|
||||||
|
}
|
||||||
|
|
||||||
|
writestruct(wd, DATA, HairFiberCurve, hsys->curve_data.totcurves, hsys->curve_data.curves);
|
||||||
|
writestruct(wd, DATA, HairFiberVertex, hsys->curve_data.totverts, hsys->curve_data.verts);
|
||||||
|
}
|
||||||
|
|
||||||
static void write_modifiers(WriteData *wd, ListBase *modbase)
|
static void write_modifiers(WriteData *wd, ListBase *modbase)
|
||||||
{
|
{
|
||||||
ModifierData *md;
|
ModifierData *md;
|
||||||
@@ -1788,6 +1801,18 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (md->type == eModifierType_Hair) {
|
||||||
|
HairModifierData *hmd = (HairModifierData *)md;
|
||||||
|
|
||||||
|
if (hmd->hair_system) {
|
||||||
|
writestruct(wd, DATA, HairSystem, 1, hmd->hair_system);
|
||||||
|
write_hair(wd, hmd->hair_system);
|
||||||
|
}
|
||||||
|
if (hmd->draw_settings)
|
||||||
|
{
|
||||||
|
writestruct(wd, DATA, HairDrawSettings, 1, hmd->draw_settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -58,6 +58,7 @@ set(SRC
|
|||||||
intern/draw_cache.c
|
intern/draw_cache.c
|
||||||
intern/draw_cache_impl_curve.c
|
intern/draw_cache_impl_curve.c
|
||||||
intern/draw_cache_impl_displist.c
|
intern/draw_cache_impl_displist.c
|
||||||
|
intern/draw_cache_impl_hair.c
|
||||||
intern/draw_cache_impl_lattice.c
|
intern/draw_cache_impl_lattice.c
|
||||||
intern/draw_cache_impl_mesh.c
|
intern/draw_cache_impl_mesh.c
|
||||||
intern/draw_cache_impl_metaball.c
|
intern/draw_cache_impl_metaball.c
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include "BLI_rand.h"
|
#include "BLI_rand.h"
|
||||||
#include "BLI_string_utils.h"
|
#include "BLI_string_utils.h"
|
||||||
|
|
||||||
|
#include "BKE_hair.h"
|
||||||
#include "BKE_particle.h"
|
#include "BKE_particle.h"
|
||||||
#include "BKE_paint.h"
|
#include "BKE_paint.h"
|
||||||
#include "BKE_pbvh.h"
|
#include "BKE_pbvh.h"
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
#include "DNA_world_types.h"
|
#include "DNA_world_types.h"
|
||||||
#include "DNA_modifier_types.h"
|
#include "DNA_modifier_types.h"
|
||||||
#include "DNA_view3d_types.h"
|
#include "DNA_view3d_types.h"
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
|
|
||||||
#include "GPU_material.h"
|
#include "GPU_material.h"
|
||||||
|
|
||||||
@@ -812,6 +814,7 @@ struct GPUMaterial *EEVEE_material_hair_get(
|
|||||||
options |= eevee_material_shadow_option(shadow_method);
|
options |= eevee_material_shadow_option(shadow_method);
|
||||||
|
|
||||||
GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
|
GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
|
||||||
|
|
||||||
if (mat) {
|
if (mat) {
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
@@ -862,14 +865,16 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create(
|
|||||||
**/
|
**/
|
||||||
static struct DRWShadingGroup *EEVEE_default_shading_group_get(
|
static struct DRWShadingGroup *EEVEE_default_shading_group_get(
|
||||||
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
|
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)
|
bool is_hair, bool is_flat_normal, bool use_ssr, int shadow_method)
|
||||||
{
|
{
|
||||||
static int ssr_id;
|
static int ssr_id;
|
||||||
ssr_id = (use_ssr) ? 1 : -1;
|
ssr_id = (use_ssr) ? 1 : -1;
|
||||||
int options = VAR_MAT_MESH;
|
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_hair) options |= VAR_MAT_HAIR;
|
||||||
if (is_flat_normal) options |= VAR_MAT_FLAT;
|
if (is_flat_normal) options |= VAR_MAT_FLAT;
|
||||||
@@ -894,10 +899,19 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_hair) {
|
if (is_hair) {
|
||||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(ob, psys, md,
|
DRWShadingGroup *shgrp = NULL;
|
||||||
vedata->psl->default_pass[options],
|
if (psys && md) {
|
||||||
e_data.default_lit[options]);
|
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);
|
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL, true, true, false, false, false);
|
||||||
|
|
||||||
return shgrp;
|
return shgrp;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1266,7 +1280,7 @@ static void material_opaque(
|
|||||||
if (*shgrp == NULL) {
|
if (*shgrp == NULL) {
|
||||||
bool use_ssr = ((effects->enabled_effects & EFFECT_SSR) != 0);
|
bool use_ssr = ((effects->enabled_effects & EFFECT_SSR) != 0);
|
||||||
*shgrp = EEVEE_default_shading_group_get(sldata, vedata,
|
*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);
|
false, use_flat_nor, use_ssr, linfo->shadow_method);
|
||||||
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
|
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
|
||||||
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
|
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
|
||||||
@@ -1409,6 +1423,217 @@ static void material_transparent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void material_particle_hair(
|
||||||
|
EEVEE_Data *vedata,
|
||||||
|
EEVEE_ViewLayerData *sldata,
|
||||||
|
Object *ob,
|
||||||
|
ParticleSystem *psys,
|
||||||
|
ModifierData *md)
|
||||||
|
{
|
||||||
|
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||||
|
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||||
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||||
|
Scene *scene = draw_ctx->scene;
|
||||||
|
bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0);
|
||||||
|
|
||||||
|
if (!psys_check_enabled(ob, psys, false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!DRW_check_psys_visible_within_active_context(ob, psys)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ParticleSettings *part = psys->part;
|
||||||
|
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
|
||||||
|
if (draw_as != PART_DRAW_PATH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRWShadingGroup *shgrp = NULL;
|
||||||
|
Material *ma = give_current_material(ob, part->omat);
|
||||||
|
|
||||||
|
if (ma == NULL) {
|
||||||
|
ma = &defmaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
float *color_p = &ma->r;
|
||||||
|
float *metal_p = &ma->metallic;
|
||||||
|
float *spec_p = &ma->spec;
|
||||||
|
float *rough_p = &ma->roughness;
|
||||||
|
|
||||||
|
shgrp = DRW_shgroup_particle_hair_create(
|
||||||
|
ob, psys, md,
|
||||||
|
psl->depth_pass,
|
||||||
|
e_data.default_hair_prepass_sh);
|
||||||
|
|
||||||
|
shgrp = DRW_shgroup_particle_hair_create(
|
||||||
|
ob, psys, md,
|
||||||
|
psl->depth_pass_clip,
|
||||||
|
e_data.default_hair_prepass_clip_sh);
|
||||||
|
DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
|
||||||
|
|
||||||
|
shgrp = NULL;
|
||||||
|
if (ma->use_nodes && ma->nodetree) {
|
||||||
|
static int ssr_id;
|
||||||
|
ssr_id = (use_ssr) ? 1 : -1;
|
||||||
|
static float half = 0.5f;
|
||||||
|
static float error_col[3] = {1.0f, 0.0f, 1.0f};
|
||||||
|
static float compile_col[3] = {0.5f, 0.5f, 0.5f};
|
||||||
|
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
|
||||||
|
|
||||||
|
switch (GPU_material_status(gpumat)) {
|
||||||
|
case GPU_MAT_SUCCESS:
|
||||||
|
{
|
||||||
|
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
|
||||||
|
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
|
||||||
|
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
|
||||||
|
|
||||||
|
shgrp = DRW_shgroup_material_particle_hair_create(
|
||||||
|
ob, psys, md,
|
||||||
|
psl->material_pass,
|
||||||
|
gpumat);
|
||||||
|
|
||||||
|
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL,
|
||||||
|
use_diffuse, use_glossy, use_refract, false, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GPU_MAT_QUEUED:
|
||||||
|
{
|
||||||
|
color_p = compile_col;
|
||||||
|
metal_p = spec_p = rough_p = ½
|
||||||
|
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)
|
void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow)
|
||||||
{
|
{
|
||||||
EEVEE_PassList *psl = vedata->psl;
|
EEVEE_PassList *psl = vedata->psl;
|
||||||
@@ -1600,112 +1825,24 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sld
|
|||||||
|
|
||||||
void EEVEE_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow)
|
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();
|
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->type == OB_MESH) {
|
||||||
if (ob != draw_ctx->object_edit) {
|
if (ob != draw_ctx->object_edit) {
|
||||||
for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
|
for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
|
||||||
if (md->type != eModifierType_ParticleSystem) {
|
if (md->type == eModifierType_ParticleSystem) {
|
||||||
continue;
|
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
|
||||||
|
material_particle_hair(vedata, sldata, ob, psys, md);
|
||||||
|
*cast_shadow = true;
|
||||||
}
|
}
|
||||||
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
|
else if (md->type == eModifierType_Hair) {
|
||||||
if (!psys_check_enabled(ob, psys, false)) {
|
HairModifierData *hmd = (HairModifierData *)md;
|
||||||
continue;
|
|
||||||
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,8 @@ out vec3 normal_viewport;
|
|||||||
out vec2 uv_interp;
|
out vec2 uv_interp;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
out int DEBUG;
|
||||||
|
|
||||||
/* From http://libnoise.sourceforge.net/noisegen/index.html */
|
/* From http://libnoise.sourceforge.net/noisegen/index.html */
|
||||||
float integer_noise(int n)
|
float integer_noise(int n)
|
||||||
{
|
{
|
||||||
@@ -34,6 +36,7 @@ float integer_noise(int n)
|
|||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
#ifdef HAIR_SHADER
|
#ifdef HAIR_SHADER
|
||||||
|
DEBUG = hair_get_strand_id();
|
||||||
# ifdef V3D_SHADING_TEXTURE_COLOR
|
# ifdef V3D_SHADING_TEXTURE_COLOR
|
||||||
vec2 uv = hair_get_customdata_vec2(u);
|
vec2 uv = hair_get_customdata_vec2(u);
|
||||||
# endif
|
# endif
|
||||||
|
@@ -633,7 +633,7 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o
|
|||||||
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ?
|
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ?
|
||||||
wpd->prepass_solid_hair_sh :
|
wpd->prepass_solid_hair_sh :
|
||||||
wpd->prepass_texture_hair_sh;
|
wpd->prepass_texture_hair_sh;
|
||||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
|
DRWShadingGroup *shgrp = DRW_shgroup_particle_hair_create(
|
||||||
ob, psys, md,
|
ob, psys, md,
|
||||||
psl->prepass_hair_pass,
|
psl->prepass_hair_pass,
|
||||||
shader);
|
shader);
|
||||||
|
@@ -425,7 +425,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
|
|||||||
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
|
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
|
||||||
? wpd->transparent_accum_hair_sh
|
? wpd->transparent_accum_hair_sh
|
||||||
: wpd->transparent_accum_texture_hair_sh;
|
: wpd->transparent_accum_texture_hair_sh;
|
||||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
|
DRWShadingGroup *shgrp = DRW_shgroup_particle_hair_create(
|
||||||
ob, psys, md,
|
ob, psys, md,
|
||||||
psl->transparent_accum_pass,
|
psl->transparent_accum_pass,
|
||||||
shader);
|
shader);
|
||||||
@@ -444,7 +444,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
|
|||||||
if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
|
if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
|
||||||
DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
|
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,
|
vedata->psl->object_outline_pass,
|
||||||
e_data.object_outline_hair_sh);
|
e_data.object_outline_hair_sh);
|
||||||
DRW_shgroup_uniform_int(shgrp, "object_id", &material->object_id, 1);
|
DRW_shgroup_uniform_int(shgrp, "object_id", &material->object_id, 1);
|
||||||
|
@@ -3366,6 +3366,20 @@ GPUBatch *DRW_cache_particles_get_prim(int type)
|
|||||||
return NULL;
|
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 */
|
/* 3D cursor */
|
||||||
GPUBatch *DRW_cache_cursor_get(bool crosshair_lines)
|
GPUBatch *DRW_cache_cursor_get(bool crosshair_lines)
|
||||||
{
|
{
|
||||||
|
@@ -31,6 +31,8 @@ struct GPUMaterial;
|
|||||||
struct ModifierData;
|
struct ModifierData;
|
||||||
struct Object;
|
struct Object;
|
||||||
struct PTCacheEdit;
|
struct PTCacheEdit;
|
||||||
|
struct HairSystem;
|
||||||
|
struct HairExportCache;
|
||||||
|
|
||||||
void DRW_shape_cache_free(void);
|
void DRW_shape_cache_free(void);
|
||||||
void DRW_shape_cache_reset(void);
|
void DRW_shape_cache_reset(void);
|
||||||
@@ -193,6 +195,10 @@ struct GPUBatch *DRW_cache_particles_get_edit_tip_points(
|
|||||||
struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit);
|
struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit);
|
||||||
struct GPUBatch *DRW_cache_particles_get_prim(int type);
|
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 */
|
/* Metaball */
|
||||||
struct GPUBatch *DRW_cache_mball_surface_get(struct Object *ob);
|
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);
|
struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len);
|
||||||
|
@@ -36,6 +36,8 @@ struct ListBase;
|
|||||||
struct ModifierData;
|
struct ModifierData;
|
||||||
struct ParticleSystem;
|
struct ParticleSystem;
|
||||||
struct PTCacheEdit;
|
struct PTCacheEdit;
|
||||||
|
struct HairSystem;
|
||||||
|
struct HairExportCache;
|
||||||
|
|
||||||
struct Curve;
|
struct Curve;
|
||||||
struct Lattice;
|
struct Lattice;
|
||||||
@@ -62,6 +64,9 @@ void DRW_particle_batch_cache_free(struct ParticleSystem *psys);
|
|||||||
void DRW_gpencil_batch_cache_dirty(struct bGPdata *gpd);
|
void DRW_gpencil_batch_cache_dirty(struct bGPdata *gpd);
|
||||||
void DRW_gpencil_batch_cache_free(struct bGPdata *gpd);
|
void DRW_gpencil_batch_cache_free(struct bGPdata *gpd);
|
||||||
|
|
||||||
|
void DRW_hair_batch_cache_dirty(struct HairSystem *hsys, int mode);
|
||||||
|
void DRW_hair_batch_cache_free(struct HairSystem *hsys);
|
||||||
|
|
||||||
/* Curve */
|
/* 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_wire_edge(struct Curve *cu, struct CurveCache *ob_curve_cache);
|
||||||
struct GPUBatch *DRW_curve_batch_cache_get_normal_edge(
|
struct GPUBatch *DRW_curve_batch_cache_get_normal_edge(
|
||||||
@@ -146,4 +151,8 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(
|
|||||||
struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(
|
struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(
|
||||||
struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit);
|
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__ */
|
#endif /* __DRAW_CACHE_IMPL_H__ */
|
||||||
|
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);
|
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)
|
static void particle_batch_cache_clear(ParticleSystem *psys)
|
||||||
{
|
{
|
||||||
ParticleBatchCache *cache = psys->batch_cache;
|
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(
|
static int particle_batch_cache_fill_segments_indices(
|
||||||
|
const ParticleSystem *psys,
|
||||||
ParticleCacheKey **path_cache,
|
ParticleCacheKey **path_cache,
|
||||||
const int start_index,
|
const int start_index,
|
||||||
const int num_path_keys,
|
const int num_path_keys,
|
||||||
const int res,
|
const int subdiv,
|
||||||
|
const int thickness_res,
|
||||||
GPUIndexBufBuilder *elb)
|
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;
|
int curr_point = start_index;
|
||||||
for (int i = 0; i < num_path_keys; i++) {
|
for (int i = 0; i < num_path_keys; i++) {
|
||||||
ParticleCacheKey *path = path_cache[i];
|
ParticleCacheKey *path = path_cache[i];
|
||||||
if (path->segments <= 0) {
|
if (path->segments <= 0) {
|
||||||
continue;
|
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_generic_vert(elb, curr_point++);
|
||||||
}
|
}
|
||||||
GPU_indexbuf_add_primitive_restart(elb);
|
GPU_indexbuf_add_primitive_restart(elb);
|
||||||
@@ -749,24 +725,93 @@ static int particle_batch_cache_fill_strands_data(
|
|||||||
return curr_point;
|
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(
|
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,
|
ParticleHairCache *cache,
|
||||||
int subdiv)
|
int subdiv)
|
||||||
{
|
{
|
||||||
/* Same format as point_tex. */
|
/* Same format as point_tex. */
|
||||||
GPUVertFormat format = { 0 };
|
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 */
|
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_hair_index_buf, cache->final[subdiv].point_len);
|
||||||
/* Thoses are points! not line segments. */
|
|
||||||
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_buf, cache->final[subdiv].strands_res * cache->strands_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. */
|
/* 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(
|
static void particle_batch_cache_ensure_procedural_strand_data(
|
||||||
@@ -940,9 +985,7 @@ static void particle_batch_cache_ensure_procedural_indices(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int verts_per_hair = cache->final[subdiv].strands_res * thickness_res;
|
int element_count = cache->final[subdiv].elems_len;
|
||||||
/* +1 for primitive restart */
|
|
||||||
int element_count = (verts_per_hair + 1) * cache->strands_len;
|
|
||||||
GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
|
GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
|
||||||
|
|
||||||
static GPUVertFormat format = { 0 };
|
static GPUVertFormat format = { 0 };
|
||||||
@@ -959,7 +1002,7 @@ static void particle_batch_cache_ensure_procedural_indices(
|
|||||||
|
|
||||||
if (edit != NULL && edit->pathcache != NULL) {
|
if (edit != NULL && edit->pathcache != NULL) {
|
||||||
particle_batch_cache_fill_segments_indices(
|
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 {
|
else {
|
||||||
int curr_point = 0;
|
int curr_point = 0;
|
||||||
@@ -967,12 +1010,12 @@ static void particle_batch_cache_ensure_procedural_indices(
|
|||||||
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
|
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
|
||||||
{
|
{
|
||||||
curr_point = particle_batch_cache_fill_segments_indices(
|
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) {
|
if (psys->childcache) {
|
||||||
const int child_count = psys->totchild * psys->part->disp / 100;
|
const int child_count = psys->totchild * psys->part->disp / 100;
|
||||||
curr_point = particle_batch_cache_fill_segments_indices(
|
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;
|
ParticleDrawSource source;
|
||||||
drw_particle_get_hair_source(object, psys, md, NULL, &source);
|
drw_particle_get_hair_source(object, psys, md, NULL, &source);
|
||||||
|
|
||||||
ParticleSettings *part = source.psys->part;
|
|
||||||
ParticleBatchCache *cache = particle_batch_cache_get(source.psys);
|
ParticleBatchCache *cache = particle_batch_cache_get(source.psys);
|
||||||
*r_hair_cache = &cache->hair;
|
*r_hair_cache = &cache->hair;
|
||||||
|
|
||||||
(*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv);
|
|
||||||
|
|
||||||
/* Refreshed on combing and simulation. */
|
/* Refreshed on combing and simulation. */
|
||||||
if ((*r_hair_cache)->proc_point_buf == NULL) {
|
if ((*r_hair_cache)->proc_point_buf == NULL) {
|
||||||
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
|
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. */
|
/* Refreshed only on subdiv count change. */
|
||||||
if ((*r_hair_cache)->final[subdiv].proc_buf == NULL) {
|
if ((*r_hair_cache)->final[subdiv].proc_point_buf == NULL) {
|
||||||
particle_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
|
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;
|
need_ft_update = true;
|
||||||
}
|
}
|
||||||
if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
|
if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
|
||||||
|
@@ -26,11 +26,17 @@
|
|||||||
#ifndef __DRAW_COMMON_H__
|
#ifndef __DRAW_COMMON_H__
|
||||||
#define __DRAW_COMMON_H__
|
#define __DRAW_COMMON_H__
|
||||||
|
|
||||||
|
struct Mesh;
|
||||||
struct DRWPass;
|
struct DRWPass;
|
||||||
struct DRWShadingGroup;
|
struct DRWShadingGroup;
|
||||||
struct GPUBatch;
|
struct GPUBatch;
|
||||||
struct GPUMaterial;
|
struct GPUMaterial;
|
||||||
|
struct GPUShader;
|
||||||
|
struct GPUTexture;
|
||||||
|
struct HairDrawSettings;
|
||||||
|
struct HairSystem;
|
||||||
struct Object;
|
struct Object;
|
||||||
|
struct Scene;
|
||||||
struct ViewLayer;
|
struct ViewLayer;
|
||||||
struct ModifierData;
|
struct ModifierData;
|
||||||
struct ParticleSystem;
|
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.
|
/* This creates a shading group with display hairs.
|
||||||
* The draw call is already added by this function, just add additional uniforms. */
|
* 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 Object *object, struct ParticleSystem *psys, struct ModifierData *md,
|
||||||
struct DRWPass *hair_pass,
|
struct DRWPass *hair_pass,
|
||||||
struct GPUShader *shader);
|
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 Object *object, struct ParticleSystem *psys, struct ModifierData *md,
|
||||||
struct DRWPass *hair_pass,
|
struct DRWPass *hair_pass,
|
||||||
struct GPUMaterial *material);
|
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_init(void);
|
||||||
void DRW_hair_update(void);
|
void DRW_hair_update(void);
|
||||||
void DRW_hair_free(void);
|
void DRW_hair_free(void);
|
||||||
|
@@ -34,12 +34,14 @@
|
|||||||
#include "BLI_utildefines.h"
|
#include "BLI_utildefines.h"
|
||||||
#include "BLI_string_utils.h"
|
#include "BLI_string_utils.h"
|
||||||
|
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
#include "DNA_mesh_types.h"
|
#include "DNA_mesh_types.h"
|
||||||
#include "DNA_meshdata_types.h"
|
#include "DNA_meshdata_types.h"
|
||||||
#include "DNA_modifier_types.h"
|
#include "DNA_modifier_types.h"
|
||||||
#include "DNA_particle_types.h"
|
#include "DNA_particle_types.h"
|
||||||
#include "DNA_customdata_types.h"
|
#include "DNA_customdata_types.h"
|
||||||
|
|
||||||
|
#include "BKE_hair.h"
|
||||||
#include "BKE_mesh.h"
|
#include "BKE_mesh.h"
|
||||||
#include "BKE_particle.h"
|
#include "BKE_particle.h"
|
||||||
#include "BKE_pointcache.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);
|
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,
|
Object *object, ParticleSystem *psys, ModifierData *md,
|
||||||
DRWPass *hair_pass,
|
DRWPass *hair_pass,
|
||||||
struct GPUMaterial *gpu_mat, GPUShader *gpu_shader)
|
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_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_int_copy(shgrp, "hairThicknessRes", thickness_res);
|
||||||
DRW_shgroup_uniform_float(shgrp, "hairRadShape", &part->shape, 1);
|
DRW_shgroup_uniform_float(shgrp, "hairRadShape", &part->shape, 1);
|
||||||
DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", part->rad_root * part->rad_scale * 0.5f);
|
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. */
|
/* Transform Feedback subdiv. */
|
||||||
if (need_ft_update) {
|
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);
|
GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
|
||||||
DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass,
|
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, "hairPointBuffer", hair_cache->point_tex);
|
||||||
DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", hair_cache->strand_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, hair_cache->final[subdiv].point_len, NULL);
|
||||||
DRW_shgroup_call_procedural_points_add(tf_shgrp, final_points_len, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shgrp;
|
return shgrp;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRWShadingGroup *DRW_shgroup_hair_create(
|
DRWShadingGroup *DRW_shgroup_particle_hair_create(
|
||||||
Object *object, ParticleSystem *psys, ModifierData *md,
|
Object *object, ParticleSystem *psys, ModifierData *md,
|
||||||
DRWPass *hair_pass,
|
DRWPass *hair_pass,
|
||||||
GPUShader *shader)
|
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,
|
Object *object, ParticleSystem *psys, ModifierData *md,
|
||||||
DRWPass *hair_pass,
|
DRWPass *hair_pass,
|
||||||
struct GPUMaterial *material)
|
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)
|
void DRW_hair_update(void)
|
||||||
|
@@ -39,16 +39,22 @@ struct Object;
|
|||||||
struct ParticleSystem;
|
struct ParticleSystem;
|
||||||
struct ModifierData;
|
struct ModifierData;
|
||||||
struct ParticleHairCache;
|
struct ParticleHairCache;
|
||||||
|
struct HairSystem;
|
||||||
|
|
||||||
typedef struct ParticleHairFinalCache {
|
typedef struct ParticleHairFinalCache {
|
||||||
/* Output of the subdivision stage: vertex buff sized to subdiv level. */
|
/* Output of the subdivision stage: vertex buff sized to subdiv level. */
|
||||||
GPUVertBuf *proc_buf;
|
GPUVertBuf *proc_point_buf;
|
||||||
GPUTexture *proc_tex;
|
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];
|
GPUBatch *proc_hairs[MAX_THICKRES];
|
||||||
|
|
||||||
int strands_res; /* points per hair, at least 2 */
|
int strands_len;
|
||||||
|
int elems_len;
|
||||||
|
int point_len;
|
||||||
} ParticleHairFinalCache;
|
} ParticleHairFinalCache;
|
||||||
|
|
||||||
typedef struct ParticleHairCache {
|
typedef struct ParticleHairCache {
|
||||||
@@ -81,6 +87,8 @@ typedef struct ParticleHairCache {
|
|||||||
int point_len;
|
int point_len;
|
||||||
} ParticleHairCache;
|
} ParticleHairCache;
|
||||||
|
|
||||||
|
void particle_batch_cache_clear_hair(struct ParticleHairCache *hair_cache);
|
||||||
|
|
||||||
bool particles_ensure_procedural_data(
|
bool particles_ensure_procedural_data(
|
||||||
struct Object *object,
|
struct Object *object,
|
||||||
struct ParticleSystem *psys,
|
struct ParticleSystem *psys,
|
||||||
@@ -89,4 +97,12 @@ bool particles_ensure_procedural_data(
|
|||||||
int subdiv,
|
int subdiv,
|
||||||
int thickness_res);
|
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__ */
|
#endif /* __DRAW_HAIR_PRIVATE_H__ */
|
||||||
|
@@ -2464,6 +2464,9 @@ void DRW_engines_register(void)
|
|||||||
/* BKE: gpencil.c */
|
/* BKE: gpencil.c */
|
||||||
extern void *BKE_gpencil_batch_cache_dirty_cb;
|
extern void *BKE_gpencil_batch_cache_dirty_cb;
|
||||||
extern void *BKE_gpencil_batch_cache_free_cb;
|
extern void *BKE_gpencil_batch_cache_free_cb;
|
||||||
|
/* BKE: hair.c */
|
||||||
|
extern void *BKE_hair_batch_cache_dirty_cb;
|
||||||
|
extern void *BKE_hair_batch_cache_free_cb;
|
||||||
|
|
||||||
BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty;
|
BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty;
|
||||||
BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
|
BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
|
||||||
@@ -2482,6 +2485,9 @@ void DRW_engines_register(void)
|
|||||||
|
|
||||||
BKE_gpencil_batch_cache_dirty_cb = DRW_gpencil_batch_cache_dirty;
|
BKE_gpencil_batch_cache_dirty_cb = DRW_gpencil_batch_cache_dirty;
|
||||||
BKE_gpencil_batch_cache_free_cb = DRW_gpencil_batch_cache_free;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -114,6 +114,7 @@ typedef struct OBJECT_PassList {
|
|||||||
struct DRWPass *bone_envelope;
|
struct DRWPass *bone_envelope;
|
||||||
struct DRWPass *bone_axes;
|
struct DRWPass *bone_axes;
|
||||||
struct DRWPass *particle;
|
struct DRWPass *particle;
|
||||||
|
struct DRWPass *hair;
|
||||||
struct DRWPass *lightprobes;
|
struct DRWPass *lightprobes;
|
||||||
/* use for empty/background images */
|
/* use for empty/background images */
|
||||||
struct DRWPass *reference_image;
|
struct DRWPass *reference_image;
|
||||||
@@ -247,6 +248,9 @@ typedef struct OBJECT_PrivateData {
|
|||||||
/* Texture Space */
|
/* Texture Space */
|
||||||
DRWShadingGroup *texspace;
|
DRWShadingGroup *texspace;
|
||||||
|
|
||||||
|
/* Hair Systems */
|
||||||
|
DRWShadingGroup *hair_verts;
|
||||||
|
|
||||||
/* Outlines id offset */
|
/* Outlines id offset */
|
||||||
int id_ofs_active;
|
int id_ofs_active;
|
||||||
int id_ofs_select;
|
int id_ofs_select;
|
||||||
@@ -1350,6 +1354,20 @@ static void OBJECT_cache_init(void *vedata)
|
|||||||
DRW_STATE_POINT | DRW_STATE_BLEND);
|
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 */
|
/* Empty/Background Image Pass */
|
||||||
psl->reference_image = DRW_pass_create(
|
psl->reference_image = DRW_pass_create(
|
||||||
@@ -2623,6 +2641,7 @@ static void OBJECT_draw_scene(void *vedata)
|
|||||||
DRW_draw_pass(psl->lightprobes);
|
DRW_draw_pass(psl->lightprobes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DRW_draw_pass(psl->hair);
|
||||||
DRW_draw_pass(psl->ob_center);
|
DRW_draw_pass(psl->ob_center);
|
||||||
|
|
||||||
if (DRW_state_is_fbo()) {
|
if (DRW_state_is_fbo()) {
|
||||||
|
@@ -5,13 +5,6 @@
|
|||||||
* of data the CPU has to precompute and transfert for each update.
|
* 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.
|
* hairThicknessRes : Subdiv around the hair.
|
||||||
* 1 - Wire Hair: Only one pixel thick, independant of view distance.
|
* 1 - Wire Hair: Only one pixel thick, independant of view distance.
|
||||||
@@ -33,6 +26,7 @@ uniform samplerBuffer hairPointBuffer; /* RGBA32F */
|
|||||||
|
|
||||||
/* -- Per strands data -- */
|
/* -- Per strands data -- */
|
||||||
uniform usamplerBuffer hairStrandBuffer; /* R32UI */
|
uniform usamplerBuffer hairStrandBuffer; /* R32UI */
|
||||||
|
uniform usamplerBuffer hairIndexBuffer; /* R32UI */
|
||||||
|
|
||||||
/* Not used, use one buffer per uv layer */
|
/* Not used, use one buffer per uv layer */
|
||||||
//uniform samplerBuffer hairUVBuffer; /* RG32F */
|
//uniform samplerBuffer hairUVBuffer; /* RG32F */
|
||||||
@@ -49,6 +43,13 @@ void unpack_strand_data(uint data, out int strand_offset, out int strand_segment
|
|||||||
#endif
|
#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 -- */
|
/* -- Subdivision stage -- */
|
||||||
/**
|
/**
|
||||||
* We use a transform feedback to preprocess the strands and add more subdivision to it.
|
* 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
|
#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 time_per_strand_seg = 1.0 / float(strand_segments);
|
||||||
|
|
||||||
float ratio = local_time / time_per_strand_seg;
|
float ratio = local_time / time_per_strand_seg;
|
||||||
|
interp_segment = int(ratio);
|
||||||
interp_time = fract(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)
|
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 strand_index = hair_get_strand_id();
|
||||||
|
uint strand_data = texelFetch(hairStrandBuffer, strand_index).x;
|
||||||
int hair_id = gl_VertexID / hairStrandsRes;
|
|
||||||
uint strand_data = texelFetch(hairStrandBuffer, hair_id).x;
|
|
||||||
|
|
||||||
int strand_offset, strand_segments;
|
int strand_offset, strand_segments;
|
||||||
unpack_strand_data(strand_data, 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);
|
if (interp_segment <= 0) {
|
||||||
data1 = texelFetch(hairPointBuffer, ofs_id);
|
|
||||||
data2 = texelFetch(hairPointBuffer, ofs_id + 1);
|
|
||||||
data3 = texelFetch(hairPointBuffer, ofs_id + 2);
|
|
||||||
|
|
||||||
if (id <= 0) {
|
|
||||||
/* root points. Need to reconstruct previous data. */
|
/* root points. Need to reconstruct previous data. */
|
||||||
data0 = data1 * 2.0 - data2;
|
data0 = data1 * 2.0 - data2;
|
||||||
}
|
}
|
||||||
if (id + 1 >= strand_segments) {
|
if (interp_segment + 1 >= strand_segments) {
|
||||||
/* tip points. Need to reconstruct next data. */
|
/* tip points. Need to reconstruct next data. */
|
||||||
data3 = data2 * 2.0 - data1;
|
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
|
#ifndef HAIR_PHASE_SUBDIV
|
||||||
int hair_get_strand_id(void)
|
|
||||||
{
|
|
||||||
return gl_VertexID / (hairStrandsRes * hairThicknessRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hair_get_base_id(void)
|
int hair_get_base_id(void)
|
||||||
{
|
{
|
||||||
return gl_VertexID / hairThicknessRes;
|
return gl_VertexID / hairThicknessRes;
|
||||||
@@ -165,25 +161,25 @@ void hair_get_pos_tan_binor_time(
|
|||||||
|
|
||||||
vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
|
vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
|
||||||
{
|
{
|
||||||
int id = hair_get_strand_id();
|
int strand_index = hair_get_strand_id();
|
||||||
return texelFetch(cd_buf, id).rg;
|
return texelFetch(cd_buf, strand_index).rg;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 hair_get_customdata_vec3(const samplerBuffer cd_buf)
|
vec3 hair_get_customdata_vec3(const samplerBuffer cd_buf)
|
||||||
{
|
{
|
||||||
int id = hair_get_strand_id();
|
int strand_index = hair_get_strand_id();
|
||||||
return texelFetch(cd_buf, id).rgb;
|
return texelFetch(cd_buf, strand_index).rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 hair_get_customdata_vec4(const samplerBuffer cd_buf)
|
vec4 hair_get_customdata_vec4(const samplerBuffer cd_buf)
|
||||||
{
|
{
|
||||||
int id = hair_get_strand_id();
|
int strand_index = hair_get_strand_id();
|
||||||
return texelFetch(cd_buf, id).rgba;
|
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;
|
return texelFetch(hairPointBuffer, id).point_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -175,6 +175,7 @@ void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot);
|
|||||||
void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
|
void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
|
||||||
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
|
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
|
||||||
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
|
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
|
||||||
|
void OBJECT_OT_hair_generate_follicles(struct wmOperatorType *ot);
|
||||||
|
|
||||||
/* grease pencil modifiers */
|
/* grease pencil modifiers */
|
||||||
void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot);
|
void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot);
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
#include "DNA_anim_types.h"
|
#include "DNA_anim_types.h"
|
||||||
#include "DNA_armature_types.h"
|
#include "DNA_armature_types.h"
|
||||||
#include "DNA_curve_types.h"
|
#include "DNA_curve_types.h"
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
#include "DNA_key_types.h"
|
#include "DNA_key_types.h"
|
||||||
#include "DNA_mesh_types.h"
|
#include "DNA_mesh_types.h"
|
||||||
#include "DNA_meshdata_types.h"
|
#include "DNA_meshdata_types.h"
|
||||||
@@ -58,12 +59,14 @@
|
|||||||
#include "BKE_DerivedMesh.h"
|
#include "BKE_DerivedMesh.h"
|
||||||
#include "BKE_effect.h"
|
#include "BKE_effect.h"
|
||||||
#include "BKE_global.h"
|
#include "BKE_global.h"
|
||||||
|
#include "BKE_hair.h"
|
||||||
#include "BKE_key.h"
|
#include "BKE_key.h"
|
||||||
#include "BKE_lattice.h"
|
#include "BKE_lattice.h"
|
||||||
#include "BKE_main.h"
|
#include "BKE_main.h"
|
||||||
#include "BKE_mesh.h"
|
#include "BKE_mesh.h"
|
||||||
#include "BKE_mesh_mapping.h"
|
#include "BKE_mesh_mapping.h"
|
||||||
#include "BKE_mesh_runtime.h"
|
#include "BKE_mesh_runtime.h"
|
||||||
|
#include "BKE_mesh_sample.h"
|
||||||
#include "BKE_modifier.h"
|
#include "BKE_modifier.h"
|
||||||
#include "BKE_multires.h"
|
#include "BKE_multires.h"
|
||||||
#include "BKE_report.h"
|
#include "BKE_report.h"
|
||||||
@@ -2417,3 +2420,88 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
|
|||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||||
edit_modifier_properties(ot);
|
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);
|
||||||
|
}
|
||||||
|
@@ -263,6 +263,7 @@ void ED_operatortypes_object(void)
|
|||||||
WM_operatortype_append(OBJECT_OT_data_transfer);
|
WM_operatortype_append(OBJECT_OT_data_transfer);
|
||||||
WM_operatortype_append(OBJECT_OT_datalayout_transfer);
|
WM_operatortype_append(OBJECT_OT_datalayout_transfer);
|
||||||
WM_operatortype_append(OBJECT_OT_surfacedeform_bind);
|
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_clear);
|
||||||
WM_operatortype_append(OBJECT_OT_hide_view_set);
|
WM_operatortype_append(OBJECT_OT_hide_view_set);
|
||||||
|
@@ -1039,6 +1039,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
|
|||||||
case eModifierType_WeightedNormal:
|
case eModifierType_WeightedNormal:
|
||||||
data.icon = ICON_MOD_NORMALEDIT;
|
data.icon = ICON_MOD_NORMALEDIT;
|
||||||
break;
|
break;
|
||||||
|
case eModifierType_Hair:
|
||||||
|
data.icon = ICON_STRANDS;
|
||||||
|
break;
|
||||||
/* Default */
|
/* Default */
|
||||||
case eModifierType_None:
|
case eModifierType_None:
|
||||||
case eModifierType_ShapeKey:
|
case eModifierType_ShapeKey:
|
||||||
|
@@ -107,10 +107,10 @@ typedef enum GPUTextureFormat {
|
|||||||
|
|
||||||
/* Texture only format */
|
/* Texture only format */
|
||||||
GPU_RGB16F,
|
GPU_RGB16F,
|
||||||
|
GPU_RGB32F,
|
||||||
#if 0
|
#if 0
|
||||||
GPU_RGBA16_SNORM,
|
GPU_RGBA16_SNORM,
|
||||||
GPU_RGBA8_SNORM,
|
GPU_RGBA8_SNORM,
|
||||||
GPU_RGB32F,
|
|
||||||
GPU_RGB32I,
|
GPU_RGB32I,
|
||||||
GPU_RGB32UI,
|
GPU_RGB32UI,
|
||||||
GPU_RGB16_SNORM,
|
GPU_RGB16_SNORM,
|
||||||
|
136
source/blender/makesdna/DNA_hair_types.h
Normal file
136
source/blender/makesdna/DNA_hair_types.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contributor(s): Blender Foundation
|
||||||
|
*
|
||||||
|
* ***** END GPL LICENSE BLOCK *****
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file DNA_hair_types.h
|
||||||
|
* \ingroup DNA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DNA_HAIR_TYPES_H__
|
||||||
|
#define __DNA_HAIR_TYPES_H__
|
||||||
|
|
||||||
|
#include "DNA_defs.h"
|
||||||
|
#include "DNA_listBase.h"
|
||||||
|
#include "DNA_ID.h"
|
||||||
|
#include "DNA_meshdata_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Root point (follicle) of a hair on a surface */
|
||||||
|
typedef struct HairFollicle {
|
||||||
|
MeshSample mesh_sample; /* Sample on the scalp mesh for the root vertex */
|
||||||
|
unsigned int curve; /* Index of the curve used by the fiber */
|
||||||
|
int pad;
|
||||||
|
} HairFollicle;
|
||||||
|
|
||||||
|
/* Collection of hair roots on a surface */
|
||||||
|
typedef struct HairPattern {
|
||||||
|
struct HairFollicle *follicles;
|
||||||
|
int num_follicles;
|
||||||
|
int pad;
|
||||||
|
} HairPattern;
|
||||||
|
|
||||||
|
typedef struct HairFiberCurve {
|
||||||
|
int vertstart; /* Offset in the vertex array where the curve starts */
|
||||||
|
int numverts; /* Number of vertices in the curve */
|
||||||
|
|
||||||
|
/* Shape */
|
||||||
|
float taper_length; /* Distance at which final thickness is reached */
|
||||||
|
float taper_thickness; /* Relative thickness of the strand */
|
||||||
|
} HairFiberCurve;
|
||||||
|
|
||||||
|
typedef struct HairFiberVertex {
|
||||||
|
int flag;
|
||||||
|
float co[3];
|
||||||
|
} HairFiberVertex;
|
||||||
|
|
||||||
|
/* Hair curve data */
|
||||||
|
typedef struct HairCurveData
|
||||||
|
{
|
||||||
|
/* Curves for shaping hair fibers */
|
||||||
|
struct HairFiberCurve *curves;
|
||||||
|
/* Control vertices on curves */
|
||||||
|
struct HairFiberVertex *verts;
|
||||||
|
/* Number of curves */
|
||||||
|
int totcurves;
|
||||||
|
/* Number of curve vertices */
|
||||||
|
int totverts;
|
||||||
|
} HairCurveData;
|
||||||
|
|
||||||
|
typedef struct HairSystem {
|
||||||
|
int flag;
|
||||||
|
int pad;
|
||||||
|
|
||||||
|
/* Set of hair follicles on the scalp mesh */
|
||||||
|
struct HairPattern *pattern;
|
||||||
|
|
||||||
|
/* Curve data */
|
||||||
|
HairCurveData curve_data;
|
||||||
|
|
||||||
|
/* Data buffers for drawing */
|
||||||
|
void *draw_batch_cache;
|
||||||
|
/* Texture buffer for drawing */
|
||||||
|
void *draw_texture_cache;
|
||||||
|
} HairSystem;
|
||||||
|
|
||||||
|
typedef enum eHairSystemFlag
|
||||||
|
{
|
||||||
|
/* Curve positions have changed, rebind hair follicles */
|
||||||
|
HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING = (1 << 8),
|
||||||
|
} eHairSystemFlag;
|
||||||
|
|
||||||
|
typedef struct HairDrawSettings
|
||||||
|
{
|
||||||
|
short follicle_mode;
|
||||||
|
short fiber_mode;
|
||||||
|
short shape_flag;
|
||||||
|
short pad;
|
||||||
|
|
||||||
|
float shape;
|
||||||
|
float root_radius;
|
||||||
|
float tip_radius;
|
||||||
|
float radius_scale;
|
||||||
|
} HairDrawSettings;
|
||||||
|
|
||||||
|
typedef enum eHairDrawFollicleMode
|
||||||
|
{
|
||||||
|
HAIR_DRAW_FOLLICLE_NONE = 0,
|
||||||
|
HAIR_DRAW_FOLLICLE_POINTS = 1,
|
||||||
|
} eHairDrawFollicleMode;
|
||||||
|
|
||||||
|
typedef enum eHairDrawFiberMode
|
||||||
|
{
|
||||||
|
HAIR_DRAW_FIBER_NONE = 0,
|
||||||
|
HAIR_DRAW_FIBER_CURVES = 1,
|
||||||
|
} eHairDrawFiberMode;
|
||||||
|
|
||||||
|
typedef enum eHairDrawShapeFlag {
|
||||||
|
HAIR_DRAW_CLOSE_TIP = (1<<0),
|
||||||
|
} eHairDrawShapeFlag;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __DNA_HAIR_TYPES_H__ */
|
@@ -380,6 +380,13 @@ enum {
|
|||||||
FREESTYLE_FACE_MARK = 1,
|
FREESTYLE_FACE_MARK = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct MeshSample {
|
||||||
|
unsigned int orig_verts[3];
|
||||||
|
float orig_weights[3]; /* also used as volume sample location */
|
||||||
|
unsigned int orig_poly;
|
||||||
|
unsigned int orig_loops[3];
|
||||||
|
} MeshSample;
|
||||||
|
|
||||||
/* mvert->flag */
|
/* mvert->flag */
|
||||||
enum {
|
enum {
|
||||||
/* SELECT = (1 << 0), */
|
/* SELECT = (1 << 0), */
|
||||||
|
@@ -91,6 +91,7 @@ typedef enum ModifierType {
|
|||||||
eModifierType_MeshSequenceCache = 52,
|
eModifierType_MeshSequenceCache = 52,
|
||||||
eModifierType_SurfaceDeform = 53,
|
eModifierType_SurfaceDeform = 53,
|
||||||
eModifierType_WeightedNormal = 54,
|
eModifierType_WeightedNormal = 54,
|
||||||
|
eModifierType_Hair = 55,
|
||||||
NUM_MODIFIER_TYPES
|
NUM_MODIFIER_TYPES
|
||||||
} ModifierType;
|
} ModifierType;
|
||||||
|
|
||||||
@@ -1692,4 +1693,32 @@ enum {
|
|||||||
#define MOD_MESHSEQ_READ_ALL \
|
#define MOD_MESHSEQ_READ_ALL \
|
||||||
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
|
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
|
||||||
|
|
||||||
|
/* Hair modifier */
|
||||||
|
typedef struct HairModifierFiberCurve {
|
||||||
|
struct HairModifierFiberCurve *next, *prev;
|
||||||
|
|
||||||
|
/* Index for the mesh sample buffer */
|
||||||
|
int mesh_sample_index;
|
||||||
|
/* Number of vertices in the curve */
|
||||||
|
int numverts;
|
||||||
|
/* Vertex array */
|
||||||
|
struct HairFiberVertex *verts;
|
||||||
|
} HairModifierFiberCurve;
|
||||||
|
|
||||||
|
typedef struct HairModifierData {
|
||||||
|
ModifierData modifier;
|
||||||
|
|
||||||
|
int flag;
|
||||||
|
int pad;
|
||||||
|
|
||||||
|
struct HairSystem *hair_system;
|
||||||
|
struct HairDrawSettings *draw_settings;
|
||||||
|
|
||||||
|
/* Follicle distribution parameters */
|
||||||
|
int follicle_seed;
|
||||||
|
int follicle_count;
|
||||||
|
|
||||||
|
ListBase fiber_curves;
|
||||||
|
} HairModifierData;
|
||||||
|
|
||||||
#endif /* __DNA_MODIFIER_TYPES_H__ */
|
#endif /* __DNA_MODIFIER_TYPES_H__ */
|
||||||
|
@@ -131,6 +131,7 @@ static const char *includefiles[] = {
|
|||||||
"DNA_layer_types.h",
|
"DNA_layer_types.h",
|
||||||
"DNA_workspace_types.h",
|
"DNA_workspace_types.h",
|
||||||
"DNA_lightprobe_types.h",
|
"DNA_lightprobe_types.h",
|
||||||
|
"DNA_hair_types.h",
|
||||||
|
|
||||||
/* see comment above before editing! */
|
/* see comment above before editing! */
|
||||||
|
|
||||||
@@ -1357,5 +1358,6 @@ int main(int argc, char **argv)
|
|||||||
#include "DNA_layer_types.h"
|
#include "DNA_layer_types.h"
|
||||||
#include "DNA_workspace_types.h"
|
#include "DNA_workspace_types.h"
|
||||||
#include "DNA_lightprobe_types.h"
|
#include "DNA_lightprobe_types.h"
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
|
|
||||||
/* end of list */
|
/* end of list */
|
||||||
|
@@ -277,6 +277,9 @@ extern StructRNA RNA_GPencilSculptBrush;
|
|||||||
extern StructRNA RNA_GaussianBlurSequence;
|
extern StructRNA RNA_GaussianBlurSequence;
|
||||||
extern StructRNA RNA_GlowSequence;
|
extern StructRNA RNA_GlowSequence;
|
||||||
extern StructRNA RNA_GreasePencil;
|
extern StructRNA RNA_GreasePencil;
|
||||||
|
extern StructRNA RNA_HairGroup;
|
||||||
|
extern StructRNA RNA_HairModifier;
|
||||||
|
extern StructRNA RNA_HairPattern;
|
||||||
extern StructRNA RNA_Header;
|
extern StructRNA RNA_Header;
|
||||||
extern StructRNA RNA_HemiLight;
|
extern StructRNA RNA_HemiLight;
|
||||||
extern StructRNA RNA_Histogram;
|
extern StructRNA RNA_Histogram;
|
||||||
|
@@ -52,6 +52,7 @@ set(DEFSRC
|
|||||||
rna_gpencil_modifier.c
|
rna_gpencil_modifier.c
|
||||||
rna_shader_fx.c
|
rna_shader_fx.c
|
||||||
rna_group.c
|
rna_group.c
|
||||||
|
rna_hair.c
|
||||||
rna_image.c
|
rna_image.c
|
||||||
rna_key.c
|
rna_key.c
|
||||||
rna_lamp.c
|
rna_lamp.c
|
||||||
@@ -62,6 +63,7 @@ set(DEFSRC
|
|||||||
rna_mask.c
|
rna_mask.c
|
||||||
rna_material.c
|
rna_material.c
|
||||||
rna_mesh.c
|
rna_mesh.c
|
||||||
|
rna_mesh_sample.c
|
||||||
rna_meta.c
|
rna_meta.c
|
||||||
rna_modifier.c
|
rna_modifier.c
|
||||||
rna_movieclip.c
|
rna_movieclip.c
|
||||||
|
@@ -3405,6 +3405,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
|
|||||||
{"rna_fluidsim.c", NULL, RNA_def_fluidsim},
|
{"rna_fluidsim.c", NULL, RNA_def_fluidsim},
|
||||||
{"rna_gpencil.c", NULL, RNA_def_gpencil},
|
{"rna_gpencil.c", NULL, RNA_def_gpencil},
|
||||||
{"rna_group.c", NULL, RNA_def_collections},
|
{"rna_group.c", NULL, RNA_def_collections},
|
||||||
|
{"rna_hair.c", NULL, RNA_def_hair},
|
||||||
{"rna_image.c", "rna_image_api.c", RNA_def_image},
|
{"rna_image.c", "rna_image_api.c", RNA_def_image},
|
||||||
{"rna_key.c", NULL, RNA_def_key},
|
{"rna_key.c", NULL, RNA_def_key},
|
||||||
{"rna_lamp.c", NULL, RNA_def_light},
|
{"rna_lamp.c", NULL, RNA_def_light},
|
||||||
@@ -3414,6 +3415,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
|
|||||||
{"rna_main.c", "rna_main_api.c", RNA_def_main},
|
{"rna_main.c", "rna_main_api.c", RNA_def_main},
|
||||||
{"rna_material.c", "rna_material_api.c", RNA_def_material},
|
{"rna_material.c", "rna_material_api.c", RNA_def_material},
|
||||||
{"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh},
|
{"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh},
|
||||||
|
{"rna_mesh_sample.c", NULL, RNA_def_mesh_sample},
|
||||||
{"rna_meta.c", "rna_meta_api.c", RNA_def_meta},
|
{"rna_meta.c", "rna_meta_api.c", RNA_def_meta},
|
||||||
{"rna_modifier.c", NULL, RNA_def_modifier},
|
{"rna_modifier.c", NULL, RNA_def_modifier},
|
||||||
{"rna_gpencil_modifier.c", NULL, RNA_def_greasepencil_modifier},
|
{"rna_gpencil_modifier.c", NULL, RNA_def_greasepencil_modifier},
|
||||||
|
222
source/blender/makesrna/intern/rna_hair.c
Normal file
222
source/blender/makesrna/intern/rna_hair.c
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contributor(s): Blender Foundation.
|
||||||
|
*
|
||||||
|
* ***** END GPL LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file blender/makesrna/intern/rna_hair.c
|
||||||
|
* \ingroup RNA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "RNA_define.h"
|
||||||
|
#include "RNA_enum_types.h"
|
||||||
|
|
||||||
|
#include "rna_internal.h"
|
||||||
|
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
|
|
||||||
|
#include "WM_types.h"
|
||||||
|
|
||||||
|
#ifdef RNA_RUNTIME
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_listbase.h"
|
||||||
|
|
||||||
|
#include "DNA_mesh_types.h"
|
||||||
|
#include "DNA_object_types.h"
|
||||||
|
|
||||||
|
#include "BKE_context.h"
|
||||||
|
#include "BKE_hair.h"
|
||||||
|
#include "BKE_main.h"
|
||||||
|
|
||||||
|
#include "DEG_depsgraph.h"
|
||||||
|
#include "DEG_depsgraph_query.h"
|
||||||
|
|
||||||
|
#include "RNA_access.h"
|
||||||
|
|
||||||
|
#include "WM_api.h"
|
||||||
|
#include "WM_types.h"
|
||||||
|
|
||||||
|
static void UNUSED_FUNCTION(rna_HairSystem_update)(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_HairDrawSettings_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
/* XXX Only need to update render engines
|
||||||
|
* However, that requires finding all hair systems using these draw settings,
|
||||||
|
* then flagging the cache as dirty.
|
||||||
|
*/
|
||||||
|
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
|
||||||
|
#else
|
||||||
|
DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
|
||||||
|
#endif
|
||||||
|
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ptr->id.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_HairSystem_generate_follicles(
|
||||||
|
HairSystem *hsys,
|
||||||
|
struct bContext *C,
|
||||||
|
Object *scalp,
|
||||||
|
int seed,
|
||||||
|
int count)
|
||||||
|
{
|
||||||
|
if (!scalp)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Depsgraph *depsgraph = CTX_data_depsgraph(C);
|
||||||
|
|
||||||
|
BLI_assert(scalp && scalp->type == OB_MESH);
|
||||||
|
Mesh *scalp_mesh = (Mesh *)DEG_get_evaluated_id(depsgraph, scalp->data);
|
||||||
|
|
||||||
|
BKE_hair_generate_follicles(hsys, scalp_mesh, (unsigned int)seed, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void rna_def_hair_follicle(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "HairFollicle", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair Follicle", "Single follicle on a surface");
|
||||||
|
RNA_def_struct_sdna(srna, "HairFollicle");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "mesh_sample", PROP_POINTER, PROP_NONE);
|
||||||
|
RNA_def_property_struct_type(prop, "MeshSample");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_def_hair_pattern(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "HairPattern", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair Pattern", "Set of hair follicles distributed on a surface");
|
||||||
|
RNA_def_struct_sdna(srna, "HairPattern");
|
||||||
|
RNA_def_struct_ui_icon(srna, ICON_STRANDS);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "follicles", PROP_COLLECTION, PROP_NONE);
|
||||||
|
RNA_def_property_collection_sdna(prop, NULL, "follicles", "num_follicles");
|
||||||
|
RNA_def_property_struct_type(prop, "HairFollicle");
|
||||||
|
RNA_def_property_ui_text(prop, "Follicles", "Hair fiber follicles");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_def_hair_system(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
FunctionRNA *func;
|
||||||
|
PropertyRNA *prop, *parm;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "HairSystem", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair System", "Hair rendering and deformation data");
|
||||||
|
RNA_def_struct_sdna(srna, "HairSystem");
|
||||||
|
RNA_def_struct_ui_icon(srna, ICON_STRANDS);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "pattern", PROP_POINTER, PROP_NONE);
|
||||||
|
RNA_def_property_struct_type(prop, "HairPattern");
|
||||||
|
RNA_def_property_ui_text(prop, "Pattern", "Hair pattern");
|
||||||
|
|
||||||
|
func = RNA_def_function(srna, "generate_follicles", "rna_HairSystem_generate_follicles");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||||
|
parm = RNA_def_pointer(func, "scalp", "Object", "Scalp", "Scalp object on which to place hair follicles");
|
||||||
|
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||||
|
parm = RNA_def_int(func, "seed", 0, 0, INT_MAX, "Seed", "Seed value for random numbers", 0, INT_MAX);
|
||||||
|
parm = RNA_def_int(func, "count", 0, 0, INT_MAX, "Count", "Maximum number of follicles to generate", 1, 1e5);
|
||||||
|
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_def_hair_draw_settings(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
static const EnumPropertyItem follicle_mode_items[] = {
|
||||||
|
{HAIR_DRAW_FOLLICLE_NONE, "NONE", 0, "None", ""},
|
||||||
|
{HAIR_DRAW_FOLLICLE_POINTS, "POINTS", 0, "Points", "Draw a point for each follicle"},
|
||||||
|
{0, NULL, 0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const EnumPropertyItem fiber_mode_items[] = {
|
||||||
|
{HAIR_DRAW_FIBER_NONE, "NONE", 0, "None", ""},
|
||||||
|
{HAIR_DRAW_FIBER_CURVES, "CURVES", 0, "Curves", "Draw fiber curves"},
|
||||||
|
{0, NULL, 0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "HairDrawSettings", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair Draw Settings", "Settings for drawing hair systems");
|
||||||
|
RNA_def_struct_sdna(srna, "HairDrawSettings");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "follicle_mode", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_items(prop, follicle_mode_items);
|
||||||
|
RNA_def_property_ui_text(prop, "Follicle Mode", "Draw follicles on the scalp surface");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "fiber_mode", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_items(prop, fiber_mode_items);
|
||||||
|
RNA_def_property_ui_text(prop, "Fiber Mode", "Draw fiber curves");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
|
||||||
|
|
||||||
|
/* hair shape */
|
||||||
|
prop = RNA_def_property(srna, "use_close_tip", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "shape_flag", HAIR_DRAW_CLOSE_TIP);
|
||||||
|
RNA_def_property_ui_text(prop, "Close Tip", "Set tip radius to zero");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "shape", PROP_FLOAT, PROP_FACTOR);
|
||||||
|
RNA_def_property_range(prop, -1.0f, 1.0f);
|
||||||
|
RNA_def_property_ui_text(prop, "Shape", "Strand shape parameter");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "root_radius", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||||
|
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2);
|
||||||
|
RNA_def_property_ui_text(prop, "Root", "Strand width at the root");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "tip_radius", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||||
|
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2);
|
||||||
|
RNA_def_property_ui_text(prop, "Tip", "Strand width at the tip");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "radius_scale", PROP_FLOAT, PROP_NONE);
|
||||||
|
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||||
|
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2);
|
||||||
|
RNA_def_property_ui_text(prop, "Scaling", "Multiplier of radius properties");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RNA_def_hair(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
rna_def_hair_follicle(brna);
|
||||||
|
rna_def_hair_pattern(brna);
|
||||||
|
rna_def_hair_system(brna);
|
||||||
|
rna_def_hair_draw_settings(brna);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -163,6 +163,7 @@ void RNA_def_linestyle(struct BlenderRNA *brna);
|
|||||||
void RNA_def_main(struct BlenderRNA *brna);
|
void RNA_def_main(struct BlenderRNA *brna);
|
||||||
void RNA_def_material(struct BlenderRNA *brna);
|
void RNA_def_material(struct BlenderRNA *brna);
|
||||||
void RNA_def_mesh(struct BlenderRNA *brna);
|
void RNA_def_mesh(struct BlenderRNA *brna);
|
||||||
|
void RNA_def_mesh_sample(struct BlenderRNA *brna);
|
||||||
void RNA_def_meta(struct BlenderRNA *brna);
|
void RNA_def_meta(struct BlenderRNA *brna);
|
||||||
void RNA_def_modifier(struct BlenderRNA *brna);
|
void RNA_def_modifier(struct BlenderRNA *brna);
|
||||||
void RNA_def_nla(struct BlenderRNA *brna);
|
void RNA_def_nla(struct BlenderRNA *brna);
|
||||||
@@ -201,6 +202,7 @@ void RNA_def_world(struct BlenderRNA *brna);
|
|||||||
void RNA_def_movieclip(struct BlenderRNA *brna);
|
void RNA_def_movieclip(struct BlenderRNA *brna);
|
||||||
void RNA_def_tracking(struct BlenderRNA *brna);
|
void RNA_def_tracking(struct BlenderRNA *brna);
|
||||||
void RNA_def_mask(struct BlenderRNA *brna);
|
void RNA_def_mask(struct BlenderRNA *brna);
|
||||||
|
void RNA_def_hair(struct BlenderRNA *brna);
|
||||||
|
|
||||||
/* Common Define functions */
|
/* Common Define functions */
|
||||||
|
|
||||||
|
73
source/blender/makesrna/intern/rna_mesh_sample.c
Normal file
73
source/blender/makesrna/intern/rna_mesh_sample.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* ***** END GPL LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file blender/makesrna/intern/rna_mesh_sample.c
|
||||||
|
* \ingroup RNA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "DNA_meshdata_types.h"
|
||||||
|
|
||||||
|
#include "BLI_utildefines.h"
|
||||||
|
|
||||||
|
#include "BKE_mesh_sample.h"
|
||||||
|
|
||||||
|
#include "RNA_access.h"
|
||||||
|
#include "RNA_define.h"
|
||||||
|
#include "RNA_types.h"
|
||||||
|
|
||||||
|
#include "rna_internal.h"
|
||||||
|
|
||||||
|
#include "WM_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef RNA_RUNTIME
|
||||||
|
|
||||||
|
#include "WM_api.h"
|
||||||
|
#include "WM_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void rna_def_mesh_sample(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "MeshSample", NULL);
|
||||||
|
RNA_def_struct_sdna(srna, "MeshSample");
|
||||||
|
RNA_def_struct_ui_text(srna, "Mesh Sample", "Point on a mesh that follows deformation");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "vertex_indices", PROP_INT, PROP_UNSIGNED);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "orig_verts");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_ui_text(prop, "Vertex Indices", "Index of the mesh vertices used for interpolation");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RNA_def_mesh_sample(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
rna_def_mesh_sample(brna);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -115,6 +115,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
|
|||||||
{eModifierType_DynamicPaint, "DYNAMIC_PAINT", ICON_MOD_DYNAMICPAINT, "Dynamic Paint", ""},
|
{eModifierType_DynamicPaint, "DYNAMIC_PAINT", ICON_MOD_DYNAMICPAINT, "Dynamic Paint", ""},
|
||||||
{eModifierType_Explode, "EXPLODE", ICON_MOD_EXPLODE, "Explode", ""},
|
{eModifierType_Explode, "EXPLODE", ICON_MOD_EXPLODE, "Explode", ""},
|
||||||
{eModifierType_Fluidsim, "FLUID_SIMULATION", ICON_MOD_FLUIDSIM, "Fluid Simulation", ""},
|
{eModifierType_Fluidsim, "FLUID_SIMULATION", ICON_MOD_FLUIDSIM, "Fluid Simulation", ""},
|
||||||
|
{eModifierType_Hair, "HAIR", ICON_STRANDS, "Hair", ""},
|
||||||
{eModifierType_Ocean, "OCEAN", ICON_MOD_OCEAN, "Ocean", ""},
|
{eModifierType_Ocean, "OCEAN", ICON_MOD_OCEAN, "Ocean", ""},
|
||||||
{eModifierType_ParticleInstance, "PARTICLE_INSTANCE", ICON_MOD_PARTICLES, "Particle Instance", ""},
|
{eModifierType_ParticleInstance, "PARTICLE_INSTANCE", ICON_MOD_PARTICLES, "Particle Instance", ""},
|
||||||
{eModifierType_ParticleSystem, "PARTICLE_SYSTEM", ICON_MOD_PARTICLES, "Particle System", ""},
|
{eModifierType_ParticleSystem, "PARTICLE_SYSTEM", ICON_MOD_PARTICLES, "Particle System", ""},
|
||||||
@@ -281,12 +282,17 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
|
|||||||
|
|
||||||
#ifdef RNA_RUNTIME
|
#ifdef RNA_RUNTIME
|
||||||
|
|
||||||
|
#include "BLI_listbase.h"
|
||||||
|
|
||||||
#include "DNA_particle_types.h"
|
#include "DNA_particle_types.h"
|
||||||
#include "DNA_curve_types.h"
|
#include "DNA_curve_types.h"
|
||||||
#include "DNA_smoke_types.h"
|
#include "DNA_smoke_types.h"
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
|
#include "DNA_meshdata_types.h"
|
||||||
|
|
||||||
#include "BKE_cachefile.h"
|
#include "BKE_cachefile.h"
|
||||||
#include "BKE_context.h"
|
#include "BKE_context.h"
|
||||||
|
#include "BKE_hair.h"
|
||||||
#include "BKE_library.h"
|
#include "BKE_library.h"
|
||||||
#include "BKE_mesh_runtime.h"
|
#include "BKE_mesh_runtime.h"
|
||||||
#include "BKE_modifier.h"
|
#include "BKE_modifier.h"
|
||||||
@@ -295,6 +301,7 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
|
|||||||
|
|
||||||
#include "DEG_depsgraph.h"
|
#include "DEG_depsgraph.h"
|
||||||
#include "DEG_depsgraph_build.h"
|
#include "DEG_depsgraph_build.h"
|
||||||
|
#include "DEG_depsgraph_query.h"
|
||||||
|
|
||||||
#ifdef WITH_ALEMBIC
|
#ifdef WITH_ALEMBIC
|
||||||
# include "ABC_alembic.h"
|
# include "ABC_alembic.h"
|
||||||
@@ -417,6 +424,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
|
|||||||
return &RNA_SurfaceDeformModifier;
|
return &RNA_SurfaceDeformModifier;
|
||||||
case eModifierType_WeightedNormal:
|
case eModifierType_WeightedNormal:
|
||||||
return &RNA_WeightedNormalModifier;
|
return &RNA_WeightedNormalModifier;
|
||||||
|
case eModifierType_Hair:
|
||||||
|
return &RNA_HairModifier;
|
||||||
/* Default */
|
/* Default */
|
||||||
case eModifierType_None:
|
case eModifierType_None:
|
||||||
case eModifierType_ShapeKey:
|
case eModifierType_ShapeKey:
|
||||||
@@ -1174,6 +1183,58 @@ static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr, co
|
|||||||
CLAMP_MIN(psmd->psys, 1);
|
CLAMP_MIN(psmd->psys, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_Hair_fiber_curves_clear(HairModifierData *hmd)
|
||||||
|
{
|
||||||
|
for (HairModifierFiberCurve* curve = hmd->fiber_curves.first; curve; curve = curve->next)
|
||||||
|
{
|
||||||
|
if (curve->verts)
|
||||||
|
{
|
||||||
|
MEM_freeN(curve->verts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BLI_freelistN(&hmd->fiber_curves);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_Hair_fiber_curves_new(HairModifierData *hmd, ReportList *UNUSED(reports), int numverts)
|
||||||
|
{
|
||||||
|
HairModifierFiberCurve *curve = MEM_callocN(sizeof(HairModifierFiberCurve), "hair fiber curve");
|
||||||
|
curve->numverts = numverts;
|
||||||
|
curve->verts = MEM_callocN(sizeof(HairFiberVertex) * numverts, "hair fiber curve vertices");
|
||||||
|
|
||||||
|
BLI_addtail(&hmd->fiber_curves, curve);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_Hair_fiber_curves_apply(ID *id, HairModifierData *hmd, bContext *C, ReportList *UNUSED(reports))
|
||||||
|
{
|
||||||
|
const int totcurves = BLI_listbase_count(&hmd->fiber_curves);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
BKE_hair_fiber_curves_begin(hmd->hair_system, totcurves);
|
||||||
|
i = 0;
|
||||||
|
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next, ++i)
|
||||||
|
{
|
||||||
|
BKE_hair_set_fiber_curve(hmd->hair_system, i, curve->numverts, 0.1, 1.0);
|
||||||
|
}
|
||||||
|
BKE_hair_fiber_curves_end(hmd->hair_system);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < curve->numverts; ++j, ++i)
|
||||||
|
{
|
||||||
|
BKE_hair_set_fiber_vertex(hmd->hair_system, i, curve->verts[j].flag, curve->verts[j].co);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Mesh *scalp = (Mesh *)DEG_get_evaluated_id(CTX_data_depsgraph(C), ((Object*)id)->data);
|
||||||
|
BKE_hair_bind_follicles(hmd->hair_system, scalp);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEG_id_tag_update(id, OB_RECALC_DATA);
|
||||||
|
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[])
|
static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[])
|
||||||
@@ -4916,6 +4977,52 @@ static void rna_def_modifier_surfacedeform(BlenderRNA *brna)
|
|||||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_def_modifier_hair_fiber_curve(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "HairModifierFiberCurve", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Curve", "");
|
||||||
|
RNA_def_struct_sdna(srna, "HairModifierFiberCurve");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE);
|
||||||
|
RNA_def_property_collection_sdna(prop, NULL, "verts", "numverts");
|
||||||
|
RNA_def_property_struct_type(prop, "HairModifierFiberVertex");
|
||||||
|
RNA_def_property_ui_text(prop, "Vertices", "Fiber vertices");
|
||||||
|
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "HairModifierFiberVertex", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Vertex", "");
|
||||||
|
RNA_def_struct_sdna(srna, "HairFiberVertex");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "co");
|
||||||
|
RNA_def_property_ui_text(prop, "Location", "Location of the vertex relative to the root");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_def_modifier_hair_fiber_curves_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
FunctionRNA *func;
|
||||||
|
PropertyRNA *parm;
|
||||||
|
|
||||||
|
RNA_def_property_srna(cprop, "HairModifierFiberCurves");
|
||||||
|
srna = RNA_def_struct(brna, "HairModifierFiberCurves", NULL);
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Curves", "");
|
||||||
|
RNA_def_struct_sdna(srna, "HairModifierData");
|
||||||
|
|
||||||
|
/*func =*/ RNA_def_function(srna, "clear", "rna_Hair_fiber_curves_clear");
|
||||||
|
|
||||||
|
func = RNA_def_function(srna, "new", "rna_Hair_fiber_curves_new");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_REPORTS);
|
||||||
|
parm = RNA_def_int(func, "vertex_count", 2, 0, INT_MAX, "Vertex Count", "Number of vertices", 2, 1000);
|
||||||
|
RNA_def_property_flag(parm, PARM_REQUIRED);
|
||||||
|
|
||||||
|
func = RNA_def_function(srna, "apply", "rna_Hair_fiber_curves_apply");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
|
||||||
|
}
|
||||||
|
|
||||||
static void rna_def_modifier_weightednormal(BlenderRNA *brna)
|
static void rna_def_modifier_weightednormal(BlenderRNA *brna)
|
||||||
{
|
{
|
||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
@@ -4978,6 +5085,43 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
|
|||||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_def_modifier_hair(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "HairModifier", "Modifier");
|
||||||
|
RNA_def_struct_ui_text(srna, "Hair Modifier", "");
|
||||||
|
RNA_def_struct_sdna(srna, "HairModifierData");
|
||||||
|
RNA_def_struct_ui_icon(srna, ICON_STRANDS);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE);
|
||||||
|
RNA_def_property_ui_text(prop, "Hair", "Hair data");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "follicle_seed", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_range(prop, 0, INT_MAX);
|
||||||
|
RNA_def_property_ui_text(prop, "Seed", "Follicle distribution random seed value");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "follicle_count", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_default(prop, 100000);
|
||||||
|
RNA_def_property_range(prop, 0, INT_MAX);
|
||||||
|
RNA_def_property_ui_range(prop, 1, 1e5, 1, 1);
|
||||||
|
RNA_def_property_ui_text(prop, "Follicle Count", "Maximum number of follicles");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "draw_settings", PROP_POINTER, PROP_NONE);
|
||||||
|
RNA_def_property_ui_text(prop, "Draw Settings", "Hair draw settings");
|
||||||
|
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "fiber_curves", PROP_COLLECTION, PROP_NONE);
|
||||||
|
RNA_def_property_collection_sdna(prop, NULL, "fiber_curves", NULL);
|
||||||
|
RNA_def_property_struct_type(prop, "HairModifierFiberCurve");
|
||||||
|
RNA_def_property_ui_text(prop, "Fiber Curves", "Fiber curve data");
|
||||||
|
rna_def_modifier_hair_fiber_curves_api(brna, prop);
|
||||||
|
|
||||||
|
rna_def_modifier_hair_fiber_curve(brna);
|
||||||
|
}
|
||||||
|
|
||||||
void RNA_def_modifier(BlenderRNA *brna)
|
void RNA_def_modifier(BlenderRNA *brna)
|
||||||
{
|
{
|
||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
@@ -5100,6 +5244,7 @@ void RNA_def_modifier(BlenderRNA *brna)
|
|||||||
rna_def_modifier_meshseqcache(brna);
|
rna_def_modifier_meshseqcache(brna);
|
||||||
rna_def_modifier_surfacedeform(brna);
|
rna_def_modifier_surfacedeform(brna);
|
||||||
rna_def_modifier_weightednormal(brna);
|
rna_def_modifier_weightednormal(brna);
|
||||||
|
rna_def_modifier_hair(brna);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -63,6 +63,7 @@ set(SRC
|
|||||||
intern/MOD_explode.c
|
intern/MOD_explode.c
|
||||||
intern/MOD_fluidsim.c
|
intern/MOD_fluidsim.c
|
||||||
intern/MOD_fluidsim_util.c
|
intern/MOD_fluidsim_util.c
|
||||||
|
intern/MOD_hair.c
|
||||||
intern/MOD_hook.c
|
intern/MOD_hook.c
|
||||||
intern/MOD_laplaciandeform.c
|
intern/MOD_laplaciandeform.c
|
||||||
intern/MOD_laplaciansmooth.c
|
intern/MOD_laplaciansmooth.c
|
||||||
|
@@ -87,6 +87,7 @@ extern ModifierTypeInfo modifierType_CorrectiveSmooth;
|
|||||||
extern ModifierTypeInfo modifierType_MeshSequenceCache;
|
extern ModifierTypeInfo modifierType_MeshSequenceCache;
|
||||||
extern ModifierTypeInfo modifierType_SurfaceDeform;
|
extern ModifierTypeInfo modifierType_SurfaceDeform;
|
||||||
extern ModifierTypeInfo modifierType_WeightedNormal;
|
extern ModifierTypeInfo modifierType_WeightedNormal;
|
||||||
|
extern ModifierTypeInfo modifierType_Hair;
|
||||||
|
|
||||||
/* MOD_util.c */
|
/* MOD_util.c */
|
||||||
void modifier_type_init(ModifierTypeInfo *types[]);
|
void modifier_type_init(ModifierTypeInfo *types[]);
|
||||||
|
169
source/blender/modifiers/intern/MOD_hair.c
Normal file
169
source/blender/modifiers/intern/MOD_hair.c
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* The Original Code is Copyright (C) 2005 by the Blender Foundation.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s): Daniel Dunbar
|
||||||
|
* Ton Roosendaal,
|
||||||
|
* Ben Batt,
|
||||||
|
* Brecht Van Lommel,
|
||||||
|
* Campbell Barton
|
||||||
|
*
|
||||||
|
* ***** END GPL LICENSE BLOCK *****
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file blender/modifiers/intern/MOD_hair.c
|
||||||
|
* \ingroup modifiers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_utildefines.h"
|
||||||
|
#include "BLI_listbase.h"
|
||||||
|
|
||||||
|
#include "DNA_object_types.h"
|
||||||
|
#include "DNA_hair_types.h"
|
||||||
|
|
||||||
|
#include "BKE_hair.h"
|
||||||
|
#include "BKE_library.h"
|
||||||
|
#include "BKE_library_query.h"
|
||||||
|
#include "BKE_modifier.h"
|
||||||
|
|
||||||
|
#include "DEG_depsgraph_build.h"
|
||||||
|
|
||||||
|
#include "MOD_util.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void initData(ModifierData *md)
|
||||||
|
{
|
||||||
|
HairModifierData *hmd = (HairModifierData *) md;
|
||||||
|
|
||||||
|
hmd->hair_system = BKE_hair_new();
|
||||||
|
|
||||||
|
hmd->flag |= 0;
|
||||||
|
|
||||||
|
hmd->follicle_count = 100000;
|
||||||
|
|
||||||
|
hmd->draw_settings = BKE_hair_draw_settings_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
|
||||||
|
{
|
||||||
|
const HairModifierData *hmd = (HairModifierData *) md;
|
||||||
|
HairModifierData *tfmd = (HairModifierData *) target;
|
||||||
|
|
||||||
|
modifier_copyData_generic(md, target, flag);
|
||||||
|
|
||||||
|
if (hmd->hair_system) {
|
||||||
|
tfmd->hair_system = BKE_hair_copy(hmd->hair_system);
|
||||||
|
}
|
||||||
|
if (hmd->draw_settings)
|
||||||
|
{
|
||||||
|
tfmd->draw_settings = BKE_hair_draw_settings_copy(hmd->draw_settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeData(ModifierData *md)
|
||||||
|
{
|
||||||
|
HairModifierData *hmd = (HairModifierData *) md;
|
||||||
|
|
||||||
|
if (hmd->hair_system) {
|
||||||
|
BKE_hair_free(hmd->hair_system);
|
||||||
|
}
|
||||||
|
if (hmd->draw_settings)
|
||||||
|
{
|
||||||
|
BKE_hair_draw_settings_free(hmd->draw_settings);
|
||||||
|
}
|
||||||
|
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next)
|
||||||
|
{
|
||||||
|
if (curve->verts)
|
||||||
|
{
|
||||||
|
MEM_freeN(curve->verts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BLI_freelistN(&hmd->fiber_curves);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx,
|
||||||
|
struct Mesh *mesh)
|
||||||
|
{
|
||||||
|
HairModifierData *hmd = (HairModifierData *) md;
|
||||||
|
|
||||||
|
UNUSED_VARS(hmd, ctx);
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void foreachObjectLink(
|
||||||
|
ModifierData *md,
|
||||||
|
Object *ob,
|
||||||
|
ObjectWalkFunc walk,
|
||||||
|
void *userData)
|
||||||
|
{
|
||||||
|
HairModifierData *hmd = (HairModifierData *) md;
|
||||||
|
UNUSED_VARS(ob, walk, userData, hmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void foreachIDLink(
|
||||||
|
ModifierData *md,
|
||||||
|
Object *ob,
|
||||||
|
IDWalkFunc walk,
|
||||||
|
void *userData)
|
||||||
|
{
|
||||||
|
HairModifierData *hmd = (HairModifierData *) md;
|
||||||
|
UNUSED_VARS(hmd);
|
||||||
|
|
||||||
|
foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModifierTypeInfo modifierType_Hair = {
|
||||||
|
/* name */ "Hair",
|
||||||
|
/* structName */ "HairModifierData",
|
||||||
|
/* structSize */ sizeof(HairModifierData),
|
||||||
|
/* type */ eModifierTypeType_NonGeometrical,
|
||||||
|
/* flags */ eModifierTypeFlag_AcceptsMesh |
|
||||||
|
eModifierTypeFlag_SupportsEditmode,
|
||||||
|
|
||||||
|
/* copyData */ copyData,
|
||||||
|
|
||||||
|
/* deformVerts_DM */ NULL,
|
||||||
|
/* deformMatrices_DM */ NULL,
|
||||||
|
/* deformVertsEM_DM */ NULL,
|
||||||
|
/* deformMatricesEM_DM*/NULL,
|
||||||
|
/* applyModifier_DM */ NULL,
|
||||||
|
/* applyModifierEM_DM */NULL,
|
||||||
|
|
||||||
|
/* deformVerts */ NULL,
|
||||||
|
/* deformMatrices */ NULL,
|
||||||
|
/* deformVertsEM */ NULL,
|
||||||
|
/* deformMatricesEM */ NULL,
|
||||||
|
/* applyModifier */ applyModifier,
|
||||||
|
/* applyModifierEM */ NULL,
|
||||||
|
|
||||||
|
/* initData */ initData,
|
||||||
|
/* requiredDataMask */ NULL,
|
||||||
|
/* freeData */ freeData,
|
||||||
|
/* isDisabled */ NULL,
|
||||||
|
/* updateDepsgraph */ NULL,
|
||||||
|
/* dependsOnTime */ NULL,
|
||||||
|
/* dependsOnNormals */ NULL,
|
||||||
|
/* foreachObjectLink */ foreachObjectLink,
|
||||||
|
/* foreachIDLink */ foreachIDLink,
|
||||||
|
/* foreachTexLink */ NULL,
|
||||||
|
};
|
@@ -291,5 +291,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
|
|||||||
INIT_TYPE(MeshSequenceCache);
|
INIT_TYPE(MeshSequenceCache);
|
||||||
INIT_TYPE(SurfaceDeform);
|
INIT_TYPE(SurfaceDeform);
|
||||||
INIT_TYPE(WeightedNormal);
|
INIT_TYPE(WeightedNormal);
|
||||||
|
INIT_TYPE(Hair);
|
||||||
#undef INIT_TYPE
|
#undef INIT_TYPE
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ if(WITH_GTESTS)
|
|||||||
|
|
||||||
add_subdirectory(testing)
|
add_subdirectory(testing)
|
||||||
add_subdirectory(blenlib)
|
add_subdirectory(blenlib)
|
||||||
|
add_subdirectory(blenkernel)
|
||||||
add_subdirectory(guardedalloc)
|
add_subdirectory(guardedalloc)
|
||||||
add_subdirectory(bmesh)
|
add_subdirectory(bmesh)
|
||||||
if(WITH_ALEMBIC)
|
if(WITH_ALEMBIC)
|
||||||
|
334
tests/gtests/blenkernel/BKE_mesh_sample_test.cc
Normal file
334
tests/gtests/blenkernel/BKE_mesh_sample_test.cc
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
/* Apache License, Version 2.0 */
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "testing/testing.h"
|
||||||
|
|
||||||
|
#include "BKE_mesh_test_util.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_math.h"
|
||||||
|
#include "BLI_path_util.h"
|
||||||
|
#include "BLI_rand.h"
|
||||||
|
|
||||||
|
#include "DNA_mesh_types.h"
|
||||||
|
#include "DNA_meshdata_types.h"
|
||||||
|
|
||||||
|
#include "BKE_appdir.h"
|
||||||
|
#include "BKE_global.h"
|
||||||
|
#include "BKE_main.h"
|
||||||
|
#include "BKE_mesh.h"
|
||||||
|
#include "BKE_mesh_sample.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TEST_MESH_OUTPUT_FILE "mesh_dump_"
|
||||||
|
|
||||||
|
static const float verts[][3] = { {-1, -1, -1}, {1, -1, -1}, {-1, 1, -1}, {1, 1, -1},
|
||||||
|
{-1, -1, 1}, {1, -1, 1}, {-1, 1, 1}, {1, 1, 1} };
|
||||||
|
static const int faces[] = { 0, 1, 3, 2,
|
||||||
|
4, 5, 7, 6,
|
||||||
|
0, 1, 5, 4,
|
||||||
|
2, 3, 7, 6,
|
||||||
|
0, 2, 6, 4,
|
||||||
|
1, 3, 7, 5,
|
||||||
|
};
|
||||||
|
static const int face_lengths[] = { 4, 4, 4, 4, 4, 4 };
|
||||||
|
|
||||||
|
class MeshSampleTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const unsigned int m_seed;
|
||||||
|
static const int m_numsamples;
|
||||||
|
|
||||||
|
std::string get_testname() const;
|
||||||
|
|
||||||
|
void load_mesh(const float (*verts)[3], int numverts,
|
||||||
|
const int (*edges)[2], int numedges,
|
||||||
|
const int *faces, const int *face_lengths, int numfaces);
|
||||||
|
void load_mesh(const char *filename);
|
||||||
|
void unload_mesh();
|
||||||
|
|
||||||
|
void generate_samples_simple(struct MeshSampleGenerator *gen);
|
||||||
|
void generate_samples_batch(struct MeshSampleGenerator *gen);
|
||||||
|
void generate_samples_batch_threaded(struct MeshSampleGenerator *gen);
|
||||||
|
void compare_samples(const struct MeshSample *ground_truth);
|
||||||
|
void test_samples(struct MeshSampleGenerator *gen, const struct MeshSample *ground_truth, int count);
|
||||||
|
|
||||||
|
void dump_samples();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SetUp() override;
|
||||||
|
void TearDown() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Mesh *m_mesh;
|
||||||
|
|
||||||
|
MeshSample *m_samples;
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned int MeshSampleTest::m_seed = 8343;
|
||||||
|
const int MeshSampleTest::m_numsamples = 100000;
|
||||||
|
|
||||||
|
std::string MeshSampleTest::get_testname() const
|
||||||
|
{
|
||||||
|
std::stringstream testname;
|
||||||
|
testname << ::testing::UnitTest::GetInstance()->current_test_info()->name();
|
||||||
|
return testname.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::load_mesh(const float (*verts)[3], int numverts,
|
||||||
|
const int (*edges)[2], int numedges,
|
||||||
|
const int *faces, const int *face_lengths, int numfaces)
|
||||||
|
{
|
||||||
|
m_mesh = BKE_mesh_test_from_data(verts, numverts, edges, numedges, faces, face_lengths, numfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::load_mesh(const char *filename)
|
||||||
|
{
|
||||||
|
const char *folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "tests");
|
||||||
|
char path[FILE_MAX];
|
||||||
|
|
||||||
|
BLI_make_file_string(G.main->name, path, folder, filename);
|
||||||
|
|
||||||
|
if (path[0]) {
|
||||||
|
m_mesh = BKE_mesh_test_from_csv(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::unload_mesh()
|
||||||
|
{
|
||||||
|
if (m_mesh) {
|
||||||
|
BKE_mesh_free(m_mesh);
|
||||||
|
MEM_freeN(m_mesh);
|
||||||
|
m_mesh = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::SetUp()
|
||||||
|
{
|
||||||
|
load_mesh("suzanne.csv");
|
||||||
|
if (!m_mesh) {
|
||||||
|
int numverts = ARRAY_SIZE(verts);
|
||||||
|
int numfaces = ARRAY_SIZE(face_lengths);
|
||||||
|
load_mesh(verts, numverts, NULL, 0, faces, face_lengths, numfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_samples = (MeshSample *)MEM_mallocN(sizeof(MeshSample) * m_numsamples, "mesh samples");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::TearDown()
|
||||||
|
{
|
||||||
|
if (m_samples) {
|
||||||
|
MEM_freeN(m_samples);
|
||||||
|
m_samples = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unload_mesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MeshSampleTest::dump_samples()
|
||||||
|
{
|
||||||
|
#ifdef TEST_MESH_OUTPUT_FILE
|
||||||
|
int numverts = m_mesh->totvert;
|
||||||
|
|
||||||
|
int dbg_numverts = numverts + m_numsamples;
|
||||||
|
float (*dbg_verts)[3] = (float (*)[3])MEM_mallocN(sizeof(float[3]) * dbg_numverts, "vertices");
|
||||||
|
for (int i = 0; i < numverts; ++i) {
|
||||||
|
copy_v3_v3(dbg_verts[i], m_mesh->mvert[i].co);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < m_numsamples; ++i) {
|
||||||
|
float nor[3], tang[3];
|
||||||
|
BKE_mesh_sample_eval(m_mesh, &m_samples[i], dbg_verts[numverts + i], nor, tang);
|
||||||
|
}
|
||||||
|
int *dbg_faces = (int *)MEM_mallocN(sizeof(int) * m_mesh->totloop, "faces");
|
||||||
|
int *dbg_face_lengths = (int *)MEM_mallocN(sizeof(int) * m_mesh->totpoly, "face_lengths");
|
||||||
|
int loopstart = 0;
|
||||||
|
for (int i = 0; i < m_mesh->totpoly; ++i) {
|
||||||
|
const MPoly *mp = &m_mesh->mpoly[i];
|
||||||
|
dbg_face_lengths[i] = mp->totloop;
|
||||||
|
for (int k = 0; k < mp->totloop; ++k) {
|
||||||
|
dbg_faces[loopstart + k] = m_mesh->mloop[mp->loopstart + k].v;
|
||||||
|
}
|
||||||
|
loopstart += mp->totloop;
|
||||||
|
}
|
||||||
|
Mesh *dbg_mesh = BKE_mesh_test_from_data(dbg_verts, dbg_numverts, NULL, 0, dbg_faces, dbg_face_lengths, m_mesh->totpoly);
|
||||||
|
MEM_freeN(dbg_verts);
|
||||||
|
MEM_freeN(dbg_faces);
|
||||||
|
MEM_freeN(dbg_face_lengths);
|
||||||
|
|
||||||
|
std::stringstream filename;
|
||||||
|
filename << TEST_MESH_OUTPUT_FILE << get_testname() << ".py";
|
||||||
|
std::fstream s(filename.str(), s.trunc | s.out);
|
||||||
|
|
||||||
|
BKE_mesh_test_dump_mesh(dbg_mesh, get_testname().c_str(), s);
|
||||||
|
|
||||||
|
BKE_mesh_free(dbg_mesh);
|
||||||
|
MEM_freeN(dbg_mesh);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::compare_samples(const MeshSample *ground_truth)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_numsamples; ++i) {
|
||||||
|
EXPECT_EQ(ground_truth[i].orig_verts[0], m_samples[i].orig_verts[0]);
|
||||||
|
EXPECT_EQ(ground_truth[i].orig_verts[1], m_samples[i].orig_verts[1]);
|
||||||
|
EXPECT_EQ(ground_truth[i].orig_verts[2], m_samples[i].orig_verts[2]);
|
||||||
|
|
||||||
|
EXPECT_EQ(ground_truth[i].orig_weights[0], m_samples[i].orig_weights[0]);
|
||||||
|
EXPECT_EQ(ground_truth[i].orig_weights[1], m_samples[i].orig_weights[1]);
|
||||||
|
EXPECT_EQ(ground_truth[i].orig_weights[2], m_samples[i].orig_weights[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::generate_samples_simple(MeshSampleGenerator *gen)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_numsamples; ++i) {
|
||||||
|
BKE_mesh_sample_generate(gen, &m_samples[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::generate_samples_batch(MeshSampleGenerator *gen)
|
||||||
|
{
|
||||||
|
BKE_mesh_sample_generate_batch_ex(gen, m_samples, sizeof(MeshSample), m_numsamples, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::generate_samples_batch_threaded(MeshSampleGenerator *gen)
|
||||||
|
{
|
||||||
|
BKE_mesh_sample_generate_batch_ex(gen, m_samples, sizeof(MeshSample), m_numsamples, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshSampleTest::test_samples(MeshSampleGenerator *gen, const MeshSample *ground_truth, int count)
|
||||||
|
{
|
||||||
|
BKE_mesh_sample_generator_bind(gen, m_mesh);
|
||||||
|
|
||||||
|
if (ground_truth) {
|
||||||
|
EXPECT_EQ(count, m_numsamples) << "Ground truth size does not match number of samples";
|
||||||
|
if (count != m_numsamples) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_samples_simple(gen);
|
||||||
|
compare_samples(ground_truth);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
generate_samples_simple(gen);
|
||||||
|
// Use simple sample generation as ground truth if not provided explicitly
|
||||||
|
ground_truth = m_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_samples_batch(gen);
|
||||||
|
compare_samples(ground_truth);
|
||||||
|
|
||||||
|
generate_samples_batch_threaded(gen);
|
||||||
|
compare_samples(ground_truth);
|
||||||
|
|
||||||
|
BKE_mesh_sample_generator_unbind(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MeshSampleTest, SurfaceVertices)
|
||||||
|
{
|
||||||
|
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_vertices();
|
||||||
|
ASSERT_TRUE(gen != NULL) << "No generator created";
|
||||||
|
|
||||||
|
test_samples(gen, NULL, 0);
|
||||||
|
dump_samples();
|
||||||
|
|
||||||
|
BKE_mesh_sample_free_generator(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MeshSampleTest, SurfaceRandom)
|
||||||
|
{
|
||||||
|
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_random(m_seed, true, NULL, NULL);
|
||||||
|
ASSERT_TRUE(gen != NULL) << "No generator created";
|
||||||
|
|
||||||
|
test_samples(gen, NULL, 0);
|
||||||
|
dump_samples();
|
||||||
|
|
||||||
|
BKE_mesh_sample_free_generator(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float poisson_disk_mindist = 0.01f;
|
||||||
|
|
||||||
|
TEST_F(MeshSampleTest, SurfacePoissonDisk)
|
||||||
|
{
|
||||||
|
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_poissondisk(m_seed, poisson_disk_mindist, 10000000, NULL, NULL);
|
||||||
|
ASSERT_TRUE(gen != NULL) << "No generator created";
|
||||||
|
|
||||||
|
test_samples(gen, NULL, 0);
|
||||||
|
dump_samples();
|
||||||
|
|
||||||
|
BKE_mesh_sample_free_generator(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
static const unsigned int raycast_seed = 85344;
|
||||||
|
static const float raycast_radius = 100.0f;
|
||||||
|
|
||||||
|
static void* raycast_thread_context_create(void *UNUSED(userdata), int start)
|
||||||
|
{
|
||||||
|
RNG *rng = BLI_rng_new(raycast_seed);
|
||||||
|
BLI_rng_skip(rng, start * 2);
|
||||||
|
return rng;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raycast_thread_context_free(void *UNUSED(userdata), void *thread_ctx)
|
||||||
|
{
|
||||||
|
BLI_rng_free((RNG *)thread_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool raycast_ray(void *UNUSED(userdata), void *thread_ctx, float ray_start[3], float ray_end[3])
|
||||||
|
{
|
||||||
|
RNG *rng = (RNG *)thread_ctx;
|
||||||
|
float v[3];
|
||||||
|
{
|
||||||
|
float r;
|
||||||
|
v[2] = (2.0f * BLI_rng_get_float(rng)) - 1.0f;
|
||||||
|
float a = (float)(M_PI * 2.0) * BLI_rng_get_float(rng);
|
||||||
|
if ((r = 1.0f - (v[2] * v[2])) > 0.0f) {
|
||||||
|
r = sqrtf(r);
|
||||||
|
v[0] = r * cosf(a);
|
||||||
|
v[1] = r * sinf(a);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
v[2] = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mul_v3_fl(v, raycast_radius);
|
||||||
|
copy_v3_v3(ray_start, v);
|
||||||
|
negate_v3_v3(ray_end, v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /*extern "C"*/
|
||||||
|
|
||||||
|
TEST_F(MeshSampleTest, SurfaceRaycast)
|
||||||
|
{
|
||||||
|
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_raycast(
|
||||||
|
raycast_thread_context_create, raycast_thread_context_free, raycast_ray, NULL);
|
||||||
|
ASSERT_TRUE(gen != NULL) << "No generator created";
|
||||||
|
|
||||||
|
test_samples(gen, NULL, 0);
|
||||||
|
dump_samples();
|
||||||
|
|
||||||
|
BKE_mesh_sample_free_generator(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float volume_bbray_density = 0.1f;
|
||||||
|
|
||||||
|
TEST_F(MeshSampleTest, VolumeBBRay)
|
||||||
|
{
|
||||||
|
MeshSampleGenerator *gen = BKE_mesh_sample_gen_volume_random_bbray(m_seed, volume_bbray_density);
|
||||||
|
ASSERT_TRUE(gen != NULL) << "No generator created";
|
||||||
|
|
||||||
|
test_samples(gen, NULL, 0);
|
||||||
|
dump_samples();
|
||||||
|
|
||||||
|
BKE_mesh_sample_free_generator(gen);
|
||||||
|
}
|
319
tests/gtests/blenkernel/BKE_mesh_test_util.cc
Normal file
319
tests/gtests/blenkernel/BKE_mesh_test_util.cc
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
/* Apache License, Version 2.0 */
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include "BKE_mesh_test_util.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_math.h"
|
||||||
|
|
||||||
|
#include "DNA_mesh_types.h"
|
||||||
|
#include "DNA_meshdata_types.h"
|
||||||
|
|
||||||
|
#include "BKE_customdata.h"
|
||||||
|
#include "BKE_mesh.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mesh_update(Mesh *mesh, int calc_edges, int calc_tessface)
|
||||||
|
{
|
||||||
|
bool tessface_input = false;
|
||||||
|
|
||||||
|
if (mesh->totface > 0 && mesh->totpoly == 0) {
|
||||||
|
BKE_mesh_convert_mfaces_to_mpolys(mesh);
|
||||||
|
|
||||||
|
/* would only be converting back again, don't bother */
|
||||||
|
tessface_input = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calc_edges || ((mesh->totpoly || mesh->totface) && mesh->totedge == 0))
|
||||||
|
BKE_mesh_calc_edges(mesh, calc_edges, true);
|
||||||
|
|
||||||
|
if (calc_tessface) {
|
||||||
|
if (tessface_input == false) {
|
||||||
|
BKE_mesh_tessface_calc(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* default state is not to have tessface's so make sure this is the case */
|
||||||
|
BKE_mesh_tessface_clear(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_mesh_calc_normals(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mesh_add_verts(Mesh *mesh, int len)
|
||||||
|
{
|
||||||
|
CustomData vdata;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int totvert = mesh->totvert + len;
|
||||||
|
CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
|
||||||
|
CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
|
||||||
|
|
||||||
|
if (!CustomData_has_layer(&vdata, CD_MVERT))
|
||||||
|
CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
|
||||||
|
|
||||||
|
CustomData_free(&mesh->vdata, mesh->totvert);
|
||||||
|
mesh->vdata = vdata;
|
||||||
|
BKE_mesh_update_customdata_pointers(mesh, false);
|
||||||
|
|
||||||
|
/* scan the input list and insert the new vertices */
|
||||||
|
|
||||||
|
/* set final vertex list size */
|
||||||
|
mesh->totvert = totvert;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mesh_add_edges(Mesh *mesh, int len)
|
||||||
|
{
|
||||||
|
CustomData edata;
|
||||||
|
MEdge *medge;
|
||||||
|
int i, totedge;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
totedge = mesh->totedge + len;
|
||||||
|
|
||||||
|
/* update customdata */
|
||||||
|
CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge);
|
||||||
|
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
|
||||||
|
|
||||||
|
if (!CustomData_has_layer(&edata, CD_MEDGE))
|
||||||
|
CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
|
||||||
|
|
||||||
|
CustomData_free(&mesh->edata, mesh->totedge);
|
||||||
|
mesh->edata = edata;
|
||||||
|
BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */
|
||||||
|
|
||||||
|
/* set default flags */
|
||||||
|
medge = &mesh->medge[mesh->totedge];
|
||||||
|
for (i = 0; i < len; i++, medge++)
|
||||||
|
medge->flag = ME_EDGEDRAW | ME_EDGERENDER;
|
||||||
|
|
||||||
|
mesh->totedge = totedge;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mesh_add_loops(Mesh *mesh, int len)
|
||||||
|
{
|
||||||
|
CustomData ldata;
|
||||||
|
int totloop;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
totloop = mesh->totloop + len; /* new face count */
|
||||||
|
|
||||||
|
/* update customdata */
|
||||||
|
CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloop);
|
||||||
|
CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
|
||||||
|
|
||||||
|
if (!CustomData_has_layer(&ldata, CD_MLOOP))
|
||||||
|
CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop);
|
||||||
|
|
||||||
|
CustomData_free(&mesh->ldata, mesh->totloop);
|
||||||
|
mesh->ldata = ldata;
|
||||||
|
BKE_mesh_update_customdata_pointers(mesh, true);
|
||||||
|
|
||||||
|
mesh->totloop = totloop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mesh_add_polys(Mesh *mesh, int len)
|
||||||
|
{
|
||||||
|
CustomData pdata;
|
||||||
|
MPoly *mpoly;
|
||||||
|
int i, totpoly;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
totpoly = mesh->totpoly + len; /* new face count */
|
||||||
|
|
||||||
|
/* update customdata */
|
||||||
|
CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpoly);
|
||||||
|
CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
|
||||||
|
|
||||||
|
if (!CustomData_has_layer(&pdata, CD_MPOLY))
|
||||||
|
CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly);
|
||||||
|
|
||||||
|
CustomData_free(&mesh->pdata, mesh->totpoly);
|
||||||
|
mesh->pdata = pdata;
|
||||||
|
BKE_mesh_update_customdata_pointers(mesh, true);
|
||||||
|
|
||||||
|
/* set default flags */
|
||||||
|
mpoly = &mesh->mpoly[mesh->totpoly];
|
||||||
|
for (i = 0; i < len; i++, mpoly++)
|
||||||
|
mpoly->flag = ME_FACE_SEL;
|
||||||
|
|
||||||
|
mesh->totpoly = totpoly;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh* BKE_mesh_test_from_data(
|
||||||
|
const float (*verts)[3], int numverts,
|
||||||
|
const int (*edges)[2], int numedges,
|
||||||
|
const int *loops, const int *face_lengths, int numfaces)
|
||||||
|
{
|
||||||
|
Mesh *me = (Mesh *)MEM_callocN(sizeof(Mesh), "Mesh");
|
||||||
|
|
||||||
|
BKE_mesh_init(me);
|
||||||
|
|
||||||
|
int numloops = 0;
|
||||||
|
for (int i = 0; i < numfaces; ++i) {
|
||||||
|
numloops += face_lengths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh_add_verts(me, numverts);
|
||||||
|
mesh_add_edges(me, numedges);
|
||||||
|
mesh_add_loops(me, numloops);
|
||||||
|
mesh_add_polys(me, numfaces);
|
||||||
|
|
||||||
|
{
|
||||||
|
MVert *v = me->mvert;
|
||||||
|
for (int i = 0; i < numverts; ++i, ++v) {
|
||||||
|
copy_v3_v3(v->co, verts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MEdge *e = me->medge;
|
||||||
|
for (int i = 0; i < numedges; ++i, ++e) {
|
||||||
|
e->v1 = edges[i][0];
|
||||||
|
e->v2 = edges[i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MLoop *l = me->mloop;
|
||||||
|
for (int i = 0; i < numloops; ++i, ++l) {
|
||||||
|
l->v = loops[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MPoly *p = me->mpoly;
|
||||||
|
int loopstart = 0;
|
||||||
|
for (int i = 0; i < numfaces; ++i, ++p) {
|
||||||
|
int totloop = face_lengths[i];
|
||||||
|
p->loopstart = loopstart;
|
||||||
|
p->totloop = totloop;
|
||||||
|
|
||||||
|
loopstart += totloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numfaces > 0 && numedges == 0) {
|
||||||
|
mesh_update(me, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return me;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh* BKE_mesh_test_from_csv(const char *filename)
|
||||||
|
{
|
||||||
|
std::ifstream ifs (filename, std::ifstream::in);
|
||||||
|
|
||||||
|
char delim;
|
||||||
|
|
||||||
|
int numverts, numloops, numfaces;
|
||||||
|
float (*verts)[3] = NULL;
|
||||||
|
int *loops = NULL;
|
||||||
|
int *face_lengths = NULL;
|
||||||
|
|
||||||
|
ifs >> numverts;
|
||||||
|
ifs >> delim;
|
||||||
|
verts = (float (*)[3])MEM_mallocN(sizeof(float[3]) * numverts, "verts");
|
||||||
|
for (int i = 0; i < numverts; ++i) {
|
||||||
|
ifs >> verts[i][0];
|
||||||
|
ifs >> delim;
|
||||||
|
ifs >> verts[i][1];
|
||||||
|
ifs >> delim;
|
||||||
|
ifs >> verts[i][2];
|
||||||
|
ifs >> delim;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifs >> numloops;
|
||||||
|
ifs >> delim;
|
||||||
|
loops = (int *)MEM_mallocN(sizeof(int) * numloops, "loops");
|
||||||
|
for (int i = 0; i < numloops; ++i) {
|
||||||
|
ifs >> loops[i];
|
||||||
|
ifs >> delim;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifs >> numfaces;
|
||||||
|
ifs >> delim;
|
||||||
|
face_lengths = (int *)MEM_mallocN(sizeof(int) * numfaces, "face_lengths");
|
||||||
|
for (int i = 0; i < numfaces; ++i) {
|
||||||
|
ifs >> face_lengths[i];
|
||||||
|
ifs >> delim;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BKE_mesh_test_from_data(verts, numverts, NULL, 0, loops, face_lengths, numfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BKE_mesh_test_dump_verts(Mesh *me, std::ostream &str)
|
||||||
|
{
|
||||||
|
int numverts = me->totvert;
|
||||||
|
|
||||||
|
str << std::setprecision(5);
|
||||||
|
|
||||||
|
MVert *v = me->mvert;
|
||||||
|
str << "[";
|
||||||
|
for (int i = 0; i < numverts; ++i, ++v) {
|
||||||
|
str << "(" << v->co[0] << ", " << v->co[1] << ", " << v->co[2] << "), ";
|
||||||
|
}
|
||||||
|
str << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
void BKE_mesh_test_dump_edges(Mesh *me, std::ostream &str)
|
||||||
|
{
|
||||||
|
int numedges = me->totedge;
|
||||||
|
|
||||||
|
MEdge *e = me->medge;
|
||||||
|
str << "[";
|
||||||
|
for (int i = 0; i < numedges; ++i, ++e) {
|
||||||
|
str << "(" << e->v1 << ", " << e->v2 << "), ";
|
||||||
|
}
|
||||||
|
str << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
void BKE_mesh_test_dump_faces(Mesh *me, std::ostream &str)
|
||||||
|
{
|
||||||
|
int numpolys = me->totpoly;
|
||||||
|
|
||||||
|
MPoly *p = me->mpoly;
|
||||||
|
str << "[";
|
||||||
|
for (int i = 0; i < numpolys; ++i, ++p) {
|
||||||
|
int totloop = p->totloop;
|
||||||
|
MLoop *l = me->mloop + p->loopstart;
|
||||||
|
|
||||||
|
str << "(";
|
||||||
|
for (int k = 0; k < totloop; ++k, ++l) {
|
||||||
|
str << l->v << ", ";
|
||||||
|
}
|
||||||
|
str << "), ";
|
||||||
|
}
|
||||||
|
str << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
void BKE_mesh_test_dump_mesh(Mesh *me, const char *name, std::ostream &str)
|
||||||
|
{
|
||||||
|
str << "import bpy\n";
|
||||||
|
str << "from bpy_extras.object_utils import object_data_add\n";
|
||||||
|
str << "mesh = bpy.data.meshes.new(name=\"" << name << "\")\n";
|
||||||
|
|
||||||
|
str << "mesh.from_pydata(";
|
||||||
|
str << "vertices=";
|
||||||
|
BKE_mesh_test_dump_verts(me, str);
|
||||||
|
str << ", edges=";
|
||||||
|
BKE_mesh_test_dump_edges(me, str);
|
||||||
|
str << ", faces=";
|
||||||
|
BKE_mesh_test_dump_faces(me, str);
|
||||||
|
str << ")\n";
|
||||||
|
|
||||||
|
str << "object_data_add(bpy.context, mesh)\n";
|
||||||
|
}
|
17
tests/gtests/blenkernel/BKE_mesh_test_util.h
Normal file
17
tests/gtests/blenkernel/BKE_mesh_test_util.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/* Apache License, Version 2.0 */
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
struct Mesh;
|
||||||
|
|
||||||
|
struct Mesh* BKE_mesh_test_from_data(
|
||||||
|
const float (*verts)[3], int numverts,
|
||||||
|
const int (*edges)[2], int numedges,
|
||||||
|
const int *loops, const int *face_lengths, int numfaces);
|
||||||
|
|
||||||
|
struct Mesh* BKE_mesh_test_from_csv(const char *filename);
|
||||||
|
|
||||||
|
void BKE_mesh_test_dump_verts(struct Mesh *me, std::ostream &str);
|
||||||
|
void BKE_mesh_test_dump_edges(struct Mesh *me, std::ostream &str);
|
||||||
|
void BKE_mesh_test_dump_faces(struct Mesh *me, std::ostream &str);
|
||||||
|
void BKE_mesh_test_dump_mesh(struct Mesh *me, const char *name, std::ostream &str);
|
57
tests/gtests/blenkernel/CMakeLists.txt
Normal file
57
tests/gtests/blenkernel/CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# The Original Code is Copyright (C) 2014, Blender Foundation
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Contributor(s): Sergey Sharybin
|
||||||
|
#
|
||||||
|
# ***** END GPL LICENSE BLOCK *****
|
||||||
|
|
||||||
|
set(INC
|
||||||
|
.
|
||||||
|
..
|
||||||
|
../../../source/blender/blenlib
|
||||||
|
../../../source/blender/blenkernel
|
||||||
|
../../../source/blender/makesdna
|
||||||
|
../../../source/blender/depsgraph
|
||||||
|
../../../intern/guardedalloc
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${INC})
|
||||||
|
|
||||||
|
set(SRC
|
||||||
|
BKE_mesh_test_util.cc
|
||||||
|
BKE_mesh_sample_test.cc
|
||||||
|
|
||||||
|
BKE_mesh_test_util.h
|
||||||
|
)
|
||||||
|
|
||||||
|
setup_libdirs()
|
||||||
|
get_property(BLENDER_SORTED_LIBS GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP)
|
||||||
|
|
||||||
|
if(WITH_BUILDINFO)
|
||||||
|
set(_buildinfo_src "$<TARGET_OBJECTS:buildinfoobj>")
|
||||||
|
else()
|
||||||
|
set(_buildinfo_src "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# For motivation on doubling BLENDER_SORTED_LIBS, see ../bmesh/CMakeLists.txt
|
||||||
|
BLENDER_SRC_GTEST(blenkernel "${SRC};${_buildinfo_src}" "${BLENDER_SORTED_LIBS};${BLENDER_SORTED_LIBS}")
|
||||||
|
|
||||||
|
unset(_buildinfo_src)
|
||||||
|
|
||||||
|
setup_liblinks(blenkernel_test)
|
Reference in New Issue
Block a user