This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/cycles/blender/blender_curves.cpp
Dalai Felinto 83e3d25bca Remove (ifdef) RNA for simulation, point cloud and particles
For 2.90 release this should not be exposed in the RNA API.

In master this needs to be ON by default, that's all.

Differential Revision: https://developer.blender.org/D8589
2020-08-18 14:41:16 +02:00

893 lines
30 KiB
C++

/*
* Copyright 2011-2013 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "render/attribute.h"
#include "render/camera.h"
#include "render/curves.h"
#include "render/hair.h"
#include "render/object.h"
#include "render/scene.h"
#include "blender/blender_sync.h"
#include "blender/blender_util.h"
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_logging.h"
CCL_NAMESPACE_BEGIN
ParticleCurveData::ParticleCurveData()
{
}
ParticleCurveData::~ParticleCurveData()
{
}
static float shaperadius(float shape, float root, float tip, float time)
{
assert(time >= 0.0f);
assert(time <= 1.0f);
float radius = 1.0f - time;
if (shape != 0.0f) {
if (shape < 0.0f)
radius = powf(radius, 1.0f + shape);
else
radius = powf(radius, 1.0f / (1.0f - shape));
}
return (radius * (root - tip)) + tip;
}
/* curve functions */
static bool ObtainCacheParticleData(
Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
{
int curvenum = 0;
int keyno = 0;
if (!(hair && b_mesh && b_ob && CData))
return false;
Transform tfm = get_transform(b_ob->matrix_world());
Transform itfm = transform_quick_inverse(tfm);
BL::Object::modifiers_iterator b_mod;
for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
(background ? b_mod->show_render() : b_mod->show_viewport())) {
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
(b_part.type() == BL::ParticleSettings::type_HAIR)) {
int shader = clamp(b_part.material() - 1, 0, hair->used_shaders.size() - 1);
int display_step = background ? b_part.render_step() : b_part.display_step();
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() :
(int)((float)b_psys.child_particles.length() *
(float)b_part.display_percentage() / 100.0f);
int totcurves = totchild;
if (b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if (totcurves == 0)
continue;
int ren_step = (1 << display_step) + 1;
if (b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
ren_step += b_part.kink_extra_steps();
CData->psys_firstcurve.push_back_slow(curvenum);
CData->psys_curvenum.push_back_slow(totcurves);
CData->psys_shader.push_back_slow(shader);
float radius = b_part.radius_scale() * 0.5f;
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
CData->psys_shape.push_back_slow(b_part.shape());
CData->psys_closetip.push_back_slow(b_part.use_close_tip());
int pa_no = 0;
if (!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts + totchild - pa_no);
CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
CData->curve_length.reserve(CData->curve_length.size() + num_add);
CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add * ren_step);
CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add * ren_step);
for (; pa_no < totparts + totchild; pa_no++) {
int keynum = 0;
CData->curve_firstkey.push_back_slow(keyno);
float curve_length = 0.0f;
float3 prev_co_world = make_float3(0.0f, 0.0f, 0.0f);
float3 prev_co_object = make_float3(0.0f, 0.0f, 0.0f);
for (int step_no = 0; step_no < ren_step; step_no++) {
float3 co_world = prev_co_world;
b_psys.co_hair(*b_ob, pa_no, step_no, &co_world.x);
float3 co_object = transform_point(&itfm, co_world);
if (step_no > 0) {
const float step_length = len(co_object - prev_co_object);
curve_length += step_length;
}
CData->curvekey_co.push_back_slow(co_object);
CData->curvekey_time.push_back_slow(curve_length);
prev_co_object = co_object;
prev_co_world = co_world;
keynum++;
}
keyno += keynum;
CData->curve_keynum.push_back_slow(keynum);
CData->curve_length.push_back_slow(curve_length);
curvenum++;
}
}
}
}
return true;
}
static bool ObtainCacheParticleUV(Hair *hair,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int uv_num)
{
if (!(hair && b_mesh && b_ob && CData))
return false;
CData->curve_uv.clear();
BL::Object::modifiers_iterator b_mod;
for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
(background ? b_mod->show_render() : b_mod->show_viewport())) {
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
(b_part.type() == BL::ParticleSettings::type_HAIR)) {
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() :
(int)((float)b_psys.child_particles.length() *
(float)b_part.display_percentage() / 100.0f);
int totcurves = totchild;
if (b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if (totcurves == 0)
continue;
int pa_no = 0;
if (!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts + totchild - pa_no);
CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
BL::ParticleSystem::particles_iterator b_pa;
b_psys.particles.begin(b_pa);
for (; pa_no < totparts + totchild; pa_no++) {
/* Add UVs */
BL::Mesh::uv_layers_iterator l;
b_mesh->uv_layers.begin(l);
float2 uv = make_float2(0.0f, 0.0f);
if (b_mesh->uv_layers.length())
b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
CData->curve_uv.push_back_slow(uv);
if (pa_no < totparts && b_pa != b_psys.particles.end())
++b_pa;
}
}
}
}
return true;
}
static bool ObtainCacheParticleVcol(Hair *hair,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int vcol_num)
{
if (!(hair && b_mesh && b_ob && CData))
return false;
CData->curve_vcol.clear();
BL::Object::modifiers_iterator b_mod;
for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
(background ? b_mod->show_render() : b_mod->show_viewport())) {
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
(b_part.type() == BL::ParticleSettings::type_HAIR)) {
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() :
(int)((float)b_psys.child_particles.length() *
(float)b_part.display_percentage() / 100.0f);
int totcurves = totchild;
if (b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if (totcurves == 0)
continue;
int pa_no = 0;
if (!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts + totchild - pa_no);
CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
BL::ParticleSystem::particles_iterator b_pa;
b_psys.particles.begin(b_pa);
for (; pa_no < totparts + totchild; pa_no++) {
/* Add vertex colors */
BL::Mesh::vertex_colors_iterator l;
b_mesh->vertex_colors.begin(l);
float4 vcol = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
if (b_mesh->vertex_colors.length())
b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
CData->curve_vcol.push_back_slow(vcol);
if (pa_no < totparts && b_pa != b_psys.particles.end())
++b_pa;
}
}
}
}
return true;
}
static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CData)
{
int num_keys = 0;
int num_curves = 0;
if (hair->num_curves())
return;
Attribute *attr_intercept = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT))
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM))
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
/* compute and reserve size of arrays */
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++) {
num_keys += CData->curve_keynum[curve];
num_curves++;
}
}
if (num_curves > 0) {
VLOG(1) << "Exporting curve segments for mesh " << hair->name;
}
hair->reserve_curves(hair->num_curves() + num_curves, hair->curve_keys.size() + num_keys);
num_keys = 0;
num_curves = 0;
/* actually export */
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++) {
size_t num_curve_keys = 0;
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)) {
radius = 0.0f;
}
hair->add_curve_key(ickey_loc, radius);
if (attr_intercept)
attr_intercept->add(time);
num_curve_keys++;
}
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(num_curves, 0));
}
hair->add_curve(num_keys, CData->psys_shader[sys]);
num_keys += num_curve_keys;
num_curves++;
}
}
/* check allocation */
if ((hair->curve_keys.size() != num_keys) || (hair->num_curves() != num_curves)) {
VLOG(1) << "Allocation failed, clearing data";
hair->clear();
}
}
static float4 CurveSegmentMotionCV(ParticleCurveData *CData, int sys, int curve, int 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];
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))
radius = 0.0f;
/* curve motion keys store both position and radius in float4 */
float4 mP = float3_to_float4(ickey_loc);
mP.w = radius;
return mP;
}
static float4 LerpCurveSegmentMotionCV(ParticleCurveData *CData, int sys, int curve, float step)
{
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);
int curvekey = (int)floorf(curve_key_f);
const float remainder = curve_key_f - curvekey;
if (remainder == 0.0f) {
return CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey);
}
int curvekey2 = curvekey + 1;
if (curvekey2 >= (CData->curve_keynum[curve] - 1)) {
curvekey2 = (CData->curve_keynum[curve] - 1);
curvekey = curvekey2 - 1;
}
const float4 mP = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey);
const float4 mP2 = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey2);
return lerp(mP, mP2, remainder);
}
static void export_hair_motion_validate_attribute(Hair *hair,
int motion_step,
int num_motion_keys,
bool have_motion)
{
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
const int num_keys = hair->curve_keys.size();
if (num_motion_keys != num_keys || !have_motion) {
/* No motion or hair "topology" changed, remove attributes again. */
if (num_motion_keys != num_keys) {
VLOG(1) << "Hair topology changed, removing attribute.";
}
else {
VLOG(1) << "No motion, removing attribute.";
}
hair->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
}
else if (motion_step > 0) {
VLOG(1) << "Filling in new motion vertex position for motion_step " << motion_step;
/* 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++) {
mP[key] = float3_to_float4(hair->curve_keys[key]);
mP[key].w = hair->curve_radius[key];
}
}
}
}
static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, int motion_step)
{
VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step "
<< motion_step;
/* find attribute */
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
bool new_attribute = false;
/* add new attribute if it doesn't exist already */
if (!attr_mP) {
VLOG(1) << "Creating new motion vertex position attribute";
attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
new_attribute = true;
}
/* export motion vectors for curve keys */
size_t numkeys = hair->curve_keys.size();
float4 *mP = attr_mP->data_float4() + motion_step * numkeys;
bool have_motion = false;
int i = 0;
int num_curves = 0;
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++) {
/* Curve lengths may not match! Curves can be clipped. */
int curve_key_end = (num_curves + 1 < (int)hair->curve_first_key.size() ?
hair->curve_first_key[num_curves + 1] :
(int)hair->curve_keys.size());
const int num_center_curve_keys = curve_key_end - hair->curve_first_key[num_curves];
const int is_num_keys_different = CData->curve_keynum[curve] - num_center_curve_keys;
if (!is_num_keys_different) {
for (int curvekey = CData->curve_firstkey[curve];
curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
curvekey++) {
if (i < hair->curve_keys.size()) {
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 */
float4 curve_key = float3_to_float4(hair->curve_keys[i]);
curve_key.w = hair->curve_radius[i];
if (len_squared(mP[i] - curve_key) > 1e-5f * 1e-5f)
have_motion = true;
}
}
i++;
}
}
else {
/* Number of keys has changed. Generate an interpolated version
* to preserve motion blur. */
const float step_size = num_center_curve_keys > 1 ? 1.0f / (num_center_curve_keys - 1) :
0.0f;
for (int step_index = 0; step_index < num_center_curve_keys; ++step_index) {
const float step = step_index * step_size;
mP[i] = LerpCurveSegmentMotionCV(CData, sys, curve, step);
i++;
}
have_motion = true;
}
num_curves++;
}
}
/* In case of new attribute, we verify if there really was any motion. */
if (new_attribute) {
export_hair_motion_validate_attribute(hair, motion_step, i, have_motion);
}
}
/* Hair Curve Sync */
bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
{
/* Test if the object has a particle modifier with hair. */
BL::Object::modifiers_iterator b_mod;
for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
(preview ? b_mod->show_viewport() : b_mod->show_render())) {
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
(b_part.type() == BL::ParticleSettings::type_HAIR)) {
return true;
}
}
}
return false;
}
/* Old particle hair. */
void BlenderSync::sync_particle_hair(
Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step)
{
/* obtain general settings */
if (b_ob.mode() == b_ob.mode_PARTICLE_EDIT || b_ob.mode() == b_ob.mode_EDIT) {
return;
}
/* extract particle hair data - should be combined with connecting to mesh later*/
ParticleCurveData CData;
ObtainCacheParticleData(hair, &b_mesh, &b_ob, &CData, !preview);
/* add hair geometry */
if (motion)
ExportCurveSegmentsMotion(hair, &CData, motion_step);
else
ExportCurveSegments(scene, hair, &CData);
/* generated coordinates from first key. we should ideally get this from
* blender to handle deforming objects */
if (!motion) {
if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
float3 loc, size;
mesh_texture_space(b_mesh, loc, size);
Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
float3 *generated = attr_generated->data_float3();
for (size_t i = 0; i < hair->num_curves(); i++) {
float3 co = hair->curve_keys[hair->get_curve(i).first_key];
generated[i] = co * size - loc;
}
}
}
/* create vertex color attributes */
if (!motion) {
BL::Mesh::vertex_colors_iterator l;
int vcol_num = 0;
for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l, vcol_num++) {
if (!hair->need_attribute(scene, ustring(l->name().c_str())))
continue;
ObtainCacheParticleVcol(hair, &b_mesh, &b_ob, &CData, !preview, vcol_num);
Attribute *attr_vcol = hair->attributes.add(
ustring(l->name().c_str()), TypeRGBA, ATTR_ELEMENT_CURVE);
float4 *fdata = attr_vcol->data_float4();
if (fdata) {
size_t i = 0;
/* Encode vertex color using the sRGB curve. */
for (size_t curve = 0; curve < CData.curve_vcol.size(); curve++) {
fdata[i++] = color_srgb_to_linear_v4(CData.curve_vcol[curve]);
}
}
}
}
/* create UV attributes */
if (!motion) {
BL::Mesh::uv_layers_iterator l;
int uv_num = 0;
for (b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l, uv_num++) {
bool active_render = l->active_render();
AttributeStandard std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
ustring name = ustring(l->name().c_str());
/* UV map */
if (hair->need_attribute(scene, name) || hair->need_attribute(scene, std)) {
Attribute *attr_uv;
ObtainCacheParticleUV(hair, &b_mesh, &b_ob, &CData, !preview, uv_num);
if (active_render)
attr_uv = hair->attributes.add(std, name);
else
attr_uv = hair->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CURVE);
float2 *uv = attr_uv->data_float2();
if (uv) {
size_t i = 0;
for (size_t curve = 0; curve < CData.curve_uv.size(); curve++) {
uv[i++] = CData.curve_uv[curve];
}
}
}
}
}
}
#ifdef WITH_HAIR_NODES
static float4 hair_point_as_float4(BL::HairPoint b_point)
{
float4 mP = float3_to_float4(get_float3(b_point.co()));
mP.w = b_point.radius();
return mP;
}
static float4 interpolate_hair_points(BL::Hair b_hair,
const int first_point_index,
const int num_points,
const float step)
{
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;
return lerp(hair_point_as_float4(b_hair.points[first_point_index + point_a]),
hair_point_as_float4(b_hair.points[first_point_index + point_b]),
t);
}
static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
{
/* TODO: optimize so we can straight memcpy arrays from Blender? */
/* Add requested attributes. */
Attribute *attr_intercept = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
}
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
}
/* Reserve memory. */
const int num_keys = b_hair.points.length();
const int num_curves = b_hair.curves.length();
if (num_curves > 0) {
VLOG(1) << "Exporting curve segments for hair " << hair->name;
}
hair->reserve_curves(num_curves, num_keys);
/* Export curves and points. */
vector<float> points_length;
BL::Hair::curves_iterator b_curve_iter;
for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) {
BL::HairCurve b_curve = *b_curve_iter;
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
float3 prev_co = make_float3(0.0f, 0.0f, 0.0f);
float length = 0.0f;
if (attr_intercept) {
points_length.clear();
points_length.reserve(num_points);
}
/* Position and radius. */
for (int i = 0; i < num_points; i++) {
BL::HairPoint b_point = b_hair.points[first_point_index + i];
const float3 co = get_float3(b_point.co());
const float radius = b_point.radius();
hair->add_curve_key(co, radius);
if (attr_intercept) {
if (i > 0) {
length += len(co - prev_co);
points_length.push_back(length);
}
prev_co = co;
}
}
/* Normalized 0..1 attribute along curve. */
if (attr_intercept) {
for (int i = 0; i < num_points; i++) {
attr_intercept->add((length == 0.0f) ? 0.0f : points_length[i] / length);
}
}
/* Random number per curve. */
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(b_curve.index(), 0));
}
/* Curve. */
const int shader_index = 0;
hair->add_curve(first_point_index, shader_index);
}
}
static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_step)
{
VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step "
<< motion_step;
/* Find or add attribute. */
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
bool new_attribute = false;
if (!attr_mP) {
VLOG(1) << "Creating new motion vertex position attribute";
attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
new_attribute = true;
}
/* Export motion keys. */
const int num_keys = hair->curve_keys.size();
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
bool have_motion = false;
int num_motion_keys = 0;
int curve_index = 0;
BL::Hair::curves_iterator b_curve_iter;
for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) {
BL::HairCurve b_curve = *b_curve_iter;
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
Hair::Curve curve = hair->get_curve(curve_index);
curve_index++;
if (num_points == curve.num_keys) {
/* Number of keys matches. */
for (int i = 0; i < num_points; i++) {
int point_index = first_point_index + i;
if (point_index < num_keys) {
mP[num_motion_keys] = hair_point_as_float4(b_hair.points[point_index]);
num_motion_keys++;
if (!have_motion) {
/* TODO: use epsilon for comparison? Was needed for particles due to
* transform, but ideally should not happen anymore. */
float4 curve_key = float3_to_float4(hair->curve_keys[i]);
curve_key.w = hair->curve_radius[i];
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;
mP[num_motion_keys] = interpolate_hair_points(b_hair, first_point_index, num_points, step);
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. */
void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
{
/* Convert Blender hair to Cycles curves. */
BL::Hair b_hair(b_ob.data());
if (motion) {
export_hair_curves_motion(hair, b_hair, motion_step);
}
else {
export_hair_curves(scene, hair, b_hair);
}
}
#else
void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
{
(void)hair;
(void)b_ob;
(void)motion;
(void)motion_step;
}
#endif
void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
Hair *hair,
const vector<Shader *> &used_shaders)
{
/* Compares curve_keys rather than strands in order to handle quick hair
* adjustments in dynamic BVH - other methods could probably do this better. */
array<float3> oldcurve_keys;
array<float> oldcurve_radius;
oldcurve_keys.steal_data(hair->curve_keys);
oldcurve_radius.steal_data(hair->curve_radius);
hair->clear();
hair->used_shaders = used_shaders;
if (view_layer.use_hair) {
if (b_ob.type() == BL::Object::type_HAIR) {
/* Hair object. */
sync_hair(hair, b_ob, false);
}
else {
/* Particle hair. */
bool need_undeformed = hair->need_attribute(scene, ATTR_STD_GENERATED);
BL::Mesh b_mesh = object_to_mesh(
b_data, b_ob, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE);
if (b_mesh) {
sync_particle_hair(hair, b_mesh, b_ob, false);
free_object_to_mesh(b_data, b_ob, b_mesh);
}
}
}
/* tag update */
const bool rebuild = ((oldcurve_keys != hair->curve_keys) ||
(oldcurve_radius != hair->curve_radius));
hair->tag_update(scene, rebuild);
}
void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
Hair *hair,
int motion_step)
{
/* Skip if nothing exported. */
if (hair->num_keys() == 0) {
return;
}
/* Export deformed coordinates. */
if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
if (b_ob.type() == BL::Object::type_HAIR) {
/* Hair object. */
sync_hair(hair, b_ob, true, motion_step);
return;
}
else {
/* Particle hair. */
BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
if (b_mesh) {
sync_particle_hair(hair, b_mesh, b_ob, true, motion_step);
free_object_to_mesh(b_data, b_ob, b_mesh);
return;
}
}
}
/* No deformation on this frame, copy coordinates if other frames did have it. */
hair->copy_center_to_motion_step(motion_step);
}
CCL_NAMESPACE_END