2023-06-14 08:52:36 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
2020-02-02 12:04:19 +01:00
|
|
|
|
2020-12-10 14:18:25 +01:00
|
|
|
#include "bvh/bvh.h"
|
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/curves.h"
|
|
|
|
#include "scene/hair.h"
|
|
|
|
#include "scene/object.h"
|
|
|
|
#include "scene/scene.h"
|
2020-02-02 12:04:19 +01:00
|
|
|
|
2021-09-20 16:16:11 +02:00
|
|
|
#include "integrator/shader_eval.h"
|
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "util/progress.h"
|
2021-09-20 16:16:11 +02:00
|
|
|
|
2020-02-02 12:04:19 +01:00
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
/* Hair Curve */
|
2023-02-27 10:39:19 +01:00
|
|
|
void Hair::Curve::bounds_grow(const int k, const float4 *keys, BoundBox &bounds) const
|
|
|
|
{
|
|
|
|
float3 P[4];
|
|
|
|
|
|
|
|
P[0] = float4_to_float3(keys[max(first_key + k - 1, first_key)]);
|
|
|
|
P[1] = float4_to_float3(keys[first_key + k]);
|
|
|
|
P[2] = float4_to_float3(keys[first_key + k + 1]);
|
|
|
|
P[3] = float4_to_float3(keys[min(first_key + k + 2, first_key + num_keys - 1)]);
|
|
|
|
|
|
|
|
float3 lower;
|
|
|
|
float3 upper;
|
|
|
|
|
|
|
|
curvebounds(&lower.x, &upper.x, P, 0);
|
|
|
|
curvebounds(&lower.y, &upper.y, P, 1);
|
|
|
|
curvebounds(&lower.z, &upper.z, P, 2);
|
|
|
|
|
|
|
|
float mr = max(keys[1].w, keys[2].w);
|
|
|
|
|
|
|
|
bounds.grow(lower, mr);
|
|
|
|
bounds.grow(upper, mr);
|
|
|
|
}
|
2020-02-02 12:04:19 +01:00
|
|
|
|
|
|
|
void Hair::Curve::bounds_grow(const int k,
|
|
|
|
const float3 *curve_keys,
|
|
|
|
const float *curve_radius,
|
|
|
|
BoundBox &bounds) const
|
|
|
|
{
|
|
|
|
float3 P[4];
|
|
|
|
|
|
|
|
P[0] = curve_keys[max(first_key + k - 1, first_key)];
|
|
|
|
P[1] = curve_keys[first_key + k];
|
|
|
|
P[2] = curve_keys[first_key + k + 1];
|
|
|
|
P[3] = curve_keys[min(first_key + k + 2, first_key + num_keys - 1)];
|
|
|
|
|
|
|
|
float3 lower;
|
|
|
|
float3 upper;
|
|
|
|
|
|
|
|
curvebounds(&lower.x, &upper.x, P, 0);
|
|
|
|
curvebounds(&lower.y, &upper.y, P, 1);
|
|
|
|
curvebounds(&lower.z, &upper.z, P, 2);
|
|
|
|
|
|
|
|
float mr = max(curve_radius[first_key + k], curve_radius[first_key + k + 1]);
|
|
|
|
|
|
|
|
bounds.grow(lower, mr);
|
|
|
|
bounds.grow(upper, mr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::Curve::bounds_grow(const int k,
|
|
|
|
const float3 *curve_keys,
|
|
|
|
const float *curve_radius,
|
|
|
|
const Transform &aligned_space,
|
|
|
|
BoundBox &bounds) const
|
|
|
|
{
|
|
|
|
float3 P[4];
|
|
|
|
|
|
|
|
P[0] = curve_keys[max(first_key + k - 1, first_key)];
|
|
|
|
P[1] = curve_keys[first_key + k];
|
|
|
|
P[2] = curve_keys[first_key + k + 1];
|
|
|
|
P[3] = curve_keys[min(first_key + k + 2, first_key + num_keys - 1)];
|
|
|
|
|
|
|
|
P[0] = transform_point(&aligned_space, P[0]);
|
|
|
|
P[1] = transform_point(&aligned_space, P[1]);
|
|
|
|
P[2] = transform_point(&aligned_space, P[2]);
|
|
|
|
P[3] = transform_point(&aligned_space, P[3]);
|
|
|
|
|
|
|
|
float3 lower;
|
|
|
|
float3 upper;
|
|
|
|
|
|
|
|
curvebounds(&lower.x, &upper.x, P, 0);
|
|
|
|
curvebounds(&lower.y, &upper.y, P, 1);
|
|
|
|
curvebounds(&lower.z, &upper.z, P, 2);
|
|
|
|
|
|
|
|
float mr = max(curve_radius[first_key + k], curve_radius[first_key + k + 1]);
|
|
|
|
|
|
|
|
bounds.grow(lower, mr);
|
|
|
|
bounds.grow(upper, mr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::Curve::bounds_grow(float4 keys[4], BoundBox &bounds) const
|
|
|
|
{
|
|
|
|
float3 P[4] = {
|
|
|
|
float4_to_float3(keys[0]),
|
|
|
|
float4_to_float3(keys[1]),
|
|
|
|
float4_to_float3(keys[2]),
|
|
|
|
float4_to_float3(keys[3]),
|
|
|
|
};
|
|
|
|
|
|
|
|
float3 lower;
|
|
|
|
float3 upper;
|
|
|
|
|
|
|
|
curvebounds(&lower.x, &upper.x, P, 0);
|
|
|
|
curvebounds(&lower.y, &upper.y, P, 1);
|
|
|
|
curvebounds(&lower.z, &upper.z, P, 2);
|
|
|
|
|
|
|
|
float mr = max(keys[1].w, keys[2].w);
|
|
|
|
|
|
|
|
bounds.grow(lower, mr);
|
|
|
|
bounds.grow(upper, mr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::Curve::motion_keys(const float3 *curve_keys,
|
|
|
|
const float *curve_radius,
|
2023-02-27 10:39:19 +01:00
|
|
|
const float4 *key_steps,
|
2020-02-02 12:04:19 +01:00
|
|
|
size_t num_curve_keys,
|
|
|
|
size_t num_steps,
|
|
|
|
float time,
|
|
|
|
size_t k0,
|
|
|
|
size_t k1,
|
|
|
|
float4 r_keys[2]) const
|
|
|
|
{
|
|
|
|
/* Figure out which steps we need to fetch and their interpolation factor. */
|
|
|
|
const size_t max_step = num_steps - 1;
|
2022-02-10 10:37:00 +01:00
|
|
|
const size_t step = std::min((size_t)(time * max_step), max_step - 1);
|
2020-02-02 12:04:19 +01:00
|
|
|
const float t = time * max_step - step;
|
|
|
|
/* Fetch vertex coordinates. */
|
|
|
|
float4 curr_keys[2];
|
|
|
|
float4 next_keys[2];
|
|
|
|
keys_for_step(
|
|
|
|
curve_keys, curve_radius, key_steps, num_curve_keys, num_steps, step, k0, k1, curr_keys);
|
|
|
|
keys_for_step(
|
|
|
|
curve_keys, curve_radius, key_steps, num_curve_keys, num_steps, step + 1, k0, k1, next_keys);
|
|
|
|
/* Interpolate between steps. */
|
|
|
|
r_keys[0] = (1.0f - t) * curr_keys[0] + t * next_keys[0];
|
|
|
|
r_keys[1] = (1.0f - t) * curr_keys[1] + t * next_keys[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::Curve::cardinal_motion_keys(const float3 *curve_keys,
|
|
|
|
const float *curve_radius,
|
2023-02-27 10:39:19 +01:00
|
|
|
const float4 *key_steps,
|
2020-02-02 12:04:19 +01:00
|
|
|
size_t num_curve_keys,
|
|
|
|
size_t num_steps,
|
|
|
|
float time,
|
|
|
|
size_t k0,
|
|
|
|
size_t k1,
|
|
|
|
size_t k2,
|
|
|
|
size_t k3,
|
|
|
|
float4 r_keys[4]) const
|
|
|
|
{
|
|
|
|
/* Figure out which steps we need to fetch and their interpolation factor. */
|
|
|
|
const size_t max_step = num_steps - 1;
|
2022-02-10 10:37:00 +01:00
|
|
|
const size_t step = min((size_t)(time * max_step), max_step - 1);
|
2020-02-02 12:04:19 +01:00
|
|
|
const float t = time * max_step - step;
|
|
|
|
/* Fetch vertex coordinates. */
|
|
|
|
float4 curr_keys[4];
|
|
|
|
float4 next_keys[4];
|
|
|
|
cardinal_keys_for_step(curve_keys,
|
|
|
|
curve_radius,
|
|
|
|
key_steps,
|
|
|
|
num_curve_keys,
|
|
|
|
num_steps,
|
|
|
|
step,
|
|
|
|
k0,
|
|
|
|
k1,
|
|
|
|
k2,
|
|
|
|
k3,
|
|
|
|
curr_keys);
|
|
|
|
cardinal_keys_for_step(curve_keys,
|
|
|
|
curve_radius,
|
|
|
|
key_steps,
|
|
|
|
num_curve_keys,
|
|
|
|
num_steps,
|
|
|
|
step + 1,
|
|
|
|
k0,
|
|
|
|
k1,
|
|
|
|
k2,
|
|
|
|
k3,
|
|
|
|
next_keys);
|
|
|
|
/* Interpolate between steps. */
|
|
|
|
r_keys[0] = (1.0f - t) * curr_keys[0] + t * next_keys[0];
|
|
|
|
r_keys[1] = (1.0f - t) * curr_keys[1] + t * next_keys[1];
|
|
|
|
r_keys[2] = (1.0f - t) * curr_keys[2] + t * next_keys[2];
|
|
|
|
r_keys[3] = (1.0f - t) * curr_keys[3] + t * next_keys[3];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::Curve::keys_for_step(const float3 *curve_keys,
|
|
|
|
const float *curve_radius,
|
2023-02-27 10:39:19 +01:00
|
|
|
const float4 *key_steps,
|
2020-02-02 12:04:19 +01:00
|
|
|
size_t num_curve_keys,
|
|
|
|
size_t num_steps,
|
|
|
|
size_t step,
|
|
|
|
size_t k0,
|
|
|
|
size_t k1,
|
|
|
|
float4 r_keys[2]) const
|
|
|
|
{
|
2022-02-10 10:37:00 +01:00
|
|
|
k0 = max(k0, (size_t)0);
|
|
|
|
k1 = min(k1, (size_t)(num_keys - 1));
|
2020-02-02 12:04:19 +01:00
|
|
|
const size_t center_step = ((num_steps - 1) / 2);
|
|
|
|
if (step == center_step) {
|
|
|
|
/* Center step: regular key location. */
|
|
|
|
/* TODO(sergey): Consider adding make_float4(float3, float)
|
|
|
|
* function.
|
|
|
|
*/
|
|
|
|
r_keys[0] = make_float4(curve_keys[first_key + k0].x,
|
|
|
|
curve_keys[first_key + k0].y,
|
|
|
|
curve_keys[first_key + k0].z,
|
|
|
|
curve_radius[first_key + k0]);
|
|
|
|
r_keys[1] = make_float4(curve_keys[first_key + k1].x,
|
|
|
|
curve_keys[first_key + k1].y,
|
|
|
|
curve_keys[first_key + k1].z,
|
|
|
|
curve_radius[first_key + k1]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Center step is not stored in this array. */
|
|
|
|
if (step > center_step) {
|
|
|
|
step--;
|
|
|
|
}
|
|
|
|
const size_t offset = first_key + step * num_curve_keys;
|
|
|
|
r_keys[0] = make_float4(key_steps[offset + k0].x,
|
|
|
|
key_steps[offset + k0].y,
|
|
|
|
key_steps[offset + k0].z,
|
|
|
|
curve_radius[first_key + k0]);
|
|
|
|
r_keys[1] = make_float4(key_steps[offset + k1].x,
|
|
|
|
key_steps[offset + k1].y,
|
|
|
|
key_steps[offset + k1].z,
|
|
|
|
curve_radius[first_key + k1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::Curve::cardinal_keys_for_step(const float3 *curve_keys,
|
|
|
|
const float *curve_radius,
|
2023-02-27 10:39:19 +01:00
|
|
|
const float4 *key_steps,
|
2020-02-02 12:04:19 +01:00
|
|
|
size_t num_curve_keys,
|
|
|
|
size_t num_steps,
|
|
|
|
size_t step,
|
|
|
|
size_t k0,
|
|
|
|
size_t k1,
|
|
|
|
size_t k2,
|
|
|
|
size_t k3,
|
|
|
|
float4 r_keys[4]) const
|
|
|
|
{
|
2022-02-10 10:37:00 +01:00
|
|
|
k0 = max(k0, (size_t)0);
|
|
|
|
k3 = min(k3, (size_t)(num_keys - 1));
|
2020-02-02 12:04:19 +01:00
|
|
|
const size_t center_step = ((num_steps - 1) / 2);
|
|
|
|
if (step == center_step) {
|
|
|
|
/* Center step: regular key location. */
|
|
|
|
r_keys[0] = make_float4(curve_keys[first_key + k0].x,
|
|
|
|
curve_keys[first_key + k0].y,
|
|
|
|
curve_keys[first_key + k0].z,
|
|
|
|
curve_radius[first_key + k0]);
|
|
|
|
r_keys[1] = make_float4(curve_keys[first_key + k1].x,
|
|
|
|
curve_keys[first_key + k1].y,
|
|
|
|
curve_keys[first_key + k1].z,
|
|
|
|
curve_radius[first_key + k1]);
|
|
|
|
r_keys[2] = make_float4(curve_keys[first_key + k2].x,
|
|
|
|
curve_keys[first_key + k2].y,
|
|
|
|
curve_keys[first_key + k2].z,
|
|
|
|
curve_radius[first_key + k2]);
|
|
|
|
r_keys[3] = make_float4(curve_keys[first_key + k3].x,
|
|
|
|
curve_keys[first_key + k3].y,
|
|
|
|
curve_keys[first_key + k3].z,
|
|
|
|
curve_radius[first_key + k3]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Center step is not stored in this array. */
|
|
|
|
if (step > center_step) {
|
|
|
|
step--;
|
|
|
|
}
|
|
|
|
const size_t offset = first_key + step * num_curve_keys;
|
|
|
|
r_keys[0] = make_float4(key_steps[offset + k0].x,
|
|
|
|
key_steps[offset + k0].y,
|
|
|
|
key_steps[offset + k0].z,
|
|
|
|
curve_radius[first_key + k0]);
|
|
|
|
r_keys[1] = make_float4(key_steps[offset + k1].x,
|
|
|
|
key_steps[offset + k1].y,
|
|
|
|
key_steps[offset + k1].z,
|
|
|
|
curve_radius[first_key + k1]);
|
|
|
|
r_keys[2] = make_float4(key_steps[offset + k2].x,
|
|
|
|
key_steps[offset + k2].y,
|
|
|
|
key_steps[offset + k2].z,
|
|
|
|
curve_radius[first_key + k2]);
|
|
|
|
r_keys[3] = make_float4(key_steps[offset + k3].x,
|
|
|
|
key_steps[offset + k3].y,
|
|
|
|
key_steps[offset + k3].z,
|
|
|
|
curve_radius[first_key + k3]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hair */
|
|
|
|
|
|
|
|
NODE_DEFINE(Hair)
|
|
|
|
{
|
2021-03-15 16:11:12 +01:00
|
|
|
NodeType *type = NodeType::add("hair", create, NodeType::NONE, Geometry::get_node_base_type());
|
2020-02-02 12:04:19 +01:00
|
|
|
|
|
|
|
SOCKET_POINT_ARRAY(curve_keys, "Curve Keys", array<float3>());
|
|
|
|
SOCKET_FLOAT_ARRAY(curve_radius, "Curve Radius", array<float>());
|
|
|
|
SOCKET_INT_ARRAY(curve_first_key, "Curve First Key", array<int>());
|
|
|
|
SOCKET_INT_ARRAY(curve_shader, "Curve Shader", array<int>());
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2021-03-15 16:11:12 +01:00
|
|
|
Hair::Hair() : Geometry(get_node_type(), Geometry::HAIR)
|
2020-02-02 12:04:19 +01:00
|
|
|
{
|
2021-02-28 23:23:24 +01:00
|
|
|
curve_key_offset = 0;
|
|
|
|
curve_segment_offset = 0;
|
2020-06-10 19:07:07 +02:00
|
|
|
curve_shape = CURVE_RIBBON;
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
|
2023-03-29 16:50:54 +02:00
|
|
|
Hair::~Hair() {}
|
2020-02-02 12:04:19 +01:00
|
|
|
|
|
|
|
void Hair::resize_curves(int numcurves, int numkeys)
|
|
|
|
{
|
|
|
|
curve_keys.resize(numkeys);
|
|
|
|
curve_radius.resize(numkeys);
|
|
|
|
curve_first_key.resize(numcurves);
|
|
|
|
curve_shader.resize(numcurves);
|
|
|
|
|
|
|
|
attributes.resize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::reserve_curves(int numcurves, int numkeys)
|
|
|
|
{
|
|
|
|
curve_keys.reserve(numkeys);
|
|
|
|
curve_radius.reserve(numkeys);
|
|
|
|
curve_first_key.reserve(numcurves);
|
|
|
|
curve_shader.reserve(numcurves);
|
|
|
|
|
|
|
|
attributes.resize(true);
|
|
|
|
}
|
|
|
|
|
2020-10-31 17:21:07 +01:00
|
|
|
void Hair::clear(bool preserve_shaders)
|
2020-02-02 12:04:19 +01:00
|
|
|
{
|
2020-10-31 17:21:07 +01:00
|
|
|
Geometry::clear(preserve_shaders);
|
2020-02-02 12:04:19 +01:00
|
|
|
|
|
|
|
curve_keys.clear();
|
|
|
|
curve_radius.clear();
|
|
|
|
curve_first_key.clear();
|
|
|
|
curve_shader.clear();
|
|
|
|
|
|
|
|
attributes.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::add_curve_key(float3 co, float radius)
|
|
|
|
{
|
|
|
|
curve_keys.push_back_reserved(co);
|
|
|
|
curve_radius.push_back_reserved(radius);
|
2020-11-04 11:17:38 +01:00
|
|
|
|
|
|
|
tag_curve_keys_modified();
|
|
|
|
tag_curve_radius_modified();
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::add_curve(int first_key, int shader)
|
|
|
|
{
|
|
|
|
curve_first_key.push_back_reserved(first_key);
|
|
|
|
curve_shader.push_back_reserved(shader);
|
2020-11-04 11:17:38 +01:00
|
|
|
|
|
|
|
tag_curve_first_key_modified();
|
|
|
|
tag_curve_shader_modified();
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::copy_center_to_motion_step(const int motion_step)
|
|
|
|
{
|
|
|
|
Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
if (attr_mP) {
|
|
|
|
float3 *keys = &curve_keys[0];
|
|
|
|
size_t numkeys = curve_keys.size();
|
|
|
|
memcpy(attr_mP->data_float3() + motion_step * numkeys, keys, sizeof(float3) * numkeys);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::get_uv_tiles(ustring map, unordered_set<int> &tiles)
|
|
|
|
{
|
|
|
|
Attribute *attr;
|
|
|
|
|
|
|
|
if (map.empty()) {
|
|
|
|
attr = attributes.find(ATTR_STD_UV);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
attr = attributes.find(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attr) {
|
2020-02-03 21:40:58 +01:00
|
|
|
attr->get_uv_tiles(this, ATTR_PRIM_GEOMETRY, tiles);
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::compute_bounds()
|
|
|
|
{
|
|
|
|
BoundBox bnds = BoundBox::empty;
|
|
|
|
size_t curve_keys_size = curve_keys.size();
|
|
|
|
|
|
|
|
if (curve_keys_size > 0) {
|
2023-09-17 01:01:48 +02:00
|
|
|
for (size_t i = 0; i < curve_keys_size; i++) {
|
2020-02-02 12:04:19 +01:00
|
|
|
bnds.grow(curve_keys[i], curve_radius[i]);
|
2023-09-17 01:01:48 +02:00
|
|
|
}
|
2020-02-02 12:04:19 +01:00
|
|
|
|
|
|
|
Attribute *curve_attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
if (use_motion_blur && curve_attr) {
|
|
|
|
size_t steps_size = curve_keys.size() * (motion_steps - 1);
|
2023-02-27 10:39:19 +01:00
|
|
|
// Attribute data is stored as a float4 and is not
|
|
|
|
// interchangeable with float3
|
|
|
|
float4 *key_steps = curve_attr->data_float4();
|
2020-02-02 12:04:19 +01:00
|
|
|
|
2023-09-17 01:01:48 +02:00
|
|
|
for (size_t i = 0; i < steps_size; i++) {
|
2023-02-27 10:39:19 +01:00
|
|
|
bnds.grow(float4_to_float3(key_steps[i]));
|
2023-09-17 01:01:48 +02:00
|
|
|
}
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!bnds.valid()) {
|
|
|
|
bnds = BoundBox::empty;
|
|
|
|
|
|
|
|
/* skip nan or inf coordinates */
|
2023-09-17 01:01:48 +02:00
|
|
|
for (size_t i = 0; i < curve_keys_size; i++) {
|
2020-02-02 12:04:19 +01:00
|
|
|
bnds.grow_safe(curve_keys[i], curve_radius[i]);
|
2023-09-17 01:01:48 +02:00
|
|
|
}
|
2020-02-02 12:04:19 +01:00
|
|
|
|
|
|
|
if (use_motion_blur && curve_attr) {
|
|
|
|
size_t steps_size = curve_keys.size() * (motion_steps - 1);
|
2023-02-27 10:39:19 +01:00
|
|
|
// Attribute data is stored as a float4 which is not
|
|
|
|
// interchangeable with float4
|
|
|
|
float4 *key_steps = curve_attr->data_float4();
|
2020-02-02 12:04:19 +01:00
|
|
|
|
2023-09-17 01:01:48 +02:00
|
|
|
for (size_t i = 0; i < steps_size; i++) {
|
2023-02-27 10:39:19 +01:00
|
|
|
bnds.grow_safe(float4_to_float3(key_steps[i]));
|
2023-09-17 01:01:48 +02:00
|
|
|
}
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bnds.valid()) {
|
|
|
|
/* empty mesh */
|
2021-02-17 01:47:18 +01:00
|
|
|
bnds.grow(zero_float3());
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bounds = bnds;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::apply_transform(const Transform &tfm, const bool apply_to_motion)
|
|
|
|
{
|
|
|
|
/* compute uniform scale */
|
|
|
|
float3 c0 = transform_get_column(&tfm, 0);
|
|
|
|
float3 c1 = transform_get_column(&tfm, 1);
|
|
|
|
float3 c2 = transform_get_column(&tfm, 2);
|
|
|
|
float scalar = powf(fabsf(dot(cross(c0, c1), c2)), 1.0f / 3.0f);
|
|
|
|
|
|
|
|
/* apply transform to curve keys */
|
|
|
|
for (size_t i = 0; i < curve_keys.size(); i++) {
|
|
|
|
float3 co = transform_point(&tfm, curve_keys[i]);
|
|
|
|
float radius = curve_radius[i] * scalar;
|
|
|
|
|
|
|
|
/* scale for curve radius is only correct for uniform scale */
|
|
|
|
curve_keys[i] = co;
|
|
|
|
curve_radius[i] = radius;
|
|
|
|
}
|
|
|
|
|
2021-10-08 13:45:34 +02:00
|
|
|
tag_curve_keys_modified();
|
|
|
|
tag_curve_radius_modified();
|
|
|
|
|
2020-02-02 12:04:19 +01:00
|
|
|
if (apply_to_motion) {
|
|
|
|
Attribute *curve_attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
|
|
|
|
|
|
|
if (curve_attr) {
|
|
|
|
/* apply transform to motion curve keys */
|
|
|
|
size_t steps_size = curve_keys.size() * (motion_steps - 1);
|
|
|
|
float4 *key_steps = curve_attr->data_float4();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < steps_size; i++) {
|
|
|
|
float3 co = transform_point(&tfm, float4_to_float3(key_steps[i]));
|
|
|
|
float radius = key_steps[i].w * scalar;
|
|
|
|
|
|
|
|
/* scale for curve radius is only correct for uniform scale */
|
|
|
|
key_steps[i] = float3_to_float4(co);
|
|
|
|
key_steps[i].w = radius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hair::pack_curves(Scene *scene,
|
|
|
|
float4 *curve_key_co,
|
2021-02-28 23:23:24 +01:00
|
|
|
KernelCurve *curves,
|
|
|
|
KernelCurveSegment *curve_segments)
|
2020-02-02 12:04:19 +01:00
|
|
|
{
|
|
|
|
size_t curve_keys_size = curve_keys.size();
|
|
|
|
|
|
|
|
/* pack curve keys */
|
|
|
|
if (curve_keys_size) {
|
|
|
|
float3 *keys_ptr = curve_keys.data();
|
|
|
|
float *radius_ptr = curve_radius.data();
|
|
|
|
|
2023-09-17 01:01:48 +02:00
|
|
|
for (size_t i = 0; i < curve_keys_size; i++) {
|
2020-02-02 12:04:19 +01:00
|
|
|
curve_key_co[i] = make_float4(keys_ptr[i].x, keys_ptr[i].y, keys_ptr[i].z, radius_ptr[i]);
|
2023-09-17 01:01:48 +02:00
|
|
|
}
|
2020-02-02 12:04:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* pack curve segments */
|
2021-02-28 23:23:24 +01:00
|
|
|
const PrimitiveType type = primitive_type();
|
|
|
|
|
2020-02-02 12:04:19 +01:00
|
|
|
size_t curve_num = num_curves();
|
2021-02-28 23:23:24 +01:00
|
|
|
size_t index = 0;
|
2020-02-02 12:04:19 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < curve_num; i++) {
|
|
|
|
Curve curve = get_curve(i);
|
|
|
|
int shader_id = curve_shader[i];
|
2020-11-04 11:17:38 +01:00
|
|
|
Shader *shader = (shader_id < used_shaders.size()) ?
|
|
|
|
static_cast<Shader *>(used_shaders[shader_id]) :
|
|
|
|
scene->default_surface;
|
2020-02-02 12:04:19 +01:00
|
|
|
shader_id = scene->shader_manager->get_shader_id(shader, false);
|
|
|
|
|
2021-02-28 23:23:24 +01:00
|
|
|
curves[i].shader_id = shader_id;
|
|
|
|
curves[i].first_key = curve_key_offset + curve.first_key;
|
|
|
|
curves[i].num_keys = curve.num_keys;
|
|
|
|
curves[i].type = type;
|
2020-02-02 12:04:19 +01:00
|
|
|
|
2021-02-28 23:23:24 +01:00
|
|
|
for (int k = 0; k < curve.num_segments(); ++k, ++index) {
|
|
|
|
curve_segments[index].prim = prim_offset + i;
|
|
|
|
curve_segments[index].type = PRIMITIVE_PACK_SEGMENT(type, k);
|
2021-05-10 18:23:32 +02:00
|
|
|
}
|
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
|
|
|
}
|
2021-02-28 23:23:24 +01:00
|
|
|
}
|
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
|
|
|
|
2021-02-28 23:23:24 +01:00
|
|
|
PrimitiveType Hair::primitive_type() const
|
|
|
|
{
|
|
|
|
return has_motion_blur() ?
|
|
|
|
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
|
|
|
|
PRIMITIVE_MOTION_CURVE_THICK) :
|
|
|
|
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
|
2020-12-10 14:18:25 +01:00
|
|
|
}
|
|
|
|
|
2021-09-20 16:16:11 +02:00
|
|
|
/* Fill in coordinates for curve transparency shader evaluation on device. */
|
|
|
|
static int fill_shader_input(const Hair *hair,
|
2023-04-04 20:00:56 +02:00
|
|
|
const size_t object_index,
|
2021-09-20 16:16:11 +02:00
|
|
|
device_vector<KernelShaderEvalInput> &d_input)
|
|
|
|
{
|
|
|
|
int d_input_size = 0;
|
|
|
|
KernelShaderEvalInput *d_input_data = d_input.data();
|
|
|
|
|
|
|
|
const int num_curves = hair->num_curves();
|
|
|
|
for (int i = 0; i < num_curves; i++) {
|
|
|
|
const Hair::Curve curve = hair->get_curve(i);
|
|
|
|
const int num_segments = curve.num_segments();
|
|
|
|
|
|
|
|
for (int j = 0; j < num_segments + 1; j++) {
|
|
|
|
KernelShaderEvalInput in;
|
|
|
|
in.object = object_index;
|
|
|
|
in.prim = hair->prim_offset + i;
|
|
|
|
in.u = (j < num_segments) ? 0.0f : 1.0f;
|
|
|
|
in.v = (j < num_segments) ? __int_as_float(j) : __int_as_float(j - 1);
|
|
|
|
d_input_data[d_input_size++] = in;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return d_input_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read back curve transparency shader output. */
|
|
|
|
static void read_shader_output(float *shadow_transparency,
|
|
|
|
bool &is_fully_opaque,
|
|
|
|
const device_vector<float> &d_output)
|
|
|
|
{
|
|
|
|
const int num_keys = d_output.size();
|
|
|
|
const float *output_data = d_output.data();
|
|
|
|
bool is_opaque = true;
|
|
|
|
|
|
|
|
for (int i = 0; i < num_keys; i++) {
|
|
|
|
shadow_transparency[i] = output_data[i];
|
|
|
|
if (shadow_transparency[i] > 0.0f) {
|
|
|
|
is_opaque = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
is_fully_opaque = is_opaque;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Hair::need_shadow_transparency()
|
|
|
|
{
|
|
|
|
for (const Node *node : used_shaders) {
|
|
|
|
const Shader *shader = static_cast<const Shader *>(node);
|
|
|
|
if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Hair::update_shadow_transparency(Device *device, Scene *scene, Progress &progress)
|
|
|
|
{
|
|
|
|
if (!need_shadow_transparency()) {
|
|
|
|
/* If no shaders with shadow transparency, remove attribute. */
|
|
|
|
Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY);
|
|
|
|
if (attr) {
|
|
|
|
attributes.remove(attr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string msg = string_printf("Computing Shadow Transparency %s", name.c_str());
|
|
|
|
progress.set_status("Updating Hair", msg);
|
|
|
|
|
|
|
|
/* Create shadow transparency attribute. */
|
|
|
|
Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY);
|
|
|
|
const bool attribute_exists = (attr != nullptr);
|
|
|
|
if (!attribute_exists) {
|
|
|
|
attr = attributes.add(ATTR_STD_SHADOW_TRANSPARENCY);
|
|
|
|
}
|
|
|
|
|
|
|
|
float *attr_data = attr->data_float();
|
|
|
|
|
|
|
|
/* Find object index. */
|
|
|
|
size_t object_index = OBJECT_NONE;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < scene->objects.size(); i++) {
|
|
|
|
if (scene->objects[i]->get_geometry() == this) {
|
|
|
|
object_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Evaluate shader on device. */
|
|
|
|
ShaderEval shader_eval(device, progress);
|
|
|
|
bool is_fully_opaque = false;
|
|
|
|
shader_eval.eval(SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY,
|
|
|
|
num_keys(),
|
|
|
|
1,
|
|
|
|
function_bind(&fill_shader_input, this, object_index, _1),
|
|
|
|
function_bind(&read_shader_output, attr_data, is_fully_opaque, _1));
|
|
|
|
|
|
|
|
if (is_fully_opaque) {
|
|
|
|
attributes.remove(attr);
|
|
|
|
return attribute_exists;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-02 12:04:19 +01:00
|
|
|
CCL_NAMESPACE_END
|