Cycles: replace spot light disk sampling with sphere sampling #109329
|
@ -147,7 +147,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
|
|||
ls->eval_fac = 1.0f;
|
||||
}
|
||||
else if (type == LIGHT_SPOT) {
|
||||
if (!spot_light_sample<in_volume_segment>(klight, rand, P, ls)) {
|
||||
if (!spot_light_sample<in_volume_segment>(klight, rand, P, N, shader_flags, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -469,7 +469,7 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
|
|||
ls->group = lamp_lightgroup(kg, lamp);
|
||||
|
||||
if (type == LIGHT_SPOT) {
|
||||
if (!spot_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) {
|
||||
if (!spot_light_sample_from_intersection(klight, isect, ray_P, ray_D, N, path_flag, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
|
|||
|
||||
ls->P = P + ls->D * ls->t;
|
||||
|
||||
ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea;
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
if (r_sq == 0) {
|
||||
/* Use intensity instead of radiance for point light. */
|
||||
ls->eval_fac /= sqr(ls->t);
|
||||
|
@ -100,7 +100,7 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern
|
|||
ls->Ng = normalize(ls->P - klight->co);
|
||||
}
|
||||
else {
|
||||
ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea;
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
|
||||
ls->Ng = -ls->D;
|
||||
|
||||
|
@ -136,7 +136,7 @@ ccl_device_inline bool point_light_sample_from_intersection(
|
|||
const uint32_t path_flag,
|
||||
ccl_private LightSample *ccl_restrict ls)
|
||||
{
|
||||
ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea;
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
|
||||
const float radius = klight->spot.radius;
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ ccl_device_forceinline void light_sample_update(KernelGlobals kg,
|
|||
point_light_mnee_sample_update(klight, ls, P, N, path_flag);
|
||||
}
|
||||
else if (ls->type == LIGHT_SPOT) {
|
||||
spot_light_mnee_sample_update(klight, ls, P);
|
||||
spot_light_mnee_sample_update(klight, ls, P, N, path_flag);
|
||||
}
|
||||
else if (ls->type == LIGHT_AREA) {
|
||||
area_light_mnee_sample_update(klight, ls, P);
|
||||
|
|
|
@ -8,96 +8,187 @@
|
|||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device float spot_light_attenuation(const ccl_global KernelSpotLight *spot, float3 ray)
|
||||
/* Transform vector to spot light's local coordinate system. */
|
||||
ccl_device float3 spot_light_to_local(const ccl_global KernelSpotLight *spot, const float3 ray)
|
||||
{
|
||||
const float3 scaled_ray = safe_normalize(make_float3(dot(ray, spot->scaled_axis_u),
|
||||
dot(ray, spot->scaled_axis_v),
|
||||
dot(ray, spot->dir * spot->inv_len_z)));
|
||||
return safe_normalize(make_float3(dot(ray, spot->scaled_axis_u),
|
||||
dot(ray, spot->scaled_axis_v),
|
||||
dot(ray, spot->dir * spot->inv_len_z)));
|
||||
}
|
||||
|
||||
return smoothstepf((scaled_ray.z - spot->cos_half_spot_angle) * spot->spot_smooth);
|
||||
/* Compute spot light attenuation of a ray given in local coordinate system. */
|
||||
ccl_device float spot_light_attenuation(const ccl_global KernelSpotLight *spot, const float3 ray)
|
||||
{
|
||||
return smoothstepf((ray.z - spot->cos_half_spot_angle) * spot->spot_smooth);
|
||||
}
|
||||
|
||||
ccl_device void spot_light_uv(const float3 ray,
|
||||
const float half_cot_half_spot_angle,
|
||||
ccl_private float *u,
|
||||
ccl_private float *v)
|
||||
{
|
||||
/* Ensures that the spot light projects the full image regarless of the spot angle. */
|
||||
const float factor = half_cot_half_spot_angle / ray.z;
|
||||
|
||||
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
|
||||
*u = ray.y * factor + 0.5f;
|
||||
*v = -(ray.x + ray.y) * factor;
|
||||
}
|
||||
|
||||
template<bool in_volume_segment>
|
||||
ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
||||
const float2 rand,
|
||||
const float3 P,
|
||||
const float3 N,
|
||||
const int shader_flags,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
ls->P = klight->co;
|
||||
const float radius = klight->spot.radius;
|
||||
const float r_sq = sqr(klight->spot.radius);
|
||||
|
||||
const float3 center = klight->co;
|
||||
const float radius = klight->spot.radius;
|
||||
/* disk oriented normal */
|
||||
const float3 lightN = normalize(P - center);
|
||||
ls->P = center;
|
||||
|
||||
if (radius > 0.0f) {
|
||||
/* disk light */
|
||||
ls->P += disk_light_sample(lightN, rand) * radius;
|
||||
float3 lightN = P - center;
|
||||
const float d_sq = len_squared(lightN);
|
||||
const float d = sqrtf(d_sq);
|
||||
lightN /= d;
|
||||
|
||||
float cos_theta;
|
||||
ls->t = FLT_MAX;
|
||||
if (d_sq > r_sq) {
|
||||
const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_spot_angle;
|
||||
const float one_minus_cos_half_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq);
|
||||
|
||||
if (in_volume_segment || one_minus_cos_half_angle < one_minus_cos_half_spot_spread) {
|
||||
/* Sample visible part of the sphere. */
|
||||
sample_uniform_cone_concentric(
|
||||
-lightN, one_minus_cos_half_angle, rand, &cos_theta, &ls->D, &ls->pdf);
|
||||
}
|
||||
else {
|
||||
/* Sample spread cone. */
|
||||
sample_uniform_cone_concentric(
|
||||
-klight->spot.dir, one_minus_cos_half_spot_spread, rand, &cos_theta, &ls->D, &ls->pdf);
|
||||
|
||||
if (!ray_sphere_intersect(P, ls->D, 0.0f, FLT_MAX, center, radius, &ls->P, &ls->t)) {
|
||||
/* Sampled direction does not intersect with the light. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION);
|
||||
if (has_transmission) {
|
||||
ls->D = sample_uniform_sphere(rand);
|
||||
ls->pdf = M_1_2PI_F * 0.5f;
|
||||
}
|
||||
else {
|
||||
sample_cos_hemisphere(N, rand, &ls->D, &ls->pdf);
|
||||
}
|
||||
cos_theta = -dot(ls->D, lightN);
|
||||
}
|
||||
|
||||
const float invarea = klight->spot.invarea;
|
||||
ls->pdf = invarea;
|
||||
if (ls->t == FLT_MAX) {
|
||||
/* Law of cosines. */
|
||||
ls->t = d * cos_theta -
|
||||
copysignf(safe_sqrtf(r_sq - d_sq + d_sq * sqr(cos_theta)), d_sq - r_sq);
|
||||
ls->P = P + ls->D * ls->t;
|
||||
}
|
||||
else {
|
||||
/* Already computed when sampling the spread cone. */
|
||||
}
|
||||
|
||||
ls->D = normalize_len(ls->P - P, &ls->t);
|
||||
/* we set the light normal to the outgoing direction to support texturing */
|
||||
ls->Ng = -ls->D;
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
if (d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
|
||||
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
|
||||
|
||||
/* spot light attenuation */
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, -ls->D);
|
||||
if (!in_volume_segment && ls->eval_fac == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float2 uv = map_to_sphere(ls->Ng);
|
||||
ls->u = uv.x;
|
||||
ls->v = uv.y;
|
||||
if (r_sq == 0) {
|
||||
/* Use intensity instead of radiance when the radius is zero. */
|
||||
ls->eval_fac /= sqr(ls->t);
|
||||
/* `ls->Ng` is not well-defined when the radius is zero, use the incoming direction instead. */
|
||||
ls->Ng = -ls->D;
|
||||
}
|
||||
else {
|
||||
ls->Ng = normalize(ls->P - center);
|
||||
/* Remap sampled point onto the sphere to prevent precision issues with small radius. */
|
||||
ls->P = ls->Ng * radius + center;
|
||||
}
|
||||
|
||||
spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
|
||||
|
||||
ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t);
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_device_forceinline float spot_light_pdf(const float cos_half_spread,
|
||||
const float d_sq,
|
||||
const float r_sq,
|
||||
const float3 N,
|
||||
const float3 D,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
if (d_sq > r_sq) {
|
||||
return M_1_2PI_F / min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - cos_half_spread);
|
||||
}
|
||||
|
||||
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
|
||||
return has_transmission ? M_1_2PI_F * 0.5f : pdf_cos_hemisphere(N, D);
|
||||
}
|
||||
|
||||
ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global KernelLight *klight,
|
||||
ccl_private LightSample *ls,
|
||||
const float3 P)
|
||||
const float3 P,
|
||||
const float3 N,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
ls->D = normalize_len(ls->P - P, &ls->t);
|
||||
ls->Ng = -ls->D;
|
||||
|
||||
float2 uv = map_to_sphere(ls->Ng);
|
||||
ls->u = uv.x;
|
||||
ls->v = uv.y;
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D);
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
|
||||
float invarea = klight->spot.invarea;
|
||||
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
|
||||
/* NOTE : preserve pdf in area measure. */
|
||||
ls->pdf = invarea;
|
||||
const float radius = klight->spot.radius;
|
||||
|
||||
/* spot light attenuation */
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, ls->Ng);
|
||||
if (radius > 0) {
|
||||
const float d_sq = len_squared(P - klight->co);
|
||||
const float r_sq = sqr(radius);
|
||||
const float t_sq = sqr(ls->t);
|
||||
|
||||
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ls->D, path_flag);
|
||||
|
||||
/* NOTE : preserve pdf in area measure. */
|
||||
ls->pdf *= 0.5f * fabsf(d_sq - r_sq - t_sq) / (radius * ls->t * t_sq);
|
||||
|
||||
ls->Ng = normalize(ls->P - klight->co);
|
||||
|
||||
if (d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ls->Ng = -ls->D;
|
||||
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
|
||||
/* PDF does not change. */
|
||||
}
|
||||
|
||||
spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
|
||||
}
|
||||
|
||||
ccl_device_inline bool spot_light_intersect(const ccl_global KernelLight *klight,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private float *t)
|
||||
{
|
||||
/* Spot/Disk light. */
|
||||
const float3 lightP = klight->co;
|
||||
const float radius = klight->spot.radius;
|
||||
if (radius == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
/* disk oriented normal */
|
||||
const float3 lightN = normalize(ray->P - lightP);
|
||||
/* One sided. */
|
||||
if (dot(ray->D, lightN) >= 0.0f) {
|
||||
if (dot(ray->D, ray->P - klight->co) >= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float3 P;
|
||||
return ray_disk_intersect(ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, t);
|
||||
return point_light_intersect(klight, ray, t);
|
||||
}
|
||||
|
||||
ccl_device_inline bool spot_light_sample_from_intersection(
|
||||
|
@ -105,35 +196,27 @@ ccl_device_inline bool spot_light_sample_from_intersection(
|
|||
ccl_private const Intersection *ccl_restrict isect,
|
||||
const float3 ray_P,
|
||||
const float3 ray_D,
|
||||
const float3 N,
|
||||
const uint32_t path_flag,
|
||||
ccl_private LightSample *ccl_restrict ls)
|
||||
{
|
||||
/* the normal of the oriented disk */
|
||||
const float3 lightN = normalize(ray_P - klight->co);
|
||||
/* We set the light normal to the outgoing direction to support texturing. */
|
||||
ls->Ng = -ls->D;
|
||||
const float d_sq = len_squared(ray_P - klight->co);
|
||||
const float r_sq = sqr(klight->spot.radius);
|
||||
|
||||
float invarea = klight->spot.invarea;
|
||||
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
|
||||
ls->pdf = invarea;
|
||||
ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ray_D, path_flag);
|
||||
|
||||
/* spot light attenuation */
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, -ls->D);
|
||||
|
||||
if (ls->eval_fac == 0.0f) {
|
||||
const float3 local_ray = spot_light_to_local(&klight->spot, -ray_D);
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
if (d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
if (ls->eval_fac == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float2 uv = map_to_sphere(ls->Ng);
|
||||
ls->u = uv.x;
|
||||
ls->v = uv.y;
|
||||
ls->Ng = r_sq > 0 ? normalize(ls->P - klight->co) : -ray_D;
|
||||
|
||||
/* compute pdf */
|
||||
if (ls->t != FLT_MAX) {
|
||||
ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t);
|
||||
}
|
||||
else {
|
||||
ls->pdf = 0.f;
|
||||
}
|
||||
spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -146,18 +229,20 @@ ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLi
|
|||
ccl_private float2 &distance,
|
||||
ccl_private float3 &point_to_centroid)
|
||||
{
|
||||
float min_distance;
|
||||
const float3 point_to_centroid_ = safe_normalize_len(centroid - P, &min_distance);
|
||||
float dist_point_to_centroid;
|
||||
const float3 point_to_centroid_ = safe_normalize_len(centroid - P, &dist_point_to_centroid);
|
||||
|
||||
const float radius = klight->spot.radius;
|
||||
const float hypotenus = sqrtf(sqr(radius) + sqr(min_distance));
|
||||
cos_theta_u = min_distance / hypotenus;
|
||||
cos_theta_u = (dist_point_to_centroid > radius) ? cos_from_sin(radius / dist_point_to_centroid) :
|
||||
-1.0f;
|
||||
|
||||
if (in_volume_segment) {
|
||||
return true;
|
||||
}
|
||||
|
||||
distance = make_float2(hypotenus, min_distance);
|
||||
distance = (dist_point_to_centroid > radius) ?
|
||||
dist_point_to_centroid * make_float2(1.0f / cos_theta_u, 1.0f) :
|
||||
one_float2() * radius / M_SQRT2_F;
|
||||
point_to_centroid = point_to_centroid_;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1353,12 +1353,13 @@ typedef struct KernelSpotLight {
|
|||
packed_float3 scaled_axis_u;
|
||||
float radius;
|
||||
packed_float3 scaled_axis_v;
|
||||
float invarea;
|
||||
float eval_fac;
|
||||
packed_float3 dir;
|
||||
float cos_half_spot_angle;
|
||||
float half_cot_half_spot_angle;
|
||||
float inv_len_z;
|
||||
float spot_smooth;
|
||||
float pad[2];
|
||||
float pad;
|
||||
} KernelSpotLight;
|
||||
|
||||
/* PointLight is SpotLight with only radius and invarea being used. */
|
||||
|
|
|
@ -1210,22 +1210,22 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||
klights[light_index].strength[1] = light->strength.y;
|
||||
klights[light_index].strength[2] = light->strength.z;
|
||||
|
||||
if (light->light_type == LIGHT_POINT) {
|
||||
if (light->light_type == LIGHT_POINT || light->light_type == LIGHT_SPOT) {
|
||||
shader_id &= ~SHADER_AREA_LIGHT;
|
||||
|
||||
float radius = light->size;
|
||||
/* TODO: `invarea` was used for disk sampling, with the current solid angle sampling this
|
||||
* becomes unnecessary. We could store `eval_fac` instead, but currently it shares the same
|
||||
* #KernelSpotLight type with #LIGHT_SPOT, so keep it know until refactor for spot light. */
|
||||
float invarea = (light->normalize && radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) :
|
||||
|
||||
float invarea = (light->normalize && radius > 0.0f) ? 1.0f / (M_4PI_F * radius * radius) :
|
||||
1.0f;
|
||||
/* Convert radiant flux to radiance or radiant intensity. */
|
||||
float eval_fac = (radius > 0) ? invarea * M_1_PI_F : 0.25f * M_1_PI_F;
|
||||
|
||||
if (light->use_mis && radius > 0.0f)
|
||||
shader_id |= SHADER_USE_MIS;
|
||||
|
||||
klights[light_index].co = co;
|
||||
klights[light_index].spot.radius = radius;
|
||||
klights[light_index].spot.invarea = invarea;
|
||||
klights[light_index].spot.eval_fac = eval_fac;
|
||||
}
|
||||
else if (light->light_type == LIGHT_DISTANT) {
|
||||
shader_id &= ~SHADER_AREA_LIGHT;
|
||||
|
@ -1306,9 +1306,7 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||
klights[light_index].area.tan_half_spread = tan_half_spread;
|
||||
klights[light_index].area.normalize_spread = normalize_spread;
|
||||
}
|
||||
else if (light->light_type == LIGHT_SPOT) {
|
||||
shader_id &= ~SHADER_AREA_LIGHT;
|
||||
|
||||
if (light->light_type == LIGHT_SPOT) {
|
||||
/* Scale axes to accommodate non-uniform scaling. */
|
||||
float3 scaled_axis_u = light->axisu / len_squared(light->axisu);
|
||||
float3 scaled_axis_v = light->axisv / len_squared(light->axisv);
|
||||
|
@ -1316,22 +1314,14 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||
/* Keep direction normalized. */
|
||||
float3 dir = safe_normalize_len(light->dir, &len_z);
|
||||
|
||||
float radius = light->size;
|
||||
float invarea = (light->normalize && radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) :
|
||||
1.0f;
|
||||
float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
|
||||
float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth);
|
||||
|
||||
if (light->use_mis && radius > 0.0f)
|
||||
shader_id |= SHADER_USE_MIS;
|
||||
|
||||
klights[light_index].co = co;
|
||||
klights[light_index].spot.scaled_axis_u = scaled_axis_u;
|
||||
klights[light_index].spot.radius = radius;
|
||||
klights[light_index].spot.scaled_axis_v = scaled_axis_v;
|
||||
klights[light_index].spot.invarea = invarea;
|
||||
klights[light_index].spot.dir = dir;
|
||||
klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle;
|
||||
klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tanf(light->spot_angle * 0.5f);
|
||||
klights[light_index].spot.inv_len_z = 1.0f / len_z;
|
||||
klights[light_index].spot.spot_smooth = spot_smooth;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ class Light : public Node {
|
|||
NODE_SOCKET_API(uint64_t, light_set_membership);
|
||||
NODE_SOCKET_API(uint64_t, shadow_set_membership);
|
||||
|
||||
/* Normalize power by the surface area of the light. */
|
||||
NODE_SOCKET_API(bool, normalize)
|
||||
|
||||
void tag_update(Scene *scene);
|
||||
|
|
|
@ -213,6 +213,37 @@ static void versioning_remove_microfacet_sharp_distribution(bNodeTree *ntree)
|
|||
}
|
||||
}
|
||||
|
||||
static void version_replace_texcoord_normal_socket(bNodeTree *ntree)
|
||||
{
|
||||
/* The normal of a spot light was set to the incoming light direction, replace with the
|
||||
* `Incoming` socket from the Geometry shader node. */
|
||||
bNode *geometry_node = nullptr;
|
||||
bNode *transform_node = nullptr;
|
||||
bNodeSocket *incoming_socket = nullptr;
|
||||
bNodeSocket *vec_in_socket = nullptr;
|
||||
bNodeSocket *vec_out_socket = nullptr;
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
||||
if (link->fromnode->type == SH_NODE_TEX_COORD && STREQ(link->fromsock->identifier, "Normal")) {
|
||||
if (geometry_node == nullptr) {
|
||||
geometry_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_NEW_GEOMETRY);
|
||||
incoming_socket = nodeFindSocket(geometry_node, SOCK_OUT, "Incoming");
|
||||
|
||||
transform_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_VECT_TRANSFORM);
|
||||
vec_in_socket = nodeFindSocket(transform_node, SOCK_IN, "Vector");
|
||||
vec_out_socket = nodeFindSocket(transform_node, SOCK_OUT, "Vector");
|
||||
|
||||
NodeShaderVectTransform *nodeprop = (NodeShaderVectTransform *)transform_node->storage;
|
||||
nodeprop->type = SHD_VECT_TRANSFORM_TYPE_NORMAL;
|
||||
|
||||
nodeAddLink(ntree, geometry_node, incoming_socket, transform_node, vec_in_socket);
|
||||
}
|
||||
nodeAddLink(ntree, transform_node, vec_out_socket, link->tonode, link->tosock);
|
||||
nodeRemLink(ntree, link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
{
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 400, 1)) {
|
||||
|
@ -277,6 +308,15 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
act->frame_end = min_ff(act->frame_end, MAXFRAMEF);
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 400, 9)) {
|
||||
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
|
||||
if (light->type == LA_SPOT && light->nodetree) {
|
||||
version_replace_texcoord_normal_socket(light->nodetree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue