Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
134eccf5bf
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* Copyright 2011-2013 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "scene/pointcloud.h"
|
||||
#include "scene/attribute.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
#include "blender/sync.h"
|
||||
#include "blender/util.h"
|
||||
|
||||
#include "util/foreach.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
template<typename TypeInCycles, typename GetValueAtIndex>
|
||||
static void fill_generic_attribute(BL::PointCloud &b_pointcloud,
|
||||
TypeInCycles *data,
|
||||
const GetValueAtIndex &get_value_at_index)
|
||||
{
|
||||
const int num_points = b_pointcloud.points.length();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
data[i] = get_value_at_index(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_attributes(PointCloud *pointcloud, BL::PointCloud b_pointcloud)
|
||||
{
|
||||
AttributeSet &attributes = pointcloud->attributes;
|
||||
for (BL::Attribute &b_attribute : b_pointcloud.attributes) {
|
||||
const ustring name{b_attribute.name().c_str()};
|
||||
|
||||
if (attributes.find(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const AttributeElement element = ATTR_ELEMENT_VERTEX;
|
||||
const BL::Attribute::data_type_enum b_data_type = b_attribute.data_type();
|
||||
switch (b_data_type) {
|
||||
case BL::Attribute::data_type_FLOAT: {
|
||||
BL::FloatAttribute b_float_attribute{b_attribute};
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(
|
||||
b_pointcloud, data, [&](int i) { return b_float_attribute.data[i].value(); });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_BOOLEAN: {
|
||||
BL::BoolAttribute b_bool_attribute{b_attribute};
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(
|
||||
b_pointcloud, data, [&](int i) { return (float)b_bool_attribute.data[i].value(); });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_INT: {
|
||||
BL::IntAttribute b_int_attribute{b_attribute};
|
||||
Attribute *attr = attributes.add(name, TypeFloat, element);
|
||||
float *data = attr->data_float();
|
||||
fill_generic_attribute(
|
||||
b_pointcloud, data, [&](int i) { return (float)b_int_attribute.data[i].value(); });
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_VECTOR: {
|
||||
BL::FloatVectorAttribute b_vector_attribute{b_attribute};
|
||||
Attribute *attr = attributes.add(name, TypeVector, element);
|
||||
float3 *data = attr->data_float3();
|
||||
fill_generic_attribute(b_pointcloud, data, [&](int i) {
|
||||
BL::Array<float, 3> v = b_vector_attribute.data[i].vector();
|
||||
return make_float3(v[0], v[1], v[2]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT_COLOR: {
|
||||
BL::FloatColorAttribute b_color_attribute{b_attribute};
|
||||
Attribute *attr = attributes.add(name, TypeRGBA, element);
|
||||
float4 *data = attr->data_float4();
|
||||
fill_generic_attribute(b_pointcloud, data, [&](int i) {
|
||||
BL::Array<float, 4> v = b_color_attribute.data[i].color();
|
||||
return make_float4(v[0], v[1], v[2], v[3]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case BL::Attribute::data_type_FLOAT2: {
|
||||
BL::Float2Attribute b_float2_attribute{b_attribute};
|
||||
Attribute *attr = attributes.add(name, TypeFloat2, element);
|
||||
float2 *data = attr->data_float2();
|
||||
fill_generic_attribute(b_pointcloud, data, [&](int i) {
|
||||
BL::Array<float, 2> v = b_float2_attribute.data[i].vector();
|
||||
return make_float2(v[0], v[1]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Not supported. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void export_pointcloud(Scene *scene, PointCloud *pointcloud, BL::PointCloud b_pointcloud)
|
||||
{
|
||||
/* TODO: optimize so we can straight memcpy arrays from Blender? */
|
||||
|
||||
/* Add requested attributes. */
|
||||
Attribute *attr_random = NULL;
|
||||
if (pointcloud->need_attribute(scene, ATTR_STD_POINT_RANDOM)) {
|
||||
attr_random = pointcloud->attributes.add(ATTR_STD_POINT_RANDOM);
|
||||
}
|
||||
|
||||
/* Reserve memory. */
|
||||
const int num_points = b_pointcloud.points.length();
|
||||
pointcloud->reserve(num_points);
|
||||
|
||||
/* Export points. */
|
||||
BL::PointCloud::points_iterator b_point_iter;
|
||||
for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end();
|
||||
++b_point_iter) {
|
||||
BL::Point b_point = *b_point_iter;
|
||||
const float3 co = get_float3(b_point.co());
|
||||
const float radius = b_point.radius();
|
||||
pointcloud->add_point(co, radius);
|
||||
|
||||
/* Random number per point. */
|
||||
if (attr_random != NULL) {
|
||||
attr_random->add(hash_uint2_to_float(b_point.index(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
/* Export attributes */
|
||||
copy_attributes(pointcloud, b_pointcloud);
|
||||
}
|
||||
|
||||
static void export_pointcloud_motion(PointCloud *pointcloud,
|
||||
BL::PointCloud b_pointcloud,
|
||||
int motion_step)
|
||||
{
|
||||
/* Find or add attribute. */
|
||||
Attribute *attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
bool new_attribute = false;
|
||||
|
||||
if (!attr_mP) {
|
||||
attr_mP = pointcloud->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
new_attribute = true;
|
||||
}
|
||||
|
||||
/* Export motion points. */
|
||||
const int num_points = pointcloud->num_points();
|
||||
float3 *mP = attr_mP->data_float3() + motion_step * num_points;
|
||||
bool have_motion = false;
|
||||
int num_motion_points = 0;
|
||||
const array<float3> &pointcloud_points = pointcloud->get_points();
|
||||
|
||||
BL::PointCloud::points_iterator b_point_iter;
|
||||
for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end();
|
||||
++b_point_iter) {
|
||||
BL::Point b_point = *b_point_iter;
|
||||
|
||||
if (num_motion_points < num_points) {
|
||||
float3 P = get_float3(b_point.co());
|
||||
P.w = b_point.radius();
|
||||
mP[num_motion_points] = P;
|
||||
have_motion = have_motion || (P != pointcloud_points[num_motion_points]);
|
||||
num_motion_points++;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of new attribute, we verify if there really was any motion. */
|
||||
if (new_attribute) {
|
||||
if (num_motion_points != num_points || !have_motion) {
|
||||
pointcloud->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
}
|
||||
else if (motion_step > 0) {
|
||||
/* Motion, fill up previous steps that we might have skipped because
|
||||
* they had no motion, but we need them anyway now. */
|
||||
for (int step = 0; step < motion_step; step++) {
|
||||
pointcloud->copy_center_to_motion_step(step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Export attributes */
|
||||
copy_attributes(pointcloud, b_pointcloud);
|
||||
}
|
||||
|
||||
void BlenderSync::sync_pointcloud(PointCloud *pointcloud, BObjectInfo &b_ob_info)
|
||||
{
|
||||
size_t old_numpoints = pointcloud->num_points();
|
||||
|
||||
array<Node *> used_shaders = pointcloud->get_used_shaders();
|
||||
|
||||
PointCloud new_pointcloud;
|
||||
new_pointcloud.set_used_shaders(used_shaders);
|
||||
|
||||
/* TODO: add option to filter out points in the view layer. */
|
||||
BL::PointCloud b_pointcloud(b_ob_info.object_data);
|
||||
export_pointcloud(scene, &new_pointcloud, b_pointcloud);
|
||||
|
||||
/* update original sockets */
|
||||
for (const SocketType &socket : new_pointcloud.type->inputs) {
|
||||
/* Those sockets are updated in sync_object, so do not modify them. */
|
||||
if (socket.name == "use_motion_blur" || socket.name == "motion_steps" ||
|
||||
socket.name == "used_shaders") {
|
||||
continue;
|
||||
}
|
||||
pointcloud->set_value(socket, new_pointcloud, socket);
|
||||
}
|
||||
|
||||
pointcloud->attributes.clear();
|
||||
foreach (Attribute &attr, new_pointcloud.attributes.attributes) {
|
||||
pointcloud->attributes.attributes.push_back(std::move(attr));
|
||||
}
|
||||
|
||||
/* tag update */
|
||||
const bool rebuild = (pointcloud && old_numpoints != pointcloud->num_points());
|
||||
pointcloud->tag_update(scene, rebuild);
|
||||
}
|
||||
|
||||
void BlenderSync::sync_pointcloud_motion(PointCloud *pointcloud,
|
||||
BObjectInfo &b_ob_info,
|
||||
int motion_step)
|
||||
{
|
||||
/* Skip if nothing exported. */
|
||||
if (pointcloud->num_points() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Export deformed coordinates. */
|
||||
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
|
||||
/* PointCloud object. */
|
||||
BL::PointCloud b_pointcloud(b_ob_info.object_data);
|
||||
export_pointcloud_motion(pointcloud, b_pointcloud, motion_step);
|
||||
}
|
||||
else {
|
||||
/* No deformation on this frame, copy coordinates if other frames did have it. */
|
||||
pointcloud->copy_center_to_motion_step(motion_step);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
|
@ -179,11 +179,14 @@ set(SRC_KERNEL_GEOM_HEADERS
|
|||
geom/curve.h
|
||||
geom/curve_intersect.h
|
||||
geom/motion_curve.h
|
||||
geom/motion_point.h
|
||||
geom/motion_triangle.h
|
||||
geom/motion_triangle_intersect.h
|
||||
geom/motion_triangle_shader.h
|
||||
geom/object.h
|
||||
geom/patch.h
|
||||
geom/point.h
|
||||
geom/point_intersect.h
|
||||
geom/primitive.h
|
||||
geom/shader_data.h
|
||||
geom/subd_triangle.h
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Motion Point Primitive
|
||||
*
|
||||
* These are stored as regular points, plus extra positions and radii at times
|
||||
* other than the frame center. Computing the point at a given ray time is
|
||||
* a matter of interpolation of the two steps between which the ray time lies.
|
||||
*
|
||||
* The extra points are stored as ATTR_STD_MOTION_VERTEX_POSITION.
|
||||
*/
|
||||
|
||||
#ifdef __POINTCLOUD__
|
||||
|
||||
ccl_device_inline float4
|
||||
motion_point_for_step(KernelGlobals kg, int offset, int numkeys, int numsteps, int step, int prim)
|
||||
{
|
||||
if (step == numsteps) {
|
||||
/* center step: regular key location */
|
||||
return kernel_tex_fetch(__points, prim);
|
||||
}
|
||||
else {
|
||||
/* center step is not stored in this array */
|
||||
if (step > numsteps)
|
||||
step--;
|
||||
|
||||
offset += step * numkeys;
|
||||
|
||||
return kernel_tex_fetch(__attributes_float4, offset + prim);
|
||||
}
|
||||
}
|
||||
|
||||
/* return 2 point key locations */
|
||||
ccl_device_inline float4 motion_point(KernelGlobals kg, int object, int prim, float time)
|
||||
{
|
||||
/* get motion info */
|
||||
int numsteps, numkeys;
|
||||
object_motion_info(kg, object, &numsteps, NULL, &numkeys);
|
||||
|
||||
/* figure out which steps we need to fetch and their interpolation factor */
|
||||
int maxstep = numsteps * 2;
|
||||
int step = min((int)(time * maxstep), maxstep - 1);
|
||||
float t = time * maxstep - step;
|
||||
|
||||
/* find attribute */
|
||||
int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
kernel_assert(offset != ATTR_STD_NOT_FOUND);
|
||||
|
||||
/* fetch key coordinates */
|
||||
float4 point = motion_point_for_step(kg, offset, numkeys, numsteps, step, prim);
|
||||
float4 next_point = motion_point_for_step(kg, offset, numkeys, numsteps, step + 1, prim);
|
||||
|
||||
/* interpolate between steps */
|
||||
return (1.0f - t) * point + t * next_point;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Point Primitive
|
||||
*
|
||||
* Point primitive for rendering point clouds.
|
||||
*/
|
||||
|
||||
#ifdef __POINTCLOUD__
|
||||
|
||||
/* Reading attributes on various point elements */
|
||||
|
||||
ccl_device float point_attribute_float(KernelGlobals kg,
|
||||
ccl_private const ShaderData *sd,
|
||||
const AttributeDescriptor desc,
|
||||
ccl_private float *dx,
|
||||
ccl_private float *dy)
|
||||
{
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
if (dx)
|
||||
*dx = 0.0f;
|
||||
if (dy)
|
||||
*dy = 0.0f;
|
||||
# endif
|
||||
|
||||
if (desc.element == ATTR_ELEMENT_VERTEX) {
|
||||
return kernel_tex_fetch(__attributes_float, desc.offset + sd->prim);
|
||||
}
|
||||
else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device float2 point_attribute_float2(KernelGlobals kg,
|
||||
ccl_private const ShaderData *sd,
|
||||
const AttributeDescriptor desc,
|
||||
ccl_private float2 *dx,
|
||||
ccl_private float2 *dy)
|
||||
{
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
if (dx)
|
||||
*dx = make_float2(0.0f, 0.0f);
|
||||
if (dy)
|
||||
*dy = make_float2(0.0f, 0.0f);
|
||||
# endif
|
||||
|
||||
if (desc.element == ATTR_ELEMENT_VERTEX) {
|
||||
return kernel_tex_fetch(__attributes_float2, desc.offset + sd->prim);
|
||||
}
|
||||
else {
|
||||
return make_float2(0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device float3 point_attribute_float3(KernelGlobals kg,
|
||||
ccl_private const ShaderData *sd,
|
||||
const AttributeDescriptor desc,
|
||||
ccl_private float3 *dx,
|
||||
ccl_private float3 *dy)
|
||||
{
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
if (dx)
|
||||
*dx = make_float3(0.0f, 0.0f, 0.0f);
|
||||
if (dy)
|
||||
*dy = make_float3(0.0f, 0.0f, 0.0f);
|
||||
# endif
|
||||
|
||||
if (desc.element == ATTR_ELEMENT_VERTEX) {
|
||||
return float4_to_float3(kernel_tex_fetch(__attributes_float4, desc.offset + sd->prim));
|
||||
}
|
||||
else {
|
||||
return make_float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device float4 point_attribute_float4(KernelGlobals kg,
|
||||
ccl_private const ShaderData *sd,
|
||||
const AttributeDescriptor desc,
|
||||
ccl_private float4 *dx,
|
||||
ccl_private float4 *dy)
|
||||
{
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
if (dx)
|
||||
*dx = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
if (dy)
|
||||
*dy = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
# endif
|
||||
|
||||
if (desc.element == ATTR_ELEMENT_VERTEX) {
|
||||
return kernel_tex_fetch(__attributes_float4, desc.offset + sd->prim);
|
||||
}
|
||||
else {
|
||||
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/* Point radius */
|
||||
|
||||
ccl_device float point_radius(KernelGlobals kg, ccl_private const ShaderData *sd)
|
||||
{
|
||||
if (sd->type & PRIMITIVE_POINT) {
|
||||
return kernel_tex_fetch(__points, sd->prim).w;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Point location for motion pass, linear interpolation between keys and
|
||||
* ignoring radius because we do the same for the motion keys */
|
||||
|
||||
ccl_device float3 point_motion_center_location(KernelGlobals kg, ccl_private const ShaderData *sd)
|
||||
{
|
||||
return float4_to_float3(kernel_tex_fetch(__points, sd->prim));
|
||||
}
|
||||
|
||||
#endif /* __POINTCLOUD__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Based on Embree code, copyright 2009-2020 Intel Corporation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Point primitive intersection functions. */
|
||||
|
||||
#ifdef __POINTCLOUD__
|
||||
|
||||
ccl_device_forceinline bool point_intersect_test(
|
||||
const float4 point, const float3 P, const float3 dir, const float tmax, ccl_private float *t)
|
||||
{
|
||||
const float3 center = float4_to_float3(point);
|
||||
const float radius = point.w;
|
||||
|
||||
const float rd2 = 1.0f / dot(dir, dir);
|
||||
|
||||
const float3 c0 = center - P;
|
||||
const float projC0 = dot(c0, dir) * rd2;
|
||||
const float3 perp = c0 - projC0 * dir;
|
||||
const float l2 = dot(perp, perp);
|
||||
const float r2 = radius * radius;
|
||||
if (!(l2 <= r2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float td = sqrt((r2 - l2) * rd2);
|
||||
const float t_front = projC0 - td;
|
||||
const bool valid_front = (0.0f <= t_front) & (t_front <= tmax);
|
||||
|
||||
/* Always back-face culling for now. */
|
||||
# if 0
|
||||
const float t_back = projC0 + td;
|
||||
const bool valid_back = (0.0f <= t_back) & (t_back <= tmax);
|
||||
|
||||
/* check if there is a first hit */
|
||||
const bool valid_first = valid_front | valid_back;
|
||||
if (!valid_first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*t = (valid_front) ? t_front : t_back;
|
||||
return true;
|
||||
# else
|
||||
if (!valid_front) {
|
||||
return false;
|
||||
}
|
||||
*t = t_front;
|
||||
return true;
|
||||
# endif
|
||||
}
|
||||
|
||||
ccl_device_forceinline bool point_intersect(KernelGlobals kg,
|
||||
ccl_private Intersection *isect,
|
||||
const float3 P,
|
||||
const float3 dir,
|
||||
const float tmax,
|
||||
const int object,
|
||||
const int prim,
|
||||
const float time,
|
||||
const int type)
|
||||
{
|
||||
const float4 point = (type & PRIMITIVE_MOTION) ? motion_point(kg, object, prim, time) :
|
||||
kernel_tex_fetch(__points, prim);
|
||||
|
||||
if (!point_intersect_test(point, P, dir, tmax, &isect->t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isect->prim = prim;
|
||||
isect->object = object;
|
||||
isect->type = type;
|
||||
isect->u = 0.0f;
|
||||
isect->v = 0.0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_device_inline void point_shader_setup(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
ccl_private const Intersection *isect,
|
||||
ccl_private const Ray *ray)
|
||||
{
|
||||
sd->shader = kernel_tex_fetch(__points_shader, isect->prim);
|
||||
sd->P = ray->P + ray->D * isect->t;
|
||||
|
||||
/* Texture coordinates, zero for now. */
|
||||
# ifdef __UV__
|
||||
sd->u = isect->u;
|
||||
sd->v = isect->v;
|
||||
# endif
|
||||
|
||||
/* Compute point center for normal. */
|
||||
float3 center = float4_to_float3((isect->type & PRIMITIVE_MOTION) ?
|
||||
motion_point(kg, sd->object, sd->prim, sd->time) :
|
||||
kernel_tex_fetch(__points, sd->prim));
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
object_position_transform_auto(kg, sd, ¢er);
|
||||
}
|
||||
|
||||
/* Normal */
|
||||
sd->Ng = normalize(sd->P - center);
|
||||
sd->N = sd->Ng;
|
||||
|
||||
# ifdef __DPDU__
|
||||
/* dPdu/dPdv */
|
||||
sd->dPdu = make_float3(0.0f, 0.0f, 0.0f);
|
||||
sd->dPdv = make_float3(0.0f, 0.0f, 0.0f);
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* Copyright 2011-2020 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "bvh/bvh.h"
|
||||
|
||||
#include "scene/pointcloud.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* PointCloud Point */
|
||||
|
||||
void PointCloud::Point::bounds_grow(const float3 *points,
|
||||
const float *radius,
|
||||
BoundBox &bounds) const
|
||||
{
|
||||
bounds.grow(points[index], radius[index]);
|
||||
}
|
||||
|
||||
void PointCloud::Point::bounds_grow(const float3 *points,
|
||||
const float *radius,
|
||||
const Transform &aligned_space,
|
||||
BoundBox &bounds) const
|
||||
{
|
||||
float3 P = transform_point(&aligned_space, points[index]);
|
||||
bounds.grow(P, radius[index]);
|
||||
}
|
||||
|
||||
void PointCloud::Point::bounds_grow(const float4 &point, BoundBox &bounds) const
|
||||
{
|
||||
bounds.grow(float4_to_float3(point), point.w);
|
||||
}
|
||||
|
||||
float4 PointCloud::Point::motion_key(const float3 *points,
|
||||
const float *radius,
|
||||
const float3 *point_steps,
|
||||
size_t num_points,
|
||||
size_t num_steps,
|
||||
float time,
|
||||
size_t p) const
|
||||
{
|
||||
/* Figure out which steps we need to fetch and their
|
||||
* interpolation factor. */
|
||||
const size_t max_step = num_steps - 1;
|
||||
const size_t step = min((int)(time * max_step), max_step - 1);
|
||||
const float t = time * max_step - step;
|
||||
/* Fetch vertex coordinates. */
|
||||
const float4 curr_key = point_for_step(
|
||||
points, radius, point_steps, num_points, num_steps, step, p);
|
||||
const float4 next_key = point_for_step(
|
||||
points, radius, point_steps, num_points, num_steps, step + 1, p);
|
||||
/* Interpolate between steps. */
|
||||
return (1.0f - t) * curr_key + t * next_key;
|
||||
}
|
||||
|
||||
float4 PointCloud::Point::point_for_step(const float3 *points,
|
||||
const float *radius,
|
||||
const float3 *point_steps,
|
||||
size_t num_points,
|
||||
size_t num_steps,
|
||||
size_t step,
|
||||
size_t p) const
|
||||
{
|
||||
const size_t center_step = ((num_steps - 1) / 2);
|
||||
if (step == center_step) {
|
||||
/* Center step: regular key location. */
|
||||
return make_float4(points[p].x, points[p].y, points[p].z, radius[p]);
|
||||
}
|
||||
else {
|
||||
/* Center step is not stored in this array. */
|
||||
if (step > center_step) {
|
||||
step--;
|
||||
}
|
||||
const size_t offset = step * num_points;
|
||||
return make_float4(point_steps[offset + p].x,
|
||||
point_steps[offset + p].y,
|
||||
point_steps[offset + p].z,
|
||||
radius[offset + p]);
|
||||
}
|
||||
}
|
||||
|
||||
/* PointCloud */
|
||||
|
||||
NODE_DEFINE(PointCloud)
|
||||
{
|
||||
NodeType *type = NodeType::add(
|
||||
"pointcloud", create, NodeType::NONE, Geometry::get_node_base_type());
|
||||
|
||||
SOCKET_POINT_ARRAY(points, "Points", array<float3>());
|
||||
SOCKET_FLOAT_ARRAY(radius, "Radius", array<float>());
|
||||
SOCKET_INT_ARRAY(shader, "Shader", array<int>());
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
PointCloud::PointCloud() : Geometry(node_type, Geometry::POINTCLOUD)
|
||||
{
|
||||
}
|
||||
|
||||
PointCloud::~PointCloud()
|
||||
{
|
||||
}
|
||||
|
||||
void PointCloud::resize(int numpoints)
|
||||
{
|
||||
points.resize(numpoints);
|
||||
radius.resize(numpoints);
|
||||
shader.resize(numpoints);
|
||||
attributes.resize();
|
||||
|
||||
tag_points_modified();
|
||||
tag_radius_modified();
|
||||
tag_shader_modified();
|
||||
}
|
||||
|
||||
void PointCloud::reserve(int numpoints)
|
||||
{
|
||||
points.reserve(numpoints);
|
||||
radius.reserve(numpoints);
|
||||
shader.reserve(numpoints);
|
||||
attributes.resize(true);
|
||||
}
|
||||
|
||||
void PointCloud::clear(const bool preserve_shaders)
|
||||
{
|
||||
Geometry::clear(preserve_shaders);
|
||||
|
||||
points.clear();
|
||||
radius.clear();
|
||||
shader.clear();
|
||||
attributes.clear();
|
||||
|
||||
tag_points_modified();
|
||||
tag_radius_modified();
|
||||
tag_shader_modified();
|
||||
}
|
||||
|
||||
void PointCloud::add_point(float3 co, float r, int shader_index)
|
||||
{
|
||||
points.push_back_reserved(co);
|
||||
radius.push_back_reserved(r);
|
||||
shader.push_back_reserved(shader_index);
|
||||
|
||||
tag_points_modified();
|
||||
tag_radius_modified();
|
||||
tag_shader_modified();
|
||||
}
|
||||
|
||||
void PointCloud::copy_center_to_motion_step(const int motion_step)
|
||||
{
|
||||
Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (attr_mP) {
|
||||
float3 *points_data = points.data();
|
||||
size_t numpoints = points.size();
|
||||
memcpy(
|
||||
attr_mP->data_float3() + motion_step * numpoints, points_data, sizeof(float3) * numpoints);
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloud::get_uv_tiles(ustring map, unordered_set<int> &tiles)
|
||||
{
|
||||
Attribute *attr;
|
||||
|
||||
if (map.empty()) {
|
||||
attr = attributes.find(ATTR_STD_UV);
|
||||
}
|
||||
else {
|
||||
attr = attributes.find(map);
|
||||
}
|
||||
|
||||
if (attr) {
|
||||
attr->get_uv_tiles(this, ATTR_PRIM_GEOMETRY, tiles);
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloud::compute_bounds()
|
||||
{
|
||||
BoundBox bnds = BoundBox::empty;
|
||||
size_t numpoints = points.size();
|
||||
|
||||
if (numpoints > 0) {
|
||||
for (size_t i = 0; i < numpoints; i++) {
|
||||
bnds.grow(points[i], radius[i]);
|
||||
}
|
||||
|
||||
Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
if (use_motion_blur && attr) {
|
||||
size_t steps_size = points.size() * (motion_steps - 1);
|
||||
float3 *point_steps = attr->data_float3();
|
||||
|
||||
for (size_t i = 0; i < steps_size; i++)
|
||||
bnds.grow(point_steps[i]);
|
||||
}
|
||||
|
||||
if (!bnds.valid()) {
|
||||
bnds = BoundBox::empty;
|
||||
|
||||
/* skip nan or inf coordinates */
|
||||
for (size_t i = 0; i < numpoints; i++)
|
||||
bnds.grow_safe(points[i], radius[i]);
|
||||
|
||||
if (use_motion_blur && attr) {
|
||||
size_t steps_size = points.size() * (motion_steps - 1);
|
||||
float3 *point_steps = attr->data_float3();
|
||||
|
||||
for (size_t i = 0; i < steps_size; i++)
|
||||
bnds.grow_safe(point_steps[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bnds.valid()) {
|
||||
/* empty mesh */
|
||||
bnds.grow(make_float3(0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
bounds = bnds;
|
||||
}
|
||||
|
||||
void PointCloud::apply_transform(const Transform &tfm, const bool apply_to_motion)
|
||||
{
|
||||
/* compute uniform scale */
|
||||
float3 c0 = transform_get_column(&tfm, 0);
|
||||
float3 c1 = transform_get_column(&tfm, 1);
|
||||
float3 c2 = transform_get_column(&tfm, 2);
|
||||
float scalar = powf(fabsf(dot(cross(c0, c1), c2)), 1.0f / 3.0f);
|
||||
|
||||
/* apply transform to curve keys */
|
||||
for (size_t i = 0; i < points.size(); i++) {
|
||||
float3 co = transform_point(&tfm, points[i]);
|
||||
float r = radius[i] * scalar;
|
||||
|
||||
/* scale for curve radius is only correct for uniform scale
|
||||
*/
|
||||
points[i] = co;
|
||||
radius[i] = r;
|
||||
}
|
||||
|
||||
if (apply_to_motion) {
|
||||
Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
|
||||
if (attr) {
|
||||
/* apply transform to motion curve keys */
|
||||
size_t steps_size = points.size() * (motion_steps - 1);
|
||||
float4 *point_steps = attr->data_float4();
|
||||
|
||||
for (size_t i = 0; i < steps_size; i++) {
|
||||
float3 co = transform_point(&tfm, float4_to_float3(point_steps[i]));
|
||||
float radius = point_steps[i].w * scalar;
|
||||
|
||||
/* scale for curve radius is only correct for uniform
|
||||
* scale */
|
||||
point_steps[i] = float3_to_float4(co);
|
||||
point_steps[i].w = radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloud::pack(Scene *scene, float4 *packed_points, uint *packed_shader)
|
||||
{
|
||||
size_t numpoints = points.size();
|
||||
float3 *points_data = points.data();
|
||||
float *radius_data = radius.data();
|
||||
int *shader_data = shader.data();
|
||||
|
||||
for (size_t i = 0; i < numpoints; i++) {
|
||||
packed_points[i] = make_float4(
|
||||
points_data[i].x, points_data[i].y, points_data[i].z, radius_data[i]);
|
||||
}
|
||||
|
||||
uint shader_id = 0;
|
||||
uint last_shader = -1;
|
||||
for (size_t i = 0; i < numpoints; i++) {
|
||||
if (last_shader != shader_data[i]) {
|
||||
last_shader = shader_data[i];
|
||||
Shader *shader = (last_shader < used_shaders.size()) ?
|
||||
static_cast<Shader *>(used_shaders[last_shader]) :
|
||||
scene->default_surface;
|
||||
shader_id = scene->shader_manager->get_shader_id(shader);
|
||||
}
|
||||
packed_shader[i] = shader_id;
|
||||
}
|
||||
}
|
||||
|
||||
PrimitiveType PointCloud::primitive_type() const
|
||||
{
|
||||
return has_motion_blur() ? PRIMITIVE_MOTION_POINT : PRIMITIVE_POINT;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2011-2020 Blender Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __POINTCLOUD_H__
|
||||
# define __POINTCLOUD_H__
|
||||
|
||||
# include "scene/geometry.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class PointCloud : public Geometry {
|
||||
public:
|
||||
NODE_DECLARE
|
||||
|
||||
/* PointCloud Point */
|
||||
struct Point {
|
||||
int index;
|
||||
|
||||
void bounds_grow(const float3 *points, const float *radius, BoundBox &bounds) const;
|
||||
void bounds_grow(const float3 *points,
|
||||
const float *radius,
|
||||
const Transform &aligned_space,
|
||||
BoundBox &bounds) const;
|
||||
void bounds_grow(const float4 &point, BoundBox &bounds) const;
|
||||
|
||||
float4 motion_key(const float3 *points,
|
||||
const float *radius,
|
||||
const float3 *point_steps,
|
||||
size_t num_points,
|
||||
size_t num_steps,
|
||||
float time,
|
||||
size_t p) const;
|
||||
float4 point_for_step(const float3 *points,
|
||||
const float *radius,
|
||||
const float3 *point_steps,
|
||||
size_t num_points,
|
||||
size_t num_steps,
|
||||
size_t step,
|
||||
size_t p) const;
|
||||
};
|
||||
|
||||
NODE_SOCKET_API_ARRAY(array<float3>, points)
|
||||
NODE_SOCKET_API_ARRAY(array<float>, radius)
|
||||
NODE_SOCKET_API_ARRAY(array<int>, shader)
|
||||
|
||||
/* Constructor/Destructor */
|
||||
PointCloud();
|
||||
~PointCloud();
|
||||
|
||||
/* Geometry */
|
||||
void clear(const bool preserver_shaders = false) override;
|
||||
|
||||
void resize(int numpoints);
|
||||
void reserve(int numpoints);
|
||||
void add_point(float3 loc, float radius, int shader = 0);
|
||||
|
||||
void copy_center_to_motion_step(const int motion_step);
|
||||
|
||||
void compute_bounds() override;
|
||||
void apply_transform(const Transform &tfm, const bool apply_to_motion) override;
|
||||
|
||||
/* Points */
|
||||
Point get_point(int i) const
|
||||
{
|
||||
Point point = {i};
|
||||
return point;
|
||||
}
|
||||
|
||||
size_t num_points() const
|
||||
{
|
||||
return points.size();
|
||||
}
|
||||
|
||||
size_t num_attributes() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* UDIM */
|
||||
void get_uv_tiles(ustring map, unordered_set<int> &tiles) override;
|
||||
|
||||
PrimitiveType primitive_type() const override;
|
||||
|
||||
/* BVH */
|
||||
void pack(Scene *scene, float4 *packed_points, uint *packed_shader);
|
||||
|
||||
private:
|
||||
friend class BVH2;
|
||||
friend class BVHBuild;
|
||||
friend class BVHSpatialSplit;
|
||||
friend class DiagSplit;
|
||||
friend class EdgeDice;
|
||||
friend class GeometryManager;
|
||||
friend class ObjectManager;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* __POINTCLOUD_H__ */
|
|
@ -267,6 +267,49 @@ void MEM_use_guarded_allocator(void);
|
|||
#endif /* __cplusplus */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
# include <type_traits>
|
||||
# include <utility>
|
||||
|
||||
/**
|
||||
* Allocate new memory for and constructs an object of type #T.
|
||||
* #MEM_delete should be used to delete the object. Just calling #MEM_freeN is not enough when #T
|
||||
* is not a trivial type.
|
||||
*/
|
||||
template<typename T, typename... Args>
|
||||
inline T *MEM_new(const char *allocation_name, Args &&...args)
|
||||
{
|
||||
void *buffer = MEM_mallocN(sizeof(T), allocation_name);
|
||||
return new (buffer) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates zero-initialized memory for an object of type #T. The constructor of #T is not called,
|
||||
* therefor this should only used with trivial types (like all C types).
|
||||
* It's valid to call #MEM_freeN on a pointer returned by this, because a destructor call is not
|
||||
* necessary, because the type is trivial.
|
||||
*/
|
||||
template<typename T> inline T *MEM_cnew(const char *allocation_name)
|
||||
{
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new should be used.");
|
||||
return static_cast<T *>(MEM_callocN(sizeof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructs and deallocates an object previously allocated with any `MEM_*` function.
|
||||
* Passing in null does nothing.
|
||||
*/
|
||||
template<typename T> inline void MEM_delete(const T *ptr)
|
||||
{
|
||||
if (ptr == nullptr) {
|
||||
/* Support #ptr being null, because C++ `delete` supports that as well. */
|
||||
return;
|
||||
}
|
||||
/* C++ allows destruction of const objects, so the pointer is allowed to be const. */
|
||||
ptr->~T();
|
||||
MEM_freeN(const_cast<T *>(ptr));
|
||||
}
|
||||
|
||||
/* Allocation functions (for C++ only). */
|
||||
# define MEM_CXX_CLASS_ALLOC_FUNCS(_id) \
|
||||
public: \
|
||||
|
@ -300,36 +343,6 @@ void MEM_use_guarded_allocator(void);
|
|||
{ \
|
||||
}
|
||||
|
||||
/* Needed when type includes a namespace, then the namespace should not be
|
||||
* specified after ~, so using a macro fails. */
|
||||
template<class T> inline void OBJECT_GUARDED_DESTRUCTOR(T *what)
|
||||
{
|
||||
what->~T();
|
||||
}
|
||||
|
||||
# if defined __GNUC__
|
||||
# define OBJECT_GUARDED_NEW(type, args...) new (MEM_mallocN(sizeof(type), __func__)) type(args)
|
||||
# else
|
||||
# define OBJECT_GUARDED_NEW(type, ...) \
|
||||
new (MEM_mallocN(sizeof(type), __FUNCTION__)) type(__VA_ARGS__)
|
||||
# endif
|
||||
# define OBJECT_GUARDED_DELETE(what, type) \
|
||||
{ \
|
||||
if (what) { \
|
||||
OBJECT_GUARDED_DESTRUCTOR((type *)what); \
|
||||
MEM_freeN(what); \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
# define OBJECT_GUARDED_SAFE_DELETE(what, type) \
|
||||
{ \
|
||||
if (what) { \
|
||||
OBJECT_GUARDED_DESTRUCTOR((type *)what); \
|
||||
MEM_freeN(what); \
|
||||
what = NULL; \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __MEM_GUARDEDALLOC_H__ */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3084,5 +3084,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
/* Add node storage for map range node. */
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
if (node->type == SH_NODE_MAP_RANGE) {
|
||||
if (node->storage == NULL) {
|
||||
NodeMapRange *data = MEM_callocN(sizeof(NodeMapRange), __func__);
|
||||
data->clamp = node->custom1;
|
||||
data->data_type = CD_PROP_FLOAT;
|
||||
data->interpolation_type = node->custom2;
|
||||
node->storage = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_NODETREE_END;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string_search.h"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "node_intern.hh"
|
||||
|
||||
using blender::nodes::SocketLinkOperation;
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
|
||||
struct LinkDragSearchStorage {
|
||||
bNode &from_node;
|
||||
bNodeSocket &from_socket;
|
||||
float2 cursor;
|
||||
Vector<SocketLinkOperation> search_link_ops;
|
||||
char search[256];
|
||||
|
||||
eNodeSocketInOut in_out() const
|
||||
{
|
||||
return static_cast<eNodeSocketInOut>(from_socket.in_out);
|
||||
}
|
||||
};
|
||||
|
||||
static void add_reroute_node_fn(nodes::LinkSearchOpParams ¶ms)
|
||||
{
|
||||
bNode &reroute = params.add_node("NodeReroute");
|
||||
if (params.socket.in_out == SOCK_IN) {
|
||||
nodeAddLink(¶ms.node_tree,
|
||||
&reroute,
|
||||
static_cast<bNodeSocket *>(reroute.outputs.first),
|
||||
¶ms.node,
|
||||
¶ms.socket);
|
||||
}
|
||||
else {
|
||||
nodeAddLink(¶ms.node_tree,
|
||||
¶ms.node,
|
||||
¶ms.socket,
|
||||
&reroute,
|
||||
static_cast<bNodeSocket *>(reroute.inputs.first));
|
||||
}
|
||||
}
|
||||
|
||||
static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms)
|
||||
{
|
||||
/* Add a group input based on the connected socket, and add a new group input node. */
|
||||
bNodeSocket *interface_socket = ntreeAddSocketInterfaceFromSocket(
|
||||
¶ms.node_tree, ¶ms.node, ¶ms.socket);
|
||||
const int group_input_index = BLI_findindex(¶ms.node_tree.inputs, interface_socket);
|
||||
|
||||
bNode &group_input = params.add_node("NodeGroupInput");
|
||||
|
||||
/* This is necessary to create the new sockets in the other input nodes. */
|
||||
ntreeUpdateTree(CTX_data_main(¶ms.C), ¶ms.node_tree);
|
||||
|
||||
/* Hide the new input in all other group input nodes, to avoid making them taller. */
|
||||
LISTBASE_FOREACH (bNode *, node, ¶ms.node_tree.nodes) {
|
||||
if (node->type == NODE_GROUP_INPUT) {
|
||||
bNodeSocket *new_group_input_socket = (bNodeSocket *)BLI_findlink(&node->outputs,
|
||||
group_input_index);
|
||||
new_group_input_socket->flag |= SOCK_HIDDEN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide all existing inputs in the new group input node, to only display the new one. */
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &group_input.outputs) {
|
||||
socket->flag |= SOCK_HIDDEN;
|
||||
}
|
||||
|
||||
bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index);
|
||||
if (socket == nullptr) {
|
||||
/* Adding sockets can fail in some cases. There's no good reason not to be safe here. */
|
||||
return;
|
||||
}
|
||||
/* Unhide the socket for the new input in the new node and make a connection to it. */
|
||||
socket->flag &= ~SOCK_HIDDEN;
|
||||
nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the callback to gather compatible socket connections for all node types, and the operations
|
||||
* that will actually make the connections. Also add some custom operations like connecting a group
|
||||
* output node.
|
||||
*/
|
||||
static void gather_socket_link_operations(bNodeTree &node_tree,
|
||||
const bNodeSocket &socket,
|
||||
Vector<SocketLinkOperation> &search_link_ops)
|
||||
{
|
||||
NODE_TYPES_BEGIN (node_type) {
|
||||
if (StringRef(node_type->idname).find("Legacy") != StringRef::not_found) {
|
||||
continue;
|
||||
}
|
||||
const char *disabled_hint;
|
||||
if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node_type->gather_link_search_ops) {
|
||||
nodes::GatherLinkSearchOpParams params{*node_type, node_tree, socket, search_link_ops};
|
||||
node_type->gather_link_search_ops(params);
|
||||
}
|
||||
}
|
||||
NODE_TYPES_END;
|
||||
|
||||
search_link_ops.append({IFACE_("Reroute"), add_reroute_node_fn});
|
||||
|
||||
const bool is_node_group = !(node_tree.id.flag & LIB_EMBEDDED_DATA);
|
||||
|
||||
if (is_node_group && socket.in_out == SOCK_IN) {
|
||||
search_link_ops.append({IFACE_("Group Input"), add_group_input_node_fn});
|
||||
}
|
||||
}
|
||||
|
||||
static void link_drag_search_update_fn(const bContext *UNUSED(C),
|
||||
void *arg,
|
||||
const char *str,
|
||||
uiSearchItems *items,
|
||||
const bool is_first)
|
||||
{
|
||||
LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg);
|
||||
|
||||
StringSearch *search = BLI_string_search_new();
|
||||
|
||||
for (SocketLinkOperation &op : storage.search_link_ops) {
|
||||
BLI_string_search_add(search, op.name.c_str(), &op, op.weight);
|
||||
}
|
||||
|
||||
/* Don't filter when the menu is first opened, but still run the search
|
||||
* so the items are in the same order they will appear in while searching. */
|
||||
const char *string = is_first ? "" : str;
|
||||
SocketLinkOperation **filtered_items;
|
||||
const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items);
|
||||
|
||||
for (const int i : IndexRange(filtered_amount)) {
|
||||
SocketLinkOperation &item = *filtered_items[i];
|
||||
if (!UI_search_item_add(items, item.name.c_str(), &item, ICON_NONE, 0, 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(filtered_items);
|
||||
BLI_string_search_free(search);
|
||||
}
|
||||
|
||||
static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
|
||||
{
|
||||
Main &bmain = *CTX_data_main(C);
|
||||
SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg1);
|
||||
SocketLinkOperation *item = static_cast<SocketLinkOperation *>(arg2);
|
||||
if (item == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
node_deselect_all(snode);
|
||||
|
||||
Vector<bNode *> new_nodes;
|
||||
nodes::LinkSearchOpParams params{
|
||||
*C, *snode.edittree, storage.from_node, storage.from_socket, new_nodes};
|
||||
item->fn(params);
|
||||
if (new_nodes.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* For now, assume that only one node is created by the callback. */
|
||||
BLI_assert(new_nodes.size() == 1);
|
||||
bNode *new_node = new_nodes.first();
|
||||
|
||||
new_node->locx = storage.cursor.x / UI_DPI_FAC;
|
||||
new_node->locy = storage.cursor.y / UI_DPI_FAC + 20 * UI_DPI_FAC;
|
||||
if (storage.in_out() == SOCK_IN) {
|
||||
new_node->locx -= new_node->width;
|
||||
}
|
||||
|
||||
nodeSetSelected(new_node, true);
|
||||
nodeSetActive(snode.edittree, new_node);
|
||||
|
||||
/* Ideally it would be possible to tag the node tree in some way so it updates only after the
|
||||
* translate operation is finished, but normally moving nodes around doesn't cause updates. */
|
||||
ntreeUpdateTree(&bmain, snode.edittree);
|
||||
snode_notify(*C, snode);
|
||||
snode_dag_update(*C, snode);
|
||||
|
||||
/* Start translation operator with the new node. */
|
||||
wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_translate", true);
|
||||
BLI_assert(ot);
|
||||
PointerRNA ptr;
|
||||
WM_operator_properties_create_ptr(&ptr, ot);
|
||||
RNA_boolean_set(&ptr, "view2d_edge_pan", true);
|
||||
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
|
||||
WM_operator_properties_free(&ptr);
|
||||
}
|
||||
|
||||
static void link_drag_search_free_fn(void *arg)
|
||||
{
|
||||
LinkDragSearchStorage *storage = static_cast<LinkDragSearchStorage *>(arg);
|
||||
delete storage;
|
||||
}
|
||||
|
||||
static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *arg_op)
|
||||
{
|
||||
LinkDragSearchStorage &storage = *(LinkDragSearchStorage *)arg_op;
|
||||
|
||||
bNodeTree *node_tree = CTX_wm_space_node(C)->nodetree;
|
||||
gather_socket_link_operations(*node_tree, storage.from_socket, storage.search_link_ops);
|
||||
|
||||
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
|
||||
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
|
||||
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
|
||||
|
||||
uiBut *but = uiDefSearchBut(block,
|
||||
storage.search,
|
||||
0,
|
||||
ICON_VIEWZOOM,
|
||||
sizeof(storage.search),
|
||||
storage.in_out() == SOCK_OUT ? 10 : 10 - UI_searchbox_size_x(),
|
||||
10,
|
||||
UI_searchbox_size_x(),
|
||||
UI_UNIT_Y,
|
||||
0,
|
||||
0,
|
||||
"");
|
||||
UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
|
||||
UI_but_func_search_set(but,
|
||||
nullptr,
|
||||
link_drag_search_update_fn,
|
||||
&storage,
|
||||
false,
|
||||
link_drag_search_free_fn,
|
||||
link_drag_search_exec_fn,
|
||||
nullptr);
|
||||
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
|
||||
|
||||
/* Fake button to hold space for the search items. */
|
||||
uiDefBut(block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
"",
|
||||
storage.in_out() == SOCK_OUT ? 10 : 10 - UI_searchbox_size_x(),
|
||||
10 - UI_searchbox_size_y(),
|
||||
UI_searchbox_size_x(),
|
||||
UI_searchbox_size_y(),
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
const int offset[2] = {0, -UI_UNIT_Y};
|
||||
UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, offset);
|
||||
return block;
|
||||
}
|
||||
|
||||
void invoke_node_link_drag_add_menu(bContext &C,
|
||||
bNode &node,
|
||||
bNodeSocket &socket,
|
||||
const float2 &cursor)
|
||||
{
|
||||
LinkDragSearchStorage *storage = new LinkDragSearchStorage{node, socket, cursor};
|
||||
/* Use the "_ex" variant with `can_refresh` false to avoid a double free when closing Blender. */
|
||||
UI_popup_block_invoke_ex(&C, create_search_popup_block, storage, nullptr, false);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::space_node
|
|
@ -969,11 +969,9 @@ typedef struct ParticleEditSettings {
|
|||
struct Object *shape_object;
|
||||
} ParticleEditSettings;
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/* Sculpt */
|
||||
|
||||
struct BrushChannelSet;
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/** Sculpt. */
|
||||
typedef struct Sculpt {
|
||||
Paint paint;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_node_types.h" /* Necessary for eNodeSocketInOut. */
|
||||
|
||||
#include "NOD_node_declaration.hh"
|
||||
|
||||
struct bContext;
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
/**
|
||||
* Parameters for the operation operation of adding a node after the link drag search menu closes.
|
||||
*/
|
||||
class LinkSearchOpParams {
|
||||
private:
|
||||
/**
|
||||
* Keep track of the nodes added by the callback, so they can be selected or moved afterwards.
|
||||
*/
|
||||
Vector<bNode *> &added_nodes_;
|
||||
|
||||
public:
|
||||
const bContext &C;
|
||||
bNodeTree &node_tree;
|
||||
/**
|
||||
* The node that contains the #socket.
|
||||
*/
|
||||
bNode &node;
|
||||
/**
|
||||
* The existing socket to connect any added nodes to. Might be an input or output socket.
|
||||
*/
|
||||
bNodeSocket &socket;
|
||||
|
||||
LinkSearchOpParams(const bContext &C,
|
||||
bNodeTree &node_tree,
|
||||
bNode &node,
|
||||
bNodeSocket &socket,
|
||||
Vector<bNode *> &added_nodes)
|
||||
: added_nodes_(added_nodes), C(C), node_tree(node_tree), node(node), socket(socket)
|
||||
{
|
||||
}
|
||||
|
||||
bNode &add_node(StringRef idname);
|
||||
bNode &add_node(const bNodeType &type);
|
||||
/**
|
||||
* Find a socket with the given name (correctly checks for inputs and outputs)
|
||||
* and connect it to the socket the link drag started from (#socket).
|
||||
*/
|
||||
void connect_available_socket(bNode &new_node, StringRef socket_name);
|
||||
/**
|
||||
* Like #connect_available_socket, but also calls the node's update function.
|
||||
*/
|
||||
void update_and_connect_available_socket(bNode &new_node, StringRef socket_name);
|
||||
};
|
||||
|
||||
struct SocketLinkOperation {
|
||||
using LinkSocketFn = std::function<void(LinkSearchOpParams &link_params)>;
|
||||
|
||||
std::string name;
|
||||
LinkSocketFn fn;
|
||||
int weight = 0;
|
||||
};
|
||||
|
||||
class GatherLinkSearchOpParams {
|
||||
/** The current node type. */
|
||||
const bNodeType &node_type_;
|
||||
|
||||
const bNodeTree &node_tree_;
|
||||
|
||||
const bNodeSocket &other_socket_;
|
||||
|
||||
/* The operations currently being built. Owned by the caller. */
|
||||
Vector<SocketLinkOperation> &items_;
|
||||
|
||||
public:
|
||||
GatherLinkSearchOpParams(const bNodeType &node_type,
|
||||
const bNodeTree &node_tree,
|
||||
const bNodeSocket &other_socket,
|
||||
Vector<SocketLinkOperation> &items)
|
||||
: node_type_(node_type), node_tree_(node_tree), other_socket_(other_socket), items_(items)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* The node on the other side of the dragged link.
|
||||
*/
|
||||
const bNodeSocket &other_socket() const;
|
||||
|
||||
/**
|
||||
* The node tree the user is editing when the search menu is created.
|
||||
*/
|
||||
const bNodeTree &node_tree() const;
|
||||
|
||||
/**
|
||||
* The type of the node in the current callback.
|
||||
*/
|
||||
const bNodeType &node_type() const;
|
||||
|
||||
/**
|
||||
* Whether to list the input or output sockets of the node.
|
||||
*/
|
||||
eNodeSocketInOut in_out() const;
|
||||
|
||||
/**
|
||||
* \param weight: Used to customize the order when multiple search items match.
|
||||
*
|
||||
* \warning When creating lambdas for the #fn argument, be careful not to capture this class
|
||||
* itself, since it is temporary. That is why we tend to use the same variable name for this
|
||||
* class (`params`) that we do for the argument to `LinkSocketFn`.
|
||||
*/
|
||||
void add_item(std::string socket_name, SocketLinkOperation::LinkSocketFn fn, int weight = 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* This callback can be used for a node type when a few things are true about its inputs.
|
||||
* To avoid creating more boilerplate, it is the default callback for node types.
|
||||
* - Either all declared sockets are visible in the default state of the node, *OR* the node's
|
||||
* type's declaration has been extended with #make_available functions for those sockets.
|
||||
*
|
||||
* If a node type does not meet these criteria, the function will do nothing in a release build.
|
||||
* In a debug build, an assert will most likely be hit.
|
||||
*
|
||||
* \note For nodes with the deprecated #bNodeSocketTemplate instead of a declaration,
|
||||
* these criteria do not apply and the function just tries its best without asserting.
|
||||
*/
|
||||
void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms);
|
||||
|
||||
void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms,
|
||||
Span<SocketDeclarationPtr> declarations);
|
||||
|
||||
} // namespace blender::nodes
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_node.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "NOD_node_declaration.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
void GatherLinkSearchOpParams::add_item(std::string socket_name,
|
||||
SocketLinkOperation::LinkSocketFn fn,
|
||||
const int weight)
|
||||
{
|
||||
|
||||
std::string name = std::string(node_type_.ui_name) + " " + UI_MENU_ARROW_SEP + socket_name;
|
||||
|
||||
items_.append({std::move(name), std::move(fn), weight});
|
||||
}
|
||||
|
||||
const bNodeSocket &GatherLinkSearchOpParams::other_socket() const
|
||||
{
|
||||
return other_socket_;
|
||||
}
|
||||
|
||||
const bNodeTree &GatherLinkSearchOpParams::node_tree() const
|
||||
{
|
||||
return node_tree_;
|
||||
}
|
||||
|
||||
const bNodeType &GatherLinkSearchOpParams::node_type() const
|
||||
{
|
||||
return node_type_;
|
||||
}
|
||||
|
||||
eNodeSocketInOut GatherLinkSearchOpParams::in_out() const
|
||||
{
|
||||
return other_socket_.in_out == SOCK_IN ? SOCK_OUT : SOCK_IN;
|
||||
}
|
||||
|
||||
void LinkSearchOpParams::connect_available_socket(bNode &new_node, StringRef socket_name)
|
||||
{
|
||||
const eNodeSocketInOut in_out = socket.in_out == SOCK_IN ? SOCK_OUT : SOCK_IN;
|
||||
bNodeSocket *new_node_socket = bke::node_find_enabled_socket(new_node, in_out, socket_name);
|
||||
if (new_node_socket == nullptr) {
|
||||
/* If the socket isn't found, some node's search gather functions probably aren't configured
|
||||
* properly. It's likely enough that it's worth avoiding a crash in a release build though. */
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
nodeAddLink(&node_tree, &new_node, new_node_socket, &node, &socket);
|
||||
}
|
||||
|
||||
bNode &LinkSearchOpParams::add_node(StringRef idname)
|
||||
{
|
||||
std::string idname_str = idname;
|
||||
bNode *node = nodeAddNode(&C, &node_tree, idname_str.c_str());
|
||||
BLI_assert(node != nullptr);
|
||||
added_nodes_.append(node);
|
||||
return *node;
|
||||
}
|
||||
|
||||
bNode &LinkSearchOpParams::add_node(const bNodeType &node_type)
|
||||
{
|
||||
return this->add_node(node_type.idname);
|
||||
}
|
||||
|
||||
void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node,
|
||||
StringRef socket_name)
|
||||
{
|
||||
if (new_node.typeinfo->updatefunc) {
|
||||
new_node.typeinfo->updatefunc(&node_tree, &new_node);
|
||||
}
|
||||
this->connect_available_socket(new_node, socket_name);
|
||||
}
|
||||
|
||||
void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms,
|
||||
Span<SocketDeclarationPtr> declarations)
|
||||
{
|
||||
const bNodeType &node_type = params.node_type();
|
||||
|
||||
const SocketDeclaration *main_socket = nullptr;
|
||||
Vector<const SocketDeclaration *> connectable_sockets;
|
||||
|
||||
Set<StringRef> socket_names;
|
||||
for (const int i : declarations.index_range()) {
|
||||
const SocketDeclaration &socket = *declarations[i];
|
||||
if (!socket_names.add(socket.name())) {
|
||||
/* Don't add sockets with the same name to the search. Needed to support being called from
|
||||
* #search_link_ops_for_basic_node, which should have "okay" behavior for nodes with
|
||||
* duplicate socket names. */
|
||||
continue;
|
||||
}
|
||||
if (!socket.can_connect(params.other_socket())) {
|
||||
continue;
|
||||
}
|
||||
if (socket.is_default_link_socket() || main_socket == nullptr) {
|
||||
/* Either the first connectable or explicitly tagged socket is the main socket. */
|
||||
main_socket = &socket;
|
||||
}
|
||||
connectable_sockets.append(&socket);
|
||||
}
|
||||
for (const int i : connectable_sockets.index_range()) {
|
||||
const SocketDeclaration &socket = *connectable_sockets[i];
|
||||
/* Give non-main sockets a lower weight so that they don't show up at the top of the search
|
||||
* when they are not explicitly searched for. The -1 is used to make sure that the first socket
|
||||
* has a smaller weight than zero so that it does not have the same weight as the main socket.
|
||||
* Negative weights are used to avoid making the highest weight dependent on the number of
|
||||
* sockets. */
|
||||
const int weight = (&socket == main_socket) ? 0 : -1 - i;
|
||||
params.add_item(
|
||||
socket.name(),
|
||||
[&node_type, &socket](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node(node_type);
|
||||
socket.make_available(node);
|
||||
params.update_and_connect_available_socket(node, socket.name());
|
||||
},
|
||||
weight);
|
||||
}
|
||||
}
|
||||
|
||||
static void search_link_ops_for_socket_templates(GatherLinkSearchOpParams ¶ms,
|
||||
const bNodeSocketTemplate *templates,
|
||||
const eNodeSocketInOut in_out)
|
||||
{
|
||||
const bNodeType &node_type = params.node_type();
|
||||
const bNodeTreeType &node_tree_type = *params.node_tree().typeinfo;
|
||||
|
||||
Set<StringRef> socket_names;
|
||||
for (const bNodeSocketTemplate *socket_template = templates; socket_template->type != -1;
|
||||
socket_template++) {
|
||||
eNodeSocketDatatype from = (eNodeSocketDatatype)socket_template->type;
|
||||
eNodeSocketDatatype to = (eNodeSocketDatatype)params.other_socket().type;
|
||||
if (in_out == SOCK_IN) {
|
||||
std::swap(from, to);
|
||||
}
|
||||
if (node_tree_type.validate_link && !node_tree_type.validate_link(from, to)) {
|
||||
continue;
|
||||
}
|
||||
if (!socket_names.add(socket_template->name)) {
|
||||
/* See comment in #search_link_ops_for_declarations. */
|
||||
continue;
|
||||
}
|
||||
|
||||
params.add_item(
|
||||
socket_template->name, [socket_template, node_type, in_out](LinkSearchOpParams ¶ms) {
|
||||
bNode &node = params.add_node(node_type);
|
||||
bNodeSocket *new_node_socket = bke::node_find_enabled_socket(
|
||||
node, in_out, socket_template->name);
|
||||
if (new_node_socket != nullptr) {
|
||||
/* Rely on the way #nodeAddLink switches in/out if necessary. */
|
||||
nodeAddLink(¶ms.node_tree, ¶ms.node, ¶ms.socket, &node, new_node_socket);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
const bNodeType &node_type = params.node_type();
|
||||
|
||||
if (node_type.declare) {
|
||||
if (node_type.declaration_is_dynamic) {
|
||||
/* Dynamic declarations (whatever they end up being) aren't supported
|
||||
* by this function, but still avoid a crash in release builds. */
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
const NodeDeclaration &declaration = *node_type.fixed_declaration;
|
||||
|
||||
search_link_ops_for_declarations(params, declaration.sockets(params.in_out()));
|
||||
}
|
||||
else if (node_type.inputs && params.in_out() == SOCK_IN) {
|
||||
search_link_ops_for_socket_templates(params, node_type.inputs, SOCK_IN);
|
||||
}
|
||||
else if (node_type.outputs && params.in_out() == SOCK_OUT) {
|
||||
search_link_ops_for_socket_templates(params, node_type.outputs, SOCK_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
Loading…
Reference in New Issue