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