Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/cycles/blender/blender_shader.cpp13
-rw-r--r--intern/cycles/blender/blender_util.h15
-rw-r--r--intern/cycles/graph/node_type.h13
-rw-r--r--intern/cycles/kernel/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/kernel_light.h4
-rw-r--r--intern/cycles/kernel/kernel_textures.h3
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp8
-rw-r--r--intern/cycles/kernel/shaders/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/shaders/node_ies_light.osl42
-rw-r--r--intern/cycles/kernel/svm/svm.h4
-rw-r--r--intern/cycles/kernel/svm/svm_ies.h110
-rw-r--r--intern/cycles/kernel/svm/svm_types.h1
-rw-r--r--intern/cycles/render/graph.cpp6
-rw-r--r--intern/cycles/render/light.cpp122
-rw-r--r--intern/cycles/render/light.h17
-rw-r--r--intern/cycles/render/nodes.cpp100
-rw-r--r--intern/cycles/render/nodes.h22
-rw-r--r--intern/cycles/render/osl.cpp9
-rw-r--r--intern/cycles/render/osl.h5
-rw-r--r--intern/cycles/render/scene.cpp3
-rw-r--r--intern/cycles/render/scene.h3
-rw-r--r--intern/cycles/render/svm.cpp7
-rw-r--r--intern/cycles/render/svm.h5
-rw-r--r--intern/cycles/util/CMakeLists.txt2
-rw-r--r--intern/cycles/util/util_ies.cpp392
-rw-r--r--intern/cycles/util/util_ies.h61
-rw-r--r--intern/cycles/util/util_math.h11
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/intern/bpath.c8
-rw-r--r--source/blender/blenkernel/intern/node.c1
-rw-r--r--source/blender/editors/space_node/drawnode.c18
-rw-r--r--source/blender/makesdna/DNA_node_types.h9
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c56
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_shader.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_ies_light.c61
39 files changed, 1119 insertions, 20 deletions
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index eb9968a85c2..c6144cef1bf 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -818,6 +818,19 @@ static ShaderNode *add_node(Scene *scene,
get_tex_mapping(&sky->tex_mapping, b_texture_mapping);
node = sky;
}
+ else if(b_node.is_a(&RNA_ShaderNodeTexIES)) {
+ BL::ShaderNodeTexIES b_ies_node(b_node);
+ IESLightNode *ies = new IESLightNode();
+ switch(b_ies_node.mode()) {
+ case BL::ShaderNodeTexIES::mode_EXTERNAL:
+ ies->filename = blender_absolute_path(b_data, b_ntree, b_ies_node.filepath());
+ break;
+ case BL::ShaderNodeTexIES::mode_INTERNAL:
+ ies->ies = get_text_datablock_content(b_ies_node.ies().ptr);
+ break;
+ }
+ node = ies;
+ }
else if(b_node.is_a(&RNA_ShaderNodeNormalMap)) {
BL::ShaderNodeNormalMap b_normal_map_node(b_node);
NormalMapNode *nmap = new NormalMapNode();
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index c418b19a637..87d6c7eba8a 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -468,6 +468,21 @@ static inline string blender_absolute_path(BL::BlendData& b_data,
return path;
}
+static inline string get_text_datablock_content(const PointerRNA&& ptr)
+{
+ if(ptr.data == NULL) {
+ return "";
+ }
+
+ string content;
+ BL::Text::lines_iterator iter;
+ for(iter.begin(ptr); iter; ++iter) {
+ content += iter->body() + "\n";
+ }
+
+ return content;
+}
+
/* Texture Space */
static inline void mesh_texture_space(BL::Mesh& b_mesh,
diff --git a/intern/cycles/graph/node_type.h b/intern/cycles/graph/node_type.h
index 7d46e31ce24..15d34a79bb8 100644
--- a/intern/cycles/graph/node_type.h
+++ b/intern/cycles/graph/node_type.h
@@ -73,12 +73,13 @@ struct SocketType
INTERNAL = (1 << 2) | (1 << 3),
LINK_TEXTURE_GENERATED = (1 << 4),
- LINK_TEXTURE_UV = (1 << 5),
- LINK_INCOMING = (1 << 6),
- LINK_NORMAL = (1 << 7),
- LINK_POSITION = (1 << 8),
- LINK_TANGENT = (1 << 9),
- DEFAULT_LINK_MASK = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9)
+ LINK_TEXTURE_NORMAL = (1 << 5),
+ LINK_TEXTURE_UV = (1 << 6),
+ LINK_INCOMING = (1 << 7),
+ LINK_NORMAL = (1 << 8),
+ LINK_POSITION = (1 << 9),
+ LINK_TANGENT = (1 << 10),
+ DEFAULT_LINK_MASK = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)
};
ustring name;
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 9b7f4e00084..457602ae4e7 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -178,6 +178,7 @@ set(SRC_SVM_HEADERS
svm/svm_geometry.h
svm/svm_gradient.h
svm/svm_hsv.h
+ svm/svm_ies.h
svm/svm_image.h
svm/svm_invert.h
svm/svm_light_path.h
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index efab69ee37d..ec7203d36eb 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -170,7 +170,7 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float
float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res);
/* importance-sampled V direction */
- float dv = (randv - cdf_v.y) / (cdf_next_v.y - cdf_v.y);
+ float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv);
float v = (index_v + dv) / res;
/* this is basically std::lower_bound as used by pbrt */
@@ -196,7 +196,7 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float
float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + res);
/* importance-sampled U direction */
- float du = (randu - cdf_u.y) / (cdf_next_u.y - cdf_u.y);
+ float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu);
float u = (index_u + du) / res;
/* compute pdf */
diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h
index 9047b93a0b2..a7b8c492ee9 100644
--- a/intern/cycles/kernel/kernel_textures.h
+++ b/intern/cycles/kernel/kernel_textures.h
@@ -81,5 +81,8 @@ KERNEL_TEX(uint, __sobol_directions)
/* image textures */
KERNEL_TEX(TextureInfo, __texture_info)
+/* ies lights */
+KERNEL_TEX(float, __ies)
+
#undef KERNEL_TEX
diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp
index 0c5e5e30e47..32d86b7192a 100644
--- a/intern/cycles/kernel/osl/osl_services.cpp
+++ b/intern/cycles/kernel/osl/osl_services.cpp
@@ -956,9 +956,15 @@ bool OSLRenderServices::texture(ustring filename,
status = true;
}
}
+ else if(filename[1] == 'l') {
+ /* IES light. */
+ int slot = atoi(filename.c_str() + 2);
+ result[0] = kernel_ies_interp(kg, slot, s, t);
+ status = true;
+ }
else {
/* Packed texture. */
- int slot = atoi(filename.c_str() + 1);
+ int slot = atoi(filename.c_str() + 2);
float4 rgba = kernel_tex_image_interp(kg, slot, s, 1.0f - t);
result[0] = rgba[0];
diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt
index 6ec651a96d8..b28d017c1c2 100644
--- a/intern/cycles/kernel/shaders/CMakeLists.txt
+++ b/intern/cycles/kernel/shaders/CMakeLists.txt
@@ -39,6 +39,7 @@ set(SRC_OSL
node_principled_volume.osl
node_holdout.osl
node_hsv.osl
+ node_ies_light.osl
node_image_texture.osl
node_invert.osl
node_layer_weight.osl
diff --git a/intern/cycles/kernel/shaders/node_ies_light.osl b/intern/cycles/kernel/shaders/node_ies_light.osl
new file mode 100644
index 00000000000..a0954e3a444
--- /dev/null
+++ b/intern/cycles/kernel/shaders/node_ies_light.osl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011-2015 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 "stdosl.h"
+#include "node_texture.h"
+
+/* IES Light */
+
+shader node_ies_light(
+ int use_mapping = 0,
+ matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ int slot = 0,
+ float Strength = 1.0,
+ point Vector = I,
+ output float Fac = 0.0)
+{
+ point p = Vector;
+
+ if (use_mapping) {
+ p = transform(mapping, p);
+ }
+
+ p = normalize(p);
+
+ float v_angle = acos(-p[2]);
+ float h_angle = atan2(p[0], p[1]) + M_PI;
+
+ Fac = Strength * texture(format("@l%d", slot), h_angle, v_angle);
+}
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index 39cd5da7b12..bfa146f2d93 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -157,6 +157,7 @@ CCL_NAMESPACE_END
#include "kernel/svm/svm_camera.h"
#include "kernel/svm/svm_geometry.h"
#include "kernel/svm/svm_hsv.h"
+#include "kernel/svm/svm_ies.h"
#include "kernel/svm/svm_image.h"
#include "kernel/svm/svm_gamma.h"
#include "kernel/svm/svm_brightness.h"
@@ -421,6 +422,9 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a
case NODE_LIGHT_FALLOFF:
svm_node_light_falloff(sd, stack, node);
break;
+ case NODE_IES:
+ svm_node_ies(kg, sd, stack, node, &offset);
+ break;
# endif /* __EXTRA_NODES__ */
#endif /* NODES_GROUP(NODE_GROUP_LEVEL_2) */
diff --git a/intern/cycles/kernel/svm/svm_ies.h b/intern/cycles/kernel/svm/svm_ies.h
new file mode 100644
index 00000000000..6130c3348b0
--- /dev/null
+++ b/intern/cycles/kernel/svm/svm_ies.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+/* IES Light */
+
+ccl_device_inline float interpolate_ies_vertical(KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h)
+{
+ /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end of v
+ * (corresponding to the north pole) would result in artifacts.
+ * The proper way of dealing with this would be to lookup the corresponding value on the other side of the pole,
+ * but since the horizontal coordinates might be nonuniform, this would require yet another interpolation.
+ * Therefore, the assumtion is made that the light is going to be symmetrical, which means that we can just take
+ * the corresponding value at the current horizontal coordinate. */
+
+#define IES_LOOKUP(v) kernel_tex_fetch(__ies, ofs+h*v_num+(v))
+ /* If v is zero, assume symmetry and read at v=1 instead of v=-1. */
+ float a = IES_LOOKUP((v == 0)? 1 : v-1);
+ float b = IES_LOOKUP(v);
+ float c = IES_LOOKUP(v+1);
+ float d = IES_LOOKUP(min(v+2, v_num-1));
+#undef IES_LOOKUP
+
+ return cubic_interp(a, b, c, d, v_frac);
+}
+
+ccl_device_inline float kernel_ies_interp(KernelGlobals *kg, int slot, float h_angle, float v_angle)
+{
+ /* Find offset of the IES data in the table. */
+ int ofs = __float_as_int(kernel_tex_fetch(__ies, slot));
+ if(ofs == -1) {
+ return 100.0f;
+ }
+
+ int h_num = __float_as_int(kernel_tex_fetch(__ies, ofs++));
+ int v_num = __float_as_int(kernel_tex_fetch(__ies, ofs++));
+
+#define IES_LOOKUP_ANGLE_H(h) kernel_tex_fetch(__ies, ofs+(h))
+#define IES_LOOKUP_ANGLE_V(v) kernel_tex_fetch(__ies, ofs+h_num+(v))
+
+ /* Check whether the angle is within the bounds of the IES texture. */
+ if(v_angle >= IES_LOOKUP_ANGLE_V(v_num-1)) {
+ return 0.0f;
+ }
+ kernel_assert(v_angle >= IES_LOOKUP_ANGLE_V(0));
+ kernel_assert(h_angle >= IES_LOOKUP_ANGLE_H(0));
+ kernel_assert(h_angle <= IES_LOOKUP_ANGLE_H(h_num-1));
+
+ /* Lookup the angles to find the table position. */
+ int h_i, v_i;
+ /* TODO(lukas): Consider using bisection. Probably not worth it for the vast majority of IES files. */
+ for(h_i = 0; IES_LOOKUP_ANGLE_H(h_i+1) < h_angle; h_i++);
+ for(v_i = 0; IES_LOOKUP_ANGLE_V(v_i+1) < v_angle; v_i++);
+
+ float h_frac = inverse_lerp(IES_LOOKUP_ANGLE_H(h_i), IES_LOOKUP_ANGLE_H(h_i+1), h_angle);
+ float v_frac = inverse_lerp(IES_LOOKUP_ANGLE_V(v_i), IES_LOOKUP_ANGLE_V(v_i+1), v_angle);
+
+#undef IES_LOOKUP_ANGLE_H
+#undef IES_LOOKUP_ANGLE_V
+
+ /* Skip forward to the actual intensity data. */
+ ofs += h_num+v_num;
+
+ /* Perform cubic interpolation along the horizontal coordinate to get the intensity value.
+ * If h_i is zero, just wrap around since the horizontal angles always go over the full circle.
+ * However, the last entry (360°) equals the first one, so we need to wrap around to the one before that. */
+ float a = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, (h_i == 0)? h_num-2 : h_i-1);
+ float b = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i);
+ float c = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, h_i+1);
+ /* Same logic here, wrap around to the second element if necessary. */
+ float d = interpolate_ies_vertical(kg, ofs, v_i, v_num, v_frac, (h_i+2 == h_num)? 1 : h_i+2);
+
+ /* Cubic interpolation can result in negative values, so get rid of them. */
+ return max(cubic_interp(a, b, c, d, h_frac), 0.0f);
+}
+
+ccl_device void svm_node_ies(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
+{
+ uint vector_offset, strength_offset, fac_offset, dummy, slot = node.z;
+ decode_node_uchar4(node.y, &strength_offset, &vector_offset, &fac_offset, &dummy);
+
+ float3 vector = stack_load_float3(stack, vector_offset);
+ float strength = stack_load_float_default(stack, strength_offset, node.w);
+
+ vector = normalize(vector);
+ float v_angle = safe_acosf(-vector.z);
+ float h_angle = atan2f(vector.x, vector.y) + M_PI_F;
+
+ float fac = strength * kernel_ies_interp(kg, slot, h_angle, v_angle);
+
+ if(stack_valid(fac_offset)) {
+ stack_store_float(stack, fac_offset, fac);
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index dc62e25b340..ac24d23ecd2 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -136,6 +136,7 @@ typedef enum ShaderNodeType {
NODE_DISPLACEMENT,
NODE_VECTOR_DISPLACEMENT,
NODE_PRINCIPLED_VOLUME,
+ NODE_IES,
} ShaderNodeType;
typedef enum NodeAttributeType {
diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp
index 096de878e51..59e1a12c3a1 100644
--- a/intern/cycles/render/graph.cpp
+++ b/intern/cycles/render/graph.cpp
@@ -774,6 +774,12 @@ void ShaderGraph::default_inputs(bool do_osl)
connect(texco->output("Generated"), input);
}
+ if(input->flags() & SocketType::LINK_TEXTURE_NORMAL) {
+ if(!texco)
+ texco = new TextureCoordinateNode();
+
+ connect(texco->output("Normal"), input);
+ }
else if(input->flags() & SocketType::LINK_TEXTURE_UV) {
if(!texco)
texco = new TextureCoordinateNode();
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 8dec7e4ea64..f0824ef4319 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -25,6 +25,8 @@
#include "render/shader.h"
#include "util/util_foreach.h"
+#include "util/util_hash.h"
+#include "util/util_path.h"
#include "util/util_progress.h"
#include "util/util_logging.h"
@@ -175,6 +177,9 @@ LightManager::LightManager()
LightManager::~LightManager()
{
+ foreach(IESSlot *slot, ies_slots) {
+ delete slot;
+ }
}
bool LightManager::has_background_light(Scene *scene)
@@ -858,6 +863,9 @@ void LightManager::device_update(Device *device, DeviceScene *dscene, Scene *sce
device_update_background(device, dscene, scene, progress);
if(progress.get_cancel()) return;
+ device_update_ies(dscene);
+ if(progress.get_cancel()) return;
+
if(use_light_visibility != scene->film->use_light_visibility) {
scene->film->use_light_visibility = use_light_visibility;
scene->film->tag_update(scene);
@@ -872,6 +880,7 @@ void LightManager::device_free(Device *, DeviceScene *dscene)
dscene->lights.free();
dscene->light_background_marginal_cdf.free();
dscene->light_background_conditional_cdf.free();
+ dscene->ies_lights.free();
}
void LightManager::tag_update(Scene * /*scene*/)
@@ -879,5 +888,118 @@ void LightManager::tag_update(Scene * /*scene*/)
need_update = true;
}
+int LightManager::add_ies_from_file(ustring filename)
+{
+ string content;
+ /* If the file can't be opened, call with an empty string */
+ path_read_text(filename.c_str(), content);
+
+ return add_ies(ustring(content));
+}
+
+int LightManager::add_ies(ustring content)
+{
+ uint hash = hash_string(content.c_str());
+
+ thread_scoped_lock ies_lock(ies_mutex);
+
+ /* Check whether this IES already has a slot. */
+ size_t slot;
+ for(slot = 0; slot < ies_slots.size(); slot++) {
+ if(ies_slots[slot]->hash == hash) {
+ ies_slots[slot]->users++;
+ return slot;
+ }
+ }
+
+ /* Try to find an empty slot for the new IES. */
+ for(slot = 0; slot < ies_slots.size(); slot++) {
+ if(ies_slots[slot]->users == 0 && ies_slots[slot]->hash == 0) {
+ break;
+ }
+ }
+
+ /* If there's no free slot, add one. */
+ if(slot == ies_slots.size()) {
+ ies_slots.push_back(new IESSlot());
+ }
+
+ ies_slots[slot]->ies.load(content);
+ ies_slots[slot]->users = 1;
+ ies_slots[slot]->hash = hash;
+
+ need_update = true;
+
+ return slot;
+}
+
+void LightManager::remove_ies(int slot)
+{
+ thread_scoped_lock ies_lock(ies_mutex);
+
+ if(slot < 0 || slot >= ies_slots.size()) {
+ assert(false);
+ return;
+ }
+
+ assert(ies_slots[slot]->users > 0);
+ ies_slots[slot]->users--;
+
+ /* If the slot has no more users, update the device to remove it. */
+ need_update |= (ies_slots[slot]->users == 0);
+}
+
+void LightManager::device_update_ies(DeviceScene *dscene)
+{
+ /* Clear empty slots. */
+ foreach(IESSlot *slot, ies_slots) {
+ if(slot->users == 0) {
+ slot->hash = 0;
+ slot->ies.clear();
+ }
+ }
+
+ /* Shrink the slot table by removing empty slots at the end. */
+ int slot_end;
+ for(slot_end = ies_slots.size(); slot_end; slot_end--) {
+ if(ies_slots[slot_end-1]->users > 0) {
+ /* If the preceding slot has users, we found the new end of the table. */
+ break;
+ }
+ else {
+ /* The slot will be past the new end of the table, so free it. */
+ delete ies_slots[slot_end-1];
+ }
+ }
+ ies_slots.resize(slot_end);
+
+ if(ies_slots.size() > 0) {
+ int packed_size = 0;
+ foreach(IESSlot *slot, ies_slots) {
+ packed_size += slot->ies.packed_size();
+ }
+
+ /* ies_lights starts with an offset table that contains the offset of every slot,
+ * or -1 if the slot is invalid.
+ * Following that table, the packed valid IES lights are stored. */
+ float *data = dscene->ies_lights.alloc(ies_slots.size() + packed_size);
+
+ int offset = ies_slots.size();
+ for(int i = 0; i < ies_slots.size(); i++) {
+ int size = ies_slots[i]->ies.packed_size();
+ if(size > 0) {
+ data[i] = __int_as_float(offset);
+ ies_slots[i]->ies.pack(data + offset);
+ offset += size;
+ }
+ else {
+ data[i] = __int_as_float(-1);
+ }
+ }
+
+ dscene->ies_lights.copy_to_device();
+ }
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h
index 97b7b971c73..5f8677ee2f2 100644
--- a/intern/cycles/render/light.h
+++ b/intern/cycles/render/light.h
@@ -21,6 +21,8 @@
#include "graph/node.h"
+#include "util/util_ies.h"
+#include "util/util_thread.h"
#include "util/util_types.h"
#include "util/util_vector.h"
@@ -86,6 +88,11 @@ public:
LightManager();
~LightManager();
+ /* IES texture management */
+ int add_ies(ustring ies);
+ int add_ies_from_file(ustring filename);
+ void remove_ies(int slot);
+
void device_update(Device *device,
DeviceScene *dscene,
Scene *scene,
@@ -115,9 +122,19 @@ protected:
DeviceScene *dscene,
Scene *scene,
Progress& progress);
+ void device_update_ies(DeviceScene *dscene);
/* Check whether light manager can use the object as a light-emissive. */
bool object_usable_as_light(Object *object);
+
+ struct IESSlot {
+ IESFile ies;
+ uint hash;
+ int users;
+ };
+
+ vector<IESSlot*> ies_slots;
+ thread_mutex ies_mutex;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index c468924fa66..3dad4d1a346 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -16,6 +16,7 @@
#include "render/image.h"
#include "render/integrator.h"
+#include "render/light.h"
#include "render/nodes.h"
#include "render/scene.h"
#include "render/svm.h"
@@ -384,10 +385,10 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
/* TODO(sergey): It's not so simple to pass custom attribute
* to the texture() function in order to make builtin images
* support more clear. So we use special file name which is
- * "@<slot_number>" and check whether file name matches this
+ * "@i<slot_number>" and check whether file name matches this
* mask in the OSLRenderServices::texture().
*/
- compiler.parameter("filename", string_printf("@%d", slot).c_str());
+ compiler.parameter("filename", string_printf("@i%d", slot).c_str());
}
if(is_linear || color_space != NODE_COLOR_SPACE_COLOR)
compiler.parameter("color_space", "linear");
@@ -567,7 +568,7 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler)
compiler.parameter(this, "filename");
}
else {
- compiler.parameter("filename", string_printf("@%d", slot).c_str());
+ compiler.parameter("filename", string_printf("@i%d", slot).c_str());
}
compiler.parameter(this, "projection");
if(is_linear || color_space != NODE_COLOR_SPACE_COLOR)
@@ -954,6 +955,97 @@ void VoronoiTextureNode::compile(OSLCompiler& compiler)
compiler.add(this, "node_voronoi_texture");
}
+/* IES Light */
+
+NODE_DEFINE(IESLightNode)
+{
+ NodeType* type = NodeType::add("ies_light", create, NodeType::SHADER);
+
+ TEXTURE_MAPPING_DEFINE(IESLightNode);
+
+ SOCKET_STRING(ies, "IES", ustring());
+ SOCKET_STRING(filename, "File Name", ustring());
+
+ SOCKET_IN_FLOAT(strength, "Strength", 1.0f);
+ SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_NORMAL);
+
+ SOCKET_OUT_FLOAT(fac, "Fac");
+
+ return type;
+}
+
+IESLightNode::IESLightNode()
+: TextureNode(node_type)
+{
+ light_manager = NULL;
+ slot = -1;
+}
+
+ShaderNode *IESLightNode::clone() const
+{
+ IESLightNode *node = new IESLightNode(*this);
+
+ node->light_manager = NULL;
+ node->slot = -1;
+
+ return node;
+}
+
+IESLightNode::~IESLightNode()
+{
+ if(light_manager) {
+ light_manager->remove_ies(slot);
+ }
+}
+
+void IESLightNode::get_slot()
+{
+ assert(light_manager);
+
+ if(slot == -1) {
+ if(ies.empty()) {
+ slot = light_manager->add_ies_from_file(filename);
+ }
+ else {
+ slot = light_manager->add_ies(ies);
+ }
+ }
+}
+
+void IESLightNode::compile(SVMCompiler& compiler)
+{
+ light_manager = compiler.light_manager;
+ get_slot();
+
+ ShaderInput *strength_in = input("Strength");
+ ShaderInput *vector_in = input("Vector");
+ ShaderOutput *fac_out = output("Fac");
+
+ int vector_offset = tex_mapping.compile_begin(compiler, vector_in);
+
+ compiler.add_node(NODE_IES,
+ compiler.encode_uchar4(
+ compiler.stack_assign_if_linked(strength_in),
+ vector_offset,
+ compiler.stack_assign(fac_out),
+ 0),
+ slot,
+ __float_as_int(strength));
+
+ tex_mapping.compile_end(compiler, vector_in, vector_offset);
+}
+
+void IESLightNode::compile(OSLCompiler& compiler)
+{
+ light_manager = compiler.light_manager;
+ get_slot();
+
+ tex_mapping.compile(compiler);
+
+ compiler.parameter("slot", slot);
+ compiler.add(this, "node_ies_light");
+}
+
/* Musgrave Texture */
NODE_DEFINE(MusgraveTextureNode)
@@ -1470,7 +1562,7 @@ void PointDensityTextureNode::compile(OSLCompiler& compiler)
}
if(slot != -1) {
- compiler.parameter("filename", string_printf("@%d", slot).c_str());
+ compiler.parameter("filename", string_printf("@i%d", slot).c_str());
}
if(space == NODE_TEX_VOXEL_SPACE_WORLD) {
compiler.parameter("mapping", tfm);
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index 58c3d472cd3..35a7df690c3 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -25,6 +25,7 @@
CCL_NAMESPACE_BEGIN
class ImageManager;
+class LightManager;
class Scene;
class Shader;
@@ -281,6 +282,27 @@ public:
}
};
+class IESLightNode : public TextureNode {
+public:
+ SHADER_NODE_NO_CLONE_CLASS(IESLightNode)
+
+ ~IESLightNode();
+ ShaderNode *clone() const;
+ virtual int get_group() { return NODE_GROUP_LEVEL_2; }
+
+ ustring filename;
+ ustring ies;
+
+ float strength;
+ float3 vector;
+
+private:
+ LightManager *light_manager;
+ int slot;
+
+ void get_slot();
+};
+
class MappingNode : public ShaderNode {
public:
SHADER_NODE_CLASS(MappingNode)
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index f1a22350060..dde622bff8a 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -99,7 +99,9 @@ void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
* compile shaders alternating */
thread_scoped_lock lock(ss_mutex);
- OSLCompiler compiler((void*)this, (void*)ss, scene->image_manager);
+ OSLCompiler compiler((void*)this, (void*)ss,
+ scene->image_manager,
+ scene->light_manager);
compiler.background = (shader == scene->default_background);
compiler.compile(scene, og, shader);
@@ -546,11 +548,14 @@ OSLNode *OSLShaderManager::osl_node(const std::string& filepath,
/* Graph Compiler */
-OSLCompiler::OSLCompiler(void *manager_, void *shadingsys_, ImageManager *image_manager_)
+OSLCompiler::OSLCompiler(void *manager_, void *shadingsys_,
+ ImageManager *image_manager_,
+ LightManager *light_manager_)
{
manager = manager_;
shadingsys = shadingsys_;
image_manager = image_manager_;
+ light_manager = light_manager_;
current_type = SHADER_TYPE_SURFACE;
current_shader = NULL;
background = false;
diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h
index 95e35dd857b..7a3208c402a 100644
--- a/intern/cycles/render/osl.h
+++ b/intern/cycles/render/osl.h
@@ -120,7 +120,9 @@ protected:
class OSLCompiler {
public:
- OSLCompiler(void *manager, void *shadingsys, ImageManager *image_manager);
+ OSLCompiler(void *manager, void *shadingsys,
+ ImageManager *image_manager,
+ LightManager *light_manager);
void compile(Scene *scene, OSLGlobals *og, Shader *shader);
void add(ShaderNode *node, const char *name, bool isfilepath = false);
@@ -146,6 +148,7 @@ public:
bool background;
ImageManager *image_manager;
+ LightManager *light_manager;
private:
#ifdef WITH_OSL
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index ba47e3ab6f8..b35cdbd3db5 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -76,7 +76,8 @@ DeviceScene::DeviceScene(Device *device)
svm_nodes(device, "__svm_nodes", MEM_TEXTURE),
shaders(device, "__shaders", MEM_TEXTURE),
lookup_table(device, "__lookup_table", MEM_TEXTURE),
- sobol_directions(device, "__sobol_directions", MEM_TEXTURE)
+ sobol_directions(device, "__sobol_directions", MEM_TEXTURE),
+ ies_lights(device, "__ies", MEM_TEXTURE)
{
memset(&data, 0, sizeof(data));
}
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 04bd4735a86..6c67433c9fc 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -119,6 +119,9 @@ public:
/* integrator */
device_vector<uint> sobol_directions;
+ /* ies lights */
+ device_vector<float> ies_lights;
+
KernelData data;
DeviceScene(Device *device);
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index c5b4060d5c3..eb8a35a271f 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -58,7 +58,7 @@ void SVMShaderManager::device_update_shader(Scene *scene,
svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0));
SVMCompiler::Summary summary;
- SVMCompiler compiler(scene->shader_manager, scene->image_manager);
+ SVMCompiler compiler(scene->shader_manager, scene->image_manager, scene->light_manager);
compiler.background = (shader == scene->default_background);
compiler.compile(scene, shader, svm_nodes, 0, &summary);
@@ -154,10 +154,13 @@ void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s
/* Graph Compiler */
-SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_)
+SVMCompiler::SVMCompiler(ShaderManager *shader_manager_,
+ ImageManager *image_manager_,
+ LightManager *light_manager_)
{
shader_manager = shader_manager_;
image_manager = image_manager_;
+ light_manager = light_manager_;
max_stack_use = 0;
current_type = SHADER_TYPE_SURFACE;
current_shader = NULL;
diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h
index 18be0fa9a22..7cf1e4ad791 100644
--- a/intern/cycles/render/svm.h
+++ b/intern/cycles/render/svm.h
@@ -95,7 +95,9 @@ public:
string full_report() const;
};
- SVMCompiler(ShaderManager *shader_manager, ImageManager *image_manager);
+ SVMCompiler(ShaderManager *shader_manager,
+ ImageManager *image_manager,
+ LightManager *light_manager);
void compile(Scene *scene,
Shader *shader,
array<int4>& svm_nodes,
@@ -125,6 +127,7 @@ public:
ImageManager *image_manager;
ShaderManager *shader_manager;
+ LightManager *light_manager;
bool background;
protected:
diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt
index 24043e2231b..3b690860d53 100644
--- a/intern/cycles/util/CMakeLists.txt
+++ b/intern/cycles/util/CMakeLists.txt
@@ -11,6 +11,7 @@ set(INC_SYS
set(SRC
util_aligned_malloc.cpp
util_debug.cpp
+ util_ies.cpp
util_logging.cpp
util_math_cdf.cpp
util_md5.cpp
@@ -45,6 +46,7 @@ set(SRC_HEADERS
util_guarded_allocator.h
util_half.h
util_hash.h
+ util_ies.h
util_image.h
util_image_impl.h
util_list.h
diff --git a/intern/cycles/util/util_ies.cpp b/intern/cycles/util/util_ies.cpp
new file mode 100644
index 00000000000..4824c886609
--- /dev/null
+++ b/intern/cycles/util/util_ies.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2011-2018 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 "util/util_foreach.h"
+#include "util/util_ies.h"
+#include "util/util_math.h"
+#include "util/util_string.h"
+
+CCL_NAMESPACE_BEGIN
+
+bool IESFile::load(ustring ies)
+{
+ clear();
+ if(!parse(ies) || !process()) {
+ clear();
+ return false;
+ }
+ return true;
+}
+
+void IESFile::clear()
+{
+ intensity.clear();
+ v_angles.clear();
+ h_angles.clear();
+}
+
+int IESFile::packed_size()
+{
+ if(v_angles.size() && h_angles.size() > 0) {
+ return 2 + h_angles.size() + v_angles.size() + h_angles.size()*v_angles.size();
+ }
+ return 0;
+}
+
+void IESFile::pack(float *data)
+{
+ if(v_angles.size() && h_angles.size()) {
+ *(data++) = __int_as_float(h_angles.size());
+ *(data++) = __int_as_float(v_angles.size());
+
+ memcpy(data, &h_angles[0], h_angles.size()*sizeof(float));
+ data += h_angles.size();
+ memcpy(data, &v_angles[0], v_angles.size()*sizeof(float));
+ data += v_angles.size();
+
+ for(int h = 0; h < intensity.size(); h++) {
+ memcpy(data, &intensity[h][0], v_angles.size()*sizeof(float));
+ data += v_angles.size();
+ }
+ }
+}
+
+class IESTextParser {
+public:
+ vector<char> text;
+ char *data;
+
+ IESTextParser(ustring str)
+ : text(str.begin(), str.end())
+ {
+ std::replace(text.begin(), text.end(), ',', ' ');
+ data = strstr(&text[0], "\nTILT=");
+ }
+
+ bool eof() {
+ return (data == NULL) || (data[0] == '\0');
+ }
+
+ double get_double() {
+ if(eof()) {
+ return 0.0;
+ }
+ char *old_data = data;
+ double val = strtod(data, &data);
+ if(data == old_data) {
+ data = NULL;
+ return 0.0;
+ }
+ return val;
+ }
+
+ long get_long() {
+ if(eof()) {
+ return 0;
+ }
+ char *old_data = data;
+ long val = strtol(data, &data, 10);
+ if(data == old_data) {
+ data = NULL;
+ return 0;
+ }
+ return val;
+ }
+};
+
+bool IESFile::parse(ustring ies)
+{
+ IESTextParser parser(ies);
+ if(parser.eof()) {
+ return false;
+ }
+
+ /* Handle the tilt data block. */
+ if(strncmp(parser.data, "\nTILT=INCLUDE", 13) == 0) {
+ parser.data += 13;
+ parser.get_double(); /* Lamp to Luminaire geometry */
+ int num_tilt = parser.get_long(); /* Amount of tilt angles and factors */
+ /* Skip over angles and factors. */
+ for(int i = 0; i < 2*num_tilt; i++) {
+ parser.get_double();
+ }
+ }
+ else {
+ /* Skip to next line. */
+ parser.data = strstr(parser.data+1, "\n");
+ }
+
+ if(parser.eof()) {
+ return false;
+ }
+ parser.data++;
+
+ parser.get_long(); /* Number of lamps */
+ parser.get_double(); /* Lumens per lamp */
+ double factor = parser.get_double(); /* Candela multiplier */
+ int v_angles_num = parser.get_long(); /* Number of vertical angles */
+ int h_angles_num = parser.get_long(); /* Number of horizontal angles */
+ type = (IESType) parser.get_long(); /* Photometric type */
+
+ /* TODO(lukas): Test whether the current type B processing can also deal with type A files.
+ * In theory the only difference should be orientation which we ignore anyways, but with IES you never know...
+ */
+ if(type != TYPE_B && type != TYPE_C) {
+ return false;
+ }
+
+ parser.get_long(); /* Unit of the geometry data */
+ parser.get_double(); /* Width */
+ parser.get_double(); /* Length */
+ parser.get_double(); /* Height */
+ factor *= parser.get_double(); /* Ballast factor */
+ factor *= parser.get_double(); /* Ballast-Lamp Photometric factor */
+ parser.get_double(); /* Input Watts */
+
+ /* Intensity values in IES files are specified in candela (lumen/sr), a photometric quantity.
+ * Cycles expects radiometric quantities, though, which requires a conversion.
+ * However, the Luminous efficacy (ratio of lumens per Watt) depends on the spectral distribution
+ * of the light source since lumens take human perception into account.
+ * Since this spectral distribution is not known from the IES file, a typical one must be assumed.
+ * The D65 standard illuminant has a Luminous efficacy of 177.83, which is used here to convert to Watt/sr.
+ * A more advanced approach would be to add a Blackbody Temperature input to the node and numerically
+ * integrate the Luminous efficacy from the resulting spectral distribution.
+ * Also, the Watt/sr value must be multiplied by 4*pi to get the Watt value that Cycles expects
+ * for lamp strength. Therefore, the conversion here uses 4*pi/177.83 as a Candela to Watt factor.
+ */
+ factor *= 0.0706650768394;
+
+ v_angles.reserve(v_angles_num);
+ for(int i = 0; i < v_angles_num; i++) {
+ v_angles.push_back((float) parser.get_double());
+ }
+
+ h_angles.reserve(h_angles_num);
+ for(int i = 0; i < h_angles_num; i++) {
+ h_angles.push_back((float) parser.get_double());
+ }
+
+ intensity.resize(h_angles_num);
+ for(int i = 0; i < h_angles_num; i++) {
+ intensity[i].reserve(v_angles_num);
+ for(int j = 0; j < v_angles_num; j++) {
+ intensity[i].push_back((float) (factor * parser.get_double()));
+ }
+ }
+
+ return !parser.eof();
+}
+
+bool IESFile::process_type_b()
+{
+ vector<vector<float> > newintensity;
+ newintensity.resize(v_angles.size());
+ for(int i = 0; i < v_angles.size(); i++) {
+ newintensity[i].reserve(h_angles.size());
+ for(int j = 0; j < h_angles.size(); j++) {
+ newintensity[i].push_back(intensity[j][i]);
+ }
+ }
+ intensity.swap(newintensity);
+ h_angles.swap(v_angles);
+
+ float h_first = h_angles[0], h_last = h_angles[h_angles.size()-1];
+ if(h_last != 90.0f) {
+ return false;
+ }
+
+ if(h_first == 0.0f) {
+ /* The range in the file corresponds to 90°-180°, we need to mirror that to get the
+ * full 180° range. */
+ vector<float> new_h_angles;
+ vector<vector<float> > new_intensity;
+ int hnum = h_angles.size();
+ new_h_angles.reserve(2*hnum-1);
+ new_intensity.reserve(2*hnum-1);
+ for(int i = hnum-1; i > 0; i--) {
+ new_h_angles.push_back(90.0f - h_angles[i]);
+ new_intensity.push_back(intensity[i]);
+ }
+ for(int i = 0; i < hnum; i++) {
+ new_h_angles.push_back(90.0f + h_angles[i]);
+ new_intensity.push_back(intensity[i]);
+ }
+ h_angles.swap(new_h_angles);
+ intensity.swap(new_intensity);
+ }
+ else if(h_first == -90.0f) {
+ /* We have full 180° coverage, so just shift to match the angle range convention. */
+ for(int i = 0; i < h_angles.size(); i++) {
+ h_angles[i] += 90.0f;
+ }
+ }
+ /* To get correct results with the cubic interpolation in the kernel, the horizontal range
+ * has to cover all 360°. Therefore, we copy the 0° entry to 360° to ensure full coverage
+ * and seamless interpolation. */
+ h_angles.push_back(360.0f);
+ intensity.push_back(intensity[0]);
+
+ float v_first = v_angles[0], v_last = v_angles[v_angles.size()-1];
+ if(v_last != 90.0f) {
+ return false;
+ }
+
+ if(v_first == 0.0f) {
+ /* The range in the file corresponds to 90°-180°, we need to mirror that to get the
+ * full 180° range. */
+ vector<float> new_v_angles;
+ int hnum = h_angles.size();
+ int vnum = v_angles.size();
+ new_v_angles.reserve(2*vnum-1);
+ for(int i = vnum-1; i > 0; i--) {
+ new_v_angles.push_back(90.0f - v_angles[i]);
+ }
+ for(int i = 0; i < vnum; i++) {
+ new_v_angles.push_back(90.0f + v_angles[i]);
+ }
+ for(int i = 0; i < hnum; i++) {
+ vector<float> new_intensity;
+ new_intensity.reserve(2*vnum-1);
+ for(int j = vnum-2; j >= 0; j--) {
+ new_intensity.push_back(intensity[i][j]);
+ }
+ new_intensity.insert(new_intensity.end(), intensity[i].begin(), intensity[i].end());
+ intensity[i].swap(new_intensity);
+ }
+ v_angles.swap(new_v_angles);
+ }
+ else if(v_first == -90.0f) {
+ /* We have full 180° coverage, so just shift to match the angle range convention. */
+ for(int i = 0; i < v_angles.size(); i++) {
+ v_angles[i] += 90.0f;
+ }
+ }
+
+ return true;
+}
+
+bool IESFile::process_type_c()
+{
+ if(h_angles[0] == 90.0f) {
+ /* Some files are stored from 90° to 270°, so we just rotate them to the regular 0°-180° range here. */
+ for(int i = 0; i < v_angles.size(); i++) {
+ h_angles[i] -= 90.0f;
+ }
+ }
+
+ if(h_angles[0] != 0.0f) {
+ return false;
+ }
+
+ if(h_angles.size() == 1) {
+ h_angles.push_back(360.0f);
+ intensity.push_back(intensity[0]);
+ }
+
+ if(h_angles[h_angles.size()-1] == 90.0f) {
+ /* Only one quadrant is defined, so we need to mirror twice (from one to two, then to four).
+ * Since the two->four mirroring step might also be required if we get an input of two quadrants,
+ * we only do the first mirror here and later do the second mirror in either case. */
+ int hnum = h_angles.size();
+ for(int i = hnum-2; i >= 0; i--) {
+ h_angles.push_back(180.0f - h_angles[i]);
+ intensity.push_back(intensity[i]);
+ }
+ }
+
+ if(h_angles[h_angles.size()-1] == 180.0f) {
+ /* Mirror half to the full range. */
+ int hnum = h_angles.size();
+ for(int i = hnum-2; i >= 0; i--) {
+ h_angles.push_back(360.0f - h_angles[i]);
+ intensity.push_back(intensity[i]);
+ }
+ }
+
+ /* Some files skip the 360° entry (contrary to standard) because it's supposed to be identical to the 0° entry.
+ * If the file has a discernible order in its spacing, just fix this. */
+ if(h_angles[h_angles.size()-1] != 360.0f) {
+ int hnum = h_angles.size();
+ float last_step = h_angles[hnum-1]-h_angles[hnum-2];
+ float first_step = h_angles[1]-h_angles[0];
+ float difference = 360.0f - h_angles[hnum-1];
+ if(last_step == difference || first_step == difference) {
+ h_angles.push_back(360.0f);
+ intensity.push_back(intensity[0]);
+ }
+ else {
+ return false;
+ }
+ }
+
+ float v_first = v_angles[0], v_last = v_angles[v_angles.size()-1];
+ if(v_first == 90.0f) {
+ if(v_last == 180.0f) {
+ /* Flip to ensure that vertical angles always start at 0°. */
+ for(int i = 0; i < v_angles.size(); i++) {
+ v_angles[i] = 180.0f - v_angles[i];
+ }
+ }
+ else {
+ return false;
+ }
+ }
+ else if(v_first != 0.0f) {
+ return false;
+ }
+
+ return true;
+}
+
+bool IESFile::process()
+{
+ if(h_angles.size() == 0 || v_angles.size() == 0) {
+ return false;
+ }
+
+ if(type == TYPE_B) {
+ if(!process_type_b()) {
+ return false;
+ }
+ }
+ else {
+ assert(type == TYPE_C);
+ if(!process_type_c()) {
+ return false;
+ }
+ }
+
+ assert(v_angles[0] == 0.0f);
+ assert(h_angles[0] == 0.0f);
+ assert(h_angles[h_angles.size()-1] == 360.0f);
+
+ /* Convert from deg to rad. */
+ for(int i = 0; i < v_angles.size(); i++) {
+ v_angles[i] *= M_PI_F / 180.f;
+ }
+ for(int i = 0; i < h_angles.size(); i++) {
+ h_angles[i] *= M_PI_F / 180.f;
+ }
+
+ return true;
+}
+
+IESFile::~IESFile()
+{
+ clear();
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_ies.h b/intern/cycles/util/util_ies.h
new file mode 100644
index 00000000000..5933cb3962a
--- /dev/null
+++ b/intern/cycles/util/util_ies.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011-2018 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.
+ */
+
+#ifndef __UTIL_IES_H__
+#define __UTIL_IES_H__
+
+#include "util/util_param.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class IESFile {
+public:
+ IESFile() {}
+ ~IESFile();
+
+ int packed_size();
+ void pack(float *data);
+
+ bool load(ustring ies);
+ void clear();
+
+protected:
+ bool parse(ustring ies);
+ bool process();
+ bool process_type_b();
+ bool process_type_c();
+
+ /* The brightness distribution is stored in spherical coordinates.
+ * The horizontal angles correspond to to theta in the regular notation
+ * and always span the full range from 0° to 360°.
+ * The vertical angles correspond to phi and always start at 0°. */
+ vector<float> v_angles, h_angles;
+ /* The actual values are stored here, with every entry storing the values
+ * of one horizontal segment. */
+ vector<vector<float> > intensity;
+
+ /* Types of angle representation in IES files. Currently, only B and C are supported. */
+ enum IESType {
+ TYPE_A = 3,
+ TYPE_B = 2,
+ TYPE_C = 1
+ } type;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __UTIL_IES_H__ */
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index d0e91a2a1c9..fd3199f209f 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -310,6 +310,17 @@ ccl_device_inline float4 float3_to_float4(const float3 a)
return make_float4(a.x, a.y, a.z, 1.0f);
}
+ccl_device_inline float inverse_lerp(float a, float b, float x)
+{
+ return (x - a) / (b - a);
+}
+
+/* Cubic interpolation between b and c, a and d are the previous and next point. */
+ccl_device_inline float cubic_interp(float a, float b, float c, float d, float x)
+{
+ return 0.5f*(((d + 3.0f*(b-c) - a)*x + (2.0f*a - 5.0f*b + 4.0f*c - d))*x + (c - a))*x + b;
+}
+
CCL_NAMESPACE_END
#include "util/util_math_int2.h"
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 7089f691ff2..60a8471ca3b 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -259,6 +259,7 @@ shader_node_categories = [
NodeItem("ShaderNodeTexChecker"),
NodeItem("ShaderNodeTexBrick"),
NodeItem("ShaderNodeTexPointDensity"),
+ NodeItem("ShaderNodeTexIES"),
]),
ShaderNewNodeCategory("SH_NEW_OP_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index efad8e48e3d..bd3b191c96c 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -789,6 +789,7 @@ struct ShadeResult;
#define SH_NODE_UVALONGSTROKE 191
#define SH_NODE_TEX_POINTDENSITY 192
#define SH_NODE_BSDF_PRINCIPLED 193
+#define SH_NODE_TEX_IES 195
#define SH_NODE_BEVEL 197
#define SH_NODE_DISPLACEMENT 198
#define SH_NODE_VECTOR_DISPLACEMENT 199
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index e9a8de4469d..cc4c28e8016 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -560,6 +560,10 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
}
+ else if (node->type == SH_NODE_TEX_IES) {
+ NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
+ rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
+ }
}
}
break;
@@ -576,6 +580,10 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
}
+ else if (node->type == SH_NODE_TEX_IES) {
+ NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
+ rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
+ }
}
}
break;
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 5d4b1ae8170..521874bb14f 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -3640,6 +3640,7 @@ static void registerShaderNodes(void)
register_node_type_sh_tex_checker();
register_node_type_sh_tex_brick();
register_node_type_sh_tex_pointdensity();
+ register_node_type_sh_tex_ies();
}
static void registerTextureNodes(void)
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 5665a5cff2d..ea8d4b36d3b 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -1109,6 +1109,21 @@ static void node_shader_buts_hair(uiLayout *layout, bContext *UNUSED(C), Pointer
uiItemR(layout, ptr, "component", 0, "", ICON_NONE);
}
+static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayout *row;
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ row = uiLayoutRow(layout, true);
+
+ if (RNA_enum_get(ptr, "mode") == NODE_IES_INTERNAL)
+ uiItemR(row, ptr, "ies", 0, "", ICON_NONE);
+ else
+ uiItemR(row, ptr, "filepath", 0, "", ICON_NONE);
+}
+
static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *row;
@@ -1290,6 +1305,9 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_OUTPUT_LINESTYLE:
ntype->draw_buttons = node_buts_output_linestyle;
break;
+ case SH_NODE_TEX_IES:
+ ntype->draw_buttons = node_shader_buts_ies;
+ break;
case SH_NODE_BEVEL:
ntype->draw_buttons = node_shader_buts_bevel;
break;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 8d3ab29a2fb..95101ac1e51 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -893,6 +893,12 @@ typedef struct NodeShaderUVMap {
char uv_map[64];
} NodeShaderUVMap;
+typedef struct NodeShaderTexIES {
+ int mode;
+
+ char filepath[1024]; /* 1024 = FILE_MAX */
+} NodeShaderTexIES;
+
typedef struct NodeSunBeams {
float source[2];
@@ -906,6 +912,9 @@ typedef struct NodeSunBeams {
/* script node flag */
#define NODE_SCRIPT_AUTO_UPDATE 1
+/* ies node mode */
+#define NODE_IES_INTERNAL 0
+#define NODE_IES_EXTERNAL 1
/* frame node flags */
#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index e512aebfa71..73beee51a8b 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -529,6 +529,7 @@ extern StructRNA RNA_ShaderNodeCombineRGB;
extern StructRNA RNA_ShaderNodeExtendedMaterial;
extern StructRNA RNA_ShaderNodeGeometry;
extern StructRNA RNA_ShaderNodeHueSaturation;
+extern StructRNA RNA_ShaderNodeIESLight;
extern StructRNA RNA_ShaderNodeInvert;
extern StructRNA RNA_ShaderNodeLampData;
extern StructRNA RNA_ShaderNodeMapping;
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index f5c464c0758..8dbb133a8ac 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2946,6 +2946,30 @@ static bNodeSocket *rna_NodeOutputFile_slots_new(ID *id, bNode *node, bContext *
return sock;
}
+static void rna_ShaderNodeTexIES_mode_set(PointerRNA *ptr, int value)
+{
+ bNode *node = (bNode *)ptr->data;
+ NodeShaderTexIES *nss = node->storage;
+
+ if (nss->mode != value) {
+ nss->mode = value;
+ nss->filepath[0] = '\0';
+
+ /* replace text datablock by filepath */
+ if (node->id) {
+ Text *text = (Text *)node->id;
+
+ if (value == NODE_IES_EXTERNAL && text->name) {
+ BLI_strncpy(nss->filepath, text->name, sizeof(nss->filepath));
+ BLI_path_rel(nss->filepath, G.main->name);
+ }
+
+ id_us_min(node->id);
+ node->id = NULL;
+ }
+ }
+}
+
static void rna_ShaderNodeScript_mode_set(PointerRNA *ptr, int value)
{
bNode *node = (bNode *)ptr->data;
@@ -3293,6 +3317,12 @@ static const EnumPropertyItem node_script_mode_items[] = {
{0, NULL, 0, NULL, NULL}
};
+static EnumPropertyItem node_ies_mode_items[] = {
+ {NODE_IES_INTERNAL, "INTERNAL", 0, "Internal", "Use internal text datablock"},
+ {NODE_IES_EXTERNAL, "EXTERNAL", 0, "External", "Use external .ies file"},
+ {0, NULL, 0, NULL, NULL}
+};
+
static const EnumPropertyItem node_principled_distribution_items[] = {
{SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""},
{SHD_GLOSSY_MULTI_GGX, "MULTI_GGX", 0, "Multiscatter GGX", ""},
@@ -4471,6 +4501,32 @@ static void def_sh_subsurface(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNodeSubsurface_update");
}
+static void def_sh_tex_ies(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "ies", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "Text");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_ui_text(prop, "IES Text", "Internal IES file");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "NodeShaderTexIES", "storage");
+
+ prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_ui_text(prop, "File Path", "IES light path");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_funcs(prop, NULL, "rna_ShaderNodeTexIES_mode_set", NULL);
+ RNA_def_property_enum_items(prop, node_ies_mode_items);
+ RNA_def_property_ui_text(prop, "Source", "Whether the IES file is loaded from disk or from a Text datablock");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "bNode", NULL);
+}
+
static void def_sh_script(StructRNA *srna)
{
PropertyRNA *prop;
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 5247599bdf7..cc0bef30047 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -177,6 +177,7 @@ set(SRC
shader/nodes/node_shader_fresnel.c
shader/nodes/node_shader_geometry.c
shader/nodes/node_shader_holdout.c
+ shader/nodes/node_shader_ies_light.c
shader/nodes/node_shader_layer_weight.c
shader/nodes/node_shader_light_falloff.c
shader/nodes/node_shader_light_path.c
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index b00307ed7fb..7cb52cda550 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -138,6 +138,7 @@ void register_node_type_sh_tex_musgrave(void);
void register_node_type_sh_tex_noise(void);
void register_node_type_sh_tex_checker(void);
void register_node_type_sh_bump(void);
+void register_node_type_sh_tex_ies(void);
#endif
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index bc90e33ed03..5864b3deb39 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -130,6 +130,7 @@ DefNode( ShaderNode, SH_NODE_COMBXYZ, 0, "CO
DefNode( ShaderNode, SH_NODE_BEVEL, def_sh_bevel, "BEVEL", Bevel, "Bevel", "" )
DefNode( ShaderNode, SH_NODE_DISPLACEMENT, def_sh_displacement, "DISPLACEMENT", Displacement, "Displacement", "" )
DefNode( ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement","" )
+DefNode( ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "" )
DefNode( CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode( CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
diff --git a/source/blender/nodes/shader/nodes/node_shader_ies_light.c b/source/blender/nodes/shader/nodes/node_shader_ies_light.c
new file mode 100644
index 00000000000..8084f445e34
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_ies_light.c
@@ -0,0 +1,61 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2018 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../node_shader_util.h"
+
+/* **************** IES Light ******************** */
+
+static bNodeSocketTemplate sh_node_tex_ies_in[] = {
+ { SOCK_VECTOR, 1, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ { SOCK_FLOAT, 1, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000000.0f, PROP_NONE},
+ { -1, 0, "" }
+};
+
+static bNodeSocketTemplate sh_node_tex_ies_out[] = {
+ { SOCK_FLOAT, 0, N_("Fac")},
+ { -1, 0, "" }
+};
+
+static void node_shader_init_tex_ies(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeShaderTexIES *tex = MEM_callocN(sizeof(NodeShaderTexIES), "NodeShaderIESLight");
+ node->storage = tex;
+}
+
+/* node type definition */
+void register_node_type_sh_tex_ies(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_TEX_IES, "IES Texture", NODE_CLASS_TEXTURE, 0);
+ node_type_compatibility(&ntype, NODE_NEW_SHADING);
+ node_type_socket_templates(&ntype, sh_node_tex_ies_in, sh_node_tex_ies_out);
+ node_type_init(&ntype, node_shader_init_tex_ies);
+ node_type_storage(&ntype, "NodeShaderTexIES", node_free_standard_storage, node_copy_standard_storage);
+
+ nodeRegisterType(&ntype);
+}