Merge with master

This commit is contained in:
Joseph Eagar 2021-12-20 14:31:59 -05:00
parent af0350393e
commit e0039cb302
391 changed files with 7010 additions and 11699 deletions

View File

@ -2083,9 +2083,9 @@ compile_OIIO() {
cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION"
if [ "$_with_built_openexr" = true ]; then
cmake_d="$cmake_d -D ILMBASE_HOME=$INST/openexr"
cmake_d="$cmake_d -D OPENEXR_HOME=$INST/openexr"
INFO "ILMBASE_HOME=$INST/openexr"
cmake_d="$cmake_d -D ILMBASE_ROOT=$INST/openexr"
cmake_d="$cmake_d -D OPENEXR_ROOT=$INST/openexr"
INFO "Ilmbase_ROOT=$INST/openexr"
fi
# ptex is only needed when nicholas bishop is ready
@ -2374,9 +2374,9 @@ compile_OSL() {
#~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
if [ "$_with_built_openexr" = true ]; then
INFO "ILMBASE_HOME=$INST/openexr"
cmake_d="$cmake_d -D OPENEXR_ROOT_DIR=$INST/openexr"
cmake_d="$cmake_d -D ILMBASE_ROOT_DIR=$INST/openexr"
cmake_d="$cmake_d -D ILMBASE_ROOT=$INST/openexr"
cmake_d="$cmake_d -D OPENEXR_ROOT=$INST/openexr"
INFO "Ilmbase_ROOT=$INST/openexr"
# XXX Temp workaround... sigh, ILMBase really messed the things up by defining their custom names ON by default :(
fi

View File

@ -197,3 +197,38 @@ index 67ec0d15f..6dc3e85a0 100644
#else
#error Unknown architecture.
#endif
diff --git a/pxr/base/arch/demangle.cpp b/pxr/base/arch/demangle.cpp
index 67ec0d15f..6dc3e85a0 100644
--- a/pxr/base/arch/demangle.cpp
+++ b/pxr/base/arch/demangle.cpp
@@ -36,6 +36,7 @@
#if (ARCH_COMPILER_GCC_MAJOR == 3 && ARCH_COMPILER_GCC_MINOR >= 1) || \
ARCH_COMPILER_GCC_MAJOR > 3 || defined(ARCH_COMPILER_CLANG)
#define _AT_LEAST_GCC_THREE_ONE_OR_CLANG
+#include <cxxabi.h>
#endif
PXR_NAMESPACE_OPEN_SCOPE
@@ -138,7 +139,6 @@
#endif
#if defined(_AT_LEAST_GCC_THREE_ONE_OR_CLANG)
-#include <cxxabi.h>
/*
* This routine doesn't work when you get to gcc3.4.
diff --git a/pxr/base/work/singularTask.h b/pxr/base/work/singularTask.h
index 67ec0d15f..6dc3e85a0 100644
--- a/pxr/base/work/singularTask.h
+++ b/pxr/base/work/singularTask.h
@@ -120,7 +120,7 @@
// case we go again to ensure the task can do whatever it
// was awakened to do. Once we successfully take the count
// to zero, we stop.
- size_t old = count;
+ std::size_t old = count;
do { _fn(); } while (
!count.compare_exchange_strong(old, 0));
});

View File

@ -40,6 +40,7 @@ set(SRC
object_cull.cpp
output_driver.cpp
particles.cpp
pointcloud.cpp
curves.cpp
logging.cpp
python.cpp

View File

@ -1020,7 +1020,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
def poll(cls, context):
ob = context.object
if CyclesButtonsPanel.poll(context) and ob:
if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA'}:
if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'HAIR', 'POINTCLOUD'}:
return True
if ob.instance_type == 'COLLECTION' and ob.instance_collection:
return True

View File

@ -19,6 +19,7 @@
#include "scene/hair.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
#include "scene/volume.h"
#include "blender/sync.h"
@ -39,6 +40,10 @@ static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_parti
return Geometry::HAIR;
}
if (b_ob_info.object_data.is_a(&RNA_PointCloud)) {
return Geometry::POINTCLOUD;
}
if (b_ob_info.object_data.is_a(&RNA_Volume) ||
(b_ob_info.object_data == b_ob_info.real_object.data() &&
object_fluid_gas_domain_find(b_ob_info.real_object))) {
@ -111,6 +116,9 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
else if (geom_type == Geometry::VOLUME) {
geom = scene->create_node<Volume>();
}
else if (geom_type == Geometry::POINTCLOUD) {
geom = scene->create_node<PointCloud>();
}
else {
geom = scene->create_node<Mesh>();
}
@ -170,6 +178,10 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
Volume *volume = static_cast<Volume *>(geom);
sync_volume(b_ob_info, volume);
}
else if (geom_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
sync_pointcloud(pointcloud, b_ob_info);
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh(b_depsgraph, b_ob_info, mesh);
@ -231,6 +243,10 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
object_fluid_gas_domain_find(b_ob_info.real_object)) {
/* No volume motion blur support yet. */
}
else if (b_ob_info.object_data.is_a(&RNA_PointCloud)) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
sync_pointcloud_motion(pointcloud, b_ob_info, motion_step);
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh_motion(b_depsgraph, b_ob_info, mesh, motion_step);

View File

@ -72,7 +72,8 @@ bool BlenderSync::object_is_geometry(BObjectInfo &b_ob_info)
BL::Object::type_enum type = b_ob_info.iter_object.type();
if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR) {
if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR ||
type == BL::Object::type_POINTCLOUD) {
/* Will be exported attached to mesh. */
return true;
}
@ -206,7 +207,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
return NULL;
}
/* only interested in object that we can create meshes from */
/* only interested in object that we can create geometry from */
if (!object_is_geometry(b_ob_info)) {
return NULL;
}

View File

@ -396,6 +396,13 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
/* set the current view */
b_engine.active_view_set(b_rview_name.c_str());
/* Force update in this case, since the camera transform on each frame changes
* in different views. This could be optimized by somehow storing the animated
* camera transforms separate from the fixed stereo transform. */
if ((scene->need_motion() != Scene::MOTION_NONE) && view_index > 0) {
sync->tag_update();
}
/* update scene */
BL::Object b_camera_override(b_engine.camera_override());
sync->sync_camera(b_render, b_camera_override, width, height, b_rview_name.c_str());

View File

@ -95,6 +95,11 @@ void BlenderSync::reset(BL::BlendData &b_data, BL::Scene &b_scene)
this->b_scene = b_scene;
}
void BlenderSync::tag_update()
{
has_updates_ = true;
}
/* Sync */
void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d)

View File

@ -66,6 +66,8 @@ class BlenderSync {
void reset(BL::BlendData &b_data, BL::Scene &b_scene);
void tag_update();
/* sync */
void sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d);
void sync_data(BL::RenderSettings &b_render,
@ -167,12 +169,16 @@ class BlenderSync {
Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, int motion_step = 0);
bool object_has_particle_hair(BL::Object b_ob);
/* Point Cloud */
void sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info);
void sync_pointcloud_motion(PointCloud *pointcloud, BObjectInfo &b_ob_info, int motion_step = 0);
/* Camera */
void sync_camera_motion(
BL::RenderSettings &b_render, BL::Object &b_ob, int width, int height, float motion_time);
/* Geometry */
Geometry *sync_geometry(BL::Depsgraph &b_depsgrpah,
Geometry *sync_geometry(BL::Depsgraph &b_depsgraph,
BObjectInfo &b_ob_info,
bool object_updated,
bool use_particle_hair,
@ -267,7 +273,6 @@ class BlenderSync {
Progress &progress;
protected:
/* Indicates that `sync_recalc()` detected changes in the scene.
* If this flag is false then the data is considered to be up-to-date and will not be
* synchronized at all. */

View File

@ -26,6 +26,7 @@
#include "scene/hair.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
#include "scene/scene.h"
#include "util/algorithm.h"
@ -113,9 +114,9 @@ void BVHBuild::add_reference_triangles(BoundBox &root,
else {
/* Motion triangles, trace optimized case: we split triangle
* primitives into separate nodes for each of the time steps.
* This way we minimize overlap of neighbor curve primitives.
* This way we minimize overlap of neighbor triangle primitives.
*/
const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1;
const int num_bvh_steps = params.num_motion_triangle_steps * 2 + 1;
const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
const size_t num_verts = mesh->verts.size();
const size_t num_steps = mesh->motion_steps;
@ -269,6 +270,101 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair
}
}
void BVHBuild::add_reference_points(BoundBox &root,
BoundBox &center,
PointCloud *pointcloud,
int i)
{
const Attribute *point_attr_mP = NULL;
if (pointcloud->has_motion_blur()) {
point_attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
}
const float3 *points_data = &pointcloud->points[0];
const float *radius_data = &pointcloud->radius[0];
const size_t num_points = pointcloud->num_points();
const float3 *motion_data = (point_attr_mP) ? point_attr_mP->data_float3() : NULL;
const size_t num_steps = pointcloud->get_motion_steps();
if (point_attr_mP == NULL) {
/* Really simple logic for static points. */
for (uint j = 0; j < num_points; j++) {
const PointCloud::Point point = pointcloud->get_point(j);
BoundBox bounds = BoundBox::empty;
point.bounds_grow(points_data, radius_data, bounds);
if (bounds.valid()) {
references.push_back(BVHReference(bounds, j, i, PRIMITIVE_POINT));
root.grow(bounds);
center.grow(bounds.center2());
}
}
}
else if (params.num_motion_point_steps == 0 || params.use_spatial_split) {
/* Simple case of motion points: single node for the whole
* shutter time. Lowest memory usage but less optimal
* rendering.
*/
/* TODO(sergey): Support motion steps for spatially split BVH. */
for (uint j = 0; j < num_points; j++) {
const PointCloud::Point point = pointcloud->get_point(j);
BoundBox bounds = BoundBox::empty;
point.bounds_grow(points_data, radius_data, bounds);
for (size_t step = 0; step < num_steps - 1; step++) {
point.bounds_grow(motion_data + step * num_points, radius_data, bounds);
}
if (bounds.valid()) {
references.push_back(BVHReference(bounds, j, i, PRIMITIVE_MOTION_POINT));
root.grow(bounds);
center.grow(bounds.center2());
}
}
}
else {
/* Motion points, trace optimized case: we split point
* primitives into separate nodes for each of the time steps.
* This way we minimize overlap of neighbor point primitives.
*/
const int num_bvh_steps = params.num_motion_point_steps * 2 + 1;
const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
for (uint j = 0; j < num_points; j++) {
const PointCloud::Point point = pointcloud->get_point(j);
const size_t num_steps = pointcloud->get_motion_steps();
const float3 *point_steps = point_attr_mP->data_float3();
/* Calculate bounding box of the previous time step.
* Will be reused later to avoid duplicated work on
* calculating BVH time step boundbox.
*/
float4 prev_key = point.motion_key(
points_data, radius_data, point_steps, num_points, num_steps, 0.0f, j);
BoundBox prev_bounds = BoundBox::empty;
point.bounds_grow(prev_key, prev_bounds);
/* Create all primitive time steps, */
for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) {
const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1;
float4 curr_key = point.motion_key(
points_data, radius_data, point_steps, num_points, num_steps, curr_time, j);
BoundBox curr_bounds = BoundBox::empty;
point.bounds_grow(curr_key, curr_bounds);
BoundBox bounds = prev_bounds;
bounds.grow(curr_bounds);
if (bounds.valid()) {
const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
references.push_back(
BVHReference(bounds, j, i, PRIMITIVE_MOTION_POINT, prev_time, curr_time));
root.grow(bounds);
center.grow(bounds.center2());
}
/* Current time boundbox becomes previous one for the
* next time step.
*/
prev_bounds = curr_bounds;
}
}
}
}
void BVHBuild::add_reference_geometry(BoundBox &root,
BoundBox &center,
Geometry *geom,
@ -282,6 +378,10 @@ void BVHBuild::add_reference_geometry(BoundBox &root,
Hair *hair = static_cast<Hair *>(geom);
add_reference_curves(root, center, hair, object_index);
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
add_reference_points(root, center, pointcloud, object_index);
}
}
void BVHBuild::add_reference_object(BoundBox &root, BoundBox &center, Object *ob, int i)
@ -311,6 +411,10 @@ static size_t count_primitives(Geometry *geom)
Hair *hair = static_cast<Hair *>(geom);
return count_curve_segments(hair);
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
return pointcloud->num_points();
}
return 0;
}
@ -328,8 +432,9 @@ void BVHBuild::add_references(BVHRange &root)
if (!ob->get_geometry()->is_instanced()) {
num_alloc_references += count_primitives(ob->get_geometry());
}
else
else {
num_alloc_references++;
}
}
else {
num_alloc_references += count_primitives(ob->get_geometry());
@ -394,7 +499,7 @@ BVHNode *BVHBuild::run()
spatial_min_overlap = root.bounds().safe_area() * params.spatial_split_alpha;
spatial_free_index = 0;
need_prim_time = params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0;
need_prim_time = params.use_motion_steps();
/* init progress updates */
double build_start_time;
@ -535,7 +640,8 @@ bool BVHBuild::range_within_max_leaf_size(const BVHRange &range,
const vector<BVHReference> &references) const
{
size_t size = range.size();
size_t max_leaf_size = max(params.max_triangle_leaf_size, params.max_curve_leaf_size);
size_t max_leaf_size = max(max(params.max_triangle_leaf_size, params.max_curve_leaf_size),
params.max_point_leaf_size);
if (size > max_leaf_size)
return false;
@ -544,32 +650,44 @@ bool BVHBuild::range_within_max_leaf_size(const BVHRange &range,
size_t num_motion_triangles = 0;
size_t num_curves = 0;
size_t num_motion_curves = 0;
size_t num_points = 0;
size_t num_motion_points = 0;
for (int i = 0; i < size; i++) {
const BVHReference &ref = references[range.start() + i];
if (ref.prim_type() & PRIMITIVE_ALL_CURVE) {
if (ref.prim_type() & PRIMITIVE_ALL_MOTION) {
if (ref.prim_type() & PRIMITIVE_CURVE) {
if (ref.prim_type() & PRIMITIVE_MOTION) {
num_motion_curves++;
}
else {
num_curves++;
}
}
else if (ref.prim_type() & PRIMITIVE_ALL_TRIANGLE) {
if (ref.prim_type() & PRIMITIVE_ALL_MOTION) {
else if (ref.prim_type() & PRIMITIVE_TRIANGLE) {
if (ref.prim_type() & PRIMITIVE_MOTION) {
num_motion_triangles++;
}
else {
num_triangles++;
}
}
else if (ref.prim_type() & PRIMITIVE_POINT) {
if (ref.prim_type() & PRIMITIVE_MOTION) {
num_motion_points++;
}
else {
num_points++;
}
}
}
return (num_triangles <= params.max_triangle_leaf_size) &&
(num_motion_triangles <= params.max_motion_triangle_leaf_size) &&
(num_curves <= params.max_curve_leaf_size) &&
(num_motion_curves <= params.max_motion_curve_leaf_size);
(num_motion_curves <= params.max_motion_curve_leaf_size) &&
(num_points <= params.max_point_leaf_size) &&
(num_motion_points <= params.max_motion_point_leaf_size);
}
/* multithreaded binning builder */
@ -855,7 +973,7 @@ BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vector<BVHRefer
for (int i = 0; i < range.size(); i++) {
const BVHReference &ref = references[range.start() + i];
if (ref.prim_index() != -1) {
uint32_t type_index = bitscan((uint32_t)(ref.prim_type() & PRIMITIVE_ALL));
uint32_t type_index = PRIMITIVE_INDEX(ref.prim_type() & PRIMITIVE_ALL);
p_ref[type_index].push_back(ref);
p_type[type_index].push_back(ref.prim_type());
p_index[type_index].push_back(ref.prim_index());

View File

@ -39,6 +39,7 @@ class Geometry;
class Hair;
class Mesh;
class Object;
class PointCloud;
class Progress;
/* BVH Builder */
@ -68,6 +69,7 @@ class BVHBuild {
/* Adding references. */
void add_reference_triangles(BoundBox &root, BoundBox &center, Mesh *mesh, int i);
void add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair, int i);
void add_reference_points(BoundBox &root, BoundBox &center, PointCloud *pointcloud, int i);
void add_reference_geometry(BoundBox &root, BoundBox &center, Geometry *geom, int i);
void add_reference_object(BoundBox &root, BoundBox &center, Object *ob, int i);
void add_references(BVHRange &root);

View File

@ -20,6 +20,7 @@
#include "scene/hair.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
#include "bvh/build.h"
#include "bvh/node.h"
@ -386,7 +387,7 @@ void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility
}
else {
/* Primitives. */
if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
if (pack.prim_type[prim] & PRIMITIVE_CURVE) {
/* Curves. */
const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
int prim_offset = (params.top_level) ? hair->prim_offset : 0;
@ -409,6 +410,30 @@ void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility
}
}
}
else if (pack.prim_type[prim] & PRIMITIVE_POINT) {
/* Points. */
const PointCloud *pointcloud = static_cast<const PointCloud *>(ob->get_geometry());
int prim_offset = (params.top_level) ? pointcloud->prim_offset : 0;
const float3 *points = &pointcloud->points[0];
const float *radius = &pointcloud->radius[0];
PointCloud::Point point = pointcloud->get_point(pidx - prim_offset);
point.bounds_grow(points, radius, bbox);
/* Motion points. */
if (pointcloud->get_use_motion_blur()) {
Attribute *attr = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr) {
size_t pointcloud_size = pointcloud->points.size();
size_t steps = pointcloud->get_motion_steps() - 1;
float3 *point_steps = attr->data_float3();
for (size_t i = 0; i < steps; i++)
point.bounds_grow(point_steps + i * pointcloud_size, radius, bbox);
}
}
}
else {
/* Triangles. */
const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
@ -505,7 +530,8 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
pack.leaf_nodes.resize(leaf_nodes_size);
pack.object_node.resize(objects.size());
if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0 ||
params.num_motion_point_steps > 0) {
pack.prim_time.resize(prim_index_size);
}
@ -564,13 +590,7 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL;
for (size_t i = 0; i < bvh_prim_index_size; i++) {
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
}
else {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
}
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances

View File

@ -45,6 +45,7 @@
# include "scene/hair.h"
# include "scene/mesh.h"
# include "scene/object.h"
# include "scene/pointcloud.h"
# include "util/foreach.h"
# include "util/log.h"
@ -90,7 +91,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
++ctx->num_hits;
/* Always use baked shadow transparency for curves. */
if (current_isect.type & PRIMITIVE_ALL_CURVE) {
if (current_isect.type & PRIMITIVE_CURVE) {
ctx->throughput *= intersection_curve_shadow_transparency(
kg, current_isect.object, current_isect.prim, current_isect.u);
@ -245,7 +246,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
}
}
static void rtc_filter_func_thick_curve(const RTCFilterFunctionNArguments *args)
static void rtc_filter_func_backface_cull(const RTCFilterFunctionNArguments *args)
{
const RTCRay *ray = (RTCRay *)args->ray;
RTCHit *hit = (RTCHit *)args->hit;
@ -258,7 +259,7 @@ static void rtc_filter_func_thick_curve(const RTCFilterFunctionNArguments *args)
}
}
static void rtc_filter_occluded_func_thick_curve(const RTCFilterFunctionNArguments *args)
static void rtc_filter_occluded_func_backface_cull(const RTCFilterFunctionNArguments *args)
{
const RTCRay *ray = (RTCRay *)args->ray;
RTCHit *hit = (RTCHit *)args->hit;
@ -410,6 +411,12 @@ void BVHEmbree::add_object(Object *ob, int i)
add_curves(ob, hair, i);
}
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
if (pointcloud->num_points() > 0) {
add_points(ob, pointcloud, i);
}
}
}
void BVHEmbree::add_instance(Object *ob, int i)
@ -624,6 +631,89 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c
}
}
void BVHEmbree::set_point_vertex_buffer(RTCGeometry geom_id,
const PointCloud *pointcloud,
const bool update)
{
const Attribute *attr_mP = NULL;
size_t num_motion_steps = 1;
if (pointcloud->has_motion_blur()) {
attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
num_motion_steps = pointcloud->get_motion_steps();
}
}
const size_t num_points = pointcloud->num_points();
/* Copy the point data to Embree */
const int t_mid = (num_motion_steps - 1) / 2;
const float *radius = pointcloud->get_radius().data();
for (int t = 0; t < num_motion_steps; ++t) {
const float3 *verts;
if (t == t_mid || attr_mP == NULL) {
verts = pointcloud->get_points().data();
}
else {
int t_ = (t > t_mid) ? (t - 1) : t;
verts = &attr_mP->data_float3()[t_ * num_points];
}
float4 *rtc_verts = (update) ? (float4 *)rtcGetGeometryBufferData(
geom_id, RTC_BUFFER_TYPE_VERTEX, t) :
(float4 *)rtcSetNewGeometryBuffer(geom_id,
RTC_BUFFER_TYPE_VERTEX,
t,
RTC_FORMAT_FLOAT4,
sizeof(float) * 4,
num_points);
assert(rtc_verts);
if (rtc_verts) {
for (size_t j = 0; j < num_points; ++j) {
rtc_verts[j] = float3_to_float4(verts[j]);
rtc_verts[j].w = radius[j];
}
}
if (update) {
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
}
}
}
void BVHEmbree::add_points(const Object *ob, const PointCloud *pointcloud, int i)
{
size_t prim_offset = pointcloud->prim_offset;
const Attribute *attr_mP = NULL;
size_t num_motion_steps = 1;
if (pointcloud->has_motion_blur()) {
attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
num_motion_steps = pointcloud->get_motion_steps();
}
}
enum RTCGeometryType type = RTC_GEOMETRY_TYPE_SPHERE_POINT;
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
rtcSetGeometryBuildQuality(geom_id, build_quality);
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
set_point_vertex_buffer(geom_id, pointcloud, false);
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func_backface_cull);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func_backface_cull);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
rtcCommitGeometry(geom_id);
rtcAttachGeometryByID(scene, geom_id, i * 2);
rtcReleaseGeometry(geom_id);
}
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
{
size_t prim_offset = hair->curve_segment_offset;
@ -678,8 +768,8 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
}
else {
rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func_thick_curve);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func_thick_curve);
rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func_backface_cull);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func_backface_cull);
}
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@ -716,6 +806,14 @@ void BVHEmbree::refit(Progress &progress)
rtcCommitGeometry(geom);
}
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
if (pointcloud->num_points() > 0) {
RTCGeometry geom = rtcGetGeometry(scene, geom_id);
set_point_vertex_buffer(geom, pointcloud, true);
rtcCommitGeometry(geom);
}
}
}
geom_id += 2;
}

