Cycles: Use Blender headers to access geometry data, avoid copy
Since 34b4487844, attributes are always made mutable when
accessed from the RNA API. This can result in unnecessary copies, which
increases memory usage and reduces performance.
Cycles is the only user of the C++ RNA API, which we'd like to remove
in the future since it doesn't really make sense in the big picture.
Hydra is now a better alternative for external render engines.
To start that change and fix the unnecessary copies, this commit
moves to use Blender headers directly for accessing attribute and
other geometry data. This also removes the few places that still had
overhead from the RNA API after the changes ([0]) in 3.6. In a simple
test with a large grid, I observed a 1.76x performance improvement,
from 1.04 to 0.59 seconds to extract the mesh data to Cycles.
[0]: https://wiki.blender.org/wiki/Reference/Release_Notes/3.6/Cycles#Performance
Pull Request: https://projects.blender.org/blender/blender/pulls/112306
This commit is contained in:
@@ -41,6 +41,7 @@ set(SRC
|
||||
viewport.cpp
|
||||
volume.cpp
|
||||
|
||||
attribute_convert.h
|
||||
CCL_api.h
|
||||
device.h
|
||||
display_driver.h
|
||||
|
||||
94
intern/cycles/blender/attribute_convert.h
Normal file
94
intern/cycles/blender/attribute_convert.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef __BLENDER_ATTRIBUTE_CONVERT_H__
|
||||
#define __BLENDER_ATTRIBUTE_CONVERT_H__
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/color.h"
|
||||
#include "util/param.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BLI_math_color.hh"
|
||||
#include "BLI_math_quaternion_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
template<typename BlenderT> struct AttributeConverter {
|
||||
using CyclesT = void;
|
||||
};
|
||||
|
||||
template<> struct AttributeConverter<float> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static CyclesT convert(const float &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<int> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static CyclesT convert(const int &value)
|
||||
{
|
||||
return float(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::float3> {
|
||||
using CyclesT = float3;
|
||||
static constexpr auto type_desc = TypeVector;
|
||||
static CyclesT convert(const blender::float3 &value)
|
||||
{
|
||||
return make_float3(value[0], value[1], value[2]);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::ColorGeometry4f> {
|
||||
using CyclesT = float4;
|
||||
static constexpr auto type_desc = TypeRGBA;
|
||||
static CyclesT convert(const blender::ColorGeometry4f &value)
|
||||
{
|
||||
return make_float4(value[0], value[1], value[2], value[3]);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::ColorGeometry4b> {
|
||||
using CyclesT = float4;
|
||||
static constexpr auto type_desc = TypeRGBA;
|
||||
static CyclesT convert(const blender::ColorGeometry4b &value)
|
||||
{
|
||||
return color_srgb_to_linear(make_float4(byte_to_float(value[0]),
|
||||
byte_to_float(value[1]),
|
||||
byte_to_float(value[2]),
|
||||
byte_to_float(value[3])));
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<bool> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static CyclesT convert(const bool &value)
|
||||
{
|
||||
return float(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<int8_t> {
|
||||
using CyclesT = float;
|
||||
static constexpr auto type_desc = TypeFloat;
|
||||
static CyclesT convert(const int8_t &value)
|
||||
{
|
||||
return float(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<blender::math::Quaternion> {
|
||||
using CyclesT = float4;
|
||||
static constexpr auto type_desc = TypeFloat4;
|
||||
static CyclesT convert(const blender::math::Quaternion &value)
|
||||
{
|
||||
return make_float4(value.w, value.x, value.y, value.z);
|
||||
}
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* __BLENDER_ATTRIBUTE_CONVERT_H__ */
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "blender/attribute_convert.h"
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "util/hash.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ParticleCurveData::ParticleCurveData() {}
|
||||
@@ -576,7 +578,7 @@ void BlenderSync::sync_particle_hair(
|
||||
if (!motion) {
|
||||
if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
|
||||
float3 loc, size;
|
||||
mesh_texture_space(b_mesh, loc, size);
|
||||
mesh_texture_space(*static_cast<const ::Mesh *>(b_mesh.ptr.data), loc, size);
|
||||
|
||||
Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
|
||||
float3 *generated = attr_generated->data_float3();
|
||||
@@ -653,44 +655,6 @@ void BlenderSync::sync_particle_hair(
|
||||
}
|
||||
}
|
||||
|
||||
static const float *find_radius_attribute(BL::Curves b_curves)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_curves.attributes) {
|
||||
if (b_attribute.name() != "radius") {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
|
||||
continue;
|
||||
}
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
if (b_float_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const float *>(b_float_attribute.data[0].ptr.data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const float (*find_position_attribute(BL::Curves b_curves))[3]
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_curves.attributes) {
|
||||
if (b_attribute.name() != "position") {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT_VECTOR) {
|
||||
continue;
|
||||
}
|
||||
BL::FloatVectorAttribute b_float3_attribute{b_attribute};
|
||||
if (b_float3_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const float(*)[3]>(b_float3_attribute.data[0].ptr.data);
|
||||
}
|
||||
/* The position attribute must exist. */
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename TypeInCycles, typename GetValueAtIndex>
|
||||
static void fill_generic_attribute(const int num_curves,
|
||||
const int num_points,
|
||||
@@ -718,16 +682,10 @@ static void fill_generic_attribute(const int num_curves,
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_create_motion(Hair *hair, BL::Attribute &b_attribute, const float motion_scale)
|
||||
static void attr_create_motion(Hair *hair,
|
||||
const blender::Span<blender::float3> src,
|
||||
const float motion_scale)
|
||||
{
|
||||
if (!(b_attribute.domain() == BL::Attribute::domain_POINT) &&
|
||||
(b_attribute.data_type() == BL::Attribute::data_type_FLOAT_VECTOR))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BL::FloatVectorAttribute b_vector_attribute(b_attribute);
|
||||
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);
|
||||
const int num_curve_keys = hair->get_curve_keys().size();
|
||||
|
||||
/* Find or add attribute */
|
||||
@@ -750,177 +708,97 @@ static void attr_create_motion(Hair *hair, BL::Attribute &b_attribute, const flo
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_create_uv(AttributeSet &attributes,
|
||||
const int num_curves,
|
||||
const int num_points,
|
||||
BL::Attribute &b_attribute,
|
||||
const ustring name)
|
||||
{
|
||||
BL::Float2Attribute b_float2_attribute{b_attribute};
|
||||
const float(*src)[2] = static_cast<const float(*)[2]>(b_float2_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(ATTR_STD_UV, name);
|
||||
|
||||
float2 *data = attr->data_float2();
|
||||
fill_generic_attribute(num_curves, num_points, data, ATTR_ELEMENT_CURVE, [&](int i) {
|
||||
return make_float2(src[i][0], src[i][1]);
|
||||
});
|
||||
}
|
||||
|
||||
static void attr_create_generic(Scene *scene,
|
||||
Hair *hair,
|
||||
BL::Curves &b_curves,
|
||||
const blender::bke::CurvesGeometry &b_curves,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
const int num_keys = b_curves.points.length();
|
||||
const int num_curves = b_curves.curves.length();
|
||||
const blender::bke::AttributeAccessor b_attributes = b_curves.attributes();
|
||||
|
||||
AttributeSet &attributes = hair->attributes;
|
||||
static const ustring u_velocity("velocity");
|
||||
const bool need_uv = hair->need_attribute(scene, ATTR_STD_UV);
|
||||
bool have_uv = false;
|
||||
|
||||
for (BL::Attribute &b_attribute : b_curves.attributes) {
|
||||
const ustring name{b_attribute.name().c_str()};
|
||||
b_attributes.for_all([&](const blender::bke::AttributeIDRef &id,
|
||||
const blender::bke::AttributeMetaData meta_data) {
|
||||
const ustring name{std::string_view(id.name())};
|
||||
|
||||
const BL::Attribute::domain_enum b_domain = b_attribute.domain();
|
||||
const BL::Attribute::data_type_enum b_data_type = b_attribute.data_type();
|
||||
const eAttrDomain b_domain = meta_data.domain;
|
||||
const eCustomDataType b_data_type = meta_data.data_type;
|
||||
|
||||
if (need_motion && name == u_velocity) {
|
||||
attr_create_motion(hair, b_attribute, motion_scale);
|
||||
continue;
|
||||
const blender::VArraySpan b_attr = *b_attributes.lookup<blender::float3>(id,
|
||||
ATTR_DOMAIN_POINT);
|
||||
attr_create_motion(hair, b_attr, motion_scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Weak, use first float2 attribute as standard UV. */
|
||||
if (need_uv && !have_uv && b_data_type == BL::Attribute::data_type_FLOAT2 &&
|
||||
b_domain == BL::Attribute::domain_CURVE)
|
||||
{
|
||||
attr_create_uv(attributes, num_curves, num_keys, b_attribute, name);
|
||||
if (need_uv && !have_uv && b_data_type == CD_PROP_FLOAT2 && b_domain == ATTR_DOMAIN_CURVE) {
|
||||
Attribute *attr = attributes.add(ATTR_STD_UV, name);
|
||||
|
||||
const blender::VArraySpan b_attr = *b_attributes.lookup<blender::float2>(id);
|
||||
|
||||
static_assert(sizeof(blender::float2) == sizeof(float2));
|
||||
const blender::Span src = b_attr.cast<float2>();
|
||||
std::copy(src.begin(), src.end(), attr->data_float2());
|
||||
have_uv = true;
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!hair->need_attribute(scene, name)) {
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
if (attributes.find(name)) {
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
const blender::bke::GAttributeReader b_attr = b_attributes.lookup(id);
|
||||
|
||||
AttributeElement element = ATTR_ELEMENT_NONE;
|
||||
switch (b_domain) {
|
||||
case BL::Attribute::domain_POINT:
|
||||
switch (b_attr.domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
element = ATTR_ELEMENT_CURVE_KEY;
|
||||
break;
|
||||
case BL::Attribute::domain_CURVE:
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
element = ATTR_ELEMENT_CURVE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
if (element == ATTR_ELEMENT_NONE) {
|
||||
/* Not supported. */
|
||||
continue;
|
||||
}
|
||||
switch (b_data_type) {
|
||||
case BL::Attribute::data_type_FLOAT: {
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
const float *src = static_cast<const float *>(b_float_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) { return src[i]; });
|
||||
break;
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
case BL::Attribute::data_type_BOOLEAN: {
|
||||
BL::BoolAttribute b_bool_attribute{b_attribute};
|
||||
const bool *src = static_cast<const bool *>(b_bool_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(
|
||||
num_curves, num_keys, data, element, [&](int i) { return float(src[i]); });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_INT: {
|
||||
BL::IntAttribute b_int_attribute{b_attribute};
|
||||
const int *src = static_cast<const int *>(b_int_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(
|
||||
num_curves, num_keys, data, element, [&](int i) { return float(src[i]); });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_INT32_2D: {
|
||||
BL::Int2Attribute b_int2_attribute{b_attribute};
|
||||
const int2 *src = static_cast<const int2 *>(b_int2_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat2, element);
|
||||
float2 *data = attr->data_float2();
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return make_float2(float(src[i][0]), float(src[i][1]));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_VECTOR: {
|
||||
BL::FloatVectorAttribute b_vector_attribute{b_attribute};
|
||||
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeVector, element);
|
||||
float3 *data = attr->data_float3();
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return make_float3(src[i][0], src[i][1], src[i][2]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_BYTE_COLOR: {
|
||||
BL::ByteColorAttribute b_color_attribute{b_attribute};
|
||||
const uchar(*src)[4] = static_cast<const uchar(*)[4]>(b_color_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, element);
|
||||
float4 *data = attr->data_float4();
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return make_float4(color_srgb_to_linear(byte_to_float(src[i][0])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][1])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][2])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][3])));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_COLOR: {
|
||||
BL::FloatColorAttribute b_color_attribute{b_attribute};
|
||||
const float(*src)[4] = static_cast<const float(*)[4]>(b_color_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, element);
|
||||
float4 *data = attr->data_float4();
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return make_float4(src[i][0], src[i][1], src[i][2], src[i][3]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT2: {
|
||||
BL::Float2Attribute b_float2_attribute{b_attribute};
|
||||
const float(*src)[2] = static_cast<const float(*)[2]>(b_float2_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat2, element);
|
||||
float2 *data = attr->data_float2();
|
||||
fill_generic_attribute(num_curves, num_keys, data, element, [&](int i) {
|
||||
return make_float2(src[i][0], src[i][1]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Not supported. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static float4 curve_point_as_float4(const float (*b_attr_position)[3],
|
||||
const float *b_attr_radius,
|
||||
static float4 curve_point_as_float4(const blender::Span<blender::float3> b_positions,
|
||||
const blender::Span<float> b_radius,
|
||||
const int index)
|
||||
{
|
||||
float4 mP = make_float4(
|
||||
b_attr_position[index][0], b_attr_position[index][1], b_attr_position[index][2], 0.0f);
|
||||
mP.w = b_attr_radius ? b_attr_radius[index] : 0.005f;
|
||||
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];
|
||||
return mP;
|
||||
}
|
||||
|
||||
static float4 interpolate_curve_points(const float (*b_attr_position)[3],
|
||||
const float *b_attr_radius,
|
||||
static float4 interpolate_curve_points(const blender::Span<blender::float3> b_positions,
|
||||
const blender::Span<float> b_radius,
|
||||
const int first_point_index,
|
||||
const int num_points,
|
||||
const float step)
|
||||
@@ -929,21 +807,21 @@ static float4 interpolate_curve_points(const float (*b_attr_position)[3],
|
||||
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 mix(curve_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_a),
|
||||
curve_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_b),
|
||||
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),
|
||||
t);
|
||||
}
|
||||
|
||||
static void export_hair_curves(Scene *scene,
|
||||
Hair *hair,
|
||||
BL::Curves b_curves,
|
||||
const blender::bke::CurvesGeometry &b_curves,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
const int num_keys = b_curves.points.length();
|
||||
const int num_curves = b_curves.curves.length();
|
||||
const blender::Span<blender::float3> positions = b_curves.positions();
|
||||
const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
|
||||
|
||||
hair->resize_curves(num_curves, num_keys);
|
||||
hair->resize_curves(points_by_curve.size(), positions.size());
|
||||
|
||||
float3 *curve_keys = hair->get_curve_keys().data();
|
||||
float *curve_radius = hair->get_curve_radius().data();
|
||||
@@ -957,11 +835,14 @@ static void export_hair_curves(Scene *scene,
|
||||
if (hair->need_attribute(scene, ATTR_STD_VERTEX_NORMAL)) {
|
||||
/* Get geometry normals. */
|
||||
float3 *attr_normal = hair->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3();
|
||||
int i = 0;
|
||||
for (BL::FloatVectorValueReadOnly &normal : b_curves.normals) {
|
||||
attr_normal[i++] = get_float3(normal.vector());
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
|
||||
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT)->data_float();
|
||||
}
|
||||
@@ -970,42 +851,40 @@ static void export_hair_curves(Scene *scene,
|
||||
}
|
||||
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
|
||||
float *attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM)->data_float();
|
||||
for (int i = 0; i < num_curves; i++) {
|
||||
for (const int i : points_by_curve.index_range()) {
|
||||
attr_random[i] = hash_uint2_to_float(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const int *point_offsets = static_cast<const int *>(b_curves.curve_offset_data[0].ptr.data);
|
||||
const float(*b_attr_position)[3] = find_position_attribute(b_curves);
|
||||
const float *b_attr_radius = find_radius_attribute(b_curves);
|
||||
const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>("radius",
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
std::copy(point_offsets, point_offsets + num_curves, curve_first_key);
|
||||
std::fill(curve_shader, curve_shader + num_curves, 0);
|
||||
if (b_attr_radius) {
|
||||
std::copy(b_attr_radius, b_attr_radius + num_keys, curve_radius);
|
||||
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);
|
||||
}
|
||||
else {
|
||||
std::fill(curve_radius, curve_radius + num_keys, 0.005f);
|
||||
std::fill(curve_radius, curve_radius + positions.size(), 0.005f);
|
||||
}
|
||||
|
||||
/* Export curves and points. */
|
||||
for (int i = 0; i < num_curves; i++) {
|
||||
const int first_point_index = point_offsets[i];
|
||||
const int num_points = point_offsets[i + 1] - first_point_index;
|
||||
for (const int curve : points_by_curve.index_range()) {
|
||||
const blender::IndexRange points = points_by_curve[curve];
|
||||
|
||||
float3 prev_co = zero_float3();
|
||||
float length = 0.0f;
|
||||
|
||||
/* Position and radius. */
|
||||
for (int j = 0; j < num_points; j++) {
|
||||
const int point = first_point_index + j;
|
||||
const float3 co = make_float3(
|
||||
b_attr_position[point][0], b_attr_position[point][1], b_attr_position[point][2]);
|
||||
for (const int point : points) {
|
||||
const float3 co = make_float3(positions[point][0], positions[point][1], positions[point][2]);
|
||||
|
||||
curve_keys[point] = co;
|
||||
|
||||
if (attr_length || attr_intercept) {
|
||||
if (j > 0) {
|
||||
if (point != points.first()) {
|
||||
length += len(co - prev_co);
|
||||
}
|
||||
prev_co = co;
|
||||
@@ -1018,22 +897,23 @@ static void export_hair_curves(Scene *scene,
|
||||
|
||||
/* Normalized 0..1 attribute along curve. */
|
||||
if (attr_intercept && length > 0.0f) {
|
||||
for (int j = 1; j < num_points; j++) {
|
||||
const int point = first_point_index + j;
|
||||
for (const int point : points.drop_front(1)) {
|
||||
attr_intercept[point] /= length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Curve length. */
|
||||
if (attr_length) {
|
||||
attr_length[i] = length;
|
||||
attr_length[curve] = length;
|
||||
}
|
||||
}
|
||||
|
||||
attr_create_generic(scene, hair, b_curves, need_motion, motion_scale);
|
||||
}
|
||||
|
||||
static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motion_step)
|
||||
static void export_hair_curves_motion(Hair *hair,
|
||||
const blender::bke::CurvesGeometry &b_curves,
|
||||
int motion_step)
|
||||
{
|
||||
/* Find or add attribute. */
|
||||
Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
@@ -1046,30 +926,29 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio
|
||||
|
||||
/* Export motion keys. */
|
||||
const int num_keys = hair->get_curve_keys().size();
|
||||
const int num_curves = b_curves.curves.length();
|
||||
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
|
||||
bool have_motion = false;
|
||||
int num_motion_keys = 0;
|
||||
int curve_index = 0;
|
||||
|
||||
const int *point_offsets = static_cast<const int *>(b_curves.curve_offset_data[0].ptr.data);
|
||||
const float(*b_attr_position)[3] = find_position_attribute(b_curves);
|
||||
const float *b_attr_radius = find_radius_attribute(b_curves);
|
||||
const blender::Span<blender::float3> b_positions = b_curves.positions();
|
||||
const blender::OffsetIndices points_by_curve = b_curves.points_by_curve();
|
||||
const blender::VArraySpan b_radius = *b_curves.attributes().lookup<float>("radius",
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
for (int i = 0; i < num_curves; i++) {
|
||||
const int first_point_index = point_offsets[i];
|
||||
const int num_points = point_offsets[i + 1] - first_point_index;
|
||||
for (const int i : points_by_curve.index_range()) {
|
||||
const blender::IndexRange points = points_by_curve[i];
|
||||
|
||||
Hair::Curve curve = hair->get_curve(curve_index);
|
||||
curve_index++;
|
||||
|
||||
if (num_points == curve.num_keys) {
|
||||
if (points.size() == curve.num_keys) {
|
||||
/* Number of keys matches. */
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
int point = first_point_index + i;
|
||||
for (const int i : points.index_range()) {
|
||||
int point = points[i];
|
||||
|
||||
if (point < num_keys) {
|
||||
mP[num_motion_keys] = curve_point_as_float4(b_attr_position, b_attr_radius, point);
|
||||
mP[num_motion_keys] = curve_point_as_float4(b_positions, b_radius, point);
|
||||
num_motion_keys++;
|
||||
|
||||
if (!have_motion) {
|
||||
@@ -1089,7 +968,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio
|
||||
for (int i = 0; i < curve.num_keys; i++) {
|
||||
const float step = i * step_size;
|
||||
mP[num_motion_keys] = interpolate_curve_points(
|
||||
b_attr_position, b_attr_radius, first_point_index, num_points, step);
|
||||
b_positions, b_radius, points.start(), points.size(), step);
|
||||
num_motion_keys++;
|
||||
}
|
||||
have_motion = true;
|
||||
@@ -1113,7 +992,8 @@ void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int
|
||||
0.0f;
|
||||
|
||||
/* Convert Blender hair to Cycles curves. */
|
||||
BL::Curves b_curves(b_ob_info.object_data);
|
||||
const blender::bke::CurvesGeometry &b_curves(
|
||||
static_cast<const ::Curves *>(b_ob_info.object_data.ptr.data)->geometry.wrap());
|
||||
if (motion) {
|
||||
export_hair_curves_motion(hair, b_curves, motion_step);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "blender/attribute_convert.h"
|
||||
#include "blender/session.h"
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
@@ -27,14 +28,16 @@
|
||||
|
||||
#include "mikktspace.hh"
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Tangent Space */
|
||||
|
||||
template<bool is_subd> struct MikkMeshWrapper {
|
||||
MikkMeshWrapper(const BL::Mesh &b_mesh,
|
||||
MikkMeshWrapper(const ::Mesh &b_mesh,
|
||||
const char *layer_name,
|
||||
const Mesh *mesh,
|
||||
float3 *tangent,
|
||||
@@ -52,7 +55,7 @@ template<bool is_subd> struct MikkMeshWrapper {
|
||||
if (attr_orco) {
|
||||
orco = attr_orco->data_float3();
|
||||
float3 orco_size;
|
||||
mesh_texture_space(*(BL::Mesh *)&b_mesh, orco_loc, orco_size);
|
||||
mesh_texture_space(b_mesh, orco_loc, orco_size);
|
||||
inv_orco_size = 1.0f / orco_size;
|
||||
}
|
||||
}
|
||||
@@ -179,7 +182,7 @@ template<bool is_subd> struct MikkMeshWrapper {
|
||||
};
|
||||
|
||||
static void mikk_compute_tangents(
|
||||
const BL::Mesh &b_mesh, const char *layer_name, Mesh *mesh, bool need_sign, bool active_render)
|
||||
const ::Mesh &b_mesh, const char *layer_name, Mesh *mesh, bool need_sign, bool active_render)
|
||||
{
|
||||
/* Create tangent attributes. */
|
||||
const bool is_subd = mesh->get_num_subd_faces();
|
||||
@@ -233,117 +236,10 @@ static void mikk_compute_tangents(
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeInCycles, typename GetValueAtIndex>
|
||||
static void fill_generic_attribute(BL::Mesh &b_mesh,
|
||||
TypeInCycles *data,
|
||||
const BL::Attribute::domain_enum b_domain,
|
||||
const bool subdivision,
|
||||
const GetValueAtIndex &get_value_at_index)
|
||||
static void attr_create_motion(Mesh *mesh,
|
||||
const blender::Span<blender::float3> b_attr,
|
||||
const float motion_scale)
|
||||
{
|
||||
switch (b_domain) {
|
||||
case BL::Attribute::domain_CORNER: {
|
||||
if (subdivision) {
|
||||
const int polys_num = b_mesh.polygons.length();
|
||||
if (polys_num == 0) {
|
||||
return;
|
||||
}
|
||||
const int *face_offsets = static_cast<const int *>(b_mesh.polygons[0].ptr.data);
|
||||
for (int i = 0; i < polys_num; i++) {
|
||||
const int poly_start = face_offsets[i];
|
||||
const int poly_size = face_offsets[i + 1] - poly_start;
|
||||
for (int j = 0; j < poly_size; j++) {
|
||||
*data = get_value_at_index(poly_start + j);
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const int tris_num = b_mesh.loop_triangles.length();
|
||||
const MLoopTri *looptris = static_cast<const MLoopTri *>(
|
||||
b_mesh.loop_triangles[0].ptr.data);
|
||||
for (int i = 0; i < tris_num; i++) {
|
||||
const MLoopTri &tri = looptris[i];
|
||||
data[i * 3 + 0] = get_value_at_index(tri.tri[0]);
|
||||
data[i * 3 + 1] = get_value_at_index(tri.tri[1]);
|
||||
data[i * 3 + 2] = get_value_at_index(tri.tri[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::domain_EDGE: {
|
||||
const size_t edges_num = b_mesh.edges.length();
|
||||
if (edges_num == 0) {
|
||||
return;
|
||||
}
|
||||
if constexpr (std::is_same_v<TypeInCycles, uchar4>) {
|
||||
/* uchar4 edge attributes do not exist, and averaging in place
|
||||
* would not work. */
|
||||
assert(0);
|
||||
}
|
||||
else {
|
||||
const int2 *edges = static_cast<const int2 *>(b_mesh.edges[0].ptr.data);
|
||||
const size_t verts_num = b_mesh.vertices.length();
|
||||
vector<int> count(verts_num, 0);
|
||||
|
||||
/* Average edge attributes at vertices. */
|
||||
for (int i = 0; i < edges_num; i++) {
|
||||
TypeInCycles value = get_value_at_index(i);
|
||||
|
||||
const int2 &b_edge = edges[i];
|
||||
data[b_edge[0]] += value;
|
||||
data[b_edge[1]] += value;
|
||||
count[b_edge[0]]++;
|
||||
count[b_edge[1]]++;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < verts_num; i++) {
|
||||
if (count[i] > 1) {
|
||||
data[i] /= (float)count[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::domain_POINT: {
|
||||
const int num_verts = b_mesh.vertices.length();
|
||||
for (int i = 0; i < num_verts; i++) {
|
||||
data[i] = get_value_at_index(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::domain_FACE: {
|
||||
if (subdivision) {
|
||||
const int num_polygons = b_mesh.polygons.length();
|
||||
for (int i = 0; i < num_polygons; i++) {
|
||||
data[i] = get_value_at_index(i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const int tris_num = b_mesh.loop_triangles.length();
|
||||
const int *looptri_faces = static_cast<const int *>(
|
||||
b_mesh.loop_triangle_polygons[0].ptr.data);
|
||||
for (int i = 0; i < tris_num; i++) {
|
||||
data[i] = get_value_at_index(looptri_faces[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_create_motion(Mesh *mesh, BL::Attribute &b_attribute, const float motion_scale)
|
||||
{
|
||||
if (!(b_attribute.domain() == BL::Attribute::domain_POINT) &&
|
||||
(b_attribute.data_type() == BL::Attribute::data_type_FLOAT_VECTOR))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BL::FloatVectorAttribute b_vector_attribute(b_attribute);
|
||||
const int numverts = mesh->get_verts().size();
|
||||
|
||||
/* Find or add attribute */
|
||||
@@ -361,209 +257,193 @@ static void attr_create_motion(Mesh *mesh, BL::Attribute &b_attribute, const flo
|
||||
float3 *mP = attr_mP->data_float3() + step * numverts;
|
||||
|
||||
for (int i = 0; i < numverts; i++) {
|
||||
mP[i] = P[i] + get_float3(b_vector_attribute.data[i].vector()) * relative_time;
|
||||
mP[i] = P[i] + make_float3(b_attr[i][0], b_attr[i][1], b_attr[i][2]) * relative_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_create_generic(Scene *scene,
|
||||
Mesh *mesh,
|
||||
BL::Mesh &b_mesh,
|
||||
const ::Mesh &b_mesh,
|
||||
const bool subdivision,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
blender::Span<MLoopTri> looptris;
|
||||
blender::Span<int> looptri_faces;
|
||||
if (!subdivision) {
|
||||
looptris = b_mesh.looptris();
|
||||
looptri_faces = b_mesh.looptri_faces();
|
||||
}
|
||||
const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
|
||||
AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
|
||||
static const ustring u_velocity("velocity");
|
||||
const ustring default_color_name{b_mesh.attributes.default_color_name().c_str()};
|
||||
const ustring default_color_name{BKE_id_attributes_default_color_name(&b_mesh.id)};
|
||||
|
||||
for (BL::Attribute &b_attribute : b_mesh.attributes) {
|
||||
const ustring name{b_attribute.name().c_str()};
|
||||
b_attributes.for_all([&](const blender::bke::AttributeIDRef &id,
|
||||
const blender::bke::AttributeMetaData meta_data) {
|
||||
const ustring name{std::string_view(id.name())};
|
||||
const bool is_render_color = name == default_color_name;
|
||||
|
||||
if (need_motion && name == u_velocity) {
|
||||
const blender::VArraySpan b_attribute = *b_attributes.lookup<blender::float3>(
|
||||
id, ATTR_DOMAIN_POINT);
|
||||
attr_create_motion(mesh, b_attribute, motion_scale);
|
||||
}
|
||||
|
||||
if (!(mesh->need_attribute(scene, name) ||
|
||||
(is_render_color && mesh->need_attribute(scene, ATTR_STD_VERTEX_COLOR))))
|
||||
{
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
if (attributes.find(name)) {
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
const BL::Attribute::domain_enum b_domain = b_attribute.domain();
|
||||
const BL::Attribute::data_type_enum b_data_type = b_attribute.data_type();
|
||||
eAttrDomain b_domain = meta_data.domain;
|
||||
if (b_domain == ATTR_DOMAIN_EDGE) {
|
||||
/* Blender's attribute API handles edge to vertex attribute domain interpolation. */
|
||||
b_domain = ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
||||
const blender::bke::GAttributeReader b_attr = b_attributes.lookup(id, b_domain);
|
||||
if (b_attr.varray.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (b_attr.domain == ATTR_DOMAIN_CORNER && meta_data.data_type == CD_PROP_BYTE_COLOR) {
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE);
|
||||
if (is_render_color) {
|
||||
attr->std = ATTR_STD_VERTEX_COLOR;
|
||||
}
|
||||
|
||||
uchar4 *data = attr->data_uchar4();
|
||||
const blender::VArraySpan src = b_attr.varray.typed<blender::ColorGeometry4b>();
|
||||
if (subdivision) {
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = make_uchar4(src[i][0], src[i][1], src[i][2], src[i][3]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : looptris.index_range()) {
|
||||
const MLoopTri &tri = looptris[i];
|
||||
data[i * 3 + 0] = make_uchar4(
|
||||
src[tri.tri[0]][0], src[tri.tri[0]][1], src[tri.tri[0]][2], src[tri.tri[0]][3]);
|
||||
data[i * 3 + 1] = make_uchar4(
|
||||
src[tri.tri[1]][0], src[tri.tri[1]][1], src[tri.tri[1]][2], src[tri.tri[1]][3]);
|
||||
data[i * 3 + 2] = make_uchar4(
|
||||
src[tri.tri[2]][0], src[tri.tri[2]][1], src[tri.tri[2]][2], src[tri.tri[2]][3]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
AttributeElement element = ATTR_ELEMENT_NONE;
|
||||
switch (b_domain) {
|
||||
case BL::Attribute::domain_CORNER:
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
element = ATTR_ELEMENT_CORNER;
|
||||
break;
|
||||
case BL::Attribute::domain_POINT:
|
||||
case ATTR_DOMAIN_POINT:
|
||||
element = ATTR_ELEMENT_VERTEX;
|
||||
break;
|
||||
case BL::Attribute::domain_EDGE:
|
||||
element = ATTR_ELEMENT_VERTEX;
|
||||
break;
|
||||
case BL::Attribute::domain_FACE:
|
||||
case ATTR_DOMAIN_FACE:
|
||||
element = ATTR_ELEMENT_FACE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
if (element == ATTR_ELEMENT_NONE) {
|
||||
/* Not supported. */
|
||||
continue;
|
||||
}
|
||||
switch (b_data_type) {
|
||||
case BL::Attribute::data_type_FLOAT: {
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
if (b_float_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const float *src = static_cast<const float *>(b_float_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) { return src[i]; });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_BOOLEAN: {
|
||||
BL::BoolAttribute b_bool_attribute{b_attribute};
|
||||
if (b_bool_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const bool *src = static_cast<const bool *>(b_bool_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(
|
||||
b_mesh, data, b_domain, subdivision, [&](int i) { return (float)src[i]; });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_INT: {
|
||||
BL::IntAttribute b_int_attribute{b_attribute};
|
||||
if (b_int_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const int *src = static_cast<const int *>(b_int_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(
|
||||
b_mesh, data, b_domain, subdivision, [&](int i) { return (float)src[i]; });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_VECTOR: {
|
||||
BL::FloatVectorAttribute b_vector_attribute{b_attribute};
|
||||
if (b_vector_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeVector, element);
|
||||
float3 *data = attr->data_float3();
|
||||
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) {
|
||||
return make_float3(src[i][0], src[i][1], src[i][2]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_BYTE_COLOR: {
|
||||
BL::ByteColorAttribute b_color_attribute{b_attribute};
|
||||
if (b_color_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const uchar(*src)[4] = static_cast<const uchar(*)[4]>(b_color_attribute.data[0].ptr.data);
|
||||
|
||||
if (element == ATTR_ELEMENT_CORNER) {
|
||||
element = ATTR_ELEMENT_CORNER_BYTE;
|
||||
}
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, element);
|
||||
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);
|
||||
if (is_render_color) {
|
||||
attr->std = ATTR_STD_VERTEX_COLOR;
|
||||
}
|
||||
|
||||
if (element == ATTR_ELEMENT_CORNER_BYTE) {
|
||||
uchar4 *data = attr->data_uchar4();
|
||||
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) {
|
||||
/* Compress/encode vertex color using the sRGB curve. */
|
||||
return make_uchar4(src[i][0], src[i][1], src[i][2], src[i][3]);
|
||||
});
|
||||
}
|
||||
else {
|
||||
float4 *data = attr->data_float4();
|
||||
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) {
|
||||
return make_float4(color_srgb_to_linear(byte_to_float(src[i][0])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][1])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][2])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][3])));
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_COLOR: {
|
||||
BL::FloatColorAttribute b_color_attribute{b_attribute};
|
||||
if (b_color_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const float(*src)[4] = static_cast<const float(*)[4]>(b_color_attribute.data[0].ptr.data);
|
||||
CyclesT *data = reinterpret_cast<CyclesT *>(attr->data());
|
||||
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, element);
|
||||
if (is_render_color) {
|
||||
attr->std = ATTR_STD_VERTEX_COLOR;
|
||||
const blender::VArraySpan src = b_attr.varray.typed<BlenderT>();
|
||||
switch (b_attr.domain) {
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
if (subdivision) {
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : looptris.index_range()) {
|
||||
const MLoopTri &tri = looptris[i];
|
||||
data[i * 3 + 0] = Converter::convert(src[tri.tri[0]]);
|
||||
data[i * 3 + 1] = Converter::convert(src[tri.tri[1]]);
|
||||
data[i * 3 + 2] = Converter::convert(src[tri.tri[2]]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
if (subdivision) {
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : looptris.index_range()) {
|
||||
data[i] = Converter::convert(src[looptri_faces[i]]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
float4 *data = attr->data_float4();
|
||||
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) {
|
||||
return make_float4(src[i][0], src[i][1], src[i][2], src[i][3]);
|
||||
});
|
||||
break;
|
||||
static set<ustring> get_blender_uv_names(const ::Mesh &b_mesh)
|
||||
{
|
||||
set<ustring> uv_names;
|
||||
b_mesh.attributes().for_all([&](const blender::bke::AttributeIDRef &id,
|
||||
const blender::bke::AttributeMetaData meta_data) {
|
||||
if (meta_data.domain == ATTR_DOMAIN_CORNER && meta_data.data_type == CD_PROP_FLOAT2) {
|
||||
if (!id.is_anonymous()) {
|
||||
uv_names.emplace(std::string_view(id.name()));
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT2: {
|
||||
BL::Float2Attribute b_float2_attribute{b_attribute};
|
||||
if (b_float2_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const float(*src)[2] = static_cast<const float(*)[2]>(b_float2_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat2, element);
|
||||
float2 *data = attr->data_float2();
|
||||
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) {
|
||||
return make_float2(src[i][0], src[i][1]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_INT32_2D: {
|
||||
BL::Int2Attribute b_int2_attribute{b_attribute};
|
||||
if (b_int2_attribute.data.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
const int2 *src = static_cast<const int2 *>(b_int2_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat2, element);
|
||||
float2 *data = attr->data_float2();
|
||||
fill_generic_attribute(b_mesh, data, b_domain, subdivision, [&](int i) {
|
||||
return make_float2(float(src[i][0]), float(src[i][1]));
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Not supported. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return uv_names;
|
||||
}
|
||||
|
||||
/* Create uv map attributes. */
|
||||
static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh)
|
||||
static void attr_create_uv_map(Scene *scene,
|
||||
Mesh *mesh,
|
||||
const ::Mesh &b_mesh,
|
||||
const set<ustring> &blender_uv_names)
|
||||
{
|
||||
if (!b_mesh.uv_layers.empty()) {
|
||||
const int tris_num = b_mesh.loop_triangles.length();
|
||||
const MLoopTri *looptris = static_cast<const MLoopTri *>(b_mesh.loop_triangles[0].ptr.data);
|
||||
|
||||
for (BL::MeshUVLoopLayer &l : b_mesh.uv_layers) {
|
||||
const bool active_render = l.active_render();
|
||||
const blender::Span<MLoopTri> looptris = b_mesh.looptris();
|
||||
const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
|
||||
const ustring render_name(CustomData_get_render_layer_name(&b_mesh.loop_data, CD_PROP_FLOAT2));
|
||||
if (!blender_uv_names.empty()) {
|
||||
for (const ustring &uv_name : blender_uv_names) {
|
||||
const bool active_render = uv_name == render_name;
|
||||
AttributeStandard uv_std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
|
||||
ustring uv_name = ustring(l.name().c_str());
|
||||
AttributeStandard tangent_std = (active_render) ? ATTR_STD_UV_TANGENT : ATTR_STD_NONE;
|
||||
ustring tangent_name = ustring((string(l.name().c_str()) + ".tangent").c_str());
|
||||
ustring tangent_name = ustring((string(uv_name) + ".tangent").c_str());
|
||||
|
||||
/* Denotes whether UV map was requested directly. */
|
||||
const bool need_uv = mesh->need_attribute(scene, uv_name) ||
|
||||
@@ -585,9 +465,10 @@ static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh)
|
||||
uv_attr = mesh->attributes.add(uv_name, TypeFloat2, ATTR_ELEMENT_CORNER);
|
||||
}
|
||||
|
||||
const float(*b_uv_map)[2] = static_cast<const float(*)[2]>(l.uv[0].ptr.data);
|
||||
const blender::VArraySpan b_uv_map = *b_attributes.lookup<blender::float2>(
|
||||
uv_name.c_str(), ATTR_DOMAIN_CORNER);
|
||||
float2 *fdata = uv_attr->data_float2();
|
||||
for (int i = 0; i < tris_num; i++) {
|
||||
for (const int i : looptris.index_range()) {
|
||||
const MLoopTri &tri = looptris[i];
|
||||
fdata[i * 3 + 0] = make_float2(b_uv_map[tri.tri[0]][0], b_uv_map[tri.tri[0]][1]);
|
||||
fdata[i * 3 + 1] = make_float2(b_uv_map[tri.tri[1]][0], b_uv_map[tri.tri[1]][1]);
|
||||
@@ -598,10 +479,10 @@ static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh)
|
||||
/* UV tangent */
|
||||
if (need_tangent) {
|
||||
AttributeStandard sign_std = (active_render) ? ATTR_STD_UV_TANGENT_SIGN : ATTR_STD_NONE;
|
||||
ustring sign_name = ustring((string(l.name().c_str()) + ".tangent_sign").c_str());
|
||||
ustring sign_name = ustring((string(uv_name) + ".tangent_sign").c_str());
|
||||
bool need_sign = (mesh->need_attribute(scene, sign_name) ||
|
||||
mesh->need_attribute(scene, sign_std));
|
||||
mikk_compute_tangents(b_mesh, l.name().c_str(), mesh, need_sign, active_render);
|
||||
mikk_compute_tangents(b_mesh, uv_name.c_str(), mesh, need_sign, active_render);
|
||||
}
|
||||
/* Remove temporarily created UV attribute. */
|
||||
if (!need_uv && uv_attr != NULL) {
|
||||
@@ -618,24 +499,25 @@ static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh)
|
||||
}
|
||||
}
|
||||
|
||||
static void attr_create_subd_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivide_uvs)
|
||||
static void attr_create_subd_uv_map(Scene *scene,
|
||||
Mesh *mesh,
|
||||
const ::Mesh &b_mesh,
|
||||
bool subdivide_uvs,
|
||||
const set<ustring> &blender_uv_names)
|
||||
{
|
||||
const int polys_num = b_mesh.polygons.length();
|
||||
if (polys_num == 0) {
|
||||
const blender::OffsetIndices faces = b_mesh.faces();
|
||||
if (faces.is_empty()) {
|
||||
return;
|
||||
}
|
||||
const int *face_offsets = static_cast<const int *>(b_mesh.polygons[0].ptr.data);
|
||||
|
||||
if (!b_mesh.uv_layers.empty()) {
|
||||
BL::Mesh::uv_layers_iterator l;
|
||||
int i = 0;
|
||||
|
||||
for (b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l, ++i) {
|
||||
bool active_render = l->active_render();
|
||||
if (!blender_uv_names.empty()) {
|
||||
const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
|
||||
const ustring render_name(CustomData_get_render_layer_name(&b_mesh.loop_data, CD_PROP_FLOAT2));
|
||||
for (const ustring &uv_name : blender_uv_names) {
|
||||
const bool active_render = uv_name == render_name;
|
||||
AttributeStandard uv_std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
|
||||
ustring uv_name = ustring(l->name().c_str());
|
||||
AttributeStandard tangent_std = (active_render) ? ATTR_STD_UV_TANGENT : ATTR_STD_NONE;
|
||||
ustring tangent_name = ustring((string(l->name().c_str()) + ".tangent").c_str());
|
||||
ustring tangent_name = ustring((string(uv_name) + ".tangent").c_str());
|
||||
|
||||
/* Denotes whether UV map was requested directly. */
|
||||
const bool need_uv = mesh->need_attribute(scene, uv_name) ||
|
||||
@@ -659,13 +541,14 @@ static void attr_create_subd_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh,
|
||||
uv_attr->flags |= ATTR_SUBDIVIDED;
|
||||
}
|
||||
|
||||
const blender::VArraySpan b_uv_map = *b_attributes.lookup<blender::float2>(
|
||||
uv_name.c_str(), ATTR_DOMAIN_CORNER);
|
||||
float2 *fdata = uv_attr->data_float2();
|
||||
|
||||
for (int i = 0; i < polys_num; i++) {
|
||||
const int poly_start = face_offsets[i];
|
||||
const int poly_size = face_offsets[i + 1] - poly_start;
|
||||
for (int j = 0; j < poly_size; j++) {
|
||||
*(fdata++) = get_float2(l->data[poly_start + j].uv());
|
||||
for (const int i : faces.index_range()) {
|
||||
const blender::IndexRange face = faces[i];
|
||||
for (const int corner : face) {
|
||||
*(fdata++) = make_float2(b_uv_map[corner][0], b_uv_map[corner][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -673,10 +556,10 @@ static void attr_create_subd_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh,
|
||||
/* UV tangent */
|
||||
if (need_tangent) {
|
||||
AttributeStandard sign_std = (active_render) ? ATTR_STD_UV_TANGENT_SIGN : ATTR_STD_NONE;
|
||||
ustring sign_name = ustring((string(l->name().c_str()) + ".tangent_sign").c_str());
|
||||
ustring sign_name = ustring((string(uv_name) + ".tangent_sign").c_str());
|
||||
bool need_sign = (mesh->need_attribute(scene, sign_name) ||
|
||||
mesh->need_attribute(scene, sign_std));
|
||||
mikk_compute_tangents(b_mesh, l->name().c_str(), mesh, need_sign, active_render);
|
||||
mikk_compute_tangents(b_mesh, uv_name.c_str(), mesh, need_sign, active_render);
|
||||
}
|
||||
/* Remove temporarily created UV attribute. */
|
||||
if (!need_uv && uv_attr != NULL) {
|
||||
@@ -717,16 +600,16 @@ class VertexAverageComparator {
|
||||
const array<float3> &verts_;
|
||||
};
|
||||
|
||||
static void attr_create_pointiness(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivision)
|
||||
static void attr_create_pointiness(Mesh *mesh,
|
||||
const blender::Span<blender::float3> positions,
|
||||
const blender::Span<blender::float3> b_vert_normals,
|
||||
const blender::Span<blender::int2> edges,
|
||||
bool subdivision)
|
||||
{
|
||||
if (!mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
|
||||
const int num_verts = positions.size();
|
||||
if (positions.is_empty()) {
|
||||
return;
|
||||
}
|
||||
const int num_verts = b_mesh.vertices.length();
|
||||
if (num_verts == 0) {
|
||||
return;
|
||||
}
|
||||
const float(*positions)[3] = static_cast<const float(*)[3]>(b_mesh.vertices[0].ptr.data);
|
||||
|
||||
/* STEP 1: Find out duplicated vertices and point duplicates to a single
|
||||
* original vertex.
|
||||
@@ -782,8 +665,6 @@ static void attr_create_pointiness(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, b
|
||||
*/
|
||||
vector<float3> vert_normal(num_verts, zero_float3());
|
||||
/* First we accumulate all vertex normals in the original index. */
|
||||
const float(*b_vert_normals)[3] = static_cast<const float(*)[3]>(
|
||||
b_mesh.vertex_normals[0].ptr.data);
|
||||
for (int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
const float *b_vert_normal = b_vert_normals[vert_index];
|
||||
const int orig_index = vert_orig_index[vert_index];
|
||||
@@ -803,11 +684,8 @@ static void attr_create_pointiness(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, b
|
||||
EdgeMap visited_edges;
|
||||
memset(&counter[0], 0, sizeof(int) * counter.size());
|
||||
|
||||
const int2 *edges = static_cast<int2 *>(b_mesh.edges[0].ptr.data);
|
||||
const int edges_num = b_mesh.edges.length();
|
||||
|
||||
for (int i = 0; i < edges_num; i++) {
|
||||
const int2 &b_edge = edges[i];
|
||||
for (const int i : edges.index_range()) {
|
||||
const blender::int2 b_edge = edges[i];
|
||||
const int v0 = vert_orig_index[b_edge[0]];
|
||||
const int v1 = vert_orig_index[b_edge[1]];
|
||||
if (visited_edges.exists(v0, v1)) {
|
||||
@@ -844,8 +722,8 @@ static void attr_create_pointiness(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, b
|
||||
memcpy(data, &raw_data[0], sizeof(float) * raw_data.size());
|
||||
memset(&counter[0], 0, sizeof(int) * counter.size());
|
||||
visited_edges.clear();
|
||||
for (int i = 0; i < edges_num; i++) {
|
||||
const int2 &b_edge = edges[i];
|
||||
for (const int i : edges.index_range()) {
|
||||
const blender::int2 b_edge = edges[i];
|
||||
const int v0 = vert_orig_index[b_edge[0]];
|
||||
const int v1 = vert_orig_index[b_edge[1]];
|
||||
if (visited_edges.exists(v0, v1)) {
|
||||
@@ -867,27 +745,6 @@ static void attr_create_pointiness(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, b
|
||||
}
|
||||
}
|
||||
|
||||
static const int *find_corner_vert_attribute(BL::Mesh b_mesh)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_mesh.attributes) {
|
||||
if (b_attribute.domain() != BL::Attribute::domain_CORNER) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_INT) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.name() != ".corner_vert") {
|
||||
continue;
|
||||
}
|
||||
BL::IntAttribute b_int_attribute{b_attribute};
|
||||
if (b_int_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const int *>(b_int_attribute.data[0].ptr.data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* The Random Per Island attribute is a random float associated with each
|
||||
* connected component (island) of the mesh. The attribute is computed by
|
||||
* first classifying the vertices into different sets using a Disjoint Set
|
||||
@@ -900,25 +757,23 @@ static const int *find_corner_vert_attribute(BL::Mesh b_mesh)
|
||||
* making the output unsafe to hash. */
|
||||
static void attr_create_random_per_island(Scene *scene,
|
||||
Mesh *mesh,
|
||||
BL::Mesh &b_mesh,
|
||||
const ::Mesh &b_mesh,
|
||||
bool subdivision)
|
||||
{
|
||||
if (!mesh->need_attribute(scene, ATTR_STD_RANDOM_PER_ISLAND)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int number_of_vertices = b_mesh.vertices.length();
|
||||
if (number_of_vertices == 0) {
|
||||
if (b_mesh.totvert == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisjointSet vertices_sets(number_of_vertices);
|
||||
DisjointSet vertices_sets(b_mesh.totvert);
|
||||
|
||||
const int2 *edges = static_cast<int2 *>(b_mesh.edges[0].ptr.data);
|
||||
const int edges_num = b_mesh.edges.length();
|
||||
const int *corner_verts = find_corner_vert_attribute(b_mesh);
|
||||
const blender::Span<blender::int2> edges = b_mesh.edges();
|
||||
const blender::Span<int> corner_verts = b_mesh.corner_verts();
|
||||
|
||||
for (int i = 0; i < edges_num; i++) {
|
||||
for (const int i : edges.index_range()) {
|
||||
vertices_sets.join(edges[i][0], edges[i][1]);
|
||||
}
|
||||
|
||||
@@ -927,21 +782,19 @@ static void attr_create_random_per_island(Scene *scene,
|
||||
float *data = attribute->data_float();
|
||||
|
||||
if (!subdivision) {
|
||||
const int tris_num = b_mesh.loop_triangles.length();
|
||||
if (tris_num != 0) {
|
||||
const MLoopTri *looptris = static_cast<const MLoopTri *>(b_mesh.loop_triangles[0].ptr.data);
|
||||
for (int i = 0; i < tris_num; i++) {
|
||||
const blender::Span<MLoopTri> looptris = b_mesh.looptris();
|
||||
if (!looptris.is_empty()) {
|
||||
for (const int i : looptris.index_range()) {
|
||||
const int vert = corner_verts[looptris[i].tri[0]];
|
||||
data[i] = hash_uint_to_float(vertices_sets.find(vert));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const int polys_num = b_mesh.polygons.length();
|
||||
if (polys_num != 0) {
|
||||
const int *face_offsets = static_cast<const int *>(b_mesh.polygons[0].ptr.data);
|
||||
for (int i = 0; i < polys_num; i++) {
|
||||
const int vert = corner_verts[face_offsets[i]];
|
||||
const blender::OffsetIndices<int> faces = b_mesh.faces();
|
||||
if (!faces.is_empty()) {
|
||||
for (const int i : faces.index_range()) {
|
||||
const int vert = corner_verts[faces[i].start()];
|
||||
data[i] = hash_uint_to_float(vertices_sets.find(vert));
|
||||
}
|
||||
}
|
||||
@@ -950,118 +803,38 @@ static void attr_create_random_per_island(Scene *scene,
|
||||
|
||||
/* Create Mesh */
|
||||
|
||||
static const int *find_material_index_attribute(BL::Mesh b_mesh)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_mesh.attributes) {
|
||||
if (b_attribute.domain() != BL::Attribute::domain_FACE) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_INT) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.name() != "material_index") {
|
||||
continue;
|
||||
}
|
||||
BL::IntAttribute b_int_attribute{b_attribute};
|
||||
if (b_int_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const int *>(b_int_attribute.data[0].ptr.data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const bool *find_sharp_face_attribute(BL::Mesh b_mesh)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_mesh.attributes) {
|
||||
if (b_attribute.domain() != BL::Attribute::domain_FACE) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_BOOLEAN) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.name() != "sharp_face") {
|
||||
continue;
|
||||
}
|
||||
BL::IntAttribute b_int_attribute{b_attribute};
|
||||
if (b_int_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const bool *>(b_int_attribute.data[0].ptr.data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const float *find_edge_crease_attribute(BL::Mesh b_mesh)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_mesh.attributes) {
|
||||
if (b_attribute.domain() != BL::Attribute::domain_EDGE) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.name() != "crease_edge") {
|
||||
continue;
|
||||
}
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
if (b_float_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const float *>(b_float_attribute.data[0].ptr.data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const float *find_vert_crease_attribute(BL::Mesh b_mesh)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_mesh.attributes) {
|
||||
if (b_attribute.domain() != BL::Attribute::domain_POINT) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.name() != "crease_vert") {
|
||||
continue;
|
||||
}
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
if (b_float_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const float *>(b_float_attribute.data[0].ptr.data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void create_mesh(Scene *scene,
|
||||
Mesh *mesh,
|
||||
BL::Mesh &b_mesh,
|
||||
const ::Mesh &b_mesh,
|
||||
const array<Node *> &used_shaders,
|
||||
const bool need_motion,
|
||||
const float motion_scale,
|
||||
const bool subdivision = false,
|
||||
const bool subdivide_uvs = true)
|
||||
{
|
||||
const int numverts = b_mesh.vertices.length();
|
||||
const int polys_num = b_mesh.polygons.length();
|
||||
int numfaces = (!subdivision) ? b_mesh.loop_triangles.length() : b_mesh.polygons.length();
|
||||
const int numcorners = b_mesh.loops.length();
|
||||
bool use_loop_normals = b_mesh.use_auto_smooth() &&
|
||||
const blender::Span<blender::float3> positions = b_mesh.vert_positions();
|
||||
const blender::OffsetIndices faces = b_mesh.faces();
|
||||
const blender::Span<int> corner_verts = b_mesh.corner_verts();
|
||||
const blender::bke::AttributeAccessor b_attributes = b_mesh.attributes();
|
||||
int numfaces = (!subdivision) ? b_mesh.looptris().size() : faces.size();
|
||||
|
||||
bool use_loop_normals = (b_mesh.flag & ME_AUTOSMOOTH) &&
|
||||
(mesh->get_subdivision_type() != Mesh::SUBDIVISION_CATMULL_CLARK);
|
||||
|
||||
/* If no faces, create empty mesh. */
|
||||
if (numfaces == 0) {
|
||||
if (faces.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float(*positions)[3] = static_cast<const float(*)[3]>(b_mesh.vertices[0].ptr.data);
|
||||
const int *corner_verts = find_corner_vert_attribute(b_mesh);
|
||||
const int *material_indices = find_material_index_attribute(b_mesh);
|
||||
const bool *sharp_faces = find_sharp_face_attribute(b_mesh);
|
||||
const float(*corner_normals)[3] = nullptr;
|
||||
const blender::VArraySpan material_indices = *b_attributes.lookup<int>("material_index",
|
||||
ATTR_DOMAIN_FACE);
|
||||
const blender::VArraySpan sharp_faces = *b_attributes.lookup<bool>("sharp_face",
|
||||
ATTR_DOMAIN_FACE);
|
||||
blender::Span<blender::float3> corner_normals;
|
||||
if (use_loop_normals) {
|
||||
corner_normals = static_cast<const float(*)[3]>(b_mesh.corner_normals[0].ptr.data);
|
||||
corner_normals = {
|
||||
static_cast<const blender::float3 *>(CustomData_get_layer(&b_mesh.loop_data, CD_NORMAL)),
|
||||
corner_verts.size()};
|
||||
}
|
||||
|
||||
int numngons = 0;
|
||||
@@ -1070,22 +843,20 @@ static void create_mesh(Scene *scene,
|
||||
numtris = numfaces;
|
||||
}
|
||||
else {
|
||||
const int *face_offsets = static_cast<const int *>(b_mesh.polygons[0].ptr.data);
|
||||
for (int i = 0; i < polys_num; i++) {
|
||||
const int poly_start = face_offsets[i];
|
||||
const int poly_size = face_offsets[i + 1] - poly_start;
|
||||
numngons += (poly_size == 4) ? 0 : 1;
|
||||
const blender::OffsetIndices faces = b_mesh.faces();
|
||||
for (const int i : faces.index_range()) {
|
||||
numngons += (faces[i].size() == 4) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate memory */
|
||||
if (subdivision) {
|
||||
mesh->resize_subd_faces(numfaces, numngons, numcorners);
|
||||
mesh->resize_subd_faces(numfaces, numngons, corner_verts.size());
|
||||
}
|
||||
mesh->resize_mesh(numverts, numtris);
|
||||
mesh->resize_mesh(positions.size(), numtris);
|
||||
|
||||
float3 *verts = mesh->get_verts().data();
|
||||
for (int i = 0; i < numverts; i++) {
|
||||
for (const int i : positions.index_range()) {
|
||||
verts[i] = make_float3(positions[i][0], positions[i][1], positions[i][2]);
|
||||
}
|
||||
|
||||
@@ -1093,31 +864,43 @@ static void create_mesh(Scene *scene,
|
||||
Attribute *attr_N = attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
float3 *N = attr_N->data_float3();
|
||||
|
||||
if (subdivision || !(use_loop_normals && corner_normals)) {
|
||||
const float(*b_vert_normals)[3] = static_cast<const float(*)[3]>(
|
||||
b_mesh.vertex_normals[0].ptr.data);
|
||||
for (int i = 0; i < numverts; i++) {
|
||||
const float *b_vert_normal = b_vert_normals[i];
|
||||
N[i] = make_float3(b_vert_normal[0], b_vert_normal[1], b_vert_normal[2]);
|
||||
if (subdivision || !(use_loop_normals && !corner_normals.is_empty())) {
|
||||
const blender::Span<blender::float3> vert_normals = b_mesh.vert_normals();
|
||||
for (const int i : vert_normals.index_range()) {
|
||||
N[i] = make_float3(vert_normals[i][0], vert_normals[i][1], vert_normals[i][2]);
|
||||
}
|
||||
}
|
||||
|
||||
const set<ustring> blender_uv_names = get_blender_uv_names(b_mesh);
|
||||
|
||||
/* create generated coordinates from undeformed coordinates */
|
||||
const bool need_default_tangent = (subdivision == false) && (b_mesh.uv_layers.empty()) &&
|
||||
const bool need_default_tangent = (subdivision == false) && (blender_uv_names.empty()) &&
|
||||
(mesh->need_attribute(scene, ATTR_STD_UV_TANGENT));
|
||||
if (mesh->need_attribute(scene, ATTR_STD_GENERATED) || need_default_tangent) {
|
||||
const float(*orco)[3] = static_cast<const float(*)[3]>(
|
||||
CustomData_get_layer(&b_mesh.vert_data, CD_ORCO));
|
||||
Attribute *attr = attributes.add(ATTR_STD_GENERATED);
|
||||
attr->flags |= ATTR_SUBDIVIDED;
|
||||
|
||||
float3 loc, size;
|
||||
mesh_texture_space(b_mesh, loc, size);
|
||||
|
||||
float3 *generated = attr->data_float3();
|
||||
size_t i = 0;
|
||||
float texspace_location[3], texspace_size[3];
|
||||
BKE_mesh_texspace_get(const_cast<::Mesh *>(b_mesh.texcomesh ? b_mesh.texcomesh : &b_mesh),
|
||||
texspace_location,
|
||||
texspace_size);
|
||||
|
||||
BL::Mesh::vertices_iterator v;
|
||||
for (b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v) {
|
||||
generated[i++] = get_float3(v->undeformed_co()) * size - loc;
|
||||
float3 *generated = attr->data_float3();
|
||||
|
||||
for (const int i : positions.index_range()) {
|
||||
blender::float3 value;
|
||||
if (orco) {
|
||||
madd_v3_v3v3v3(value, texspace_location, orco[i], texspace_size);
|
||||
}
|
||||
else {
|
||||
value = positions[i];
|
||||
}
|
||||
generated[i] = make_float3(value[0], value[1], value[2]) * size - loc;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,18 +914,17 @@ static void create_mesh(Scene *scene,
|
||||
bool *smooth = mesh->get_smooth().data();
|
||||
int *shader = mesh->get_shader().data();
|
||||
|
||||
const MLoopTri *looptris = static_cast<const MLoopTri *>(b_mesh.loop_triangles[0].ptr.data);
|
||||
for (int i = 0; i < numtris; i++) {
|
||||
const blender::Span<MLoopTri> looptris = b_mesh.looptris();
|
||||
for (const int i : looptris.index_range()) {
|
||||
const MLoopTri &tri = looptris[i];
|
||||
triangles[i * 3 + 0] = corner_verts[tri.tri[0]];
|
||||
triangles[i * 3 + 1] = corner_verts[tri.tri[1]];
|
||||
triangles[i * 3 + 2] = corner_verts[tri.tri[2]];
|
||||
}
|
||||
|
||||
if (material_indices) {
|
||||
const int *looptri_faces = static_cast<const int *>(
|
||||
b_mesh.loop_triangle_polygons[0].ptr.data);
|
||||
for (int i = 0; i < numtris; i++) {
|
||||
if (!material_indices.is_empty()) {
|
||||
const blender::Span<int> looptri_faces = b_mesh.looptri_faces();
|
||||
for (const int i : looptris.index_range()) {
|
||||
shader[i] = clamp_material_index(material_indices[looptri_faces[i]]);
|
||||
}
|
||||
}
|
||||
@@ -1150,10 +932,9 @@ static void create_mesh(Scene *scene,
|
||||
std::fill(shader, shader + numtris, 0);
|
||||
}
|
||||
|
||||
if (sharp_faces && !(use_loop_normals && corner_normals)) {
|
||||
const int *looptri_faces = static_cast<const int *>(
|
||||
b_mesh.loop_triangle_polygons[0].ptr.data);
|
||||
for (int i = 0; i < numtris; i++) {
|
||||
if (!sharp_faces.is_empty() && !(use_loop_normals && !corner_normals.is_empty())) {
|
||||
const blender::Span<int> looptri_faces = b_mesh.looptri_faces();
|
||||
for (const int i : looptris.index_range()) {
|
||||
smooth[i] = !sharp_faces[looptri_faces[i]];
|
||||
}
|
||||
}
|
||||
@@ -1161,8 +942,8 @@ static void create_mesh(Scene *scene,
|
||||
std::fill(smooth, smooth + numtris, true);
|
||||
}
|
||||
|
||||
if (use_loop_normals && corner_normals) {
|
||||
for (int i = 0; i < numtris; i++) {
|
||||
if (use_loop_normals && !corner_normals.is_empty()) {
|
||||
for (const int i : looptris.index_range()) {
|
||||
const MLoopTri &tri = looptris[i];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const int corner = tri.tri[i];
|
||||
@@ -1185,7 +966,7 @@ static void create_mesh(Scene *scene,
|
||||
int *subd_ptex_offset = mesh->get_subd_ptex_offset().data();
|
||||
int *subd_face_corners = mesh->get_subd_face_corners().data();
|
||||
|
||||
if (sharp_faces && !use_loop_normals) {
|
||||
if (!sharp_faces.is_empty() && !use_loop_normals) {
|
||||
for (int i = 0; i < numfaces; i++) {
|
||||
subd_smooth[i] = !sharp_faces[i];
|
||||
}
|
||||
@@ -1194,7 +975,7 @@ static void create_mesh(Scene *scene,
|
||||
std::fill(subd_smooth, subd_smooth + numfaces, true);
|
||||
}
|
||||
|
||||
if (material_indices) {
|
||||
if (!material_indices.is_empty()) {
|
||||
for (int i = 0; i < numfaces; i++) {
|
||||
subd_shader[i] = clamp_material_index(material_indices[i]);
|
||||
}
|
||||
@@ -1203,18 +984,17 @@ static void create_mesh(Scene *scene,
|
||||
std::fill(subd_shader, subd_shader + numfaces, 0);
|
||||
}
|
||||
|
||||
std::copy(corner_verts, corner_verts + numcorners, subd_face_corners);
|
||||
std::copy(corner_verts.data(), corner_verts.data() + corner_verts.size(), subd_face_corners);
|
||||
|
||||
const int *face_offsets = static_cast<const int *>(b_mesh.polygons[0].ptr.data);
|
||||
const blender::OffsetIndices faces = b_mesh.faces();
|
||||
int ptex_offset = 0;
|
||||
for (int i = 0; i < numfaces; i++) {
|
||||
const int poly_start = face_offsets[i];
|
||||
const int poly_size = face_offsets[i + 1] - poly_start;
|
||||
for (const int i : faces.index_range()) {
|
||||
const blender::IndexRange face = faces[i];
|
||||
|
||||
subd_start_corner[i] = poly_start;
|
||||
subd_num_corners[i] = poly_size;
|
||||
subd_start_corner[i] = face.start();
|
||||
subd_num_corners[i] = face.size();
|
||||
subd_ptex_offset[i] = ptex_offset;
|
||||
const int num_ptex = (poly_size == 4) ? 1 : poly_size;
|
||||
const int num_ptex = (face.size() == 4) ? 1 : face.size();
|
||||
ptex_offset += num_ptex;
|
||||
}
|
||||
|
||||
@@ -1229,15 +1009,17 @@ static void create_mesh(Scene *scene,
|
||||
/* Create all needed attributes.
|
||||
* The calculate functions will check whether they're needed or not.
|
||||
*/
|
||||
attr_create_pointiness(scene, mesh, b_mesh, subdivision);
|
||||
if (mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
|
||||
attr_create_pointiness(mesh, positions, b_mesh.vert_normals(), b_mesh.edges(), subdivision);
|
||||
}
|
||||
attr_create_random_per_island(scene, mesh, b_mesh, subdivision);
|
||||
attr_create_generic(scene, mesh, b_mesh, subdivision, need_motion, motion_scale);
|
||||
|
||||
if (subdivision) {
|
||||
attr_create_subd_uv_map(scene, mesh, b_mesh, subdivide_uvs);
|
||||
attr_create_subd_uv_map(scene, mesh, b_mesh, subdivide_uvs, blender_uv_names);
|
||||
}
|
||||
else {
|
||||
attr_create_uv_map(scene, mesh, b_mesh);
|
||||
attr_create_uv_map(scene, mesh, b_mesh, blender_uv_names);
|
||||
}
|
||||
|
||||
/* For volume objects, create a matrix to transform from object space to
|
||||
@@ -1257,7 +1039,7 @@ static void create_mesh(Scene *scene,
|
||||
static void create_subd_mesh(Scene *scene,
|
||||
Mesh *mesh,
|
||||
BObjectInfo &b_ob_info,
|
||||
BL::Mesh &b_mesh,
|
||||
const ::Mesh &b_mesh,
|
||||
const array<Node *> &used_shaders,
|
||||
const bool need_motion,
|
||||
const float motion_scale,
|
||||
@@ -1271,14 +1053,11 @@ static void create_subd_mesh(Scene *scene,
|
||||
|
||||
create_mesh(scene, mesh, b_mesh, used_shaders, need_motion, motion_scale, true, subdivide_uvs);
|
||||
|
||||
const int verts_num = b_mesh.vertices.length();
|
||||
const int edges_num = b_mesh.edges.length();
|
||||
|
||||
const float *creases = find_edge_crease_attribute(b_mesh);
|
||||
|
||||
if (edges_num != 0 && creases != nullptr) {
|
||||
const blender::VArraySpan creases = *b_mesh.attributes().lookup<float>("crease_edge",
|
||||
ATTR_DOMAIN_EDGE);
|
||||
if (!creases.is_empty()) {
|
||||
size_t num_creases = 0;
|
||||
for (int i = 0; i < edges_num; i++) {
|
||||
for (const int i : creases.index_range()) {
|
||||
if (creases[i] != 0.0f) {
|
||||
num_creases++;
|
||||
}
|
||||
@@ -1286,18 +1065,20 @@ static void create_subd_mesh(Scene *scene,
|
||||
|
||||
mesh->reserve_subd_creases(num_creases);
|
||||
|
||||
const int2 *edges = static_cast<int2 *>(b_mesh.edges[0].ptr.data);
|
||||
for (int i = 0; i < edges_num; i++) {
|
||||
const blender::Span<blender::int2> edges = b_mesh.edges();
|
||||
for (const int i : edges.index_range()) {
|
||||
const float crease = creases[i];
|
||||
if (crease != 0.0f) {
|
||||
const int2 &b_edge = edges[i];
|
||||
const blender::int2 &b_edge = edges[i];
|
||||
mesh->add_edge_crease(b_edge[0], b_edge[1], crease);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const float *vert_creases = find_vert_crease_attribute(b_mesh)) {
|
||||
for (int i = 0; i < verts_num; i++) {
|
||||
const blender::VArraySpan vert_creases = *b_mesh.attributes().lookup<float>("crease_vert",
|
||||
ATTR_DOMAIN_POINT);
|
||||
if (!vert_creases.is_empty()) {
|
||||
for (const int i : vert_creases.index_range()) {
|
||||
if (vert_creases[i] != 0.0f) {
|
||||
mesh->add_vertex_crease(i, vert_creases[i]);
|
||||
}
|
||||
@@ -1350,7 +1131,7 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, M
|
||||
create_subd_mesh(scene,
|
||||
&new_mesh,
|
||||
b_ob_info,
|
||||
b_mesh,
|
||||
*static_cast<const ::Mesh *>(b_mesh.ptr.data),
|
||||
new_mesh.get_used_shaders(),
|
||||
need_motion,
|
||||
motion_scale,
|
||||
@@ -1360,7 +1141,7 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, M
|
||||
else {
|
||||
create_mesh(scene,
|
||||
&new_mesh,
|
||||
b_mesh,
|
||||
*static_cast<const ::Mesh *>(b_mesh.ptr.data),
|
||||
new_mesh.get_used_shaders(),
|
||||
need_motion,
|
||||
motion_scale,
|
||||
@@ -1413,19 +1194,21 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
|
||||
|
||||
/* Skip objects without deforming modifiers. this is not totally reliable,
|
||||
* would need a more extensive check to see which objects are animated. */
|
||||
BL::Mesh b_mesh(PointerRNA_NULL);
|
||||
BL::Mesh b_mesh_rna(PointerRNA_NULL);
|
||||
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
|
||||
/* get derived mesh */
|
||||
b_mesh = object_to_mesh(b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
|
||||
b_mesh_rna = object_to_mesh(b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
|
||||
}
|
||||
|
||||
const std::string ob_name = b_ob_info.real_object.name();
|
||||
|
||||
/* TODO(sergey): Perform preliminary check for number of vertices. */
|
||||
if (b_mesh) {
|
||||
const int b_verts_num = b_mesh.vertices.length();
|
||||
if (b_verts_num == 0) {
|
||||
free_object_to_mesh(b_data, b_ob_info, b_mesh);
|
||||
if (b_mesh_rna) {
|
||||
const ::Mesh &b_mesh = *static_cast<const ::Mesh *>(b_mesh_rna.ptr.data);
|
||||
const int b_verts_num = b_mesh.totvert;
|
||||
const blender::Span<blender::float3> positions = b_mesh.vert_positions();
|
||||
if (positions.is_empty()) {
|
||||
free_object_to_mesh(b_data, b_ob_info, b_mesh_rna);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1448,8 +1231,6 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
|
||||
float3 *mP = attr_mP->data_float3() + motion_step * numverts;
|
||||
float3 *mN = (attr_mN) ? attr_mN->data_float3() + motion_step * numverts : NULL;
|
||||
|
||||
const float(*positions)[3] = static_cast<const float(*)[3]>(b_mesh.vertices[0].ptr.data);
|
||||
|
||||
/* NOTE: We don't copy more that existing amount of vertices to prevent
|
||||
* possible memory corruption.
|
||||
*/
|
||||
@@ -1457,11 +1238,9 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
|
||||
mP[i] = make_float3(positions[i][0], positions[i][1], positions[i][2]);
|
||||
}
|
||||
if (mN) {
|
||||
const float(*b_vert_normals)[3] = static_cast<const float(*)[3]>(
|
||||
b_mesh.vertex_normals[0].ptr.data);
|
||||
const blender::Span<blender::float3> b_vert_normals = b_mesh.vert_normals();
|
||||
for (int i = 0; i < std::min<size_t>(b_verts_num, numverts); i++) {
|
||||
const float *b_vert_normal = b_vert_normals[i];
|
||||
mN[i] = make_float3(b_vert_normal[0], b_vert_normal[1], b_vert_normal[2]);
|
||||
mN[i] = make_float3(b_vert_normals[i][0], b_vert_normals[i][1], b_vert_normals[i][2]);
|
||||
}
|
||||
}
|
||||
if (new_attribute) {
|
||||
@@ -1505,7 +1284,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
|
||||
}
|
||||
}
|
||||
|
||||
free_object_to_mesh(b_data, b_ob_info, b_mesh);
|
||||
free_object_to_mesh(b_data, b_ob_info, b_mesh_rna);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "scene/pointcloud.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
#include "blender/attribute_convert.h"
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
@@ -15,19 +16,16 @@
|
||||
#include "util/foreach.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_pointcloud.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static void attr_create_motion(PointCloud *pointcloud,
|
||||
BL::Attribute &b_attribute,
|
||||
const blender::Span<blender::float3> b_attribute,
|
||||
const float motion_scale)
|
||||
{
|
||||
if (!(b_attribute.domain() == BL::Attribute::domain_POINT) &&
|
||||
(b_attribute.data_type() == BL::Attribute::data_type_FLOAT_VECTOR))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BL::FloatVectorAttribute b_vector_attribute(b_attribute);
|
||||
const int num_points = pointcloud->get_points().size();
|
||||
|
||||
/* Find or add attribute */
|
||||
@@ -46,196 +44,91 @@ static void attr_create_motion(PointCloud *pointcloud,
|
||||
float4 *mP = attr_mP->data_float4() + step * num_points;
|
||||
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
float3 Pi = P[i] + get_float3(b_vector_attribute.data[i].vector()) * relative_time;
|
||||
float3 Pi = P[i] + make_float3(b_attribute[i][0], b_attribute[i][1], b_attribute[i][2]) *
|
||||
relative_time;
|
||||
mP[i] = make_float4(Pi.x, Pi.y, Pi.z, radius[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_attributes(PointCloud *pointcloud,
|
||||
BL::PointCloud b_pointcloud,
|
||||
const ::PointCloud &b_pointcloud,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
const int num_points = b_pointcloud.points.length();
|
||||
if (num_points == 0) {
|
||||
const blender::bke::AttributeAccessor b_attributes = b_pointcloud.attributes();
|
||||
if (b_attributes.domain_size(ATTR_DOMAIN_POINT) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AttributeSet &attributes = pointcloud->attributes;
|
||||
static const ustring u_velocity("velocity");
|
||||
for (BL::Attribute &b_attribute : b_pointcloud.attributes) {
|
||||
const ustring name{b_attribute.name().c_str()};
|
||||
b_attributes.for_all([&](const blender::bke::AttributeIDRef &id,
|
||||
const blender::bke::AttributeMetaData /*meta_data*/) {
|
||||
const ustring name{std::string_view(id.name())};
|
||||
|
||||
if (need_motion && name == u_velocity) {
|
||||
attr_create_motion(pointcloud, b_attribute, motion_scale);
|
||||
const blender::VArraySpan b_attr = *b_attributes.lookup<blender::float3>(id);
|
||||
attr_create_motion(pointcloud, b_attr, motion_scale);
|
||||
}
|
||||
|
||||
if (attributes.find(name)) {
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
const AttributeElement element = ATTR_ELEMENT_VERTEX;
|
||||
const BL::Attribute::data_type_enum b_data_type = b_attribute.data_type();
|
||||
switch (b_data_type) {
|
||||
case BL::Attribute::data_type_FLOAT: {
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
const float *src = static_cast<const float *>(b_float_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
std::copy(src, src + num_points, data);
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_BOOLEAN: {
|
||||
BL::BoolAttribute b_bool_attribute{b_attribute};
|
||||
const bool *src = static_cast<const bool *>(b_bool_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = float(src[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_INT: {
|
||||
BL::IntAttribute b_int_attribute{b_attribute};
|
||||
const int *src = static_cast<const int *>(b_int_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = float(src[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_INT32_2D: {
|
||||
BL::Int2Attribute b_int2_attribute{b_attribute};
|
||||
const int2 *src = static_cast<const int2 *>(b_int2_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat2, element);
|
||||
float2 *data = attr->data_float2();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = make_float2(float(src[i][0]), float(src[i][1]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_VECTOR: {
|
||||
BL::FloatVectorAttribute b_vector_attribute{b_attribute};
|
||||
const float(*src)[3] = static_cast<const float(*)[3]>(b_vector_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeVector, element);
|
||||
float3 *data = attr->data_float3();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = make_float3(src[i][0], src[i][1], src[i][2]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_BYTE_COLOR: {
|
||||
BL::ByteColorAttribute b_color_attribute{b_attribute};
|
||||
const uchar(*src)[4] = static_cast<const uchar(*)[4]>(b_color_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, element);
|
||||
float4 *data = attr->data_float4();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = make_float4(color_srgb_to_linear(byte_to_float(src[i][0])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][1])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][2])),
|
||||
color_srgb_to_linear(byte_to_float(src[i][3])));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_COLOR: {
|
||||
BL::FloatColorAttribute b_color_attribute{b_attribute};
|
||||
const float(*src)[4] = static_cast<const float(*)[4]>(b_color_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, element);
|
||||
float4 *data = attr->data_float4();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = make_float4(src[i][0], src[i][1], src[i][2], src[i][3]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT2: {
|
||||
BL::Float2Attribute b_float2_attribute{b_attribute};
|
||||
const float(*src)[2] = static_cast<const float(*)[2]>(b_float2_attribute.data[0].ptr.data);
|
||||
Attribute *attr = attributes.add(name, TypeFloat2, element);
|
||||
float2 *data = attr->data_float2();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = make_float2(src[i][0], src[i][1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Not supported. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const blender::bke::GAttributeReader b_attr = b_attributes.lookup(id);
|
||||
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, ATTR_ELEMENT_VERTEX);
|
||||
CyclesT *data = reinterpret_cast<CyclesT *>(attr->data());
|
||||
|
||||
static const float *find_radius_attribute(BL::PointCloud b_pointcloud)
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_pointcloud.attributes) {
|
||||
if (b_attribute.name() != "radius") {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
|
||||
continue;
|
||||
}
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
if (b_float_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const float *>(b_float_attribute.data[0].ptr.data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
const blender::VArraySpan src = b_attr.varray.typed<BlenderT>();
|
||||
for (const int i : src.index_range()) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
static const float (*find_position_attribute(BL::PointCloud b_pointcloud))[3]
|
||||
{
|
||||
for (BL::Attribute &b_attribute : b_pointcloud.attributes) {
|
||||
if (b_attribute.name() != "position") {
|
||||
continue;
|
||||
}
|
||||
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT_VECTOR) {
|
||||
continue;
|
||||
}
|
||||
BL::FloatVectorAttribute b_float3_attribute{b_attribute};
|
||||
if (b_float3_attribute.data.length() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const float(*)[3]>(b_float3_attribute.data[0].ptr.data);
|
||||
}
|
||||
/* The position attribute must exist. */
|
||||
assert(false);
|
||||
return nullptr;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void export_pointcloud(Scene *scene,
|
||||
PointCloud *pointcloud,
|
||||
BL::PointCloud b_pointcloud,
|
||||
const ::PointCloud &b_pointcloud,
|
||||
const bool need_motion,
|
||||
const float motion_scale)
|
||||
{
|
||||
const int num_points = b_pointcloud.points.length();
|
||||
pointcloud->resize(num_points);
|
||||
const blender::Span<blender::float3> b_positions = b_pointcloud.positions();
|
||||
const blender::VArraySpan b_radius = *b_pointcloud.attributes().lookup<float>("radius",
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
pointcloud->resize(b_positions.size());
|
||||
|
||||
const float(*b_attr_position)[3] = find_position_attribute(b_pointcloud);
|
||||
float3 *points = pointcloud->get_points().data();
|
||||
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
points[i] = make_float3(b_attr_position[i][0], b_attr_position[i][1], b_attr_position[i][2]);
|
||||
for (const int i : b_positions.index_range()) {
|
||||
points[i] = make_float3(b_positions[i][0], b_positions[i][1], b_positions[i][2]);
|
||||
}
|
||||
|
||||
const float *b_attr_radius = find_radius_attribute(b_pointcloud);
|
||||
float *radius = pointcloud->get_radius().data();
|
||||
if (b_attr_radius) {
|
||||
std::copy(b_attr_radius, b_attr_radius + num_points, radius);
|
||||
if (!b_radius.is_empty()) {
|
||||
std::copy(b_radius.data(), b_radius.data() + b_positions.size(), radius);
|
||||
}
|
||||
else {
|
||||
std::fill(radius, radius + num_points, 0.01f);
|
||||
std::fill(radius, radius + b_positions.size(), 0.01f);
|
||||
}
|
||||
|
||||
int *shader = pointcloud->get_shader().data();
|
||||
std::fill(shader, shader + num_points, 0);
|
||||
std::fill(shader, shader + b_positions.size(), 0);
|
||||
|
||||
if (pointcloud->need_attribute(scene, ATTR_STD_POINT_RANDOM)) {
|
||||
Attribute *attr_random = pointcloud->attributes.add(ATTR_STD_POINT_RANDOM);
|
||||
float *data = attr_random->data_float();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
for (const int i : b_positions.index_range()) {
|
||||
data[i] = hash_uint2_to_float(i, 0);
|
||||
}
|
||||
}
|
||||
@@ -244,7 +137,7 @@ static void export_pointcloud(Scene *scene,
|
||||
}
|
||||
|
||||
static void export_pointcloud_motion(PointCloud *pointcloud,
|
||||
BL::PointCloud b_pointcloud,
|
||||
const ::PointCloud &b_pointcloud,
|
||||
int motion_step)
|
||||
{
|
||||
/* Find or add attribute. */
|
||||
@@ -264,21 +157,20 @@ static void export_pointcloud_motion(PointCloud *pointcloud,
|
||||
bool have_motion = false;
|
||||
const array<float3> &pointcloud_points = pointcloud->get_points();
|
||||
|
||||
const int b_points_num = b_pointcloud.points.length();
|
||||
const float(*b_attr_position)[3] = find_position_attribute(b_pointcloud);
|
||||
const float *b_attr_radius = find_radius_attribute(b_pointcloud);
|
||||
const blender::Span<blender::float3> b_positions = b_pointcloud.positions();
|
||||
const blender::VArraySpan b_radius = *b_pointcloud.attributes().lookup<float>("radius",
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
for (int i = 0; i < std::min(num_points, b_points_num); i++) {
|
||||
const float3 P = make_float3(
|
||||
b_attr_position[i][0], b_attr_position[i][1], b_attr_position[i][2]);
|
||||
const float radius = b_attr_radius ? b_attr_radius[i] : 0.01f;
|
||||
for (int i = 0; i < std::min<int>(num_points, b_positions.size()); i++) {
|
||||
const float3 P = make_float3(b_positions[i][0], b_positions[i][1], b_positions[i][2]);
|
||||
const float radius = b_radius.is_empty() ? 0.01f : b_radius[i];
|
||||
mP[i] = make_float4(P.x, P.y, P.z, radius);
|
||||
have_motion = have_motion || (P != pointcloud_points[i]);
|
||||
}
|
||||
|
||||
/* In case of new attribute, we verify if there really was any motion. */
|
||||
if (new_attribute) {
|
||||
if (b_points_num != num_points || !have_motion) {
|
||||
if (b_positions.size() != num_points || !have_motion) {
|
||||
pointcloud->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
}
|
||||
else if (motion_step > 0) {
|
||||
@@ -311,7 +203,11 @@ void BlenderSync::sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info
|
||||
scene->motion_shutter_time() /
|
||||
(b_scene.render().fps() / b_scene.render().fps_base()) :
|
||||
0.0f;
|
||||
export_pointcloud(scene, &new_pointcloud, b_pointcloud, need_motion, motion_scale);
|
||||
export_pointcloud(scene,
|
||||
&new_pointcloud,
|
||||
*static_cast<const ::PointCloud *>(b_pointcloud.ptr.data),
|
||||
need_motion,
|
||||
motion_scale);
|
||||
|
||||
/* Update original sockets. */
|
||||
for (const SocketType &socket : new_pointcloud.type->inputs) {
|
||||
@@ -347,7 +243,8 @@ void BlenderSync::sync_pointcloud_motion(PointCloud *pointcloud,
|
||||
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
|
||||
/* PointCloud object. */
|
||||
BL::PointCloud b_pointcloud(b_ob_info.object_data);
|
||||
export_pointcloud_motion(pointcloud, b_pointcloud, motion_step);
|
||||
export_pointcloud_motion(
|
||||
pointcloud, *static_cast<const ::PointCloud *>(b_pointcloud.ptr.data), motion_step);
|
||||
}
|
||||
else {
|
||||
/* No deformation on this frame, copy coordinates if other frames did have it. */
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "util/types.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
/* Hacks to hook into Blender API
|
||||
* todo: clean this up ... */
|
||||
|
||||
@@ -524,10 +526,13 @@ static inline string get_text_datablock_content(const PointerRNA &ptr)
|
||||
|
||||
/* Texture Space */
|
||||
|
||||
static inline void mesh_texture_space(BL::Mesh &b_mesh, float3 &loc, float3 &size)
|
||||
static inline void mesh_texture_space(const ::Mesh &b_mesh, float3 &loc, float3 &size)
|
||||
{
|
||||
loc = get_float3(b_mesh.texspace_location());
|
||||
size = get_float3(b_mesh.texspace_size());
|
||||
float texspace_location[3], texspace_size[3];
|
||||
BKE_mesh_texspace_get(const_cast<::Mesh *>(&b_mesh), texspace_location, texspace_size);
|
||||
|
||||
loc = make_float3(texspace_location[0], texspace_location[1], texspace_location[2]);
|
||||
size = make_float3(texspace_size[0], texspace_size[1], texspace_size[2]);
|
||||
|
||||
if (size.x != 0.0f)
|
||||
size.x = 0.5f / size.x;
|
||||
|
||||
@@ -25,8 +25,8 @@ class BlenderSmokeLoader : public ImageLoader {
|
||||
BlenderSmokeLoader(BL::Object &b_ob, AttributeStandard attribute)
|
||||
: b_domain(object_fluid_gas_domain_find(b_ob)), attribute(attribute)
|
||||
{
|
||||
BL::Mesh b_mesh(b_ob.data());
|
||||
mesh_texture_space(b_mesh, texspace_loc, texspace_size);
|
||||
mesh_texture_space(
|
||||
*static_cast<const ::Mesh *>(b_ob.data().ptr.data), texspace_loc, texspace_size);
|
||||
}
|
||||
|
||||
bool load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata) override
|
||||
|
||||
@@ -443,6 +443,8 @@ class CurvesEditHints {
|
||||
bool is_valid() const;
|
||||
};
|
||||
|
||||
void curves_normals_point_domain_calc(const CurvesGeometry &curves, MutableSpan<float3> normals);
|
||||
|
||||
namespace curves {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_geometry_fields.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_idtype.h"
|
||||
@@ -367,4 +368,13 @@ bool CurvesEditHints::is_valid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void curves_normals_point_domain_calc(const CurvesGeometry &curves, MutableSpan<float3> normals)
|
||||
{
|
||||
const bke::CurvesFieldContext context(curves, ATTR_DOMAIN_POINT);
|
||||
fn::FieldEvaluator evaluator(context, curves.points_num());
|
||||
fn::Field<float3> field(std::make_shared<bke::NormalFieldInput>());
|
||||
evaluator.add_with_destination(std::move(field), normals);
|
||||
evaluator.evaluate();
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_fields.hh"
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
@@ -39,14 +36,7 @@ float (*ED_curves_point_normals_array_create(const Curves *curves_id))[3]
|
||||
using namespace blender;
|
||||
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
||||
const int size = curves.points_num();
|
||||
|
||||
float3 *data = static_cast<float3 *>(MEM_malloc_arrayN(size, sizeof(float3), __func__));
|
||||
|
||||
const bke::CurvesFieldContext context(curves, ATTR_DOMAIN_POINT);
|
||||
fn::FieldEvaluator evaluator(context, size);
|
||||
fn::Field<float3> field(std::make_shared<bke::NormalFieldInput>());
|
||||
evaluator.add_with_destination(std::move(field), {data, size});
|
||||
evaluator.evaluate();
|
||||
|
||||
bke::curves_normals_point_domain_calc(curves, {data, size});
|
||||
return reinterpret_cast<float(*)[3]>(data);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user