Blender V5.0
blender/curves.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <optional>
6
8#include "blender/sync.h"
9#include "blender/util.h"
10
11#include "scene/attribute.h"
12#include "scene/camera.h"
13#include "scene/curves.h"
14#include "scene/hair.h"
15#include "scene/object.h"
16#include "scene/scene.h"
17
18#include "util/color.h"
19
20#include "util/hash.h"
21#include "util/log.h"
22
23#include "BKE_attribute.hh"
24#include "BKE_curves.hh"
25
27
29
31
32static float shaperadius(const float shape, const float root, const float tip, const float time)
33{
34 assert(time >= 0.0f);
35 assert(time <= 1.0f);
36 float radius = 1.0f - time;
37
38 if (shape != 0.0f) {
39 if (shape < 0.0f) {
40 radius = powf(radius, 1.0f + shape);
41 }
42 else {
43 radius = powf(radius, 1.0f / (1.0f - shape));
44 }
45 }
46 return (radius * (root - tip)) + tip;
47}
48
49/* curve functions */
50
52 Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
53{
54 int curvenum = 0;
55 int keyno = 0;
56
57 if (!(hair && b_mesh && b_ob && CData)) {
58 return false;
59 }
60
61 const Transform tfm = get_transform(b_ob->matrix_world());
62 const Transform itfm = transform_inverse(tfm);
63
64 for (BL::Modifier &b_mod : b_ob->modifiers) {
65 if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
66 (background ? b_mod.show_render() : b_mod.show_viewport()))
67 {
68 BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
69 BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
70 BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
71
72 if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
73 (b_part.type() == BL::ParticleSettings::type_HAIR))
74 {
75 const int shader = clamp(b_part.material() - 1, 0, hair->get_used_shaders().size() - 1);
76 const int display_step = background ? b_part.render_step() : b_part.display_step();
77 const int totparts = b_psys.particles.length();
78 const int totchild = background ? b_psys.child_particles.length() :
79 (int)((float)b_psys.child_particles.length() *
80 (float)b_part.display_percentage() / 100.0f);
81 int totcurves = totchild;
82
83 if (b_part.child_type() == 0 || totchild == 0) {
84 totcurves += totparts;
85 }
86
87 if (totcurves == 0) {
88 continue;
89 }
90
91 int ren_step = (1 << display_step) + 1;
92 if (b_part.kink() == BL::ParticleSettings::kink_SPIRAL) {
93 ren_step += b_part.kink_extra_steps();
94 }
95
96 CData->psys_firstcurve.push_back_slow(curvenum);
97 CData->psys_curvenum.push_back_slow(totcurves);
98 CData->psys_shader.push_back_slow(shader);
99
100 const float radius = b_part.radius_scale() * 0.5f;
101
102 CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
103 CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
104 CData->psys_shape.push_back_slow(b_part.shape());
105 CData->psys_closetip.push_back_slow(b_part.use_close_tip());
106
107 int pa_no = 0;
108 if (!(b_part.child_type() == 0) && totchild != 0) {
109 pa_no = totparts;
110 }
111
112 const int num_add = (totparts + totchild - pa_no);
113 CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
114 CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
115 CData->curve_length.reserve(CData->curve_length.size() + num_add);
116 CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add * ren_step);
117 CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add * ren_step);
118
119 for (; pa_no < totparts + totchild; pa_no++) {
120 int keynum = 0;
121 CData->curve_firstkey.push_back_slow(keyno);
122
123 float curve_length = 0.0f;
124 float3 prev_co_world = zero_float3();
125 float3 prev_co_object = zero_float3();
126 for (int step_no = 0; step_no < ren_step; step_no++) {
127 float3 co_world = prev_co_world;
128 b_psys.co_hair(*b_ob, pa_no, step_no, &co_world.x);
129 const float3 co_object = transform_point(&itfm, co_world);
130 if (step_no > 0) {
131 const float step_length = len(co_object - prev_co_object);
132 curve_length += step_length;
133 }
134 CData->curvekey_co.push_back_slow(co_object);
135 CData->curvekey_time.push_back_slow(curve_length);
136 prev_co_object = co_object;
137 prev_co_world = co_world;
138 keynum++;
139 }
140 keyno += keynum;
141
142 CData->curve_keynum.push_back_slow(keynum);
143 CData->curve_length.push_back_slow(curve_length);
144 curvenum++;
145 }
146 }
147 }
148 }
149
150 return true;
151}
152
153static bool ObtainCacheParticleUV(Hair *hair,
154 BL::Mesh *b_mesh,
155 BL::Object *b_ob,
156 ParticleCurveData *CData,
157 bool background,
158 const int uv_num)
159{
160 if (!(hair && b_mesh && b_ob && CData)) {
161 return false;
162 }
163
164 CData->curve_uv.clear();
165
166 for (BL::Modifier &b_mod : b_ob->modifiers) {
167 if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
168 (background ? b_mod.show_render() : b_mod.show_viewport()))
169 {
170 BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
171 BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
172 BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
173
174 if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
175 (b_part.type() == BL::ParticleSettings::type_HAIR))
176 {
177 const int totparts = b_psys.particles.length();
178 const int totchild = background ? b_psys.child_particles.length() :
179 (int)((float)b_psys.child_particles.length() *
180 (float)b_part.display_percentage() / 100.0f);
181 int totcurves = totchild;
182
183 if (b_part.child_type() == 0 || totchild == 0) {
184 totcurves += totparts;
185 }
186
187 if (totcurves == 0) {
188 continue;
189 }
190
191 int pa_no = 0;
192 if (!(b_part.child_type() == 0) && totchild != 0) {
193 pa_no = totparts;
194 }
195
196 const int num_add = (totparts + totchild - pa_no);
197 CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
198
199 BL::ParticleSystem::particles_iterator b_pa;
200 b_psys.particles.begin(b_pa);
201 for (; pa_no < totparts + totchild; pa_no++) {
202 /* Add UVs */
203 BL::Mesh::uv_layers_iterator l;
204 b_mesh->uv_layers.begin(l);
205
206 float2 uv = zero_float2();
207 if (!b_mesh->uv_layers.empty()) {
208 b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
209 }
210 CData->curve_uv.push_back_slow(uv);
211
212 if (pa_no < totparts && b_pa != b_psys.particles.end()) {
213 ++b_pa;
214 }
215 }
216 }
217 }
218 }
219
220 return true;
221}
222
224 BL::Mesh *b_mesh,
225 BL::Object *b_ob,
226 ParticleCurveData *CData,
227 bool background,
228 const int vcol_num)
229{
230 if (!(hair && b_mesh && b_ob && CData)) {
231 return false;
232 }
233
234 CData->curve_vcol.clear();
235
236 for (BL::Modifier &b_mod : b_ob->modifiers) {
237 if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
238 (background ? b_mod.show_render() : b_mod.show_viewport()))
239 {
240 BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
241 BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
242 BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
243
244 if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
245 (b_part.type() == BL::ParticleSettings::type_HAIR))
246 {
247 const int totparts = b_psys.particles.length();
248 const int totchild = background ? b_psys.child_particles.length() :
249 (int)((float)b_psys.child_particles.length() *
250 (float)b_part.display_percentage() / 100.0f);
251 int totcurves = totchild;
252
253 if (b_part.child_type() == 0 || totchild == 0) {
254 totcurves += totparts;
255 }
256
257 if (totcurves == 0) {
258 continue;
259 }
260
261 int pa_no = 0;
262 if (!(b_part.child_type() == 0) && totchild != 0) {
263 pa_no = totparts;
264 }
265
266 const int num_add = (totparts + totchild - pa_no);
267 CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
268
269 BL::ParticleSystem::particles_iterator b_pa;
270 b_psys.particles.begin(b_pa);
271 for (; pa_no < totparts + totchild; pa_no++) {
272 /* Add vertex colors */
273 BL::Mesh::vertex_colors_iterator l;
274 b_mesh->vertex_colors.begin(l);
275
276 float4 vcol = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
277 if (!b_mesh->vertex_colors.empty()) {
278 b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
279 }
280 CData->curve_vcol.push_back_slow(vcol);
281
282 if (pa_no < totparts && b_pa != b_psys.particles.end()) {
283 ++b_pa;
284 }
285 }
286 }
287 }
288 }
289
290 return true;
291}
292
293static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CData)
294{
295 int num_keys = 0;
296 int num_curves = 0;
297
298 if (hair->num_curves()) {
299 return;
300 }
301
302 Attribute *attr_normal = nullptr;
303 Attribute *attr_intercept = nullptr;
304 Attribute *attr_length = nullptr;
305 Attribute *attr_random = nullptr;
306
307 if (hair->need_attribute(scene, ATTR_STD_VERTEX_NORMAL)) {
308 attr_normal = hair->attributes.add(ATTR_STD_VERTEX_NORMAL);
309 }
310 if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
311 attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
312 }
313 if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) {
314 attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
315 }
316 if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
317 attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
318 }
319
320 /* compute and reserve size of arrays */
321 for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
322 for (int curve = CData->psys_firstcurve[sys];
323 curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
324 curve++)
325 {
326 num_keys += CData->curve_keynum[curve];
327 num_curves++;
328 }
329 }
330
331 hair->reserve_curves(hair->num_curves() + num_curves, hair->get_curve_keys().size() + num_keys);
332
333 num_keys = 0;
334 num_curves = 0;
335
336 /* actually export */
337 for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
338 for (int curve = CData->psys_firstcurve[sys];
339 curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
340 curve++)
341 {
342 size_t num_curve_keys = 0;
343
344 for (int curvekey = CData->curve_firstkey[curve];
345 curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
346 curvekey++)
347 {
348 const float3 ickey_loc = CData->curvekey_co[curvekey];
349 const float curve_time = CData->curvekey_time[curvekey];
350 const float curve_length = CData->curve_length[curve];
351 const float time = (curve_length > 0.0f) ? curve_time / curve_length : 0.0f;
352 float radius = shaperadius(
353 CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], time);
354 if (CData->psys_closetip[sys] &&
355 (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1))
356 {
357 radius = 0.0f;
358 }
359 hair->add_curve_key(ickey_loc, radius);
360 if (attr_intercept) {
361 attr_intercept->add(time);
362 }
363
364 if (attr_normal) {
365 /* NOTE: the geometry normals are not computed for legacy particle hairs. This hair
366 * system is expected to be deprecated. */
367 attr_normal->add(make_float3(0.0f, 0.0f, 0.0f));
368 }
369
370 num_curve_keys++;
371 }
372
373 if (attr_length != nullptr) {
374 attr_length->add(CData->curve_length[curve]);
375 }
376
377 if (attr_random != nullptr) {
378 attr_random->add(hash_uint2_to_float(num_curves, 0));
379 }
380
381 hair->add_curve(num_keys, CData->psys_shader[sys]);
382 num_keys += num_curve_keys;
383 num_curves++;
384 }
385 }
386
387 /* check allocation */
388 if ((hair->get_curve_keys().size() != num_keys) || (hair->num_curves() != num_curves)) {
389 LOG_ERROR << "Hair memory allocation failed, clearing data.";
390 hair->clear(true);
391 }
392}
393
395 const int sys,
396 const int curve,
397 const int curvekey)
398{
399 const float3 ickey_loc = CData->curvekey_co[curvekey];
400 const float curve_time = CData->curvekey_time[curvekey];
401 const float curve_length = CData->curve_length[curve];
402 const float time = (curve_length > 0.0f) ? curve_time / curve_length : 0.0f;
403 float radius = shaperadius(
404 CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], time);
405
406 if (CData->psys_closetip[sys] &&
407 (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1))
408 {
409 radius = 0.0f;
410 }
411
412 /* curve motion keys store both position and radius in float4 */
413 float4 mP = make_float4(ickey_loc);
414 mP.w = radius;
415 return mP;
416}
417
419 const int sys,
420 const int curve,
421 const float step)
422{
423 assert(step >= 0.0f);
424 assert(step <= 1.0f);
425 const int first_curve_key = CData->curve_firstkey[curve];
426 const float curve_key_f = step * (CData->curve_keynum[curve] - 1);
427 int curvekey = (int)floorf(curve_key_f);
428 const float remainder = curve_key_f - curvekey;
429 if (remainder == 0.0f) {
430 return CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey);
431 }
432 int curvekey2 = curvekey + 1;
433 if (curvekey2 >= (CData->curve_keynum[curve] - 1)) {
434 curvekey2 = (CData->curve_keynum[curve] - 1);
435 curvekey = curvekey2 - 1;
436 }
437 const float4 mP = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey);
438 const float4 mP2 = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey2);
439 return mix(mP, mP2, remainder);
440}
441
443 const int motion_step,
444 const int num_motion_keys,
445 bool have_motion)
446{
448 const int num_keys = hair->get_curve_keys().size();
449
450 if (num_motion_keys != num_keys || !have_motion) {
451 /* No motion or hair "topology" changed, remove attributes again. */
452 if (num_motion_keys != num_keys) {
453 LOG_DEBUG << "Hair topology changed, removing motion attribute.";
454 }
456 }
457 else if (motion_step > 0) {
458 /* Motion, fill up previous steps that we might have skipped because
459 * they had no motion, but we need them anyway now. */
460 for (int step = 0; step < motion_step; step++) {
461 float4 *mP = attr_mP->data_float4() + step * num_keys;
462
463 for (int key = 0; key < num_keys; key++) {
464 mP[key] = make_float4(hair->get_curve_keys()[key]);
465 mP[key].w = hair->get_curve_radius()[key];
466 }
467 }
468 }
469}
470
471static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, const int motion_step)
472{
473 /* find attribute */
475 bool new_attribute = false;
476
477 /* add new attribute if it doesn't exist already */
478 if (!attr_mP) {
480 new_attribute = true;
481 }
482
483 /* export motion vectors for curve keys */
484 const size_t numkeys = hair->get_curve_keys().size();
485 float4 *mP = attr_mP->data_float4() + motion_step * numkeys;
486 bool have_motion = false;
487 int i = 0;
488 int num_curves = 0;
489
490 for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
491 for (int curve = CData->psys_firstcurve[sys];
492 curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
493 curve++)
494 {
495 /* Curve lengths may not match! Curves can be clipped. */
496 const int curve_key_end = (num_curves + 1 < (int)hair->get_curve_first_key().size() ?
497 hair->get_curve_first_key()[num_curves + 1] :
498 (int)hair->get_curve_keys().size());
499 const int num_center_curve_keys = curve_key_end - hair->get_curve_first_key()[num_curves];
500 const int is_num_keys_different = CData->curve_keynum[curve] - num_center_curve_keys;
501
502 if (!is_num_keys_different) {
503 for (int curvekey = CData->curve_firstkey[curve];
504 curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
505 curvekey++)
506 {
507 if (i < hair->get_curve_keys().size()) {
508 mP[i] = CurveSegmentMotionCV(CData, sys, curve, curvekey);
509 if (!have_motion) {
510 /* unlike mesh coordinates, these tend to be slightly different
511 * between frames due to particle transforms into/out of object
512 * space, so we use an epsilon to detect actual changes */
513 float4 curve_key = make_float4(hair->get_curve_keys()[i]);
514 curve_key.w = hair->get_curve_radius()[i];
515 if (len_squared(mP[i] - curve_key) > 1e-5f * 1e-5f) {
516 have_motion = true;
517 }
518 }
519 }
520 i++;
521 }
522 }
523 else {
524 /* Number of keys has changed. Generate an interpolated version
525 * to preserve motion blur. */
526 const float step_size = num_center_curve_keys > 1 ? 1.0f / (num_center_curve_keys - 1) :
527 0.0f;
528 for (int step_index = 0; step_index < num_center_curve_keys; ++step_index) {
529 const float step = step_index * step_size;
530 mP[i] = LerpCurveSegmentMotionCV(CData, sys, curve, step);
531 i++;
532 }
533 have_motion = true;
534 }
535 num_curves++;
536 }
537 }
538
539 /* In case of new attribute, we verify if there really was any motion. */
540 if (new_attribute) {
541 export_hair_motion_validate_attribute(hair, motion_step, i, have_motion);
542 }
543}
544
545/* Hair Curve Sync */
546
547bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
548{
549 /* Test if the object has a particle modifier with hair. */
550 for (BL::Modifier &b_mod : b_ob.modifiers) {
551 if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
552 (preview ? b_mod.show_viewport() : b_mod.show_render()))
553 {
554 BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
555 BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
556 BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
557
558 if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
559 (b_part.type() == BL::ParticleSettings::type_HAIR))
560 {
561 return true;
562 }
563 }
564 }
565
566 return false;
567}
568
569/* Old particle hair. */
570void BlenderSync::sync_particle_hair(
571 Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, const int motion_step)
572{
573 if (!b_ob_info.is_real_object_data()) {
574 return;
575 }
576 BL::Object b_ob = b_ob_info.real_object;
577
578 /* obtain general settings */
579 if (b_ob.mode() == BL::Object::mode_PARTICLE_EDIT || b_ob.mode() == BL::Object::mode_EDIT) {
580 return;
581 }
582
583 /* Extract particle hair data - should be combined with connecting to mesh later. */
584
585 ParticleCurveData CData;
586
587 ObtainCacheParticleData(hair, &b_mesh, &b_ob, &CData, !preview);
588
589 /* add hair geometry */
590 if (motion) {
591 ExportCurveSegmentsMotion(hair, &CData, motion_step);
592 }
593 else {
594 ExportCurveSegments(scene, hair, &CData);
595 }
596
597 /* generated coordinates from first key. we should ideally get this from
598 * blender to handle deforming objects */
599 if (!motion) {
600 if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
601 float3 loc;
602 float3 size;
603 mesh_texture_space(*static_cast<const ::Mesh *>(b_mesh.ptr.data), loc, size);
604
605 Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
606 float3 *generated = attr_generated->data_float3();
607
608 for (size_t i = 0; i < hair->num_curves(); i++) {
609 const float3 co = hair->get_curve_keys()[hair->get_curve(i).first_key];
610 generated[i] = co * size - loc;
611 }
612 }
613 }
614
615 /* create vertex color attributes */
616 if (!motion) {
617 BL::Mesh::vertex_colors_iterator l;
618 int vcol_num = 0;
619
620 for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l, vcol_num++) {
621 if (!hair->need_attribute(scene, ustring(l->name().c_str()))) {
622 continue;
623 }
624
625 ObtainCacheParticleVcol(hair, &b_mesh, &b_ob, &CData, !preview, vcol_num);
626
627 Attribute *attr_vcol = hair->attributes.add(
628 ustring(l->name().c_str()), TypeRGBA, ATTR_ELEMENT_CURVE);
629
630 float4 *fdata = attr_vcol->data_float4();
631
632 if (fdata) {
633 size_t i = 0;
634
635 /* Encode vertex color using the sRGB curve. */
636 for (size_t curve = 0; curve < CData.curve_vcol.size(); curve++) {
637 fdata[i++] = color_srgb_to_linear_v4(CData.curve_vcol[curve]);
638 }
639 }
640 }
641 }
642
643 /* create UV attributes */
644 if (!motion) {
645 BL::Mesh::uv_layers_iterator l;
646 int uv_num = 0;
647
648 for (b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l, uv_num++) {
649 const bool active_render = l->active_render();
650 const AttributeStandard std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
651 const ustring name = ustring(l->name().c_str());
652
653 /* UV map */
654 if (hair->need_attribute(scene, name) || hair->need_attribute(scene, std)) {
655 Attribute *attr_uv;
656
657 ObtainCacheParticleUV(hair, &b_mesh, &b_ob, &CData, !preview, uv_num);
658
659 if (active_render) {
660 attr_uv = hair->attributes.add(std, name);
661 }
662 else {
663 attr_uv = hair->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CURVE);
664 }
665
666 float2 *uv = attr_uv->data_float2();
667
668 if (uv) {
669 size_t i = 0;
670
671 for (size_t curve = 0; curve < CData.curve_uv.size(); curve++) {
672 uv[i++] = CData.curve_uv[curve];
673 }
674 }
675 }
676 }
677 }
678
679 hair->curve_shape = scene->params.hair_shape;
680}
681
682template<typename TypeInCycles, typename GetValueAtIndex>
683static void fill_generic_attribute(const int num_curves,
684 const int num_points,
685 TypeInCycles *data,
687 const GetValueAtIndex &get_value_at_index)
688{
689 switch (element) {
691 for (int i = 0; i < num_points; i++) {
692 data[i] = get_value_at_index(i);
693 }
694 break;
695 }
696 case ATTR_ELEMENT_CURVE: {
697 for (int i = 0; i < num_curves; i++) {
698 data[i] = get_value_at_index(i);
699 }
700 break;
701 }
702 default: {
703 assert(false);
704 break;
705 }
706 }
707}
708
711 const float motion_scale)
712{
713 const int num_curve_keys = hair->get_curve_keys().size();
714
715 /* Override motion steps to fixed number. */
716 hair->set_motion_steps(3);
717
718 /* Find or add attribute */
719 float3 *P = hair->get_curve_keys().data();
721
722 if (!attr_mP) {
724 }
725
726 /* Only export previous and next frame, we don't have any in between data. */
727 const float motion_times[2] = {-1.0f, 1.0f};
728 for (int step = 0; step < 2; step++) {
729 const float relative_time = motion_times[step] * 0.5f * motion_scale;
730 float3 *mP = attr_mP->data_float3() + step * num_curve_keys;
731
732 for (int i = 0; i < num_curve_keys; i++) {
733 mP[i] = P[i] + make_float3(src[i][0], src[i][1], src[i][2]) * relative_time;
734 }
735 }
736}
737
738static void attr_create_generic(Scene *scene,
739 Hair *hair,
740 const blender::bke::CurvesGeometry &b_curves,
741 const bool need_motion,
742 const float motion_scale)
743{
744 const blender::bke::AttributeAccessor b_attributes = b_curves.attributes();
745
746 AttributeSet &attributes = hair->attributes;
747 static const ustring u_velocity("velocity");
748 const bool need_uv = hair->need_attribute(scene, ATTR_STD_UV);
749 bool have_uv = false;
750
751 b_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
752 const ustring name{std::string_view(iter.name)};
753
754 const blender::bke::AttrDomain b_domain = iter.domain;
755 const blender::bke::AttrType b_data_type = iter.data_type;
756
757 if (need_motion && name == u_velocity) {
758 const blender::VArraySpan b_attr = *iter.get<blender::float3>(
760 attr_create_motion_from_velocity(hair, b_attr, motion_scale);
761 return;
762 }
763
764 /* Weak, use first float2 attribute as standard UV. */
765 if (need_uv && !have_uv && b_data_type == blender::bke::AttrType::Float2 &&
767 {
768 Attribute *attr = attributes.add(ATTR_STD_UV, name);
769
770 const blender::VArraySpan b_attr = *iter.get<blender::float2>();
771
772 static_assert(sizeof(blender::float2) == sizeof(float2));
773 const blender::Span src = b_attr.cast<float2>();
774 std::copy(src.begin(), src.end(), attr->data_float2());
775 have_uv = true;
776 return;
777 }
778
779 if (!hair->need_attribute(scene, name)) {
780 return;
781 }
782 if (attributes.find(name)) {
783 return;
784 }
785
786 const blender::bke::GAttributeReader b_attr = iter.get();
787
789 switch (b_attr.domain) {
792 break;
795 break;
796 default:
797 return;
798 }
799
801 using BlenderT = decltype(dummy);
802 using Converter = typename ccl::AttributeConverter<BlenderT>;
803 using CyclesT = typename Converter::CyclesT;
804 if constexpr (!std::is_void_v<CyclesT>) {
805 Attribute *attr = attributes.add(name, Converter::type_desc, element);
806 CyclesT *data = reinterpret_cast<CyclesT *>(attr->data());
807
808 const blender::VArraySpan src = b_attr.varray.typed<BlenderT>();
809 for (const int i : src.index_range()) {
810 data[i] = Converter::convert(src[i]);
811 }
812 }
813 });
814 });
815}
816
818 const blender::Span<float> b_radius,
819 const int index)
820{
821 float4 mP = make_float4(
822 b_positions[index][0], b_positions[index][1], b_positions[index][2], 0.0f);
823 mP.w = b_radius.is_empty() ? 0.005f : b_radius[index];
824 return mP;
825}
826
828 const blender::Span<float> b_radius,
829 const int first_point_index,
830 const int num_points,
831 const float step)
832{
833 const float curve_t = step * (num_points - 1);
834 const int point_a = clamp((int)curve_t, 0, num_points - 1);
835 const int point_b = min(point_a + 1, num_points - 1);
836 const float t = curve_t - (float)point_a;
837 return mix(curve_point_as_float4(b_positions, b_radius, first_point_index + point_a),
838 curve_point_as_float4(b_positions, b_radius, first_point_index + point_b),
839 t);
840}
841
842static void export_hair_curves(Scene *scene,
843 Hair *hair,
844 const blender::bke::CurvesGeometry &b_curves,
845 const bool need_motion,
846 const float motion_scale)
847{
848 const blender::Span<blender::float3> positions = b_curves.positions();
849 const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
850
851 hair->resize_curves(points_by_curve.size(), positions.size());
852
853 float3 *curve_keys = hair->get_curve_keys().data();
854 float *curve_radius = hair->get_curve_radius().data();
855 int *curve_first_key = hair->get_curve_first_key().data();
856 int *curve_shader = hair->get_curve_shader().data();
857
858 /* Add requested attributes. */
859 float *attr_intercept = nullptr;
860 float *attr_length = nullptr;
861
862 if (hair->need_attribute(scene, ATTR_STD_VERTEX_NORMAL)) {
863 /* Get geometry normals. */
864 float3 *attr_normal = hair->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3();
865 vector<blender::float3> point_normals(positions.size());
867 b_curves, {point_normals.data(), int64_t(point_normals.size())});
868 for (const int i : positions.index_range()) {
869 attr_normal[i] = make_float3(point_normals[i][0], point_normals[i][1], point_normals[i][2]);
870 }
871 }
872
873 if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
874 attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT)->data_float();
875 }
876 if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) {
877 attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH)->data_float();
878 }
879 if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
880 float *attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM)->data_float();
881 for (const int i : points_by_curve.index_range()) {
882 attr_random[i] = hash_uint2_to_float(i, 0);
883 }
884 }
885
886 const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>(
888
889 std::copy(points_by_curve.data().data(),
890 points_by_curve.data().data() + points_by_curve.size(),
891 curve_first_key);
892 std::fill(curve_shader, curve_shader + points_by_curve.size(), 0);
893 if (!b_radius.is_empty()) {
894 std::copy(b_radius.data(), b_radius.data() + positions.size(), curve_radius);
895 }
896 else {
897 std::fill(curve_radius, curve_radius + positions.size(), 0.005f);
898 }
899
900 /* Export curves and points. */
901 for (const int curve : points_by_curve.index_range()) {
902 const blender::IndexRange points = points_by_curve[curve];
903
904 float3 prev_co = zero_float3();
905 float length = 0.0f;
906
907 /* Position and radius. */
908 for (const int point : points) {
909 const float3 co = make_float3(positions[point][0], positions[point][1], positions[point][2]);
910
911 curve_keys[point] = co;
912
913 if (attr_length || attr_intercept) {
914 if (point != points.first()) {
915 length += len(co - prev_co);
916 }
917 prev_co = co;
918
919 if (attr_intercept) {
920 attr_intercept[point] = length;
921 }
922 }
923 }
924
925 /* Normalized 0..1 attribute along curve. */
926 if (attr_intercept && length > 0.0f) {
927 for (const int point : points.drop_front(1)) {
928 attr_intercept[point] /= length;
929 }
930 }
931
932 /* Curve length. */
933 if (attr_length) {
934 attr_length[curve] = length;
935 }
936 }
937
938 attr_create_generic(scene, hair, b_curves, need_motion, motion_scale);
939}
940
942 const blender::bke::CurvesGeometry &b_curves,
943 const int motion_step)
944{
945 /* Find or add attribute. */
947 bool new_attribute = false;
948
949 if (!attr_mP) {
951 new_attribute = true;
952 }
953
954 /* Export motion keys. */
955 const size_t num_keys = hair->num_keys();
956 const size_t num_curves = hair->num_curves();
957 float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
958 bool have_motion = false;
959 int num_motion_keys = 0;
960 int curve_index = 0;
961
962 const blender::Span<blender::float3> b_positions = b_curves.positions();
963 const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
964 const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>(
966
967 for (const int i : points_by_curve.index_range()) {
968 const blender::IndexRange points = points_by_curve[i];
969 if (curve_index >= num_curves) {
970 break;
971 }
972
973 const Hair::Curve curve = hair->get_curve(curve_index);
974 curve_index++;
975
976 if (points.size() == curve.num_keys) {
977 /* Number of keys matches. */
978 for (const int i : points.index_range()) {
979 const int point = points[i];
980
981 if (point < num_keys) {
982 mP[num_motion_keys] = curve_point_as_float4(b_positions, b_radius, point);
983 num_motion_keys++;
984
985 if (!have_motion) {
986 /* TODO: use epsilon for comparison? Was needed for particles due to
987 * transform, but ideally should not happen anymore. */
988 float4 curve_key = make_float4(hair->get_curve_keys()[i]);
989 curve_key.w = hair->get_curve_radius()[i];
990 have_motion = !(mP[i] == curve_key);
991 }
992 }
993 }
994 }
995 else {
996 /* Number of keys has changed. Generate an interpolated version
997 * to preserve motion blur. */
998 const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f;
999 for (int i = 0; i < curve.num_keys; i++) {
1000 const float step = i * step_size;
1001 mP[num_motion_keys] = interpolate_curve_points(
1002 b_positions, b_radius, points.start(), points.size(), step);
1003 num_motion_keys++;
1004 }
1005 have_motion = true;
1006 }
1007 }
1008
1009 /* In case of new attribute, we verify if there really was any motion. */
1010 if (new_attribute) {
1011 export_hair_motion_validate_attribute(hair, motion_step, num_motion_keys, have_motion);
1012 }
1013}
1014
1015/* Hair object. */
1016void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, const int motion_step)
1017{
1018 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
1019 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
1020 const float motion_scale = (need_motion) ?
1021 scene->motion_shutter_time() /
1022 (b_scene.render().fps() / b_scene.render().fps_base()) :
1023 0.0f;
1024
1025 /* Convert Blender hair to Cycles curves. */
1026 const blender::bke::CurvesGeometry &b_curves(
1027 static_cast<const ::Curves *>(b_ob_info.object_data.ptr.data)->geometry.wrap());
1028 if (motion) {
1029 export_hair_curves_motion(hair, b_curves, motion_step);
1030 }
1031 else {
1032 export_hair_curves(scene, hair, b_curves, need_motion, motion_scale);
1033 }
1034
1035 const blender::VArray<int8_t> b_types = b_curves.curve_types();
1036 /* This does not handle cases where the curve type is not the same across all curves */
1037 if (!b_types.is_empty() && b_types[0] == CURVE_TYPE_POLY) {
1039 }
1040 else {
1041 hair->curve_shape = scene->params.hair_shape;
1042 }
1043}
1044
1045void BlenderSync::sync_hair(BObjectInfo &b_ob_info, Hair *hair)
1046{
1047 /* make a copy of the shaders as the caller in the main thread still need them for syncing the
1048 * attributes */
1049 array<Node *> used_shaders = hair->get_used_shaders();
1050
1051 Hair new_hair;
1052 new_hair.set_used_shaders(used_shaders);
1053
1054 if (view_layer.use_hair) {
1055 if (b_ob_info.object_data.is_a(&RNA_Curves)) {
1056 /* Hair object. */
1057 sync_hair(&new_hair, b_ob_info, false);
1058 }
1059 else {
1060 /* Particle hair. */
1061 BL::Mesh b_mesh = object_to_mesh(b_ob_info);
1062
1063 if (b_mesh) {
1064 sync_particle_hair(&new_hair, b_mesh, b_ob_info, false);
1065 free_object_to_mesh(b_ob_info, b_mesh);
1066 }
1067 }
1068 }
1069
1070 /* update original sockets */
1071
1072 for (const SocketType &socket : new_hair.type->inputs) {
1073 /* Those sockets are updated in sync_object, so do not modify them. */
1074 if (socket.name == "use_motion_blur" || socket.name == "used_shaders") {
1075 continue;
1076 }
1077 hair->set_value(socket, new_hair, socket);
1078 }
1079
1080 hair->attributes.update(std::move(new_hair.attributes));
1081
1082 hair->curve_shape = new_hair.curve_shape;
1083
1084 /* tag update */
1085
1086 /* Compares curve_keys rather than strands in order to handle quick hair
1087 * adjustments in dynamic BVH - other methods could probably do this better. */
1088 const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
1089
1090 hair->tag_update(scene, rebuild);
1091}
1092
1093void BlenderSync::sync_hair_motion(BObjectInfo &b_ob_info, Hair *hair, const int motion_step)
1094{
1095 /* Skip if nothing exported. */
1096 if (hair->num_keys() == 0) {
1097 return;
1098 }
1099
1100 /* Export deformed coordinates. */
1101 if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
1102 if (b_ob_info.object_data.is_a(&RNA_Curves)) {
1103 /* Hair object. */
1104 sync_hair(hair, b_ob_info, true, motion_step);
1105 return;
1106 }
1107
1108 /* Particle hair. */
1109 BL::Mesh b_mesh = object_to_mesh(b_ob_info);
1110 if (b_mesh) {
1111 sync_particle_hair(hair, b_mesh, b_ob_info, true, motion_step);
1112 free_object_to_mesh(b_ob_info, b_mesh);
1113 return;
1114 }
1115 }
1116
1117 /* No deformation on this frame, copy coordinates if other frames did have it. */
1118 hair->copy_center_to_motion_step(motion_step);
1119}
1120
Low-level operations for curves.
struct Curves Curves
struct Mesh Mesh
static bool ObtainCacheParticleData(Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, const int motion_step)
static bool ObtainCacheParticleUV(Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, const int uv_num)
static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CData)
static void export_hair_curves_motion(Hair *hair, const blender::bke::CurvesGeometry &b_curves, const int motion_step)
static float4 LerpCurveSegmentMotionCV(ParticleCurveData *CData, const int sys, const int curve, const float step)
static void export_hair_curves(Scene *scene, Hair *hair, const blender::bke::CurvesGeometry &b_curves, const bool need_motion, const float motion_scale)
static float4 curve_point_as_float4(const blender::Span< blender::float3 > b_positions, const blender::Span< float > b_radius, const int index)
static float shaperadius(const float shape, const float root, const float tip, const float time)
static float4 CurveSegmentMotionCV(ParticleCurveData *CData, const int sys, const int curve, const int curvekey)
static void attr_create_motion_from_velocity(Hair *hair, const blender::Span< blender::float3 > src, const float motion_scale)
static float4 interpolate_curve_points(const blender::Span< blender::float3 > b_positions, const blender::Span< float > b_radius, const int first_point_index, const int num_points, const float step)
static void export_hair_motion_validate_attribute(Hair *hair, const int motion_step, const int num_motion_keys, bool have_motion)
static void fill_generic_attribute(const int num_curves, const int num_points, TypeInCycles *data, const AttributeElement element, const GetValueAtIndex &get_value_at_index)
static void attr_create_generic(Scene *scene, Hair *hair, const blender::bke::CurvesGeometry &b_curves, const bool need_motion, const float motion_scale)
static bool ObtainCacheParticleVcol(Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, const int vcol_num)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const void * element
ATTR_WARN_UNUSED_RESULT const BMLoop * l
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void update(AttributeSet &&new_attributes)
Attribute * find(ustring name) const
Attribute * add(ustring name, const TypeDesc type, AttributeElement element)
void remove(ustring name)
void tag_update(Scene *scene, bool rebuild)
bool need_attribute(Scene *scene, AttributeStandard std)
AttributeSet attributes
Definition hair.h:13
void add_curve_key(const float3 co, const float radius)
Definition hair.cpp:333
Curve get_curve(const size_t i) const
Definition hair.h:111
void add_curve(const int first_key, const int shader)
Definition hair.cpp:342
size_t num_curves() const
Definition hair.h:126
void copy_center_to_motion_step(const int motion_step)
Definition hair.cpp:351
void resize_curves(const int numcurves, const int numkeys)
Definition hair.cpp:301
CurveShapeType curve_shape
Definition hair.h:91
void clear(bool preserve_shaders=false) override
Definition hair.cpp:321
size_t num_keys() const
Definition hair.h:121
void reserve_curves(const int numcurves, const int numkeys)
Definition hair.cpp:311
array< int > curve_firstkey
array< bool > psys_closetip
array< float > psys_tipradius
array< int > psys_shader
array< float > curve_length
array< float4 > curve_vcol
array< float > curvekey_time
array< float > psys_rootradius
array< int > psys_firstcurve
array< int > psys_curvenum
array< float2 > curve_uv
array< float > psys_shape
array< int > curve_keynum
array< float3 > curvekey_co
size_t size() const
void reserve(const size_t newcapacity)
void push_back_slow(const T &t)
constexpr int64_t first() const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr IndexRange index_range() const
constexpr IndexRange drop_front(int64_t n) const
Span< NewT > constexpr cast() const
Definition BLI_span.hh:418
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader get() const
OffsetIndices< int > points_by_curve() const
Span< float3 > positions() const
AttributeAccessor attributes() const
nullptr float
ccl_device float4 color_srgb_to_linear_v4(const float4 c)
Definition color.h:347
static BL::Mesh object_to_mesh(BObjectInfo &b_ob_info)
static void mesh_texture_space(const ::Mesh &b_mesh, float3 &loc, float3 &size)
static void free_object_to_mesh(BObjectInfo &b_ob_info, BL::Mesh &mesh)
static bool object_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
static Transform get_transform(const BL::Array< float, 16 > &array)
#define powf(x, y)
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define assert(assertion)
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
constexpr T clamp(T, U, U) RET
float length(VecOp< float, D >) RET
VecBase< float, 2 > float2
VecBase< float, 4 > float4
VecBase< float, 3 > float3
ccl_device_inline float hash_uint2_to_float(const uint kx, const uint ky)
Definition hash.h:197
AttributeStandard
@ ATTR_STD_CURVE_INTERCEPT
@ ATTR_STD_UV
@ ATTR_STD_VERTEX_NORMAL
@ ATTR_STD_NONE
@ ATTR_STD_MOTION_VERTEX_POSITION
@ ATTR_STD_CURVE_RANDOM
@ ATTR_STD_GENERATED
@ ATTR_STD_CURVE_LENGTH
@ CURVE_THICK_LINEAR
AttributeElement
@ ATTR_ELEMENT_NONE
@ ATTR_ELEMENT_CURVE_KEY
@ ATTR_ELEMENT_CURVE
#define LOG_DEBUG
Definition log.h:107
#define LOG_ERROR
Definition log.h:101
CCL_NAMESPACE_BEGIN ccl_device_inline float2 zero_float2()
Definition math_float2.h:13
ccl_device_inline float len_squared(const float2 a)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void curves_normals_point_domain_calc(const CurvesGeometry &curves, MutableSpan< float3 > normals)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
static constexpr TypeDesc TypeRGBA(TypeDesc::FLOAT, TypeDesc::VEC4, TypeDesc::COLOR)
const char * name
#define mix
#define floorf
#define make_float4
#define min(a, b)
Definition sort.cc:36
float * data_float()
float3 * data_float3()
float4 * data_float4()
void add(const float &f)
float2 * data_float2()
BL::Object real_object
bool is_real_object_data() const
CurvesGeometry geometry
int first_key
Definition hair.h:19
int num_keys
Definition hair.h:20
vector< SocketType, std::allocator< SocketType > > inputs
Definition node_type.h:128
const NodeType * type
Definition graph/node.h:178
void set_value(const SocketType &socket, const Node &other, const SocketType &other_socket)
ustring name
Definition node_type.h:81
float x
float x
Definition sky_math.h:136
float x
Definition sky_math.h:225
float w
Definition sky_math.h:225
i
Definition text_draw.cc:230
ccl_device_inline Transform transform_inverse(const Transform tfm)
Definition transform.h:525
ccl_device_inline float3 transform_point(const ccl_private Transform *t, const float3 a)
Definition transform.h:56
uint len