View File

@ -33,6 +33,7 @@ CCL_NAMESPACE_BEGIN
class Hair;
class Mesh;
class PointCloud;
class BVHEmbree : public BVH {
public:
@ -51,11 +52,15 @@ class BVHEmbree : public BVH {
void add_object(Object *ob, int i);
void add_instance(Object *ob, int i);
void add_curves(const Object *ob, const Hair *hair, int i);
void add_points(const Object *ob, const PointCloud *pointcloud, int i);
void add_triangles(const Object *ob, const Mesh *mesh, int i);
private:
void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update);
void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update);
void set_point_vertex_buffer(RTCGeometry geom_id,
const PointCloud *pointcloud,
const bool update);
RTCDevice rtc_device;
enum RTCBuildQuality build_quality;

View File

@ -83,6 +83,8 @@ class BVHParams {
int max_motion_triangle_leaf_size;
int max_curve_leaf_size;
int max_motion_curve_leaf_size;
int max_point_leaf_size;
int max_motion_point_leaf_size;
/* object or mesh level bvh */
bool top_level;
@ -98,13 +100,13 @@ class BVHParams {
/* Split time range to this number of steps and create leaf node for each
* of this time steps.
*
* Speeds up rendering of motion curve primitives in the cost of higher
* memory usage.
* Speeds up rendering of motion primitives in the cost of higher memory usage.
*/
int num_motion_curve_steps;
/* Same as above, but for triangle primitives. */
int num_motion_triangle_steps;
int num_motion_curve_steps;
int num_motion_point_steps;
/* Same as in SceneParams. */
int bvh_type;
@ -132,6 +134,8 @@ class BVHParams {
max_motion_triangle_leaf_size = 8;
max_curve_leaf_size = 1;
max_motion_curve_leaf_size = 4;
max_point_leaf_size = 8;
max_motion_point_leaf_size = 8;
top_level = false;
bvh_layout = BVH_LAYOUT_BVH2;
@ -139,6 +143,7 @@ class BVHParams {
num_motion_curve_steps = 0;
num_motion_triangle_steps = 0;
num_motion_point_steps = 0;
bvh_type = 0;
@ -166,6 +171,12 @@ class BVHParams {
return (size <= min_leaf_size || level >= MAX_DEPTH);
}
bool use_motion_steps()
{
return num_motion_curve_steps > 0 || num_motion_triangle_steps > 0 ||
num_motion_point_steps > 0;
}
/* Gets best matching BVH.
*
* If the requested layout is supported by the device, it will be used.

View File

@ -23,6 +23,7 @@
#include "scene/hair.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
#include "util/algorithm.h"
@ -426,6 +427,32 @@ void BVHSpatialSplit::split_curve_primitive(const Hair *hair,
}
}
void BVHSpatialSplit::split_point_primitive(const PointCloud *pointcloud,
const Transform *tfm,
int prim_index,
int dim,
float pos,
BoundBox &left_bounds,
BoundBox &right_bounds)
{
/* No real splitting support for points, assume they are small enough for it
* not to matter. */
float3 point = pointcloud->get_points()[prim_index];
if (tfm != NULL) {
point = transform_point(tfm, point);
}
point = get_unaligned_point(point);
if (point[dim] <= pos) {
left_bounds.grow(point);
}
if (point[dim] >= pos) {
right_bounds.grow(point);
}
}
void BVHSpatialSplit::split_triangle_reference(const BVHReference &ref,
const Mesh *mesh,
int dim,
@ -453,6 +480,16 @@ void BVHSpatialSplit::split_curve_reference(const BVHReference &ref,
right_bounds);
}
void BVHSpatialSplit::split_point_reference(const BVHReference &ref,
const PointCloud *pointcloud,
int dim,
float pos,
BoundBox &left_bounds,
BoundBox &right_bounds)
{
split_point_primitive(pointcloud, NULL, ref.prim_index(), dim, pos, left_bounds, right_bounds);
}
void BVHSpatialSplit::split_object_reference(
const Object *object, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
{
@ -475,6 +512,13 @@ void BVHSpatialSplit::split_object_reference(
}
}
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
for (int point_idx = 0; point_idx < pointcloud->num_points(); ++point_idx) {
split_point_primitive(
pointcloud, &object->get_tfm(), point_idx, dim, pos, left_bounds, right_bounds);
}
}
}
void BVHSpatialSplit::split_reference(const BVHBuild &builder,
@ -491,14 +535,18 @@ void BVHSpatialSplit::split_reference(const BVHBuild &builder,
/* loop over vertices/edges. */
const Object *ob = builder.objects[ref.prim_object()];
if (ref.prim_type() & PRIMITIVE_ALL_TRIANGLE) {
if (ref.prim_type() & PRIMITIVE_TRIANGLE) {
Mesh *mesh = static_cast<Mesh *>(ob->get_geometry());
split_triangle_reference(ref, mesh, dim, pos, left_bounds, right_bounds);
}
else if (ref.prim_type() & PRIMITIVE_ALL_CURVE) {
else if (ref.prim_type() & PRIMITIVE_CURVE) {
Hair *hair = static_cast<Hair *>(ob->get_geometry());
split_curve_reference(ref, hair, dim, pos, left_bounds, right_bounds);
}
else if (ref.prim_type() & PRIMITIVE_POINT) {
PointCloud *pointcloud = static_cast<PointCloud *>(ob->get_geometry());
split_point_reference(ref, pointcloud, dim, pos, left_bounds, right_bounds);
}
else {
split_object_reference(ob, dim, pos, left_bounds, right_bounds);
}

View File

@ -26,6 +26,7 @@ CCL_NAMESPACE_BEGIN
class BVHBuild;
class Hair;
class Mesh;
class PointCloud;
struct Transform;
/* Object Split */
@ -123,6 +124,13 @@ class BVHSpatialSplit {
float pos,
BoundBox &left_bounds,
BoundBox &right_bounds);
void split_point_primitive(const PointCloud *pointcloud,
const Transform *tfm,
int prim_index,
int dim,
float pos,
BoundBox &left_bounds,
BoundBox &right_bounds);
/* Lower-level functions which calculates boundaries of left and right nodes
* needed for spatial split.
@ -141,6 +149,12 @@ class BVHSpatialSplit {
float pos,
BoundBox &left_bounds,
BoundBox &right_bounds);
void split_point_reference(const BVHReference &ref,
const PointCloud *pointcloud,
int dim,
float pos,
BoundBox &left_bounds,
BoundBox &right_bounds);
void split_object_reference(
const Object *object, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds);

View File

@ -69,7 +69,7 @@ bool BVHUnaligned::compute_aligned_space(const BVHReference &ref, Transform *ali
const int packed_type = ref.prim_type();
const int type = (packed_type & PRIMITIVE_ALL);
/* No motion blur curves here, we can't fit them to aligned boxes well. */
if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_CURVE_THICK)) {
if ((type & PRIMITIVE_CURVE) && !(type & PRIMITIVE_MOTION)) {
const int curve_index = ref.prim_index();
const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type);
const Hair *hair = static_cast<const Hair *>(object->get_geometry());
@ -95,7 +95,7 @@ BoundBox BVHUnaligned::compute_aligned_prim_boundbox(const BVHReference &prim,
const int packed_type = prim.prim_type();
const int type = (packed_type & PRIMITIVE_ALL);
/* No motion blur curves here, we can't fit them to aligned boxes well. */
if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_CURVE_THICK)) {
if ((type & PRIMITIVE_CURVE) && !(type & PRIMITIVE_MOTION)) {
const int curve_index = prim.prim_index();
const int segment = PRIMITIVE_UNPACK_SEGMENT(packed_type);
const Hair *hair = static_cast<const Hair *>(object->get_geometry());

View File

@ -28,6 +28,7 @@
# include "scene/mesh.h"
# include "scene/object.h"
# include "scene/pass.h"
# include "scene/pointcloud.h"
# include "scene/scene.h"
# include "util/debug.h"
@ -242,6 +243,9 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
else
pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM;
}
if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM;
}
/* Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds
* This is necessary since objects may be reported to have motion if the Vector pass is
@ -372,6 +376,18 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
}
}
/* Pointclouds */
if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
group_descs[PG_HITD_POINTCLOUD] = group_descs[PG_HITD];
group_descs[PG_HITD_POINTCLOUD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
group_descs[PG_HITD_POINTCLOUD].hitgroup.moduleIS = optix_module;
group_descs[PG_HITD_POINTCLOUD].hitgroup.entryFunctionNameIS = "__intersection__point";
group_descs[PG_HITS_POINTCLOUD] = group_descs[PG_HITS];
group_descs[PG_HITS_POINTCLOUD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
group_descs[PG_HITS_POINTCLOUD].hitgroup.moduleIS = optix_module;
group_descs[PG_HITS_POINTCLOUD].hitgroup.entryFunctionNameIS = "__intersection__point";
}
if (kernel_features & (KERNEL_FEATURE_SUBSURFACE | KERNEL_FEATURE_NODE_RAYTRACE)) {
/* Add hit group for local intersections. */
group_descs[PG_HITL].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
@ -419,6 +435,10 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH);
trace_css = std::max(trace_css,
stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH);
trace_css = std::max(
trace_css, stack_size[PG_HITD_POINTCLOUD].cssIS + stack_size[PG_HITD_POINTCLOUD].cssAH);
trace_css = std::max(
trace_css, stack_size[PG_HITS_POINTCLOUD].cssIS + stack_size[PG_HITS_POINTCLOUD].cssAH);
OptixPipelineLinkOptions link_options = {};
link_options.maxTraceDepth = 1;
@ -444,6 +464,10 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
pipeline_groups.push_back(groups[PG_HITD_MOTION]);
pipeline_groups.push_back(groups[PG_HITS_MOTION]);
}
if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
pipeline_groups.push_back(groups[PG_HITD_POINTCLOUD]);
pipeline_groups.push_back(groups[PG_HITS_POINTCLOUD]);
}
pipeline_groups.push_back(groups[PG_CALL_SVM_AO]);
pipeline_groups.push_back(groups[PG_CALL_SVM_BEVEL]);
@ -483,6 +507,10 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
pipeline_groups.push_back(groups[PG_HITD_MOTION]);
pipeline_groups.push_back(groups[PG_HITS_MOTION]);
}
if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
pipeline_groups.push_back(groups[PG_HITD_POINTCLOUD]);
pipeline_groups.push_back(groups[PG_HITS_POINTCLOUD]);
}
optix_assert(optixPipelineCreate(context,
&pipeline_options,
@ -1374,6 +1402,86 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
build_input.triangleArray.numSbtRecords = 1;
build_input.triangleArray.primitiveIndexOffset = mesh->prim_offset;
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
/* Build BLAS for points primitives. */
PointCloud *const pointcloud = static_cast<PointCloud *const>(geom);
const size_t num_points = pointcloud->num_points();
if (num_points == 0) {
return;
}
size_t num_motion_steps = 1;
Attribute *motion_points = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (motion_blur && pointcloud->get_use_motion_blur() && motion_points) {
num_motion_steps = pointcloud->get_motion_steps();
}
device_vector<OptixAabb> aabb_data(this, "optix temp aabb data", MEM_READ_ONLY);
aabb_data.alloc(num_points * num_motion_steps);
/* Get AABBs for each motion step. */
for (size_t step = 0; step < num_motion_steps; ++step) {
/* The center step for motion vertices is not stored in the attribute. */
const float3 *points = pointcloud->get_points().data();
const float *radius = pointcloud->get_radius().data();
size_t center_step = (num_motion_steps - 1) / 2;
if (step != center_step) {
size_t attr_offset = (step > center_step) ? step - 1 : step;
/* Technically this is a float4 array, but sizeof(float3) == sizeof(float4). */
points = motion_points->data_float3() + attr_offset * num_points;
}
for (size_t i = 0; i < num_points; ++i) {
const PointCloud::Point point = pointcloud->get_point(i);
BoundBox bounds = BoundBox::empty;
point.bounds_grow(points, radius, bounds);
const size_t index = step * num_points + i;
aabb_data[index].minX = bounds.min.x;
aabb_data[index].minY = bounds.min.y;
aabb_data[index].minZ = bounds.min.z;
aabb_data[index].maxX = bounds.max.x;
aabb_data[index].maxY = bounds.max.y;
aabb_data[index].maxZ = bounds.max.z;
}
}
/* Upload AABB data to GPU. */
aabb_data.copy_to_device();
vector<device_ptr> aabb_ptrs;
aabb_ptrs.reserve(num_motion_steps);
for (size_t step = 0; step < num_motion_steps; ++step) {
aabb_ptrs.push_back(aabb_data.device_pointer + step * num_points * sizeof(OptixAabb));
}
/* Disable visibility test any-hit program, since it is already checked during
* intersection. Those trace calls that require anyhit can force it with a ray flag.
* For those, force a single any-hit call, so shadow record-all behavior works correctly. */
unsigned int build_flags = OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT |
OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;
OptixBuildInput build_input = {};
build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES;
# if OPTIX_ABI_VERSION < 23
build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data();
build_input.aabbArray.numPrimitives = num_points;
build_input.aabbArray.strideInBytes = sizeof(OptixAabb);
build_input.aabbArray.flags = &build_flags;
build_input.aabbArray.numSbtRecords = 1;
build_input.aabbArray.primitiveIndexOffset = pointcloud->prim_offset;
# else
build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data();
build_input.customPrimitiveArray.numPrimitives = num_points;
build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb);
build_input.customPrimitiveArray.flags = &build_flags;
build_input.customPrimitiveArray.numSbtRecords = 1;
build_input.customPrimitiveArray.primitiveIndexOffset = pointcloud->prim_offset;
# endif
if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
progress.set_error("Failed to build OptiX acceleration structure");
}
@ -1461,12 +1569,22 @@ void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
}
}
else if (ob->get_geometry()->geometry_type == Geometry::POINTCLOUD) {
/* Use the hit group that has an intersection program for point clouds. */
instance.sbtOffset = PG_HITD_POINTCLOUD - PG_HITD;
/* Also skip point clouds in local trace calls. */
instance.visibilityMask |= 4;
}
# if OPTIX_ABI_VERSION < 55
/* Cannot disable any-hit program for thick curves, since it needs to filter out end-caps. */
else
# endif
{
/* Can disable __anyhit__kernel_optix_visibility_test by default.
/* Can disable __anyhit__kernel_optix_visibility_test by default (except for thick curves,
* since it needs to filter out end-caps there).
* It is enabled where necessary (visibility mask exceeds 8 bits or the other any-hit
* programs like __anyhit__kernel_optix_shadow_all_hit) via OPTIX_RAY_FLAG_ENFORCE_ANYHIT.
*/

View File

@ -44,6 +44,8 @@ enum {
PG_HITV, /* __VOLUME__ hit group. */
PG_HITD_MOTION,
PG_HITS_MOTION,
PG_HITD_POINTCLOUD,
PG_HITS_POINTCLOUD,
PG_CALL_SVM_AO,
PG_CALL_SVM_BEVEL,
NUM_PROGRAM_GROUPS
@ -52,9 +54,9 @@ enum {
static const int MISS_PROGRAM_GROUP_OFFSET = PG_MISS;
static const int NUM_MIS_PROGRAM_GROUPS = 1;
static const int HIT_PROGAM_GROUP_OFFSET = PG_HITD;
static const int NUM_HIT_PROGRAM_GROUPS = 6;
static const int NUM_HIT_PROGRAM_GROUPS = 8;
static const int CALLABLE_PROGRAM_GROUPS_BASE = PG_CALL_SVM_AO;
static const int NUM_CALLABLE_PROGRAM_GROUPS = 3;
static const int NUM_CALLABLE_PROGRAM_GROUPS = 2;
/* List of OptiX pipelines. */
enum { PIP_SHADE_RAYTRACE, PIP_INTERSECT, NUM_PIPELINES };

View File

@ -1086,8 +1086,14 @@ void RenderScheduler::update_start_resolution_divider()
first_render_time_.denoise_time +
first_render_time_.display_update_time;
/* Allow some percent of tolerance, so that if the render time is close enough to the higher
* resolution we prefer to use it instead of going way lower resolution and time way below the
* desired one. */
const int resolution_divider_for_update = calculate_resolution_divider_for_time(
desired_update_interval_in_seconds, actual_time_per_update, start_resolution_divider_);
desired_update_interval_in_seconds * 1.4, actual_time_per_update);
/* TODO(sergey): Need to add hysteresis to avoid resolution divider bouncing around when actual
* render time is somewhere on a boundary between two resolutions. */
/* Never increase resolution to higher than the pixel size (which is possible if the scene is
* simple and compute device is fast). */
@ -1174,14 +1180,12 @@ void RenderScheduler::check_time_limit_reached()
* Utility functions.
*/
int RenderScheduler::calculate_resolution_divider_for_time(double desired_time,
double actual_time,
int previous_resolution_divider)
int RenderScheduler::calculate_resolution_divider_for_time(double desired_time, double actual_time)
{
/* TODO(sergey): There should a non-iterative analytical formula here. */
int resolution_divider = 1;
double pre_division_time = actual_time;
/* This algorithm iterates through resolution dividers until a divider is found that achieves
* the desired render time. A limit of default_start_resolution_divider_ is put in place as the
* maximum resolution divider to avoid an unreadable viewport due to a low resolution.
@ -1189,26 +1193,12 @@ int RenderScheduler::calculate_resolution_divider_for_time(double desired_time,
* calculation to better predict the performance impact of changing resolution divisions as
* the sample count can also change between resolution divisions. */
while (actual_time > desired_time && resolution_divider < default_start_resolution_divider_) {
pre_division_time = actual_time;
int pre_resolution_division_samples = get_num_samples_during_navigation(resolution_divider);
resolution_divider = resolution_divider * 2;
int post_resolution_division_samples = get_num_samples_during_navigation(resolution_divider);
actual_time /= 4.0 * pre_resolution_division_samples / post_resolution_division_samples;
}
/* Hysteresis to avoid resolution divider bouncing around when actual render time is somewhere
* on a boundary between two resolutions. This system allows some percentage of tolerance so
* that the viewport tends towards remaining at higher resolutions rather than dropping to a
* lower resolution if performance is close to, but not quite reaching the target frame rate.
* This system also tends to not increase the viewport resolution unless the hardware can
* exceed the performance target by a certain percentage. This is to help ensure the viewport
* remains responsive after increasing the resolution. */
if (resolution_divider > previous_resolution_divider && pre_division_time < desired_time * 1.5) {
return resolution_divider / 2;
}
if (resolution_divider < previous_resolution_divider && pre_division_time > desired_time / 1.3) {
return resolution_divider * 2;
}
return resolution_divider;
}

View File

@ -462,9 +462,7 @@ class RenderScheduler {
* desired one. This call assumes linear dependency of render time from number of pixels
* (quadratic dependency from the resolution divider): resolution divider of 2 brings render time
* down by a factor of 4. */
int calculate_resolution_divider_for_time(double desired_time,
double actual_time,
int previous_resolution_divider);
int calculate_resolution_divider_for_time(double desired_time, double actual_time);
};
int calculate_resolution_divider_for_resolution(int width, int height, int resolution);

View File

@ -49,24 +49,24 @@ CCL_NAMESPACE_BEGIN
# include "kernel/bvh/nodes.h"
# define BVH_FUNCTION_NAME bvh_intersect
# define BVH_FUNCTION_FEATURES 0
# define BVH_FUNCTION_FEATURES BVH_POINTCLOUD
# include "kernel/bvh/traversal.h"
# if defined(__HAIR__)
# define BVH_FUNCTION_NAME bvh_intersect_hair
# define BVH_FUNCTION_FEATURES BVH_HAIR
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_POINTCLOUD
# include "kernel/bvh/traversal.h"
# endif
# if defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_motion
# define BVH_FUNCTION_FEATURES BVH_MOTION
# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_POINTCLOUD
# include "kernel/bvh/traversal.h"
# endif
# if defined(__HAIR__) && defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_hair_motion
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION | BVH_POINTCLOUD
# include "kernel/bvh/traversal.h"
# endif
@ -102,26 +102,27 @@ CCL_NAMESPACE_BEGIN
# if defined(__SHADOW_RECORD_ALL__)
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all
# define BVH_FUNCTION_FEATURES 0
# define BVH_FUNCTION_FEATURES BVH_POINTCLOUD
# include "kernel/bvh/shadow_all.h"
# if defined(__HAIR__)
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair
# define BVH_FUNCTION_FEATURES BVH_HAIR
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_POINTCLOUD
# include "kernel/bvh/shadow_all.h"
# endif
# if defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_motion
# define BVH_FUNCTION_FEATURES BVH_MOTION
# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_POINTCLOUD
# include "kernel/bvh/shadow_all.h"
# endif
# if defined(__HAIR__) && defined(__OBJECT_MOTION__)
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair_motion
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION | BVH_POINTCLOUD
# include "kernel/bvh/shadow_all.h"
# endif
# endif /* __SHADOW_RECORD_ALL__ */
/* Record all intersections - Volume BVH traversal. */

View File

@ -28,6 +28,7 @@
* without new features slowing things down.
*
* BVH_HAIR: hair curve rendering
* BVH_POINTCLOUD: point cloud rendering
* BVH_MOTION: motion blur rendering
*/
@ -173,7 +174,7 @@ ccl_device_inline
case PRIMITIVE_MOTION_CURVE_THICK:
case PRIMITIVE_CURVE_RIBBON:
case PRIMITIVE_MOTION_CURVE_RIBBON: {
if ((type & PRIMITIVE_ALL_MOTION) && kernel_data.bvh.use_bvh_steps) {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
if (ray->time < prim_time.x || ray->time > prim_time.y) {
hit = false;
@ -199,6 +200,34 @@ ccl_device_inline
break;
}
#endif
#if BVH_FEATURE(BVH_POINTCLOUD)
case PRIMITIVE_POINT:
case PRIMITIVE_MOTION_POINT: {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
if (ray->time < prim_time.x || ray->time > prim_time.y) {
hit = false;
break;
}
}
const int point_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int point_prim = kernel_tex_fetch(__prim_index, prim_addr);
const int point_type = kernel_tex_fetch(__prim_type, prim_addr);
hit = point_intersect(kg,
&isect,
P,
dir,
t_max_current,
point_object,
point_prim,
ray->time,
point_type);
break;
}
#endif /* BVH_FEATURE(BVH_POINTCLOUD) */
default: {
hit = false;
break;
@ -226,7 +255,7 @@ ccl_device_inline
bool record_intersection = true;
/* Always use baked shadow transparency for curves. */
if (isect.type & PRIMITIVE_ALL_CURVE) {
if (isect.type & PRIMITIVE_CURVE) {
*throughput *= intersection_curve_shadow_transparency(
kg, isect.object, isect.prim, isect.u);

View File

@ -28,6 +28,7 @@
* without new features slowing things down.
*
* BVH_HAIR: hair curve rendering
* BVH_POINTCLOUD: point cloud rendering
* BVH_MOTION: motion blur rendering
*/
@ -165,7 +166,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
case PRIMITIVE_CURVE_RIBBON:
case PRIMITIVE_MOTION_CURVE_RIBBON: {
for (; prim_addr < prim_addr2; prim_addr++) {
if ((type & PRIMITIVE_ALL_MOTION) && kernel_data.bvh.use_bvh_steps) {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
if (ray->time < prim_time.x || ray->time > prim_time.y) {
continue;
@ -188,6 +189,33 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
break;
}
#endif /* BVH_FEATURE(BVH_HAIR) */
#if BVH_FEATURE(BVH_POINTCLOUD)
case PRIMITIVE_POINT:
case PRIMITIVE_MOTION_POINT: {
for (; prim_addr < prim_addr2; prim_addr++) {
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr);
if (ray->time < prim_time.x || ray->time > prim_time.y) {
continue;
}
}
const int point_object = (object == OBJECT_NONE) ?
kernel_tex_fetch(__prim_object, prim_addr) :
object;
const int point_prim = kernel_tex_fetch(__prim_index, prim_addr);
const int point_type = kernel_tex_fetch(__prim_type, prim_addr);
const bool hit = point_intersect(
kg, isect, P, dir, isect->t, point_object, point_prim, ray->time, point_type);
if (hit) {
/* shadow ray early termination */
if (visibility & PATH_RAY_SHADOW_OPAQUE)
return true;
}
}
break;
}
#endif /* BVH_FEATURE(BVH_POINTCLOUD) */
}
}
else {

View File

@ -34,6 +34,7 @@ CCL_NAMESPACE_BEGIN
#define BVH_MOTION 1
#define BVH_HAIR 2
#define BVH_POINTCLOUD 4
#define BVH_NAME_JOIN(x, y) x##_##y
#define BVH_NAME_EVAL(x, y) BVH_NAME_JOIN(x, y)

View File

@ -118,14 +118,16 @@ ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals kg,
{
int shader = 0;
#ifdef __HAIR__
if (type & PRIMITIVE_ALL_TRIANGLE)
#endif
{
if (type & PRIMITIVE_TRIANGLE) {
shader = kernel_tex_fetch(__tri_shader, prim);
}
#ifdef __POINTCLOUD__
else if (type & PRIMITIVE_POINT) {
shader = kernel_tex_fetch(__points_shader, prim);
}
#endif
#ifdef __HAIR__
else {
else if (type & PRIMITIVE_CURVE) {
shader = kernel_tex_fetch(__curves, prim).shader_id;
}
#endif
@ -139,14 +141,16 @@ ccl_device_forceinline int intersection_get_shader_from_isect_prim(KernelGlobals
{
int shader = 0;
#ifdef __HAIR__
if (isect_type & PRIMITIVE_ALL_TRIANGLE)
#endif
{
if (isect_type & PRIMITIVE_TRIANGLE) {
shader = kernel_tex_fetch(__tri_shader, prim);
}
#ifdef __POINTCLOUD__
else if (isect_type & PRIMITIVE_POINT) {
shader = kernel_tex_fetch(__points_shader, prim);
}
#endif
#ifdef __HAIR__
else {
else if (isect_type & PRIMITIVE_CURVE) {
shader = kernel_tex_fetch(__curves, prim).shader_id;
}
#endif

View File

@ -124,7 +124,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
/* For curves use the smooth normal, particularly for ribbons the geometric
* normal gives too much darkening otherwise. */
int label;
const float3 Ng = (sd->type & PRIMITIVE_ALL_CURVE) ? sc->N : sd->Ng;
const float3 Ng = (sd->type & PRIMITIVE_CURVE) ? sc->N : sd->Ng;
switch (sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:

View File

@ -213,9 +213,7 @@ ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd,
/* TODO: we convert this value to a cosine later and discard the sign, so
* we could probably save some operations. */
float h = (sd->type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) ?
-sd->v :
dot(cross(sd->Ng, X), Z);
float h = (sd->type & PRIMITIVE_CURVE_RIBBON) ? -sd->v : dot(cross(sd->Ng, X), Z);
kernel_assert(fabsf(h) < 1.0f + 1e-4f);
kernel_assert(isfinite3_safe(Y));

View File

@ -211,7 +211,7 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
}
/* Always use baked shadow transparency for curves. */
if (type & PRIMITIVE_ALL_CURVE) {
if (type & PRIMITIVE_CURVE) {
float throughput = payload.throughput;
throughput *= context.intersection_curve_shadow_transparency(nullptr, object, prim, u);
payload.throughput = throughput;
@ -476,7 +476,7 @@ __intersection__curve_ribbon(constant KernelParamsMetal &launch_params_metal [[b
result.continue_search = true;
result.distance = ray_tmax;
if (segment.type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
if (segment.type & PRIMITIVE_CURVE_RIBBON) {
metalrt_intersection_curve(launch_params_metal, payload, object, segment.prim, segment.type, ray_origin, ray_direction,
# if defined(__METALRT_MOTION__)
payload.time,
@ -507,7 +507,7 @@ __intersection__curve_ribbon_shadow(constant KernelParamsMetal &launch_params_me
result.continue_search = true;
result.distance = ray_tmax;
if (segment.type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
if (segment.type & PRIMITIVE_CURVE_RIBBON) {
metalrt_intersection_curve_shadow(launch_params_metal, payload, object, segment.prim, segment.type, ray_origin, ray_direction,
# if defined(__METALRT_MOTION__)
payload.time,

View File

@ -97,9 +97,9 @@ extern "C" __global__ void __miss__kernel_optix_miss()
extern "C" __global__ void __anyhit__kernel_optix_local_hit()
{
#ifdef __HAIR__
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (!optixIsTriangleHit()) {
/* Ignore curves. */
/* Ignore curves and points. */
return optixIgnoreIntersection();
}
#endif
@ -194,7 +194,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
type = kernel_tex_fetch(__objects, object).primitive_type;
}
# ifdef __HAIR__
else {
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
u = __uint_as_float(optixGetAttribute_0());
v = __uint_as_float(optixGetAttribute_1());
@ -210,6 +210,11 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
# endif
}
# endif
else {
type = kernel_tex_fetch(__objects, object).primitive_type;
u = 0.0f;
v = 0.0f;
}
# ifndef __TRANSPARENT_SHADOWS__
/* No transparent shadows support compiled in, make opaque. */
@ -229,7 +234,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
}
/* Always use baked shadow transparency for curves. */
if (type & PRIMITIVE_ALL_CURVE) {
if (type & PRIMITIVE_CURVE) {
float throughput = __uint_as_float(optixGetPayload_1());
throughput *= intersection_curve_shadow_transparency(nullptr, object, prim, u);
optixSetPayload_1(__float_as_uint(throughput));
@ -291,7 +296,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
extern "C" __global__ void __anyhit__kernel_optix_volume_test()
{
#ifdef __HAIR__
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (!optixIsTriangleHit()) {
/* Ignore curves. */
return optixIgnoreIntersection();
@ -315,7 +320,7 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
{
#ifdef __HAIR__
# if OPTIX_ABI_VERSION < 55
if (!optixIsTriangleHit()) {
if (optixGetPrimitiveType() == OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE) {
/* Filter out curve endcaps. */
const float u = __uint_as_float(optixGetAttribute_0());
if (u == 0.0f || u == 1.0f) {
@ -354,13 +359,19 @@ extern "C" __global__ void __closesthit__kernel_optix_hit()
optixSetPayload_3(prim);
optixSetPayload_5(kernel_tex_fetch(__objects, object).primitive_type);
}
else {
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, prim);
optixSetPayload_1(optixGetAttribute_0()); /* Same as 'optixGetCurveParameter()' */
optixSetPayload_2(optixGetAttribute_1());
optixSetPayload_3(segment.prim);
optixSetPayload_5(segment.type);
}
else {
optixSetPayload_1(0);
optixSetPayload_2(0);
optixSetPayload_3(prim);
optixSetPayload_5(kernel_tex_fetch(__objects, object).primitive_type);
}
}
#ifdef __HAIR__
@ -395,6 +406,7 @@ ccl_device_inline void optix_intersection_curve(const int prim, const int type)
isect.t *= len;
if (curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) {
static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use");
optixReportIntersection(isect.t / len,
type & PRIMITIVE_ALL,
__float_as_int(isect.u), /* Attribute_0 */
@ -407,8 +419,50 @@ extern "C" __global__ void __intersection__curve_ribbon()
const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, optixGetPrimitiveIndex());
const int prim = segment.prim;
const int type = segment.type;
if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
if (type & PRIMITIVE_CURVE_RIBBON) {
optix_intersection_curve(prim, type);
}
}
#endif
#ifdef __POINTCLOUD__
extern "C" __global__ void __intersection__point()
{
const int prim = optixGetPrimitiveIndex();
const int object = get_object_id();
const int type = kernel_tex_fetch(__objects, object).primitive_type;
# ifdef __VISIBILITY_FLAG__
const uint visibility = optixGetPayload_4();
if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) {
return;
}
# endif
float3 P = optixGetObjectRayOrigin();
float3 dir = optixGetObjectRayDirection();
/* The direction is not normalized by default, the point intersection routine expects that. */
float len;
dir = normalize_len(dir, &len);
# ifdef __OBJECT_MOTION__
const float time = optixGetRayTime();
# else
const float time = 0.0f;
# endif
Intersection isect;
isect.t = optixGetRayTmax();
/* Transform maximum distance into object space. */
if (isect.t != FLT_MAX) {
isect.t *= len;
}
if (point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) {
static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use");
optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL);
}
}
#endif

View File

@ -36,7 +36,7 @@ ccl_device_inline uint subd_triangle_patch(KernelGlobals kg, ccl_private const S
ccl_device_inline uint attribute_primitive_type(KernelGlobals kg, ccl_private const ShaderData *sd)
{
if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && subd_triangle_patch(kg, sd) != ~0) {
if ((sd->type & PRIMITIVE_TRIANGLE) && subd_triangle_patch(kg, sd) != ~0) {
return ATTR_PRIM_SUBD;
}
else {

View File

@ -205,14 +205,14 @@ ccl_device float curve_thickness(KernelGlobals kg, ccl_private const ShaderData
{
float r = 0.0f;
if (sd->type & PRIMITIVE_ALL_CURVE) {
if (sd->type & PRIMITIVE_CURVE) {
KernelCurve curve = kernel_tex_fetch(__curves, sd->prim);
int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
int k1 = k0 + 1;
float4 P_curve[2];
if (!(sd->type & PRIMITIVE_ALL_MOTION)) {
if (!(sd->type & PRIMITIVE_MOTION)) {
P_curve[0] = kernel_tex_fetch(__curve_keys, k0);
P_curve[1] = kernel_tex_fetch(__curve_keys, k1);
}
@ -249,7 +249,7 @@ ccl_device float3 curve_tangent_normal(KernelGlobals kg, ccl_private const Shade
{
float3 tgN = make_float3(0.0f, 0.0f, 0.0f);
if (sd->type & PRIMITIVE_ALL_CURVE) {
if (sd->type & PRIMITIVE_CURVE) {
tgN = -(-sd->I - sd->dPdu * (dot(sd->dPdu, -sd->I) / len_squared(sd->dPdu)));
tgN = normalize(tgN);

View File

@ -635,7 +635,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg,
float time,
int type)
{
const bool is_motion = (type & PRIMITIVE_ALL_MOTION);
const bool is_motion = (type & PRIMITIVE_MOTION);
KernelCurve kcurve = kernel_tex_fetch(__curves, prim);
@ -655,7 +655,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg,
motion_curve_keys(kg, object, prim, time, ka, k0, k1, kb, curve);
}
if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
if (type & PRIMITIVE_CURVE_RIBBON) {
/* todo: adaptive number of subdivisions could help performance here. */
const int subdivisions = kernel_data.bvh.curve_subdivisions;
if (ribbon_intersect(P, dir, tmax, subdivisions, curve, isect)) {
@ -704,7 +704,7 @@ ccl_device_inline void curve_shader_setup(KernelGlobals kg,
float4 P_curve[4];
if (!(sd->type & PRIMITIVE_ALL_MOTION)) {
if (!(sd->type & PRIMITIVE_MOTION)) {
P_curve[0] = kernel_tex_fetch(__curve_keys, ka);
P_curve[1] = kernel_tex_fetch(__curve_keys, k0);
P_curve[2] = kernel_tex_fetch(__curve_keys, k1);
@ -719,7 +719,7 @@ ccl_device_inline void curve_shader_setup(KernelGlobals kg,
const float4 dPdu4 = catmull_rom_basis_derivative(P_curve, sd->u);
const float3 dPdu = float4_to_float3(dPdu4);
if (sd->type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) {
if (sd->type & PRIMITIVE_CURVE_RIBBON) {
/* Rounded smooth normals for ribbons, to approximate thick curve shape. */
const float3 tangent = normalize(dPdu);
const float3 bitangent = normalize(cross(tangent, -D));
@ -727,8 +727,6 @@ ccl_device_inline void curve_shader_setup(KernelGlobals kg,
const float cosine = safe_sqrtf(1.0f - sine * sine);
sd->N = normalize(sine * bitangent - cosine * normalize(cross(tangent, bitangent)));
sd->Ng = -D;
# if 0
/* This approximates the position and geometric normal of a thick curve too,
* but gives too many issues with wrong self intersections. */
@ -744,25 +742,27 @@ ccl_device_inline void curve_shader_setup(KernelGlobals kg,
/* NOTE: It is possible that P will be the same as P_inside (precision issues, or very small
* radius). In this case use the view direction to approximate the normal. */
const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, sd->u));
const float3 Ng = (!isequal_float3(P, P_inside)) ? normalize(P - P_inside) : -sd->I;
const float3 N = (!isequal_float3(P, P_inside)) ? normalize(P - P_inside) : -sd->I;
sd->N = Ng;
sd->Ng = Ng;
sd->N = N;
sd->v = 0.0f;
}
# ifdef __DPDU__
/* dPdu/dPdv */
sd->dPdu = dPdu;
sd->dPdv = cross(dPdu, sd->Ng);
# endif
/* Convert to world space. */
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
const Transform tfm = object_get_transform(kg, sd);
P = transform_point(&tfm, P);
object_position_transform_auto(kg, sd, &P);
object_normal_transform_auto(kg, sd, &sd->N);
object_dir_transform_auto(kg, sd, &sd->dPdu);
}
sd->P = P;
sd->Ng = (sd->type & PRIMITIVE_CURVE_RIBBON) ? sd->I : sd->N;
sd->dPdv = cross(sd->dPdu, sd->Ng);
sd->shader = kernel_tex_fetch(__curves, sd->prim).shader_id;
}

View File

@ -29,6 +29,9 @@
#include "kernel/geom/motion_triangle_intersect.h"
#include "kernel/geom/motion_triangle_shader.h"
#include "kernel/geom/motion_curve.h"
#include "kernel/geom/motion_point.h"
#include "kernel/geom/point.h"
#include "kernel/geom/point_intersect.h"
#include "kernel/geom/curve.h"
#include "kernel/geom/curve_intersect.h"
#include "kernel/geom/volume.h"

View File

@ -37,16 +37,21 @@ ccl_device_inline float primitive_surface_attribute_float(KernelGlobals kg,
ccl_private float *dx,
ccl_private float *dy)
{
if (sd->type & PRIMITIVE_ALL_TRIANGLE) {
if (sd->type & PRIMITIVE_TRIANGLE) {
if (subd_triangle_patch(kg, sd) == ~0)
return triangle_attribute_float(kg, sd, desc, dx, dy);
else
return subd_triangle_attribute_float(kg, sd, desc, dx, dy);
}
#ifdef __HAIR__
else if (sd->type & PRIMITIVE_ALL_CURVE) {
else if (sd->type & PRIMITIVE_CURVE) {
return curve_attribute_float(kg, sd, desc, dx, dy);
}
#endif
#ifdef __POINTCLOUD__
else if (sd->type & PRIMITIVE_POINT) {
return point_attribute_float(kg, sd, desc, dx, dy);
}
#endif
else {
if (dx)
@ -63,16 +68,21 @@ ccl_device_inline float2 primitive_surface_attribute_float2(KernelGlobals kg,
ccl_private float2 *dx,
ccl_private float2 *dy)
{
if (sd->type & PRIMITIVE_ALL_TRIANGLE) {
if (sd->type & PRIMITIVE_TRIANGLE) {
if (subd_triangle_patch(kg, sd) == ~0)
return triangle_attribute_float2(kg, sd, desc, dx, dy);
else
return subd_triangle_attribute_float2(kg, sd, desc, dx, dy);
}
#ifdef __HAIR__
else if (sd->type & PRIMITIVE_ALL_CURVE) {
else if (sd->type & PRIMITIVE_CURVE) {
return curve_attribute_float2(kg, sd, desc, dx, dy);
}
#endif
#ifdef __POINTCLOUD__
else if (sd->type & PRIMITIVE_POINT) {
return point_attribute_float2(kg, sd, desc, dx, dy);
}
#endif
else {
if (dx)
@ -89,16 +99,21 @@ ccl_device_inline float3 primitive_surface_attribute_float3(KernelGlobals kg,
ccl_private float3 *dx,
ccl_private float3 *dy)
{
if (sd->type & PRIMITIVE_ALL_TRIANGLE) {
if (sd->type & PRIMITIVE_TRIANGLE) {
if (subd_triangle_patch(kg, sd) == ~0)
return triangle_attribute_float3(kg, sd, desc, dx, dy);
else
return subd_triangle_attribute_float3(kg, sd, desc, dx, dy);
}
#ifdef __HAIR__
else if (sd->type & PRIMITIVE_ALL_CURVE) {
else if (sd->type & PRIMITIVE_CURVE) {
return curve_attribute_float3(kg, sd, desc, dx, dy);
}
#endif
#ifdef __POINTCLOUD__
else if (sd->type & PRIMITIVE_POINT) {
return point_attribute_float3(kg, sd, desc, dx, dy);
}
#endif
else {
if (dx)
@ -115,16 +130,21 @@ ccl_device_forceinline float4 primitive_surface_attribute_float4(KernelGlobals k
ccl_private float4 *dx,
ccl_private float4 *dy)
{
if (sd->type & PRIMITIVE_ALL_TRIANGLE) {
if (sd->type & PRIMITIVE_TRIANGLE) {
if (subd_triangle_patch(kg, sd) == ~0)
return triangle_attribute_float4(kg, sd, desc, dx, dy);
else
return subd_triangle_attribute_float4(kg, sd, desc, dx, dy);
}
#ifdef __HAIR__
else if (sd->type & PRIMITIVE_ALL_CURVE) {
else if (sd->type & PRIMITIVE_CURVE) {
return curve_attribute_float4(kg, sd, desc, dx, dy);
}
#endif
#ifdef __POINTCLOUD__
else if (sd->type & PRIMITIVE_POINT) {
return point_attribute_float4(kg, sd, desc, dx, dy);
}
#endif
else {
if (dx)
@ -225,8 +245,8 @@ ccl_device bool primitive_ptex(KernelGlobals kg,
ccl_device float3 primitive_tangent(KernelGlobals kg, ccl_private ShaderData *sd)
{
#ifdef __HAIR__
if (sd->type & PRIMITIVE_ALL_CURVE)
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (sd->type & (PRIMITIVE_CURVE | PRIMITIVE_POINT))
# ifdef __DPDU__
return normalize(sd->dPdu);
# else
@ -261,10 +281,21 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals kg,
/* center position */
float3 center;
#ifdef __HAIR__
bool is_curve_primitive = sd->type & PRIMITIVE_ALL_CURVE;
if (is_curve_primitive) {
center = curve_motion_center_location(kg, sd);
#if defined(__HAIR__) || defined(__POINTCLOUD__)
bool is_curve_or_point = sd->type & (PRIMITIVE_CURVE | PRIMITIVE_POINT);
if (is_curve_or_point) {
center = make_float3(0.0f, 0.0f, 0.0f);
if (sd->type & PRIMITIVE_CURVE) {
# if defined(__HAIR__)
center = curve_motion_center_location(kg, sd);
# endif
}
else if (sd->type & PRIMITIVE_POINT) {
# if defined(__POINTCLOUD__)
center = point_motion_center_location(kg, sd);
# endif
}
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &center);
@ -272,7 +303,9 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals kg,
}
else
#endif
{
center = sd->P;
}
float3 motion_pre = center, motion_post = center;
@ -284,8 +317,8 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals kg,
int numverts, numkeys;
object_motion_info(kg, sd->object, NULL, &numverts, &numkeys);
#ifdef __HAIR__
if (is_curve_primitive) {
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (is_curve_or_point) {
motion_pre = float4_to_float3(curve_attribute_float4(kg, sd, desc, NULL, NULL));
desc.offset += numkeys;
motion_post = float4_to_float3(curve_attribute_float4(kg, sd, desc, NULL, NULL));
@ -298,7 +331,7 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals kg,
}
else
#endif
if (sd->type & PRIMITIVE_ALL_TRIANGLE) {
if (sd->type & PRIMITIVE_TRIANGLE) {
/* Triangle */
if (subd_triangle_patch(kg, sd) == ~0) {
motion_pre = triangle_attribute_float3(kg, sd, desc, NULL, NULL);

View File

@ -69,49 +69,58 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg,
sd->I = -ray->D;
#ifdef __HAIR__
if (sd->type & PRIMITIVE_ALL_CURVE) {
if (sd->type & PRIMITIVE_CURVE) {
/* curve */
curve_shader_setup(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim);
}
else
#endif
if (sd->type & PRIMITIVE_TRIANGLE) {
/* static triangle */
float3 Ng = triangle_normal(kg, sd);
sd->shader = kernel_tex_fetch(__tri_shader, sd->prim);
#ifdef __POINTCLOUD__
if (sd->type & PRIMITIVE_POINT) {
/* point */
point_shader_setup(kg, sd, isect, ray);
}
else
#endif
{
if (sd->type == PRIMITIVE_TRIANGLE) {
/* static triangle */
float3 Ng = triangle_normal(kg, sd);
sd->shader = kernel_tex_fetch(__tri_shader, sd->prim);
/* vectors */
sd->P = triangle_refine(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim);
sd->Ng = Ng;
sd->N = Ng;
/* vectors */
sd->P = triangle_refine(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim);
sd->Ng = Ng;
sd->N = Ng;
/* smooth normal */
if (sd->shader & SHADER_SMOOTH_NORMAL)
sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v);
/* smooth normal */
if (sd->shader & SHADER_SMOOTH_NORMAL)
sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v);
#ifdef __DPDU__
/* dPdu/dPdv */
triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv);
/* dPdu/dPdv */
triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv);
#endif
}
else {
/* motion triangle */
motion_triangle_shader_setup(
kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim, false);
}
}
else {
/* motion triangle */
motion_triangle_shader_setup(
kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim, false);
}
sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags;
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
/* instance transform */
object_normal_transform_auto(kg, sd, &sd->N);
object_normal_transform_auto(kg, sd, &sd->Ng);
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
/* instance transform */
object_normal_transform_auto(kg, sd, &sd->N);
object_normal_transform_auto(kg, sd, &sd->Ng);
#ifdef __DPDU__
object_dir_transform_auto(kg, sd, &sd->dPdu);
object_dir_transform_auto(kg, sd, &sd->dPdv);
object_dir_transform_auto(kg, sd, &sd->dPdu);
object_dir_transform_auto(kg, sd, &sd->dPdv);
#endif
}
}
sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags;
/* backfacing test */
bool backfacing = (dot(sd->Ng, sd->I) < 0.0f);
@ -194,7 +203,7 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
object_dir_transform_auto(kg, sd, &sd->I);
}
if (sd->type & PRIMITIVE_TRIANGLE) {
if (sd->type == PRIMITIVE_TRIANGLE) {
/* smooth normal */
if (sd->shader & SHADER_SMOOTH_NORMAL) {
sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v);

View File

@ -82,7 +82,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
# ifdef __HAIR__
if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) &&
(sd->type & PRIMITIVE_ALL_TRIANGLE))
(sd->type & PRIMITIVE_TRIANGLE))
# else
if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS))
# endif

View File

@ -191,7 +191,7 @@ ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg,
float3 Ng = (transmit ? -sd->Ng : sd->Ng);
float3 P = ray_offset(sd->P, Ng);
if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) {
if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) {
const float offset_cutoff =
kernel_tex_fetch(__objects, sd->object).shadow_terminator_geometry_offset;
/* Do ray offset (heavy stuff) only for close to be terminated triangles:

View File

@ -28,6 +28,7 @@
#include "scene/colorspace.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
#include "scene/scene.h"
#include "kernel/osl/closures.h"
@ -113,6 +114,8 @@ ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness");
ustring OSLRenderServices::u_curve_length("geom:curve_length");
ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
ustring OSLRenderServices::u_curve_random("geom:curve_random");
ustring OSLRenderServices::u_is_point("geom:is_point");
ustring OSLRenderServices::u_point_radius("geom:point_radius");
ustring OSLRenderServices::u_normal_map_normal("geom:normal_map_normal");
ustring OSLRenderServices::u_path_ray_length("path:ray_length");
ustring OSLRenderServices::u_path_ray_depth("path:ray_depth");
@ -957,13 +960,15 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg
return set_attribute_int(3, type, derivatives, val);
}
else if ((name == u_geom_trianglevertices || name == u_geom_polyvertices) &&
sd->type & PRIMITIVE_ALL_TRIANGLE) {
sd->type & PRIMITIVE_TRIANGLE) {
float3 P[3];
if (sd->type & PRIMITIVE_TRIANGLE)
triangle_vertices(kg, sd->prim, P);
else
if (sd->type & PRIMITIVE_MOTION) {
motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, P);
}
else {
triangle_vertices(kg, sd->prim, P);
}
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &P[0]);
@ -983,7 +988,7 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg
}
/* Hair Attributes */
else if (name == u_is_curve) {
float f = (sd->type & PRIMITIVE_ALL_CURVE) != 0;
float f = (sd->type & PRIMITIVE_CURVE) != 0;
return set_attribute_float(f, type, derivatives, val);
}
else if (name == u_curve_thickness) {
@ -994,8 +999,17 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg
float3 f = curve_tangent_normal(kg, sd);
return set_attribute_float3(f, type, derivatives, val);
}
/* point attributes */
else if (name == u_is_point) {
float f = (sd->type & PRIMITIVE_POINT) != 0;
return set_attribute_float(f, type, derivatives, val);
}
else if (name == u_point_radius) {
float f = point_radius(kg, sd);
return set_attribute_float(f, type, derivatives, val);
}
else if (name == u_normal_map_normal) {
if (sd->type & PRIMITIVE_ALL_TRIANGLE) {
if (sd->type & PRIMITIVE_TRIANGLE) {
float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v);
return set_attribute_float3(f, type, derivatives, val);
}

View File

@ -297,6 +297,8 @@ class OSLRenderServices : public OSL::RendererServices {
static ustring u_curve_length;
static ustring u_curve_tangent_normal;
static ustring u_curve_random;
static ustring u_is_point;
static ustring u_point_radius;
static ustring u_normal_map_normal;
static ustring u_path_ray_length;
static ustring u_path_ray_depth;

View File

@ -206,12 +206,12 @@ ccl_device float3 svm_bevel(
for (int hit = 0; hit < num_eval_hits; hit++) {
/* Quickly retrieve P and Ng without setting up ShaderData. */
float3 hit_P;
if (sd->type & PRIMITIVE_TRIANGLE) {
if (sd->type == PRIMITIVE_TRIANGLE) {
hit_P = triangle_refine_local(
kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim);
}
# ifdef __OBJECT_MOTION__
else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) {
else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
float3 verts[3];
motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts);
hit_P = motion_triangle_refine_local(
@ -236,11 +236,11 @@ ccl_device float3 svm_bevel(
float u = isect.hits[hit].u;
float v = isect.hits[hit].v;
if (sd->type & PRIMITIVE_TRIANGLE) {
if (sd->type == PRIMITIVE_TRIANGLE) {
N = triangle_smooth_normal(kg, N, prim, u, v);
}
# ifdef __OBJECT_MOTION__
else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) {
else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
N = motion_triangle_smooth_normal(kg, N, sd->object, prim, u, v, sd->time);
}
# endif /* __OBJECT_MOTION__ */

View File

@ -107,7 +107,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
}
float3 N = stack_valid(data_node.x) ? stack_load_float3(stack, data_node.x) : sd->N;
if (!(sd->type & PRIMITIVE_ALL_CURVE)) {
if (!(sd->type & PRIMITIVE_CURVE)) {
N = ensure_valid_reflection(sd->Ng, sd->I, N);
}
@ -191,7 +191,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
float3 clearcoat_normal = stack_valid(data_cn_ssr.x) ?
stack_load_float3(stack, data_cn_ssr.x) :
sd->N;
if (!(sd->type & PRIMITIVE_ALL_CURVE)) {
if (!(sd->type & PRIMITIVE_CURVE)) {
clearcoat_normal = ensure_valid_reflection(sd->Ng, sd->I, clearcoat_normal);
}
float3 subsurface_radius = stack_valid(data_cn_ssr.y) ?
@ -902,7 +902,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
if (stack_valid(data_node.y)) {
bsdf->T = normalize(stack_load_float3(stack, data_node.y));
}
else if (!(sd->type & PRIMITIVE_ALL_CURVE)) {
else if (!(sd->type & PRIMITIVE_CURVE)) {
bsdf->T = normalize(sd->dPdv);
bsdf->offset = 0.0f;
}

View File

@ -227,7 +227,7 @@ ccl_device_noinline void svm_node_hair_info(KernelGlobals kg,
switch (type) {
case NODE_INFO_CURVE_IS_STRAND: {
data = (sd->type & PRIMITIVE_ALL_CURVE) != 0;
data = (sd->type & PRIMITIVE_CURVE) != 0;
stack_store_float(stack, out_offset, data);
break;
}

View File

@ -291,7 +291,7 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
if (space == NODE_NORMAL_MAP_TANGENT) {
/* tangent space */
if (sd->object == OBJECT_NONE || (sd->type & PRIMITIVE_ALL_TRIANGLE) == 0) {
if (sd->object == OBJECT_NONE || (sd->type & PRIMITIVE_TRIANGLE) == 0) {
/* Fallback to unperturbed normal. */
stack_store_float3(stack, normal_offset, sd->N);
return;

View File

@ -42,8 +42,8 @@ ccl_device_inline float wireframe(KernelGlobals kg,
int pixel_size,
ccl_private float3 *P)
{
#ifdef __HAIR__
if (sd->prim != PRIM_NONE && sd->type & PRIMITIVE_ALL_TRIANGLE)
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (sd->prim != PRIM_NONE && sd->type & PRIMITIVE_TRIANGLE)
#else
if (sd->prim != PRIM_NONE)
#endif
@ -54,10 +54,12 @@ ccl_device_inline float wireframe(KernelGlobals kg,
/* Triangles */
int np = 3;
if (sd->type & PRIMITIVE_TRIANGLE)
triangle_vertices(kg, sd->prim, Co);
else
if (sd->type & PRIMITIVE_MOTION) {
motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, Co);
}
else {
triangle_vertices(kg, sd->prim, Co);
}
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &Co[0]);

View File

@ -55,6 +55,10 @@ KERNEL_TEX(KernelCurveSegment, __curve_segments)
/* patches */
KERNEL_TEX(uint, __patches)
/* pointclouds */
KERNEL_TEX(float4, __points)
KERNEL_TEX(uint, __points_shader)
/* attributes */
KERNEL_TEX(uint4, __attributes_map)
KERNEL_TEX(float, __attributes_float)

View File

@ -86,6 +86,7 @@ CCL_NAMESPACE_BEGIN
#define __AO__
#define __PASSES__
#define __HAIR__
#define __POINTCLOUD__
#define __SVM__
#define __EMISSION__
#define __HOLDOUT__
@ -125,6 +126,9 @@ CCL_NAMESPACE_BEGIN
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_HAIR)
# undef __HAIR__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_POINTCLOUD)
# undef __POINTCLOUD__
# endif
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_VOLUME)
# undef __VOLUME__
# endif
@ -533,28 +537,34 @@ typedef struct Intersection {
typedef enum PrimitiveType {
PRIMITIVE_NONE = 0,
PRIMITIVE_TRIANGLE = (1 << 0),
PRIMITIVE_MOTION_TRIANGLE = (1 << 1),
PRIMITIVE_CURVE_THICK = (1 << 2),
PRIMITIVE_MOTION_CURVE_THICK = (1 << 3),
PRIMITIVE_CURVE_RIBBON = (1 << 4),
PRIMITIVE_MOTION_CURVE_RIBBON = (1 << 5),
PRIMITIVE_VOLUME = (1 << 6),
PRIMITIVE_LAMP = (1 << 7),
PRIMITIVE_CURVE_THICK = (1 << 1),
PRIMITIVE_CURVE_RIBBON = (1 << 2),
PRIMITIVE_POINT = (1 << 3),
PRIMITIVE_VOLUME = (1 << 4),
PRIMITIVE_LAMP = (1 << 5),
PRIMITIVE_ALL_TRIANGLE = (PRIMITIVE_TRIANGLE | PRIMITIVE_MOTION_TRIANGLE),
PRIMITIVE_ALL_CURVE = (PRIMITIVE_CURVE_THICK | PRIMITIVE_MOTION_CURVE_THICK |
PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON),
PRIMITIVE_ALL_VOLUME = (PRIMITIVE_VOLUME),
PRIMITIVE_ALL_MOTION = (PRIMITIVE_MOTION_TRIANGLE | PRIMITIVE_MOTION_CURVE_THICK |
PRIMITIVE_MOTION_CURVE_RIBBON),
PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE | PRIMITIVE_ALL_CURVE | PRIMITIVE_ALL_VOLUME |
PRIMITIVE_LAMP),
PRIMITIVE_MOTION = (1 << 6),
PRIMITIVE_MOTION_TRIANGLE = (PRIMITIVE_TRIANGLE | PRIMITIVE_MOTION),
PRIMITIVE_MOTION_CURVE_THICK = (PRIMITIVE_CURVE_THICK | PRIMITIVE_MOTION),
PRIMITIVE_MOTION_CURVE_RIBBON = (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION),
PRIMITIVE_MOTION_POINT = (PRIMITIVE_POINT | PRIMITIVE_MOTION),
PRIMITIVE_NUM = 8,
PRIMITIVE_CURVE = (PRIMITIVE_CURVE_THICK | PRIMITIVE_CURVE_RIBBON),
PRIMITIVE_ALL = (PRIMITIVE_TRIANGLE | PRIMITIVE_CURVE | PRIMITIVE_POINT | PRIMITIVE_VOLUME |
PRIMITIVE_LAMP | PRIMITIVE_MOTION),
PRIMITIVE_NUM_SHAPES = 6,
PRIMITIVE_NUM_BITS = PRIMITIVE_NUM_SHAPES + 1, /* All shapes + motion bit. */
PRIMITIVE_NUM = PRIMITIVE_NUM_SHAPES * 2, /* With and without motion. */
} PrimitiveType;
#define PRIMITIVE_PACK_SEGMENT(type, segment) ((segment << PRIMITIVE_NUM) | (type))
#define PRIMITIVE_UNPACK_SEGMENT(type) (type >> PRIMITIVE_NUM)
/* Convert type to index in range 0..PRIMITIVE_NUM-1. */
#define PRIMITIVE_INDEX(type) (bitscan((uint32_t)(type)) * 2 + (((type)&PRIMITIVE_MOTION) ? 1 : 0))
/* Pack segment into type value to save space. */
#define PRIMITIVE_PACK_SEGMENT(type, segment) ((segment << PRIMITIVE_NUM_BITS) | (type))
#define PRIMITIVE_UNPACK_SEGMENT(type) (type >> PRIMITIVE_NUM_BITS)
typedef enum CurveShapeType {
CURVE_RIBBON = 0,
@ -605,6 +615,7 @@ typedef enum AttributeStandard {
ATTR_STD_CURVE_INTERCEPT,
ATTR_STD_CURVE_LENGTH,
ATTR_STD_CURVE_RANDOM,
ATTR_STD_POINT_RANDOM,
ATTR_STD_PTEX_FACE_ID,
ATTR_STD_PTEX_UV,
ATTR_STD_VOLUME_DENSITY,
@ -1604,6 +1615,9 @@ enum KernelFeatureFlag : unsigned int {
KERNEL_FEATURE_AO_PASS = (1U << 25U),
KERNEL_FEATURE_AO_ADDITIVE = (1U << 26U),
KERNEL_FEATURE_AO = (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE),
/* Point clouds. */
KERNEL_FEATURE_POINTCLOUD = (1U << 27U),
};
/* Shader node feature mask, to specialize shader evaluation for kernels. */

View File

@ -40,6 +40,7 @@ set(SRC
mesh_displace.cpp
mesh_subdivision.cpp
procedural.cpp
pointcloud.cpp
object.cpp
osl.cpp
particles.cpp
@ -81,6 +82,7 @@ set(SRC_HEADERS
particles.h
pass.h
procedural.h
pointcloud.h
curves.h
scene.h
shader.h

View File

@ -21,6 +21,7 @@
#include "scene/curves.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
#include "scene/scene.h"
#include "scene/shader.h"
@ -99,6 +100,9 @@ void CachedData::clear()
triangles.clear();
uv_loops.clear();
vertices.clear();
points.clear();
radiuses.clear();
points_shader.clear();
for (CachedAttribute &attr : attributes) {
attr.data.clear();
@ -146,6 +150,9 @@ bool CachedData::is_constant() const
CHECK_IF_CONSTANT(triangles)
CHECK_IF_CONSTANT(uv_loops)
CHECK_IF_CONSTANT(vertices)
CHECK_IF_CONSTANT(points)
CHECK_IF_CONSTANT(radiuses)
CHECK_IF_CONSTANT(points_shader)
for (const CachedAttribute &attr : attributes) {
if (!attr.data.is_constant()) {
@ -185,6 +192,9 @@ void CachedData::invalidate_last_loaded_time(bool attributes_only)
triangles.invalidate_last_loaded_time();
uv_loops.invalidate_last_loaded_time();
vertices.invalidate_last_loaded_time();
points.invalidate_last_loaded_time();
radiuses.invalidate_last_loaded_time();
points_shader.invalidate_last_loaded_time();
}
void CachedData::set_time_sampling(TimeSampling time_sampling)
@ -206,6 +216,9 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
triangles.set_time_sampling(time_sampling);
uv_loops.set_time_sampling(time_sampling);
vertices.set_time_sampling(time_sampling);
points.set_time_sampling(time_sampling);
radiuses.set_time_sampling(time_sampling);
points_shader.set_time_sampling(time_sampling);
for (CachedAttribute &attr : attributes) {
attr.data.set_time_sampling(time_sampling);
@ -233,6 +246,9 @@ size_t CachedData::memory_used() const
mem_used += triangles.memory_used();
mem_used += uv_loops.memory_used();
mem_used += vertices.memory_used();
mem_used += points.memory_used();
mem_used += radiuses.memory_used();
mem_used += points_shader.memory_used();
for (const CachedAttribute &attr : attributes) {
mem_used += attr.data.memory_used();
@ -901,6 +917,9 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
else if (object->schema_type == AlembicObject::CURVES) {
read_curves(object, frame_time);
}
else if (object->schema_type == AlembicObject::POINTS) {
read_points(object, frame_time);
}
else if (object->schema_type == AlembicObject::SUBD) {
read_subd(object, frame_time);
}
@ -970,6 +989,9 @@ void AlembicProcedural::load_objects(Progress &progress)
if (abc_object->schema_type == AlembicObject::CURVES) {
geometry = scene_->create_node<Hair>();
}
else if (abc_object->schema_type == AlembicObject::POINTS) {
geometry = scene_->create_node<PointCloud>();
}
else if (abc_object->schema_type == AlembicObject::POLY_MESH ||
abc_object->schema_type == AlembicObject::SUBD) {
geometry = scene_->create_node<Mesh>();
@ -1202,6 +1224,45 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t fra
hair->tag_update(scene_, rebuild);
}
void AlembicProcedural::read_points(AlembicObject *abc_object, Abc::chrono_t frame_time)
{
CachedData &cached_data = abc_object->get_cached_data();
/* update sockets */
Object *object = abc_object->get_object();
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
if (object->is_modified()) {
object->tag_update(scene_);
}
/* Only update sockets for the original Geometry. */
if (abc_object->instance_of) {
return;
}
PointCloud *point_cloud = static_cast<PointCloud *>(object->get_geometry());
/* Make sure shader ids are also updated. */
if (point_cloud->used_shaders_is_modified()) {
point_cloud->tag_shader_modified();
}
cached_data.points.copy_to_socket(frame_time, point_cloud, point_cloud->get_points_socket());
cached_data.radiuses.copy_to_socket(frame_time, point_cloud, point_cloud->get_radius_socket());
cached_data.points_shader.copy_to_socket(
frame_time, point_cloud, point_cloud->get_shader_socket());
/* update attributes */
update_attributes(point_cloud->attributes, cached_data, frame_time);
const bool rebuild = (point_cloud->points_is_modified() || point_cloud->radius_is_modified() ||
point_cloud->shader_is_modified());
point_cloud->tag_update(scene_, rebuild);
}
void AlembicProcedural::walk_hierarchy(
IObject parent,
const ObjectHeader &header,
@ -1314,7 +1375,23 @@ void AlembicProcedural::walk_hierarchy(
// ignore the face set, it will be read along with the data
}
else if (IPoints::matches(header)) {
// unsupported for now
IPoints points(parent, header.getName());
unordered_map<std::string, AlembicObject *>::const_iterator iter;
iter = object_map.find(points.getFullName());
if (iter != object_map.end()) {
AlembicObject *abc_object = iter->second;
abc_object->iobject = points;
abc_object->schema_type = AlembicObject::POINTS;
if (matrix_samples_data.samples) {
abc_object->xform_samples = *matrix_samples_data.samples;
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
}
}
next_object = points;
}
else if (INuPatch::matches(header)) {
// unsupported for now
@ -1391,6 +1468,14 @@ void AlembicProcedural::build_caches(Progress &progress)
object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
}
else if (object->schema_type == AlembicObject::POINTS) {
if (!object->has_data_loaded() || default_radius_is_modified() ||
object->radius_scale_is_modified()) {
IPoints points(object->iobject, Alembic::Abc::kWrapExisting);
IPointsSchema schema = points.getSchema();
object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
}
else if (object->schema_type == AlembicObject::SUBD) {
if (!object->has_data_loaded()) {
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);

View File

@ -327,6 +327,11 @@ struct CachedData {
DataStore<array<int>> curve_first_key;
DataStore<array<int>> curve_shader;
/* point data */
DataStore<array<float3>> points;
DataStore<array<float>> radiuses;
DataStore<array<int>> points_shader;
struct CachedAttribute {
AttributeStandard std;
AttributeElement element;
@ -414,6 +419,7 @@ class AlembicObject : public Node {
POLY_MESH,
SUBD,
CURVES,
POINTS,
};
bool need_shader_update = true;
@ -550,6 +556,10 @@ class AlembicProcedural : public Procedural {
* Object Nodes in the Cycles scene if none exist yet. */
void read_curves(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
/* Read the data for an IPoints at the specified frame_time. Creates corresponding Geometry and
* Object Nodes in the Cycles scene if none exist yet. */
void read_points(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
/* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and
* Object Nodes in the Cycles scene if none exist yet. */
void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);

View File

@ -609,6 +609,55 @@ void read_geometry_data(AlembicProcedural *proc,
read_data_loop(proc, cached_data, data, read_curves_data, progress);
}
/* Points Geometries. */
static void read_points_data(CachedData &cached_data, const PointsSchemaData &data, chrono_t time)
{
const ISampleSelector iss = ISampleSelector(time);
const P3fArraySamplePtr position = data.positions.getValue(iss);
FloatArraySamplePtr radiuses;
array<float3> a_positions;
array<float> a_radius;
array<int> a_shader;
a_positions.reserve(position->size());
a_radius.reserve(position->size());
a_shader.reserve(position->size());
if (data.radiuses.valid()) {
IFloatGeomParam::Sample wsample = data.radiuses.getExpandedValue(iss);
radiuses = wsample.getVals();
}
const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius;
int offset = 0;
for (size_t i = 0; i < position->size(); i++) {
const V3f &f = position->get()[offset + i];
a_positions.push_back_slow(make_float3_from_yup(f));
if (do_radius) {
radius = (*radiuses)[offset + i];
a_radius.push_back_slow(radius);
}
a_shader.push_back_slow((int)0);
}
cached_data.points.add_data(a_positions, time);
cached_data.radiuses.add_data(a_radius, time);
cached_data.points_shader.add_data(a_shader, time);
}
void read_geometry_data(AlembicProcedural *proc,
CachedData &cached_data,
const PointsSchemaData &data,
Progress &progress)
{
read_data_loop(proc, cached_data, data, read_points_data, progress);
}
/* Attributes conversions. */
/* Type traits for converting between Alembic and Cycles types.

View File

@ -122,6 +122,26 @@ void read_geometry_data(AlembicProcedural *proc,
const CurvesSchemaData &data,
Progress &progress);
/* Data of a ICurvesSchema that we need to read. */
struct PointsSchemaData {
Alembic::AbcGeom::TimeSamplingPtr time_sampling;
size_t num_samples;
float default_radius;
float radius_scale;
Alembic::AbcGeom::IP3fArrayProperty positions;
Alembic::AbcGeom::IInt32ArrayProperty num_points;
Alembic::AbcGeom::IFloatGeomParam radiuses;
// Those are unsupported for now.
Alembic::AbcGeom::IV3fArrayProperty velocities;
};
void read_geometry_data(AlembicProcedural *proc,
CachedData &cached_data,
const PointsSchemaData &data,
Progress &progress);
void read_attributes(AlembicProcedural *proc,
CachedData &cache,
const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,

View File

@ -18,6 +18,7 @@
#include "scene/hair.h"
#include "scene/image.h"
#include "scene/mesh.h"
#include "scene/pointcloud.h"
#include "util/foreach.h"
#include "util/log.h"
@ -205,6 +206,10 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
size -= mesh->get_num_subd_verts();
}
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
size = pointcloud->num_points();
}
break;
case ATTR_ELEMENT_VERTEX_MOTION:
if (geom->geometry_type == Geometry::MESH) {
@ -215,6 +220,10 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
size -= mesh->get_num_subd_verts() * (mesh->get_motion_steps() - 1);
}
}
else if (geom->geometry_type == Geometry::POINTCLOUD) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
size = pointcloud->num_points() * (pointcloud->get_motion_steps() - 1);
}
break;
case ATTR_ELEMENT_FACE:
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
@ -346,6 +355,8 @@ const char *Attribute::standard_name(AttributeStandard std)
return "curve_length";
case ATTR_STD_CURVE_RANDOM:
return "curve_random";
case ATTR_STD_POINT_RANDOM:
return "point_random";
case ATTR_STD_PTEX_FACE_ID:
return "ptex_face_id";
case ATTR_STD_PTEX_UV:
@ -555,6 +566,28 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
break;
}
}
else if (geometry->geometry_type == Geometry::POINTCLOUD) {
switch (std) {
case ATTR_STD_UV:
attr = add(name, TypeFloat2, ATTR_ELEMENT_VERTEX);
break;
case ATTR_STD_GENERATED:
attr = add(name, TypeDesc::TypePoint, ATTR_ELEMENT_VERTEX);
break;
case ATTR_STD_MOTION_VERTEX_POSITION:
attr = add(name, TypeDesc::TypeFloat4, ATTR_ELEMENT_VERTEX_MOTION);
break;
case ATTR_STD_POINT_RANDOM:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
break;
case ATTR_STD_GENERATED_TRANSFORM:
attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH);
break;
default:
assert(0);
break;
}
}
else if (geometry->geometry_type == Geometry::VOLUME) {
switch (std) {
case ATTR_STD_VERTEX_NORMAL:

View File

@ -26,6 +26,7 @@
#include "scene/light.h"
#include "scene/mesh.h"
#include "scene/object.h"
#include "scene/pointcloud.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/shader_nodes.h"
@ -240,6 +241,7 @@ void Geometry::compute_bvh(
params->use_bvh_unaligned_nodes;
bparams.num_motion_triangle_steps = params->num_bvh_time_steps;
bparams.num_motion_curve_steps = params->num_bvh_time_steps;
bparams.num_motion_point_steps = params->num_bvh_time_steps;
bparams.bvh_type = params->bvh_type;
bparams.curve_subdivisions = params->curve_subdivisions();
@ -723,6 +725,12 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
else if (element == ATTR_ELEMENT_CURVE_KEY_MOTION)
offset -= hair->curve_key_offset;
}
else if (geom->is_pointcloud()) {
if (element == ATTR_ELEMENT_VERTEX)
offset -= geom->prim_offset;
else if (element == ATTR_ELEMENT_VERTEX_MOTION)
offset -= geom->prim_offset;
}
}
else {
/* attribute not found */
@ -1006,6 +1014,8 @@ void GeometryManager::mesh_calc_offset(Scene *scene, BVHLayout bvh_layout)
size_t curve_key_size = 0;
size_t curve_segment_size = 0;
size_t point_size = 0;
size_t patch_size = 0;
size_t face_size = 0;
size_t corner_size = 0;
@ -1054,6 +1064,14 @@ void GeometryManager::mesh_calc_offset(Scene *scene, BVHLayout bvh_layout)
curve_key_size += hair->get_curve_keys().size();
curve_segment_size += hair->num_segments();
}
else if (geom->is_pointcloud()) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
prim_offset_changed = (pointcloud->prim_offset != point_size);
pointcloud->prim_offset = point_size;
point_size += pointcloud->num_points();
}
if (prim_offset_changed) {
/* Need to rebuild BVH in OptiX, since refit only allows modified mesh data there */
@ -1079,6 +1097,8 @@ void GeometryManager::device_update_mesh(Device *,
size_t curve_size = 0;
size_t curve_segment_size = 0;
size_t point_size = 0;
size_t patch_size = 0;
foreach (Geometry *geom, scene->geometry) {
@ -1106,6 +1126,10 @@ void GeometryManager::device_update_mesh(Device *,
curve_size += hair->num_curves();
curve_segment_size += hair->num_segments();
}
else if (geom->is_pointcloud()) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
point_size += pointcloud->num_points();
}
}
/* Fill in all the arrays. */
@ -1201,6 +1225,26 @@ void GeometryManager::device_update_mesh(Device *,
dscene->curve_segments.copy_to_device_if_modified();
}
if (point_size != 0) {
progress.set_status("Updating Mesh", "Copying Point clouds to device");
float4 *points = dscene->points.alloc(point_size);
uint *points_shader = dscene->points_shader.alloc(point_size);
foreach (Geometry *geom, scene->geometry) {
if (geom->is_pointcloud()) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
pointcloud->pack(
scene, &points[pointcloud->prim_offset], &points_shader[pointcloud->prim_offset]);
if (progress.get_cancel())
return;
}
}
dscene->points.copy_to_device();
dscene->points_shader.copy_to_device();
}
if (patch_size != 0 && dscene->patches.need_realloc()) {
progress.set_status("Updating Mesh", "Copying Patches to device");
@ -1242,6 +1286,7 @@ void GeometryManager::device_update_bvh(Device *device,
scene->params.use_bvh_unaligned_nodes;
bparams.num_motion_triangle_steps = scene->params.num_bvh_time_steps;
bparams.num_motion_curve_steps = scene->params.num_bvh_time_steps;
bparams.num_motion_point_steps = scene->params.num_bvh_time_steps;
bparams.bvh_type = scene->params.bvh_type;
bparams.curve_subdivisions = scene->params.curve_subdivisions();
@ -1323,26 +1368,30 @@ void GeometryManager::device_update_bvh(Device *device,
enum {
DEVICE_CURVE_DATA_MODIFIED = (1 << 0),
DEVICE_MESH_DATA_MODIFIED = (1 << 1),
DEVICE_POINT_DATA_MODIFIED = (1 << 2),
ATTR_FLOAT_MODIFIED = (1 << 2),
ATTR_FLOAT2_MODIFIED = (1 << 3),
ATTR_FLOAT3_MODIFIED = (1 << 4),
ATTR_FLOAT4_MODIFIED = (1 << 5),
ATTR_UCHAR4_MODIFIED = (1 << 6),
ATTR_FLOAT_MODIFIED = (1 << 3),
ATTR_FLOAT2_MODIFIED = (1 << 4),
ATTR_FLOAT3_MODIFIED = (1 << 5),
ATTR_FLOAT4_MODIFIED = (1 << 6),
ATTR_UCHAR4_MODIFIED = (1 << 7),
CURVE_DATA_NEED_REALLOC = (1 << 7),
MESH_DATA_NEED_REALLOC = (1 << 8),
CURVE_DATA_NEED_REALLOC = (1 << 8),
MESH_DATA_NEED_REALLOC = (1 << 9),
POINT_DATA_NEED_REALLOC = (1 << 10),
ATTR_FLOAT_NEEDS_REALLOC = (1 << 9),
ATTR_FLOAT2_NEEDS_REALLOC = (1 << 10),
ATTR_FLOAT3_NEEDS_REALLOC = (1 << 11),
ATTR_FLOAT4_NEEDS_REALLOC = (1 << 12),
ATTR_UCHAR4_NEEDS_REALLOC = (1 << 13),
ATTR_FLOAT_NEEDS_REALLOC = (1 << 11),
ATTR_FLOAT2_NEEDS_REALLOC = (1 << 12),
ATTR_FLOAT3_NEEDS_REALLOC = (1 << 13),
ATTR_FLOAT4_NEEDS_REALLOC = (1 << 14),
ATTR_UCHAR4_NEEDS_REALLOC = (1 << 15),
ATTRS_NEED_REALLOC = (ATTR_FLOAT_NEEDS_REALLOC | ATTR_FLOAT2_NEEDS_REALLOC |
ATTR_FLOAT3_NEEDS_REALLOC | ATTR_FLOAT4_NEEDS_REALLOC |
ATTR_UCHAR4_NEEDS_REALLOC),
DEVICE_MESH_DATA_NEEDS_REALLOC = (MESH_DATA_NEED_REALLOC | ATTRS_NEED_REALLOC),
DEVICE_POINT_DATA_NEEDS_REALLOC = (POINT_DATA_NEED_REALLOC | ATTRS_NEED_REALLOC),
DEVICE_CURVE_DATA_NEEDS_REALLOC = (CURVE_DATA_NEED_REALLOC | ATTRS_NEED_REALLOC),
};
@ -1529,6 +1578,17 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
device_update_flags |= DEVICE_MESH_DATA_MODIFIED;
}
}
if (geom->is_pointcloud()) {
PointCloud *pointcloud = static_cast<PointCloud *>(geom);
if (pointcloud->need_update_rebuild) {
device_update_flags |= DEVICE_POINT_DATA_NEEDS_REALLOC;
}
else if (pointcloud->is_modified()) {
device_update_flags |= DEVICE_POINT_DATA_MODIFIED;
}
}
}
if (update_flags & (MESH_ADDED | MESH_REMOVED)) {
@ -1539,10 +1599,15 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
device_update_flags |= DEVICE_CURVE_DATA_NEEDS_REALLOC;
}
if (update_flags & (POINT_ADDED | POINT_REMOVED)) {
device_update_flags |= DEVICE_POINT_DATA_NEEDS_REALLOC;
}
/* tag the device arrays for reallocation or modification */
DeviceScene *dscene = &scene->dscene;
if (device_update_flags & (DEVICE_MESH_DATA_NEEDS_REALLOC | DEVICE_CURVE_DATA_NEEDS_REALLOC)) {
if (device_update_flags & (DEVICE_MESH_DATA_NEEDS_REALLOC | DEVICE_CURVE_DATA_NEEDS_REALLOC |
DEVICE_POINT_DATA_NEEDS_REALLOC)) {
delete scene->bvh;
scene->bvh = nullptr;
@ -1570,6 +1635,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
dscene->curve_keys.tag_realloc();
dscene->curve_segments.tag_realloc();
}
if (device_update_flags & DEVICE_POINT_DATA_NEEDS_REALLOC) {
dscene->points.tag_realloc();
dscene->points_shader.tag_realloc();
}
}
if ((update_flags & VISIBILITY_MODIFIED) != 0) {
@ -1630,6 +1700,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
dscene->curve_segments.tag_modified();
}
if (device_update_flags & DEVICE_POINT_DATA_MODIFIED) {
dscene->points.tag_modified();
dscene->points_shader.tag_modified();
}
need_flags_update = false;
}
@ -2064,6 +2139,8 @@ void GeometryManager::device_update(Device *device,
dscene->curves.clear_modified();
dscene->curve_keys.clear_modified();
dscene->curve_segments.clear_modified();
dscene->points.clear_modified();
dscene->points_shader.clear_modified();
dscene->patches.clear_modified();
dscene->attributes_map.clear_modified();
dscene->attributes_float.clear_modified();
@ -2092,6 +2169,8 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc
dscene->curves.free_if_need_realloc(force_free);
dscene->curve_keys.free_if_need_realloc(force_free);
dscene->curve_segments.free_if_need_realloc(force_free);
dscene->points.free_if_need_realloc(force_free);
dscene->points_shader.free_if_need_realloc(force_free);
dscene->patches.free_if_need_realloc(force_free);
dscene->attributes_map.free_if_need_realloc(force_free);
dscene->attributes_float.free_if_need_realloc(force_free);

View File

@ -55,6 +55,7 @@ class Geometry : public Node {
MESH,
HAIR,
VOLUME,
POINTCLOUD,
};
Type geometry_type;
@ -155,6 +156,11 @@ class Geometry : public Node {
return geometry_type == HAIR;
}
bool is_pointcloud() const
{
return geometry_type == POINTCLOUD;
}
bool is_volume() const
{
return geometry_type == VOLUME;
@ -181,12 +187,14 @@ class GeometryManager {
MESH_REMOVED = (1 << 5),
HAIR_ADDED = (1 << 6),
HAIR_REMOVED = (1 << 7),
POINT_ADDED = (1 << 12),
POINT_REMOVED = (1 << 13),
SHADER_ATTRIBUTE_MODIFIED = (1 << 8),
SHADER_DISPLACEMENT_MODIFIED = (1 << 9),
GEOMETRY_ADDED = MESH_ADDED | HAIR_ADDED,
GEOMETRY_REMOVED = MESH_REMOVED | HAIR_REMOVED,
GEOMETRY_ADDED = MESH_ADDED | HAIR_ADDED | POINT_ADDED,
GEOMETRY_REMOVED = MESH_REMOVED | HAIR_REMOVED | POINT_REMOVED,
TRANSFORM_MODIFIED = (1 << 10),

View File

@ -23,6 +23,7 @@
#include "scene/light.h"
#include "scene/mesh.h"
#include "scene/particles.h"
#include "scene/pointcloud.h"
#include "scene/scene.h"
#include "scene/stats.h"
#include "scene/volume.h"
@ -69,6 +70,7 @@ struct UpdateObjectTransformState {
/* Flags which will be synchronized to Integrator. */
bool have_motion;
bool have_curves;
// bool have_points;
/* ** Scheduling queue. ** */
Scene *scene;
@ -435,7 +437,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
state->have_motion = true;
}
if (geom->geometry_type == Geometry::MESH) {
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::POINTCLOUD) {
/* TODO: why only mesh? */
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) {
@ -491,6 +493,8 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
kobject.dupli_generated[2] = ob->dupli_generated[2];
kobject.numkeys = (geom->geometry_type == Geometry::HAIR) ?
static_cast<Hair *>(geom)->get_curve_keys().size() :
(geom->geometry_type == Geometry::POINTCLOUD) ?
static_cast<PointCloud *>(geom)->num_points() :
0;
kobject.dupli_uv[0] = ob->dupli_uv[0];
kobject.dupli_uv[1] = ob->dupli_uv[1];

View File

@ -30,6 +30,7 @@
#include "scene/object.h"
#include "scene/osl.h"
#include "scene/particles.h"
#include "scene/pointcloud.h"
#include "scene/procedural.h"
#include "scene/scene.h"
#include "scene/shader.h"
@ -64,6 +65,8 @@ DeviceScene::DeviceScene(Device *device)
curve_keys(device, "__curve_keys", MEM_GLOBAL),
curve_segments(device, "__curve_segments", MEM_GLOBAL),
patches(device, "__patches", MEM_GLOBAL),
points(device, "__points", MEM_GLOBAL),
points_shader(device, "__points_shader", MEM_GLOBAL),
objects(device, "__objects", MEM_GLOBAL),
object_motion_pass(device, "__object_motion_pass", MEM_GLOBAL),
object_motion(device, "__object_motion", MEM_GLOBAL),
@ -523,6 +526,9 @@ void Scene::update_kernel_features()
else if (geom->is_hair()) {
kernel_features |= KERNEL_FEATURE_HAIR;
}
else if (geom->is_pointcloud()) {
kernel_features |= KERNEL_FEATURE_POINTCLOUD;
}
}
if (bake_manager->get_baking()) {
@ -575,6 +581,7 @@ static void log_kernel_features(const uint features)
VLOG(2) << "Use Path Tracing " << string_from_bool(features & KERNEL_FEATURE_PATH_TRACING)
<< "\n";
VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_HAIR) << "\n";
VLOG(2) << "Use Pointclouds " << string_from_bool(features & KERNEL_FEATURE_POINTCLOUD) << "\n";
VLOG(2) << "Use Object Motion " << string_from_bool(features & KERNEL_FEATURE_OBJECT_MOTION)
<< "\n";
VLOG(2) << "Use Camera Motion " << string_from_bool(features & KERNEL_FEATURE_CAMERA_MOTION)
@ -757,6 +764,15 @@ template<> Volume *Scene::create_node<Volume>()
return node;
}
template<> PointCloud *Scene::create_node<PointCloud>()
{
PointCloud *node = new PointCloud();
node->set_owner(this);
geometry.push_back(node);
geometry_manager->tag_update(this, GeometryManager::POINT_ADDED);
return node;
}
template<> Object *Scene::create_node<Object>()
{
Object *node = new Object();
@ -844,6 +860,12 @@ template<> void Scene::delete_node_impl(Volume *node)
geometry_manager->tag_update(this, GeometryManager::MESH_REMOVED);
}
template<> void Scene::delete_node_impl(PointCloud *node)
{
delete_node_from_array(geometry, static_cast<Geometry *>(node));
geometry_manager->tag_update(this, GeometryManager::POINT_REMOVED);
}
template<> void Scene::delete_node_impl(Geometry *node)
{
uint flag;

View File

@ -54,6 +54,7 @@ class Object;
class ObjectManager;
class ParticleSystemManager;
class ParticleSystem;
class PointCloud;
class Procedural;
class ProceduralManager;
class CurveSystemManager;
@ -94,6 +95,10 @@ class DeviceScene {
device_vector<uint> patches;
/* pointcloud */
device_vector<float4> points;
device_vector<uint> points_shader;
/* objects */
device_vector<KernelObject> objects;
device_vector<Transform> object_motion_pass;
@ -365,6 +370,8 @@ template<> Hair *Scene::create_node<Hair>();
template<> Volume *Scene::create_node<Volume>();
template<> PointCloud *Scene::create_node<PointCloud>();
template<> ParticleSystem *Scene::create_node<ParticleSystem>();
template<> Shader *Scene::create_node<Shader>();
@ -379,6 +386,8 @@ template<> void Scene::delete_node_impl(Mesh *node);
template<> void Scene::delete_node_impl(Volume *node);
template<> void Scene::delete_node_impl(PointCloud *node);
template<> void Scene::delete_node_impl(Hair *node);
template<> void Scene::delete_node_impl(Geometry *node);

View File

@ -27,9 +27,21 @@
#ifdef WITH_LIBMV_GUARDED_ALLOC
# include "MEM_guardedalloc.h"
# define LIBMV_OBJECT_NEW OBJECT_GUARDED_NEW
# define LIBMV_OBJECT_DELETE OBJECT_GUARDED_DELETE
# define LIBMV_OBJECT_DELETE OBJECT_GUARDED_DELETE
# if defined __GNUC__
# define LIBMV_OBJECT_NEW(type, args...) \
new (MEM_mallocN(sizeof(type), __func__)) type(args)
# else
# define LIBMV_OBJECT_NEW(type, ...) \
new (MEM_mallocN(sizeof(type), __FUNCTION__)) type(__VA_ARGS__)
# endif
# define LIBMV_OBJECT_DELETE(what, type) \
{ \
if (what) { \
((type*)what)->~type(); \
MEM_freeN(what); \
} \
} \
(void)0
# define LIBMV_STRUCT_NEW(type, count) \
(type*)MEM_mallocN(sizeof(type) * count, __func__)
# define LIBMV_STRUCT_DELETE(what) MEM_freeN(what)

View File

@ -67,7 +67,7 @@ static void OCIO_reportException(Exception &exception)
OCIO_ConstConfigRcPtr *OCIOImpl::getCurrentConfig(void)
{
ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
ConstConfigRcPtr *config = MEM_new<ConstConfigRcPtr>(__func__);
try {
*config = GetCurrentConfig();
@ -79,7 +79,7 @@ OCIO_ConstConfigRcPtr *OCIOImpl::getCurrentConfig(void)
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
MEM_delete(config);
return NULL;
}
@ -96,7 +96,7 @@ void OCIOImpl::setCurrentConfig(const OCIO_ConstConfigRcPtr *config)
OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromEnv(void)
{
ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
ConstConfigRcPtr *config = MEM_new<ConstConfigRcPtr>(__func__);
try {
*config = Config::CreateFromEnv();
@ -108,14 +108,14 @@ OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromEnv(void)
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
MEM_delete(config);
return NULL;
}
OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromFile(const char *filename)
{
ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
ConstConfigRcPtr *config = MEM_new<ConstConfigRcPtr>(__func__);
try {
*config = Config::CreateFromFile(filename);
@ -127,14 +127,14 @@ OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromFile(const char *filename)
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
MEM_delete(config);
return NULL;
}
void OCIOImpl::configRelease(OCIO_ConstConfigRcPtr *config)
{
OBJECT_GUARDED_DELETE((ConstConfigRcPtr *)config, ConstConfigRcPtr);
MEM_delete((ConstConfigRcPtr *)config);
}
int OCIOImpl::configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config)
@ -164,7 +164,7 @@ const char *OCIOImpl::configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *conf
OCIO_ConstColorSpaceRcPtr *OCIOImpl::configGetColorSpace(OCIO_ConstConfigRcPtr *config,
const char *name)
{
ConstColorSpaceRcPtr *cs = OBJECT_GUARDED_NEW(ConstColorSpaceRcPtr);
ConstColorSpaceRcPtr *cs = MEM_new<ConstColorSpaceRcPtr>(__func__);
try {
*cs = (*(ConstConfigRcPtr *)config)->getColorSpace(name);
@ -176,7 +176,7 @@ OCIO_ConstColorSpaceRcPtr *OCIOImpl::configGetColorSpace(OCIO_ConstConfigRcPtr *
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(cs, ConstColorSpaceRcPtr);
MEM_delete(cs);
return NULL;
}
@ -380,7 +380,7 @@ const char *OCIOImpl::configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, in
OCIO_ConstLookRcPtr *OCIOImpl::configGetLook(OCIO_ConstConfigRcPtr *config, const char *name)
{
ConstLookRcPtr *look = OBJECT_GUARDED_NEW(ConstLookRcPtr);
ConstLookRcPtr *look = MEM_new<ConstLookRcPtr>(__func__);
try {
*look = (*(ConstConfigRcPtr *)config)->getLook(name);
@ -392,7 +392,7 @@ OCIO_ConstLookRcPtr *OCIOImpl::configGetLook(OCIO_ConstConfigRcPtr *config, cons
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(look, ConstLookRcPtr);
MEM_delete(look);
return NULL;
}
@ -404,7 +404,7 @@ const char *OCIOImpl::lookGetProcessSpace(OCIO_ConstLookRcPtr *look)
void OCIOImpl::lookRelease(OCIO_ConstLookRcPtr *look)
{
OBJECT_GUARDED_DELETE((ConstLookRcPtr *)look, ConstLookRcPtr);
MEM_delete((ConstLookRcPtr *)look);
}
int OCIOImpl::colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs_)
@ -521,14 +521,14 @@ void OCIOImpl::colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config_,
void OCIOImpl::colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs)
{
OBJECT_GUARDED_DELETE((ConstColorSpaceRcPtr *)cs, ConstColorSpaceRcPtr);
MEM_delete((ConstColorSpaceRcPtr *)cs);
}
OCIO_ConstProcessorRcPtr *OCIOImpl::configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config,
const char *srcName,
const char *dstName)
{
ConstProcessorRcPtr *processor = OBJECT_GUARDED_NEW(ConstProcessorRcPtr);
ConstProcessorRcPtr *processor = MEM_new<ConstProcessorRcPtr>(__func__);
try {
*processor = (*(ConstConfigRcPtr *)config)->getProcessor(srcName, dstName);
@ -540,20 +540,20 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::configGetProcessorWithNames(OCIO_ConstConfig
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(processor, ConstProcessorRcPtr);
MEM_delete(processor);
return 0;
}
void OCIOImpl::processorRelease(OCIO_ConstProcessorRcPtr *processor)
{
OBJECT_GUARDED_DELETE(processor, ConstProcessorRcPtr);
MEM_delete(processor);
}
OCIO_ConstCPUProcessorRcPtr *OCIOImpl::processorGetCPUProcessor(
OCIO_ConstProcessorRcPtr *processor)
{
ConstCPUProcessorRcPtr *cpu_processor = OBJECT_GUARDED_NEW(ConstCPUProcessorRcPtr);
ConstCPUProcessorRcPtr *cpu_processor = MEM_new<ConstCPUProcessorRcPtr>(__func__);
*cpu_processor = (*(ConstProcessorRcPtr *)processor)->getDefaultCPUProcessor();
return (OCIO_ConstCPUProcessorRcPtr *)cpu_processor;
}
@ -636,7 +636,7 @@ void OCIOImpl::cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_
void OCIOImpl::cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor)
{
OBJECT_GUARDED_DELETE(cpu_processor, ConstCPUProcessorRcPtr);
MEM_delete(cpu_processor);
}
const char *OCIOImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs)
@ -725,7 +725,7 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr
/* Create processor from transform. This is the moment were OCIO validates
* the entire transform, no need to check for the validity of inputs above. */
ConstProcessorRcPtr *p = OBJECT_GUARDED_NEW(ConstProcessorRcPtr);
ConstProcessorRcPtr *p = MEM_new<ConstProcessorRcPtr>(__func__);
try {
*p = config->getProcessor(group);
@ -737,7 +737,7 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(p, ConstProcessorRcPtr);
MEM_delete(p);
return NULL;
}
@ -771,7 +771,7 @@ OCIO_PackedImageDesc *OCIOImpl::createOCIO_PackedImageDesc(float *data,
void OCIOImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id)
{
OBJECT_GUARDED_DELETE((PackedImageDesc *)id, PackedImageDesc);
MEM_delete((PackedImageDesc *)id);
}
const char *OCIOImpl::getVersionString(void)

View File

@ -156,7 +156,7 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator)
OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
OpenSubdiv_TopologyRefiner *topology_refiner)
{
OpenSubdiv_Evaluator *evaluator = OBJECT_GUARDED_NEW(OpenSubdiv_Evaluator);
OpenSubdiv_Evaluator *evaluator = MEM_new<OpenSubdiv_Evaluator>(__func__);
assignFunctionPointers(evaluator);
evaluator->impl = openSubdiv_createEvaluatorInternal(topology_refiner);
return evaluator;
@ -165,5 +165,5 @@ OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator *evaluator)
{
openSubdiv_deleteEvaluatorInternal(evaluator->impl);
OBJECT_GUARDED_DELETE(evaluator, OpenSubdiv_Evaluator);
MEM_delete(evaluator);
}

View File

@ -225,7 +225,7 @@ void assignFunctionPointers(OpenSubdiv_TopologyRefiner *topology_refiner)
OpenSubdiv_TopologyRefiner *allocateTopologyRefiner()
{
OpenSubdiv_TopologyRefiner *topology_refiner = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefiner);
OpenSubdiv_TopologyRefiner *topology_refiner = MEM_new<OpenSubdiv_TopologyRefiner>(__func__);
assignFunctionPointers(topology_refiner);
return topology_refiner;
}
@ -252,7 +252,7 @@ OpenSubdiv_TopologyRefiner *openSubdiv_createTopologyRefinerFromConverter(
void openSubdiv_deleteTopologyRefiner(OpenSubdiv_TopologyRefiner *topology_refiner)
{
delete topology_refiner->impl;
OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefiner);
MEM_delete(topology_refiner);
}
bool openSubdiv_topologyRefinerCompareWithConverter(

@ -1 +1 @@
Subproject commit ece39d809ceb27b5a06da7c4b8f25ca77016b151
Subproject commit c2eeb9bb4732602c324a011f7f913a33a5e8b1f2

@ -1 +1 @@
Subproject commit 42da56aa73726710107031787af5eea186797984
Subproject commit 7936dde9ece881d531b1a2ee6c45ddb56d30038c

View File

@ -588,7 +588,7 @@ class TOPBAR_MT_edit(Menu):
layout.separator()
layout.operator("ed.undo_history", text="Undo History...")
layout.menu("TOPBAR_MT_undo_history")
layout.separator()

View File

@ -100,9 +100,17 @@ void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dva
*/
void driver_change_variable_type(struct DriverVar *dvar, int type);
/**
* Validate driver name (after being renamed).
* Validate driver variable name (after being renamed).
*
*/
void driver_variable_name_validate(struct DriverVar *dvar);
/**
* Ensure the driver variable's name is unique.
*
* Assumes the driver variable has already been assigned to the driver, so that
* the prev/next pointers can be used to find the other variables.
*/
void driver_variable_unique_name(struct DriverVar *dvar);
/**
* Add a new driver variable.
*/

View File

@ -50,9 +50,6 @@ typedef struct Global {
/** Last used location for library link/append. */
char lib[1024];
/** When set: `G_MAIN->filepath` contains valid relative base path. */
bool relbase_valid;
/**
* Strings of recently opened files to show in the file menu.
* A list of #RecentFile read from #BLENDER_HISTORY_FILE.

View File

@ -116,6 +116,7 @@ enum {
typedef struct Main {
struct Main *next, *prev;
/** The file-path of this blend file, an empty string indicates an unsaved file. */
char filepath[1024]; /* 1024 = FILE_MAX */
short versionfile, subversionfile; /* see BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION */
short minversionfile, minsubversionfile;

View File

@ -33,6 +33,10 @@
#include "RNA_types.h"
#ifdef __cplusplus
# include "BLI_string_ref.hh"
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -114,6 +118,7 @@ namespace nodes {
class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
class NodeDeclarationBuilder;
class GatherLinkSearchOpParams;
} // namespace nodes
namespace fn {
class CPPType;
@ -129,10 +134,15 @@ using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, voi
using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket,
void *r_value);
/* Adds socket link operations that are specific to this node type. */
using NodeGatherSocketLinkOperationsFunction =
void (*)(blender::nodes::GatherLinkSearchOpParams &params);
#else
typedef void *NodeMultiFunctionBuildFunction;
typedef void *NodeGeometryExecFunction;
typedef void *NodeDeclareFunction;
typedef void *NodeGatherSocketLinkOperationsFunction;
typedef void *SocketGetCPPTypeFunction;
typedef void *SocketGetGeometryNodesCPPTypeFunction;
typedef void *SocketGetGeometryNodesCPPValueFunction;
@ -284,7 +294,7 @@ typedef struct bNodeType {
/**
* Can this node type be added to a node tree?
* \param r_disabled_hint: Optional hint to display in the UI when the poll fails.
* \param r_disabled_hint: Hint to display in the UI when the poll fails.
* The callback can set this to a static string without having to
* null-check it (or without setting it to null if it's not used).
* The caller must pass a valid `const char **` and null-initialize it
@ -325,6 +335,13 @@ typedef struct bNodeType {
/* Declaration to be used when it is not dynamic. */
NodeDeclarationHandle *fixed_declaration;
/**
* Add to the list of search names and operations gathered by node link drag searching.
* Usually it isn't necessary to override the default behavior here, but a node type can have
* custom behavior here like adding custom search items.
*/
NodeGatherSocketLinkOperationsFunction gather_link_search_ops;
/** True when the node cannot be muted. */
bool no_muting;
@ -402,7 +419,7 @@ typedef struct bNodeTreeType {
/* Tree update. Overrides `nodetype->updatetreefunc` ! */
void (*update)(struct bNodeTree *ntree);
bool (*validate_link)(struct bNodeTree *ntree, struct bNodeLink *link);
bool (*validate_link)(eNodeSocketDatatype from, eNodeSocketDatatype to);
void (*node_add_init)(struct bNodeTree *ntree, struct bNode *bnode);
@ -943,10 +960,7 @@ bNodePreview *BKE_node_preview_verify(
struct bNodeInstanceHash *previews, bNodeInstanceKey key, int xsize, int ysize, bool create);
bNodePreview *BKE_node_preview_copy(struct bNodePreview *preview);
void BKE_node_preview_free(struct bNodePreview *preview);
void BKE_node_preview_init_tree(struct bNodeTree *ntree,
int xsize,
int ysize,
bool create_previews);
void BKE_node_preview_init_tree(struct bNodeTree *ntree, int xsize, int ysize);
void BKE_node_preview_free_tree(struct bNodeTree *ntree);
void BKE_node_preview_remove_unused(struct bNodeTree *ntree);
void BKE_node_preview_clear(struct bNodePreview *preview);
@ -957,14 +971,6 @@ void BKE_node_preview_merge_tree(struct bNodeTree *to_ntree,
struct bNodeTree *from_ntree,
bool remove_old);
/**
* Hack warning! this function is only used for shader previews,
* and since it gets called multiple times per pixel for Z-transparency we only add the color once.
* Preview gets cleared before it starts render though.
*/
void BKE_node_preview_set_pixel(
struct bNodePreview *preview, const float col[4], int x, int y, bool do_manage);
/** \} */
/* -------------------------------------------------------------------- */
@ -1153,7 +1159,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree,
#define SH_NODE_SEPRGB 120
#define SH_NODE_COMBRGB 121
#define SH_NODE_HUE_SAT 122
#define NODE_DYNAMIC 123
#define SH_NODE_OUTPUT_MATERIAL 124
#define SH_NODE_OUTPUT_WORLD 125
@ -1768,6 +1773,18 @@ extern struct bNodeSocketType NodeSocketTypeUndefined;
}
#endif
#ifdef __cplusplus
namespace blender::bke {
bNodeSocket *node_find_enabled_socket(bNode &node, eNodeSocketInOut in_out, StringRef name);
bNodeSocket *node_find_enabled_input_socket(bNode &node, StringRef name);
bNodeSocket *node_find_enabled_output_socket(bNode &node, StringRef name);
} // namespace blender::bke
#endif
#define NODE_STORAGE_FUNCS(StorageT) \
[[maybe_unused]] static StorageT &node_storage(bNode &node) \
{ \

View File

@ -91,7 +91,7 @@ typedef struct UndoStep {
/** When this is true, undo/memfile read code is allowed to re-use old data-blocks for unchanged
* IDs, and existing depsgraphes. This has to be forbidden in some cases (like renamed IDs). */
bool use_old_bmain_data;
/** For use by undo systems that accumulate changes (text editor, painting). */
/** For use by undo systems that accumulate changes (mesh-sculpt & image-painting). */
bool is_applied;
/* Over alloc 'type->struct_size'. */
} UndoStep;

View File

@ -124,7 +124,7 @@ set(SRC
intern/context.c
intern/crazyspace.c
intern/cryptomatte.cc
intern/curve.c
intern/curve.cc
intern/curve_bevel.c
intern/curve_convert.c
intern/curve_decimate.c

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,7 @@
struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset)
{
CurveProfile *profile = (CurveProfile *)MEM_callocN(sizeof(CurveProfile), __func__);
CurveProfile *profile = MEM_cnew<CurveProfile>(__func__);
BKE_curveprofile_set_defaults(profile);
profile->preset = preset;

View File

@ -1528,7 +1528,7 @@ void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
bool doit = false;
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
const int tot = (dl->type == DL_INDEX3) ? dl->nr : dl->nr * dl->parts;
const int tot = (ELEM(dl->type, DL_INDEX3, DL_INDEX4)) ? dl->nr : dl->nr * dl->parts;
for (const int i : IndexRange(tot)) {
minmax_v3v3_v3(min, max, &dl->verts[i * 3]);
}

View File

@ -29,6 +29,7 @@
#include "BLI_alloca.h"
#include "BLI_expr_pylike_eval.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string_utils.h"
#include "BLI_threads.h"
@ -864,6 +865,12 @@ void driver_variable_name_validate(DriverVar *dvar)
}
}
void driver_variable_unique_name(DriverVar *dvar)
{
ListBase variables = BLI_listbase_from_link((Link *)dvar);
BLI_uniquename(&variables, dvar, dvar->name, '_', offsetof(DriverVar, name), sizeof(dvar->name));
}
DriverVar *driver_add_new_variable(ChannelDriver *driver)
{
DriverVar *dvar;

View File

@ -31,6 +31,8 @@
#include "attribute_access_intern.hh"
#include "FN_cpp_type_make.hh"
using blender::float4x4;
using blender::Map;
using blender::MutableSpan;
@ -39,6 +41,8 @@ using blender::Span;
using blender::VectorSet;
using blender::fn::GSpan;
MAKE_CPP_TYPE(InstanceReference, InstanceReference, CPPTypeFlags::None)
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */

View File

@ -2062,9 +2062,10 @@ static void stampdata(
time_t t;
if (scene->r.stamp & R_STAMP_FILENAME) {
const char *blendfile_path = BKE_main_blendfile_path_from_global();
SNPRINTF(stamp_data->file,
do_prefix ? "File %s" : "%s",
G.relbase_valid ? BKE_main_blendfile_path_from_global() : "<untitled>");
(blendfile_path[0] != '\0') ? blendfile_path : "<untitled>");
}
else {
stamp_data->file[0] = '\0';

View File

@ -57,6 +57,7 @@
#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_memarena.h"
#include "BLI_string.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@ -452,8 +453,60 @@ typedef struct LibOverrideGroupTagData {
bool is_override;
/* Whether we are creating new override, or resyncing existing one. */
bool is_resync;
/* Mapping linked objects to all their instantiating collections (as a linked list).
* Avoids calling #BKE_collection_object_find over and over, this function is very expansive. */
GHash *linked_object_to_instantiating_collections;
MemArena *mem_arena;
} LibOverrideGroupTagData;
static void lib_override_group_tag_data_object_to_collection_init_collection_process(
LibOverrideGroupTagData *data, Collection *collection)
{
LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
Object *ob = collection_object->ob;
if (!ID_IS_LINKED(ob)) {
continue;
}
LinkNodePair **collections_linkedlist_p;
if (!BLI_ghash_ensure_p(data->linked_object_to_instantiating_collections,
ob,
(void ***)&collections_linkedlist_p)) {
*collections_linkedlist_p = BLI_memarena_calloc(data->mem_arena,
sizeof(**collections_linkedlist_p));
}
BLI_linklist_append_arena(*collections_linkedlist_p, collection, data->mem_arena);
}
}
/* Initialize complex data, `data` is expected to be already initialized with basic pointers and
* other simple data.
*
* NOTE: Currently creates a mapping from linked object to all of their instantiating collections
* (as returned by #BKE_collection_object_find). */
static void lib_override_group_tag_data_object_to_collection_init(LibOverrideGroupTagData *data)
{
data->mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
data->linked_object_to_instantiating_collections = BLI_ghash_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
if (data->scene != NULL) {
lib_override_group_tag_data_object_to_collection_init_collection_process(
data, data->scene->master_collection);
}
LISTBASE_FOREACH (Collection *, collection, &data->bmain->collections) {
lib_override_group_tag_data_object_to_collection_init_collection_process(data, collection);
}
}
static void lib_override_group_tag_data_clear(LibOverrideGroupTagData *data)
{
BLI_ghash_free(data->linked_object_to_instantiating_collections, NULL, NULL);
BLI_memarena_free(data->mem_arena);
memset(data, 0, sizeof(*data));
}
/* Tag all IDs in dependency relationships within an override hierarchy/group.
*
* Requires existing `Main.relations`.
@ -572,7 +625,6 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
Scene *scene = data->scene;
ID *id_root = data->id_root;
const bool is_resync = data->is_resync;
BLI_assert(!data->is_override);
@ -610,18 +662,26 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
Collection *instantiating_collection_override_candidate = NULL;
/* Loop over all collections instantiating the object, if we already have a 'locale' one we
* have nothing to do, otherwise try to find a 'linked' one that we can override too. */
while ((instantiating_collection = BKE_collection_object_find(
bmain, scene, instantiating_collection, ob)) != NULL) {
/* In (recursive) resync case, if a collection of a 'parent' lib instantiates the linked
* object, it is also fine. */
if (!ID_IS_LINKED(instantiating_collection) ||
(is_resync && ID_IS_LINKED(id_root) &&
instantiating_collection->id.lib->temp_index < id_root->lib->temp_index)) {
break;
}
if (ID_IS_LINKED(instantiating_collection) &&
(!is_resync || instantiating_collection->id.lib == id_root->lib)) {
instantiating_collection_override_candidate = instantiating_collection;
LinkNodePair *instantiating_collection_linklist = BLI_ghash_lookup(
data->linked_object_to_instantiating_collections, ob);
if (instantiating_collection_linklist != NULL) {
for (LinkNode *instantiating_collection_linknode =
instantiating_collection_linklist->list;
instantiating_collection_linknode != NULL;
instantiating_collection_linknode = instantiating_collection_linknode->next) {
instantiating_collection = instantiating_collection_linknode->link;
/* In (recursive) resync case, if a collection of a 'parent' lib instantiates the
* linked object, it is also fine. */
if (!ID_IS_LINKED(instantiating_collection) ||
(is_resync && ID_IS_LINKED(id_root) &&
instantiating_collection->id.lib->temp_index < id_root->lib->temp_index)) {
break;
}
if (ID_IS_LINKED(instantiating_collection) &&
(!is_resync || instantiating_collection->id.lib == id_root->lib)) {
instantiating_collection_override_candidate = instantiating_collection;
}
instantiating_collection = NULL;
}
}
@ -724,12 +784,14 @@ static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_roo
.missing_tag = LIB_TAG_MISSING,
.is_override = false,
.is_resync = false};
lib_override_group_tag_data_object_to_collection_init(&data);
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
lib_override_hierarchy_dependencies_recursive_tag(&data);
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
}
@ -1050,6 +1112,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
.missing_tag = LIB_TAG_MISSING,
.is_override = true,
.is_resync = true};
lib_override_group_tag_data_object_to_collection_init(&data);
lib_override_overrides_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@ -1140,6 +1203,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
lib_override_hierarchy_dependencies_recursive_tag(&data);
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
/* Make new override from linked data. */
/* Note that this call also remaps all pointers of tagged IDs from old override IDs to new
@ -1532,6 +1596,14 @@ static void lib_override_library_main_resync_on_library_indirect_level(
/* Detect all linked data that would need to be overridden if we had to create an override from
* those used by current existing overrides. */
LibOverrideGroupTagData data = {.bmain = bmain,
.scene = scene,
.id_root = NULL,
.tag = LIB_TAG_DOIT,
.missing_tag = LIB_TAG_MISSING,
.is_override = false,
.is_resync = true};
lib_override_group_tag_data_object_to_collection_init(&data);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
@ -1542,19 +1614,14 @@ static void lib_override_library_main_resync_on_library_indirect_level(
continue;
}
LibOverrideGroupTagData data = {.bmain = bmain,
.scene = scene,
.id_root = id->override_library->reference,
.tag = LIB_TAG_DOIT,
.missing_tag = LIB_TAG_MISSING,
.is_override = false,
.is_resync = true};
data.id_root = id->override_library->reference;
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
lib_override_hierarchy_dependencies_recursive_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
}
FOREACH_MAIN_ID_END;
lib_override_group_tag_data_clear(&data);
/* Now check existing overrides, those needing resync will be the one either already tagged as
* such, or the one using linked data that is now tagged as needing override. */
@ -1787,9 +1854,11 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
.missing_tag = LIB_TAG_MISSING,
.is_override = true,
.is_resync = false};
lib_override_group_tag_data_object_to_collection_init(&data);
lib_override_overrides_group_tag(&data);
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {

View File

@ -55,6 +55,8 @@
/* experimental (faster) normal calculation */
// #define USE_ACCUM_NORMAL
#define MBALL_ARRAY_LEN_INIT 4096
/* Data types */
typedef struct corner { /* corner of a cube */
@ -448,7 +450,7 @@ static void make_face(PROCESS *process, int i1, int i2, int i3, int i4)
#endif
if (UNLIKELY(process->totindex == process->curindex)) {
process->totindex += 4096;
process->totindex = process->totindex ? (process->totindex * 2) : MBALL_ARRAY_LEN_INIT;
process->indices = MEM_reallocN(process->indices, sizeof(int[4]) * process->totindex);
}
@ -946,8 +948,8 @@ static int getedge(EDGELIST *table[], int i1, int j1, int k1, int i2, int j2, in
*/
static void addtovertices(PROCESS *process, const float v[3], const float no[3])
{
if (process->curvertex == process->totvertex) {
process->totvertex += 4096;
if (UNLIKELY(process->curvertex == process->totvertex)) {
process->totvertex = process->totvertex ? process->totvertex * 2 : MBALL_ARRAY_LEN_INIT;
process->co = MEM_reallocN(process->co, process->totvertex * sizeof(float[3]));
process->no = MEM_reallocN(process->no, process->totvertex * sizeof(float[3]));
}
@ -1447,6 +1449,16 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
/* add resulting surface to displist */
if (process.curindex) {
/* Avoid over-allocation since this is stored in the displist. */
if (process.curindex != process.totindex) {
process.indices = MEM_reallocN(process.indices, sizeof(int[4]) * process.curindex);
}
if (process.curvertex != process.totvertex) {
process.co = MEM_reallocN(process.co, process.curvertex * sizeof(float[3]));
process.no = MEM_reallocN(process.no, process.curvertex * sizeof(float[3]));
}
dl = MEM_callocN(sizeof(DispList), "mballdisp");
BLI_addtail(dispbase, dl);
dl->type = DL_INDEX4;

View File

@ -917,7 +917,7 @@ static void curve_to_mesh_eval_ensure(Object &object)
* will have no modifiers. */
Object bevel_object = {{nullptr}};
if (curve.bevobj != nullptr) {
bevel_object = *curve.bevobj;
memcpy(&bevel_object, curve.bevobj, sizeof(bevel_object));
BLI_listbase_clear(&bevel_object.modifiers);
BKE_object_runtime_reset(&bevel_object);
curve.bevobj = &bevel_object;
@ -926,7 +926,7 @@ static void curve_to_mesh_eval_ensure(Object &object)
/* Same thing for taper. */
Object taper_object = {{nullptr}};
if (curve.taperobj != nullptr) {
taper_object = *curve.taperobj;
memcpy(&taper_object, curve.taperobj, sizeof(taper_object));
BLI_listbase_clear(&taper_object.modifiers);
BKE_object_runtime_reset(&taper_object);
curve.taperobj = &taper_object;
@ -1065,7 +1065,8 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
return nullptr;
}
Object object_for_eval = *object;
Object object_for_eval;
memcpy(&object_for_eval, object, sizeof(object_for_eval));
if (object_for_eval.runtime.data_orig != nullptr) {
object_for_eval.data = object_for_eval.runtime.data_orig;
}
@ -1440,7 +1441,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
/* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */
/* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh);
* check whether it is still true with Mesh */
Mesh tmp = *mesh_dst;
Mesh tmp;
memcpy(&tmp, mesh_dst, sizeof(tmp));
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;

View File

@ -933,7 +933,7 @@ const char *BKE_modifier_path_relbase(Main *bmain, Object *ob)
* - Else if the file has been saved return the blend file path.
* - Else if the file isn't saved and the ID isn't from a library, return the temp dir.
*/
if (G.relbase_valid || ID_IS_LINKED(ob)) {
if ((bmain->filepath[0] != '\0') || ID_IS_LINKED(ob)) {
return ID_BLEND_PATH(bmain, &ob->id);
}
@ -948,7 +948,8 @@ const char *BKE_modifier_path_relbase_from_global(Object *ob)
void BKE_modifier_path_init(char *path, int path_maxlen, const char *name)
{
BLI_join_dirfile(path, path_maxlen, G.relbase_valid ? "//" : BKE_tempdir_session(), name);
const char *blendfile_path = BKE_main_blendfile_path_from_global();
BLI_join_dirfile(path, path_maxlen, blendfile_path[0] ? "//" : BKE_tempdir_session(), name);
}
/**

View File

@ -102,6 +102,7 @@ using blender::MutableSpan;
using blender::Set;
using blender::Span;
using blender::Stack;
using blender::StringRef;
using blender::Vector;
using blender::VectorSet;
using blender::nodes::FieldInferencingInterface;
@ -260,8 +261,7 @@ static void ntree_free_data(ID *id)
/* XXX hack! node trees should not store execution graphs at all.
* This should be removed when old tree types no longer require it.
* Currently the execution data for texture nodes remains in the tree
* after execution, until the node tree is updated or freed.
*/
* after execution, until the node tree is updated or freed. */
if (ntree->execdata) {
switch (ntree->type) {
case NTREE_SHADER:
@ -277,7 +277,7 @@ static void ntree_free_data(ID *id)
/* XXX not nice, but needed to free localized node groups properly */
free_localized_node_groups(ntree);
/* unregister associated RNA types */
/* Unregister associated RNA types. */
ntreeInterfaceTypeFree(ntree);
BLI_freelistN(&ntree->links); /* do first, then unlink_node goes fast */
@ -521,7 +521,6 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
static void write_node_socket(BlendWriter *writer, bNodeSocket *sock)
{
/* actual socket writing */
BLO_write_struct(writer, bNodeSocket, sock);
if (sock->prop) {
@ -532,7 +531,6 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock)
}
static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock)
{
/* actual socket writing */
BLO_write_struct(writer, bNodeSocket, sock);
if (sock->prop) {
@ -546,8 +544,6 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
{
BKE_id_blend_write(writer, &ntree->id);
/* for link_list() speed, we write per list */
if (ntree->adt) {
BKE_animdata_blend_write(writer, ntree->adt);
}
@ -571,7 +567,6 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
if (node->storage) {
/* could be handlerized at some point, now only 1 exception still */
if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) &&
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) {
BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage);
@ -645,13 +640,13 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
if (node->type == CMP_NODE_OUTPUT_FILE) {
/* inputs have own storage data */
/* Inputs have their own storage data. */
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
BLO_write_struct(writer, NodeImageMultiFileSocket, sock->storage);
}
}
if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) {
/* write extra socket info */
/* Write extra socket info. */
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
BLO_write_struct(writer, NodeImageLayer, sock->storage);
}
@ -747,7 +742,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
}
if (node->storage) {
/* could be handlerized at some point */
switch (node->type) {
case SH_NODE_CURVE_VEC:
case SH_NODE_CURVE_RGB:
@ -1095,21 +1089,18 @@ static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType
return;
}
bNodeSocketTemplate *sockdef;
/* bNodeSocket *sock; */ /* UNUSED */
if (ntype->inputs) {
sockdef = ntype->inputs;
while (sockdef->type != -1) {
/* sock = */ node_add_socket_from_template(ntree, node, sockdef, SOCK_IN);
node_add_socket_from_template(ntree, node, sockdef, SOCK_IN);
sockdef++;
}
}
if (ntype->outputs) {
sockdef = ntype->outputs;
while (sockdef->type != -1) {
/* sock = */ node_add_socket_from_template(ntree, node, sockdef, SOCK_OUT);
node_add_socket_from_template(ntree, node, sockdef, SOCK_OUT);
sockdef++;
}
}
@ -1167,8 +1158,7 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr);
/* XXX Warning: context can be nullptr in case nodes are added in do_versions.
* Delayed init is not supported for nodes with context-based `initfunc_api` at the moment.
*/
* Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. */
BLI_assert(C != nullptr);
ntype->initfunc_api(C, &ptr);
}
@ -1382,18 +1372,6 @@ bNodeType *nodeTypeFind(const char *idname)
return nullptr;
}
static void free_dynamic_typeinfo(bNodeType *ntype)
{
if (ntype->type == NODE_DYNAMIC) {
if (ntype->inputs) {
MEM_freeN(ntype->inputs);
}
if (ntype->outputs) {
MEM_freeN(ntype->outputs);
}
}
}
/* callback for hash value free function */
static void node_free_type(void *nodetype_v)
{
@ -1403,11 +1381,6 @@ static void node_free_type(void *nodetype_v)
* or we'd want to update *all* active Mains, which we cannot do anyway currently. */
update_typeinfo(G_MAIN, nullptr, nullptr, nodetype, nullptr, true);
/* XXX deprecated */
if (nodetype->type == NODE_DYNAMIC) {
free_dynamic_typeinfo(nodetype);
}
delete nodetype->fixed_declaration;
nodetype->fixed_declaration = nullptr;
@ -1522,6 +1495,33 @@ struct bNodeSocket *nodeFindSocket(const bNode *node,
return nullptr;
}
namespace blender::bke {
bNodeSocket *node_find_enabled_socket(bNode &node,
const eNodeSocketInOut in_out,
const StringRef name)
{
ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs;
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
if (!(socket->flag & SOCK_UNAVAIL) && socket->name == name) {
return socket;
}
}
return nullptr;
}
bNodeSocket *node_find_enabled_input_socket(bNode &node, StringRef name)
{
return node_find_enabled_socket(node, SOCK_IN, name);
}
bNodeSocket *node_find_enabled_output_socket(bNode &node, StringRef name)
{
return node_find_enabled_socket(node, SOCK_OUT, name);
}
} // namespace blender::bke
/* find unique socket identifier */
static bool unique_identifier_check(void *arg, const char *identifier)
{
@ -1552,7 +1552,7 @@ static bNodeSocket *make_socket(bNodeTree *ntree,
/* if no explicit identifier is given, assign a unique identifier based on the name */
BLI_strncpy(auto_identifier, name, sizeof(auto_identifier));
}
/* make the identifier unique */
/* Make the identifier unique. */
BLI_uniquename_cb(
unique_identifier_check, lb, "socket", '_', auto_identifier, sizeof(auto_identifier));
@ -1982,10 +1982,7 @@ bNodeSocket *nodeInsertStaticSocket(bNodeTree *ntree,
return sock;
}
static void node_socket_free(bNodeTree *UNUSED(ntree),
bNodeSocket *sock,
bNode *UNUSED(node),
const bool do_id_user)
static void node_socket_free(bNodeSocket *sock, const bool do_id_user)
{
if (sock->prop) {
IDP_FreePropertyContent_ex(sock->prop, do_id_user);
@ -2020,7 +2017,7 @@ void nodeRemoveSocketEx(struct bNodeTree *ntree,
BLI_remlink(&node->inputs, sock);
BLI_remlink(&node->outputs, sock);
node_socket_free(ntree, sock, node, do_id_user);
node_socket_free(sock, do_id_user);
MEM_freeN(sock);
node->update |= NODE_UPDATE;
@ -2035,13 +2032,13 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node)
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) {
node_socket_free(ntree, sock, node, true);
node_socket_free(sock, true);
MEM_freeN(sock);
}
BLI_listbase_clear(&node->inputs);
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->outputs) {
node_socket_free(ntree, sock, node, true);
node_socket_free(sock, true);
MEM_freeN(sock);
}
BLI_listbase_clear(&node->outputs);
@ -2208,9 +2205,8 @@ bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type)
const char *idname = nullptr;
NODE_TYPES_BEGIN (ntype) {
/* do an extra poll here, because some int types are used
* for multiple node types, this helps find the desired type
*/
/* Do an extra poll here, because some int types are used
* for multiple node types, this helps find the desired type. */
const char *disabled_hint;
if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree, &disabled_hint))) {
idname = ntype->idname;
@ -2240,9 +2236,8 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src,
}
sock_dst->stack_index = 0;
/* XXX some compositor node (e.g. image, render layers) still store
* some persistent buffer data here, need to clear this to avoid dangling pointers.
*/
/* XXX some compositor nodes (e.g. image, render layers) still store
* some persistent buffer data here, need to clear this to avoid dangling pointers. */
sock_dst->cache = nullptr;
}
@ -2257,7 +2252,7 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree,
*node_dst = *node_src;
/* can be called for nodes outside a node tree (e.g. clipboard) */
/* Can be called for nodes outside a node tree (e.g. clipboard). */
if (ntree) {
if (unique_name) {
nodeUniqueName(ntree, node_dst);
@ -2394,7 +2389,7 @@ bNodeLink *nodeAddLink(
{
bNodeLink *link = nullptr;
/* test valid input */
/* Test valid input. */
BLI_assert(fromnode);
BLI_assert(tonode);
@ -2433,7 +2428,7 @@ bNodeLink *nodeAddLink(
void nodeRemLink(bNodeTree *ntree, bNodeLink *link)
{
/* can be called for links outside a node tree (e.g. clipboard) */
/* Can be called for links outside a node tree (e.g. clipboard). */
if (ntree) {
BLI_remlink(&ntree->links, link);
}
@ -2815,8 +2810,11 @@ bool BKE_node_preview_used(const bNode *node)
return (node->typeinfo->flag & NODE_PREVIEW) != 0;
}
bNodePreview *BKE_node_preview_verify(
bNodeInstanceHash *previews, bNodeInstanceKey key, int xsize, int ysize, bool create)
bNodePreview *BKE_node_preview_verify(bNodeInstanceHash *previews,
bNodeInstanceKey key,
const int xsize,
const int ysize,
const bool create)
{
bNodePreview *preview = (bNodePreview *)BKE_node_instance_hash_lookup(previews, key);
if (!preview) {
@ -2873,9 +2871,8 @@ void BKE_node_preview_free(bNodePreview *preview)
static void node_preview_init_tree_recursive(bNodeInstanceHash *previews,
bNodeTree *ntree,
bNodeInstanceKey parent_key,
int xsize,
int ysize,
bool create_previews)
const int xsize,
const int ysize)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
bNodeInstanceKey key = BKE_node_instance_key(parent_key, ntree, node);
@ -2884,17 +2881,16 @@ static void node_preview_init_tree_recursive(bNodeInstanceHash *previews,
node->preview_xsize = xsize;
node->preview_ysize = ysize;
BKE_node_preview_verify(previews, key, xsize, ysize, create_previews);
BKE_node_preview_verify(previews, key, xsize, ysize, false);
}
if (node->type == NODE_GROUP && node->id) {
node_preview_init_tree_recursive(
previews, (bNodeTree *)node->id, key, xsize, ysize, create_previews);
node_preview_init_tree_recursive(previews, (bNodeTree *)node->id, key, xsize, ysize);
}
}
}
void BKE_node_preview_init_tree(bNodeTree *ntree, int xsize, int ysize, bool create_previews)
void BKE_node_preview_init_tree(bNodeTree *ntree, int xsize, int ysize)
{
if (!ntree) {
return;
@ -2904,8 +2900,7 @@ void BKE_node_preview_init_tree(bNodeTree *ntree, int xsize, int ysize, bool cre
ntree->previews = BKE_node_instance_hash_new("node previews");
}
node_preview_init_tree_recursive(
ntree->previews, ntree, NODE_INSTANCE_KEY_BASE, xsize, ysize, create_previews);
node_preview_init_tree_recursive(ntree->previews, ntree, NODE_INSTANCE_KEY_BASE, xsize, ysize);
}
static void node_preview_tag_used_recursive(bNodeInstanceHash *previews,
@ -3041,27 +3036,6 @@ void BKE_node_preview_merge_tree(bNodeTree *to_ntree, bNodeTree *from_ntree, boo
}
}
void BKE_node_preview_set_pixel(
bNodePreview *preview, const float col[4], int x, int y, bool do_manage)
{
if (preview) {
if (x >= 0 && y >= 0) {
if (x < preview->xsize && y < preview->ysize) {
unsigned char *tar = preview->rect + 4 * ((preview->xsize * y) + x);
if (do_manage) {
linearrgb_to_srgb_uchar4(tar, col);
}
else {
rgba_float_to_uchar(tar, col);
}
}
// else printf("prv out bound x y %d %d\n", x, y);
}
// else printf("prv out bound x y %d %d\n", x, y);
}
}
/* ************** Free stuff ********** */
void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
@ -3137,12 +3111,12 @@ static void node_free_node(bNodeTree *ntree, bNode *node)
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) {
/* Remember, no ID user refcount management here! */
node_socket_free(ntree, sock, node, false);
node_socket_free(sock, false);
MEM_freeN(sock);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->outputs) {
/* Remember, no ID user refcount management here! */
node_socket_free(ntree, sock, node, false);
node_socket_free(sock, false);
MEM_freeN(sock);
}
@ -3238,8 +3212,7 @@ static void free_localized_node_groups(bNodeTree *ntree)
/* Only localized node trees store a copy for each node group tree.
* Each node group tree in a localized node tree can be freed,
* since it is a localized copy itself (no risk of accessing free'd
* data in main, see T37939).
*/
* data in main, see T37939). */
if (!(ntree->id.tag & LIB_TAG_LOCALIZED)) {
return;
}
@ -3435,7 +3408,7 @@ bNodeTree *ntreeLocalize(bNodeTree *ntree)
}
}
/* ensures only a single output node is enabled */
/* Ensures only a single output node is enabled. */
ntreeSetOutput(ntree);
bNode *node_src = (bNode *)ntree->nodes.first;
@ -4459,7 +4432,8 @@ static void ntree_validate_links(bNodeTree *ntree)
link->flag &= ~NODE_LINK_VALID;
}
else if (ntree->typeinfo->validate_link) {
if (!ntree->typeinfo->validate_link(ntree, link)) {
if (!ntree->typeinfo->validate_link((eNodeSocketDatatype)link->fromsock->type,
(eNodeSocketDatatype)link->tosock->type)) {
link->flag &= ~NODE_LINK_VALID;
}
}

View File

@ -147,7 +147,7 @@ static void init_context(DupliContext *r_ctx,
/**
* Create sub-context for recursive duplis.
*/
static void copy_dupli_context(
static bool copy_dupli_context(
DupliContext *r_ctx, const DupliContext *ctx, Object *ob, const float mat[4][4], int index)
{
*r_ctx = *ctx;
@ -168,9 +168,11 @@ static void copy_dupli_context(
if (r_ctx->level == MAX_DUPLI_RECUR - 1) {
std::cerr << "Warning: Maximum instance recursion level reached.\n";
return false;
}
r_ctx->gen = get_dupli_generator(r_ctx);
return true;
}
/**
@ -258,7 +260,9 @@ static void make_recursive_duplis(const DupliContext *ctx,
/* Simple preventing of too deep nested collections with #MAX_DUPLI_RECUR. */
if (ctx->level < MAX_DUPLI_RECUR) {
DupliContext rctx;
copy_dupli_context(&rctx, ctx, ob, space_mat, index);
if (!copy_dupli_context(&rctx, ctx, ob, space_mat, index)) {
return;
}
if (rctx.gen) {
ctx->instance_stack->append(ob);
rctx.gen->make_duplis(&rctx);
@ -301,13 +305,13 @@ static void make_child_duplis(const DupliContext *ctx,
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->collection, ob, mode) {
if ((ob != ctx->obedit) && is_child(ob, parent)) {
DupliContext pctx;
copy_dupli_context(&pctx, ctx, ctx->object, nullptr, _base_id);
/* Meta-balls have a different dupli handling. */
if (ob->type != OB_MBALL) {
ob->flag |= OB_DONE; /* Doesn't render. */
if (copy_dupli_context(&pctx, ctx, ctx->object, nullptr, _base_id)) {
/* Meta-balls have a different dupli handling. */
if (ob->type != OB_MBALL) {
ob->flag |= OB_DONE; /* Doesn't render. */
}
make_child_duplis_cb(&pctx, userdata, ob);
}
make_child_duplis_cb(&pctx, userdata, ob);
}
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
@ -324,14 +328,14 @@ static void make_child_duplis(const DupliContext *ctx,
DEG_OBJECT_ITER_BEGIN (ctx->depsgraph, ob, deg_objects_visibility_flags) {
if ((ob != ctx->obedit) && is_child(ob, parent)) {
DupliContext pctx;
copy_dupli_context(&pctx, ctx, ctx->object, nullptr, persistent_dupli_id);
if (copy_dupli_context(&pctx, ctx, ctx->object, nullptr, persistent_dupli_id)) {
/* Meta-balls have a different dupli-handling. */
if (ob->type != OB_MBALL) {
ob->flag |= OB_DONE; /* Doesn't render. */
}
/* Meta-balls have a different dupli-handling. */
if (ob->type != OB_MBALL) {
ob->flag |= OB_DONE; /* Doesn't render. */
make_child_duplis_cb(&pctx, userdata, ob);
}
make_child_duplis_cb(&pctx, userdata, ob);
}
persistent_dupli_id++;
}
@ -893,7 +897,9 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
* between the instances component below and the other components above. */
DupliContext new_instances_ctx;
if (creates_duplis_for_components) {
copy_dupli_context(&new_instances_ctx, ctx, ctx->object, nullptr, component_index);
if (!copy_dupli_context(&new_instances_ctx, ctx, ctx->object, nullptr, component_index)) {
return;
}
instances_ctx = &new_instances_ctx;
}
@ -928,7 +934,9 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
mul_m4_m4_pre(collection_matrix, parent_transform);
DupliContext sub_ctx;
copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id);
if (!copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id)) {
break;
}
eEvaluationMode mode = DEG_get_mode(instances_ctx->depsgraph);
int object_id = 0;
@ -951,8 +959,9 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
mul_m4_m4m4(new_transform, parent_transform, instance_offset_matrices[i].values);
DupliContext sub_ctx;
copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id);
make_duplis_geometry_set_impl(&sub_ctx, reference.geometry_set(), new_transform, true);
if (copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id)) {
make_duplis_geometry_set_impl(&sub_ctx, reference.geometry_set(), new_transform, true);
}
break;
}
case InstanceReference::Type::None: {
@ -1609,8 +1618,9 @@ static void make_duplis_particles(const DupliContext *ctx)
LISTBASE_FOREACH_INDEX (ParticleSystem *, psys, &ctx->object->particlesystem, psysid) {
/* Particles create one more level for persistent `psys` index. */
DupliContext pctx;
copy_dupli_context(&pctx, ctx, ctx->object, nullptr, psysid);
make_duplis_particle_system(&pctx, psys);
if (copy_dupli_context(&pctx, ctx, ctx->object, nullptr, psysid)) {
make_duplis_particle_system(&pctx, psys);
}
}
}

View File

@ -1322,10 +1322,11 @@ static int ptcache_frame_from_filename(const char *filename, const char *ext)
static int ptcache_path(PTCacheID *pid, char *filename)
{
const char *blendfile_path = BKE_main_blendfile_path_from_global();
Library *lib = (pid->owner_id) ? pid->owner_id->lib : NULL;
const char *blendfilename = (lib && (pid->cache->flag & PTCACHE_IGNORE_LIBPATH) == 0) ?
lib->filepath_abs :
BKE_main_blendfile_path_from_global();
blendfile_path;
size_t i;
if (pid->cache->flag & PTCACHE_EXTERNAL) {
@ -1337,7 +1338,7 @@ static int ptcache_path(PTCacheID *pid, char *filename)
return BLI_path_slash_ensure(filename); /* new strlen() */
}
if (G.relbase_valid || lib) {
if ((blendfile_path[0] != '\0') || lib) {
char file[MAX_PTCACHE_PATH]; /* we don't want the dir, only the file */
BLI_split_file_part(blendfilename, file, sizeof(file));
@ -1422,8 +1423,11 @@ static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_p
filename[0] = '\0';
newname = filename;
if (!G.relbase_valid && (pid->cache->flag & PTCACHE_EXTERNAL) == 0) {
return 0; /* save blend file before using disk pointcache */
if ((pid->cache->flag & PTCACHE_EXTERNAL) == 0) {
const char *blendfile_path = BKE_main_blendfile_path_from_global();
if (blendfile_path[0] == '\0') {
return 0; /* save blend file before using disk pointcache */
}
}
/* start with temp dir */
@ -1469,8 +1473,11 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra)
return NULL;
}
#endif
if (!G.relbase_valid && (pid->cache->flag & PTCACHE_EXTERNAL) == 0) {
return NULL; /* save blend file before using disk pointcache */
if ((pid->cache->flag & PTCACHE_EXTERNAL) == 0) {
const char *blendfile_path = BKE_main_blendfile_path_from_global();
if (blendfile_path[0] == '\0') {
return NULL; /* save blend file before using disk pointcache */
}
}
ptcache_filename(pid, filename, cfra, 1, 1);
@ -2626,8 +2633,6 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
}
#endif
// if (!G.relbase_valid) return; /* Save blend file before using pointcache. */
/* clear all files in the temp dir with the prefix of the ID and the ".bphys" suffix */
switch (mode) {
case PTCACHE_CLEAR_ALL:
@ -3446,8 +3451,9 @@ void BKE_ptcache_toggle_disk_cache(PTCacheID *pid)
{
PointCache *cache = pid->cache;
int last_exact = cache->last_exact;
const char *blendfile_path = BKE_main_blendfile_path_from_global();
if (!G.relbase_valid) {
if (blendfile_path[0] == '\0') {
cache->flag &= ~PTCACHE_DISK_CACHE;
if (G.debug & G_DEBUG) {
printf("File must be saved before using disk cache!\n");

View File

@ -257,13 +257,13 @@ void NURBSpline::calculate_knots() const
Span<float> NURBSpline::knots() const
{
if (!knots_dirty_) {
BLI_assert(knots_.size() == this->size() + order_);
BLI_assert(knots_.size() == this->knots_size());
return knots_;
}
std::lock_guard lock{knots_mutex_};
if (!knots_dirty_) {
BLI_assert(knots_.size() == this->size() + order_);
BLI_assert(knots_.size() == this->knots_size());
return knots_;
}

View File

@ -819,6 +819,9 @@ void BKE_undosys_step_load_from_index(UndoStack *ustack, bContext *C, const int
{
UndoStep *us_target = BLI_findlink(&ustack->steps, index);
BLI_assert(us_target->skip == false);
if (us_target == ustack->step_active) {
return;
}
BKE_undosys_step_load_data(ustack, C, us_target);
}

View File

@ -537,7 +537,7 @@ static void volume_copy_data(Main *UNUSED(bmain),
#ifdef WITH_OPENVDB
if (volume_src->runtime.grids) {
const VolumeGridVector &grids_src = *(volume_src->runtime.grids);
volume_dst->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector, grids_src);
volume_dst->runtime.grids = MEM_new<VolumeGridVector>(__func__, grids_src);
}
#endif
@ -551,7 +551,8 @@ static void volume_free_data(ID *id)
BKE_volume_batch_cache_free(volume);
MEM_SAFE_FREE(volume->mat);
#ifdef WITH_OPENVDB
OBJECT_GUARDED_SAFE_DELETE(volume->runtime.grids, VolumeGridVector);
MEM_delete(volume->runtime.grids);
volume->runtime.grids = nullptr;
#endif
}
@ -683,7 +684,7 @@ void BKE_volume_init_grids(Volume *volume)
{
#ifdef WITH_OPENVDB
if (volume->runtime.grids == nullptr) {
volume->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector);
volume->runtime.grids = MEM_new<VolumeGridVector>(__func__);
}
#else
UNUSED_VARS(volume);
@ -1129,16 +1130,16 @@ void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, co
if (!grids->is_loaded()) {
/* No grids loaded in CoW datablock, nothing lost by discarding. */
OBJECT_GUARDED_DELETE(grids, VolumeGridVector);
MEM_delete(grids);
}
else if (!STREQ(volume->filepath, filepath)) {
/* Filepath changed, discard grids from CoW datablock. */
OBJECT_GUARDED_DELETE(grids, VolumeGridVector);
MEM_delete(grids);
}
else {
/* Keep grids from CoW datablock. We might still unload them a little
* later in BKE_volume_eval_geometry if the frame changes. */
OBJECT_GUARDED_DELETE(volume->runtime.grids, VolumeGridVector);
MEM_delete(volume->runtime.grids);
volume->runtime.grids = grids;
}
#else

View File

@ -30,6 +30,7 @@ extern "C" {
#endif
/* Utility functions. */
void _BLI_assert_print_pos(const char *file, const int line, const char *function, const char *id);
void _BLI_assert_print_extra(const char *str);
void _BLI_assert_print_backtrace(void);

View File

@ -73,27 +73,27 @@ namespace blender {
* - Add non RGB spaces/storages ColorXyz.
*/
/* Enumeration containing the different alpha modes. */
/** Enumeration containing the different alpha modes. */
enum class eAlpha {
/* Color and alpha are unassociated. */
/** Color and alpha are unassociated. */
Straight,
/* Color and alpha are associated. */
/** Color and alpha are associated. */
Premultiplied,
};
std::ostream &operator<<(std::ostream &stream, const eAlpha &space);
/* Enumeration containing internal spaces. */
/** Enumeration containing internal spaces. */
enum class eSpace {
/* Blender theme color space (sRGB). */
/** Blender theme color space (sRGB). */
Theme,
/* Blender internal scene linear color space (maps to SceneReference role in OCIO). */
/** Blender internal scene linear color space (maps to SceneReference role in OCIO). */
SceneLinear,
/* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */
/** Blender internal scene linear color space compressed to be stored in 4 uint8_t. */
SceneLinearByteEncoded,
};
std::ostream &operator<<(std::ostream &stream, const eSpace &space);
/* Template class to store RGBA values with different precision, space and alpha association. */
/** Template class to store RGBA values with different precision, space and alpha association. */
template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA {
public:
ChannelStorageType r, g, b, a;
@ -153,11 +153,13 @@ template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGB
};
/* Forward declarations of concrete color classes. */
template<eAlpha Alpha> class ColorSceneLinear4f;
template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b;
template<typename ChannelStorageType> class ColorTheme4;
/* Forward declaration of precision conversion methods. */
BLI_INLINE ColorTheme4<float> BLI_color_convert_to_theme4f(const ColorTheme4<uint8_t> &srgb4b);
BLI_INLINE ColorTheme4<uint8_t> BLI_color_convert_to_theme4b(const ColorTheme4<float> &srgb4f);
@ -354,6 +356,7 @@ BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_l
}
/* Internal roles. For convenience to shorten the type names and hide complexity. */
using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>;
using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>;

View File

@ -34,7 +34,7 @@
#if (defined(__GNUC__) || defined(__clang__)) && defined(__cplusplus)
extern "C++" {
/* Some magic to be sure we don't have reference in the type. */
/** Some magic to be sure we don't have reference in the type. */
template<typename T> static inline T decltype_helper(T x)
{
return x;

View File

@ -222,7 +222,7 @@ void BLI_delaunay_2d_cdt_free(CDT_result *result);
namespace blender::meshintersect {
/* vec2<Arith_t> is a 2d vector with Arith_t as the type for coordinates. */
/** #vec2<Arith_t> is a 2d vector with #Arith_t as the type for coordinates. */
template<typename Arith_t> struct vec2_impl;
template<> struct vec2_impl<double> {
typedef double2 type;

View File

@ -40,7 +40,7 @@ extern "C" {
/** \name Base Structs
* \{ */
/* Basic Layout for a Node */
/** Basic Layout for a Node. */
typedef struct DLRBT_Node {
/* ListBase capabilities */
struct DLRBT_Node *next, *prev;
@ -53,7 +53,7 @@ typedef struct DLRBT_Node {
/* ... for nice alignment, next item should usually be a char too... */
} DLRBT_Node;
/* Red/Black defines for tree_col */
/** Red/Black defines for tree_col. */
typedef enum eDLRBT_Colors {
DLRBT_BLACK = 0,
DLRBT_RED,
@ -61,7 +61,7 @@ typedef enum eDLRBT_Colors {
/* -------- */
/* The Tree Data */
/** The Tree Data. */
typedef struct DLRBT_Tree {
/* ListBase capabilities */
void *first, *last; /* these should be based on DLRBT_Node-s */

View File

@ -30,6 +30,7 @@ extern "C" {
#endif
/* BLI_endian_switch_inline.h */
BLI_INLINE void BLI_endian_switch_int16(short *val) ATTR_NONNULL(1);
BLI_INLINE void BLI_endian_switch_uint16(unsigned short *val) ATTR_NONNULL(1);
BLI_INLINE void BLI_endian_switch_int32(int *val) ATTR_NONNULL(1);
@ -40,6 +41,7 @@ BLI_INLINE void BLI_endian_switch_uint64(uint64_t *val) ATTR_NONNULL(1);
BLI_INLINE void BLI_endian_switch_double(double *val) ATTR_NONNULL(1);
/* endian_switch.c */
void BLI_endian_switch_int16_array(short *val, const int size) ATTR_NONNULL(1);
void BLI_endian_switch_uint16_array(unsigned short *val, const int size) ATTR_NONNULL(1);
void BLI_endian_switch_int32_array(int *val, const int size) ATTR_NONNULL(1);

View File

@ -33,6 +33,7 @@ extern "C" {
* use bit shifting instead. */
/* *** 16 *** */
BLI_INLINE void BLI_endian_switch_int16(short *val)
{
BLI_endian_switch_uint16((unsigned short *)val);
@ -48,6 +49,7 @@ BLI_INLINE void BLI_endian_switch_uint16(unsigned short *val)
}
/* *** 32 *** */
BLI_INLINE void BLI_endian_switch_int32(int *val)
{
BLI_endian_switch_uint32((unsigned int *)val);
@ -67,6 +69,7 @@ BLI_INLINE void BLI_endian_switch_float(float *val)
}
/* *** 64 *** */
BLI_INLINE void BLI_endian_switch_int64(int64_t *val)
{
BLI_endian_switch_uint64((uint64_t *)val);

View File

@ -47,7 +47,7 @@ typedef ssize_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, siz
typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence);
typedef void (*FileReaderCloseFn)(struct FileReader *reader);
/* General structure for all FileReaders, implementations add custom fields at the end. */
/** General structure for all #FileReaders, implementations add custom fields at the end. */
typedef struct FileReader {
FileReaderReadFn read;
FileReaderSeekFn seek;
@ -64,16 +64,16 @@ typedef struct FileReader {
* take over the base FileReader and will clean it up when their clean() is called.
*/
/* Create FileReader from raw file descriptor. */
/** Create #FileReader from raw file descriptor. */
FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT;
/* Create FileReader from raw file descriptor using memory-mapped IO. */
/** Create #FileReader from raw file descriptor using memory-mapped IO. */
FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT;
/* Create FileReader from a region of memory. */
/** Create #FileReader from a region of memory. */
FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
/* Create FileReader from applying `Zstd` decompression on an underlying file. */
/** Create #FileReader from applying `Zstd` decompression on an underlying file. */
FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/* Create FileReader from applying `Gzip` decompression on an underlying file. */
/** Create #FileReader from applying `Gzip` decompression on an underlying file. */
FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
#ifdef __cplusplus

View File

@ -512,7 +512,7 @@ void *BLI_gset_pop_key(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT;
/* rely on inline api for now */
/* so we can cast but compiler sees as different */
/** Use a GSet specific type so we can cast but compiler sees as different */
typedef struct GSetIterator {
GHashIterator _ghi
#if defined(__GNUC__) && !defined(__clang__)

View File

@ -74,7 +74,7 @@ int BLI_kdtree_nd_(calc_duplicates_fast)(const KDTree *tree,
int BLI_kdtree_nd_(deduplicate)(KDTree *tree);
/* Versions of find/range search that take a squared distance callback to support bias. */
/** Versions of find/range search that take a squared distance callback to support bias. */
int BLI_kdtree_nd_(find_nearest_n_with_len_squared_cb)(
const KDTree *tree,
const float co[KD_DIMS],

View File

@ -48,18 +48,19 @@ typedef void (*LockfreeeLinkNodeFreeFP)(void *link);
/* NOTE: These functions are NOT safe for use from threads. */
/* NOTE: !!! I REPEAT: DO NOT USE THEM WITHOUT EXTERNAL LOCK !!! */
/* Make list ready for lock-free access. */
/** Make list ready for lock-free access. */
void BLI_linklist_lockfree_init(LockfreeLinkList *list);
/* Completely free the whole list, it is NOT re-usable after this. */
/** Completely free the whole list, it is NOT re-usable after this. */
void BLI_linklist_lockfree_free(LockfreeLinkList *list, LockfreeeLinkNodeFreeFP free_func);
/* Remove all the elements from the list, keep it usable for further
* inserts.
/**
* Remove all the elements from the list, keep it usable for further inserts.
*/
void BLI_linklist_lockfree_clear(LockfreeLinkList *list, LockfreeeLinkNodeFreeFP free_func);
/* Begin iteration of lock-free linked list, starting with a
/**
* Begin iteration of lock-free linked list, starting with a
* first user=defined node. Will ignore the dummy node.
*/
LockfreeLinkNode *BLI_linklist_lockfree_begin(LockfreeLinkList *list);

View File

@ -46,6 +46,11 @@ int BLI_findstringindex(const struct ListBase *listbase,
const char *id,
const int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
/**
* Return a ListBase representing the entire list the given Link is in.
*/
ListBase BLI_listbase_from_link(struct Link *some_link);
/* Find forwards. */
/**
@ -278,6 +283,23 @@ BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
lb->first = lb->last = (void *)0;
}
/**
* Equality check for ListBase.
*
* This only shallowly compares the ListBase itself (so the first/last
* pointers), and does not do any equality checks on the list items.
*/
BLI_INLINE bool BLI_listbase_equal(const struct ListBase *a, const struct ListBase *b)
{
if (a == NULL) {
return b == NULL;
}
if (b == NULL) {
return false;
}
return a->first == b->first && a->last == b->last;
}
/**
* Create a generic list node containing link to provided data.
*/
@ -353,3 +375,10 @@ struct LinkData *BLI_genericNodeN(void *data);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
BLI_INLINE bool operator==(const ListBase &a, const ListBase &b)
{
return BLI_listbase_equal(&a, &b);
}
#endif

Some files were not shown because too many files have changed in this diff Show More