Compare commits
381 Commits
temp-nodes
...
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);
|
||||
}
|
||||
|
||||
static bool ObtainCacheParticleData(Mesh *mesh,
|
||||
BL::Mesh *b_mesh,
|
||||
BL::Object *b_ob,
|
||||
ParticleCurveData *CData,
|
||||
bool background)
|
||||
static void ObtainCacheDataFromParticleSystem(Mesh *mesh,
|
||||
BL::Object *b_ob,
|
||||
BL::ParticleSystemModifier *b_psmd,
|
||||
ParticleCurveData *CData,
|
||||
bool background,
|
||||
int *curvenum,
|
||||
int *keyno)
|
||||
{
|
||||
int curvenum = 0;
|
||||
int keyno = 0;
|
||||
|
||||
if(!(mesh && b_mesh && b_ob && CData))
|
||||
return false;
|
||||
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
|
||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||
|
||||
Transform tfm = get_transform(b_ob->matrix_world());
|
||||
Transform itfm = transform_quick_inverse(tfm);
|
||||
|
||||
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
|
||||
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int shader = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
|
||||
int draw_step = background ? b_part.render_step() : b_part.draw_step();
|
||||
int totparts = b_psys.particles.length();
|
||||
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||
int totcurves = totchild;
|
||||
|
||||
if(b_part.child_type() == 0 || totchild == 0)
|
||||
totcurves += totparts;
|
||||
|
||||
if(totcurves == 0)
|
||||
return;
|
||||
|
||||
int ren_step = (1 << draw_step) + 1;
|
||||
if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
|
||||
ren_step += b_part.kink_extra_steps();
|
||||
|
||||
CData->psys_firstcurve.push_back_slow(*curvenum);
|
||||
CData->psys_curvenum.push_back_slow(totcurves);
|
||||
CData->psys_shader.push_back_slow(shader);
|
||||
|
||||
float radius = b_part.radius_scale() * 0.5f;
|
||||
|
||||
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
|
||||
CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
|
||||
CData->psys_shape.push_back_slow(b_part.shape());
|
||||
CData->psys_closetip.push_back_slow(b_part.use_close_tip());
|
||||
|
||||
int pa_no = 0;
|
||||
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||
pa_no = totparts;
|
||||
|
||||
int num_add = (totparts+totchild - pa_no);
|
||||
CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
|
||||
CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
|
||||
CData->curve_length.reserve(CData->curve_length.size() + num_add);
|
||||
CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
|
||||
CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
|
||||
|
||||
for(; pa_no < totparts+totchild; pa_no++) {
|
||||
int keynum = 0;
|
||||
CData->curve_firstkey.push_back_slow(*keyno);
|
||||
|
||||
float curve_length = 0.0f;
|
||||
float3 pcKey;
|
||||
for(int step_no = 0; step_no < ren_step; step_no++) {
|
||||
float nco[3];
|
||||
b_psys.co_hair(*b_ob, pa_no, step_no, nco);
|
||||
float3 cKey = make_float3(nco[0], nco[1], nco[2]);
|
||||
cKey = transform_point(&itfm, cKey);
|
||||
if(step_no > 0) {
|
||||
float step_length = len(cKey - pcKey);
|
||||
if(step_length == 0.0f)
|
||||
continue;
|
||||
curve_length += step_length;
|
||||
}
|
||||
CData->curvekey_co.push_back_slow(cKey);
|
||||
CData->curvekey_time.push_back_slow(curve_length);
|
||||
pcKey = cKey;
|
||||
keynum++;
|
||||
}
|
||||
*keyno += keynum;
|
||||
|
||||
CData->curve_keynum.push_back_slow(keynum);
|
||||
CData->curve_length.push_back_slow(curve_length);
|
||||
(*curvenum)++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ObtainCacheUVFromParticleSystem(BL::Mesh *b_mesh,
|
||||
BL::ParticleSystemModifier *b_psmd,
|
||||
ParticleCurveData *CData,
|
||||
bool background,
|
||||
int uv_num)
|
||||
{
|
||||
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
|
||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||
|
||||
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
|
||||
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int totparts = b_psys.particles.length();
|
||||
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||
int totcurves = totchild;
|
||||
|
||||
if(b_part.child_type() == 0 || totchild == 0)
|
||||
totcurves += totparts;
|
||||
|
||||
if(totcurves == 0)
|
||||
return;
|
||||
|
||||
int pa_no = 0;
|
||||
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||
pa_no = totparts;
|
||||
|
||||
int num_add = (totparts+totchild - pa_no);
|
||||
CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
|
||||
|
||||
BL::ParticleSystem::particles_iterator b_pa;
|
||||
b_psys.particles.begin(b_pa);
|
||||
for(; pa_no < totparts+totchild; pa_no++) {
|
||||
/* Add UVs */
|
||||
BL::Mesh::tessface_uv_textures_iterator l;
|
||||
b_mesh->tessface_uv_textures.begin(l);
|
||||
|
||||
float3 uv = make_float3(0.0f, 0.0f, 0.0f);
|
||||
if(b_mesh->tessface_uv_textures.length())
|
||||
b_psys.uv_on_emitter(*b_psmd, *b_pa, pa_no, uv_num, &uv.x);
|
||||
CData->curve_uv.push_back_slow(uv);
|
||||
|
||||
if(pa_no < totparts && b_pa != b_psys.particles.end())
|
||||
++b_pa;
|
||||
}
|
||||
}
|
||||
|
||||
static void ObtainCacheVColFromParticleSystem(BL::Mesh *b_mesh,
|
||||
BL::ParticleSystemModifier *b_psmd,
|
||||
ParticleCurveData *CData,
|
||||
bool background,
|
||||
int vcol_num)
|
||||
{
|
||||
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
|
||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||
|
||||
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
|
||||
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int totparts = b_psys.particles.length();
|
||||
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||
int totcurves = totchild;
|
||||
|
||||
if(b_part.child_type() == 0 || totchild == 0)
|
||||
totcurves += totparts;
|
||||
|
||||
if(totcurves == 0)
|
||||
return;
|
||||
|
||||
int pa_no = 0;
|
||||
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||
pa_no = totparts;
|
||||
|
||||
int num_add = (totparts+totchild - pa_no);
|
||||
CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
|
||||
|
||||
BL::ParticleSystem::particles_iterator b_pa;
|
||||
b_psys.particles.begin(b_pa);
|
||||
for(; pa_no < totparts+totchild; pa_no++) {
|
||||
/* Add vertex colors */
|
||||
BL::Mesh::tessface_vertex_colors_iterator l;
|
||||
b_mesh->tessface_vertex_colors.begin(l);
|
||||
|
||||
float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
|
||||
if(b_mesh->tessface_vertex_colors.length())
|
||||
b_psys.mcol_on_emitter(*b_psmd, *b_pa, pa_no, vcol_num, &vcol.x);
|
||||
CData->curve_vcol.push_back_slow(vcol);
|
||||
|
||||
if(pa_no < totparts && b_pa != b_psys.particles.end())
|
||||
++b_pa;
|
||||
}
|
||||
}
|
||||
|
||||
static void ObtainCacheDataFromHairSystem(BL::Object *b_ob,
|
||||
BL::HairSystem *b_hsys,
|
||||
BL::Mesh *b_scalp,
|
||||
int shader,
|
||||
bool /*background*/,
|
||||
ParticleCurveData *CData,
|
||||
int *curvenum,
|
||||
int *keyno)
|
||||
{
|
||||
Transform tfm = get_transform(b_ob->matrix_world());
|
||||
Transform itfm = transform_quick_inverse(tfm);
|
||||
|
||||
void *hair_cache = BKE_hair_export_cache_new();
|
||||
BKE_hair_export_cache_update(hair_cache, b_hsys->ptr.data, 0, b_scalp->ptr.data, 0xFFFFFFFF);
|
||||
|
||||
int totcurves, totverts;
|
||||
BKE_hair_render_get_buffer_size(hair_cache, &totcurves, &totverts);
|
||||
|
||||
if(totcurves == 0)
|
||||
{
|
||||
BKE_hair_export_cache_free(hair_cache);
|
||||
return;
|
||||
}
|
||||
|
||||
CData->psys_firstcurve.push_back_slow(*curvenum);
|
||||
CData->psys_curvenum.push_back_slow(totcurves);
|
||||
|
||||
// Material
|
||||
CData->psys_shader.push_back_slow(shader);
|
||||
|
||||
{
|
||||
// Cycles settings
|
||||
// PointerRNA cpsys = RNA_pointer_get(&b_hsys->ptr, "cycles");
|
||||
// float radius = get_float(cpsys, "radius_scale") * 0.5f;
|
||||
// CData->psys_rootradius.push_back_slow(radius * get_float(cpsys, "root_width"));
|
||||
// CData->psys_tipradius.push_back_slow(radius * get_float(cpsys, "tip_width"));
|
||||
// CData->psys_shape.push_back_slow(get_float(cpsys, "shape"));
|
||||
// CData->psys_closetip.push_back_slow(get_boolean(cpsys, "use_closetip"));
|
||||
float radius = 0.01f * 0.5f;
|
||||
CData->psys_rootradius.push_back_slow(radius * 1.0f);
|
||||
CData->psys_tipradius.push_back_slow(radius * 0.0f);
|
||||
CData->psys_shape.push_back_slow(0.0f);
|
||||
CData->psys_closetip.push_back_slow(true);
|
||||
}
|
||||
|
||||
// Allocate buffers
|
||||
int *firstkey_data;
|
||||
int *keynum_data;
|
||||
float *length_data;
|
||||
float3 *co_data;
|
||||
float *time_data;
|
||||
{
|
||||
const size_t firstkey_start = CData->curve_firstkey.size();
|
||||
const size_t keynum_start = CData->curve_keynum.size();
|
||||
const size_t length_start = CData->curve_length.size();
|
||||
const size_t co_start = CData->curvekey_co.size();
|
||||
const size_t time_start = CData->curvekey_time.size();
|
||||
CData->curve_firstkey.resize(firstkey_start + totcurves);
|
||||
CData->curve_keynum.resize(keynum_start + totcurves);
|
||||
CData->curve_length.resize(length_start + totcurves);
|
||||
CData->curvekey_co.resize(co_start + totverts);
|
||||
CData->curvekey_time.resize(time_start + totverts);
|
||||
firstkey_data = CData->curve_firstkey.data() + firstkey_start;
|
||||
keynum_data = CData->curve_keynum.data() + keynum_start;
|
||||
length_data = CData->curve_length.data() + length_start;
|
||||
co_data = CData->curvekey_co.data() + co_start;
|
||||
time_data = CData->curvekey_time.data() + time_start;
|
||||
}
|
||||
|
||||
// Import render curves from hair system
|
||||
BKE_hair_render_fill_buffers(
|
||||
hair_cache,
|
||||
(int)sizeof(float3),
|
||||
firstkey_data,
|
||||
keynum_data,
|
||||
(float*)co_data);
|
||||
|
||||
// Compute curve length and key times
|
||||
for(int c = 0; c < totcurves; ++c) {
|
||||
const int firstkey = firstkey_data[c];
|
||||
const int keynum = keynum_data[c];
|
||||
|
||||
float curve_length = 0.0f;
|
||||
float3 pcKey;
|
||||
for(int v = 0; v < keynum; v++) {
|
||||
float3 cKey = co_data[firstkey + v];
|
||||
cKey = transform_point(&itfm, cKey);
|
||||
if(v > 0) {
|
||||
float step_length = len(cKey - pcKey);
|
||||
if(step_length == 0.0f)
|
||||
continue;
|
||||
curve_length += step_length;
|
||||
}
|
||||
|
||||
co_data[firstkey + v] = cKey;
|
||||
time_data[v] = curve_length;
|
||||
pcKey = cKey;
|
||||
}
|
||||
|
||||
firstkey_data[c] = *keyno;
|
||||
length_data[c] = curve_length;
|
||||
*keyno += keynum;
|
||||
}
|
||||
*curvenum += totcurves;
|
||||
|
||||
BKE_hair_export_cache_free(hair_cache);
|
||||
}
|
||||
|
||||
static bool ObtainCacheDataFromObject(Mesh *mesh,
|
||||
BL::Object *b_ob,
|
||||
ParticleCurveData *CData,
|
||||
bool background)
|
||||
{
|
||||
int curvenum = 0;
|
||||
int keyno = 0;
|
||||
|
||||
if(!(mesh && b_ob && CData))
|
||||
return false;
|
||||
|
||||
BL::Object::modifiers_iterator b_mod;
|
||||
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
|
||||
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
|
||||
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
|
||||
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
|
||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||
|
||||
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
|
||||
int shader = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
|
||||
int draw_step = background ? b_part.render_step() : b_part.draw_step();
|
||||
int totparts = b_psys.particles.length();
|
||||
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||
int totcurves = totchild;
|
||||
|
||||
if(b_part.child_type() == 0 || totchild == 0)
|
||||
totcurves += totparts;
|
||||
|
||||
if(totcurves == 0)
|
||||
continue;
|
||||
|
||||
int ren_step = (1 << draw_step) + 1;
|
||||
if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
|
||||
ren_step += b_part.kink_extra_steps();
|
||||
|
||||
CData->psys_firstcurve.push_back_slow(curvenum);
|
||||
CData->psys_curvenum.push_back_slow(totcurves);
|
||||
CData->psys_shader.push_back_slow(shader);
|
||||
|
||||
float radius = b_part.radius_scale() * 0.5f;
|
||||
|
||||
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
|
||||
CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
|
||||
CData->psys_shape.push_back_slow(b_part.shape());
|
||||
CData->psys_closetip.push_back_slow(b_part.use_close_tip());
|
||||
|
||||
int pa_no = 0;
|
||||
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||
pa_no = totparts;
|
||||
|
||||
int num_add = (totparts+totchild - pa_no);
|
||||
CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
|
||||
CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
|
||||
CData->curve_length.reserve(CData->curve_length.size() + num_add);
|
||||
CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
|
||||
CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
|
||||
|
||||
for(; pa_no < totparts+totchild; pa_no++) {
|
||||
int keynum = 0;
|
||||
CData->curve_firstkey.push_back_slow(keyno);
|
||||
|
||||
float curve_length = 0.0f;
|
||||
float3 pcKey;
|
||||
for(int step_no = 0; step_no < ren_step; step_no++) {
|
||||
float nco[3];
|
||||
b_psys.co_hair(*b_ob, pa_no, step_no, nco);
|
||||
float3 cKey = make_float3(nco[0], nco[1], nco[2]);
|
||||
cKey = transform_point(&itfm, cKey);
|
||||
if(step_no > 0) {
|
||||
float step_length = len(cKey - pcKey);
|
||||
if(step_length == 0.0f)
|
||||
continue;
|
||||
curve_length += step_length;
|
||||
}
|
||||
CData->curvekey_co.push_back_slow(cKey);
|
||||
CData->curvekey_time.push_back_slow(curve_length);
|
||||
pcKey = cKey;
|
||||
keynum++;
|
||||
}
|
||||
keyno += keynum;
|
||||
|
||||
CData->curve_keynum.push_back_slow(keynum);
|
||||
CData->curve_length.push_back_slow(curve_length);
|
||||
curvenum++;
|
||||
}
|
||||
if (background ? b_mod->show_render() : b_mod->show_viewport())
|
||||
{
|
||||
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM)) {
|
||||
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
|
||||
ObtainCacheDataFromParticleSystem(mesh,
|
||||
b_ob,
|
||||
&b_psmd,
|
||||
CData,
|
||||
background,
|
||||
&curvenum,
|
||||
&keyno);
|
||||
}
|
||||
if((b_mod->type() == b_mod->type_HAIR)) {
|
||||
BL::HairModifier b_hmd((const PointerRNA)b_mod->ptr);
|
||||
BL::HairSystem b_hsys = b_hmd.hair_system();
|
||||
|
||||
const int material_index = 1; /* TODO */
|
||||
int shader = clamp(material_index - 1, 0, mesh->used_shaders.size()-1);
|
||||
|
||||
BL::Mesh b_scalp(b_ob->data());
|
||||
|
||||
ObtainCacheDataFromHairSystem(b_ob,
|
||||
&b_hsys,
|
||||
&b_scalp,
|
||||
shader,
|
||||
background,
|
||||
CData,
|
||||
&curvenum,
|
||||
&keyno);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,12 +453,12 @@ static bool ObtainCacheParticleData(Mesh *mesh,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ObtainCacheParticleUV(Mesh *mesh,
|
||||
BL::Mesh *b_mesh,
|
||||
BL::Object *b_ob,
|
||||
ParticleCurveData *CData,
|
||||
bool background,
|
||||
int uv_num)
|
||||
static bool ObtainCacheUVFromObject(Mesh *mesh,
|
||||
BL::Mesh *b_mesh,
|
||||
BL::Object *b_ob,
|
||||
ParticleCurveData *CData,
|
||||
bool background,
|
||||
int uv_num)
|
||||
{
|
||||
if(!(mesh && b_mesh && b_ob && CData))
|
||||
return false;
|
||||
@@ -220,44 +467,11 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
|
||||
|
||||
BL::Object::modifiers_iterator b_mod;
|
||||
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
|
||||
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
|
||||
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
|
||||
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
|
||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||
|
||||
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
|
||||
int totparts = b_psys.particles.length();
|
||||
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||
int totcurves = totchild;
|
||||
|
||||
if(b_part.child_type() == 0 || totchild == 0)
|
||||
totcurves += totparts;
|
||||
|
||||
if(totcurves == 0)
|
||||
continue;
|
||||
|
||||
int pa_no = 0;
|
||||
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||
pa_no = totparts;
|
||||
|
||||
int num_add = (totparts+totchild - pa_no);
|
||||
CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
|
||||
|
||||
BL::ParticleSystem::particles_iterator b_pa;
|
||||
b_psys.particles.begin(b_pa);
|
||||
for(; pa_no < totparts+totchild; pa_no++) {
|
||||
/* Add UVs */
|
||||
BL::Mesh::tessface_uv_textures_iterator l;
|
||||
b_mesh->tessface_uv_textures.begin(l);
|
||||
|
||||
float3 uv = make_float3(0.0f, 0.0f, 0.0f);
|
||||
if(b_mesh->tessface_uv_textures.length())
|
||||
b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
|
||||
CData->curve_uv.push_back_slow(uv);
|
||||
|
||||
if(pa_no < totparts && b_pa != b_psys.particles.end())
|
||||
++b_pa;
|
||||
}
|
||||
if (background ? b_mod->show_render() : b_mod->show_viewport())
|
||||
{
|
||||
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM)) {
|
||||
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
|
||||
ObtainCacheUVFromParticleSystem(b_mesh, &b_psmd, CData, background, uv_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,12 +479,12 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ObtainCacheParticleVcol(Mesh *mesh,
|
||||
BL::Mesh *b_mesh,
|
||||
BL::Object *b_ob,
|
||||
ParticleCurveData *CData,
|
||||
bool background,
|
||||
int vcol_num)
|
||||
static bool ObtainCacheVcolFromObject(Mesh *mesh,
|
||||
BL::Mesh *b_mesh,
|
||||
BL::Object *b_ob,
|
||||
ParticleCurveData *CData,
|
||||
bool background,
|
||||
int vcol_num)
|
||||
{
|
||||
if(!(mesh && b_mesh && b_ob && CData))
|
||||
return false;
|
||||
@@ -279,44 +493,12 @@ static bool ObtainCacheParticleVcol(Mesh *mesh,
|
||||
|
||||
BL::Object::modifiers_iterator b_mod;
|
||||
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
|
||||
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
|
||||
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
|
||||
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
|
||||
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
||||
|
||||
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
|
||||
int totparts = b_psys.particles.length();
|
||||
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
|
||||
int totcurves = totchild;
|
||||
|
||||
if(b_part.child_type() == 0 || totchild == 0)
|
||||
totcurves += totparts;
|
||||
|
||||
if(totcurves == 0)
|
||||
continue;
|
||||
|
||||
int pa_no = 0;
|
||||
if(!(b_part.child_type() == 0) && totchild != 0)
|
||||
pa_no = totparts;
|
||||
|
||||
int num_add = (totparts+totchild - pa_no);
|
||||
CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
|
||||
|
||||
BL::ParticleSystem::particles_iterator b_pa;
|
||||
b_psys.particles.begin(b_pa);
|
||||
for(; pa_no < totparts+totchild; pa_no++) {
|
||||
/* Add vertex colors */
|
||||
BL::Mesh::tessface_vertex_colors_iterator l;
|
||||
b_mesh->tessface_vertex_colors.begin(l);
|
||||
|
||||
float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
|
||||
if(b_mesh->tessface_vertex_colors.length())
|
||||
b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
|
||||
CData->curve_vcol.push_back_slow(vcol);
|
||||
|
||||
if(pa_no < totparts && b_pa != b_psys.particles.end())
|
||||
++b_pa;
|
||||
}
|
||||
if (background ? b_mod->show_render() : b_mod->show_viewport())
|
||||
{
|
||||
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM))
|
||||
{
|
||||
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
|
||||
ObtainCacheVColFromParticleSystem(b_mesh, &b_psmd, CData, background, vcol_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -906,7 +1088,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
||||
|
||||
ParticleCurveData CData;
|
||||
|
||||
ObtainCacheParticleData(mesh, &b_mesh, &b_ob, &CData, !preview);
|
||||
ObtainCacheDataFromObject(mesh, &b_ob, &CData, !preview);
|
||||
|
||||
/* add hair geometry to mesh */
|
||||
if(primitive == CURVE_TRIANGLES) {
|
||||
@@ -942,7 +1124,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
||||
|
||||
/* generated coordinates from first key. we should ideally get this from
|
||||
* blender to handle deforming objects */
|
||||
if(!motion) {
|
||||
if(b_mesh && !motion) {
|
||||
if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
|
||||
float3 loc, size;
|
||||
mesh_texture_space(b_mesh, loc, size);
|
||||
@@ -967,7 +1149,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
||||
}
|
||||
|
||||
/* create vertex color attributes */
|
||||
if(!motion) {
|
||||
if(b_mesh && !motion) {
|
||||
BL::Mesh::tessface_vertex_colors_iterator l;
|
||||
int vcol_num = 0;
|
||||
|
||||
@@ -975,7 +1157,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
||||
if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
|
||||
continue;
|
||||
|
||||
ObtainCacheParticleVcol(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
|
||||
ObtainCacheVcolFromObject(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
|
||||
|
||||
if(primitive == CURVE_TRIANGLES) {
|
||||
Attribute *attr_vcol = mesh->attributes.add(
|
||||
@@ -1004,7 +1186,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
||||
}
|
||||
|
||||
/* create UV attributes */
|
||||
if(!motion) {
|
||||
if(b_mesh && !motion) {
|
||||
BL::Mesh::tessface_uv_textures_iterator l;
|
||||
int uv_num = 0;
|
||||
|
||||
@@ -1017,7 +1199,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
|
||||
if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
|
||||
Attribute *attr_uv;
|
||||
|
||||
ObtainCacheParticleUV(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num);
|
||||
ObtainCacheUVFromObject(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num);
|
||||
|
||||
if(primitive == CURVE_TRIANGLES) {
|
||||
if(active_render)
|
||||
|
@@ -1188,10 +1188,12 @@ Mesh *BlenderSync::sync_mesh(BL::Depsgraph& b_depsgraph,
|
||||
|
||||
create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
|
||||
}
|
||||
}
|
||||
|
||||
if(view_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
|
||||
sync_curves(mesh, b_mesh, b_ob, false);
|
||||
if(view_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
|
||||
sync_curves(mesh, b_mesh, b_ob, false);
|
||||
|
||||
if(b_mesh) {
|
||||
/* free derived mesh */
|
||||
b_data.meshes.remove(b_mesh, false, true, false);
|
||||
}
|
||||
|
@@ -36,6 +36,11 @@ void BKE_image_user_frame_calc(void *iuser, int cfra, int fieldnr);
|
||||
void BKE_image_user_file_path(void *iuser, void *ima, char *path);
|
||||
unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
|
||||
float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
|
||||
void* BKE_hair_export_cache_new(void);
|
||||
int BKE_hair_export_cache_update(void *cache, const void *hsys, int subdiv, void *scalp, int requested_data);
|
||||
void BKE_hair_export_cache_free(void *hcache);
|
||||
void BKE_hair_render_get_buffer_size(void* hcache, int *r_totcurves, int *r_totverts);
|
||||
void BKE_hair_render_fill_buffers(void* hcache, int vertco_stride, int *r_curvestart, int *r_curvelen, float *r_vertco);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
@@ -44,6 +44,7 @@ _modules = [
|
||||
"properties_data_lightprobe",
|
||||
"properties_data_speaker",
|
||||
"properties_data_workspace",
|
||||
"properties_hair_common",
|
||||
"properties_mask_common",
|
||||
"properties_material",
|
||||
"properties_material_gpencil",
|
||||
|
@@ -20,7 +20,7 @@
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
|
||||
from .properties_hair_common import draw_hair_display_settings
|
||||
|
||||
class ModifierButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
@@ -1577,6 +1577,27 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
if md.rest_source == 'BIND':
|
||||
layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "Bind")
|
||||
|
||||
def HAIR(self, layout, ob, md):
|
||||
hsys = md.hair_system
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
col.label("Follicles:")
|
||||
col.prop(md, "follicle_seed")
|
||||
col.prop(md, "follicle_count")
|
||||
col.operator("object.hair_generate_follicles", text="Generate")
|
||||
|
||||
col = split.column()
|
||||
|
||||
col.separator()
|
||||
|
||||
col.prop(hsys, "material_slot", text="")
|
||||
|
||||
col = layout.column()
|
||||
col.label("Display Settings:")
|
||||
draw_hair_display_settings(col, md.draw_settings)
|
||||
|
||||
def WEIGHTED_NORMAL(self, layout, ob, md):
|
||||
layout.label("Weighting Mode:")
|
||||
split = layout.split(align=True)
|
||||
|
61
release/scripts/startup/bl_ui/properties_hair_common.py
Normal file
61
release/scripts/startup/bl_ui/properties_hair_common.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8-80 compliant>
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def draw_hair_display_settings(layout, settings):
|
||||
col = layout.column(align=True)
|
||||
col.label("Follicles:")
|
||||
col.prop(settings, "follicle_mode", expand=True)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label("Guide Curves:")
|
||||
col.prop(settings, "guide_mode", expand=True)
|
||||
|
||||
layout.prop(settings, "shape")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(settings, "root_radius")
|
||||
col.prop(settings, "tip_radius")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(settings, "radius_scale")
|
||||
col.prop(settings, "use_close_tip")
|
||||
|
||||
|
||||
class HAIR_PT_display_settings:
|
||||
# subclasses must define...
|
||||
# ~ bl_space_type = 'PROPERTIES'
|
||||
# ~ bl_region_type = 'WINDOW'
|
||||
bl_label = "Hair Display Settings"
|
||||
|
||||
def draw(self, context):
|
||||
settings = context.draw_hair_display_settings
|
||||
draw_hair_display_settings(self.layout, hair_display_settings)
|
||||
|
||||
|
||||
classes = (
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
@@ -78,7 +78,6 @@ def is_not_gpencil_edit_mode(context):
|
||||
)
|
||||
return not is_gpmode
|
||||
|
||||
|
||||
# ********** 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_gpu_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_group_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_hair_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h
|
||||
|
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 Scene;
|
||||
struct Depsgraph;
|
||||
struct Mesh;
|
||||
struct ModifierData;
|
||||
struct MTFace;
|
||||
struct MCol;
|
||||
@@ -433,6 +434,7 @@ void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFa
|
||||
float orco[3]);
|
||||
float psys_particle_value_from_verts(struct Mesh *mesh, short from, struct ParticleData *pa, float *values);
|
||||
void psys_get_from_key(struct ParticleKey *key, float loc[3], float vel[3], float rot[4], float *time);
|
||||
int psys_get_index_on_mesh(struct ParticleSystem *psys, struct Mesh *mesh, ParticleData *pa, int *mapindex, float mapfw[4]);
|
||||
|
||||
/* BLI_bvhtree_ray_cast callback */
|
||||
void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit);
|
||||
|
@@ -118,6 +118,8 @@ set(SRC
|
||||
intern/freestyle.c
|
||||
intern/gpencil.c
|
||||
intern/gpencil_modifier.c
|
||||
intern/hair.c
|
||||
intern/hair_draw.c
|
||||
intern/icons.c
|
||||
intern/icons_rasterize.c
|
||||
intern/idcode.c
|
||||
@@ -149,6 +151,7 @@ set(SRC
|
||||
intern/mesh_merge.c
|
||||
intern/mesh_remap.c
|
||||
intern/mesh_runtime.c
|
||||
intern/mesh_sample.c
|
||||
intern/mesh_tangent.c
|
||||
intern/mesh_validate.c
|
||||
intern/modifier.c
|
||||
@@ -264,6 +267,7 @@ set(SRC
|
||||
BKE_global.h
|
||||
BKE_gpencil.h
|
||||
BKE_gpencil_modifier.h
|
||||
BKE_hair.h
|
||||
BKE_icons.h
|
||||
BKE_idcode.h
|
||||
BKE_idprop.h
|
||||
@@ -288,6 +292,7 @@ set(SRC
|
||||
BKE_mesh_mapping.h
|
||||
BKE_mesh_remap.h
|
||||
BKE_mesh_runtime.h
|
||||
BKE_mesh_sample.h
|
||||
BKE_mesh_tangent.h
|
||||
BKE_modifier.h
|
||||
BKE_movieclip.h
|
||||
|
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_cachefile_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_ipo_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -1057,6 +1057,8 @@ void BKE_main_lib_objects_recalc_all(Main *bmain)
|
||||
DEG_id_type_tag(bmain, ID_OB);
|
||||
}
|
||||
|
||||
BLI_STATIC_ASSERT(MAX_LIBARRAY == INDEX_ID_NULL + 1, "MAX_LIBARRAY must be large enough for all ID types")
|
||||
|
||||
/**
|
||||
* puts into array *lb pointers to all the ListBase structs in main,
|
||||
* and returns the number of them as the function result. This is useful for
|
||||
|
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;
|
||||
}
|
||||
|
||||
static int psys_map_index_on_dm(Mesh *mesh, int from, int index, int index_dmcache, const float fw[4], float UNUSED(foffset), int *mapindex, float mapfw[4])
|
||||
static int psys_map_index_on_mesh(Mesh *mesh, int from, int index, int index_dmcache, const float fw[4], float UNUSED(foffset), int *mapindex, float mapfw[4])
|
||||
{
|
||||
if (index < 0)
|
||||
return 0;
|
||||
@@ -1497,6 +1497,11 @@ static int psys_map_index_on_dm(Mesh *mesh, int from, int index, int index_dmcac
|
||||
return 1;
|
||||
}
|
||||
|
||||
int psys_get_index_on_mesh(ParticleSystem *psys, Mesh *mesh, ParticleData *pa, int *mapindex, float mapfw[4])
|
||||
{
|
||||
return psys_map_index_on_mesh(mesh, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, mapindex, mapfw);
|
||||
}
|
||||
|
||||
/* interprets particle data to get a point on a mesh in object space */
|
||||
void psys_particle_on_dm(Mesh *mesh_final, int from, int index, int index_dmcache,
|
||||
const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3],
|
||||
@@ -1506,7 +1511,7 @@ void psys_particle_on_dm(Mesh *mesh_final, int from, int index, int index_dmcach
|
||||
float (*orcodata)[3];
|
||||
int mapindex;
|
||||
|
||||
if (!psys_map_index_on_dm(mesh_final, from, index, index_dmcache, fw, foffset, &mapindex, mapfw)) {
|
||||
if (!psys_map_index_on_mesh(mesh_final, from, index, index_dmcache, fw, foffset, &mapindex, mapfw)) {
|
||||
if (vec) { vec[0] = vec[1] = vec[2] = 0.0; }
|
||||
if (nor) { nor[0] = nor[1] = 0.0; nor[2] = 1.0; }
|
||||
if (orco) { orco[0] = orco[1] = orco[2] = 0.0; }
|
||||
@@ -1571,7 +1576,7 @@ float psys_particle_value_from_verts(Mesh *mesh, short from, ParticleData *pa, f
|
||||
float mapfw[4];
|
||||
int mapindex;
|
||||
|
||||
if (!psys_map_index_on_dm(mesh, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, &mapindex, mapfw))
|
||||
if (!psys_map_index_on_mesh(mesh, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, &mapindex, mapfw))
|
||||
return 0.0f;
|
||||
|
||||
return psys_interpolate_value_from_verts(mesh, from, mapindex, mapfw, values);
|
||||
|
@@ -376,6 +376,8 @@ bool clip_segment_v3_plane_n(
|
||||
/****************************** Interpolation ********************************/
|
||||
void interp_weights_tri_v3(float w[3], const float a[3], const float b[3], const float c[3], const float p[3]);
|
||||
void interp_weights_quad_v3(float w[4], const float a[3], const float b[3], const float c[3], const float d[3], const float p[3]);
|
||||
/* also returns three indices of the triangle actually used */
|
||||
void interp_weights_quad_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3]);
|
||||
void interp_weights_poly_v3(float w[], float v[][3], const int n, const float co[3]);
|
||||
void interp_weights_poly_v2(float w[], float v[][2], const int n, const float co[2]);
|
||||
|
||||
|
@@ -3272,6 +3272,71 @@ void interp_weights_quad_v3(float w[4], const float v1[3], const float v2[3], co
|
||||
}
|
||||
}
|
||||
|
||||
void interp_weights_quad_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3])
|
||||
{
|
||||
float w2[3];
|
||||
|
||||
w[0] = w[1] = w[2] = w[3] = 0.0f;
|
||||
tri[0] = tri[1] = tri[2] = -1;
|
||||
|
||||
/* first check for exact match */
|
||||
if (equals_v3v3(co, v1)) {
|
||||
w[0] = 1.0f;
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 3;
|
||||
}
|
||||
else if (equals_v3v3(co, v2)) {
|
||||
w[1] = 1.0f;
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 3;
|
||||
}
|
||||
else if (equals_v3v3(co, v3)) {
|
||||
w[2] = 1.0f;
|
||||
tri[0] = 1; tri[1] = 2; tri[2] = 3;
|
||||
}
|
||||
else if (v4 && equals_v3v3(co, v4)) {
|
||||
w[3] = 1.0f;
|
||||
tri[0] = 1; tri[1] = 2; tri[2] = 3;
|
||||
}
|
||||
else {
|
||||
/* otherwise compute barycentric interpolation weights */
|
||||
float n1[3], n2[3], n[3];
|
||||
bool degenerate;
|
||||
|
||||
sub_v3_v3v3(n1, v1, v3);
|
||||
if (v4) {
|
||||
sub_v3_v3v3(n2, v2, v4);
|
||||
}
|
||||
else {
|
||||
sub_v3_v3v3(n2, v2, v3);
|
||||
}
|
||||
cross_v3_v3v3(n, n1, n2);
|
||||
|
||||
/* OpenGL seems to split this way, so we do too */
|
||||
if (v4) {
|
||||
degenerate = barycentric_weights(v1, v2, v4, co, n, w);
|
||||
SWAP(float, w[2], w[3]);
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 3;
|
||||
|
||||
if (degenerate || (w[0] < 0.0f)) {
|
||||
/* if w[1] is negative, co is on the other side of the v1-v3 edge,
|
||||
* so we interpolate using the other triangle */
|
||||
degenerate = barycentric_weights(v2, v3, v4, co, n, w2);
|
||||
|
||||
if (!degenerate) {
|
||||
w[0] = 0.0f;
|
||||
w[1] = w2[0];
|
||||
w[2] = w2[1];
|
||||
w[3] = w2[2];
|
||||
tri[0] = 1; tri[1] = 2; tri[2] = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
barycentric_weights(v1, v2, v3, co, n, w);
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return 1 of point is inside triangle, 2 if it's on the edge, 0 if point is outside of triangle */
|
||||
int barycentric_inside_triangle_v2(const float w[3])
|
||||
{
|
||||
|
@@ -73,6 +73,7 @@
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_gpencil_modifier_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_shader_fx_types.h"
|
||||
#include "DNA_ipo_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
@@ -5095,6 +5096,25 @@ static void direct_link_pose(FileData *fd, bPose *pose)
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_hair(FileData *fd, HairSystem* hsys)
|
||||
{
|
||||
if (!hsys) {
|
||||
return;
|
||||
}
|
||||
|
||||
hsys->pattern = newdataadr(fd, hsys->pattern);
|
||||
if ( hsys->pattern )
|
||||
{
|
||||
hsys->pattern->follicles = newdataadr(fd, hsys->pattern->follicles);
|
||||
}
|
||||
|
||||
hsys->curve_data.curves = newdataadr(fd, hsys->curve_data.curves);
|
||||
hsys->curve_data.verts = newdataadr(fd, hsys->curve_data.verts);
|
||||
|
||||
hsys->draw_batch_cache = NULL;
|
||||
hsys->draw_texture_cache = NULL;
|
||||
}
|
||||
|
||||
static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
||||
{
|
||||
ModifierData *md;
|
||||
@@ -5418,6 +5438,16 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (md->type == eModifierType_Hair) {
|
||||
HairModifierData *hmd = (HairModifierData *)md;
|
||||
|
||||
hmd->hair_system = newdataadr(fd, hmd->hair_system);
|
||||
direct_link_hair(fd, hmd->hair_system);
|
||||
|
||||
hmd->draw_settings = newdataadr(fd, hmd->draw_settings);
|
||||
|
||||
BLI_listbase_clear(&hmd->fiber_curves); // runtime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -121,6 +121,7 @@
|
||||
#include "DNA_gpencil_modifier_types.h"
|
||||
#include "DNA_shader_fx_types.h"
|
||||
#include "DNA_fileglobal_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -1617,6 +1618,18 @@ static void write_fmaps(WriteData *wd, ListBase *fbase)
|
||||
}
|
||||
}
|
||||
|
||||
static void write_hair(WriteData *wd, HairSystem *hsys)
|
||||
{
|
||||
if ( hsys->pattern )
|
||||
{
|
||||
writestruct(wd, DATA, HairPattern, 1, hsys->pattern);
|
||||
writestruct(wd, DATA, HairFollicle, hsys->pattern->num_follicles, hsys->pattern->follicles);
|
||||
}
|
||||
|
||||
writestruct(wd, DATA, HairFiberCurve, hsys->curve_data.totcurves, hsys->curve_data.curves);
|
||||
writestruct(wd, DATA, HairFiberVertex, hsys->curve_data.totverts, hsys->curve_data.verts);
|
||||
}
|
||||
|
||||
static void write_modifiers(WriteData *wd, ListBase *modbase)
|
||||
{
|
||||
ModifierData *md;
|
||||
@@ -1788,6 +1801,18 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (md->type == eModifierType_Hair) {
|
||||
HairModifierData *hmd = (HairModifierData *)md;
|
||||
|
||||
if (hmd->hair_system) {
|
||||
writestruct(wd, DATA, HairSystem, 1, hmd->hair_system);
|
||||
write_hair(wd, hmd->hair_system);
|
||||
}
|
||||
if (hmd->draw_settings)
|
||||
{
|
||||
writestruct(wd, DATA, HairDrawSettings, 1, hmd->draw_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -58,6 +58,7 @@ set(SRC
|
||||
intern/draw_cache.c
|
||||
intern/draw_cache_impl_curve.c
|
||||
intern/draw_cache_impl_displist.c
|
||||
intern/draw_cache_impl_hair.c
|
||||
intern/draw_cache_impl_lattice.c
|
||||
intern/draw_cache_impl_mesh.c
|
||||
intern/draw_cache_impl_metaball.c
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "BKE_hair.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_pbvh.h"
|
||||
@@ -39,6 +40,7 @@
|
||||
#include "DNA_world_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
|
||||
#include "GPU_material.h"
|
||||
|
||||
@@ -812,6 +814,7 @@ struct GPUMaterial *EEVEE_material_hair_get(
|
||||
options |= eevee_material_shadow_option(shadow_method);
|
||||
|
||||
GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
|
||||
|
||||
if (mat) {
|
||||
return mat;
|
||||
}
|
||||
@@ -862,14 +865,16 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create(
|
||||
**/
|
||||
static struct DRWShadingGroup *EEVEE_default_shading_group_get(
|
||||
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
|
||||
Object *ob, ParticleSystem *psys, ModifierData *md,
|
||||
Object *ob,
|
||||
ParticleSystem *psys, ModifierData *md,
|
||||
HairSystem *hsys, const HairDrawSettings *hdraw, struct Mesh *scalp,
|
||||
bool is_hair, bool is_flat_normal, bool use_ssr, int shadow_method)
|
||||
{
|
||||
static int ssr_id;
|
||||
ssr_id = (use_ssr) ? 1 : -1;
|
||||
int options = VAR_MAT_MESH;
|
||||
|
||||
BLI_assert(!is_hair || (ob && psys && md));
|
||||
BLI_assert(!is_hair || (ob && psys && md) || (ob && hsys && hdraw && scalp));
|
||||
|
||||
if (is_hair) options |= VAR_MAT_HAIR;
|
||||
if (is_flat_normal) options |= VAR_MAT_FLAT;
|
||||
@@ -894,10 +899,19 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(
|
||||
}
|
||||
|
||||
if (is_hair) {
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(ob, psys, md,
|
||||
vedata->psl->default_pass[options],
|
||||
e_data.default_lit[options]);
|
||||
DRWShadingGroup *shgrp = NULL;
|
||||
if (psys && md) {
|
||||
shgrp = DRW_shgroup_particle_hair_create(ob, psys, md,
|
||||
vedata->psl->default_pass[options],
|
||||
e_data.default_lit[options]);
|
||||
}
|
||||
else if (hsys && hdraw && scalp) {
|
||||
shgrp = DRW_shgroup_hair_create(ob, hsys, scalp, hdraw,
|
||||
vedata->psl->default_pass[options],
|
||||
e_data.default_lit[options]);
|
||||
}
|
||||
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL, true, true, false, false, false);
|
||||
|
||||
return shgrp;
|
||||
}
|
||||
else {
|
||||
@@ -1266,7 +1280,7 @@ static void material_opaque(
|
||||
if (*shgrp == NULL) {
|
||||
bool use_ssr = ((effects->enabled_effects & EFFECT_SSR) != 0);
|
||||
*shgrp = EEVEE_default_shading_group_get(sldata, vedata,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
false, use_flat_nor, use_ssr, linfo->shadow_method);
|
||||
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
|
||||
@@ -1409,6 +1423,217 @@ static void material_transparent(
|
||||
}
|
||||
}
|
||||
|
||||
static void material_particle_hair(
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0);
|
||||
|
||||
if (!psys_check_enabled(ob, psys, false)) {
|
||||
return;
|
||||
}
|
||||
if (!DRW_check_psys_visible_within_active_context(ob, psys)) {
|
||||
return;
|
||||
}
|
||||
ParticleSettings *part = psys->part;
|
||||
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
|
||||
if (draw_as != PART_DRAW_PATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
DRWShadingGroup *shgrp = NULL;
|
||||
Material *ma = give_current_material(ob, part->omat);
|
||||
|
||||
if (ma == NULL) {
|
||||
ma = &defmaterial;
|
||||
}
|
||||
|
||||
float *color_p = &ma->r;
|
||||
float *metal_p = &ma->metallic;
|
||||
float *spec_p = &ma->spec;
|
||||
float *rough_p = &ma->roughness;
|
||||
|
||||
shgrp = DRW_shgroup_particle_hair_create(
|
||||
ob, psys, md,
|
||||
psl->depth_pass,
|
||||
e_data.default_hair_prepass_sh);
|
||||
|
||||
shgrp = DRW_shgroup_particle_hair_create(
|
||||
ob, psys, md,
|
||||
psl->depth_pass_clip,
|
||||
e_data.default_hair_prepass_clip_sh);
|
||||
DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
|
||||
|
||||
shgrp = NULL;
|
||||
if (ma->use_nodes && ma->nodetree) {
|
||||
static int ssr_id;
|
||||
ssr_id = (use_ssr) ? 1 : -1;
|
||||
static float half = 0.5f;
|
||||
static float error_col[3] = {1.0f, 0.0f, 1.0f};
|
||||
static float compile_col[3] = {0.5f, 0.5f, 0.5f};
|
||||
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
|
||||
|
||||
switch (GPU_material_status(gpumat)) {
|
||||
case GPU_MAT_SUCCESS:
|
||||
{
|
||||
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
|
||||
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
|
||||
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
|
||||
|
||||
shgrp = DRW_shgroup_material_particle_hair_create(
|
||||
ob, psys, md,
|
||||
psl->material_pass,
|
||||
gpumat);
|
||||
|
||||
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL,
|
||||
use_diffuse, use_glossy, use_refract, false, false);
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_QUEUED:
|
||||
{
|
||||
color_p = compile_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_FAILED:
|
||||
default:
|
||||
color_p = error_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to default shader */
|
||||
if (shgrp == NULL) {
|
||||
shgrp = EEVEE_default_shading_group_get(sldata, vedata,
|
||||
ob, psys, md, NULL, NULL, NULL,
|
||||
true, false, use_ssr,
|
||||
sldata->lamps->shadow_method);
|
||||
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
|
||||
}
|
||||
|
||||
/* Shadows */
|
||||
DRW_shgroup_particle_hair_create(
|
||||
ob, psys, md,
|
||||
psl->shadow_pass,
|
||||
e_data.default_hair_prepass_sh);
|
||||
}
|
||||
|
||||
static void material_hair(
|
||||
EEVEE_Data *vedata,
|
||||
EEVEE_ViewLayerData *sldata,
|
||||
Object *ob,
|
||||
HairSystem *hsys,
|
||||
const HairDrawSettings *draw_set,
|
||||
Material *ma,
|
||||
struct Mesh *scalp)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
const bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0);
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
float mat[4][4];
|
||||
copy_m4_m4(mat, ob->obmat);
|
||||
|
||||
if (draw_set->fiber_mode != HAIR_DRAW_FIBER_CURVES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ma == NULL) {
|
||||
ma = &defmaterial;
|
||||
}
|
||||
|
||||
{
|
||||
/*DRWShadingGroup *shgrp =*/ DRW_shgroup_hair_create(
|
||||
ob, hsys, scalp, draw_set,
|
||||
psl->depth_pass,
|
||||
e_data.default_hair_prepass_sh);
|
||||
}
|
||||
{
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
|
||||
ob, hsys, scalp, draw_set,
|
||||
psl->depth_pass_clip,
|
||||
e_data.default_hair_prepass_clip_sh);
|
||||
DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
|
||||
}
|
||||
|
||||
{
|
||||
float *color_p = &ma->r;
|
||||
float *metal_p = &ma->metallic;
|
||||
float *spec_p = &ma->spec;
|
||||
float *rough_p = &ma->roughness;
|
||||
|
||||
DRWShadingGroup *shgrp = NULL;
|
||||
if (ma->use_nodes && ma->nodetree) {
|
||||
static int ssr_id;
|
||||
ssr_id = (use_ssr) ? 1 : -1;
|
||||
static float half = 0.5f;
|
||||
static float error_col[3] = {1.0f, 0.0f, 1.0f};
|
||||
static float compile_col[3] = {0.5f, 0.5f, 0.5f};
|
||||
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
|
||||
|
||||
switch (GPU_material_status(gpumat)) {
|
||||
case GPU_MAT_SUCCESS:
|
||||
{
|
||||
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
|
||||
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
|
||||
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
|
||||
|
||||
shgrp = DRW_shgroup_material_hair_create(
|
||||
ob, hsys, scalp, draw_set,
|
||||
psl->material_pass,
|
||||
gpumat);
|
||||
|
||||
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL,
|
||||
use_diffuse, use_glossy, use_refract, false, false);
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_QUEUED:
|
||||
{
|
||||
color_p = compile_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_FAILED:
|
||||
default:
|
||||
color_p = error_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to default shader */
|
||||
if (shgrp == NULL) {
|
||||
shgrp = EEVEE_default_shading_group_get(sldata, vedata,
|
||||
ob, NULL, NULL, hsys, draw_set, scalp,
|
||||
true, false, use_ssr,
|
||||
sldata->lamps->shadow_method);
|
||||
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shadows */
|
||||
DRW_shgroup_hair_create(
|
||||
ob, hsys, scalp, draw_set,
|
||||
psl->shadow_pass,
|
||||
e_data.default_hair_prepass_sh);
|
||||
}
|
||||
|
||||
void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
@@ -1600,112 +1825,24 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sld
|
||||
|
||||
void EEVEE_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow)
|
||||
{
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
|
||||
bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0);
|
||||
|
||||
if (ob->type == OB_MESH) {
|
||||
if (ob != draw_ctx->object_edit) {
|
||||
for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
|
||||
if (md->type != eModifierType_ParticleSystem) {
|
||||
continue;
|
||||
if (md->type == eModifierType_ParticleSystem) {
|
||||
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
|
||||
material_particle_hair(vedata, sldata, ob, psys, md);
|
||||
*cast_shadow = true;
|
||||
}
|
||||
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
|
||||
if (!psys_check_enabled(ob, psys, false)) {
|
||||
continue;
|
||||
else if (md->type == eModifierType_Hair) {
|
||||
HairModifierData *hmd = (HairModifierData *)md;
|
||||
|
||||
const int material_index = 1; /* TODO */
|
||||
Material *material = give_current_material(ob, material_index);
|
||||
|
||||
material_hair(vedata, sldata, ob, hmd->hair_system, hmd->draw_settings, material, ob->data);
|
||||
}
|
||||
if (!DRW_check_psys_visible_within_active_context(ob, psys)) {
|
||||
continue;
|
||||
}
|
||||
ParticleSettings *part = psys->part;
|
||||
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
|
||||
if (draw_as != PART_DRAW_PATH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DRWShadingGroup *shgrp = NULL;
|
||||
Material *ma = give_current_material(ob, part->omat);
|
||||
|
||||
if (ma == NULL) {
|
||||
ma = &defmaterial;
|
||||
}
|
||||
|
||||
float *color_p = &ma->r;
|
||||
float *metal_p = &ma->metallic;
|
||||
float *spec_p = &ma->spec;
|
||||
float *rough_p = &ma->roughness;
|
||||
|
||||
shgrp = DRW_shgroup_hair_create(
|
||||
ob, psys, md,
|
||||
psl->depth_pass,
|
||||
e_data.default_hair_prepass_sh);
|
||||
|
||||
shgrp = DRW_shgroup_hair_create(
|
||||
ob, psys, md,
|
||||
psl->depth_pass_clip,
|
||||
e_data.default_hair_prepass_clip_sh);
|
||||
DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
|
||||
|
||||
shgrp = NULL;
|
||||
if (ma->use_nodes && ma->nodetree) {
|
||||
static int ssr_id;
|
||||
ssr_id = (use_ssr) ? 1 : -1;
|
||||
static float half = 0.5f;
|
||||
static float error_col[3] = {1.0f, 0.0f, 1.0f};
|
||||
static float compile_col[3] = {0.5f, 0.5f, 0.5f};
|
||||
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
|
||||
|
||||
switch (GPU_material_status(gpumat)) {
|
||||
case GPU_MAT_SUCCESS:
|
||||
{
|
||||
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
|
||||
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
|
||||
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
|
||||
|
||||
shgrp = DRW_shgroup_material_hair_create(
|
||||
ob, psys, md,
|
||||
psl->material_pass,
|
||||
gpumat);
|
||||
|
||||
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL,
|
||||
use_diffuse, use_glossy, use_refract, false, false);
|
||||
break;
|
||||
}
|
||||
case GPU_MAT_QUEUED:
|
||||
{
|
||||
color_p = compile_col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
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;
|
||||
#endif
|
||||
|
||||
out int DEBUG;
|
||||
|
||||
/* From http://libnoise.sourceforge.net/noisegen/index.html */
|
||||
float integer_noise(int n)
|
||||
{
|
||||
@@ -34,6 +36,7 @@ float integer_noise(int n)
|
||||
void main()
|
||||
{
|
||||
#ifdef HAIR_SHADER
|
||||
DEBUG = hair_get_strand_id();
|
||||
# ifdef V3D_SHADING_TEXTURE_COLOR
|
||||
vec2 uv = hair_get_customdata_vec2(u);
|
||||
# endif
|
||||
|
@@ -633,7 +633,7 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o
|
||||
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ?
|
||||
wpd->prepass_solid_hair_sh :
|
||||
wpd->prepass_texture_hair_sh;
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_particle_hair_create(
|
||||
ob, psys, md,
|
||||
psl->prepass_hair_pass,
|
||||
shader);
|
||||
|
@@ -425,7 +425,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
|
||||
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
|
||||
? wpd->transparent_accum_hair_sh
|
||||
: wpd->transparent_accum_texture_hair_sh;
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_particle_hair_create(
|
||||
ob, psys, md,
|
||||
psl->transparent_accum_pass,
|
||||
shader);
|
||||
@@ -444,7 +444,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
|
||||
if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
|
||||
DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
|
||||
}
|
||||
shgrp = DRW_shgroup_hair_create(ob, psys, md,
|
||||
shgrp = DRW_shgroup_particle_hair_create(ob, psys, md,
|
||||
vedata->psl->object_outline_pass,
|
||||
e_data.object_outline_hair_sh);
|
||||
DRW_shgroup_uniform_int(shgrp, "object_id", &material->object_id, 1);
|
||||
|
@@ -3366,6 +3366,20 @@ GPUBatch *DRW_cache_particles_get_prim(int type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Hair */
|
||||
|
||||
GPUBatch *DRW_cache_hair_get_fibers(struct HairSystem *hsys, const struct HairExportCache *hair_export)
|
||||
{
|
||||
return DRW_hair_batch_cache_get_fibers(hsys, hair_export);
|
||||
}
|
||||
|
||||
GPUBatch *DRW_cache_hair_get_follicle_points(struct HairSystem *hsys, const struct HairExportCache *hair_export)
|
||||
{
|
||||
return DRW_hair_batch_cache_get_follicle_points(hsys, hair_export);
|
||||
}
|
||||
|
||||
/* 3D cursor */
|
||||
GPUBatch *DRW_cache_cursor_get(bool crosshair_lines)
|
||||
{
|
||||
|
@@ -31,6 +31,8 @@ struct GPUMaterial;
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
struct PTCacheEdit;
|
||||
struct HairSystem;
|
||||
struct HairExportCache;
|
||||
|
||||
void DRW_shape_cache_free(void);
|
||||
void DRW_shape_cache_reset(void);
|
||||
@@ -193,6 +195,10 @@ struct GPUBatch *DRW_cache_particles_get_edit_tip_points(
|
||||
struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit);
|
||||
struct GPUBatch *DRW_cache_particles_get_prim(int type);
|
||||
|
||||
/* Hair */
|
||||
struct GPUBatch *DRW_cache_hair_get_fibers(struct HairSystem *hsys, const struct HairExportCache *hair_export);
|
||||
struct GPUBatch *DRW_cache_hair_get_follicle_points(struct HairSystem *hsys, const struct HairExportCache *hair_export);
|
||||
|
||||
/* Metaball */
|
||||
struct GPUBatch *DRW_cache_mball_surface_get(struct Object *ob);
|
||||
struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len);
|
||||
|
@@ -36,6 +36,8 @@ struct ListBase;
|
||||
struct ModifierData;
|
||||
struct ParticleSystem;
|
||||
struct PTCacheEdit;
|
||||
struct HairSystem;
|
||||
struct HairExportCache;
|
||||
|
||||
struct Curve;
|
||||
struct Lattice;
|
||||
@@ -62,6 +64,9 @@ void DRW_particle_batch_cache_free(struct ParticleSystem *psys);
|
||||
void DRW_gpencil_batch_cache_dirty(struct bGPdata *gpd);
|
||||
void DRW_gpencil_batch_cache_free(struct bGPdata *gpd);
|
||||
|
||||
void DRW_hair_batch_cache_dirty(struct HairSystem *hsys, int mode);
|
||||
void DRW_hair_batch_cache_free(struct HairSystem *hsys);
|
||||
|
||||
/* Curve */
|
||||
struct GPUBatch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu, struct CurveCache *ob_curve_cache);
|
||||
struct GPUBatch *DRW_curve_batch_cache_get_normal_edge(
|
||||
@@ -146,4 +151,8 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(
|
||||
struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(
|
||||
struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit);
|
||||
|
||||
/* Hair */
|
||||
struct GPUBatch *DRW_hair_batch_cache_get_fibers(struct HairSystem *hsys, const struct HairExportCache *hair_export);
|
||||
struct GPUBatch *DRW_hair_batch_cache_get_follicle_points(struct HairSystem *hsys, const struct HairExportCache *hair_export);
|
||||
|
||||
#endif /* __DRAW_CACHE_IMPL_H__ */
|
||||
|
556
source/blender/draw/intern/draw_cache_impl_hair.c
Normal file
556
source/blender/draw/intern/draw_cache_impl_hair.c
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2017 by Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file draw_cache_impl_strands.c
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Strands API for render engines
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_hair.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_extensions.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
#include "draw_cache_impl.h" /* own include */
|
||||
#include "draw_hair_private.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Hair GPUBatch Cache */
|
||||
|
||||
/* GPUBatch cache management. */
|
||||
|
||||
typedef struct HairBatchCache {
|
||||
ParticleHairCache hair;
|
||||
|
||||
bool is_dirty;
|
||||
} HairBatchCache;
|
||||
|
||||
static void hair_batch_cache_clear(HairSystem *hsys);
|
||||
|
||||
static bool hair_batch_cache_valid(HairSystem *hsys)
|
||||
{
|
||||
HairBatchCache *cache = hsys->draw_batch_cache;
|
||||
|
||||
if (cache == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hair_batch_cache_init(HairSystem *hsys)
|
||||
{
|
||||
HairBatchCache *cache = hsys->draw_batch_cache;
|
||||
|
||||
if (!cache) {
|
||||
cache = hsys->draw_batch_cache = MEM_callocN(sizeof(*cache), __func__);
|
||||
}
|
||||
else {
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
static HairBatchCache *hair_batch_cache_get(HairSystem *hsys)
|
||||
{
|
||||
// Hair follicle binding needs to be updated after changes
|
||||
BLI_assert(!(hsys->flag & HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING));
|
||||
|
||||
if (!hair_batch_cache_valid(hsys)) {
|
||||
hair_batch_cache_clear(hsys);
|
||||
hair_batch_cache_init(hsys);
|
||||
}
|
||||
return hsys->draw_batch_cache;
|
||||
}
|
||||
|
||||
void DRW_hair_batch_cache_dirty(HairSystem *hsys, int mode)
|
||||
{
|
||||
HairBatchCache *cache = hsys->draw_batch_cache;
|
||||
if (cache == NULL) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_HAIR_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void hair_batch_cache_clear(HairSystem *hsys)
|
||||
{
|
||||
HairBatchCache *cache = hsys->draw_batch_cache;
|
||||
if (cache) {
|
||||
particle_batch_cache_clear_hair(&cache->hair);
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_hair_batch_cache_free(HairSystem *hsys)
|
||||
{
|
||||
hair_batch_cache_clear(hsys);
|
||||
MEM_SAFE_FREE(hsys->draw_batch_cache);
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_count(
|
||||
const HairExportCache *hair_export,
|
||||
ParticleHairCache *cache)
|
||||
{
|
||||
cache->strands_len = hair_export->totfollicles;
|
||||
cache->elems_len = hair_export->totverts - hair_export->totcurves;
|
||||
cache->point_len = hair_export->totverts;
|
||||
}
|
||||
|
||||
static void hair_batch_cache_fill_segments_proc_pos(
|
||||
const HairExportCache *hair_export,
|
||||
GPUVertBufRaw *attr_step)
|
||||
{
|
||||
for (int i = 0; i < hair_export->totcurves; i++) {
|
||||
const HairFiberCurve *curve = &hair_export->fiber_curves[i];
|
||||
const HairFiberVertex *verts = &hair_export->fiber_verts[curve->vertstart];
|
||||
if (curve->numverts < 2) {
|
||||
continue;
|
||||
}
|
||||
float total_len = 0.0f;
|
||||
const float *co_prev = NULL;
|
||||
float *seg_data_first;
|
||||
for (int j = 0; j < curve->numverts; j++) {
|
||||
float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step);
|
||||
copy_v3_v3(seg_data, verts[j].co);
|
||||
if (co_prev) {
|
||||
total_len += len_v3v3(co_prev, verts[j].co);
|
||||
}
|
||||
else {
|
||||
seg_data_first = seg_data;
|
||||
}
|
||||
seg_data[3] = total_len;
|
||||
co_prev = verts[j].co;
|
||||
}
|
||||
if (total_len > 0.0f) {
|
||||
/* Divide by total length to have a [0-1] number. */
|
||||
for (int j = 0; j < curve->numverts; j++, seg_data_first += 4) {
|
||||
seg_data_first[3] /= total_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_procedural_pos(
|
||||
const HairExportCache *hair_export,
|
||||
ParticleHairCache *cache)
|
||||
{
|
||||
if (cache->proc_point_buf != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialize vertex format */
|
||||
GPUVertFormat format = {0};
|
||||
uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
|
||||
cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
|
||||
GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
|
||||
|
||||
GPUVertBufRaw pos_step;
|
||||
GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
|
||||
|
||||
hair_batch_cache_fill_segments_proc_pos(hair_export, &pos_step);
|
||||
|
||||
/* Create vbo immediatly to bind to texture buffer. */
|
||||
GPU_vertbuf_use(cache->proc_point_buf);
|
||||
|
||||
cache->point_tex = GPU_texture_create_from_vertbuf(cache->proc_point_buf);
|
||||
}
|
||||
|
||||
static void hair_pack_mcol(MCol *mcol, unsigned short r_scol[3])
|
||||
{
|
||||
/* Convert to linear ushort and swizzle */
|
||||
r_scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
|
||||
r_scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
|
||||
r_scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
|
||||
}
|
||||
|
||||
static int hair_batch_cache_fill_strands_data(
|
||||
const HairExportCache *hair_export,
|
||||
GPUVertBufRaw *data_step,
|
||||
GPUVertBufRaw *uv_step, int num_uv_layers,
|
||||
GPUVertBufRaw *col_step, int num_col_layers)
|
||||
{
|
||||
int curr_point = 0;
|
||||
for (int i = 0; i < hair_export->totcurves; i++) {
|
||||
const HairFiberCurve *curve = &hair_export->fiber_curves[i];
|
||||
if (curve->numverts < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint *seg_data = (uint *)GPU_vertbuf_raw_step(data_step);
|
||||
const uint numseg = curve->numverts - 1;
|
||||
*seg_data = (curr_point & 0xFFFFFF) | (numseg << 24);
|
||||
curr_point += curve->numverts;
|
||||
|
||||
float (*uv)[2] = NULL;
|
||||
MCol *mcol = NULL;
|
||||
|
||||
#if 0
|
||||
particle_calculate_uvs(
|
||||
psys, psmd,
|
||||
is_simple, num_uv_layers,
|
||||
is_child ? psys->child[i].parent : i,
|
||||
is_child ? i : -1,
|
||||
mtfaces,
|
||||
*r_parent_uvs, &uv);
|
||||
|
||||
particle_calculate_mcol(
|
||||
psys, psmd,
|
||||
is_simple, num_col_layers,
|
||||
is_child ? psys->child[i].parent : i,
|
||||
is_child ? i : -1,
|
||||
mcols,
|
||||
*r_parent_mcol, &mcol);
|
||||
#else
|
||||
/* XXX dummy uvs and mcols, TODO */
|
||||
uv = MEM_mallocN(sizeof(*uv) * num_uv_layers, __func__);
|
||||
mcol = MEM_mallocN(sizeof(*mcol) * num_col_layers, __func__);
|
||||
for (int k = 0; k < num_uv_layers; k++) {
|
||||
zero_v3(uv[k]);
|
||||
}
|
||||
for (int k = 0; k < num_col_layers; k++) {
|
||||
mcol[k].a = 255;
|
||||
mcol[k].r = 255;
|
||||
mcol[k].g = 0;
|
||||
mcol[k].b = 255;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int k = 0; k < num_uv_layers; k++) {
|
||||
float *t_uv = (float *)GPU_vertbuf_raw_step(uv_step + k);
|
||||
copy_v2_v2(t_uv, uv[k]);
|
||||
}
|
||||
for (int k = 0; k < num_col_layers; k++) {
|
||||
unsigned short *scol = (unsigned short *)GPU_vertbuf_raw_step(col_step + k);
|
||||
hair_pack_mcol(&mcol[k], scol);
|
||||
}
|
||||
|
||||
if (uv) {
|
||||
MEM_freeN(uv);
|
||||
}
|
||||
if (mcol) {
|
||||
MEM_freeN(mcol);
|
||||
}
|
||||
}
|
||||
return curr_point;
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_procedural_strand_data(
|
||||
const HairExportCache *hair_export,
|
||||
ParticleHairCache *cache)
|
||||
{
|
||||
int active_uv = 0;
|
||||
int active_col = 0;
|
||||
|
||||
#if 0 // TODO
|
||||
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
|
||||
|
||||
if (psmd != NULL && psmd->mesh_final != NULL) {
|
||||
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPUV)) {
|
||||
cache->num_uv_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPUV);
|
||||
active_uv = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPUV);
|
||||
}
|
||||
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL)) {
|
||||
cache->num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPCOL);
|
||||
active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
GPUVertBufRaw data_step;
|
||||
GPUVertBufRaw uv_step[MAX_MTFACE];
|
||||
GPUVertBufRaw col_step[MAX_MCOL];
|
||||
|
||||
MTFace *mtfaces[MAX_MTFACE] = {NULL};
|
||||
MCol *mcols[MAX_MCOL] = {NULL};
|
||||
|
||||
GPUVertFormat format_data = {0};
|
||||
uint data_id = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
||||
|
||||
GPUVertFormat format_uv = {0};
|
||||
uint uv_id = GPU_vertformat_attr_add(&format_uv, "uv", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
|
||||
GPUVertFormat format_col = {0};
|
||||
uint col_id = GPU_vertformat_attr_add(&format_col, "col", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
|
||||
|
||||
memset(cache->uv_layer_names, 0, sizeof(cache->uv_layer_names));
|
||||
memset(cache->col_layer_names, 0, sizeof(cache->col_layer_names));
|
||||
|
||||
/* Strand Data */
|
||||
cache->proc_strand_buf = GPU_vertbuf_create_with_format(&format_data);
|
||||
GPU_vertbuf_data_alloc(cache->proc_strand_buf, cache->strands_len);
|
||||
GPU_vertbuf_attr_get_raw_data(cache->proc_strand_buf, data_id, &data_step);
|
||||
|
||||
#if 0 // TODO
|
||||
/* UV layers */
|
||||
for (int i = 0; i < cache->num_uv_layers; i++) {
|
||||
cache->proc_uv_buf[i] = GPU_vertbuf_create_with_format(&format_uv);
|
||||
GPU_vertbuf_data_alloc(cache->proc_uv_buf[i], cache->strands_len);
|
||||
GPU_vertbuf_attr_get_raw_data(cache->proc_uv_buf[i], uv_id, &uv_step[i]);
|
||||
|
||||
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i);
|
||||
uint hash = BLI_ghashutil_strhash_p(name);
|
||||
int n = 0;
|
||||
BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "u%u", hash);
|
||||
BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash);
|
||||
|
||||
if (i == active_uv) {
|
||||
BLI_snprintf(cache->uv_layer_names[i][n], MAX_LAYER_NAME_LEN, "u");
|
||||
}
|
||||
}
|
||||
/* Vertex colors */
|
||||
for (int i = 0; i < cache->num_col_layers; i++) {
|
||||
cache->proc_col_buf[i] = GPU_vertbuf_create_with_format(&format_col);
|
||||
GPU_vertbuf_data_alloc(cache->proc_col_buf[i], cache->strands_len);
|
||||
GPU_vertbuf_attr_get_raw_data(cache->proc_col_buf[i], col_id, &col_step[i]);
|
||||
|
||||
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPCOL, i);
|
||||
uint hash = BLI_ghashutil_strhash_p(name);
|
||||
int n = 0;
|
||||
BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "c%u", hash);
|
||||
|
||||
/* We only do vcols auto name that are not overridden by uvs */
|
||||
if (CustomData_get_named_layer_index(&psmd->mesh_final->ldata, CD_MLOOPUV, name) == -1) {
|
||||
BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash);
|
||||
}
|
||||
|
||||
if (i == active_col) {
|
||||
BLI_snprintf(cache->col_layer_names[i][n], MAX_LAYER_NAME_LEN, "c");
|
||||
}
|
||||
}
|
||||
|
||||
if (cache->num_uv_layers || cache->num_col_layers) {
|
||||
BKE_mesh_tessface_ensure(psmd->mesh_final);
|
||||
if (cache->num_uv_layers) {
|
||||
for (int j = 0; j < cache->num_uv_layers; j++) {
|
||||
mtfaces[j] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, j);
|
||||
}
|
||||
}
|
||||
if (cache->num_col_layers) {
|
||||
for (int j = 0; j < cache->num_col_layers; j++) {
|
||||
mcols[j] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
hair_batch_cache_fill_strands_data(
|
||||
hair_export,
|
||||
&data_step,
|
||||
uv_step, cache->num_uv_layers,
|
||||
col_step, cache->num_col_layers);
|
||||
|
||||
/* Create vbo immediatly to bind to texture buffer. */
|
||||
GPU_vertbuf_use(cache->proc_strand_buf);
|
||||
cache->strand_tex = GPU_texture_create_from_vertbuf(cache->proc_strand_buf);
|
||||
|
||||
for (int i = 0; i < cache->num_uv_layers; i++) {
|
||||
GPU_vertbuf_use(cache->proc_uv_buf[i]);
|
||||
cache->uv_tex[i] = GPU_texture_create_from_vertbuf(cache->proc_uv_buf[i]);
|
||||
}
|
||||
for (int i = 0; i < cache->num_col_layers; i++) {
|
||||
GPU_vertbuf_use(cache->proc_col_buf[i]);
|
||||
cache->col_tex[i] = GPU_texture_create_from_vertbuf(cache->proc_col_buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_final_count(
|
||||
const HairExportCache *hair_export,
|
||||
ParticleHairFinalCache *cache,
|
||||
int subdiv,
|
||||
int thickness_res)
|
||||
{
|
||||
const int totverts = hair_export->totverts;
|
||||
const int totcurves = hair_export->totcurves;
|
||||
cache->strands_len = hair_export->totfollicles;
|
||||
/* +1 for primitive restart */
|
||||
cache->elems_len = (((totverts - totcurves) << subdiv) + totcurves) * thickness_res;
|
||||
cache->point_len = ((totverts - totcurves) << subdiv) + totcurves;
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_procedural_final_points(
|
||||
ParticleHairCache *cache,
|
||||
int subdiv)
|
||||
{
|
||||
/* Same format as point_tex. */
|
||||
GPUVertFormat format = { 0 };
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
|
||||
cache->final[subdiv].proc_point_buf = GPU_vertbuf_create_with_format(&format);
|
||||
|
||||
/* Create a destination buffer for the tranform feedback. Sized appropriately */
|
||||
/* Thoses are points! not line segments. */
|
||||
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_point_buf, cache->final[subdiv].point_len);
|
||||
|
||||
/* Create vbo immediatly to bind to texture buffer. */
|
||||
GPU_vertbuf_use(cache->final[subdiv].proc_point_buf);
|
||||
|
||||
cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_point_buf);
|
||||
}
|
||||
|
||||
static int hair_batch_cache_fill_segments_indices(
|
||||
const HairExportCache *hair_export,
|
||||
const int subdiv,
|
||||
const int thickness_res,
|
||||
GPUIndexBufBuilder *elb)
|
||||
{
|
||||
int curr_point = 0;
|
||||
for (int i = 0; i < hair_export->totcurves; i++) {
|
||||
const HairFiberCurve *curve = &hair_export->fiber_curves[i];
|
||||
if (curve->numverts < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int res = (((curve->numverts - 1) << subdiv) + 1) * thickness_res;
|
||||
for (int k = 0; k < res; k++) {
|
||||
GPU_indexbuf_add_generic_vert(elb, curr_point++);
|
||||
}
|
||||
GPU_indexbuf_add_primitive_restart(elb);
|
||||
}
|
||||
return curr_point;
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_procedural_indices(
|
||||
const HairExportCache *hair_export,
|
||||
ParticleHairCache *cache,
|
||||
int thickness_res,
|
||||
int subdiv)
|
||||
{
|
||||
BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
|
||||
|
||||
if (cache->final[subdiv].proc_hairs[thickness_res - 1] != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int element_count = cache->final[subdiv].elems_len;
|
||||
GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
|
||||
|
||||
static GPUVertFormat format = { 0 };
|
||||
GPU_vertformat_clear(&format);
|
||||
|
||||
/* initialize vertex format */
|
||||
GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
|
||||
|
||||
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
|
||||
GPU_vertbuf_data_alloc(vbo, 1);
|
||||
|
||||
GPUIndexBufBuilder elb;
|
||||
GPU_indexbuf_init_ex(&elb, prim_type, element_count, element_count, true);
|
||||
|
||||
hair_batch_cache_fill_segments_indices(hair_export, subdiv, thickness_res, &elb);
|
||||
|
||||
cache->final[subdiv].proc_hairs[thickness_res - 1] = GPU_batch_create_ex(
|
||||
prim_type,
|
||||
vbo,
|
||||
GPU_indexbuf_build(&elb),
|
||||
GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
|
||||
}
|
||||
|
||||
/* Ensure all textures and buffers needed for GPU accelerated drawing. */
|
||||
bool hair_ensure_procedural_data(
|
||||
Object *UNUSED(object),
|
||||
HairSystem *hsys,
|
||||
struct Mesh *scalp,
|
||||
ParticleHairCache **r_hair_cache,
|
||||
int subdiv,
|
||||
int thickness_res)
|
||||
{
|
||||
bool need_ft_update = false;
|
||||
|
||||
HairExportCache *hair_export = BKE_hair_export_cache_new();
|
||||
BKE_hair_export_cache_update(hair_export, hsys, subdiv, scalp, HAIR_EXPORT_ALL);
|
||||
|
||||
HairBatchCache *cache = hair_batch_cache_get(hsys);
|
||||
*r_hair_cache = &cache->hair;
|
||||
|
||||
/* Refreshed on combing and simulation. */
|
||||
if (cache->hair.proc_point_buf == NULL) {
|
||||
hair_batch_cache_ensure_count(hair_export, &cache->hair);
|
||||
|
||||
hair_batch_cache_ensure_procedural_pos(hair_export, &cache->hair);
|
||||
need_ft_update = true;
|
||||
}
|
||||
|
||||
/* Refreshed if active layer or custom data changes. */
|
||||
if (cache->hair.strand_tex == NULL) {
|
||||
hair_batch_cache_ensure_procedural_strand_data(hair_export, &cache->hair);
|
||||
}
|
||||
|
||||
/* Refreshed only on subdiv count change. */
|
||||
if (cache->hair.final[subdiv].proc_point_buf == NULL) {
|
||||
hair_batch_cache_ensure_final_count(hair_export, &cache->hair.final[subdiv], subdiv, thickness_res);
|
||||
|
||||
hair_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
|
||||
need_ft_update = true;
|
||||
}
|
||||
if (cache->hair.final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
|
||||
hair_batch_cache_ensure_procedural_indices(hair_export, &cache->hair, thickness_res, subdiv);
|
||||
}
|
||||
|
||||
BKE_hair_export_cache_free(hair_export);
|
||||
|
||||
return need_ft_update;
|
||||
}
|
||||
|
||||
GPUBatch *DRW_hair_batch_cache_get_fibers(HairSystem *hsys, const HairExportCache *hair_export)
|
||||
{
|
||||
// TODO
|
||||
UNUSED_VARS(hsys, hair_export);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GPUBatch *DRW_hair_batch_cache_get_follicle_points(HairSystem *hsys, const HairExportCache *hair_export)
|
||||
{
|
||||
// TODO
|
||||
UNUSED_VARS(hsys, hair_export);
|
||||
return NULL;
|
||||
}
|
@@ -162,37 +162,6 @@ static void particle_batch_cache_clear_point(ParticlePointCache *point_cache)
|
||||
GPU_VERTBUF_DISCARD_SAFE(point_cache->pos);
|
||||
}
|
||||
|
||||
static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
|
||||
{
|
||||
/* TODO more granular update tagging. */
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
|
||||
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->strand_tex);
|
||||
|
||||
for (int i = 0; i < MAX_MTFACE; ++i) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_uv_buf[i]);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->uv_tex[i]);
|
||||
}
|
||||
for (int i = 0; i < MAX_MCOL; ++i) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_col_buf[i]);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->col_tex[i]);
|
||||
}
|
||||
for (int i = 0; i < MAX_HAIR_SUBDIV; ++i) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->final[i].proc_buf);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->final[i].proc_tex);
|
||||
for (int j = 0; j < MAX_THICKRES; ++j) {
|
||||
GPU_BATCH_DISCARD_SAFE(hair_cache->final[i].proc_hairs[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/* "Normal" legacy hairs */
|
||||
GPU_BATCH_DISCARD_SAFE(hair_cache->hairs);
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->pos);
|
||||
GPU_INDEXBUF_DISCARD_SAFE(hair_cache->indices);
|
||||
}
|
||||
|
||||
static void particle_batch_cache_clear(ParticleSystem *psys)
|
||||
{
|
||||
ParticleBatchCache *cache = psys->batch_cache;
|
||||
@@ -654,19 +623,26 @@ static void particle_batch_cache_fill_segments_proc_pos(
|
||||
}
|
||||
|
||||
static int particle_batch_cache_fill_segments_indices(
|
||||
const ParticleSystem *psys,
|
||||
ParticleCacheKey **path_cache,
|
||||
const int start_index,
|
||||
const int num_path_keys,
|
||||
const int res,
|
||||
const int subdiv,
|
||||
const int thickness_res,
|
||||
GPUIndexBufBuilder *elb)
|
||||
{
|
||||
const ParticleSettings *part = psys->part;
|
||||
const int points_per_curve = (1 << (part->draw_step + subdiv)) + 1;
|
||||
const int points_per_hair = points_per_curve * thickness_res;
|
||||
|
||||
int curr_point = start_index;
|
||||
for (int i = 0; i < num_path_keys; i++) {
|
||||
ParticleCacheKey *path = path_cache[i];
|
||||
if (path->segments <= 0) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < res; k++) {
|
||||
|
||||
for (int k = 0; k < points_per_hair; k++) {
|
||||
GPU_indexbuf_add_generic_vert(elb, curr_point++);
|
||||
}
|
||||
GPU_indexbuf_add_primitive_restart(elb);
|
||||
@@ -749,24 +725,93 @@ static int particle_batch_cache_fill_strands_data(
|
||||
return curr_point;
|
||||
}
|
||||
|
||||
static void ensure_seg_pt_final_count(
|
||||
const ParticleSystem *psys,
|
||||
ParticleHairCache *hair_cache,
|
||||
int subdiv,
|
||||
int thickness_res)
|
||||
{
|
||||
ParticleHairFinalCache *final_cache = &hair_cache->final[subdiv];
|
||||
|
||||
const ParticleSettings *part = psys->part;
|
||||
const int points_per_curve = (1 << (part->draw_step + subdiv)) + 1;
|
||||
|
||||
final_cache->strands_len = hair_cache->strands_len;
|
||||
final_cache->point_len = points_per_curve * final_cache->strands_len;
|
||||
|
||||
/* +1 for primitive restart */
|
||||
final_cache->elems_len = (points_per_curve * thickness_res + 1) * final_cache->strands_len;
|
||||
}
|
||||
|
||||
#define USE_POSITION_HAIR_INDEX
|
||||
|
||||
static void particle_batch_cache_ensure_procedural_final_points(
|
||||
const ParticleSystem *psys,
|
||||
ParticleHairCache *cache,
|
||||
int subdiv)
|
||||
{
|
||||
|
||||
/* Same format as point_tex. */
|
||||
#ifdef USE_POSITION_HAIR_INDEX
|
||||
static GPUVertFormat format = { 0 };
|
||||
GPU_vertformat_clear(&format);
|
||||
uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
#else
|
||||
static GPUVer format = { 0 };
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
#endif
|
||||
|
||||
cache->final[subdiv].proc_point_buf = GPU_vertbuf_create_with_format(&format);
|
||||
|
||||
/* Create a destination buffer for the tranform feedback. Sized appropriately */
|
||||
/* Thoses are points! not line segments. */
|
||||
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_point_buf, cache->final[subdiv].point_len);
|
||||
|
||||
#ifdef USE_POSITION_HAIR_INDEX
|
||||
GPUVertBufRaw data_step;
|
||||
GPU_vertbuf_attr_get_raw_data(cache->final[subdiv].proc_point_buf, pos_id, &data_step);
|
||||
const int points_per_curve = (1 << (psys->part->draw_step + subdiv)) + 1;
|
||||
for (int i = 0; i < cache->final[subdiv].strands_len; i++) {
|
||||
for (int j = 0; j < points_per_curve; ++j) {
|
||||
uint *data = (uint *)GPU_vertbuf_raw_step(&data_step);
|
||||
*data = (uint)i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create vbo immediatly to bind to texture buffer. */
|
||||
GPU_vertbuf_use(cache->final[subdiv].proc_point_buf);
|
||||
|
||||
cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_point_buf);
|
||||
}
|
||||
|
||||
static void particle_batch_cache_ensure_procedural_final_hair_index(
|
||||
const ParticleSystem *psys,
|
||||
ParticleHairCache *cache,
|
||||
int subdiv)
|
||||
{
|
||||
/* Same format as point_tex. */
|
||||
GPUVertFormat format = { 0 };
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
uint hair_index_id = GPU_vertformat_attr_add(&format, "hair_index", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
||||
|
||||
cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format);
|
||||
cache->final[subdiv].proc_hair_index_buf = GPU_vertbuf_create_with_format(&format);
|
||||
|
||||
/* Create a destination buffer for the tranform feedback. Sized appropriately */
|
||||
/* Thoses are points! not line segments. */
|
||||
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_buf, cache->final[subdiv].strands_res * cache->strands_len);
|
||||
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_hair_index_buf, cache->final[subdiv].point_len);
|
||||
|
||||
GPUVertBufRaw data_step;
|
||||
GPU_vertbuf_attr_get_raw_data(cache->final[subdiv].proc_hair_index_buf, hair_index_id, &data_step);
|
||||
const int points_per_curve = (1 << (psys->part->draw_step + subdiv)) + 1;
|
||||
for (int i = 0; i < cache->final[subdiv].strands_len; i++) {
|
||||
for (int j = 0; j < points_per_curve; ++j) {
|
||||
uint *data = (uint *)GPU_vertbuf_raw_step(&data_step);
|
||||
*data = (uint)i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create vbo immediatly to bind to texture buffer. */
|
||||
GPU_vertbuf_use(cache->final[subdiv].proc_buf);
|
||||
GPU_vertbuf_use(cache->final[subdiv].proc_hair_index_buf);
|
||||
|
||||
cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_buf);
|
||||
cache->final[subdiv].hair_index_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_hair_index_buf);
|
||||
}
|
||||
|
||||
static void particle_batch_cache_ensure_procedural_strand_data(
|
||||
@@ -940,9 +985,7 @@ static void particle_batch_cache_ensure_procedural_indices(
|
||||
return;
|
||||
}
|
||||
|
||||
int verts_per_hair = cache->final[subdiv].strands_res * thickness_res;
|
||||
/* +1 for primitive restart */
|
||||
int element_count = (verts_per_hair + 1) * cache->strands_len;
|
||||
int element_count = cache->final[subdiv].elems_len;
|
||||
GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
|
||||
|
||||
static GPUVertFormat format = { 0 };
|
||||
@@ -959,7 +1002,7 @@ static void particle_batch_cache_ensure_procedural_indices(
|
||||
|
||||
if (edit != NULL && edit->pathcache != NULL) {
|
||||
particle_batch_cache_fill_segments_indices(
|
||||
edit->pathcache, 0, edit->totcached, verts_per_hair, &elb);
|
||||
psys, edit->pathcache, 0, edit->totcached, subdiv, thickness_res, &elb);
|
||||
}
|
||||
else {
|
||||
int curr_point = 0;
|
||||
@@ -967,12 +1010,12 @@ static void particle_batch_cache_ensure_procedural_indices(
|
||||
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
|
||||
{
|
||||
curr_point = particle_batch_cache_fill_segments_indices(
|
||||
psys->pathcache, 0, psys->totpart, verts_per_hair, &elb);
|
||||
psys, psys->pathcache, 0, psys->totpart, subdiv, thickness_res, &elb);
|
||||
}
|
||||
if (psys->childcache) {
|
||||
const int child_count = psys->totchild * psys->part->disp / 100;
|
||||
curr_point = particle_batch_cache_fill_segments_indices(
|
||||
psys->childcache, curr_point, child_count, verts_per_hair, &elb);
|
||||
psys, psys->childcache, curr_point, child_count, subdiv, thickness_res, &elb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1555,12 +1598,9 @@ bool particles_ensure_procedural_data(
|
||||
ParticleDrawSource source;
|
||||
drw_particle_get_hair_source(object, psys, md, NULL, &source);
|
||||
|
||||
ParticleSettings *part = source.psys->part;
|
||||
ParticleBatchCache *cache = particle_batch_cache_get(source.psys);
|
||||
*r_hair_cache = &cache->hair;
|
||||
|
||||
(*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv);
|
||||
|
||||
/* Refreshed on combing and simulation. */
|
||||
if ((*r_hair_cache)->proc_point_buf == NULL) {
|
||||
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
|
||||
@@ -1574,8 +1614,10 @@ bool particles_ensure_procedural_data(
|
||||
}
|
||||
|
||||
/* Refreshed only on subdiv count change. */
|
||||
if ((*r_hair_cache)->final[subdiv].proc_buf == NULL) {
|
||||
particle_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
|
||||
if ((*r_hair_cache)->final[subdiv].proc_point_buf == NULL) {
|
||||
ensure_seg_pt_final_count(psys, &cache->hair, subdiv, thickness_res);
|
||||
particle_batch_cache_ensure_procedural_final_points(psys, &cache->hair, subdiv);
|
||||
particle_batch_cache_ensure_procedural_final_hair_index(psys, &cache->hair, subdiv);
|
||||
need_ft_update = true;
|
||||
}
|
||||
if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
|
||||
|
@@ -26,11 +26,17 @@
|
||||
#ifndef __DRAW_COMMON_H__
|
||||
#define __DRAW_COMMON_H__
|
||||
|
||||
struct Mesh;
|
||||
struct DRWPass;
|
||||
struct DRWShadingGroup;
|
||||
struct GPUBatch;
|
||||
struct GPUMaterial;
|
||||
struct GPUShader;
|
||||
struct GPUTexture;
|
||||
struct HairDrawSettings;
|
||||
struct HairSystem;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
struct ModifierData;
|
||||
struct ParticleSystem;
|
||||
@@ -162,16 +168,28 @@ void DRW_shgroup_armature_edit(struct Object *ob, struct DRWArmaturePasses passe
|
||||
|
||||
/* This creates a shading group with display hairs.
|
||||
* The draw call is already added by this function, just add additional uniforms. */
|
||||
struct DRWShadingGroup *DRW_shgroup_hair_create(
|
||||
struct DRWShadingGroup *DRW_shgroup_particle_hair_create(
|
||||
struct Object *object, struct ParticleSystem *psys, struct ModifierData *md,
|
||||
struct DRWPass *hair_pass,
|
||||
struct GPUShader *shader);
|
||||
|
||||
struct DRWShadingGroup *DRW_shgroup_material_hair_create(
|
||||
struct DRWShadingGroup *DRW_shgroup_material_particle_hair_create(
|
||||
struct Object *object, struct ParticleSystem *psys, struct ModifierData *md,
|
||||
struct DRWPass *hair_pass,
|
||||
struct GPUMaterial *material);
|
||||
|
||||
struct DRWShadingGroup *DRW_shgroup_hair_create(
|
||||
struct Object *object, struct HairSystem *hsys,
|
||||
struct Mesh *scalp, const struct HairDrawSettings *draw_set,
|
||||
struct DRWPass *hair_pass,
|
||||
struct GPUShader *shader);
|
||||
|
||||
struct DRWShadingGroup *DRW_shgroup_material_hair_create(
|
||||
struct Object *object, struct HairSystem *hsys,
|
||||
struct Mesh *scalp, const struct HairDrawSettings *draw_set,
|
||||
struct DRWPass *hair_pass,
|
||||
struct GPUMaterial *material);
|
||||
|
||||
void DRW_hair_init(void);
|
||||
void DRW_hair_update(void);
|
||||
void DRW_hair_free(void);
|
||||
|
@@ -34,12 +34,14 @@
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "BKE_hair.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_pointcache.h"
|
||||
@@ -85,7 +87,38 @@ void DRW_hair_init(void)
|
||||
g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_TRANS_FEEDBACK);
|
||||
}
|
||||
|
||||
static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
|
||||
void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
|
||||
{
|
||||
/* TODO more granular update tagging. */
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
|
||||
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->strand_tex);
|
||||
|
||||
for (int i = 0; i < MAX_MTFACE; ++i) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_uv_buf[i]);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->uv_tex[i]);
|
||||
}
|
||||
for (int i = 0; i < MAX_MCOL; ++i) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_col_buf[i]);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->col_tex[i]);
|
||||
}
|
||||
for (int i = 0; i < MAX_HAIR_SUBDIV; ++i) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->final[i].proc_point_buf);
|
||||
DRW_TEXTURE_FREE_SAFE(hair_cache->final[i].proc_tex);
|
||||
for (int j = 0; j < MAX_THICKRES; ++j) {
|
||||
GPU_BATCH_DISCARD_SAFE(hair_cache->final[i].proc_hairs[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/* "Normal" legacy hairs */
|
||||
GPU_BATCH_DISCARD_SAFE(hair_cache->hairs);
|
||||
GPU_VERTBUF_DISCARD_SAFE(hair_cache->pos);
|
||||
GPU_INDEXBUF_DISCARD_SAFE(hair_cache->indices);
|
||||
}
|
||||
|
||||
static DRWShadingGroup *drw_shgroup_create_particle_hair_procedural_ex(
|
||||
Object *object, ParticleSystem *psys, ModifierData *md,
|
||||
DRWPass *hair_pass,
|
||||
struct GPUMaterial *gpu_mat, GPUShader *gpu_shader)
|
||||
@@ -126,7 +159,7 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
|
||||
}
|
||||
|
||||
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
|
||||
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
|
||||
DRW_shgroup_uniform_texture(shgrp, "hairIndexBuffer", hair_cache->final[subdiv].hair_index_tex);
|
||||
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
|
||||
DRW_shgroup_uniform_float(shgrp, "hairRadShape", &part->shape, 1);
|
||||
DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", part->rad_root * part->rad_scale * 0.5f);
|
||||
@@ -137,33 +170,110 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
|
||||
|
||||
/* Transform Feedback subdiv. */
|
||||
if (need_ft_update) {
|
||||
int final_points_len = hair_cache->final[subdiv].strands_res * hair_cache->strands_len;
|
||||
GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
|
||||
DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass,
|
||||
hair_cache->final[subdiv].proc_buf);
|
||||
hair_cache->final[subdiv].proc_point_buf);
|
||||
DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", hair_cache->point_tex);
|
||||
DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", hair_cache->strand_tex);
|
||||
DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
|
||||
DRW_shgroup_call_procedural_points_add(tf_shgrp, final_points_len, NULL);
|
||||
DRW_shgroup_call_procedural_points_add(tf_shgrp, hair_cache->final[subdiv].point_len, NULL);
|
||||
}
|
||||
|
||||
return shgrp;
|
||||
}
|
||||
|
||||
DRWShadingGroup *DRW_shgroup_hair_create(
|
||||
DRWShadingGroup *DRW_shgroup_particle_hair_create(
|
||||
Object *object, ParticleSystem *psys, ModifierData *md,
|
||||
DRWPass *hair_pass,
|
||||
GPUShader *shader)
|
||||
{
|
||||
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, shader);
|
||||
return drw_shgroup_create_particle_hair_procedural_ex(object, psys, md, hair_pass, NULL, shader);
|
||||
}
|
||||
|
||||
DRWShadingGroup *DRW_shgroup_material_hair_create(
|
||||
DRWShadingGroup *DRW_shgroup_material_particle_hair_create(
|
||||
Object *object, ParticleSystem *psys, ModifierData *md,
|
||||
DRWPass *hair_pass,
|
||||
struct GPUMaterial *material)
|
||||
{
|
||||
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, material, NULL);
|
||||
return drw_shgroup_create_particle_hair_procedural_ex(object, psys, md, hair_pass, material, NULL);
|
||||
}
|
||||
|
||||
static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
|
||||
Object *object, HairSystem *hsys, Mesh *scalp, const HairDrawSettings *draw_set,
|
||||
DRWPass *hair_pass,
|
||||
struct GPUMaterial *gpu_mat, GPUShader *gpu_shader)
|
||||
{
|
||||
/* TODO(fclem): Pass the scene as parameter */
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
|
||||
int subdiv = scene->r.hair_subdiv;
|
||||
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
|
||||
|
||||
ParticleHairCache *hair_cache;
|
||||
bool need_ft_update = hair_ensure_procedural_data(object, hsys, scalp, &hair_cache, subdiv, thickness_res);
|
||||
|
||||
DRWShadingGroup *shgrp;
|
||||
if (gpu_mat) {
|
||||
shgrp = DRW_shgroup_material_create(gpu_mat, hair_pass);
|
||||
}
|
||||
else if (gpu_shader) {
|
||||
shgrp = DRW_shgroup_create(gpu_shader, hair_pass);
|
||||
}
|
||||
else {
|
||||
shgrp = NULL;
|
||||
BLI_assert(0);
|
||||
}
|
||||
|
||||
/* TODO optimize this. Only bind the ones GPUMaterial needs. */
|
||||
for (int i = 0; i < hair_cache->num_uv_layers; ++i) {
|
||||
for (int n = 0; hair_cache->uv_layer_names[i][n][0] != '\0'; ++n) {
|
||||
DRW_shgroup_uniform_texture(shgrp, hair_cache->uv_layer_names[i][n], hair_cache->uv_tex[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < hair_cache->num_col_layers; ++i) {
|
||||
for (int n = 0; hair_cache->col_layer_names[i][n][0] != '\0'; ++n) {
|
||||
DRW_shgroup_uniform_texture(shgrp, hair_cache->col_layer_names[i][n], hair_cache->col_tex[i]);
|
||||
}
|
||||
}
|
||||
|
||||
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
|
||||
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
|
||||
DRW_shgroup_uniform_float(shgrp, "hairRadShape", &draw_set->shape, 1);
|
||||
DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", draw_set->root_radius * draw_set->radius_scale * 0.5f);
|
||||
DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", draw_set->tip_radius * draw_set->radius_scale * 0.5f);
|
||||
DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", (draw_set->shape_flag & PART_SHAPE_CLOSE_TIP) != 0);
|
||||
/* TODO(fclem): Until we have a better way to cull the hair and render with orco, bypass culling test. */
|
||||
DRW_shgroup_call_object_add_no_cull(shgrp, hair_cache->final[subdiv].proc_hairs[thickness_res - 1], object);
|
||||
|
||||
/* Transform Feedback subdiv. */
|
||||
if (need_ft_update) {
|
||||
GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
|
||||
DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass,
|
||||
hair_cache->final[subdiv].proc_point_buf);
|
||||
DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", hair_cache->point_tex);
|
||||
DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", hair_cache->strand_tex);
|
||||
DRW_shgroup_call_procedural_points_add(tf_shgrp, hair_cache->final[subdiv].point_len, NULL);
|
||||
}
|
||||
|
||||
return shgrp;
|
||||
}
|
||||
|
||||
DRWShadingGroup *DRW_shgroup_hair_create(
|
||||
Object *object, HairSystem *hsys,
|
||||
Mesh *scalp, const HairDrawSettings *draw_set,
|
||||
DRWPass *hair_pass,
|
||||
GPUShader *shader)
|
||||
{
|
||||
return drw_shgroup_create_hair_procedural_ex(object, hsys, scalp, draw_set, hair_pass, NULL, shader);
|
||||
}
|
||||
|
||||
DRWShadingGroup *DRW_shgroup_material_hair_create(
|
||||
Object *object, HairSystem *hsys,
|
||||
Mesh *scalp, const HairDrawSettings *draw_set,
|
||||
DRWPass *hair_pass,
|
||||
struct GPUMaterial *material)
|
||||
{
|
||||
return drw_shgroup_create_hair_procedural_ex(object, hsys, scalp, draw_set, hair_pass, material, NULL);
|
||||
}
|
||||
|
||||
void DRW_hair_update(void)
|
||||
|
@@ -39,16 +39,22 @@ struct Object;
|
||||
struct ParticleSystem;
|
||||
struct ModifierData;
|
||||
struct ParticleHairCache;
|
||||
struct HairSystem;
|
||||
|
||||
typedef struct ParticleHairFinalCache {
|
||||
/* Output of the subdivision stage: vertex buff sized to subdiv level. */
|
||||
GPUVertBuf *proc_buf;
|
||||
GPUVertBuf *proc_point_buf;
|
||||
GPUTexture *proc_tex;
|
||||
|
||||
/* Just contains a huge index buffer used to draw the final hair. */
|
||||
GPUVertBuf *proc_hair_index_buf; /* Hair strand index for each vertex */
|
||||
GPUTexture *hair_index_tex;
|
||||
|
||||
/* Just contains a huge index buffer used to draw the final hair. */
|
||||
GPUBatch *proc_hairs[MAX_THICKRES];
|
||||
|
||||
int strands_res; /* points per hair, at least 2 */
|
||||
int strands_len;
|
||||
int elems_len;
|
||||
int point_len;
|
||||
} ParticleHairFinalCache;
|
||||
|
||||
typedef struct ParticleHairCache {
|
||||
@@ -81,6 +87,8 @@ typedef struct ParticleHairCache {
|
||||
int point_len;
|
||||
} ParticleHairCache;
|
||||
|
||||
void particle_batch_cache_clear_hair(struct ParticleHairCache *hair_cache);
|
||||
|
||||
bool particles_ensure_procedural_data(
|
||||
struct Object *object,
|
||||
struct ParticleSystem *psys,
|
||||
@@ -89,4 +97,12 @@ bool particles_ensure_procedural_data(
|
||||
int subdiv,
|
||||
int thickness_res);
|
||||
|
||||
bool hair_ensure_procedural_data(
|
||||
struct Object *object,
|
||||
struct HairSystem *hsys,
|
||||
struct Mesh *scalp,
|
||||
struct ParticleHairCache **r_hair_cache,
|
||||
int subdiv,
|
||||
int thickness_res);
|
||||
|
||||
#endif /* __DRAW_HAIR_PRIVATE_H__ */
|
||||
|
@@ -2464,6 +2464,9 @@ void DRW_engines_register(void)
|
||||
/* BKE: gpencil.c */
|
||||
extern void *BKE_gpencil_batch_cache_dirty_cb;
|
||||
extern void *BKE_gpencil_batch_cache_free_cb;
|
||||
/* BKE: hair.c */
|
||||
extern void *BKE_hair_batch_cache_dirty_cb;
|
||||
extern void *BKE_hair_batch_cache_free_cb;
|
||||
|
||||
BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty;
|
||||
BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
|
||||
@@ -2482,6 +2485,9 @@ void DRW_engines_register(void)
|
||||
|
||||
BKE_gpencil_batch_cache_dirty_cb = DRW_gpencil_batch_cache_dirty;
|
||||
BKE_gpencil_batch_cache_free_cb = DRW_gpencil_batch_cache_free;
|
||||
|
||||
BKE_hair_batch_cache_dirty_cb = DRW_hair_batch_cache_dirty;
|
||||
BKE_hair_batch_cache_free_cb = DRW_hair_batch_cache_free;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -114,6 +114,7 @@ typedef struct OBJECT_PassList {
|
||||
struct DRWPass *bone_envelope;
|
||||
struct DRWPass *bone_axes;
|
||||
struct DRWPass *particle;
|
||||
struct DRWPass *hair;
|
||||
struct DRWPass *lightprobes;
|
||||
/* use for empty/background images */
|
||||
struct DRWPass *reference_image;
|
||||
@@ -247,6 +248,9 @@ typedef struct OBJECT_PrivateData {
|
||||
/* Texture Space */
|
||||
DRWShadingGroup *texspace;
|
||||
|
||||
/* Hair Systems */
|
||||
DRWShadingGroup *hair_verts;
|
||||
|
||||
/* Outlines id offset */
|
||||
int id_ofs_active;
|
||||
int id_ofs_select;
|
||||
@@ -1350,6 +1354,20 @@ static void OBJECT_cache_init(void *vedata)
|
||||
DRW_STATE_POINT | DRW_STATE_BLEND);
|
||||
}
|
||||
|
||||
{
|
||||
/* Hair */
|
||||
psl->hair = DRW_pass_create(
|
||||
"Hair Pass",
|
||||
DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND |
|
||||
DRW_STATE_POINT | DRW_STATE_WIRE);
|
||||
|
||||
GPUShader *sh_verts = GPU_shader_get_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
|
||||
stl->g_data->hair_verts = DRW_shgroup_create(sh_verts, psl->hair);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->hair_verts, "color", ts.colorVertex, 1);
|
||||
DRW_shgroup_uniform_float(stl->g_data->hair_verts, "size", &ts.sizeVertex, 1);
|
||||
DRW_shgroup_state_enable(stl->g_data->hair_verts, DRW_STATE_POINT);
|
||||
}
|
||||
|
||||
{
|
||||
/* Empty/Background Image Pass */
|
||||
psl->reference_image = DRW_pass_create(
|
||||
@@ -2623,6 +2641,7 @@ static void OBJECT_draw_scene(void *vedata)
|
||||
DRW_draw_pass(psl->lightprobes);
|
||||
}
|
||||
|
||||
DRW_draw_pass(psl->hair);
|
||||
DRW_draw_pass(psl->ob_center);
|
||||
|
||||
if (DRW_state_is_fbo()) {
|
||||
|
@@ -5,13 +5,6 @@
|
||||
* of data the CPU has to precompute and transfert for each update.
|
||||
**/
|
||||
|
||||
/**
|
||||
* hairStrandsRes: Number of points per hair strand.
|
||||
* 2 - no subdivision
|
||||
* 3+ - 1 or more interpolated points per hair.
|
||||
**/
|
||||
uniform int hairStrandsRes = 8;
|
||||
|
||||
/**
|
||||
* hairThicknessRes : Subdiv around the hair.
|
||||
* 1 - Wire Hair: Only one pixel thick, independant of view distance.
|
||||
@@ -33,6 +26,7 @@ uniform samplerBuffer hairPointBuffer; /* RGBA32F */
|
||||
|
||||
/* -- Per strands data -- */
|
||||
uniform usamplerBuffer hairStrandBuffer; /* R32UI */
|
||||
uniform usamplerBuffer hairIndexBuffer; /* R32UI */
|
||||
|
||||
/* Not used, use one buffer per uv layer */
|
||||
//uniform samplerBuffer hairUVBuffer; /* RG32F */
|
||||
@@ -49,6 +43,13 @@ void unpack_strand_data(uint data, out int strand_offset, out int strand_segment
|
||||
#endif
|
||||
}
|
||||
|
||||
int hair_get_strand_id(void)
|
||||
{
|
||||
//return gl_VertexID / (hairStrandsRes * hairThicknessRes);
|
||||
uint strand_index = texelFetch(hairIndexBuffer, gl_VertexID).x;
|
||||
return int(strand_index);
|
||||
}
|
||||
|
||||
/* -- Subdivision stage -- */
|
||||
/**
|
||||
* We use a transform feedback to preprocess the strands and add more subdivision to it.
|
||||
@@ -59,40 +60,40 @@ void unpack_strand_data(uint data, out int strand_offset, out int strand_segment
|
||||
**/
|
||||
|
||||
#ifdef HAIR_PHASE_SUBDIV
|
||||
int hair_get_base_id(float local_time, int strand_segments, out float interp_time)
|
||||
/**
|
||||
* Calculate segment and local time for interpolation
|
||||
*/
|
||||
void hair_get_interp_time(float local_time, int strand_segments, out int interp_segment, out float interp_time)
|
||||
{
|
||||
float time_per_strand_seg = 1.0 / float(strand_segments);
|
||||
|
||||
float ratio = local_time / time_per_strand_seg;
|
||||
interp_segment = int(ratio);
|
||||
interp_time = fract(ratio);
|
||||
|
||||
return int(ratio);
|
||||
}
|
||||
|
||||
void hair_get_interp_attribs(out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time)
|
||||
{
|
||||
float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1);
|
||||
|
||||
int hair_id = gl_VertexID / hairStrandsRes;
|
||||
uint strand_data = texelFetch(hairStrandBuffer, hair_id).x;
|
||||
|
||||
int strand_index = hair_get_strand_id();
|
||||
uint strand_data = texelFetch(hairStrandBuffer, strand_index).x;
|
||||
int strand_offset, strand_segments;
|
||||
unpack_strand_data(strand_data, strand_offset, strand_segments);
|
||||
|
||||
int id = hair_get_base_id(local_time, strand_segments, interp_time);
|
||||
float local_time = float(gl_VertexID - strand_offset) / float(strand_segments);
|
||||
int interp_segment;
|
||||
hair_get_interp_time(local_time, strand_segments, interp_segment, interp_time);
|
||||
int interp_point = interp_segment + strand_offset;
|
||||
|
||||
int ofs_id = id + strand_offset;
|
||||
data0 = texelFetch(hairPointBuffer, interp_point - 1);
|
||||
data1 = texelFetch(hairPointBuffer, interp_point);
|
||||
data2 = texelFetch(hairPointBuffer, interp_point + 1);
|
||||
data3 = texelFetch(hairPointBuffer, interp_point + 2);
|
||||
|
||||
data0 = texelFetch(hairPointBuffer, ofs_id - 1);
|
||||
data1 = texelFetch(hairPointBuffer, ofs_id);
|
||||
data2 = texelFetch(hairPointBuffer, ofs_id + 1);
|
||||
data3 = texelFetch(hairPointBuffer, ofs_id + 2);
|
||||
|
||||
if (id <= 0) {
|
||||
if (interp_segment <= 0) {
|
||||
/* root points. Need to reconstruct previous data. */
|
||||
data0 = data1 * 2.0 - data2;
|
||||
}
|
||||
if (id + 1 >= strand_segments) {
|
||||
if (interp_segment + 1 >= strand_segments) {
|
||||
/* tip points. Need to reconstruct next data. */
|
||||
data3 = data2 * 2.0 - data1;
|
||||
}
|
||||
@@ -105,11 +106,6 @@ void hair_get_interp_attribs(out vec4 data0, out vec4 data1, out vec4 data2, out
|
||||
**/
|
||||
|
||||
#ifndef HAIR_PHASE_SUBDIV
|
||||
int hair_get_strand_id(void)
|
||||
{
|
||||
return gl_VertexID / (hairStrandsRes * hairThicknessRes);
|
||||
}
|
||||
|
||||
int hair_get_base_id(void)
|
||||
{
|
||||
return gl_VertexID / hairThicknessRes;
|
||||
@@ -165,25 +161,25 @@ void hair_get_pos_tan_binor_time(
|
||||
|
||||
vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
|
||||
{
|
||||
int id = hair_get_strand_id();
|
||||
return texelFetch(cd_buf, id).rg;
|
||||
int strand_index = hair_get_strand_id();
|
||||
return texelFetch(cd_buf, strand_index).rg;
|
||||
}
|
||||
|
||||
vec3 hair_get_customdata_vec3(const samplerBuffer cd_buf)
|
||||
{
|
||||
int id = hair_get_strand_id();
|
||||
return texelFetch(cd_buf, id).rgb;
|
||||
int strand_index = hair_get_strand_id();
|
||||
return texelFetch(cd_buf, strand_index).rgb;
|
||||
}
|
||||
|
||||
vec4 hair_get_customdata_vec4(const samplerBuffer cd_buf)
|
||||
{
|
||||
int id = hair_get_strand_id();
|
||||
return texelFetch(cd_buf, id).rgba;
|
||||
int strand_index = hair_get_strand_id();
|
||||
return texelFetch(cd_buf, strand_index).rgba;
|
||||
}
|
||||
|
||||
vec3 hair_get_strand_pos(void)
|
||||
vec3 hair_get_strand_pos()
|
||||
{
|
||||
int id = hair_get_strand_id() * hairStrandsRes;
|
||||
int id = hair_get_base_id();
|
||||
return texelFetch(hairPointBuffer, id).point_position;
|
||||
}
|
||||
|
||||
|
@@ -175,6 +175,7 @@ void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_hair_generate_follicles(struct wmOperatorType *ot);
|
||||
|
||||
/* grease pencil modifiers */
|
||||
void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot);
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
@@ -58,12 +59,14 @@
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_hair.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_mapping.h"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_multires.h"
|
||||
#include "BKE_report.h"
|
||||
@@ -2417,3 +2420,88 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
edit_modifier_properties(ot);
|
||||
}
|
||||
|
||||
/************************ Hair follicle generate operator *********************/
|
||||
|
||||
static int hair_generate_follicles_poll(bContext *C)
|
||||
{
|
||||
return edit_modifier_poll_generic(C, &RNA_HairModifier, 0);
|
||||
}
|
||||
|
||||
static int hair_generate_follicles_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_active_context(C);
|
||||
HairModifierData *hmd = (HairModifierData *)edit_modifier_property_get(op, ob, eModifierType_Hair);
|
||||
|
||||
if (!hmd)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
BLI_assert(hmd->hair_system != NULL);
|
||||
|
||||
struct Depsgraph *depsgraph = CTX_data_depsgraph(C);
|
||||
|
||||
BLI_assert(ob && ob->type == OB_MESH);
|
||||
Mesh *scalp = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
|
||||
HairSystem *hsys = hmd->hair_system;
|
||||
|
||||
BKE_hair_generate_follicles(
|
||||
hsys,
|
||||
scalp,
|
||||
(unsigned int)hmd->follicle_seed,
|
||||
hmd->follicle_count);
|
||||
|
||||
{
|
||||
const int numverts = 5;
|
||||
const float hairlen = 0.05f;
|
||||
const float taper_length = 0.02f;
|
||||
const float taper_thickness = 0.8f;
|
||||
BKE_hair_fiber_curves_begin(hsys, hsys->pattern->num_follicles);
|
||||
for (int i = 0; i < hsys->pattern->num_follicles; ++i)
|
||||
{
|
||||
BKE_hair_set_fiber_curve(hsys, i, numverts, taper_length, taper_thickness);
|
||||
}
|
||||
BKE_hair_fiber_curves_end(hsys);
|
||||
for (int i = 0; i < hsys->pattern->num_follicles; ++i)
|
||||
{
|
||||
float loc[3], nor[3], tan[3];
|
||||
BKE_mesh_sample_eval(scalp, &hsys->pattern->follicles[i].mesh_sample, loc, nor, tan);
|
||||
for (int j = 0; j < numverts; ++j)
|
||||
{
|
||||
madd_v3_v3fl(loc, nor, hairlen / (numverts-1));
|
||||
BKE_hair_set_fiber_vertex(hsys, i * numverts + j, 0, loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_hair_bind_follicles(hmd->hair_system, scalp);
|
||||
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int hair_generate_follicles_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
||||
{
|
||||
if (edit_modifier_invoke_properties(C, op))
|
||||
return hair_generate_follicles_exec(C, op);
|
||||
else
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_hair_generate_follicles(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Hair Follicles Generate";
|
||||
ot->description = "Generate hair follicles for a hair modifier";
|
||||
ot->idname = "OBJECT_OT_hair_generate_follicles";
|
||||
|
||||
/* api callbacks */
|
||||
ot->poll = hair_generate_follicles_poll;
|
||||
ot->invoke = hair_generate_follicles_invoke;
|
||||
ot->exec = hair_generate_follicles_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
edit_modifier_properties(ot);
|
||||
}
|
||||
|
@@ -263,6 +263,7 @@ void ED_operatortypes_object(void)
|
||||
WM_operatortype_append(OBJECT_OT_data_transfer);
|
||||
WM_operatortype_append(OBJECT_OT_datalayout_transfer);
|
||||
WM_operatortype_append(OBJECT_OT_surfacedeform_bind);
|
||||
WM_operatortype_append(OBJECT_OT_hair_generate_follicles);
|
||||
|
||||
WM_operatortype_append(OBJECT_OT_hide_view_clear);
|
||||
WM_operatortype_append(OBJECT_OT_hide_view_set);
|
||||
|
@@ -1039,6 +1039,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
|
||||
case eModifierType_WeightedNormal:
|
||||
data.icon = ICON_MOD_NORMALEDIT;
|
||||
break;
|
||||
case eModifierType_Hair:
|
||||
data.icon = ICON_STRANDS;
|
||||
break;
|
||||
/* Default */
|
||||
case eModifierType_None:
|
||||
case eModifierType_ShapeKey:
|
||||
|
@@ -107,10 +107,10 @@ typedef enum GPUTextureFormat {
|
||||
|
||||
/* Texture only format */
|
||||
GPU_RGB16F,
|
||||
GPU_RGB32F,
|
||||
#if 0
|
||||
GPU_RGBA16_SNORM,
|
||||
GPU_RGBA8_SNORM,
|
||||
GPU_RGB32F,
|
||||
GPU_RGB32I,
|
||||
GPU_RGB32UI,
|
||||
GPU_RGB16_SNORM,
|
||||
|
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,
|
||||
};
|
||||
|
||||
typedef struct MeshSample {
|
||||
unsigned int orig_verts[3];
|
||||
float orig_weights[3]; /* also used as volume sample location */
|
||||
unsigned int orig_poly;
|
||||
unsigned int orig_loops[3];
|
||||
} MeshSample;
|
||||
|
||||
/* mvert->flag */
|
||||
enum {
|
||||
/* SELECT = (1 << 0), */
|
||||
|
@@ -91,6 +91,7 @@ typedef enum ModifierType {
|
||||
eModifierType_MeshSequenceCache = 52,
|
||||
eModifierType_SurfaceDeform = 53,
|
||||
eModifierType_WeightedNormal = 54,
|
||||
eModifierType_Hair = 55,
|
||||
NUM_MODIFIER_TYPES
|
||||
} ModifierType;
|
||||
|
||||
@@ -1692,4 +1693,32 @@ enum {
|
||||
#define MOD_MESHSEQ_READ_ALL \
|
||||
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
|
||||
|
||||
/* Hair modifier */
|
||||
typedef struct HairModifierFiberCurve {
|
||||
struct HairModifierFiberCurve *next, *prev;
|
||||
|
||||
/* Index for the mesh sample buffer */
|
||||
int mesh_sample_index;
|
||||
/* Number of vertices in the curve */
|
||||
int numverts;
|
||||
/* Vertex array */
|
||||
struct HairFiberVertex *verts;
|
||||
} HairModifierFiberCurve;
|
||||
|
||||
typedef struct HairModifierData {
|
||||
ModifierData modifier;
|
||||
|
||||
int flag;
|
||||
int pad;
|
||||
|
||||
struct HairSystem *hair_system;
|
||||
struct HairDrawSettings *draw_settings;
|
||||
|
||||
/* Follicle distribution parameters */
|
||||
int follicle_seed;
|
||||
int follicle_count;
|
||||
|
||||
ListBase fiber_curves;
|
||||
} HairModifierData;
|
||||
|
||||
#endif /* __DNA_MODIFIER_TYPES_H__ */
|
||||
|
@@ -131,6 +131,7 @@ static const char *includefiles[] = {
|
||||
"DNA_layer_types.h",
|
||||
"DNA_workspace_types.h",
|
||||
"DNA_lightprobe_types.h",
|
||||
"DNA_hair_types.h",
|
||||
|
||||
/* see comment above before editing! */
|
||||
|
||||
@@ -1357,5 +1358,6 @@ int main(int argc, char **argv)
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
#include "DNA_lightprobe_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
|
||||
/* end of list */
|
||||
|
@@ -277,6 +277,9 @@ extern StructRNA RNA_GPencilSculptBrush;
|
||||
extern StructRNA RNA_GaussianBlurSequence;
|
||||
extern StructRNA RNA_GlowSequence;
|
||||
extern StructRNA RNA_GreasePencil;
|
||||
extern StructRNA RNA_HairGroup;
|
||||
extern StructRNA RNA_HairModifier;
|
||||
extern StructRNA RNA_HairPattern;
|
||||
extern StructRNA RNA_Header;
|
||||
extern StructRNA RNA_HemiLight;
|
||||
extern StructRNA RNA_Histogram;
|
||||
|
@@ -52,6 +52,7 @@ set(DEFSRC
|
||||
rna_gpencil_modifier.c
|
||||
rna_shader_fx.c
|
||||
rna_group.c
|
||||
rna_hair.c
|
||||
rna_image.c
|
||||
rna_key.c
|
||||
rna_lamp.c
|
||||
@@ -62,6 +63,7 @@ set(DEFSRC
|
||||
rna_mask.c
|
||||
rna_material.c
|
||||
rna_mesh.c
|
||||
rna_mesh_sample.c
|
||||
rna_meta.c
|
||||
rna_modifier.c
|
||||
rna_movieclip.c
|
||||
|
@@ -3405,6 +3405,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
|
||||
{"rna_fluidsim.c", NULL, RNA_def_fluidsim},
|
||||
{"rna_gpencil.c", NULL, RNA_def_gpencil},
|
||||
{"rna_group.c", NULL, RNA_def_collections},
|
||||
{"rna_hair.c", NULL, RNA_def_hair},
|
||||
{"rna_image.c", "rna_image_api.c", RNA_def_image},
|
||||
{"rna_key.c", NULL, RNA_def_key},
|
||||
{"rna_lamp.c", NULL, RNA_def_light},
|
||||
@@ -3414,6 +3415,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
|
||||
{"rna_main.c", "rna_main_api.c", RNA_def_main},
|
||||
{"rna_material.c", "rna_material_api.c", RNA_def_material},
|
||||
{"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh},
|
||||
{"rna_mesh_sample.c", NULL, RNA_def_mesh_sample},
|
||||
{"rna_meta.c", "rna_meta_api.c", RNA_def_meta},
|
||||
{"rna_modifier.c", NULL, RNA_def_modifier},
|
||||
{"rna_gpencil_modifier.c", NULL, RNA_def_greasepencil_modifier},
|
||||
|
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_material(struct BlenderRNA *brna);
|
||||
void RNA_def_mesh(struct BlenderRNA *brna);
|
||||
void RNA_def_mesh_sample(struct BlenderRNA *brna);
|
||||
void RNA_def_meta(struct BlenderRNA *brna);
|
||||
void RNA_def_modifier(struct BlenderRNA *brna);
|
||||
void RNA_def_nla(struct BlenderRNA *brna);
|
||||
@@ -201,6 +202,7 @@ void RNA_def_world(struct BlenderRNA *brna);
|
||||
void RNA_def_movieclip(struct BlenderRNA *brna);
|
||||
void RNA_def_tracking(struct BlenderRNA *brna);
|
||||
void RNA_def_mask(struct BlenderRNA *brna);
|
||||
void RNA_def_hair(struct BlenderRNA *brna);
|
||||
|
||||
/* Common Define functions */
|
||||
|
||||
|
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_Explode, "EXPLODE", ICON_MOD_EXPLODE, "Explode", ""},
|
||||
{eModifierType_Fluidsim, "FLUID_SIMULATION", ICON_MOD_FLUIDSIM, "Fluid Simulation", ""},
|
||||
{eModifierType_Hair, "HAIR", ICON_STRANDS, "Hair", ""},
|
||||
{eModifierType_Ocean, "OCEAN", ICON_MOD_OCEAN, "Ocean", ""},
|
||||
{eModifierType_ParticleInstance, "PARTICLE_INSTANCE", ICON_MOD_PARTICLES, "Particle Instance", ""},
|
||||
{eModifierType_ParticleSystem, "PARTICLE_SYSTEM", ICON_MOD_PARTICLES, "Particle System", ""},
|
||||
@@ -281,12 +282,17 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "DNA_particle_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_smoke_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_cachefile.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_hair.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
#include "BKE_modifier.h"
|
||||
@@ -295,6 +301,7 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "DEG_depsgraph_build.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#ifdef WITH_ALEMBIC
|
||||
# include "ABC_alembic.h"
|
||||
@@ -417,6 +424,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
|
||||
return &RNA_SurfaceDeformModifier;
|
||||
case eModifierType_WeightedNormal:
|
||||
return &RNA_WeightedNormalModifier;
|
||||
case eModifierType_Hair:
|
||||
return &RNA_HairModifier;
|
||||
/* Default */
|
||||
case eModifierType_None:
|
||||
case eModifierType_ShapeKey:
|
||||
@@ -1174,6 +1183,58 @@ static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr, co
|
||||
CLAMP_MIN(psmd->psys, 1);
|
||||
}
|
||||
|
||||
static void rna_Hair_fiber_curves_clear(HairModifierData *hmd)
|
||||
{
|
||||
for (HairModifierFiberCurve* curve = hmd->fiber_curves.first; curve; curve = curve->next)
|
||||
{
|
||||
if (curve->verts)
|
||||
{
|
||||
MEM_freeN(curve->verts);
|
||||
}
|
||||
}
|
||||
BLI_freelistN(&hmd->fiber_curves);
|
||||
}
|
||||
|
||||
static void rna_Hair_fiber_curves_new(HairModifierData *hmd, ReportList *UNUSED(reports), int numverts)
|
||||
{
|
||||
HairModifierFiberCurve *curve = MEM_callocN(sizeof(HairModifierFiberCurve), "hair fiber curve");
|
||||
curve->numverts = numverts;
|
||||
curve->verts = MEM_callocN(sizeof(HairFiberVertex) * numverts, "hair fiber curve vertices");
|
||||
|
||||
BLI_addtail(&hmd->fiber_curves, curve);
|
||||
}
|
||||
|
||||
static void rna_Hair_fiber_curves_apply(ID *id, HairModifierData *hmd, bContext *C, ReportList *UNUSED(reports))
|
||||
{
|
||||
const int totcurves = BLI_listbase_count(&hmd->fiber_curves);
|
||||
int i = 0;
|
||||
|
||||
BKE_hair_fiber_curves_begin(hmd->hair_system, totcurves);
|
||||
i = 0;
|
||||
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next, ++i)
|
||||
{
|
||||
BKE_hair_set_fiber_curve(hmd->hair_system, i, curve->numverts, 0.1, 1.0);
|
||||
}
|
||||
BKE_hair_fiber_curves_end(hmd->hair_system);
|
||||
|
||||
i = 0;
|
||||
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next)
|
||||
{
|
||||
for (int j = 0; j < curve->numverts; ++j, ++i)
|
||||
{
|
||||
BKE_hair_set_fiber_vertex(hmd->hair_system, i, curve->verts[j].flag, curve->verts[j].co);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Mesh *scalp = (Mesh *)DEG_get_evaluated_id(CTX_data_depsgraph(C), ((Object*)id)->data);
|
||||
BKE_hair_bind_follicles(hmd->hair_system, scalp);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(id, OB_RECALC_DATA);
|
||||
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[])
|
||||
@@ -4916,6 +4977,52 @@ static void rna_def_modifier_surfacedeform(BlenderRNA *brna)
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_hair_fiber_curve(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "HairModifierFiberCurve", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Curve", "");
|
||||
RNA_def_struct_sdna(srna, "HairModifierFiberCurve");
|
||||
|
||||
prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "verts", "numverts");
|
||||
RNA_def_property_struct_type(prop, "HairModifierFiberVertex");
|
||||
RNA_def_property_ui_text(prop, "Vertices", "Fiber vertices");
|
||||
|
||||
|
||||
srna = RNA_def_struct(brna, "HairModifierFiberVertex", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Vertex", "");
|
||||
RNA_def_struct_sdna(srna, "HairFiberVertex");
|
||||
|
||||
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
|
||||
RNA_def_property_float_sdna(prop, NULL, "co");
|
||||
RNA_def_property_ui_text(prop, "Location", "Location of the vertex relative to the root");
|
||||
}
|
||||
|
||||
static void rna_def_modifier_hair_fiber_curves_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
{
|
||||
StructRNA *srna;
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
RNA_def_property_srna(cprop, "HairModifierFiberCurves");
|
||||
srna = RNA_def_struct(brna, "HairModifierFiberCurves", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Curves", "");
|
||||
RNA_def_struct_sdna(srna, "HairModifierData");
|
||||
|
||||
/*func =*/ RNA_def_function(srna, "clear", "rna_Hair_fiber_curves_clear");
|
||||
|
||||
func = RNA_def_function(srna, "new", "rna_Hair_fiber_curves_new");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS);
|
||||
parm = RNA_def_int(func, "vertex_count", 2, 0, INT_MAX, "Vertex Count", "Number of vertices", 2, 1000);
|
||||
RNA_def_property_flag(parm, PARM_REQUIRED);
|
||||
|
||||
func = RNA_def_function(srna, "apply", "rna_Hair_fiber_curves_apply");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_weightednormal(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@@ -4978,6 +5085,43 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
}
|
||||
|
||||
static void rna_def_modifier_hair(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "HairModifier", "Modifier");
|
||||
RNA_def_struct_ui_text(srna, "Hair Modifier", "");
|
||||
RNA_def_struct_sdna(srna, "HairModifierData");
|
||||
RNA_def_struct_ui_icon(srna, ICON_STRANDS);
|
||||
|
||||
prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Hair", "Hair data");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "follicle_seed", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0, INT_MAX);
|
||||
RNA_def_property_ui_text(prop, "Seed", "Follicle distribution random seed value");
|
||||
|
||||
prop = RNA_def_property(srna, "follicle_count", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_default(prop, 100000);
|
||||
RNA_def_property_range(prop, 0, INT_MAX);
|
||||
RNA_def_property_ui_range(prop, 1, 1e5, 1, 1);
|
||||
RNA_def_property_ui_text(prop, "Follicle Count", "Maximum number of follicles");
|
||||
|
||||
prop = RNA_def_property(srna, "draw_settings", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Draw Settings", "Hair draw settings");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "fiber_curves", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "fiber_curves", NULL);
|
||||
RNA_def_property_struct_type(prop, "HairModifierFiberCurve");
|
||||
RNA_def_property_ui_text(prop, "Fiber Curves", "Fiber curve data");
|
||||
rna_def_modifier_hair_fiber_curves_api(brna, prop);
|
||||
|
||||
rna_def_modifier_hair_fiber_curve(brna);
|
||||
}
|
||||
|
||||
void RNA_def_modifier(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@@ -5100,6 +5244,7 @@ void RNA_def_modifier(BlenderRNA *brna)
|
||||
rna_def_modifier_meshseqcache(brna);
|
||||
rna_def_modifier_surfacedeform(brna);
|
||||
rna_def_modifier_weightednormal(brna);
|
||||
rna_def_modifier_hair(brna);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -63,6 +63,7 @@ set(SRC
|
||||
intern/MOD_explode.c
|
||||
intern/MOD_fluidsim.c
|
||||
intern/MOD_fluidsim_util.c
|
||||
intern/MOD_hair.c
|
||||
intern/MOD_hook.c
|
||||
intern/MOD_laplaciandeform.c
|
||||
intern/MOD_laplaciansmooth.c
|
||||
|
@@ -87,6 +87,7 @@ extern ModifierTypeInfo modifierType_CorrectiveSmooth;
|
||||
extern ModifierTypeInfo modifierType_MeshSequenceCache;
|
||||
extern ModifierTypeInfo modifierType_SurfaceDeform;
|
||||
extern ModifierTypeInfo modifierType_WeightedNormal;
|
||||
extern ModifierTypeInfo modifierType_Hair;
|
||||
|
||||
/* MOD_util.c */
|
||||
void modifier_type_init(ModifierTypeInfo *types[]);
|
||||
|
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(SurfaceDeform);
|
||||
INIT_TYPE(WeightedNormal);
|
||||
INIT_TYPE(Hair);
|
||||
#undef INIT_TYPE
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ if(WITH_GTESTS)
|
||||
|
||||
add_subdirectory(testing)
|
||||
add_subdirectory(blenlib)
|
||||
add_subdirectory(blenkernel)
|
||||
add_subdirectory(guardedalloc)
|
||||
add_subdirectory(bmesh)
|
||||
if(WITH_ALEMBIC)
|
||||
|
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