2023-06-14 16:52:36 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
2012-12-28 14:21:30 +00:00
|
|
|
|
2022-02-03 10:49:51 -06:00
|
|
|
#include <optional>
|
|
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
#include "blender/attribute_convert.h"
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "blender/sync.h"
|
|
|
|
|
#include "blender/util.h"
|
|
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/attribute.h"
|
|
|
|
|
#include "scene/camera.h"
|
|
|
|
|
#include "scene/curves.h"
|
|
|
|
|
#include "scene/hair.h"
|
|
|
|
|
#include "scene/object.h"
|
|
|
|
|
#include "scene/scene.h"
|
Cycles: Make all #include statements relative to cycles source directory
The idea is to make include statements more explicit and obvious where the
file is coming from, additionally reducing chance of wrong header being
picked up.
For example, it was not obvious whether bvh.h was refferring to builder
or traversal, whenter node.h is a generic graph node or a shader node
and cases like that.
Surely this might look obvious for the active developers, but after some
time of not touching the code it becomes less obvious where file is coming
from.
This was briefly mentioned in T50824 and seems @brecht is fine with such
explicitness, but need to agree with all active developers before committing
this.
Please note that this patch is lacking changes related on GPU/OpenCL
support. This will be solved if/when we all agree this is a good idea to move
forward.
Reviewers: brecht, lukasstockner97, maiself, nirved, dingto, juicyfruit, swerner
Reviewed By: lukasstockner97, maiself, nirved, dingto
Subscribers: brecht
Differential Revision: https://developer.blender.org/D2586
2017-03-28 20:39:14 +02:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "util/color.h"
|
2024-12-26 19:41:25 +01:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "util/hash.h"
|
|
|
|
|
#include "util/log.h"
|
2012-12-28 14:21:30 +00:00
|
|
|
|
2023-12-20 13:25:08 -05:00
|
|
|
#include "BKE_attribute.hh"
|
2023-09-18 02:50:09 +02:00
|
|
|
#include "BKE_curves.hh"
|
|
|
|
|
|
2012-12-28 14:21:30 +00:00
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
ParticleCurveData::ParticleCurveData() = default;
|
2012-12-28 14:21:30 +00:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
ParticleCurveData::~ParticleCurveData() = default;
|
2012-12-28 14:21:30 +00:00
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static float shaperadius(const float shape, const float root, const float tip, const float time)
|
2012-12-28 14:21:30 +00:00
|
|
|
{
|
2018-12-20 16:45:38 +01:00
|
|
|
assert(time >= 0.0f);
|
|
|
|
|
assert(time <= 1.0f);
|
2012-12-28 14:21:30 +00:00
|
|
|
float radius = 1.0f - time;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-03 12:08:54 +00:00
|
|
|
if (shape != 0.0f) {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (shape < 0.0f) {
|
2013-05-18 11:04:29 +00:00
|
|
|
radius = powf(radius, 1.0f + shape);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2013-05-18 11:04:29 +00:00
|
|
|
radius = powf(radius, 1.0f / (1.0f - shape));
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
|
|
|
|
return (radius * (root - tip)) + tip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* curve functions */
|
|
|
|
|
|
2017-01-12 15:28:19 +01:00
|
|
|
static bool ObtainCacheParticleData(
|
2020-06-04 15:12:31 +02:00
|
|
|
Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
|
2012-12-28 14:21:30 +00:00
|
|
|
{
|
|
|
|
|
int curvenum = 0;
|
|
|
|
|
int keyno = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (!(hair && b_mesh && b_ob && CData)) {
|
2012-12-28 14:21:30 +00:00
|
|
|
return false;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const Transform tfm = get_transform(b_ob->matrix_world());
|
|
|
|
|
const Transform itfm = transform_inverse(tfm);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-01-25 16:20:10 +01:00
|
|
|
for (BL::Modifier &b_mod : b_ob->modifiers) {
|
2024-12-26 17:53:59 +01:00
|
|
|
if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
|
2021-01-25 16:20:10 +01:00
|
|
|
(background ? b_mod.show_render() : b_mod.show_viewport()))
|
|
|
|
|
{
|
|
|
|
|
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
|
2012-12-28 14:21:30 +00:00
|
|
|
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
|
|
|
|
|
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-12-21 00:20:16 +01:00
|
|
|
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
|
|
|
|
|
(b_part.type() == BL::ParticleSettings::type_HAIR))
|
|
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const int shader = clamp(b_part.material() - 1, 0, hair->get_used_shaders().size() - 1);
|
|
|
|
|
const int display_step = background ? b_part.render_step() : b_part.display_step();
|
|
|
|
|
const int totparts = b_psys.particles.length();
|
|
|
|
|
const int totchild = background ? b_psys.child_particles.length() :
|
|
|
|
|
(int)((float)b_psys.child_particles.length() *
|
|
|
|
|
(float)b_part.display_percentage() / 100.0f);
|
2012-12-28 14:21:30 +00:00
|
|
|
int totcurves = totchild;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (b_part.child_type() == 0 || totchild == 0) {
|
2012-12-28 14:21:30 +00:00
|
|
|
totcurves += totparts;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (totcurves == 0) {
|
2012-12-28 14:21:30 +00:00
|
|
|
continue;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-09-03 18:58:41 +02:00
|
|
|
int ren_step = (1 << display_step) + 1;
|
2024-12-26 17:53:59 +01:00
|
|
|
if (b_part.kink() == BL::ParticleSettings::kink_SPIRAL) {
|
2015-01-13 19:18:46 +01:00
|
|
|
ren_step += b_part.kink_extra_steps();
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-08 00:09:08 +02:00
|
|
|
CData->psys_firstcurve.push_back_slow(curvenum);
|
|
|
|
|
CData->psys_curvenum.push_back_slow(totcurves);
|
|
|
|
|
CData->psys_shader.push_back_slow(shader);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const float radius = b_part.radius_scale() * 0.5f;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-01-08 20:37:35 +01:00
|
|
|
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
|
|
|
|
|
CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
|
2018-05-29 11:22:07 +02:00
|
|
|
CData->psys_shape.push_back_slow(b_part.shape());
|
|
|
|
|
CData->psys_closetip.push_back_slow(b_part.use_close_tip());
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-28 14:21:30 +00:00
|
|
|
int pa_no = 0;
|
2024-12-26 17:53:59 +01:00
|
|
|
if (!(b_part.child_type() == 0) && totchild != 0) {
|
2012-12-28 14:21:30 +00:00
|
|
|
pa_no = totparts;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const int num_add = (totparts + totchild - pa_no);
|
2014-01-23 18:14:19 +01:00
|
|
|
CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
|
|
|
|
|
CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
|
|
|
|
|
CData->curve_length.reserve(CData->curve_length.size() + num_add);
|
2015-01-13 19:18:46 +01:00
|
|
|
CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add * ren_step);
|
|
|
|
|
CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add * ren_step);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-28 14:21:30 +00:00
|
|
|
for (; pa_no < totparts + totchild; pa_no++) {
|
2014-02-10 18:33:27 +01:00
|
|
|
int keynum = 0;
|
2016-05-08 00:09:08 +02:00
|
|
|
CData->curve_firstkey.push_back_slow(keyno);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-28 14:21:30 +00:00
|
|
|
float curve_length = 0.0f;
|
2021-02-17 01:47:18 +01:00
|
|
|
float3 prev_co_world = zero_float3();
|
|
|
|
|
float3 prev_co_object = zero_float3();
|
2015-01-13 19:18:46 +01:00
|
|
|
for (int step_no = 0; step_no < ren_step; step_no++) {
|
2020-02-03 17:40:03 +01:00
|
|
|
float3 co_world = prev_co_world;
|
|
|
|
|
b_psys.co_hair(*b_ob, pa_no, step_no, &co_world.x);
|
2024-12-29 17:32:00 +01:00
|
|
|
const float3 co_object = transform_point(&itfm, co_world);
|
2014-02-10 18:33:27 +01:00
|
|
|
if (step_no > 0) {
|
2020-02-03 17:40:03 +01:00
|
|
|
const float step_length = len(co_object - prev_co_object);
|
2014-02-10 18:33:27 +01:00
|
|
|
curve_length += step_length;
|
|
|
|
|
}
|
2020-02-03 17:40:03 +01:00
|
|
|
CData->curvekey_co.push_back_slow(co_object);
|
2016-05-08 00:09:08 +02:00
|
|
|
CData->curvekey_time.push_back_slow(curve_length);
|
2020-02-03 17:40:03 +01:00
|
|
|
prev_co_object = co_object;
|
|
|
|
|
prev_co_world = co_world;
|
2014-02-10 18:33:27 +01:00
|
|
|
keynum++;
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
2015-01-16 23:43:57 +05:00
|
|
|
keyno += keynum;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-08 00:09:08 +02:00
|
|
|
CData->curve_keynum.push_back_slow(keynum);
|
|
|
|
|
CData->curve_length.push_back_slow(curve_length);
|
2013-01-04 12:44:38 +00:00
|
|
|
curvenum++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-04 12:44:38 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
static bool ObtainCacheParticleUV(Hair *hair,
|
2017-01-12 15:28:19 +01:00
|
|
|
BL::Mesh *b_mesh,
|
|
|
|
|
BL::Object *b_ob,
|
|
|
|
|
ParticleCurveData *CData,
|
|
|
|
|
bool background,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int uv_num)
|
2013-01-04 12:44:38 +00:00
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (!(hair && b_mesh && b_ob && CData)) {
|
2013-01-04 12:44:38 +00:00
|
|
|
return false;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-23 17:15:45 +00:00
|
|
|
CData->curve_uv.clear();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-01-25 16:20:10 +01:00
|
|
|
for (BL::Modifier &b_mod : b_ob->modifiers) {
|
2024-12-26 17:53:59 +01:00
|
|
|
if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
|
2021-01-25 16:20:10 +01:00
|
|
|
(background ? b_mod.show_render() : b_mod.show_viewport()))
|
|
|
|
|
{
|
|
|
|
|
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
|
2013-01-04 12:44:38 +00:00
|
|
|
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
|
|
|
|
|
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-12-21 00:20:16 +01:00
|
|
|
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
|
|
|
|
|
(b_part.type() == BL::ParticleSettings::type_HAIR))
|
|
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const int totparts = b_psys.particles.length();
|
|
|
|
|
const int totchild = background ? b_psys.child_particles.length() :
|
|
|
|
|
(int)((float)b_psys.child_particles.length() *
|
|
|
|
|
(float)b_part.display_percentage() / 100.0f);
|
2013-01-04 12:44:38 +00:00
|
|
|
int totcurves = totchild;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (b_part.child_type() == 0 || totchild == 0) {
|
2013-01-04 12:44:38 +00:00
|
|
|
totcurves += totparts;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (totcurves == 0) {
|
2013-01-04 12:44:38 +00:00
|
|
|
continue;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-04 12:44:38 +00:00
|
|
|
int pa_no = 0;
|
2024-12-26 17:53:59 +01:00
|
|
|
if (!(b_part.child_type() == 0) && totchild != 0) {
|
2013-01-04 12:44:38 +00:00
|
|
|
pa_no = totparts;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const int num_add = (totparts + totchild - pa_no);
|
2014-01-23 18:14:19 +01:00
|
|
|
CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-04 12:44:38 +00:00
|
|
|
BL::ParticleSystem::particles_iterator b_pa;
|
|
|
|
|
b_psys.particles.begin(b_pa);
|
|
|
|
|
for (; pa_no < totparts + totchild; pa_no++) {
|
2013-12-21 00:20:16 +01:00
|
|
|
/* Add UVs */
|
2018-09-06 14:28:14 +02:00
|
|
|
BL::Mesh::uv_layers_iterator l;
|
|
|
|
|
b_mesh->uv_layers.begin(l);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-17 01:47:18 +01:00
|
|
|
float2 uv = zero_float2();
|
2024-12-26 17:53:59 +01:00
|
|
|
if (!b_mesh->uv_layers.empty()) {
|
2013-01-23 17:15:45 +00:00
|
|
|
b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2016-05-08 00:09:08 +02:00
|
|
|
CData->curve_uv.push_back_slow(uv);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (pa_no < totparts && b_pa != b_psys.particles.end()) {
|
2012-12-28 14:21:30 +00:00
|
|
|
++b_pa;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2013-01-04 12:44:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-04 12:44:38 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
static bool ObtainCacheParticleVcol(Hair *hair,
|
2017-01-12 15:28:19 +01:00
|
|
|
BL::Mesh *b_mesh,
|
|
|
|
|
BL::Object *b_ob,
|
|
|
|
|
ParticleCurveData *CData,
|
|
|
|
|
bool background,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int vcol_num)
|
2013-01-04 12:44:38 +00:00
|
|
|
{
|
2023-09-17 09:01:48 +10:00
|
|
|
if (!(hair && b_mesh && b_ob && CData)) {
|
2013-01-04 12:44:38 +00:00
|
|
|
return false;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-23 17:15:45 +00:00
|
|
|
CData->curve_vcol.clear();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-01-25 16:20:10 +01:00
|
|
|
for (BL::Modifier &b_mod : b_ob->modifiers) {
|
2024-12-26 17:53:59 +01:00
|
|
|
if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
|
2021-01-25 16:20:10 +01:00
|
|
|
(background ? b_mod.show_render() : b_mod.show_viewport()))
|
|
|
|
|
{
|
|
|
|
|
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
|
2013-01-04 12:44:38 +00:00
|
|
|
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
|
|
|
|
|
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-12-21 00:20:16 +01:00
|
|
|
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
|
|
|
|
|
(b_part.type() == BL::ParticleSettings::type_HAIR))
|
|
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const int totparts = b_psys.particles.length();
|
|
|
|
|
const int totchild = background ? b_psys.child_particles.length() :
|
|
|
|
|
(int)((float)b_psys.child_particles.length() *
|
|
|
|
|
(float)b_part.display_percentage() / 100.0f);
|
2013-01-04 12:44:38 +00:00
|
|
|
int totcurves = totchild;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (b_part.child_type() == 0 || totchild == 0) {
|
2013-01-04 12:44:38 +00:00
|
|
|
totcurves += totparts;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (totcurves == 0) {
|
2013-01-04 12:44:38 +00:00
|
|
|
continue;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-04 12:44:38 +00:00
|
|
|
int pa_no = 0;
|
2024-12-26 17:53:59 +01:00
|
|
|
if (!(b_part.child_type() == 0) && totchild != 0) {
|
2013-01-04 12:44:38 +00:00
|
|
|
pa_no = totparts;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const int num_add = (totparts + totchild - pa_no);
|
2014-01-23 18:14:19 +01:00
|
|
|
CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-04 12:44:38 +00:00
|
|
|
BL::ParticleSystem::particles_iterator b_pa;
|
|
|
|
|
b_psys.particles.begin(b_pa);
|
|
|
|
|
for (; pa_no < totparts + totchild; pa_no++) {
|
2013-12-21 00:20:16 +01:00
|
|
|
/* Add vertex colors */
|
2018-09-06 14:28:14 +02:00
|
|
|
BL::Mesh::vertex_colors_iterator l;
|
|
|
|
|
b_mesh->vertex_colors.begin(l);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-30 14:07:49 +02:00
|
|
|
float4 vcol = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
|
2024-12-26 17:53:59 +01:00
|
|
|
if (!b_mesh->vertex_colors.empty()) {
|
2013-01-04 12:44:38 +00:00
|
|
|
b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2016-05-08 00:09:08 +02:00
|
|
|
CData->curve_vcol.push_back_slow(vcol);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (pa_no < totparts && b_pa != b_psys.particles.end()) {
|
2013-01-04 12:44:38 +00:00
|
|
|
++b_pa;
|
2024-12-26 17:53:59 +01:00
|
|
|
}
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-28 14:21:30 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-02 12:04:19 +01:00
|
|
|
static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CData)
|
2012-12-28 14:21:30 +00:00
|
|
|
{
|
2013-01-03 12:09:09 +00:00
|
|
|
int num_keys = 0;
|
|
|
|
|
int num_curves = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (hair->num_curves()) {
|
2012-12-28 14:21:30 +00:00
|
|
|
return;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
Attribute *attr_normal = nullptr;
|
|
|
|
|
Attribute *attr_intercept = nullptr;
|
|
|
|
|
Attribute *attr_length = nullptr;
|
|
|
|
|
Attribute *attr_random = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (hair->need_attribute(scene, ATTR_STD_VERTEX_NORMAL)) {
|
Cycles: new Principled Hair BSDF variant with elliptical cross-section support
Implements the paper [A Microfacet-based Hair Scattering
Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by
Weizhen Huang, Matthias B. Hullin and Johannes Hanika.
### Features:
- This is a far-field model, as opposed to the previous near-field
Principled Hair BSDF model. The hair is expected to be less noisy, but
lower roughness values takes longer to render due to numerical
integration along the hair width. The hair also appears to be flat when
viewed up-close.
- The longitudinal width of the scattering lobe differs along the
azimuth, providing a higher contrast compared to the evenly spread
scattering in the near-field Principled Hair BSDF model. For a more
detailed comparison, please refer to the original paper.
- Supports elliptical cross-sections, adding more realism as human hairs
are usually elliptical. The orientation of the cross-section is aligned
with the curve normal, which can be adjusted using geometry nodes.
Default is minimal twist. During sampling, light rays that hit outside
the hair width will continue propogating as if the material is
transparent.
- There is non-physical modulation factors for the first three
lobes (Reflection, Transmission, Secondary Reflection).
### Missing:
- A good default for cross-section orientation. There was an
attempt (9039f76928) to default the orientation to align with the curve
normal in the mathematical sense, but the stability (when animated) is
unclear and it would be a hassle to generalise to all curve types. After
the model is in main, we could experiment with the geometry nodes team
to see what works the best as a default.
Co-authored-by: Lukas Stockner <lukas.stockner@freenet.de>
Pull Request: https://projects.blender.org/blender/blender/pulls/105600
2023-08-18 12:46:13 +02:00
|
|
|
attr_normal = hair->attributes.add(ATTR_STD_VERTEX_NORMAL);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
|
2020-02-02 12:04:19 +01:00
|
|
|
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) {
|
2021-09-24 07:42:36 +02:00
|
|
|
attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
|
2020-02-02 12:04:19 +01:00
|
|
|
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-01-23 18:14:19 +01:00
|
|
|
/* compute and reserve size of arrays */
|
2017-01-12 15:30:56 +01:00
|
|
|
for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
|
|
|
|
|
for (int curve = CData->psys_firstcurve[sys];
|
|
|
|
|
curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
|
|
|
|
|
curve++)
|
|
|
|
|
{
|
2014-01-23 18:14:19 +01:00
|
|
|
num_keys += CData->curve_keynum[curve];
|
|
|
|
|
num_curves++;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-04 11:17:38 +01:00
|
|
|
hair->reserve_curves(hair->num_curves() + num_curves, hair->get_curve_keys().size() + num_keys);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-01-23 18:14:19 +01:00
|
|
|
num_keys = 0;
|
|
|
|
|
num_curves = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-01-23 18:14:19 +01:00
|
|
|
/* actually export */
|
2017-01-12 15:30:56 +01:00
|
|
|
for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
|
|
|
|
|
for (int curve = CData->psys_firstcurve[sys];
|
|
|
|
|
curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
|
|
|
|
|
curve++)
|
|
|
|
|
{
|
2013-01-03 12:09:09 +00:00
|
|
|
size_t num_curve_keys = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-02-18 14:17:16 +01:00
|
|
|
for (int curvekey = CData->curve_firstkey[curve];
|
|
|
|
|
curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
|
|
|
|
|
curvekey++)
|
|
|
|
|
{
|
|
|
|
|
const float3 ickey_loc = CData->curvekey_co[curvekey];
|
|
|
|
|
const float curve_time = CData->curvekey_time[curvekey];
|
|
|
|
|
const float curve_length = CData->curve_length[curve];
|
|
|
|
|
const float time = (curve_length > 0.0f) ? curve_time / curve_length : 0.0f;
|
|
|
|
|
float radius = shaperadius(
|
|
|
|
|
CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], time);
|
|
|
|
|
if (CData->psys_closetip[sys] &&
|
|
|
|
|
(curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1))
|
|
|
|
|
{
|
2014-03-29 13:03:46 +01:00
|
|
|
radius = 0.0f;
|
2019-02-18 14:17:16 +01:00
|
|
|
}
|
2020-02-02 12:04:19 +01:00
|
|
|
hair->add_curve_key(ickey_loc, radius);
|
2023-09-17 09:01:48 +10:00
|
|
|
if (attr_intercept) {
|
2013-08-18 13:41:53 +00:00
|
|
|
attr_intercept->add(time);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Cycles: new Principled Hair BSDF variant with elliptical cross-section support
Implements the paper [A Microfacet-based Hair Scattering
Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by
Weizhen Huang, Matthias B. Hullin and Johannes Hanika.
### Features:
- This is a far-field model, as opposed to the previous near-field
Principled Hair BSDF model. The hair is expected to be less noisy, but
lower roughness values takes longer to render due to numerical
integration along the hair width. The hair also appears to be flat when
viewed up-close.
- The longitudinal width of the scattering lobe differs along the
azimuth, providing a higher contrast compared to the evenly spread
scattering in the near-field Principled Hair BSDF model. For a more
detailed comparison, please refer to the original paper.
- Supports elliptical cross-sections, adding more realism as human hairs
are usually elliptical. The orientation of the cross-section is aligned
with the curve normal, which can be adjusted using geometry nodes.
Default is minimal twist. During sampling, light rays that hit outside
the hair width will continue propogating as if the material is
transparent.
- There is non-physical modulation factors for the first three
lobes (Reflection, Transmission, Secondary Reflection).
### Missing:
- A good default for cross-section orientation. There was an
attempt (9039f76928) to default the orientation to align with the curve
normal in the mathematical sense, but the stability (when animated) is
unclear and it would be a hassle to generalise to all curve types. After
the model is in main, we could experiment with the geometry nodes team
to see what works the best as a default.
Co-authored-by: Lukas Stockner <lukas.stockner@freenet.de>
Pull Request: https://projects.blender.org/blender/blender/pulls/105600
2023-08-18 12:46:13 +02:00
|
|
|
if (attr_normal) {
|
|
|
|
|
/* NOTE: the geometry normals are not computed for legacy particle hairs. This hair
|
2023-09-06 14:03:10 +02:00
|
|
|
* system is expected to be deprecated. */
|
|
|
|
|
attr_normal->add(make_float3(0.0f, 0.0f, 0.0f));
|
Cycles: new Principled Hair BSDF variant with elliptical cross-section support
Implements the paper [A Microfacet-based Hair Scattering
Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by
Weizhen Huang, Matthias B. Hullin and Johannes Hanika.
### Features:
- This is a far-field model, as opposed to the previous near-field
Principled Hair BSDF model. The hair is expected to be less noisy, but
lower roughness values takes longer to render due to numerical
integration along the hair width. The hair also appears to be flat when
viewed up-close.
- The longitudinal width of the scattering lobe differs along the
azimuth, providing a higher contrast compared to the evenly spread
scattering in the near-field Principled Hair BSDF model. For a more
detailed comparison, please refer to the original paper.
- Supports elliptical cross-sections, adding more realism as human hairs
are usually elliptical. The orientation of the cross-section is aligned
with the curve normal, which can be adjusted using geometry nodes.
Default is minimal twist. During sampling, light rays that hit outside
the hair width will continue propogating as if the material is
transparent.
- There is non-physical modulation factors for the first three
lobes (Reflection, Transmission, Secondary Reflection).
### Missing:
- A good default for cross-section orientation. There was an
attempt (9039f76928) to default the orientation to align with the curve
normal in the mathematical sense, but the stability (when animated) is
unclear and it would be a hassle to generalise to all curve types. After
the model is in main, we could experiment with the geometry nodes team
to see what works the best as a default.
Co-authored-by: Lukas Stockner <lukas.stockner@freenet.de>
Pull Request: https://projects.blender.org/blender/blender/pulls/105600
2023-08-18 12:46:13 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-18 13:41:53 +00:00
|
|
|
num_curve_keys++;
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
if (attr_length != nullptr) {
|
2021-09-24 07:42:36 +02:00
|
|
|
attr_length->add(CData->curve_length[curve]);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
if (attr_random != nullptr) {
|
2019-08-21 20:04:09 +02:00
|
|
|
attr_random->add(hash_uint2_to_float(num_curves, 0));
|
2018-02-13 14:20:47 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-02-02 12:04:19 +01:00
|
|
|
hair->add_curve(num_keys, CData->psys_shader[sys]);
|
2013-01-03 12:09:09 +00:00
|
|
|
num_keys += num_curve_keys;
|
|
|
|
|
num_curves++;
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-11-28 16:13:00 +05:00
|
|
|
/* check allocation */
|
2020-11-04 11:17:38 +01:00
|
|
|
if ((hair->get_curve_keys().size() != num_keys) || (hair->num_curves() != num_curves)) {
|
2022-06-16 19:39:13 +02:00
|
|
|
VLOG_WARNING << "Hair memory allocation failed, clearing data.";
|
2020-10-31 19:21:07 +03:00
|
|
|
hair->clear(true);
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static float4 CurveSegmentMotionCV(ParticleCurveData *CData,
|
|
|
|
|
const int sys,
|
|
|
|
|
const int curve,
|
|
|
|
|
const int curvekey)
|
2018-11-23 13:21:55 +01:00
|
|
|
{
|
2019-02-18 14:17:16 +01:00
|
|
|
const float3 ickey_loc = CData->curvekey_co[curvekey];
|
|
|
|
|
const float curve_time = CData->curvekey_time[curvekey];
|
|
|
|
|
const float curve_length = CData->curve_length[curve];
|
2024-12-29 17:32:00 +01:00
|
|
|
const float time = (curve_length > 0.0f) ? curve_time / curve_length : 0.0f;
|
2018-11-23 13:21:55 +01:00
|
|
|
float radius = shaperadius(
|
|
|
|
|
CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], time);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-23 13:21:55 +01:00
|
|
|
if (CData->psys_closetip[sys] &&
|
|
|
|
|
(curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1))
|
2023-09-17 09:01:48 +10:00
|
|
|
{
|
2018-11-23 13:21:55 +01:00
|
|
|
radius = 0.0f;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-23 13:21:55 +01:00
|
|
|
/* curve motion keys store both position and radius in float4 */
|
2024-12-19 09:41:55 +01:00
|
|
|
float4 mP = make_float4(ickey_loc);
|
2018-11-23 13:21:55 +01:00
|
|
|
mP.w = radius;
|
|
|
|
|
return mP;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static float4 LerpCurveSegmentMotionCV(ParticleCurveData *CData,
|
|
|
|
|
const int sys,
|
|
|
|
|
const int curve,
|
|
|
|
|
const float step)
|
2018-11-23 13:21:55 +01:00
|
|
|
{
|
2018-12-20 16:45:38 +01:00
|
|
|
assert(step >= 0.0f);
|
|
|
|
|
assert(step <= 1.0f);
|
|
|
|
|
const int first_curve_key = CData->curve_firstkey[curve];
|
|
|
|
|
const float curve_key_f = step * (CData->curve_keynum[curve] - 1);
|
2018-11-23 13:21:55 +01:00
|
|
|
int curvekey = (int)floorf(curve_key_f);
|
2018-12-20 16:45:38 +01:00
|
|
|
const float remainder = curve_key_f - curvekey;
|
2018-11-23 13:21:55 +01:00
|
|
|
if (remainder == 0.0f) {
|
2018-12-20 16:45:38 +01:00
|
|
|
return CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey);
|
2018-11-23 13:21:55 +01:00
|
|
|
}
|
|
|
|
|
int curvekey2 = curvekey + 1;
|
|
|
|
|
if (curvekey2 >= (CData->curve_keynum[curve] - 1)) {
|
|
|
|
|
curvekey2 = (CData->curve_keynum[curve] - 1);
|
|
|
|
|
curvekey = curvekey2 - 1;
|
|
|
|
|
}
|
2018-12-20 16:45:38 +01:00
|
|
|
const float4 mP = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey);
|
|
|
|
|
const float4 mP2 = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey2);
|
2023-05-12 19:55:46 +02:00
|
|
|
return mix(mP, mP2, remainder);
|
2018-11-23 13:21:55 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-17 16:30:48 +01:00
|
|
|
static void export_hair_motion_validate_attribute(Hair *hair,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int motion_step,
|
|
|
|
|
const int num_motion_keys,
|
2020-03-17 16:30:48 +01:00
|
|
|
bool have_motion)
|
|
|
|
|
{
|
|
|
|
|
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
2020-11-04 11:17:38 +01:00
|
|
|
const int num_keys = hair->get_curve_keys().size();
|
2020-03-17 16:30:48 +01:00
|
|
|
|
|
|
|
|
if (num_motion_keys != num_keys || !have_motion) {
|
|
|
|
|
/* No motion or hair "topology" changed, remove attributes again. */
|
|
|
|
|
if (num_motion_keys != num_keys) {
|
2022-06-16 19:39:13 +02:00
|
|
|
VLOG_WORK << "Hair topology changed, removing motion attribute.";
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
hair->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
|
}
|
|
|
|
|
else if (motion_step > 0) {
|
|
|
|
|
/* Motion, fill up previous steps that we might have skipped because
|
|
|
|
|
* they had no motion, but we need them anyway now. */
|
|
|
|
|
for (int step = 0; step < motion_step; step++) {
|
|
|
|
|
float4 *mP = attr_mP->data_float4() + step * num_keys;
|
|
|
|
|
|
|
|
|
|
for (int key = 0; key < num_keys; key++) {
|
2024-12-19 09:41:55 +01:00
|
|
|
mP[key] = make_float4(hair->get_curve_keys()[key]);
|
2020-11-04 11:17:38 +01:00
|
|
|
mP[key].w = hair->get_curve_radius()[key];
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, const int motion_step)
|
2013-10-18 15:03:18 +00:00
|
|
|
{
|
2014-03-29 13:03:46 +01:00
|
|
|
/* find attribute */
|
2020-02-02 12:04:19 +01:00
|
|
|
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
2014-03-29 13:03:46 +01:00
|
|
|
bool new_attribute = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-03-29 13:03:46 +01:00
|
|
|
/* add new attribute if it doesn't exist already */
|
|
|
|
|
if (!attr_mP) {
|
2020-02-02 12:04:19 +01:00
|
|
|
attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
2014-03-29 13:03:46 +01:00
|
|
|
new_attribute = true;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-10-18 15:03:18 +00:00
|
|
|
/* export motion vectors for curve keys */
|
2024-12-29 17:32:00 +01:00
|
|
|
const size_t numkeys = hair->get_curve_keys().size();
|
2018-03-10 00:37:07 +01:00
|
|
|
float4 *mP = attr_mP->data_float4() + motion_step * numkeys;
|
2013-10-18 15:03:18 +00:00
|
|
|
bool have_motion = false;
|
2014-03-29 13:03:46 +01:00
|
|
|
int i = 0;
|
2018-11-23 13:21:55 +01:00
|
|
|
int num_curves = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-10-18 15:03:18 +00:00
|
|
|
for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
|
|
|
|
|
for (int curve = CData->psys_firstcurve[sys];
|
|
|
|
|
curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
|
|
|
|
|
curve++)
|
|
|
|
|
{
|
2018-11-23 13:21:55 +01:00
|
|
|
/* Curve lengths may not match! Curves can be clipped. */
|
2024-12-29 17:32:00 +01:00
|
|
|
const int curve_key_end = (num_curves + 1 < (int)hair->get_curve_first_key().size() ?
|
|
|
|
|
hair->get_curve_first_key()[num_curves + 1] :
|
|
|
|
|
(int)hair->get_curve_keys().size());
|
2020-11-04 11:17:38 +01:00
|
|
|
const int num_center_curve_keys = curve_key_end - hair->get_curve_first_key()[num_curves];
|
2018-12-20 16:45:38 +01:00
|
|
|
const int is_num_keys_different = CData->curve_keynum[curve] - num_center_curve_keys;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-20 16:45:38 +01:00
|
|
|
if (!is_num_keys_different) {
|
2018-11-23 13:21:55 +01:00
|
|
|
for (int curvekey = CData->curve_firstkey[curve];
|
|
|
|
|
curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
|
|
|
|
|
curvekey++)
|
|
|
|
|
{
|
2020-11-04 11:17:38 +01:00
|
|
|
if (i < hair->get_curve_keys().size()) {
|
2018-11-23 13:21:55 +01:00
|
|
|
mP[i] = CurveSegmentMotionCV(CData, sys, curve, curvekey);
|
|
|
|
|
if (!have_motion) {
|
|
|
|
|
/* unlike mesh coordinates, these tend to be slightly different
|
|
|
|
|
* between frames due to particle transforms into/out of object
|
|
|
|
|
* space, so we use an epsilon to detect actual changes */
|
2024-12-19 09:41:55 +01:00
|
|
|
float4 curve_key = make_float4(hair->get_curve_keys()[i]);
|
2020-11-04 11:17:38 +01:00
|
|
|
curve_key.w = hair->get_curve_radius()[i];
|
2023-09-17 09:01:48 +10:00
|
|
|
if (len_squared(mP[i] - curve_key) > 1e-5f * 1e-5f) {
|
2018-11-23 13:21:55 +01:00
|
|
|
have_motion = true;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2018-11-23 13:21:55 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-11-23 13:21:55 +01:00
|
|
|
i++;
|
2013-10-18 15:03:18 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-11-23 13:21:55 +01:00
|
|
|
else {
|
2019-04-29 14:14:14 +10:00
|
|
|
/* Number of keys has changed. Generate an interpolated version
|
2018-12-20 16:45:38 +01:00
|
|
|
* to preserve motion blur. */
|
2019-02-18 14:17:16 +01:00
|
|
|
const float step_size = num_center_curve_keys > 1 ? 1.0f / (num_center_curve_keys - 1) :
|
|
|
|
|
0.0f;
|
2018-12-20 16:45:38 +01:00
|
|
|
for (int step_index = 0; step_index < num_center_curve_keys; ++step_index) {
|
|
|
|
|
const float step = step_index * step_size;
|
2018-11-23 13:21:55 +01:00
|
|
|
mP[i] = LerpCurveSegmentMotionCV(CData, sys, curve, step);
|
2018-12-20 16:45:38 +01:00
|
|
|
i++;
|
2018-11-23 13:21:55 +01:00
|
|
|
}
|
|
|
|
|
have_motion = true;
|
|
|
|
|
}
|
|
|
|
|
num_curves++;
|
2013-10-18 15:03:18 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-17 16:30:48 +01:00
|
|
|
/* In case of new attribute, we verify if there really was any motion. */
|
2014-03-29 13:03:46 +01:00
|
|
|
if (new_attribute) {
|
2020-02-02 12:04:19 +01:00
|
|
|
export_hair_motion_validate_attribute(hair, motion_step, i, have_motion);
|
2014-03-29 13:03:46 +01:00
|
|
|
}
|
2013-10-18 15:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
2012-12-28 14:21:30 +00:00
|
|
|
/* Hair Curve Sync */
|
|
|
|
|
|
2020-02-02 13:09:18 +01:00
|
|
|
bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
|
|
|
|
|
{
|
|
|
|
|
/* Test if the object has a particle modifier with hair. */
|
2021-01-25 16:20:10 +01:00
|
|
|
for (BL::Modifier &b_mod : b_ob.modifiers) {
|
2024-12-26 17:53:59 +01:00
|
|
|
if ((b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) &&
|
2021-01-25 16:20:10 +01:00
|
|
|
(preview ? b_mod.show_viewport() : b_mod.show_render()))
|
|
|
|
|
{
|
|
|
|
|
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
|
2020-02-02 13:09:18 +01:00
|
|
|
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
|
|
|
|
|
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
|
|
|
|
|
|
|
|
|
|
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
|
|
|
|
|
(b_part.type() == BL::ParticleSettings::type_HAIR))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Old particle hair. */
|
|
|
|
|
void BlenderSync::sync_particle_hair(
|
2025-01-01 18:15:54 +01:00
|
|
|
Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, const int motion_step)
|
2012-12-28 14:21:30 +00:00
|
|
|
{
|
2021-09-06 18:22:24 +02:00
|
|
|
if (!b_ob_info.is_real_object_data()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
BL::Object b_ob = b_ob_info.real_object;
|
|
|
|
|
|
2020-02-02 12:04:19 +01:00
|
|
|
/* obtain general settings */
|
2024-12-26 17:53:59 +01:00
|
|
|
if (b_ob.mode() == BL::Object::mode_PARTICLE_EDIT || b_ob.mode() == BL::Object::mode_EDIT) {
|
2013-01-03 12:08:54 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-26 21:35:18 +10:00
|
|
|
/* Extract particle hair data - should be combined with connecting to mesh later. */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-01-03 12:08:54 +00:00
|
|
|
ParticleCurveData CData;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
ObtainCacheParticleData(hair, &b_mesh, &b_ob, &CData, !preview);
|
|
|
|
|
|
|
|
|
|
/* add hair geometry */
|
2023-09-17 09:01:48 +10:00
|
|
|
if (motion) {
|
2020-06-04 15:12:31 +02:00
|
|
|
ExportCurveSegmentsMotion(hair, &CData, motion_step);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2020-06-04 15:12:31 +02:00
|
|
|
ExportCurveSegments(scene, hair, &CData);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-02-02 01:49:57 +00:00
|
|
|
/* generated coordinates from first key. we should ideally get this from
|
|
|
|
|
* blender to handle deforming objects */
|
2013-10-18 15:03:18 +00:00
|
|
|
if (!motion) {
|
2020-06-04 15:12:31 +02:00
|
|
|
if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
|
2024-12-29 17:32:00 +01:00
|
|
|
float3 loc;
|
|
|
|
|
float3 size;
|
2023-09-18 02:50:09 +02:00
|
|
|
mesh_texture_space(*static_cast<const ::Mesh *>(b_mesh.ptr.data), loc, size);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
|
|
|
|
|
float3 *generated = attr_generated->data_float3();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
for (size_t i = 0; i < hair->num_curves(); i++) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const float3 co = hair->get_curve_keys()[hair->get_curve(i).first_key];
|
2020-06-04 15:12:31 +02:00
|
|
|
generated[i] = co * size - loc;
|
2013-01-23 17:15:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-02-02 01:49:57 +00:00
|
|
|
/* create vertex color attributes */
|
2013-10-18 15:03:18 +00:00
|
|
|
if (!motion) {
|
2018-09-06 14:28:14 +02:00
|
|
|
BL::Mesh::vertex_colors_iterator l;
|
2013-02-02 01:49:57 +00:00
|
|
|
int vcol_num = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-09-06 14:28:14 +02:00
|
|
|
for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l, vcol_num++) {
|
2023-09-17 09:01:48 +10:00
|
|
|
if (!hair->need_attribute(scene, ustring(l->name().c_str()))) {
|
2013-02-02 01:49:57 +00:00
|
|
|
continue;
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
ObtainCacheParticleVcol(hair, &b_mesh, &b_ob, &CData, !preview, vcol_num);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
Attribute *attr_vcol = hair->attributes.add(
|
2020-06-30 14:07:49 +02:00
|
|
|
ustring(l->name().c_str()), TypeRGBA, ATTR_ELEMENT_CURVE);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-30 14:07:49 +02:00
|
|
|
float4 *fdata = attr_vcol->data_float4();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
if (fdata) {
|
|
|
|
|
size_t i = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
/* Encode vertex color using the sRGB curve. */
|
|
|
|
|
for (size_t curve = 0; curve < CData.curve_vcol.size(); curve++) {
|
2020-06-30 14:07:49 +02:00
|
|
|
fdata[i++] = color_srgb_to_linear_v4(CData.curve_vcol[curve]);
|
2019-02-20 14:47:46 +01:00
|
|
|
}
|
2013-01-23 17:15:45 +00:00
|
|
|
}
|
2013-01-03 12:31:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-02-02 01:49:57 +00:00
|
|
|
/* create UV attributes */
|
2013-10-18 15:03:18 +00:00
|
|
|
if (!motion) {
|
2018-09-06 14:28:14 +02:00
|
|
|
BL::Mesh::uv_layers_iterator l;
|
2013-02-02 01:49:57 +00:00
|
|
|
int uv_num = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-09-06 14:28:14 +02:00
|
|
|
for (b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l, uv_num++) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const bool active_render = l->active_render();
|
|
|
|
|
const AttributeStandard std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
|
|
|
|
|
const ustring name = ustring(l->name().c_str());
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-02-02 01:49:57 +00:00
|
|
|
/* UV map */
|
2020-06-04 15:12:31 +02:00
|
|
|
if (hair->need_attribute(scene, name) || hair->need_attribute(scene, std)) {
|
2013-02-02 01:49:57 +00:00
|
|
|
Attribute *attr_uv;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
ObtainCacheParticleUV(hair, &b_mesh, &b_ob, &CData, !preview, uv_num);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-17 09:01:48 +10:00
|
|
|
if (active_render) {
|
2020-06-04 15:12:31 +02:00
|
|
|
attr_uv = hair->attributes.add(std, name);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2020-06-04 15:12:31 +02:00
|
|
|
attr_uv = hair->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CURVE);
|
2023-09-17 09:01:48 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
float2 *uv = attr_uv->data_float2();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
if (uv) {
|
|
|
|
|
size_t i = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
for (size_t curve = 0; curve < CData.curve_uv.size(); curve++) {
|
|
|
|
|
uv[i++] = CData.curve_uv[curve];
|
2013-01-23 17:15:45 +00:00
|
|
|
}
|
2013-02-02 01:49:57 +00:00
|
|
|
}
|
2013-01-04 12:44:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
2020-02-02 13:09:18 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-11 08:33:46 +01:00
|
|
|
template<typename TypeInCycles, typename GetValueAtIndex>
|
2023-04-07 16:05:23 -04:00
|
|
|
static void fill_generic_attribute(const int num_curves,
|
|
|
|
|
const int num_points,
|
2022-02-11 08:33:46 +01:00
|
|
|
TypeInCycles *data,
|
|
|
|
|
const AttributeElement element,
|
|
|
|
|
const GetValueAtIndex &get_value_at_index)
|
|
|
|
|
{
|
|
|
|
|
switch (element) {
|
|
|
|
|
case ATTR_ELEMENT_CURVE_KEY: {
|
|
|
|
|
for (int i = 0; i < num_points; i++) {
|
|
|
|
|
data[i] = get_value_at_index(i);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ATTR_ELEMENT_CURVE: {
|
2023-04-07 16:05:23 -04:00
|
|
|
for (int i = 0; i < num_curves; i++) {
|
2022-02-11 08:33:46 +01:00
|
|
|
data[i] = get_value_at_index(i);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
assert(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-10 18:45:15 +01:00
|
|
|
static void attr_create_motion_from_velocity(Hair *hair,
|
|
|
|
|
const blender::Span<blender::float3> src,
|
|
|
|
|
const float motion_scale)
|
2022-02-11 08:33:46 +01:00
|
|
|
{
|
|
|
|
|
const int num_curve_keys = hair->get_curve_keys().size();
|
|
|
|
|
|
2024-01-10 18:45:15 +01:00
|
|
|
/* Override motion steps to fixed number. */
|
|
|
|
|
hair->set_motion_steps(3);
|
|
|
|
|
|
2022-02-11 08:33:46 +01:00
|
|
|
/* Find or add attribute */
|
2024-12-26 17:53:59 +01:00
|
|
|
float3 *P = hair->get_curve_keys().data();
|
2022-02-11 08:33:46 +01:00
|
|
|
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
|
|
|
|
|
|
if (!attr_mP) {
|
|
|
|
|
attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only export previous and next frame, we don't have any in between data. */
|
2024-12-29 17:32:00 +01:00
|
|
|
const float motion_times[2] = {-1.0f, 1.0f};
|
2022-02-11 08:33:46 +01:00
|
|
|
for (int step = 0; step < 2; step++) {
|
|
|
|
|
const float relative_time = motion_times[step] * 0.5f * motion_scale;
|
|
|
|
|
float3 *mP = attr_mP->data_float3() + step * num_curve_keys;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < num_curve_keys; i++) {
|
2023-04-07 16:05:23 -04:00
|
|
|
mP[i] = P[i] + make_float3(src[i][0], src[i][1], src[i][2]) * relative_time;
|
2022-02-11 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void attr_create_generic(Scene *scene,
|
|
|
|
|
Hair *hair,
|
2023-09-18 02:50:09 +02:00
|
|
|
const blender::bke::CurvesGeometry &b_curves,
|
2022-02-11 08:33:46 +01:00
|
|
|
const bool need_motion,
|
|
|
|
|
const float motion_scale)
|
|
|
|
|
{
|
2023-09-18 02:50:09 +02:00
|
|
|
const blender::bke::AttributeAccessor b_attributes = b_curves.attributes();
|
2023-04-07 16:05:23 -04:00
|
|
|
|
2022-02-11 08:33:46 +01:00
|
|
|
AttributeSet &attributes = hair->attributes;
|
|
|
|
|
static const ustring u_velocity("velocity");
|
2022-08-05 19:30:31 +02:00
|
|
|
const bool need_uv = hair->need_attribute(scene, ATTR_STD_UV);
|
|
|
|
|
bool have_uv = false;
|
2022-02-11 08:33:46 +01:00
|
|
|
|
2024-09-26 12:59:00 +02:00
|
|
|
b_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
|
|
|
|
|
const ustring name{std::string_view(iter.name)};
|
2022-02-11 08:33:46 +01:00
|
|
|
|
2024-09-26 12:59:00 +02:00
|
|
|
const blender::bke::AttrDomain b_domain = iter.domain;
|
|
|
|
|
const eCustomDataType b_data_type = iter.data_type;
|
2022-08-05 19:30:31 +02:00
|
|
|
|
2022-02-11 08:33:46 +01:00
|
|
|
if (need_motion && name == u_velocity) {
|
2024-09-26 12:59:00 +02:00
|
|
|
const blender::VArraySpan b_attr = *iter.get<blender::float3>(
|
|
|
|
|
blender::bke::AttrDomain::Point);
|
2024-01-10 18:45:15 +01:00
|
|
|
attr_create_motion_from_velocity(hair, b_attr, motion_scale);
|
2024-09-26 12:59:00 +02:00
|
|
|
return;
|
2022-08-05 19:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Weak, use first float2 attribute as standard UV. */
|
2023-12-20 13:13:16 -05:00
|
|
|
if (need_uv && !have_uv && b_data_type == CD_PROP_FLOAT2 &&
|
|
|
|
|
b_domain == blender::bke::AttrDomain::Curve)
|
|
|
|
|
{
|
2023-09-18 02:50:09 +02:00
|
|
|
Attribute *attr = attributes.add(ATTR_STD_UV, name);
|
|
|
|
|
|
2024-09-26 12:59:00 +02:00
|
|
|
const blender::VArraySpan b_attr = *iter.get<blender::float2>();
|
2023-09-18 02:50:09 +02:00
|
|
|
|
|
|
|
|
static_assert(sizeof(blender::float2) == sizeof(float2));
|
|
|
|
|
const blender::Span src = b_attr.cast<float2>();
|
|
|
|
|
std::copy(src.begin(), src.end(), attr->data_float2());
|
2022-08-05 19:30:31 +02:00
|
|
|
have_uv = true;
|
2024-09-26 12:59:00 +02:00
|
|
|
return;
|
2022-02-11 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hair->need_attribute(scene, name)) {
|
2024-09-26 12:59:00 +02:00
|
|
|
return;
|
2022-02-11 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
if (attributes.find(name)) {
|
2024-09-26 12:59:00 +02:00
|
|
|
return;
|
2022-02-11 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
2024-09-26 12:59:00 +02:00
|
|
|
const blender::bke::GAttributeReader b_attr = iter.get();
|
2023-09-18 02:50:09 +02:00
|
|
|
|
2022-02-11 08:33:46 +01:00
|
|
|
AttributeElement element = ATTR_ELEMENT_NONE;
|
2023-09-18 02:50:09 +02:00
|
|
|
switch (b_attr.domain) {
|
2023-12-20 13:13:16 -05:00
|
|
|
case blender::bke::AttrDomain::Point:
|
2022-02-11 08:33:46 +01:00
|
|
|
element = ATTR_ELEMENT_CURVE_KEY;
|
|
|
|
|
break;
|
2023-12-20 13:13:16 -05:00
|
|
|
case blender::bke::AttrDomain::Curve:
|
2022-02-11 08:33:46 +01:00
|
|
|
element = ATTR_ELEMENT_CURVE;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2024-09-26 12:59:00 +02:00
|
|
|
return;
|
2022-02-11 08:33:46 +01:00
|
|
|
}
|
2023-09-18 02:50:09 +02:00
|
|
|
|
|
|
|
|
blender::bke::attribute_math::convert_to_static_type(b_attr.varray.type(), [&](auto dummy) {
|
|
|
|
|
using BlenderT = decltype(dummy);
|
|
|
|
|
using Converter = typename ccl::AttributeConverter<BlenderT>;
|
|
|
|
|
using CyclesT = typename Converter::CyclesT;
|
|
|
|
|
if constexpr (!std::is_void_v<CyclesT>) {
|
|
|
|
|
Attribute *attr = attributes.add(name, Converter::type_desc, element);
|
|
|
|
|
CyclesT *data = reinterpret_cast<CyclesT *>(attr->data());
|
|
|
|
|
|
|
|
|
|
const blender::VArraySpan src = b_attr.varray.typed<BlenderT>();
|
|
|
|
|
for (const int i : src.index_range()) {
|
|
|
|
|
data[i] = Converter::convert(src[i]);
|
|
|
|
|
}
|
2022-02-11 08:33:46 +01:00
|
|
|
}
|
2023-09-18 02:50:09 +02:00
|
|
|
});
|
|
|
|
|
});
|
2022-02-11 08:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
static float4 curve_point_as_float4(const blender::Span<blender::float3> b_positions,
|
|
|
|
|
const blender::Span<float> b_radius,
|
2023-04-07 16:33:11 -04:00
|
|
|
const int index)
|
2022-02-03 10:49:51 -06:00
|
|
|
{
|
2023-04-07 16:05:23 -04:00
|
|
|
float4 mP = make_float4(
|
2023-09-18 02:50:09 +02:00
|
|
|
b_positions[index][0], b_positions[index][1], b_positions[index][2], 0.0f);
|
|
|
|
|
mP.w = b_radius.is_empty() ? 0.005f : b_radius[index];
|
2020-03-17 16:30:48 +01:00
|
|
|
return mP;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
static float4 interpolate_curve_points(const blender::Span<blender::float3> b_positions,
|
|
|
|
|
const blender::Span<float> b_radius,
|
2023-04-07 16:33:11 -04:00
|
|
|
const int first_point_index,
|
|
|
|
|
const int num_points,
|
|
|
|
|
const float step)
|
2020-03-17 16:30:48 +01:00
|
|
|
{
|
|
|
|
|
const float curve_t = step * (num_points - 1);
|
|
|
|
|
const int point_a = clamp((int)curve_t, 0, num_points - 1);
|
|
|
|
|
const int point_b = min(point_a + 1, num_points - 1);
|
|
|
|
|
const float t = curve_t - (float)point_a;
|
2023-09-18 02:50:09 +02:00
|
|
|
return mix(curve_point_as_float4(b_positions, b_radius, first_point_index + point_a),
|
|
|
|
|
curve_point_as_float4(b_positions, b_radius, first_point_index + point_b),
|
2023-05-12 19:55:46 +02:00
|
|
|
t);
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-11 08:33:46 +01:00
|
|
|
static void export_hair_curves(Scene *scene,
|
|
|
|
|
Hair *hair,
|
2023-09-18 02:50:09 +02:00
|
|
|
const blender::bke::CurvesGeometry &b_curves,
|
2022-02-11 08:33:46 +01:00
|
|
|
const bool need_motion,
|
|
|
|
|
const float motion_scale)
|
2020-03-17 16:30:48 +01:00
|
|
|
{
|
2023-09-18 02:50:09 +02:00
|
|
|
const blender::Span<blender::float3> positions = b_curves.positions();
|
|
|
|
|
const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
|
2022-08-05 19:30:31 +02:00
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
hair->resize_curves(points_by_curve.size(), positions.size());
|
2022-08-05 19:30:31 +02:00
|
|
|
|
|
|
|
|
float3 *curve_keys = hair->get_curve_keys().data();
|
|
|
|
|
float *curve_radius = hair->get_curve_radius().data();
|
|
|
|
|
int *curve_first_key = hair->get_curve_first_key().data();
|
|
|
|
|
int *curve_shader = hair->get_curve_shader().data();
|
|
|
|
|
|
2020-03-17 16:30:48 +01:00
|
|
|
/* Add requested attributes. */
|
2024-12-26 17:53:55 +01:00
|
|
|
float *attr_intercept = nullptr;
|
|
|
|
|
float *attr_length = nullptr;
|
2020-03-17 16:30:48 +01:00
|
|
|
|
Cycles: new Principled Hair BSDF variant with elliptical cross-section support
Implements the paper [A Microfacet-based Hair Scattering
Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by
Weizhen Huang, Matthias B. Hullin and Johannes Hanika.
### Features:
- This is a far-field model, as opposed to the previous near-field
Principled Hair BSDF model. The hair is expected to be less noisy, but
lower roughness values takes longer to render due to numerical
integration along the hair width. The hair also appears to be flat when
viewed up-close.
- The longitudinal width of the scattering lobe differs along the
azimuth, providing a higher contrast compared to the evenly spread
scattering in the near-field Principled Hair BSDF model. For a more
detailed comparison, please refer to the original paper.
- Supports elliptical cross-sections, adding more realism as human hairs
are usually elliptical. The orientation of the cross-section is aligned
with the curve normal, which can be adjusted using geometry nodes.
Default is minimal twist. During sampling, light rays that hit outside
the hair width will continue propogating as if the material is
transparent.
- There is non-physical modulation factors for the first three
lobes (Reflection, Transmission, Secondary Reflection).
### Missing:
- A good default for cross-section orientation. There was an
attempt (9039f76928) to default the orientation to align with the curve
normal in the mathematical sense, but the stability (when animated) is
unclear and it would be a hassle to generalise to all curve types. After
the model is in main, we could experiment with the geometry nodes team
to see what works the best as a default.
Co-authored-by: Lukas Stockner <lukas.stockner@freenet.de>
Pull Request: https://projects.blender.org/blender/blender/pulls/105600
2023-08-18 12:46:13 +02:00
|
|
|
if (hair->need_attribute(scene, ATTR_STD_VERTEX_NORMAL)) {
|
|
|
|
|
/* Get geometry normals. */
|
|
|
|
|
float3 *attr_normal = hair->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3();
|
2023-09-18 02:50:09 +02:00
|
|
|
vector<blender::float3> point_normals(positions.size());
|
|
|
|
|
blender::bke::curves_normals_point_domain_calc(
|
|
|
|
|
b_curves, {point_normals.data(), int64_t(point_normals.size())});
|
|
|
|
|
for (const int i : positions.index_range()) {
|
|
|
|
|
attr_normal[i] = make_float3(point_normals[i][0], point_normals[i][1], point_normals[i][2]);
|
Cycles: new Principled Hair BSDF variant with elliptical cross-section support
Implements the paper [A Microfacet-based Hair Scattering
Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by
Weizhen Huang, Matthias B. Hullin and Johannes Hanika.
### Features:
- This is a far-field model, as opposed to the previous near-field
Principled Hair BSDF model. The hair is expected to be less noisy, but
lower roughness values takes longer to render due to numerical
integration along the hair width. The hair also appears to be flat when
viewed up-close.
- The longitudinal width of the scattering lobe differs along the
azimuth, providing a higher contrast compared to the evenly spread
scattering in the near-field Principled Hair BSDF model. For a more
detailed comparison, please refer to the original paper.
- Supports elliptical cross-sections, adding more realism as human hairs
are usually elliptical. The orientation of the cross-section is aligned
with the curve normal, which can be adjusted using geometry nodes.
Default is minimal twist. During sampling, light rays that hit outside
the hair width will continue propogating as if the material is
transparent.
- There is non-physical modulation factors for the first three
lobes (Reflection, Transmission, Secondary Reflection).
### Missing:
- A good default for cross-section orientation. There was an
attempt (9039f76928) to default the orientation to align with the curve
normal in the mathematical sense, but the stability (when animated) is
unclear and it would be a hassle to generalise to all curve types. After
the model is in main, we could experiment with the geometry nodes team
to see what works the best as a default.
Co-authored-by: Lukas Stockner <lukas.stockner@freenet.de>
Pull Request: https://projects.blender.org/blender/blender/pulls/105600
2023-08-18 12:46:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-18 02:50:09 +02:00
|
|
|
|
2020-03-17 16:30:48 +01:00
|
|
|
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
|
2022-08-05 19:30:31 +02:00
|
|
|
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT)->data_float();
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
2021-09-24 10:45:36 +02:00
|
|
|
if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) {
|
2022-08-05 19:30:31 +02:00
|
|
|
attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH)->data_float();
|
2021-09-24 07:42:36 +02:00
|
|
|
}
|
2020-03-17 16:30:48 +01:00
|
|
|
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
|
2023-04-07 16:05:23 -04:00
|
|
|
float *attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM)->data_float();
|
2023-09-18 02:50:09 +02:00
|
|
|
for (const int i : points_by_curve.index_range()) {
|
2023-04-07 16:05:23 -04:00
|
|
|
attr_random[i] = hash_uint2_to_float(i, 0);
|
|
|
|
|
}
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
|
2023-12-20 13:13:16 -05:00
|
|
|
const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>(
|
|
|
|
|
"radius", blender::bke::AttrDomain::Point);
|
2023-04-07 16:05:23 -04:00
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
std::copy(points_by_curve.data().data(),
|
|
|
|
|
points_by_curve.data().data() + points_by_curve.size(),
|
|
|
|
|
curve_first_key);
|
|
|
|
|
std::fill(curve_shader, curve_shader + points_by_curve.size(), 0);
|
|
|
|
|
if (!b_radius.is_empty()) {
|
|
|
|
|
std::copy(b_radius.data(), b_radius.data() + positions.size(), curve_radius);
|
2023-04-07 16:05:23 -04:00
|
|
|
}
|
|
|
|
|
else {
|
2023-09-18 02:50:09 +02:00
|
|
|
std::fill(curve_radius, curve_radius + positions.size(), 0.005f);
|
2023-04-07 16:05:23 -04:00
|
|
|
}
|
2022-02-03 10:49:51 -06:00
|
|
|
|
2020-03-17 16:30:48 +01:00
|
|
|
/* Export curves and points. */
|
2023-09-18 02:50:09 +02:00
|
|
|
for (const int curve : points_by_curve.index_range()) {
|
|
|
|
|
const blender::IndexRange points = points_by_curve[curve];
|
2020-03-17 16:30:48 +01:00
|
|
|
|
2021-02-17 01:47:18 +01:00
|
|
|
float3 prev_co = zero_float3();
|
2020-03-17 16:30:48 +01:00
|
|
|
float length = 0.0f;
|
|
|
|
|
|
|
|
|
|
/* Position and radius. */
|
2023-09-18 02:50:09 +02:00
|
|
|
for (const int point : points) {
|
|
|
|
|
const float3 co = make_float3(positions[point][0], positions[point][1], positions[point][2]);
|
2022-08-05 19:30:31 +02:00
|
|
|
|
2023-04-07 16:05:23 -04:00
|
|
|
curve_keys[point] = co;
|
2022-08-05 19:30:31 +02:00
|
|
|
|
|
|
|
|
if (attr_length || attr_intercept) {
|
2023-09-18 02:50:09 +02:00
|
|
|
if (point != points.first()) {
|
2020-03-17 16:30:48 +01:00
|
|
|
length += len(co - prev_co);
|
|
|
|
|
}
|
|
|
|
|
prev_co = co;
|
2022-08-05 19:30:31 +02:00
|
|
|
|
|
|
|
|
if (attr_intercept) {
|
2023-04-07 16:05:23 -04:00
|
|
|
attr_intercept[point] = length;
|
2022-08-05 19:30:31 +02:00
|
|
|
}
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Normalized 0..1 attribute along curve. */
|
2022-08-05 19:30:31 +02:00
|
|
|
if (attr_intercept && length > 0.0f) {
|
2023-09-18 02:50:09 +02:00
|
|
|
for (const int point : points.drop_front(1)) {
|
2023-04-07 16:05:23 -04:00
|
|
|
attr_intercept[point] /= length;
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-05 19:30:31 +02:00
|
|
|
/* Curve length. */
|
2021-09-24 10:45:36 +02:00
|
|
|
if (attr_length) {
|
2023-09-18 02:50:09 +02:00
|
|
|
attr_length[curve] = length;
|
2021-09-24 07:42:36 +02:00
|
|
|
}
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
2022-02-11 08:33:46 +01:00
|
|
|
|
|
|
|
|
attr_create_generic(scene, hair, b_curves, need_motion, motion_scale);
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
static void export_hair_curves_motion(Hair *hair,
|
|
|
|
|
const blender::bke::CurvesGeometry &b_curves,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int motion_step)
|
2020-03-17 16:30:48 +01:00
|
|
|
{
|
|
|
|
|
/* Find or add attribute. */
|
|
|
|
|
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
|
bool new_attribute = false;
|
|
|
|
|
|
|
|
|
|
if (!attr_mP) {
|
|
|
|
|
attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
|
new_attribute = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Export motion keys. */
|
2024-02-29 13:49:51 +01:00
|
|
|
const size_t num_keys = hair->num_keys();
|
|
|
|
|
const size_t num_curves = hair->num_curves();
|
2020-03-17 16:30:48 +01:00
|
|
|
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
|
|
|
|
|
bool have_motion = false;
|
|
|
|
|
int num_motion_keys = 0;
|
|
|
|
|
int curve_index = 0;
|
|
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
const blender::Span<blender::float3> b_positions = b_curves.positions();
|
|
|
|
|
const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
|
2023-12-20 13:13:16 -05:00
|
|
|
const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>(
|
|
|
|
|
"radius", blender::bke::AttrDomain::Point);
|
2022-02-03 10:49:51 -06:00
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
for (const int i : points_by_curve.index_range()) {
|
|
|
|
|
const blender::IndexRange points = points_by_curve[i];
|
2024-02-29 13:49:51 +01:00
|
|
|
if (curve_index >= num_curves) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-03-17 16:30:48 +01:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const Hair::Curve curve = hair->get_curve(curve_index);
|
2020-03-17 16:30:48 +01:00
|
|
|
curve_index++;
|
|
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
if (points.size() == curve.num_keys) {
|
2020-03-17 16:30:48 +01:00
|
|
|
/* Number of keys matches. */
|
2023-09-18 02:50:09 +02:00
|
|
|
for (const int i : points.index_range()) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const int point = points[i];
|
2020-03-17 16:30:48 +01:00
|
|
|
|
2023-04-07 16:05:23 -04:00
|
|
|
if (point < num_keys) {
|
2023-09-18 02:50:09 +02:00
|
|
|
mP[num_motion_keys] = curve_point_as_float4(b_positions, b_radius, point);
|
2020-03-17 16:30:48 +01:00
|
|
|
num_motion_keys++;
|
|
|
|
|
|
|
|
|
|
if (!have_motion) {
|
|
|
|
|
/* TODO: use epsilon for comparison? Was needed for particles due to
|
|
|
|
|
* transform, but ideally should not happen anymore. */
|
2024-12-19 09:41:55 +01:00
|
|
|
float4 curve_key = make_float4(hair->get_curve_keys()[i]);
|
2020-11-04 11:17:38 +01:00
|
|
|
curve_key.w = hair->get_curve_radius()[i];
|
2020-03-17 16:30:48 +01:00
|
|
|
have_motion = !(mP[i] == curve_key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Number of keys has changed. Generate an interpolated version
|
|
|
|
|
* to preserve motion blur. */
|
|
|
|
|
const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f;
|
|
|
|
|
for (int i = 0; i < curve.num_keys; i++) {
|
|
|
|
|
const float step = i * step_size;
|
2023-04-07 16:33:11 -04:00
|
|
|
mP[num_motion_keys] = interpolate_curve_points(
|
2023-09-18 02:50:09 +02:00
|
|
|
b_positions, b_radius, points.start(), points.size(), step);
|
2020-03-17 16:30:48 +01:00
|
|
|
num_motion_keys++;
|
|
|
|
|
}
|
|
|
|
|
have_motion = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In case of new attribute, we verify if there really was any motion. */
|
|
|
|
|
if (new_attribute) {
|
|
|
|
|
export_hair_motion_validate_attribute(hair, motion_step, num_motion_keys, have_motion);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hair object. */
|
2025-01-01 18:15:54 +01:00
|
|
|
void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, const int motion_step)
|
2020-03-17 16:30:48 +01:00
|
|
|
{
|
2022-02-11 08:33:46 +01:00
|
|
|
/* Motion blur attribute is relative to seconds, we need it relative to frames. */
|
|
|
|
|
const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
|
|
|
|
|
const float motion_scale = (need_motion) ?
|
|
|
|
|
scene->motion_shutter_time() /
|
|
|
|
|
(b_scene.render().fps() / b_scene.render().fps_base()) :
|
|
|
|
|
0.0f;
|
|
|
|
|
|
2020-03-17 16:30:48 +01:00
|
|
|
/* Convert Blender hair to Cycles curves. */
|
2023-09-18 02:50:09 +02:00
|
|
|
const blender::bke::CurvesGeometry &b_curves(
|
|
|
|
|
static_cast<const ::Curves *>(b_ob_info.object_data.ptr.data)->geometry.wrap());
|
2020-03-17 16:30:48 +01:00
|
|
|
if (motion) {
|
Curves: Rename "Hair" types, variables, and functions to "Curves"
Based on discussions from T95355 and T94193, the plan is to use
the name "Curves" to describe the data-block container for multiple
curves. Eventually this will replace the existing "Curve" data-block.
However, it will be a while before the curve data-block can be replaced
so in order to distinguish the two curve types in the UI, "Hair Curves"
will be used, but eventually changed back to "Curves".
This patch renames "hair-related" files, functions, types, and variable
names to this convention. A deep rename is preferred to keep code
consistent and to avoid any "hair" terminology from leaking, since the
new data-block is meant for all curve types, not just hair use cases.
The downside of this naming is that the difference between "Curve"
and "Curves" has become important. That was considered during
design discussons and deemed acceptable, especially given the
non-permanent nature of the somewhat common conflict.
Some points of interest:
- All DNA compatibility is lost, just like rBf59767ff9729.
- I renamed `ID_HA` to `ID_CV` so there is no complete mismatch.
- `hair_curves` is used where necessary to distinguish from the
existing "curves" plural.
- I didn't rename any of the cycles/rendering code function names,
since that is also used by the old hair particle system.
Differential Revision: https://developer.blender.org/D14007
2022-02-07 11:55:54 -06:00
|
|
|
export_hair_curves_motion(hair, b_curves, motion_step);
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2022-02-11 08:33:46 +01:00
|
|
|
export_hair_curves(scene, hair, b_curves, need_motion, motion_scale);
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-06 18:22:24 +02:00
|
|
|
void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Hair *hair)
|
2020-02-02 13:09:18 +01:00
|
|
|
{
|
2020-11-04 13:57:28 +01:00
|
|
|
/* make a copy of the shaders as the caller in the main thread still need them for syncing the
|
|
|
|
|
* attributes */
|
|
|
|
|
array<Node *> used_shaders = hair->get_used_shaders();
|
|
|
|
|
|
2020-11-04 11:17:38 +01:00
|
|
|
Hair new_hair;
|
2020-11-04 13:57:28 +01:00
|
|
|
new_hair.set_used_shaders(used_shaders);
|
2020-02-25 11:52:32 +01:00
|
|
|
|
2020-06-10 19:07:07 +02:00
|
|
|
if (view_layer.use_hair) {
|
Curves: Rename "Hair" types, variables, and functions to "Curves"
Based on discussions from T95355 and T94193, the plan is to use
the name "Curves" to describe the data-block container for multiple
curves. Eventually this will replace the existing "Curve" data-block.
However, it will be a while before the curve data-block can be replaced
so in order to distinguish the two curve types in the UI, "Hair Curves"
will be used, but eventually changed back to "Curves".
This patch renames "hair-related" files, functions, types, and variable
names to this convention. A deep rename is preferred to keep code
consistent and to avoid any "hair" terminology from leaking, since the
new data-block is meant for all curve types, not just hair use cases.
The downside of this naming is that the difference between "Curve"
and "Curves" has become important. That was considered during
design discussons and deemed acceptable, especially given the
non-permanent nature of the somewhat common conflict.
Some points of interest:
- All DNA compatibility is lost, just like rBf59767ff9729.
- I renamed `ID_HA` to `ID_CV` so there is no complete mismatch.
- `hair_curves` is used where necessary to distinguish from the
existing "curves" plural.
- I didn't rename any of the cycles/rendering code function names,
since that is also used by the old hair particle system.
Differential Revision: https://developer.blender.org/D14007
2022-02-07 11:55:54 -06:00
|
|
|
if (b_ob_info.object_data.is_a(&RNA_Curves)) {
|
2020-03-17 16:30:48 +01:00
|
|
|
/* Hair object. */
|
2021-09-06 18:22:24 +02:00
|
|
|
sync_hair(&new_hair, b_ob_info, false);
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
2022-07-19 12:10:30 -05:00
|
|
|
else {
|
2020-03-17 16:30:48 +01:00
|
|
|
/* Particle hair. */
|
2024-12-29 17:32:00 +01:00
|
|
|
const bool need_undeformed = new_hair.need_attribute(scene, ATTR_STD_GENERATED);
|
2020-03-17 16:30:48 +01:00
|
|
|
BL::Mesh b_mesh = object_to_mesh(
|
2021-09-06 18:22:24 +02:00
|
|
|
b_data, b_ob_info, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE);
|
2020-03-17 16:30:48 +01:00
|
|
|
|
|
|
|
|
if (b_mesh) {
|
2021-09-06 18:22:24 +02:00
|
|
|
sync_particle_hair(&new_hair, b_mesh, b_ob_info, false);
|
|
|
|
|
free_object_to_mesh(b_data, b_ob_info, b_mesh);
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
2020-02-02 13:09:18 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-04 11:17:38 +01:00
|
|
|
/* update original sockets */
|
|
|
|
|
|
|
|
|
|
for (const SocketType &socket : new_hair.type->inputs) {
|
|
|
|
|
/* Those sockets are updated in sync_object, so do not modify them. */
|
2025-01-29 01:59:38 +01:00
|
|
|
if (socket.name == "use_motion_blur" || socket.name == "used_shaders") {
|
2020-11-04 11:17:38 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
hair->set_value(socket, new_hair, socket);
|
|
|
|
|
}
|
|
|
|
|
|
Cycles: optimize device updates
This optimizes device updates (during user edits or frame changes in
the viewport) by avoiding unnecessary computations. To achieve this,
we use a combination of the sockets' update flags as well as some new
flags passed to the various managers when tagging for an update to tell
exactly what the tagging is for (e.g. shader was modified, object was
removed, etc.).
Besides avoiding recomputations, we also avoid resending to the devices
unmodified data arrays, thus reducing bandwidth usage. For OptiX and
Embree, BVH packing was also multithreaded.
The performance improvements may vary depending on the used device (CPU
or GPU), and the content of the scene. Simple scenes (e.g. with no adaptive
subdivision or volumes) rendered using OptiX will benefit from this work
the most.
On average, for a variety of animated scenes, this gives a 3x speedup.
Reviewed By: #cycles, brecht
Maniphest Tasks: T79174
Differential Revision: https://developer.blender.org/D9555
2021-01-22 15:01:26 +01:00
|
|
|
hair->attributes.update(std::move(new_hair.attributes));
|
2020-11-04 11:17:38 +01:00
|
|
|
|
2020-02-02 13:09:18 +01:00
|
|
|
/* tag update */
|
2020-11-04 11:17:38 +01:00
|
|
|
|
|
|
|
|
/* Compares curve_keys rather than strands in order to handle quick hair
|
|
|
|
|
* adjustments in dynamic BVH - other methods could probably do this better. */
|
|
|
|
|
const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
|
2020-02-02 12:04:19 +01:00
|
|
|
|
2020-06-04 15:12:31 +02:00
|
|
|
hair->tag_update(scene, rebuild);
|
2020-02-02 13:09:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
|
2021-09-06 18:22:24 +02:00
|
|
|
BObjectInfo &b_ob_info,
|
2020-06-04 15:12:31 +02:00
|
|
|
Hair *hair,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int motion_step)
|
2020-02-02 13:09:18 +01:00
|
|
|
{
|
2020-02-02 12:04:19 +01:00
|
|
|
/* Skip if nothing exported. */
|
2020-06-04 15:12:31 +02:00
|
|
|
if (hair->num_keys() == 0) {
|
2020-02-02 13:09:18 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Export deformed coordinates. */
|
2021-09-06 18:22:24 +02:00
|
|
|
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
|
Curves: Rename "Hair" types, variables, and functions to "Curves"
Based on discussions from T95355 and T94193, the plan is to use
the name "Curves" to describe the data-block container for multiple
curves. Eventually this will replace the existing "Curve" data-block.
However, it will be a while before the curve data-block can be replaced
so in order to distinguish the two curve types in the UI, "Hair Curves"
will be used, but eventually changed back to "Curves".
This patch renames "hair-related" files, functions, types, and variable
names to this convention. A deep rename is preferred to keep code
consistent and to avoid any "hair" terminology from leaking, since the
new data-block is meant for all curve types, not just hair use cases.
The downside of this naming is that the difference between "Curve"
and "Curves" has become important. That was considered during
design discussons and deemed acceptable, especially given the
non-permanent nature of the somewhat common conflict.
Some points of interest:
- All DNA compatibility is lost, just like rBf59767ff9729.
- I renamed `ID_HA` to `ID_CV` so there is no complete mismatch.
- `hair_curves` is used where necessary to distinguish from the
existing "curves" plural.
- I didn't rename any of the cycles/rendering code function names,
since that is also used by the old hair particle system.
Differential Revision: https://developer.blender.org/D14007
2022-02-07 11:55:54 -06:00
|
|
|
if (b_ob_info.object_data.is_a(&RNA_Curves)) {
|
2020-03-17 16:30:48 +01:00
|
|
|
/* Hair object. */
|
2021-09-06 18:22:24 +02:00
|
|
|
sync_hair(hair, b_ob_info, true, motion_step);
|
2020-02-02 13:09:18 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2024-12-26 17:53:59 +01:00
|
|
|
|
|
|
|
|
/* Particle hair. */
|
|
|
|
|
BL::Mesh b_mesh = object_to_mesh(
|
|
|
|
|
b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
|
|
|
|
|
if (b_mesh) {
|
|
|
|
|
sync_particle_hair(hair, b_mesh, b_ob_info, true, motion_step);
|
|
|
|
|
free_object_to_mesh(b_data, b_ob_info, b_mesh);
|
|
|
|
|
return;
|
2020-03-17 16:30:48 +01:00
|
|
|
}
|
2020-02-02 13:09:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No deformation on this frame, copy coordinates if other frames did have it. */
|
2020-06-04 15:12:31 +02:00
|
|
|
hair->copy_center_to_motion_step(motion_step);
|
2012-12-28 14:21:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CCL_NAMESPACE_END
|