Blender V4.5
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 VLOG_WARNING << "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 VLOG_WORK << "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
680template<typename TypeInCycles, typename GetValueAtIndex>
681static void fill_generic_attribute(const int num_curves,
682 const int num_points,
683 TypeInCycles *data,
685 const GetValueAtIndex &get_value_at_index)
686{
687 switch (element) {
689 for (int i = 0; i < num_points; i++) {
690 data[i] = get_value_at_index(i);
691 }
692 break;
693 }
694 case ATTR_ELEMENT_CURVE: {
695 for (int i = 0; i < num_curves; i++) {
696 data[i] = get_value_at_index(i);
697 }
698 break;
699 }
700 default: {
701 assert(false);
702 break;
703 }
704 }
705}
706
709 const float motion_scale)
710{
711 const int num_curve_keys = hair->get_curve_keys().size();
712
713 /* Override motion steps to fixed number. */
714 hair->set_motion_steps(3);
715
716 /* Find or add attribute */
717 float3 *P = hair->get_curve_keys().data();
719
720 if (!attr_mP) {
722 }
723
724 /* Only export previous and next frame, we don't have any in between data. */
725 const float motion_times[2] = {-1.0f, 1.0f};
726 for (int step = 0; step < 2; step++) {
727 const float relative_time = motion_times[step] * 0.5f * motion_scale;
728 float3 *mP = attr_mP->data_float3() + step * num_curve_keys;
729
730 for (int i = 0; i < num_curve_keys; i++) {
731 mP[i] = P[i] + make_float3(src[i][0], src[i][1], src[i][2]) * relative_time;
732 }
733 }
734}
735
736static void attr_create_generic(Scene *scene,
737 Hair *hair,
738 const blender::bke::CurvesGeometry &b_curves,
739 const bool need_motion,
740 const float motion_scale)
741{
742 const blender::bke::AttributeAccessor b_attributes = b_curves.attributes();
743
744 AttributeSet &attributes = hair->attributes;
745 static const ustring u_velocity("velocity");
746 const bool need_uv = hair->need_attribute(scene, ATTR_STD_UV);
747 bool have_uv = false;
748
749 b_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
750 const ustring name{std::string_view(iter.name)};
751
752 const blender::bke::AttrDomain b_domain = iter.domain;
753 const eCustomDataType b_data_type = iter.data_type;
754
755 if (need_motion && name == u_velocity) {
756 const blender::VArraySpan b_attr = *iter.get<blender::float3>(
758 attr_create_motion_from_velocity(hair, b_attr, motion_scale);
759 return;
760 }
761
762 /* Weak, use first float2 attribute as standard UV. */
763 if (need_uv && !have_uv && b_data_type == CD_PROP_FLOAT2 &&
765 {
766 Attribute *attr = attributes.add(ATTR_STD_UV, name);
767
768 const blender::VArraySpan b_attr = *iter.get<blender::float2>();
769
770 static_assert(sizeof(blender::float2) == sizeof(float2));
771 const blender::Span src = b_attr.cast<float2>();
772 std::copy(src.begin(), src.end(), attr->data_float2());
773 have_uv = true;
774 return;
775 }
776
777 if (!hair->need_attribute(scene, name)) {
778 return;
779 }
780 if (attributes.find(name)) {
781 return;
782 }
783
784 const blender::bke::GAttributeReader b_attr = iter.get();
785
787 switch (b_attr.domain) {
790 break;
793 break;
794 default:
795 return;
796 }
797
799 using BlenderT = decltype(dummy);
800 using Converter = typename ccl::AttributeConverter<BlenderT>;
801 using CyclesT = typename Converter::CyclesT;
802 if constexpr (!std::is_void_v<CyclesT>) {
803 Attribute *attr = attributes.add(name, Converter::type_desc, element);
804 CyclesT *data = reinterpret_cast<CyclesT *>(attr->data());
805
806 const blender::VArraySpan src = b_attr.varray.typed<BlenderT>();
807 for (const int i : src.index_range()) {
808 data[i] = Converter::convert(src[i]);
809 }
810 }
811 });
812 });
813}
814
816 const blender::Span<float> b_radius,
817 const int index)
818{
819 float4 mP = make_float4(
820 b_positions[index][0], b_positions[index][1], b_positions[index][2], 0.0f);
821 mP.w = b_radius.is_empty() ? 0.005f : b_radius[index];
822 return mP;
823}
824
826 const blender::Span<float> b_radius,
827 const int first_point_index,
828 const int num_points,
829 const float step)
830{
831 const float curve_t = step * (num_points - 1);
832 const int point_a = clamp((int)curve_t, 0, num_points - 1);
833 const int point_b = min(point_a + 1, num_points - 1);
834 const float t = curve_t - (float)point_a;
835 return mix(curve_point_as_float4(b_positions, b_radius, first_point_index + point_a),
836 curve_point_as_float4(b_positions, b_radius, first_point_index + point_b),
837 t);
838}
839
840static void export_hair_curves(Scene *scene,
841 Hair *hair,
842 const blender::bke::CurvesGeometry &b_curves,
843 const bool need_motion,
844 const float motion_scale)
845{
846 const blender::Span<blender::float3> positions = b_curves.positions();
847 const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
848
849 hair->resize_curves(points_by_curve.size(), positions.size());
850
851 float3 *curve_keys = hair->get_curve_keys().data();
852 float *curve_radius = hair->get_curve_radius().data();
853 int *curve_first_key = hair->get_curve_first_key().data();
854 int *curve_shader = hair->get_curve_shader().data();
855
856 /* Add requested attributes. */
857 float *attr_intercept = nullptr;
858 float *attr_length = nullptr;
859
860 if (hair->need_attribute(scene, ATTR_STD_VERTEX_NORMAL)) {
861 /* Get geometry normals. */
862 float3 *attr_normal = hair->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3();
863 vector<blender::float3> point_normals(positions.size());
865 b_curves, {point_normals.data(), int64_t(point_normals.size())});
866 for (const int i : positions.index_range()) {
867 attr_normal[i] = make_float3(point_normals[i][0], point_normals[i][1], point_normals[i][2]);
868 }
869 }
870
871 if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
872 attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT)->data_float();
873 }
874 if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) {
875 attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH)->data_float();
876 }
877 if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
878 float *attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM)->data_float();
879 for (const int i : points_by_curve.index_range()) {
880 attr_random[i] = hash_uint2_to_float(i, 0);
881 }
882 }
883
884 const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>(
886
887 std::copy(points_by_curve.data().data(),
888 points_by_curve.data().data() + points_by_curve.size(),
889 curve_first_key);
890 std::fill(curve_shader, curve_shader + points_by_curve.size(), 0);
891 if (!b_radius.is_empty()) {
892 std::copy(b_radius.data(), b_radius.data() + positions.size(), curve_radius);
893 }
894 else {
895 std::fill(curve_radius, curve_radius + positions.size(), 0.005f);
896 }
897
898 /* Export curves and points. */
899 for (const int curve : points_by_curve.index_range()) {
900 const blender::IndexRange points = points_by_curve[curve];
901
902 float3 prev_co = zero_float3();
903 float length = 0.0f;
904
905 /* Position and radius. */
906 for (const int point : points) {
907 const float3 co = make_float3(positions[point][0], positions[point][1], positions[point][2]);
908
909 curve_keys[point] = co;
910
911 if (attr_length || attr_intercept) {
912 if (point != points.first()) {
913 length += len(co - prev_co);
914 }
915 prev_co = co;
916
917 if (attr_intercept) {
918 attr_intercept[point] = length;
919 }
920 }
921 }
922
923 /* Normalized 0..1 attribute along curve. */
924 if (attr_intercept && length > 0.0f) {
925 for (const int point : points.drop_front(1)) {
926 attr_intercept[point] /= length;
927 }
928 }
929
930 /* Curve length. */
931 if (attr_length) {
932 attr_length[curve] = length;
933 }
934 }
935
936 attr_create_generic(scene, hair, b_curves, need_motion, motion_scale);
937}
938
940 const blender::bke::CurvesGeometry &b_curves,
941 const int motion_step)
942{
943 /* Find or add attribute. */
945 bool new_attribute = false;
946
947 if (!attr_mP) {
949 new_attribute = true;
950 }
951
952 /* Export motion keys. */
953 const size_t num_keys = hair->num_keys();
954 const size_t num_curves = hair->num_curves();
955 float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
956 bool have_motion = false;
957 int num_motion_keys = 0;
958 int curve_index = 0;
959
960 const blender::Span<blender::float3> b_positions = b_curves.positions();
961 const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
962 const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>(
964
965 for (const int i : points_by_curve.index_range()) {
966 const blender::IndexRange points = points_by_curve[i];
967 if (curve_index >= num_curves) {
968 break;
969 }
970
971 const Hair::Curve curve = hair->get_curve(curve_index);
972 curve_index++;
973
974 if (points.size() == curve.num_keys) {
975 /* Number of keys matches. */
976 for (const int i : points.index_range()) {
977 const int point = points[i];
978
979 if (point < num_keys) {
980 mP[num_motion_keys] = curve_point_as_float4(b_positions, b_radius, point);
981 num_motion_keys++;
982
983 if (!have_motion) {
984 /* TODO: use epsilon for comparison? Was needed for particles due to
985 * transform, but ideally should not happen anymore. */
986 float4 curve_key = make_float4(hair->get_curve_keys()[i]);
987 curve_key.w = hair->get_curve_radius()[i];
988 have_motion = !(mP[i] == curve_key);
989 }
990 }
991 }
992 }
993 else {
994 /* Number of keys has changed. Generate an interpolated version
995 * to preserve motion blur. */
996 const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f;
997 for (int i = 0; i < curve.num_keys; i++) {
998 const float step = i * step_size;
999 mP[num_motion_keys] = interpolate_curve_points(
1000 b_positions, b_radius, points.start(), points.size(), step);
1001 num_motion_keys++;
1002 }
1003 have_motion = true;
1004 }
1005 }
1006
1007 /* In case of new attribute, we verify if there really was any motion. */
1008 if (new_attribute) {
1009 export_hair_motion_validate_attribute(hair, motion_step, num_motion_keys, have_motion);
1010 }
1011}
1012
1013/* Hair object. */
1014void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, const int motion_step)
1015{
1016 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
1017 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
1018 const float motion_scale = (need_motion) ?
1019 scene->motion_shutter_time() /
1020 (b_scene.render().fps() / b_scene.render().fps_base()) :
1021 0.0f;
1022
1023 /* Convert Blender hair to Cycles curves. */
1024 const blender::bke::CurvesGeometry &b_curves(
1025 static_cast<const ::Curves *>(b_ob_info.object_data.ptr.data)->geometry.wrap());
1026 if (motion) {
1027 export_hair_curves_motion(hair, b_curves, motion_step);
1028 }
1029 else {
1030 export_hair_curves(scene, hair, b_curves, need_motion, motion_scale);
1031 }
1032}
1033
1034void BlenderSync::sync_hair(BObjectInfo &b_ob_info, Hair *hair)
1035{
1036 /* make a copy of the shaders as the caller in the main thread still need them for syncing the
1037 * attributes */
1038 array<Node *> used_shaders = hair->get_used_shaders();
1039
1040 Hair new_hair;
1041 new_hair.set_used_shaders(used_shaders);
1042
1043 if (view_layer.use_hair) {
1044 if (b_ob_info.object_data.is_a(&RNA_Curves)) {
1045 /* Hair object. */
1046 sync_hair(&new_hair, b_ob_info, false);
1047 }
1048 else {
1049 /* Particle hair. */
1050 BL::Mesh b_mesh = object_to_mesh(b_ob_info);
1051
1052 if (b_mesh) {
1053 sync_particle_hair(&new_hair, b_mesh, b_ob_info, false);
1054 free_object_to_mesh(b_ob_info, b_mesh);
1055 }
1056 }
1057 }
1058
1059 /* update original sockets */
1060
1061 for (const SocketType &socket : new_hair.type->inputs) {
1062 /* Those sockets are updated in sync_object, so do not modify them. */
1063 if (socket.name == "use_motion_blur" || socket.name == "used_shaders") {
1064 continue;
1065 }
1066 hair->set_value(socket, new_hair, socket);
1067 }
1068
1069 hair->attributes.update(std::move(new_hair.attributes));
1070
1071 /* tag update */
1072
1073 /* Compares curve_keys rather than strands in order to handle quick hair
1074 * adjustments in dynamic BVH - other methods could probably do this better. */
1075 const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
1076
1077 hair->tag_update(scene, rebuild);
1078}
1079
1080void BlenderSync::sync_hair_motion(BObjectInfo &b_ob_info, Hair *hair, const int motion_step)
1081{
1082 /* Skip if nothing exported. */
1083 if (hair->num_keys() == 0) {
1084 return;
1085 }
1086
1087 /* Export deformed coordinates. */
1088 if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
1089 if (b_ob_info.object_data.is_a(&RNA_Curves)) {
1090 /* Hair object. */
1091 sync_hair(hair, b_ob_info, true, motion_step);
1092 return;
1093 }
1094
1095 /* Particle hair. */
1096 BL::Mesh b_mesh = object_to_mesh(b_ob_info);
1097 if (b_mesh) {
1098 sync_particle_hair(hair, b_mesh, b_ob_info, true, motion_step);
1099 free_object_to_mesh(b_ob_info, b_mesh);
1100 return;
1101 }
1102 }
1103
1104 /* No deformation on this frame, copy coordinates if other frames did have it. */
1105 hair->copy_center_to_motion_step(motion_step);
1106}
1107
Low-level operations for curves.
struct Curves Curves
@ CD_PROP_FLOAT2
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
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
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 float4 make_float4(const float x, const float y, const float z, const float w)
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define floorf(x)
VecBase< float, 2 > float2
VecBase< float, 4 > float4
#define assert(assertion)
VecBase< float, 3 > float3
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
constexpr T clamp(T, U, U) RET
float length(VecOp< float, D >) RET
ccl_device_inline float hash_uint2_to_float(const uint kx, const uint ky)
Definition hash.h:148
#define mix(a, b, c)
Definition hash.h:35
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
AttributeElement
@ ATTR_ELEMENT_NONE
@ ATTR_ELEMENT_CURVE_KEY
@ ATTR_ELEMENT_CURVE
#define VLOG_WARNING
Definition log.h:69
#define VLOG_WORK
Definition log.h:74
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:15
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)
#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:125
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:79
float x
float x
Definition sky_float3.h:27
i
Definition text_draw.cc:230
ccl_device_inline Transform transform_inverse(const Transform tfm)
Definition transform.h:492
ccl_device_inline float3 transform_point(const ccl_private Transform *t, const float3 a)
Definition transform.h:56
uint len