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
path: root/source
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2018-06-08 09:10:35 +0300
committerCampbell Barton <ideasman42@gmail.com>2018-06-08 09:10:35 +0300
commit908b6960c01ffb1665af56ff6f03aaa3eac5366a (patch)
treedb609864fbe601073212b4c37541a7965ea96ea5 /source
parentd352a0adc5dadd0bfdc3b1b1ac76d92be689966b (diff)
parenta25c11fd8d602236f36c34c342453149bdc1d909 (diff)
Merge branch 'master' into blender2.8
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenfont/intern/blf.c2
-rw-r--r--source/blender/blenfont/intern/blf_dir.c16
-rw-r--r--source/blender/blenfont/intern/blf_font.c2
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c4
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h2
-rw-r--r--source/blender/collada/AnimationExporter.cpp54
-rw-r--r--source/blender/collada/AnimationExporter.h22
-rw-r--r--source/blender/collada/AnimationImporter.cpp84
-rw-r--r--source/blender/collada/AnimationImporter.h18
-rw-r--r--source/blender/collada/ArmatureExporter.cpp6
-rw-r--r--source/blender/collada/ArmatureImporter.cpp38
-rw-r--r--source/blender/collada/ArmatureImporter.h6
-rw-r--r--source/blender/collada/CameraExporter.cpp4
-rw-r--r--source/blender/collada/ControllerExporter.cpp22
-rw-r--r--source/blender/collada/DocumentExporter.cpp16
-rw-r--r--source/blender/collada/DocumentImporter.cpp88
-rw-r--r--source/blender/collada/DocumentImporter.h6
-rw-r--r--source/blender/collada/EffectExporter.cpp22
-rw-r--r--source/blender/collada/EffectExporter.h10
-rw-r--r--source/blender/collada/ErrorHandler.cpp2
-rw-r--r--source/blender/collada/ExtraHandler.cpp4
-rw-r--r--source/blender/collada/ExtraHandler.h18
-rw-r--r--source/blender/collada/ExtraTags.cpp2
-rw-r--r--source/blender/collada/ExtraTags.h16
-rw-r--r--source/blender/collada/GeometryExporter.cpp92
-rw-r--r--source/blender/collada/GeometryExporter.h8
-rw-r--r--source/blender/collada/ImageExporter.cpp8
-rw-r--r--source/blender/collada/ImageExporter.h2
-rw-r--r--source/blender/collada/InstanceWriter.cpp8
-rw-r--r--source/blender/collada/LightExporter.cpp16
-rw-r--r--source/blender/collada/MaterialExporter.h2
-rw-r--r--source/blender/collada/MeshImporter.cpp82
-rw-r--r--source/blender/collada/MeshImporter.h10
-rw-r--r--source/blender/collada/SceneExporter.cpp12
-rw-r--r--source/blender/collada/SceneExporter.h2
-rw-r--r--source/blender/collada/SkinInfo.cpp8
-rw-r--r--source/blender/collada/SkinInfo.h4
-rw-r--r--source/blender/collada/TransformReader.cpp6
-rw-r--r--source/blender/collada/TransformReader.h2
-rw-r--r--source/blender/collada/TransformWriter.cpp2
-rw-r--r--source/blender/collada/collada_internal.h8
-rw-r--r--source/blender/collada/collada_utils.cpp24
-rw-r--r--source/blender/collada/collada_utils.h8
-rw-r--r--source/blender/datatoc/datatoc_icon.c2
-rw-r--r--source/blender/ikplugin/CMakeLists.txt2
-rw-r--r--source/blender/ikplugin/intern/ikplugin_api.c2
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.cpp2
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c36
-rw-r--r--source/blender/nodes/composite/node_composite_util.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_boxmask.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_brightness.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorSpill.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_common.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_ellipsemask.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_gamma.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.c6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.c20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_inpaint.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_math.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_moviedistortion.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normalize.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_outputFile.c34
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_splitViewer.c2
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_stabilize2d.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_valToRgb.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_viewer.c2
-rw-r--r--source/blender/nodes/intern/node_common.c100
-rw-r--r--source/blender/nodes/intern/node_exec.c62
-rw-r--r--source/blender/nodes/intern/node_exec.h6
-rw-r--r--source/blender/nodes/intern/node_socket.c84
-rw-r--r--source/blender/nodes/intern/node_util.c38
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c52
-rw-r--r--source/blender/nodes/shader/node_shader_util.c20
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_brightness.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bump.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_common.c42
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_fresnel.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_gamma.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_geom.c163
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_hueSatVal.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_invert.c8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mapping.c6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_material.c374
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.c12
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_normal.c10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output.c97
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_world.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_squeeze.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_brick.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_coord.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.c6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_texture.c166
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vectMath.c24
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vectTransform.c8
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c56
-rw-r--r--source/blender/nodes/texture/node_texture_util.c16
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_at.c6
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_bricks.c26
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_checker.c8
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_common.c28
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_compose.c6
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_coord.c4
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_curves.c14
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_decompose.c6
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_distance.c4
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_hueSatVal.c16
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_image.c16
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_invert.c10
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_mixRgb.c10
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_output.c26
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_proc.c38
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_rotate.c20
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_scale.c10
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_texture.c14
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_translate.c12
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_valToNor.c10
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_valToRgb.c10
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_viewer.c6
-rw-r--r--source/blender/physics/intern/BPH_mass_spring.cpp334
-rw-r--r--source/blender/physics/intern/ConstrainedConjugateGradient.h34
-rw-r--r--source/blender/physics/intern/eigen_utils.h42
-rw-r--r--source/blender/physics/intern/hair_volume.cpp264
-rw-r--r--source/blender/physics/intern/implicit.h2
-rw-r--r--source/blender/physics/intern/implicit_blender.c436
-rw-r--r--source/blender/physics/intern/implicit_eigen.cpp326
-rw-r--r--source/blender/render/CMakeLists.txt2
-rw-r--r--source/blender/render/extern/include/RE_pipeline.h20
-rw-r--r--source/blender/render/intern/include/envmap.h54
-rw-r--r--source/blender/render/intern/include/initrender.h2
-rw-r--r--source/blender/render/intern/include/pixelblending.h65
-rw-r--r--source/blender/render/intern/include/pixelshading.h62
-rw-r--r--source/blender/render/intern/include/pointdensity.h51
-rw-r--r--source/blender/render/intern/include/raycounter.h74
-rw-r--r--source/blender/render/intern/include/rayintersection.h136
-rw-r--r--source/blender/render/intern/include/render_types.h34
-rw-r--r--source/blender/render/intern/include/rendercore.h105
-rw-r--r--source/blender/render/intern/include/shading.h105
-rw-r--r--source/blender/render/intern/include/strand.h99
-rw-r--r--source/blender/render/intern/include/sunsky.h81
-rw-r--r--source/blender/render/intern/include/texture_ocean.h35
-rw-r--r--source/blender/render/intern/include/voxeldata.h47
-rw-r--r--source/blender/render/intern/include/zbuf.h2
-rw-r--r--source/blender/render/intern/raytrace/bvh.h407
-rw-r--r--source/blender/render/intern/raytrace/rayobject.cpp534
-rw-r--r--source/blender/render/intern/raytrace/rayobject_hint.h72
-rw-r--r--source/blender/render/intern/raytrace/rayobject_instance.cpp211
-rw-r--r--source/blender/render/intern/raytrace/rayobject_octree.cpp1101
-rw-r--r--source/blender/render/intern/raytrace/rayobject_qbvh.cpp160
-rw-r--r--source/blender/render/intern/raytrace/rayobject_raycounter.cpp91
-rw-r--r--source/blender/render/intern/raytrace/rayobject_rtbuild.cpp531
-rw-r--r--source/blender/render/intern/raytrace/rayobject_rtbuild.h125
-rw-r--r--source/blender/render/intern/raytrace/rayobject_svbvh.cpp192
-rw-r--r--source/blender/render/intern/raytrace/rayobject_vbvh.cpp206
-rw-r--r--source/blender/render/intern/raytrace/reorganize.h513
-rw-r--r--source/blender/render/intern/raytrace/svbvh.h317
-rw-r--r--source/blender/render/intern/raytrace/vbvh.h238
-rw-r--r--source/blender/render/intern/source/bake.c1342
-rw-r--r--source/blender/render/intern/source/convertblender.c6014
-rw-r--r--source/blender/render/intern/source/envmap.c822
-rw-r--r--source/blender/render/intern/source/external_engine.c8
-rw-r--r--source/blender/render/intern/source/imagetexture.c132
-rw-r--r--source/blender/render/intern/source/initrender.c46
-rw-r--r--source/blender/render/intern/source/occlusion.c1533
-rw-r--r--source/blender/render/intern/source/pipeline.c182
-rw-r--r--source/blender/render/intern/source/pixelblending.c400
-rw-r--r--source/blender/render/intern/source/pixelshading.c650
-rw-r--r--source/blender/render/intern/source/pointdensity.c44
-rw-r--r--source/blender/render/intern/source/rayshade.c2503
-rw-r--r--source/blender/render/intern/source/render_result.c76
-rw-r--r--source/blender/render/intern/source/render_texture.c192
-rw-r--r--source/blender/render/intern/source/rendercore.c2030
-rw-r--r--source/blender/render/intern/source/renderdatabase.c1603
-rw-r--r--source/blender/render/intern/source/shadbuf.c2647
-rw-r--r--source/blender/render/intern/source/shadeinput.c1490
-rw-r--r--source/blender/render/intern/source/shadeoutput.c2182
-rw-r--r--source/blender/render/intern/source/sss.c1074
-rw-r--r--source/blender/render/intern/source/strand.c1069
-rw-r--r--source/blender/render/intern/source/sunsky.c506
-rw-r--r--source/blender/render/intern/source/volume_precache.c855
-rw-r--r--source/blender/render/intern/source/volumetric.c836
-rw-r--r--source/blender/render/intern/source/voxeldata.c70
-rw-r--r--source/blender/render/intern/source/zbuf.c52
191 files changed, 36025 insertions, 2057 deletions
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index fafee125264..5dd692d3855 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -172,7 +172,7 @@ static int blf_search_available(void)
for (i = 0; i < BLF_MAX_FONT; i++)
if (!global_font[i])
return i;
-
+
return -1;
}
diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c
index d3349a10834..ff5c1151a82 100644
--- a/source/blender/blenfont/intern/blf_dir.c
+++ b/source/blender/blenfont/intern/blf_dir.c
@@ -61,7 +61,7 @@ static ListBase global_font_dir = { NULL, NULL };
static DirBLF *blf_dir_find(const char *path)
{
DirBLF *p;
-
+
p = global_font_dir.first;
while (p) {
if (BLI_path_cmp(p->path, path) == 0)
@@ -74,11 +74,11 @@ static DirBLF *blf_dir_find(const char *path)
void BLF_dir_add(const char *path)
{
DirBLF *dir;
-
+
dir = blf_dir_find(path);
if (dir) /* already in the list ? just return. */
return;
-
+
dir = (DirBLF *)MEM_callocN(sizeof(DirBLF), "BLF_dir_add");
dir->path = BLI_strdup(path);
BLI_addhead(&global_font_dir, dir);
@@ -87,7 +87,7 @@ void BLF_dir_add(const char *path)
void BLF_dir_rem(const char *path)
{
DirBLF *dir;
-
+
dir = blf_dir_find(path);
if (dir) {
BLI_remlink(&global_font_dir, dir);
@@ -102,11 +102,11 @@ char **BLF_dir_get(int *ndir)
char **dirs;
char *path;
int i, count;
-
+
count = BLI_listbase_count(&global_font_dir);
if (!count)
return NULL;
-
+
dirs = (char **)MEM_callocN(sizeof(char *) * count, "BLF_dir_get");
p = global_font_dir.first;
i = 0;
@@ -123,7 +123,7 @@ void BLF_dir_free(char **dirs, int count)
{
char *path;
int i;
-
+
for (i = 0; i < count; i++) {
path = dirs[i];
MEM_freeN(path);
@@ -159,7 +159,7 @@ int blf_dir_split(const char *str, char *file, int *size)
{
int i, len;
char *s;
-
+
/* Window, Linux or Mac, this is always / */
s = strrchr(str, '/');
if (s) {
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index c31aacb0a54..1289dc6c5a6 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -553,7 +553,7 @@ static void blf_font_draw_buffer_ex(
width_clip -= chx + width_clip - buf_info->w;
if (height_clip + pen_y > buf_info->h)
height_clip -= pen_y + height_clip - buf_info->h;
-
+
/* drawing below the image? */
if (pen_y < 0) {
yb_start += (g->pitch < 0) ? -pen_y : pen_y;
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index f3789bee1fd..5a87c726566 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -292,11 +292,11 @@ GlyphBLF *blf_glyph_add(FontBLF *font, unsigned int index, unsigned int c)
if (font->flags & BLF_HINTING)
flags &= ~FT_LOAD_NO_HINTING;
-
+
if (is_sharp)
err = FT_Load_Glyph(font->face, (FT_UInt)index, FT_LOAD_TARGET_MONO);
else
- err = FT_Load_Glyph(font->face, (FT_UInt)index, flags);
+ err = FT_Load_Glyph(font->face, (FT_UInt)index, flags);
if (err) {
BLI_spin_unlock(font->ft_lib_mutex);
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 5b9d3f1eb5b..d7b526735d1 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -204,7 +204,7 @@ typedef struct FontBLF {
/* angle in radians. */
float angle;
-
+
#if 0 /* BLF_BLUR_ENABLE */
/* blur: 3 or 5 large kernel */
int blur;
diff --git a/source/blender/collada/AnimationExporter.cpp b/source/blender/collada/AnimationExporter.cpp
index fc4bbea108b..95298986f5a 100644
--- a/source/blender/collada/AnimationExporter.cpp
+++ b/source/blender/collada/AnimationExporter.cpp
@@ -270,7 +270,7 @@ void AnimationExporter::export_sampled_transrotloc_animation(Object *ob, std::ve
create_sampled_animation(3, ctimes, baked_curves[SCALE], ob_name, "scale", "", false);
create_sampled_animation(3, ctimes, baked_curves[LOC], ob_name, "location", "", false);
- /* Not sure how to export rotation as a 3channel animation,
+ /* Not sure how to export rotation as a 3channel animation,
* so separate into 3 single animations for now:
*/
@@ -314,7 +314,7 @@ void AnimationExporter::operator()(Object *ob)
//This needs to be handled by extra profiles, so postponed for now
//export_morph_animation(ob);
-
+
//Export Lamp parameter animations
if ( (ob->type == OB_LAMP) && ((Lamp *)ob->data)->adt && ((Lamp *)ob->data)->adt->action) {
FCurve *fcu = (FCurve *)(((Lamp *)ob->data)->adt->action->curves.first);
@@ -338,7 +338,7 @@ void AnimationExporter::operator()(Object *ob)
if ((STREQ(transformName, "lens")) ||
(STREQ(transformName, "ortho_scale")) ||
- (STREQ(transformName, "clip_end")) ||
+ (STREQ(transformName, "clip_end")) ||
(STREQ(transformName, "clip_start")))
{
create_keyframed_animation(ob, fcu, transformName, true);
@@ -380,7 +380,7 @@ void AnimationExporter::export_object_constraint_animation(Object *ob)
}
void AnimationExporter::export_morph_animation(Object *ob)
-{
+{
FCurve *fcu;
char *transformName;
Key *key = BKE_key_from_object(ob);
@@ -388,12 +388,12 @@ void AnimationExporter::export_morph_animation(Object *ob)
if (key->adt && key->adt->action) {
fcu = (FCurve *)key->adt->action->curves.first;
-
+
while (fcu) {
transformName = extract_transform_name(fcu->rna_path);
create_keyframed_animation(ob, fcu, transformName, true);
-
+
fcu = fcu->next;
}
}
@@ -407,17 +407,17 @@ void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector<fl
bConstraint *con;
for (con = (bConstraint *)conlist->first; con; con = con->next) {
ListBase targets = {NULL, NULL};
-
+
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
-
+
if (!validateConstraints(con)) continue;
if (cti && cti->get_constraint_targets) {
bConstraintTarget *ct;
Object *obtar;
- /* get targets
+ /* get targets
* - constraints should use ct->matrix, not directly accessing values
- * - ct->matrix members have not yet been calculated here!
+ * - ct->matrix members have not yet been calculated here!
*/
cti->get_constraint_targets(con, &targets);
@@ -438,7 +438,7 @@ void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector<fl
float *AnimationExporter::get_eul_source_for_quat(Object *ob)
{
FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
- const int keys = fcu->totvert;
+ const int keys = fcu->totvert;
float *quat = (float *)MEM_callocN(sizeof(float) * fcu->totvert * 4, "quat output source values");
float *eul = (float *)MEM_callocN(sizeof(float) * fcu->totvert * 3, "quat output source values");
float temp_quat[4];
@@ -520,11 +520,11 @@ void AnimationExporter::create_keyframed_animation(Object *ob, FCurve *fcu, char
/*
* Note: Handle transformation animations separately (to apply matrix inverse to fcurves)
- * We will use the object to evaluate the animation on all keyframes and calculate the
+ * We will use the object to evaluate the animation on all keyframes and calculate the
* resulting object matrix. We need this to incorporate the
* effects of the parent inverse matrix (when it contains a rotation component)
*
- * TODO: try to combine exported fcurves into 3 channel animations like done
+ * TODO: try to combine exported fcurves into 3 channel animations like done
* in export_sampled_animation(). For now each channel is exported as separate <Animation>.
*/
@@ -648,7 +648,7 @@ void AnimationExporter::create_keyframed_animation(Object *ob, FCurve *fcu, char
"/common/" /*profile common is only supported */ + get_transform_sid(fcu->rna_path, -1, axis_name, true);
//if shape key animation, this is the main problem, how to define the channel targets.
/*target = get_morph_id(ob) +
- "/value" +*/
+ "/value" +*/
}
addChannel(COLLADABU::URI(empty, sampler_id), target);
@@ -673,7 +673,7 @@ void AnimationExporter::write_bone_animation_matrix(Object *ob_arm, Bone *bone)
}
bool AnimationExporter::is_bone_deform_group(Bone *bone)
-{
+{
bool is_def;
//Check if current bone is deform
if ((bone->flag & BONE_NO_DEFORM) == 0) return true;
@@ -707,7 +707,7 @@ void AnimationExporter::sample_and_write_bone_animation_matrix(Object *ob_arm, B
fcu = fcu->next;
}
- if (!(fcu)) return;*/
+ if (!(fcu)) return;*/
bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name);
if (!pchan)
@@ -728,7 +728,7 @@ void AnimationExporter::sample_and_write_bone_animation_matrix(Object *ob_arm, B
dae_baked_animation(fra, ob_arm, bone);
}
- if (flag & ARM_RESTPOS)
+ if (flag & ARM_RESTPOS)
arm->flag = flag;
BKE_pose_where_is(depsgraph, scene, ob_arm);
}
@@ -921,7 +921,7 @@ void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNa
if (axis) {
param.push_back(axis);
}
- else
+ else
if (transform) {
param.push_back("TRANSFORM");
}
@@ -1307,7 +1307,7 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
enable_fcurves(ob->adt->action, bone->name);
}
-
+
std::vector<float>::iterator it;
int j = 0;
for (it = frames.begin(); it != frames.end(); it++) {
@@ -1326,7 +1326,7 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
else {
BKE_pose_where_is_bone(depsgraph, scene, ob, pchan, ctime, 1);
}
-
+
// compute bone local mat
if (bone->parent) {
invert_m4_m4(ipar, parchan->pose_mat);
@@ -1334,7 +1334,7 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
}
else
copy_m4_m4(mat, pchan->pose_mat);
-
+
/* OPEN_SIM_COMPATIBILITY
* AFAIK animation to second life is via BVH, but no
* reason to not have the collada-animation be correct
@@ -1359,7 +1359,7 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
else {
copy_m4_m4(mat, ob->obmat);
}
-
+
UnitConverter converter;
double outmat[4][4];
@@ -1515,7 +1515,7 @@ std::string AnimationExporter::get_light_param_sid(char *rna_path, int tm_type,
if (tm_name.size()) {
if (axis_name[0])
return tm_name + "." + std::string(axis_name);
- else
+ else
return tm_name;
}
@@ -1564,7 +1564,7 @@ std::string AnimationExporter::get_camera_param_sid(char *rna_path, int tm_type,
if (tm_name.size()) {
if (axis_name[0])
return tm_name + "." + std::string(axis_name);
- else
+ else
return tm_name;
}
@@ -1751,7 +1751,7 @@ void AnimationExporter::find_sampleframes(Object *ob, std::vector<float> &fra)
} while (true);
}
-/*
+/*
* find keyframes of all the objects animations
*/
void AnimationExporter::find_keyframes(Object *ob, std::vector<float> &fra)
@@ -1844,7 +1844,7 @@ void AnimationExporter::sample_and_write_bone_animation(Object *ob_arm, Bone *bo
arm->flag &= ~ARM_RESTPOS;
BKE_pose_where_is(depsgraph, scene, ob_arm);
}
- //v array will hold all values which will be exported.
+ //v array will hold all values which will be exported.
if (fra.size()) {
float *values = (float *)MEM_callocN(sizeof(float) * 3 * fra.size(), "temp. anim frames");
sample_animation(values, fra, transform_type, bone, ob_arm, pchan);
@@ -1870,7 +1870,7 @@ void AnimationExporter::sample_and_write_bone_animation(Object *ob_arm, Bone *bo
}
// restore restpos
- if (flag & ARM_RESTPOS)
+ if (flag & ARM_RESTPOS)
arm->flag = flag;
BKE_pose_where_is(depsgraph, scene, ob_arm);
}
diff --git a/source/blender/collada/AnimationExporter.h b/source/blender/collada/AnimationExporter.h
index 2ed0a92d89c..a50bcaf0ef4 100644
--- a/source/blender/collada/AnimationExporter.h
+++ b/source/blender/collada/AnimationExporter.h
@@ -23,7 +23,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
-extern "C"
+extern "C"
{
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@@ -100,8 +100,8 @@ public:
bool exportAnimations(Scene *sce);
// called for each exported object
- void operator() (Object *ob);
-
+ void operator() (Object *ob);
+
protected:
const ExportSettings *export_settings;
@@ -127,7 +127,7 @@ protected:
// dae_bone_animation -> add_bone_animation
// (blend this into dae_bone_animation)
void dae_bone_animation(std::vector<float> &fra, float *v, int tm_type, int axis, std::string ob_name, std::string bone_name);
-
+
void dae_baked_animation(std::vector<float> &fra, Object *ob_arm, Bone *bone);
void dae_baked_object_animation(std::vector<float> &fra, Object *ob);
@@ -140,9 +140,9 @@ protected:
void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param,
COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis, bool transform);
-
+
void get_source_values(BezTriple *bezt, COLLADASW::InputSemantic::Semantics semantic, bool is_angle, float *values, int *length);
-
+
float* get_eul_source_for_quat(Object *ob );
bool is_flat_line(std::vector<float> &values, int channel_count);
@@ -171,12 +171,12 @@ protected:
std::string create_interpolation_source(FCurve *fcu, const std::string& anim_id, const char *axis_name, bool *has_tangents);
std::string fake_interpolation_source(int tot, const std::string& anim_id, const char *axis_name);
-
+
// for rotation, axis name is always appended and the value of append_axis is ignored
std::string get_transform_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
std::string get_light_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
std::string get_camera_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
-
+
void find_keyframes(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name);
void find_keyframes(Object *ob, std::vector<float> &fra);
void find_sampleframes(Object *ob, std::vector<float> &fra);
@@ -185,13 +185,13 @@ protected:
void make_anim_frames_from_targets(Object *ob, std::vector<float> &frames );
void find_rotation_frames(Object *ob, std::vector<float> &fra, const char *prefix, int rotmode);
-
+
// enable fcurves driving a specific bone, disable all the rest
// if bone_name = NULL enable all fcurves
void enable_fcurves(bAction *act, char *bone_name);
-
+
bool hasAnimations(Scene *sce);
-
+
char *extract_transform_name(char *rna_path);
std::string getObjectBoneName(Object *ob, const FCurve * fcu);
diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp
index 489aceda0c2..e57f8c2f652 100644
--- a/source/blender/collada/AnimationImporter.cpp
+++ b/source/blender/collada/AnimationImporter.cpp
@@ -118,7 +118,7 @@ void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve)
// input, output
- bez.vec[1][0] = bc_get_float_value(input, j) * fps;
+ bez.vec[1][0] = bc_get_float_value(input, j) * fps;
bez.vec[1][1] = bc_get_float_value(output, j * dim + i);
@@ -135,14 +135,14 @@ void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve)
// outtangent
bez.vec[2][0] = bc_get_float_value(outtan, (j * 2 * dim) + (2 * i)) * fps;
bez.vec[2][1] = bc_get_float_value(outtan, (j * 2 * dim) + (2 * i) + 1);
- if (curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER)
+ if (curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER)
bez.ipo = BEZT_IPO_BEZ;
- else
+ else
bez.ipo = BEZT_IPO_CONST;
//bez.h1 = bez.h2 = HD_AUTO;
}
else {
- bez.h1 = bez.h2 = HD_AUTO;
+ bez.h1 = bez.h2 = HD_AUTO;
bez.ipo = BEZT_IPO_LIN;
}
// bez.ipo = U.ipo_new; /* use default interpolation mode here... */
@@ -183,10 +183,10 @@ void AnimationImporter::fcurve_is_used(FCurve *fcu)
void AnimationImporter::add_fcurves_to_object(Main *bmain, Object *ob, std::vector<FCurve *>& curves, char *rna_path, int array_index, Animation *animated)
{
bAction *act;
-
+
if (!ob->adt || !ob->adt->action) act = verify_adt_action(bmain, (ID *)&ob->id, 1);
else act = ob->adt->action;
-
+
std::vector<FCurve *>::iterator it;
int i;
@@ -198,39 +198,39 @@ void AnimationImporter::add_fcurves_to_object(Main *bmain, Object *ob, std::vect
if (is_rotation)
fcurve_deg_to_rad(fcu);
#endif
-
+
for (it = curves.begin(), i = 0; it != curves.end(); it++, i++) {
FCurve *fcu = *it;
fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
-
+
if (array_index == -1) fcu->array_index = i;
else fcu->array_index = array_index;
-
+
if (ob->type == OB_ARMATURE) {
bActionGroup *grp = NULL;
const char *bone_name = bc_get_joint_name(animated->node);
-
+
if (bone_name) {
/* try to find group */
grp = BKE_action_group_find_name(act, bone_name);
-
+
/* no matching groups, so add one */
if (grp == NULL) {
/* Add a new group, and make it active */
grp = (bActionGroup *)MEM_callocN(sizeof(bActionGroup), "bActionGroup");
-
+
grp->flag = AGRP_SELECTED;
BLI_strncpy(grp->name, bone_name, sizeof(grp->name));
-
+
BLI_addtail(&act->groups, grp);
BLI_uniquename(&act->groups, grp, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"), '.',
offsetof(bActionGroup, name), 64);
}
-
+
/* add F-Curve to group */
action_groups_add_channel(act, grp, fcu);
fcurve_is_used(fcu);
-
+
}
#if 0
if (is_rotation) {
@@ -263,7 +263,7 @@ bool AnimationImporter::write_animation(const COLLADAFW::Animation *anim)
{
if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) {
COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve *)anim;
-
+
// XXX Don't know if it's necessary
// Should we check outPhysicalDimension?
if (curve->getInPhysicalDimension() != COLLADAFW::PHYSICAL_DIMENSION_TIME) {
@@ -296,10 +296,10 @@ bool AnimationImporter::write_animation(const COLLADAFW::Animation *anim)
else {
fprintf(stderr, "FORMULA animation type is not supported yet.\n");
}
-
+
return true;
}
-
+
// called on post-process stage after writeVisualScenes
bool AnimationImporter::write_animation_list(const COLLADAFW::AnimationList *animlist)
{
@@ -340,16 +340,16 @@ virtual void AnimationImporter::change_eul_to_quat(Object *ob, bAction *act)
{
bActionGroup *grp;
int i;
-
+
for (grp = (bActionGroup *)act->groups.first; grp; grp = grp->next) {
FCurve *eulcu[3] = {NULL, NULL, NULL};
-
+
if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end())
continue;
std::vector<FCurve *> &rot_fcurves = fcurves_actionGroup_map[grp];
-
+
if (rot_fcurves.size() > 3) continue;
for (i = 0; i < rot_fcurves.size(); i++)
@@ -437,7 +437,7 @@ void AnimationImporter::modify_fcurve(std::vector<FCurve *> *curves, const char
for (it = curves->begin(), i = 0; it != curves->end(); it++, i++) {
FCurve *fcu = *it;
fcu->rna_path = BLI_strdup(rna_path);
-
+
if (array_index == -1) fcu->array_index = i;
else fcu->array_index = array_index;
@@ -674,7 +674,7 @@ void AnimationImporter:: Assign_float_animations(const COLLADAFW::UniqueId& list
}
}
}
-
+
}
float AnimationImporter::convert_to_focal_length(float in_xfov, int fov_type, float aspect, float sensorx)
@@ -711,7 +711,7 @@ void AnimationImporter::Assign_lens_animations(const COLLADAFW::UniqueId& listid
//Add the curves of the current animation to the object
for (iter = animcurves.begin(); iter != animcurves.end(); iter++) {
FCurve *fcu = *iter;
-
+
for (unsigned int i = 0; i < fcu->totvert; i++) {
fcu->bezt[i].vec[0][1] = convert_to_focal_length(fcu->bezt[i].vec[0][1], fov_type, aspect, cam->sensor_x);
fcu->bezt[i].vec[1][1] = convert_to_focal_length(fcu->bezt[i].vec[1][1], fov_type, aspect, cam->sensor_x);
@@ -949,7 +949,7 @@ void AnimationImporter::translate_Animations(Main *bmain, COLLADAFW::Node *node,
const COLLADAFW::TransformationPointerArray& nodeTransforms = node->getTransformations();
- //for each transformation in node
+ //for each transformation in node
for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) {
COLLADAFW::Transformation *transform = nodeTransforms[i];
COLLADAFW::Transformation::TransformationType tm_type = transform->getTransformationType();
@@ -986,12 +986,12 @@ void AnimationImporter::translate_Animations(Main *bmain, COLLADAFW::Node *node,
//Add the curves of the current animation to the object
for (iter = animcurves.begin(); iter != animcurves.end(); iter++) {
FCurve *fcu = *iter;
-
+
BLI_addtail(AnimCurves, fcu);
fcurve_is_used(fcu);
}
}
-
+
}
}
}
@@ -1017,7 +1017,7 @@ void AnimationImporter::translate_Animations(Main *bmain, COLLADAFW::Node *node,
const COLLADAFW::Color *col = &(light->getColor());
const COLLADAFW::UniqueId& listid = col->getAnimationList();
- Assign_color_animations(listid, AnimCurves, "color");
+ Assign_color_animations(listid, AnimCurves, "color");
}
if ((animType->light & LIGHT_FOA) != 0) {
const COLLADAFW::AnimatableFloat *foa = &(light->getFallOffAngle());
@@ -1051,14 +1051,14 @@ void AnimationImporter::translate_Animations(Main *bmain, COLLADAFW::Node *node,
if ((animType->camera & CAMERA_XFOV) != 0) {
const COLLADAFW::AnimatableFloat *xfov = &(camera->getXFov());
const COLLADAFW::UniqueId& listid = xfov->getAnimationList();
- double aspect = get_aspect_ratio(camera);
+ double aspect = get_aspect_ratio(camera);
Assign_lens_animations(listid, AnimCurves, aspect, cam, "lens", CAMERA_XFOV);
}
else if ((animType->camera & CAMERA_YFOV) != 0) {
const COLLADAFW::AnimatableFloat *yfov = &(camera->getYFov());
const COLLADAFW::UniqueId& listid = yfov->getAnimationList();
- double aspect = get_aspect_ratio(camera);
+ double aspect = get_aspect_ratio(camera);
Assign_lens_animations(listid, AnimCurves, aspect, cam, "lens", CAMERA_YFOV);
}
@@ -1281,7 +1281,7 @@ AnimationImporter::AnimMix *AnimationImporter::get_animation_type(const COLLADAF
const COLLADAFW::TransformationPointerArray& nodeTransforms = node->getTransformations();
- //for each transformation in node
+ //for each transformation in node
for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) {
COLLADAFW::Transformation *transform = nodeTransforms[i];
const COLLADAFW::UniqueId& listid = transform->getAnimationList();
@@ -1369,7 +1369,7 @@ int AnimationImporter::setAnimType(const COLLADAFW::Animatable *prop, int types,
anim_type = types;
return anim_type;
-}
+}
// Is not used anymore.
void AnimationImporter::find_frames_old(std::vector<float> *frames, COLLADAFW::Node *node, COLLADAFW::Transformation::TransformationType tm_type)
@@ -1391,7 +1391,7 @@ void AnimationImporter::find_frames_old(std::vector<float> *frames, COLLADAFW::N
const COLLADAFW::UniqueId& listid = transform->getAnimationList();
//if transform is animated its animlist must exist.
if (animlist_map.find(listid) != animlist_map.end()) {
-
+
const COLLADAFW::AnimationList *animlist = animlist_map[listid];
const COLLADAFW::AnimationList::AnimationBindings& bindings = animlist->getAnimationBindings();
@@ -1441,11 +1441,11 @@ Object *AnimationImporter::translate_animation_OLD(Main *bmain, COLLADAFW::Node
COLLADAFW::Transformation::TransformationType tm_type,
Object *par_job)
{
-
+
bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE;
bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX;
bool is_joint = node->getType() == COLLADAFW::Node::JOINT;
-
+
COLLADAFW::Node *root = root_map.find(node->getUniqueId()) == root_map.end() ? node : root_map[node->getUniqueId()];
Object *ob = is_joint ? armature_importer->get_armature_for_joint(node) : object_map[node->getUniqueId()];
const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL;
@@ -1456,11 +1456,11 @@ Object *AnimationImporter::translate_animation_OLD(Main *bmain, COLLADAFW::Node
// frames at which to sample
std::vector<float> frames;
-
+
find_frames_old(&frames, node, tm_type);
-
+
unsigned int i;
-
+
float irest_dae[4][4];
float rest[4][4], irest[4][4];
@@ -1731,9 +1731,9 @@ void AnimationImporter::evaluate_transform_at_frame(float mat[4][4], COLLADAFW::
}
}
-static void report_class_type_unsupported(const char *path,
+static void report_class_type_unsupported(const char *path,
const COLLADAFW::AnimationList::AnimationClass animclass,
- const COLLADAFW::Transformation::TransformationType type)
+ const COLLADAFW::Transformation::TransformationType type)
{
if (animclass == COLLADAFW::AnimationList::UNKNOWN_CLASS) {
fprintf(stderr, "%s: UNKNOWN animation class\n", path);
@@ -1828,7 +1828,7 @@ bool AnimationImporter::evaluate_animation(COLLADAFW::Transformation *tm, float
fprintf(stderr, "%s: expected 1 curve, got %d\n", path, (int)curves.size());
return false;
}
-
+
switch (animclass) {
case COLLADAFW::AnimationList::POSITION_X:
vec[0] = evaluate_fcurve(curves[0], fra);
@@ -2010,7 +2010,7 @@ void AnimationImporter::add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurv
{
const char *bone_name = bc_get_joint_name(node);
bAction *act = ob->adt->action;
-
+
/* try to find group */
bActionGroup *grp = BKE_action_group_find_name(act, bone_name);
diff --git a/source/blender/collada/AnimationImporter.h b/source/blender/collada/AnimationImporter.h
index 0957b1e6e06..f63329158f0 100644
--- a/source/blender/collada/AnimationImporter.h
+++ b/source/blender/collada/AnimationImporter.h
@@ -19,7 +19,7 @@
*
* ***** END GPL LICENSE BLOCK *****
*/
-
+
/** \file AnimationImporter.h
* \ingroup collada
*/
@@ -75,9 +75,9 @@ private:
std::map<COLLADAFW::UniqueId, const COLLADAFW::AnimationList*> animlist_map;
std::vector<FCurve*> unused_curves;
std::map<COLLADAFW::UniqueId, Object*> joint_objects;
-
+
FCurve *create_fcurve(int array_index, const char *rna_path);
-
+
void add_bezt(FCurve *fcu, float frame, float value, eBezTriple_Interpolation ipo=BEZT_IPO_LIN);
// create one or several fcurves depending on the number of parameters being animated
@@ -89,7 +89,7 @@ private:
void add_fcurves_to_object(Main *bmain, Object *ob, std::vector<FCurve*>& curves, char *rna_path, int array_index, Animation *animated);
-
+
int typeFlag;
std::string import_from_version;
@@ -121,7 +121,7 @@ private:
MATERIAL_TRANSPARENCY = 1 << 4,
MATERIAL_IOR = 1 << 5
};
-
+
enum AnimationType
{
INANIMATE = 0,
@@ -144,7 +144,7 @@ public:
void set_import_from_version(std::string import_from_version);
bool write_animation(const COLLADAFW::Animation* anim);
-
+
// called on post-process stage after writeVisualScenes
bool write_animation_list(const COLLADAFW::AnimationList* animlist);
@@ -163,7 +163,7 @@ public:
void apply_matrix_curves(Main *bmain, Object *ob, std::vector<FCurve*>& animcurves, COLLADAFW::Node* root, COLLADAFW::Node* node,
COLLADAFW::Transformation * tm );
-
+
void add_bone_animation_sampled(Main *bmain, Object *ob, std::vector<FCurve*>& animcurves, COLLADAFW::Node* root, COLLADAFW::Node* node, COLLADAFW::Transformation * tm);
void Assign_transform_animations(COLLADAFW::Transformation* transform,
@@ -175,7 +175,7 @@ public:
void Assign_lens_animations(const COLLADAFW::UniqueId& listid, ListBase *AnimCurves, const double aspect, Camera *cam, const char *anim_type, int fov_type);
int setAnimType ( const COLLADAFW::Animatable * prop, int type, int addition);
-
+
void modify_fcurve(std::vector<FCurve*>* curves, const char *rna_path, int array_index );
void unused_fcurve(std::vector<FCurve*>* curves );
// prerequisites:
@@ -186,7 +186,7 @@ public:
std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& root_map,
COLLADAFW::Transformation::TransformationType tm_type,
Object *par_job = NULL);
-
+
void find_frames( std::vector<float>* frames, std::vector<FCurve*>* curves );
void find_frames_old( std::vector<float>* frames, COLLADAFW::Node * node, COLLADAFW::Transformation::TransformationType tm_type );
// internal, better make it private
diff --git a/source/blender/collada/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp
index c50de1ef72e..85b9d3297ca 100644
--- a/source/blender/collada/ArmatureExporter.cpp
+++ b/source/blender/collada/ArmatureExporter.cpp
@@ -118,7 +118,7 @@ bool ArmatureExporter::add_instance_controller(Object *ob)
}
InstanceWriter::add_material_bindings(ins.getBindMaterial(), ob, this->export_settings->active_uv_only);
-
+
ins.add();
return true;
}
@@ -147,7 +147,7 @@ void ArmatureExporter::find_objects_using_armature(Object *ob_arm, std::vector<O
Base *base = (Base *) sce->base.first;
while (base) {
Object *ob = base->object;
-
+
if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) {
objects.push_back(ob);
}
@@ -265,7 +265,7 @@ void ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW:
if (!has_restmat) {
/* Have no restpose matrix stored, try old style <= Blender 2.78 */
-
+
bc_create_restpose_mat(this->export_settings, bone, bone_rest_mat, bone->arm_mat, true);
if (bone->parent) {
diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp
index 6bf6087c054..19f174d4840 100644
--- a/source/blender/collada/ArmatureImporter.cpp
+++ b/source/blender/collada/ArmatureImporter.cpp
@@ -61,7 +61,7 @@ ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh,
view_layer(view_layer),
unit_converter(conv),
import_settings(import_settings),
- empty(NULL),
+ empty(NULL),
mesh_importer(mesh) {
}
@@ -103,7 +103,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon
std::vector<COLLADAFW::Node *>::iterator it;
it = std::find(finished_joints.begin(), finished_joints.end(), node);
if (it != finished_joints.end()) return chain_length;
-
+
EditBone *bone = ED_armature_ebone_add(arm, bc_get_joint_name(node));
totbone++;
@@ -141,7 +141,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon
if (parent) bone->parent = parent;
- float loc[3], size[3], rot[3][3];
+ float loc[3], size[3], rot[3][3];
BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(arm);
BoneExtended &be = add_bone_extended(bone, node, totchild, layer_labels, extended_bones);
int layer = be.get_bone_layers();
@@ -386,7 +386,7 @@ void ArmatureImporter::set_euler_rotmode()
COLLADAFW::Node *joint = it->second;
std::map<COLLADAFW::UniqueId, SkinInfo>::iterator sit;
-
+
for (sit = skin_by_data_uid.begin(); sit != skin_by_data_uid.end(); sit++) {
SkinInfo& skin = sit->second;
@@ -410,7 +410,7 @@ void ArmatureImporter::set_euler_rotmode()
Object *ArmatureImporter::get_empty_for_leaves()
{
if (empty) return empty;
-
+
empty = bc_add_object(scene, view_layer, OB_EMPTY, NULL);
empty->empty_drawtype = OB_EMPTY_SPHERE;
@@ -456,7 +456,7 @@ void ArmatureImporter::create_armature_bones(Main *bmain, std::vector<Object *>
//if there is an armature created for root_joint next root_joint
for (ri = root_joints.begin(); ri != root_joints.end(); ri++) {
if (get_armature_for_joint(*ri) != NULL) continue;
-
+
Object *ob_arm = joint_parent_map[(*ri)->getUniqueId()];
if (!ob_arm)
continue;
@@ -572,8 +572,8 @@ Object *ArmatureImporter::create_armature_bones(Main *bmain, SkinInfo& skin)
}
if (!shared && this->joint_parent_map.size() > 0) {
- // All armatures have been created while creating the Node tree.
- // The Collada exporter currently does not create a
+ // All armatures have been created while creating the Node tree.
+ // The Collada exporter currently does not create a
// strict relationship between geometries and armatures
// So when we reimport a Blender collada file, then we have
// to guess what is meant.
@@ -657,12 +657,12 @@ void ArmatureImporter::set_pose(Object *ob_arm, COLLADAFW::Node *root_node, con
}
else {
-
+
copy_m4_m4(mat, obmat);
float invObmat[4][4];
invert_m4_m4(invObmat, ob_arm->obmat);
mul_m4_m4m4(pchan->pose_mat, invObmat, mat);
-
+
}
//float angle = 0.0f;
@@ -758,7 +758,7 @@ void ArmatureImporter::make_armatures(bContext *C, std::vector<Object *> &object
// free memory stolen from SkinControllerData
skin.free();
}
-
+
//for bones without skins
create_armature_bones(bmain, ob_arms);
@@ -808,9 +808,9 @@ bool ArmatureImporter::write_skin_controller_data(const COLLADAFW::SkinControlle
// don't forget to call defgroup_unique_name before we copy
- // controller data uid -> [armature] -> joint data,
+ // controller data uid -> [armature] -> joint data,
// [mesh object]
- //
+ //
SkinInfo skin(unit_converter);
skin.borrow_skin_controller_data(data);
@@ -868,7 +868,7 @@ void ArmatureImporter::make_shape_keys()
//Prereq: all the geometries must be imported and mesh objects must be made
Object *source_ob = this->mesh_importer->get_object_by_geom_uid((*mc)->getSource());
-
+
if (source_ob) {
Mesh *source_me = (Mesh *)source_ob->data;
@@ -876,7 +876,7 @@ void ArmatureImporter::make_shape_keys()
Key *key = source_me->key = BKE_key_add((ID *)source_me);
key->type = KEY_RELATIVE;
KeyBlock *kb;
-
+
//insert basis key
kb = BKE_keyblock_add_ctime(key, "Basis", false);
BKE_keyblock_convert_from_mesh(source_me, kb);
@@ -887,14 +887,14 @@ void ArmatureImporter::make_shape_keys()
//This'll do for now since only mesh morphing is imported
Mesh *me = this->mesh_importer->get_mesh_by_geom_uid(morphTargetIds[i]);
-
+
if (me) {
me->key = key;
std::string morph_name = *this->mesh_importer->get_geometry_name(me->id.name);
kb = BKE_keyblock_add_ctime(key, morph_name.c_str(), false);
BKE_keyblock_convert_from_mesh(me, kb);
-
+
//apply weights
weight = morphWeights.getFloatValues()->getData()[i];
kb->curval = weight;
@@ -990,14 +990,14 @@ BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Nod
has_connect = et->setData("connect", &connect_type);
bool has_roll = et->setData("roll", &roll);
-
+
layers = et->setData("layer", layers);
if (has_tail && !has_connect)
{
/* got a bone tail definition but no connect info -> bone is not connected */
has_connect = true;
- connect_type = 0;
+ connect_type = 0;
}
be->set_bone_layers(layers, layer_labels);
diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h
index 9f8961a41a2..b13e40bf855 100644
--- a/source/blender/collada/ArmatureImporter.h
+++ b/source/blender/collada/ArmatureImporter.h
@@ -158,16 +158,16 @@ public:
bool write_controller(const COLLADAFW::Controller* controller);
COLLADAFW::UniqueId *get_geometry_uid(const COLLADAFW::UniqueId& controller_uid);
-
+
Object *get_armature_for_joint(COLLADAFW::Node *node);
void get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count);
-
+
// gives a world-space mat
bool get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint);
void set_tags_map( TagsMap& tags_map);
-
+
};
#endif
diff --git a/source/blender/collada/CameraExporter.cpp b/source/blender/collada/CameraExporter.cpp
index 649288c2db4..32bd24f1e0b 100644
--- a/source/blender/collada/CameraExporter.cpp
+++ b/source/blender/collada/CameraExporter.cpp
@@ -56,9 +56,9 @@ void forEachCameraObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set
void CamerasExporter::exportCameras(Scene *sce)
{
openLibrary();
-
+
forEachCameraObjectInExportSet(sce, *this, this->export_settings->export_set);
-
+
closeLibrary();
}
void CamerasExporter::operator()(Object *ob, Scene *sce)
diff --git a/source/blender/collada/ControllerExporter.cpp b/source/blender/collada/ControllerExporter.cpp
index f6dbc965b42..122094e33a6 100644
--- a/source/blender/collada/ControllerExporter.cpp
+++ b/source/blender/collada/ControllerExporter.cpp
@@ -99,7 +99,7 @@ bool ControllerExporter::add_instance_controller(Object *ob)
}
InstanceWriter::add_material_bindings(ins.getBindMaterial(), ob, this->export_settings->active_uv_only);
-
+
ins.add();
return true;
}
@@ -148,7 +148,7 @@ void ArmatureExporter::find_objects_using_armature(Object *ob_arm, std::vector<O
Base *base = (Base *) sce->base.first;
while (base) {
Object *ob = base->object;
-
+
if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) {
objects.push_back(ob);
}
@@ -203,7 +203,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
this->export_settings->export_mesh_type,
this->export_settings->apply_modifiers,
this->export_settings->triangulate);
-
+
if (!me->dvert) return;
std::string controller_name = id_name(ob_arm);
@@ -246,7 +246,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
for (j = 0; j < vert->totweight; j++) {
int idx = vert->dw[j].def_nr;
if (idx >= joint_index_by_def_index.size()) {
- // XXX: Maybe better find out where and
+ // XXX: Maybe better find out where and
// why the Out Of Bound indexes get created ?
oob_counter += 1;
}
@@ -314,7 +314,7 @@ void ControllerExporter::export_morph_controller(Object *ob, Key *key)
std::string targets_id = add_morph_targets(key, ob);
std::string morph_weights_id = add_morph_weights(key, ob);
-
+
COLLADASW::TargetsElement targets(mSW);
COLLADASW::InputList &input = targets.getInputList();
@@ -376,7 +376,7 @@ std::string ControllerExporter::add_morph_weights(Key *key, Object *ob)
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
param.push_back("MORPH_WEIGHT");
-
+
source.prepareToAppendValues();
KeyBlock *kb = (KeyBlock *)key->block.first;
@@ -396,7 +396,7 @@ void ControllerExporter::add_weight_extras(Key *key)
{
// can also try the base element and param alternative
COLLADASW::BaseExtraTechnique extra;
-
+
KeyBlock * kb = (KeyBlock *)key->block.first;
//skip the basis
kb = kb->next;
@@ -447,7 +447,7 @@ std::string ControllerExporter::add_joints_source(Object *ob_arm, ListBase *defb
source.setArrayId(source_id + ARRAY_ID_SUFFIX);
source.setAccessorCount(totjoint);
source.setAccessorStride(1);
-
+
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
param.push_back("JOINT");
@@ -480,7 +480,7 @@ std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, ListBas
source.setArrayId(source_id + ARRAY_ID_SUFFIX);
source.setAccessorCount(totjoint); //BLI_listbase_count(defbase));
source.setAccessorStride(16);
-
+
source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4);
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
param.push_back("TRANSFORM");
@@ -509,7 +509,7 @@ std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, ListBas
float bind_mat[4][4]; /* derived from bone->arm_mat */
bool has_bindmat = bc_get_property_matrix(pchan->bone, "bind_mat", bind_mat);
-
+
if (!has_bindmat) {
/* Have no bind matrix stored, try old style <= Blender 2.78 */
@@ -571,7 +571,7 @@ std::string ControllerExporter::add_weights_source(Mesh *me, const std::string&
source.setArrayId(source_id + ARRAY_ID_SUFFIX);
source.setAccessorCount(weights.size());
source.setAccessorStride(1);
-
+
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
param.push_back("WEIGHT");
diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp
index 2e5d1757c4c..9e78c164dad 100644
--- a/source/blender/collada/DocumentExporter.cpp
+++ b/source/blender/collada/DocumentExporter.cpp
@@ -62,7 +62,7 @@
#include "COLLADASWInstanceNode.h"
#include "COLLADASWBaseInputElement.h"
-extern "C"
+extern "C"
{
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@@ -257,14 +257,14 @@ int DocumentExporter::exportCurrentScene(bContext *C, Scene *sce)
#endif
asset.getContributor().mAuthoringTool = version_buf;
asset.add();
-
+
LinkNode *export_set = this->export_settings->export_set;
// <library_cameras>
if (bc_has_object_type(export_set, OB_CAMERA)) {
CamerasExporter ce(writer, this->export_settings);
ce.exportCameras(sce);
}
-
+
// <library_lights>
if (bc_has_object_type(export_set, OB_LAMP)) {
LightsExporter le(writer, this->export_settings);
@@ -274,11 +274,11 @@ int DocumentExporter::exportCurrentScene(bContext *C, Scene *sce)
// <library_images>
ImagesExporter ie(writer, this->export_settings);
ie.exportImages(sce);
-
+
// <library_effects>
EffectsExporter ee(writer, this->export_settings);
ee.exportEffects(sce);
-
+
// <library_materials>
MaterialsExporter me(writer, this->export_settings);
me.exportMaterials(sce);
@@ -292,7 +292,7 @@ int DocumentExporter::exportCurrentScene(bContext *C, Scene *sce)
// <library_controllers>
ArmatureExporter arm_exporter(writer, this->export_settings);
ControllerExporter controller_exporter(writer, this->export_settings);
- if (bc_has_object_type(export_set, OB_ARMATURE) || this->export_settings->include_shapekeys)
+ if (bc_has_object_type(export_set, OB_ARMATURE) || this->export_settings->include_shapekeys)
{
controller_exporter.export_controllers(depsgraph, sce);
}
@@ -307,13 +307,13 @@ int DocumentExporter::exportCurrentScene(bContext *C, Scene *sce)
ae.exportAnimations(sce);
}
se.exportScene(C, depsgraph, sce);
-
+
// <scene>
std::string scene_name(translate_id(id_name(sce)));
COLLADASW::Scene scene(writer, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING,
scene_name));
scene.add();
-
+
// close <Collada>
writer->endDocument();
delete writer;
diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp
index 30e32c346ee..ae573fec0d8 100644
--- a/source/blender/collada/DocumentImporter.cpp
+++ b/source/blender/collada/DocumentImporter.cpp
@@ -130,7 +130,7 @@ bool DocumentImporter::import()
COLLADASaxFWL::Loader loader(&errorHandler);
COLLADAFW::Root root(&loader, this);
ExtraHandler *ehandler = new ExtraHandler(this, &(this->anim_importer));
-
+
loader.registerExtraDataCallbackHandler(ehandler);
// deselect all to select new objects
@@ -143,19 +143,19 @@ bool DocumentImporter::import()
delete ehandler;
return false;
}
-
+
if (errorHandler.hasError()) {
delete ehandler;
return false;
}
-
+
/** TODO set up scene graph and such here */
-
+
mImportStage = Controller;
-
+
COLLADASaxFWL::Loader loader2;
COLLADAFW::Root root2(&loader2, this);
-
+
if (!root2.loadDocument(encodedFilename)) {
fprintf(stderr, "COLLADAFW::Root::loadDocument() returned false on 2nd pass\n");
delete ehandler;
@@ -198,7 +198,7 @@ void DocumentImporter::finish()
for (sit = vscenes.begin(); sit != vscenes.end(); sit++) {
PointerRNA sceneptr, unit_settings;
PropertyRNA *system, *scale;
-
+
// for scene unit settings: system, scale_length
RNA_id_pointer_create(&sce->id, &sceneptr);
@@ -207,7 +207,7 @@ void DocumentImporter::finish()
scale = RNA_struct_find_property(&unit_settings, "scale_length");
if (this->import_settings->import_units) {
-
+
switch (unit_converter.isMetricSystem()) {
case UnitConverter::Metric:
RNA_property_enum_set(&unit_settings, system, USER_UNIT_METRIC);
@@ -272,7 +272,7 @@ void DocumentImporter::finish()
DEG_relations_tag_update(bmain);
}
-
+
bc_match_scale(objects_to_scale, unit_converter, !this->import_settings->import_units);
delete objects_to_scale;
@@ -473,7 +473,7 @@ void DocumentImporter::create_constraints(ExtraTags *et, Object *ob)
short type = 0;
et->setData("type", &type);
BKE_constraint_add_for_object(ob, "Test_con", type);
-
+
}
}
@@ -634,7 +634,7 @@ std::vector<Object *> *DocumentImporter::write_node(COLLADAFW::Node *node, COLLA
root_objects->push_back(ob);
}
}
-
+
// XXX: if there're multiple instances, only one is stored
if (!ob) {
@@ -700,7 +700,7 @@ bool DocumentImporter::writeVisualScene(const COLLADAFW::VisualScene *visualScen
{
if (mImportStage != General)
return true;
-
+
// this method called on post process after writeGeometry, writeMaterial, etc.
// for each <node> in <visual_scene>:
@@ -712,18 +712,18 @@ bool DocumentImporter::writeVisualScene(const COLLADAFW::VisualScene *visualScen
// we link Objects with Meshes here
vscenes.push_back(visualScene);
-
+
return true;
}
-/** When this method is called, the writer must handle all nodes contained in the
+/** When this method is called, the writer must handle all nodes contained in the
* library nodes.
* \return The writer should return true, if writing succeeded, false otherwise.*/
bool DocumentImporter::writeLibraryNodes(const COLLADAFW::LibraryNodes *libraryNodes)
{
if (mImportStage != General)
return true;
-
+
Scene *sce = CTX_data_scene(mContext);
const COLLADAFW::NodePointerArray& nodes = libraryNodes->getNodes();
@@ -743,7 +743,7 @@ bool DocumentImporter::writeGeometry(const COLLADAFW::Geometry *geom)
{
if (mImportStage != General)
return true;
-
+
return mesh_importer.write_geometry(geom);
}
@@ -753,13 +753,13 @@ bool DocumentImporter::writeMaterial(const COLLADAFW::Material *cmat)
{
if (mImportStage != General)
return true;
-
+
const std::string& str_mat_id = cmat->getName().size() ? cmat->getName() : cmat->getOriginalId();
Material *ma = BKE_material_add(G.main, (char *)str_mat_id.c_str());
-
+
this->uid_effect_map[cmat->getInstantiatedEffect()] = ma;
this->uid_material_map[cmat->getUniqueId()] = ma;
-
+
return true;
}
@@ -768,7 +768,7 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType();
// TODO: add back texture and extended material parameter support
-
+
// blinn
if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) {
#if 0
@@ -802,9 +802,9 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
#if 0
ma->ang = ef->getIndexOfRefraction().getFloatValue();
#endif
-
+
COLLADAFW::Color col;
-
+
// DIFFUSE
// color
if (ef->getDiffuse().isColor()) {
@@ -816,7 +816,7 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
// texture
else if (ef->getDiffuse().isTexture()) {
#if 0
- COLLADAFW::Texture ctex = ef->getDiffuse().getTexture();
+ COLLADAFW::Texture ctex = ef->getDiffuse().getTexture();
#endif
}
// AMBIENT
@@ -829,7 +829,7 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
// texture
else if (ef->getAmbient().isTexture()) {
#if 0
- COLLADAFW::Texture ctex = ef->getAmbient().getTexture();
+ COLLADAFW::Texture ctex = ef->getAmbient().getTexture();
#endif
}
// SPECULAR
@@ -843,7 +843,7 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
// texture
else if (ef->getSpecular().isTexture()) {
#if 0
- COLLADAFW::Texture ctex = ef->getSpecular().getTexture();
+ COLLADAFW::Texture ctex = ef->getSpecular().getTexture();
#endif
}
// REFLECTIVE
@@ -856,7 +856,7 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
// texture
else if (ef->getReflective().isTexture()) {
#if 0
- COLLADAFW::Texture ctex = ef->getReflective().getTexture();
+ COLLADAFW::Texture ctex = ef->getReflective().getTexture();
#endif
}
@@ -869,7 +869,7 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
// texture
else if (ef->getEmission().isTexture()) {
#if 0
- COLLADAFW::Texture ctex = ef->getEmission().getTexture();
+ COLLADAFW::Texture ctex = ef->getEmission().getTexture();
#endif
}
@@ -902,14 +902,14 @@ bool DocumentImporter::writeEffect(const COLLADAFW::Effect *effect)
{
if (mImportStage != General)
return true;
-
+
const COLLADAFW::UniqueId& uid = effect->getUniqueId();
-
+
if (uid_effect_map.find(uid) == uid_effect_map.end()) {
fprintf(stderr, "Couldn't find a material by UID.\n");
return true;
}
-
+
Material *ma = uid_effect_map[uid];
std::map<COLLADAFW::UniqueId, Material *>::iterator iter;
for (iter = uid_material_map.begin(); iter != uid_material_map.end(); iter++) {
@@ -928,7 +928,7 @@ bool DocumentImporter::writeEffect(const COLLADAFW::Effect *effect)
COLLADAFW::EffectCommon *ef = common_efs[0];
write_profile_COMMON(ef, ma);
this->FW_object_map[effect->getUniqueId()] = effect;
-
+
return true;
}
@@ -939,16 +939,16 @@ bool DocumentImporter::writeCamera(const COLLADAFW::Camera *camera)
{
if (mImportStage != General)
return true;
-
+
Camera *cam = NULL;
std::string cam_id, cam_name;
-
+
ExtraTags *et=getExtraTags(camera->getUniqueId());
cam_id = camera->getOriginalId();
cam_name = camera->getName();
if (cam_name.size()) cam = (Camera *)BKE_camera_add(G.main, (char *)cam_name.c_str());
else cam = (Camera *)BKE_camera_add(G.main, (char *)cam_id.c_str());
-
+
if (!cam) {
fprintf(stderr, "Cannot create camera.\n");
return true;
@@ -961,7 +961,7 @@ bool DocumentImporter::writeCamera(const COLLADAFW::Camera *camera)
}
cam->clipsta = camera->getNearClippingPlane().getValue();
cam->clipend = camera->getFarClippingPlane().getValue();
-
+
COLLADAFW::Camera::CameraType type = camera->getCameraType();
switch (type) {
case COLLADAFW::Camera::ORTHOGRAPHIC:
@@ -981,7 +981,7 @@ bool DocumentImporter::writeCamera(const COLLADAFW::Camera *camera)
}
break;
}
-
+
switch (camera->getDescriptionType()) {
case COLLADAFW::Camera::ASPECTRATIO_AND_Y:
{
@@ -1051,7 +1051,7 @@ bool DocumentImporter::writeCamera(const COLLADAFW::Camera *camera)
// read nothing, use blender defaults.
break;
}
-
+
this->uid_camera_map[camera->getUniqueId()] = cam;
this->FW_object_map[camera->getUniqueId()] = camera;
// XXX import camera options
@@ -1064,7 +1064,7 @@ bool DocumentImporter::writeImage(const COLLADAFW::Image *image)
{
if (mImportStage != General)
return true;
-
+
const std::string& imagepath = image->getImageURI().toNativePath();
char dir[FILE_MAX];
@@ -1075,7 +1075,7 @@ bool DocumentImporter::writeImage(const COLLADAFW::Image *image)
BLI_join_dirfile(absolute_path, sizeof(absolute_path), dir, imagepath.c_str());
if (BLI_exists(absolute_path)) {
workpath = absolute_path;
- }
+ }
else {
// Maybe imagepath was already absolute ?
if (!BLI_exists(imagepath.c_str())) {
@@ -1091,7 +1091,7 @@ bool DocumentImporter::writeImage(const COLLADAFW::Image *image)
return true;
}
this->uid_image_map[image->getUniqueId()] = ima;
-
+
return true;
}
@@ -1185,7 +1185,7 @@ bool DocumentImporter::writeLight(const COLLADAFW::Light *light)
// assuming point light (const att = 1.0);
att1 = 1.0f;
}
-
+
d *= (1.0f / unit_converter.getLinearMeter());
lamp->energy = e;
@@ -1247,7 +1247,7 @@ bool DocumentImporter::writeAnimation(const COLLADAFW::Animation *anim)
{
if (mImportStage != General)
return true;
-
+
// return true;
return anim_importer.write_animation(anim);
}
@@ -1257,7 +1257,7 @@ bool DocumentImporter::writeAnimationList(const COLLADAFW::AnimationList *animat
{
if (mImportStage != General)
return true;
-
+
// return true;
return anim_importer.write_animation_list(animationList);
}
@@ -1274,7 +1274,7 @@ bool DocumentImporter::writeController(const COLLADAFW::Controller *controller)
{
if (mImportStage != General)
return true;
-
+
return armature_importer.write_controller(controller);
}
diff --git a/source/blender/collada/DocumentImporter.h b/source/blender/collada/DocumentImporter.h
index 758caef7e60..b31a086d710 100644
--- a/source/blender/collada/DocumentImporter.h
+++ b/source/blender/collada/DocumentImporter.h
@@ -78,7 +78,7 @@ public:
void create_constraints(ExtraTags *et, Object *ob);
std::vector<Object *> *write_node(COLLADAFW::Node*, COLLADAFW::Node*, Scene*, Object*, bool);
void write_profile_COMMON(COLLADAFW::EffectCommon*, Material*);
-
+
void translate_anim_recursive(COLLADAFW::Node*, COLLADAFW::Node*, Object*);
/**
@@ -149,7 +149,7 @@ private:
ArmatureImporter armature_importer;
MeshImporter mesh_importer;
AnimationImporter anim_importer;
-
+
/** TagsMap typedef for uid_tags_map. */
typedef std::map<std::string, ExtraTags*> TagsMap;
/** Tags map of unique id as a string and ExtraTags instance. */
@@ -165,7 +165,7 @@ private:
std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> node_map;
std::vector<const COLLADAFW::VisualScene*> vscenes;
std::vector<Object*> libnode_ob;
-
+
std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> root_map; // find root joint by child joint uid, for bone tree evaluation during resampling
std::map<COLLADAFW::UniqueId, const COLLADAFW::Object*> FW_object_map;
diff --git a/source/blender/collada/EffectExporter.cpp b/source/blender/collada/EffectExporter.cpp
index dbcdfd01a9c..271dab5deea 100644
--- a/source/blender/collada/EffectExporter.cpp
+++ b/source/blender/collada/EffectExporter.cpp
@@ -57,7 +57,7 @@ static std::string getActiveUVLayerName(Object *ob)
int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
if (num_layers)
return std::string(bc_CustomData_get_active_layer_name(&me->fdata, CD_MTFACE));
-
+
return "";
}
@@ -105,12 +105,12 @@ void EffectsExporter::operator()(Material *ma, Object *ob)
// TODO: add back texture and extended material parameter support
openEffect(translate_id(id_name(ma)) + "-effect");
-
+
COLLADASW::EffectProfile ep(mSW);
ep.setProfileType(COLLADASW::EffectProfile::COMMON);
ep.openProfile();
writeLambert(ep, ma);
-
+
COLLADASW::ColorOrTexture cot;
// transparency
@@ -144,7 +144,7 @@ void EffectsExporter::operator()(Material *ma, Object *ob)
//COLLADASW::Surface surfaces[MAX_MTEX];
//void *samp_surf[MAX_MTEX][2];
void *samp_surf[MAX_MTEX];
-
+
// image to index to samp_surf map
// samp_surf[index] stores 2 pointers, sampler and surface
std::map<std::string, int> im_samp_map;
@@ -153,10 +153,10 @@ void EffectsExporter::operator()(Material *ma, Object *ob)
for (a = 0, b = 0; a < tex_indices.size(); a++) {
MTex *t = ma->mtex[tex_indices[a]];
Image *ima = t->tex->ima;
-
+
// Image not set for texture
if (!ima) continue;
-
+
std::string key(id_name(ima));
key = translate_id(key);
@@ -171,7 +171,7 @@ void EffectsExporter::operator()(Material *ma, Object *ob)
// COLLADASW::NewParamSurface surface(mSW);
// surface->setParamType(COLLADASW::CSW_SURFACE_TYPE_2D);
-
+
//<newparam> <sampler> <source>
COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
@@ -180,11 +180,11 @@ void EffectsExporter::operator()(Material *ma, Object *ob)
// copy values to arrays since they will live longer
samplers[a] = sampler;
//surfaces[a] = surface;
-
+
// store pointers so they can be used later when we create <texture>s
samp_surf[b] = &samplers[a];
//samp_surf[b][1] = &surfaces[a];
-
+
im_samp_map[key] = b;
b++;
}
@@ -238,12 +238,12 @@ COLLADASW::ColorOrTexture EffectsExporter::createTexture(Image *ima,
COLLADASW::Sampler *sampler
/*COLLADASW::Surface *surface*/)
{
-
+
COLLADASW::Texture texture(translate_id(id_name(ima)));
texture.setTexcoord(uv_layer_name);
//texture.setSurface(*surface);
texture.setSampler(*sampler);
-
+
COLLADASW::ColorOrTexture cot(texture);
return cot;
}
diff --git a/source/blender/collada/EffectExporter.h b/source/blender/collada/EffectExporter.h
index eac428ae330..a1395bfde9f 100644
--- a/source/blender/collada/EffectExporter.h
+++ b/source/blender/collada/EffectExporter.h
@@ -50,25 +50,25 @@ public:
void exportEffects(Scene *sce);
void operator()(Material *ma, Object *ob);
-
+
COLLADASW::ColorOrTexture createTexture(Image *ima,
std::string& uv_layer_name,
COLLADASW::Sampler *sampler
/*COLLADASW::Surface *surface*/);
-
+
COLLADASW::ColorOrTexture getcol(float r, float g, float b, float a);
private:
void writeLambert(COLLADASW::EffectProfile &ep, Material *ma);
void writeTextures(COLLADASW::EffectProfile &ep,
std::string &key,
- COLLADASW::Sampler *sampler,
+ COLLADASW::Sampler *sampler,
MTex *t, Image *ima,
std::string &uvname );
bool hasEffects(Scene *sce);
-
+
const ExportSettings *export_settings;
-
+
Scene *scene;
};
diff --git a/source/blender/collada/ErrorHandler.cpp b/source/blender/collada/ErrorHandler.cpp
index 7e18328f000..26674445d98 100644
--- a/source/blender/collada/ErrorHandler.cpp
+++ b/source/blender/collada/ErrorHandler.cpp
@@ -53,7 +53,7 @@ bool ErrorHandler::handleError(const COLLADASaxFWL::IError *error)
See https://github.com/KhronosGroup/OpenCOLLADA/issues/442
*/
bool isWarning = false;
-
+
if (error->getErrorClass() == COLLADASaxFWL::IError::ERROR_SAXPARSER) {
COLLADASaxFWL::SaxParserError *saxParserError = (COLLADASaxFWL::SaxParserError *) error;
const GeneratedSaxParser::ParserError& parserError = saxParserError->getError();
diff --git a/source/blender/collada/ExtraHandler.cpp b/source/blender/collada/ExtraHandler.cpp
index bef7accd9f7..5d704bc9abc 100644
--- a/source/blender/collada/ExtraHandler.cpp
+++ b/source/blender/collada/ExtraHandler.cpp
@@ -54,9 +54,9 @@ bool ExtraHandler::elementEnd(const char *elementName)
bool ExtraHandler::textData(const char *text, size_t textLength)
{
char buf[1024];
-
+
if (currentElement.length() == 0 || currentExtraTags == 0) return false;
-
+
BLI_strncpy(buf, text, textLength + 1);
currentExtraTags->addTag(currentElement, std::string(buf));
return true;
diff --git a/source/blender/collada/ExtraHandler.h b/source/blender/collada/ExtraHandler.h
index f380c3d6871..4dda862b3cc 100644
--- a/source/blender/collada/ExtraHandler.h
+++ b/source/blender/collada/ExtraHandler.h
@@ -50,31 +50,31 @@ public:
/** Handle the beginning of an element. */
bool elementBegin( const char* elementName, const char** attributes);
-
+
/** Handle the end of an element. */
bool elementEnd(const char* elementName );
-
+
/** Receive the data in text format. */
bool textData(const char* text, size_t textLength);
/** Method to ask, if the current callback handler want to read the data of the given extra element. */
- bool parseElement (
- const char* profileName,
- const unsigned long& elementHash,
+ bool parseElement (
+ const char* profileName,
+ const unsigned long& elementHash,
const COLLADAFW::UniqueId& uniqueId,
COLLADAFW::Object* object);
/** For backwards compatibility with older OpenCollada, new version added object parameter */
- bool parseElement (
- const char* profileName,
- const unsigned long& elementHash,
+ bool parseElement (
+ const char* profileName,
+ const unsigned long& elementHash,
const COLLADAFW::UniqueId& uniqueId);
private:
/** Disable default copy constructor. */
ExtraHandler(const ExtraHandler& pre);
/** Disable default assignment operator. */
const ExtraHandler& operator= ( const ExtraHandler& pre );
-
+
/** Handle to DocumentImporter for interface to extra element data saving. */
DocumentImporter* dimp;
AnimationImporter* aimp;
diff --git a/source/blender/collada/ExtraTags.cpp b/source/blender/collada/ExtraTags.cpp
index ea225d8a4ae..fd249884c3f 100644
--- a/source/blender/collada/ExtraTags.cpp
+++ b/source/blender/collada/ExtraTags.cpp
@@ -50,7 +50,7 @@ bool ExtraTags::isProfile(std::string profile)
bool ExtraTags::addTag(std::string tag, std::string data)
{
tags[tag] = data;
-
+
return true;
}
diff --git a/source/blender/collada/ExtraTags.h b/source/blender/collada/ExtraTags.h
index ad272dcba65..84857622ee8 100644
--- a/source/blender/collada/ExtraTags.h
+++ b/source/blender/collada/ExtraTags.h
@@ -41,35 +41,35 @@ public:
/** Handle the beginning of an element. */
bool addTag(std::string tag, std::string data);
-
+
/** Set given short pointer to value of tag, if it exists. */
bool setData(std::string tag, short *data);
-
+
/** Set given int pointer to value of tag, if it exists. */
bool setData(std::string tag, int *data);
-
+
/** Set given float pointer to value of tag, if it exists. */
bool setData(std::string tag, float *data);
-
+
/** Set given char pointer to value of tag, if it exists. */
bool setData(std::string tag, char *data);
std::string setData(std::string tag, std::string &data);
/** Return true if the extra tags is for specified profile. */
bool isProfile(std::string profile);
-
+
private:
/** Disable default copy constructor. */
ExtraTags(const ExtraTags& pre);
/** Disable default assignment operator. */
const ExtraTags& operator= ( const ExtraTags& pre );
-
+
/** The profile for which the tags are. */
std::string profile;
-
+
/** Map of tag and text pairs. */
std::map<std::string, std::string> tags;
-
+
/** Get text data for tag as an int. */
int asInt(std::string tag, bool *ok);
/** Get text data for tag as a float. */
diff --git a/source/blender/collada/GeometryExporter.cpp b/source/blender/collada/GeometryExporter.cpp
index 87b47353596..65e844cbe50 100644
--- a/source/blender/collada/GeometryExporter.cpp
+++ b/source/blender/collada/GeometryExporter.cpp
@@ -70,14 +70,14 @@ void GeometryExporter::exportGeom(struct Depsgraph *depsgraph, Scene *sce)
}
void GeometryExporter::operator()(Object *ob)
-{
+{
// XXX don't use DerivedMesh, Mesh instead?
-#if 0
+#if 0
DerivedMesh *dm = mesh_get_derived_final(mScene, ob, CD_MASK_BAREMESH);
#endif
bool use_instantiation = this->export_settings->use_object_instantiation;
- Mesh *me = bc_get_mesh_copy(mDepsgraph, mScene,
+ Mesh *me = bc_get_mesh_copy(mDepsgraph, mScene,
ob,
this->export_settings->export_mesh_type,
this->export_settings->apply_modifiers,
@@ -88,7 +88,7 @@ void GeometryExporter::operator()(Object *ob)
std::vector<BCPolygonNormalsIndices> norind;
// Skip if linked geometry was already exported from another reference
- if (use_instantiation &&
+ if (use_instantiation &&
exportedGeometry.find(geom_id) != exportedGeometry.end())
{
return;
@@ -104,15 +104,15 @@ void GeometryExporter::operator()(Object *ob)
// openMesh(geoId, geoName, meshId)
openMesh(geom_id, geom_name);
-
+
// writes <source> for vertex coords
createVertsSource(geom_id, me);
-
+
// writes <source> for normal coords
createNormalsSource(geom_id, me, nor);
bool has_uvs = (bool)CustomData_has_layer(&me->fdata, CD_MTFACE);
-
+
// writes <source> for uv coords if mesh has uv coords
if (has_uvs) {
createTexcoordsSource(geom_id, me);
@@ -144,9 +144,9 @@ void GeometryExporter::operator()(Object *ob)
createPolylist(0, has_uvs, has_color, ob, me, geom_id, norind);
}
}
-
+
closeMesh();
-
+
if (me->flag & ME_TWOSIDED) {
mSW->appendTextBlock("<extra><technique profile=\"MAYA\"><double_sided>1</double_sided></technique></extra>");
}
@@ -175,7 +175,7 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name);
std::vector<Normal> nor;
std::vector<BCPolygonNormalsIndices> norind;
-
+
if (exportedGeometry.find(geom_id) != exportedGeometry.end())
{
return;
@@ -191,15 +191,15 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
// openMesh(geoId, geoName, meshId)
openMesh(geom_id, geom_name);
-
+
// writes <source> for vertex coords
createVertsSource(geom_id, me);
-
+
// writes <source> for normal coords
createNormalsSource(geom_id, me, nor);
bool has_uvs = (bool)CustomData_has_layer(&me->fdata, CD_MTFACE);
-
+
// writes <source> for uv coords if mesh has uv coords
if (has_uvs) {
createTexcoordsSource(geom_id, me);
@@ -220,7 +220,7 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
//createLooseEdgeList(ob, me, geom_id, norind);
- // XXX slow
+ // XXX slow
if (ob->totcol) {
for (int a = 0; a < ob->totcol; a++) {
createPolylist(a, has_uvs, has_color, ob, me, geom_id, norind);
@@ -229,9 +229,9 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
else {
createPolylist(0, has_uvs, has_color, ob, me, geom_id, norind);
}
-
+
closeMesh();
-
+
if (me->flag & ME_TWOSIDED) {
mSW->appendTextBlock("<extra><technique profile=\"MAYA\"><double_sided>1</double_sided></technique></extra>");
}
@@ -250,9 +250,9 @@ void GeometryExporter::createLooseEdgeList(Object *ob,
std::vector<unsigned int> edge_list;
int index;
- // Find all loose edges in Mesh
+ // Find all loose edges in Mesh
// and save vertex indices in edge_list
- for (index = 0; index < totedges; index++)
+ for (index = 0; index < totedges; index++)
{
MEdge *edge = &medges[index];
@@ -273,14 +273,14 @@ void GeometryExporter::createLooseEdgeList(Object *ob,
COLLADASW::InputList &til = lines.getInputList();
-
- // creates <input> in <lines> for vertices
+
+ // creates <input> in <lines> for vertices
COLLADASW::Input input1(COLLADASW::InputSemantic::VERTEX, getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX), 0);
til.push_back(input1);
lines.prepareToAppendValues();
- for (index = 0; index < edges_in_linelist; index++)
+ for (index = 0; index < edges_in_linelist; index++)
{
lines.appendValues(edge_list[2 * index + 1]);
lines.appendValues(edge_list[2 * index]);
@@ -318,7 +318,7 @@ void GeometryExporter::createPolylist(short material_index,
// count faces with this material
for (i = 0; i < totpolys; i++) {
MPoly *p = &mpolys[i];
-
+
if (p->mat_nr == material_index) {
faces_in_polylist++;
vcount_list.push_back(p->totloop);
@@ -330,13 +330,13 @@ void GeometryExporter::createPolylist(short material_index,
fprintf(stderr, "%s: material with index %d is not used.\n", id_name(ob).c_str(), material_index);
return;
}
-
+
Material *ma = ob->totcol ? give_current_material(ob, material_index + 1) : NULL;
COLLADASW::Polylist polylist(mSW);
-
+
// sets count attribute in <polylist>
polylist.setCount(faces_in_polylist);
-
+
// sets material name
if (ma) {
std::string material_id = get_material_id(ma);
@@ -344,18 +344,18 @@ void GeometryExporter::createPolylist(short material_index,
ostr << translate_id(material_id);
polylist.setMaterial(ostr.str());
}
-
+
COLLADASW::InputList &til = polylist.getInputList();
-
- // creates <input> in <polylist> for vertices
+
+ // creates <input> in <polylist> for vertices
COLLADASW::Input input1(COLLADASW::InputSemantic::VERTEX, getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX), 0);
-
+
// creates <input> in <polylist> for normals
COLLADASW::Input input2(COLLADASW::InputSemantic::NORMAL, getUrlBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL), 1);
-
+
til.push_back(input1);
til.push_back(input2);
-
+
// if mesh has uv coords writes <input> for TEXCOORD
int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
int active_uv_index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE)-1;
@@ -387,13 +387,13 @@ void GeometryExporter::createPolylist(short material_index,
map_index++;
}
}
-
+
// sets <vcount>
polylist.setVCountList(vcount_list);
-
+
// performs the actual writing
polylist.prepareToAppendValues();
-
+
// <p>
int texindex = 0;
for (i = 0; i < totpolys; i++) {
@@ -417,7 +417,7 @@ void GeometryExporter::createPolylist(short material_index,
texindex += loop_count;
}
-
+
polylist.finish();
}
@@ -430,7 +430,7 @@ void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me)
#endif
int totverts = me->totvert;
MVert *verts = me->mvert;
-
+
COLLADASW::FloatSourceF source(mSW);
source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION));
source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION) +
@@ -450,7 +450,7 @@ void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me)
for (i = 0; i < totverts; i++) {
source.appendValues(verts[i].co[0], verts[i].co[1], verts[i].co[2]);
}
-
+
source.finish();
}
@@ -502,7 +502,7 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me)
);
}
}
-
+
source.finish();
}
}
@@ -537,20 +537,20 @@ void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me)
int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_MLOOPUV, a);
if (!this->export_settings->active_uv_only || layer_index == active_uv_index) {
MLoopUV *mloops = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, a);
-
+
COLLADASW::FloatSourceF source(mSW);
std::string layer_id = makeTexcoordSourceId(geom_id, a, this->export_settings->active_uv_only);
source.setId(layer_id);
source.setArrayId(layer_id + ARRAY_ID_SUFFIX);
-
+
source.setAccessorCount(totuv);
source.setAccessorStride(2);
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
param.push_back("S");
param.push_back("T");
-
+
source.prepareToAppendValues();
-
+
for (int index = 0; index < totpoly; index++) {
MPoly *mpoly = mpolys+index;
MLoopUV *mloop = mloops+mpoly->loopstart;
@@ -559,7 +559,7 @@ void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me)
mloop[j].uv[1]);
}
}
-
+
source.finish();
}
}
@@ -589,7 +589,7 @@ void GeometryExporter::createNormalsSource(std::string geom_id, Mesh *me, std::v
param.push_back("X");
param.push_back("Y");
param.push_back("Z");
-
+
source.prepareToAppendValues();
std::vector<Normal>::iterator it;
@@ -674,10 +674,10 @@ std::string GeometryExporter::getIdBySemantics(std::string geom_id, COLLADASW::I
COLLADASW::URI GeometryExporter::getUrlBySemantics(std::string geom_id, COLLADASW::InputSemantic::Semantics type, std::string other_suffix)
{
-
+
std::string id(getIdBySemantics(geom_id, type, other_suffix));
return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
-
+
}
COLLADASW::URI GeometryExporter::makeUrl(std::string id)
diff --git a/source/blender/collada/GeometryExporter.h b/source/blender/collada/GeometryExporter.h
index 88420b4ad2f..b0cd650adcd 100644
--- a/source/blender/collada/GeometryExporter.h
+++ b/source/blender/collada/GeometryExporter.h
@@ -96,7 +96,7 @@ public:
Mesh *me,
std::string& geom_id,
std::vector<BCPolygonNormalsIndices>& norind);
-
+
// creates <source> for positions
void createVertsSource(std::string geom_id, Mesh *me);
@@ -112,7 +112,7 @@ public:
void createNormalsSource(std::string geom_id, Mesh *me, std::vector<Normal>& nor);
void create_normals(std::vector<Normal> &nor, std::vector<BCPolygonNormalsIndices> &ind, Mesh *me);
-
+
std::string getIdBySemantics(std::string geom_id, COLLADASW::InputSemantic::Semantics type, std::string other_suffix = "");
std::string makeVertexColorSourceId(std::string& geom_id, char *layer_name);
@@ -121,10 +121,10 @@ public:
COLLADASW::URI makeUrl(std::string id);
void export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb);
-
+
private:
std::set<std::string> exportedGeometry;
-
+
const ExportSettings *export_settings;
Mesh * get_mesh(Scene *sce, Object *ob, int apply_modifiers);
diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp
index d5e3b19e3e7..bb3cebf4cf0 100644
--- a/source/blender/collada/ImageExporter.cpp
+++ b/source/blender/collada/ImageExporter.cpp
@@ -34,7 +34,7 @@ extern "C" {
#include "DNA_image_types.h"
#include "DNA_meshdata_types.h"
-#include "BKE_customdata.h"
+#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
@@ -53,7 +53,7 @@ ImagesExporter::ImagesExporter(COLLADASW::StreamWriter *sw, const ExportSettings
{
}
-void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
+void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
{
std::string name(id_name(image));
std::string translated_name(translate_id(name));
@@ -117,7 +117,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
BLI_cleanup_path(NULL, source_path);
if (use_copies) {
-
+
// This image is already located on the file system.
// But we want to create copies here.
// To move images into the same export directory.
@@ -155,7 +155,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
bool ImagesExporter::hasImages(Scene *sce)
{
LinkNode *node;
-
+
for (node = this->export_settings->export_set; node; node = node->next) {
Object *ob = (Object *)node->link;
diff --git a/source/blender/collada/ImageExporter.h b/source/blender/collada/ImageExporter.h
index f3dd2b336e4..1867c44ac9c 100644
--- a/source/blender/collada/ImageExporter.h
+++ b/source/blender/collada/ImageExporter.h
@@ -45,7 +45,7 @@ class ImagesExporter: COLLADASW::LibraryImages
{
public:
ImagesExporter(COLLADASW::StreamWriter *sw, const ExportSettings *export_settings);
-
+
void exportImages(Scene *sce);
void operator()(Material *ma, Object *ob);
private:
diff --git a/source/blender/collada/InstanceWriter.cpp b/source/blender/collada/InstanceWriter.cpp
index d96d590597a..68842596550 100644
--- a/source/blender/collada/InstanceWriter.cpp
+++ b/source/blender/collada/InstanceWriter.cpp
@@ -45,7 +45,7 @@ void InstanceWriter::add_material_bindings(COLLADASW::BindMaterial& bind_materia
{
for (int a = 0; a < ob->totcol; a++) {
Material *ma = give_current_material(ob, a + 1);
-
+
COLLADASW::InstanceMaterialList& iml = bind_material.getInstanceMaterialList();
if (ma) {
@@ -54,11 +54,11 @@ void InstanceWriter::add_material_bindings(COLLADASW::BindMaterial& bind_materia
std::ostringstream ostr;
ostr << matid;
COLLADASW::InstanceMaterial im(ostr.str(), COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid));
-
+
// create <bind_vertex_input> for each uv map
Mesh *me = (Mesh *)ob->data;
int totlayer = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
-
+
int map_index = 0;
int active_uv_index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE) -1;
for (int b = 0; b < totlayer; b++) {
@@ -67,7 +67,7 @@ void InstanceWriter::add_material_bindings(COLLADASW::BindMaterial& bind_materia
im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", map_index++));
}
}
-
+
iml.push_back(im);
}
}
diff --git a/source/blender/collada/LightExporter.cpp b/source/blender/collada/LightExporter.cpp
index d17941574d7..11377e06ce8 100644
--- a/source/blender/collada/LightExporter.cpp
+++ b/source/blender/collada/LightExporter.cpp
@@ -54,9 +54,9 @@ LightsExporter::LightsExporter(COLLADASW::StreamWriter *sw, const ExportSettings
void LightsExporter::exportLights(Scene *sce)
{
openLibrary();
-
+
forEachLampObjectInExportSet(sce, *this, this->export_settings->export_set);
-
+
closeLibrary();
}
@@ -67,11 +67,11 @@ void LightsExporter::operator()(Object *ob)
std::string la_name(id_name(la));
COLLADASW::Color col(la->r * la->energy, la->g * la->energy, la->b * la->energy);
float d, constatt, linatt, quadatt;
-
+
d = la->dist;
-
+
constatt = 1.0f;
-
+
if (la->falloff_type == LA_FALLOFF_INVLINEAR) {
linatt = 1.0f / d;
quadatt = 0.0f;
@@ -80,7 +80,7 @@ void LightsExporter::operator()(Object *ob)
linatt = 0.0f;
quadatt = 1.0f / (d * d);
}
-
+
// sun
if (la->type == LA_SUN) {
COLLADASW::DirectionalLight cla(mSW, la_id, la_name);
@@ -130,7 +130,7 @@ void LightsExporter::operator()(Object *ob)
exportBlenderProfile(cla, la);
addLight(cla);
}
-
+
}
bool LightsExporter::exportBlenderProfile(COLLADASW::Light &cla, Lamp *la)
@@ -164,6 +164,6 @@ bool LightsExporter::exportBlenderProfile(COLLADASW::Light &cla, Lamp *la)
cla.addExtraTechniqueParameter("blender", "area_size", la->area_size);
cla.addExtraTechniqueParameter("blender", "area_sizey", la->area_sizey);
cla.addExtraTechniqueParameter("blender", "area_sizez", la->area_sizez);
-
+
return true;
}
diff --git a/source/blender/collada/MaterialExporter.h b/source/blender/collada/MaterialExporter.h
index ef44bf8a03e..e830a433432 100644
--- a/source/blender/collada/MaterialExporter.h
+++ b/source/blender/collada/MaterialExporter.h
@@ -65,7 +65,7 @@ class ForEachMaterialFunctor
Functor *f;
public:
ForEachMaterialFunctor(Functor*f) : f(f) {}
-
+
void operator ()(Object *ob)
{
int a;
diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp
index add4a93bccd..e95f8ed0888 100644
--- a/source/blender/collada/MeshImporter.cpp
+++ b/source/blender/collada/MeshImporter.cpp
@@ -154,7 +154,7 @@ void UVDataWrapper::getUV(int uv_index, float *uv)
if (values->empty()) return;
uv[0] = (*values)[uv_index * stride];
uv[1] = (*values)[uv_index * stride + 1];
-
+
}
break;
case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE:
@@ -163,7 +163,7 @@ void UVDataWrapper::getUV(int uv_index, float *uv)
if (values->empty()) return;
uv[0] = (float)(*values)[uv_index * stride];
uv[1] = (float)(*values)[uv_index * stride + 1];
-
+
}
break;
case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN:
@@ -208,7 +208,7 @@ void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol)
}
MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce, ViewLayer *view_layer):
- unitconverter(unitconv),
+ unitconverter(unitconv),
scene(sce),
view_layer(view_layer),
armature_importer(arm) {
@@ -320,7 +320,7 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su
return false;
}
}
-
+
return true;
}
@@ -360,7 +360,7 @@ bool MeshImporter::primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp) {
int normals_count = mp->getNormalIndices().getCount();
if (normals_count > 0) {
int index_count = mp->getPositionIndices().getCount();
- if (index_count == normals_count)
+ if (index_count == normals_count)
has_useable_normals = true;
else {
fprintf(stderr,
@@ -391,7 +391,7 @@ bool MeshImporter::primitive_has_faces(COLLADAFW::MeshPrimitive *mp) {
break;
}
default: {
- has_faces = false;
+ has_faces = false;
break;
}
}
@@ -435,7 +435,7 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
size_t prim_poly_count = mpvc->getFaceCount();
size_t prim_loop_count = 0;
- for (int index=0; index < prim_poly_count; index++)
+ for (int index=0; index < prim_poly_count; index++)
{
int vcount = get_vertex_count(mpvc, index);
if (vcount > 0) {
@@ -547,7 +547,7 @@ unsigned int MeshImporter::get_loose_edge_count(COLLADAFW::Mesh *mesh) {
// This functin is copied from source/blender/editors/mesh/mesh_data.c
//
// TODO: (As discussed with sergey-) :
-// Maybe move this function to blenderkernel/intern/mesh.c
+// Maybe move this function to blenderkernel/intern/mesh.c
// and add definition to BKE_mesh.c
// =================================================================
void MeshImporter::mesh_add_edges(Mesh *mesh, int len)
@@ -582,7 +582,7 @@ void MeshImporter::mesh_add_edges(Mesh *mesh, int len)
// =================================================================
// Read all loose edges.
-// Important: This function assumes that all edges from existing
+// Important: This function assumes that all edges from existing
// faces have allready been generated and added to me->medge
// So this function MUST be called after read_faces() (see below)
// =================================================================
@@ -593,21 +593,21 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
unsigned int face_edge_count = me->totedge;
/* unsigned int total_edge_count = loose_edge_count + face_edge_count; */ /* UNUSED */
-
+
mesh_add_edges(me, loose_edge_count);
MEdge *med = me->medge + face_edge_count;
COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives();
for (int i = 0; i < prim_arr.getCount(); i++) {
-
+
COLLADAFW::MeshPrimitive *mp = prim_arr[i];
int type = mp->getPrimitiveType();
if (type == COLLADAFW::MeshPrimitive::LINES) {
unsigned int edge_count = mp->getFaceCount();
unsigned int *indices = mp->getPositionIndices().getData();
-
+
for (int j = 0; j < edge_count; j++, med++) {
med->bweight = 0;
med->crease = 0;
@@ -624,7 +624,7 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
// =======================================================================
// Read all faces from TRIANGLES, TRIANGLE_FANS, POLYLIST, POLYGON
-// Important: This function MUST be called before read_lines()
+// Important: This function MUST be called before read_lines()
// Otherwise we will loose all edges from faces (see read_lines() above)
//
// TODO: import uv set names
@@ -632,7 +632,7 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
{
unsigned int i;
-
+
allocate_poly_data(collada_mesh, me);
UVDataWrapper uvs(collada_mesh->getUVCoords());
@@ -648,7 +648,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
COLLADAFW::MeshVertexData& nor = collada_mesh->getNormals();
for (i = 0; i < prim_arr.getCount(); i++) {
-
+
COLLADAFW::MeshPrimitive *mp = prim_arr[i];
// faces
@@ -661,7 +661,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
bool mp_has_faces = primitive_has_faces(mp);
int collada_meshtype = mp->getPrimitiveType();
-
+
// since we cannot set mpoly->mat_nr here, we store a portion of me->mpoly in Primitive
Primitive prim = {mpoly, 0};
@@ -688,7 +688,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
mpoly->flag |= ME_SMOOTH;
normal_indices++;
}
-
+
mpoly++;
mloop += 3;
loop_index += 3;
@@ -798,7 +798,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i, int stride)
{
i *= stride;
-
+
switch (arr.getType()) {
case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT:
{
@@ -888,7 +888,7 @@ static bool bc_has_same_material_configuration(Object *ob1, Object *ob2)
{
if (ob1->totcol != ob2->totcol) return false; // not same number of materials
if (ob1->totcol == 0) return false; // no material at all
-
+
for (int index=0; index < ob1->totcol; index++) {
if (ob1->matbits[index] != ob2->matbits[index]) return false; // shouldn't happen
if (ob1->matbits[index] == 0) return false; // shouldn't happen
@@ -1019,34 +1019,34 @@ void MeshImporter::assign_material_to_geom(
short mat_index)
{
const COLLADAFW::UniqueId& ma_uid = cmaterial.getReferencedMaterial();
-
+
// do we know this material?
if (uid_material_map.find(ma_uid) == uid_material_map.end()) {
-
+
fprintf(stderr, "Cannot find material by UID.\n");
return;
}
// first time we get geom_uid, ma_uid pair. Save for later check.
materials_mapped_to_geom.insert(std::pair<COLLADAFW::UniqueId, COLLADAFW::UniqueId>(*geom_uid, ma_uid));
-
+
Material *ma = uid_material_map[ma_uid];
// Attention! This temporaly assigns material to object on purpose!
// See note above.
ob->actcol=0;
assign_material(G.main, ob, ma, mat_index + 1, BKE_MAT_ASSIGN_OBJECT);
-
+
MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[*geom_uid];
COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId();
-
+
// assign material indices to mesh faces
if (mat_prim_map.find(mat_id) != mat_prim_map.end()) {
-
+
std::vector<Primitive>& prims = mat_prim_map[mat_id];
-
+
std::vector<Primitive>::iterator it;
-
+
for (it = prims.begin(); it != prims.end(); it++) {
Primitive& prim = *it;
MPoly *mpoly = prim.mpoly;
@@ -1055,7 +1055,7 @@ void MeshImporter::assign_material_to_geom(
mpoly->mat_nr = mat_index;
}
}
- }
+ }
}
Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom,
@@ -1063,19 +1063,19 @@ Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::Insta
std::map<COLLADAFW::UniqueId, Material *>& uid_material_map)
{
const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId();
-
+
// check if node instanciates controller or geometry
if (isController) {
-
+
geom_uid = armature_importer->get_geometry_uid(*geom_uid);
-
+
if (!geom_uid) {
fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n");
return NULL;
}
}
else {
-
+
if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) {
// this could happen if a mesh was not created
// (e.g. if it contains unsupported geometry)
@@ -1084,11 +1084,11 @@ Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::Insta
}
}
if (!uid_mesh_map[*geom_uid]) return NULL;
-
+
// name Object
const std::string& id = node->getName().size() ? node->getName() : node->getOriginalId();
const char *name = (id.length()) ? id.c_str() : NULL;
-
+
// add object
Object *ob = bc_add_object(scene, view_layer, OB_MESH, name);
bc_set_mark(ob); // used later for material assignement optimization
@@ -1097,7 +1097,7 @@ Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::Insta
// store object pointer for ArmatureImporter
uid_object_map[*geom_uid] = ob;
imported_objects.push_back(ob);
-
+
// replace ob->data freeing the old one
Mesh *old_mesh = (Mesh *)ob->data;
Mesh *new_mesh = uid_mesh_map[*geom_uid];
@@ -1110,10 +1110,10 @@ Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::Insta
COLLADAFW::MaterialBindingArray& mat_array =
geom->getMaterialBindings();
-
+
// loop through geom's materials
for (unsigned int i = 0; i < mat_array.getCount(); i++) {
-
+
if (mat_array[i].getReferencedMaterial().isValid()) {
assign_material_to_geom(
mat_array[i], uid_material_map, ob, geom_uid,
@@ -1136,14 +1136,14 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom)
fprintf(stderr, "Mesh type %s is not supported\n", bc_geomTypeToStr(geom->getType()));
return true;
}
-
+
COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh *)geom;
-
+
if (!is_nice_mesh(mesh)) {
fprintf(stderr, "Ignoring mesh %s\n", bc_get_dae_name(mesh).c_str());
return true;
}
-
+
const std::string& str_geom_id = mesh->getName().size() ? mesh->getName() : mesh->getOriginalId();
Mesh *me = BKE_mesh_add(G.main, (char *)str_geom_id.c_str());
id_us_min(&me->id); // is already 1 here, but will be set later in BKE_mesh_assign_object
@@ -1152,7 +1152,7 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom)
// mesh_geom_map needed to map mesh to its geometry name (for shape key naming)
this->uid_mesh_map[mesh->getUniqueId()] = me;
this->mesh_geom_map[std::string(me->id.name)] = str_geom_id;
-
+
read_vertices(mesh, me);
read_polys(mesh, me);
diff --git a/source/blender/collada/MeshImporter.h b/source/blender/collada/MeshImporter.h
index 09b3005d795..c98126916d7 100644
--- a/source/blender/collada/MeshImporter.h
+++ b/source/blender/collada/MeshImporter.h
@@ -109,7 +109,7 @@ private:
typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive> > MaterialIdPrimitiveArrayMap;
std::map<COLLADAFW::UniqueId, MaterialIdPrimitiveArrayMap> geom_uid_mat_mapping_map; // crazy name!
std::multimap<COLLADAFW::UniqueId, COLLADAFW::UniqueId> materials_mapped_to_geom; //< materials that have already been mapped to a geometry. A pair of geom uid and mat uid, one geometry can have several materials
-
+
bool set_poly_indices(MPoly *mpoly,
MLoop *mloop,
int loop_index,
@@ -135,7 +135,7 @@ private:
bool is_nice_mesh(COLLADAFW::Mesh *mesh);
void read_vertices(COLLADAFW::Mesh *mesh, Mesh *me);
-
+
bool primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp);
bool primitive_has_faces(COLLADAFW::MeshPrimitive *mp);
@@ -165,7 +165,7 @@ public:
virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid);
virtual Mesh *get_mesh_by_geom_uid(const COLLADAFW::UniqueId& geom_uid);
-
+
void optimize_material_assignements();
void assign_material_to_geom(
@@ -173,8 +173,8 @@ public:
std::map<COLLADAFW::UniqueId, Material*>& uid_material_map,
Object *ob, const COLLADAFW::UniqueId *geom_uid,
short mat_index);
-
-
+
+
Object *create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom,
bool isController,
std::map<COLLADAFW::UniqueId, Material*>& uid_material_map);
diff --git a/source/blender/collada/SceneExporter.cpp b/source/blender/collada/SceneExporter.cpp
index 62ab4af5d1a..d909203488e 100644
--- a/source/blender/collada/SceneExporter.cpp
+++ b/source/blender/collada/SceneExporter.cpp
@@ -50,7 +50,7 @@ void SceneExporter::exportScene(bContext *C, Depsgraph *depsgraph, Scene *sce)
}
void SceneExporter::exportHierarchy(bContext *C, Depsgraph *depsgraph, Scene *sce)
-{
+{
LinkNode *node;
std::vector<Object *> base_objects;
@@ -59,7 +59,7 @@ void SceneExporter::exportHierarchy(bContext *C, Depsgraph *depsgraph, Scene *sc
Object *ob = (Object *) node->link;
ob->id.tag |= LIB_TAG_DOIT;
}
-
+
// Now find all exportable base ojects (highest in export hierarchy)
for (node = this->export_settings->export_set; node; node = node->next) {
Object *ob = (Object *) node->link;
@@ -203,17 +203,17 @@ void SceneExporter::writeNodes(bContext *C, Depsgraph *depsgraph, Object *ob, Sc
colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"rot_error",con->rot_error);
colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"tar_space",con->tarspace);
colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"lin_error",con->lin_error);
-
- //not ideal: add the target object name as another parameter.
+
+ //not ideal: add the target object name as another parameter.
//No real mapping in the .dae
//Need support for multiple target objects also.
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
if (cti && cti->get_constraint_targets) {
-
+
bConstraintTarget *ct;
Object *obtar;
-
+
cti->get_constraint_targets(con, &targets);
for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
diff --git a/source/blender/collada/SceneExporter.h b/source/blender/collada/SceneExporter.h
index 24a2dcf08c8..91f98063020 100644
--- a/source/blender/collada/SceneExporter.h
+++ b/source/blender/collada/SceneExporter.h
@@ -102,7 +102,7 @@ private:
friend class ArmatureExporter;
void exportHierarchy(bContext *C, struct Depsgraph *depsgraph, Scene *sce);
void writeNodes(bContext *C, struct Depsgraph *depsgraph, Object *ob, Scene *sce);
-
+
ArmatureExporter *arm_exporter;
const ExportSettings *export_settings;
};
diff --git a/source/blender/collada/SkinInfo.cpp b/source/blender/collada/SkinInfo.cpp
index a2cb8237d08..7ec3f04aabf 100644
--- a/source/blender/collada/SkinInfo.cpp
+++ b/source/blender/collada/SkinInfo.cpp
@@ -121,7 +121,7 @@ void SkinInfo::borrow_skin_controller_data(const COLLADAFW::SkinControllerData *
unit_converter->dae_matrix_to_mat4_(bind_shape_matrix, skin->getBindShapeMatrix());
}
-
+
void SkinInfo::free()
{
joints_per_vertex.releaseMemory();
@@ -199,7 +199,7 @@ const COLLADAFW::UniqueId& SkinInfo::get_controller_uid()
}
// check if this skin controller references a joint or any descendant of it
-//
+//
// some nodes may not be referenced by SkinController,
// in this case to determine if the node belongs to this armature,
// we need to search down the tree
@@ -259,9 +259,9 @@ void SkinInfo::link_armature(bContext *C, Object *ob, std::map<COLLADAFW::Unique
// skip joints that have invalid UID
if ((*it).joint_uid == COLLADAFW::UniqueId::INVALID) continue;
-
+
// name group by joint node name
-
+
if (joint_by_uid.find((*it).joint_uid) != joint_by_uid.end()) {
name = bc_get_joint_name(joint_by_uid[(*it).joint_uid]);
}
diff --git a/source/blender/collada/SkinInfo.h b/source/blender/collada/SkinInfo.h
index a399bff9e3c..fdfee0a943a 100644
--- a/source/blender/collada/SkinInfo.h
+++ b/source/blender/collada/SkinInfo.h
@@ -88,7 +88,7 @@ public:
void transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray& src, COLLADAFW::UIntValuesArray& dest);
void borrow_skin_controller_data(const COLLADAFW::SkinControllerData* skin);
-
+
void free();
// using inverse bind matrices to construct armature
@@ -110,7 +110,7 @@ public:
const COLLADAFW::UniqueId& get_controller_uid();
// check if this skin controller references a joint or any descendant of it
- //
+ //
// some nodes may not be referenced by SkinController,
// in this case to determine if the node belongs to this armature,
// we need to search down the tree
diff --git a/source/blender/collada/TransformReader.cpp b/source/blender/collada/TransformReader.cpp
index 7f742be7e30..bf9c34153b7 100644
--- a/source/blender/collada/TransformReader.cpp
+++ b/source/blender/collada/TransformReader.cpp
@@ -54,7 +54,7 @@ void TransformReader::get_node_mat(
float copy[4][4];
unit_m4(mat);
-
+
for (unsigned int i = 0; i < node->getTransformations().getCount(); i++) {
COLLADAFW::Transformation *tm = node->getTransformations()[i];
@@ -87,11 +87,11 @@ void TransformReader::get_node_mat(
copy_m4_m4(copy, mat);
mul_m4_m4m4(mat, copy, cur);
-
+
if (animation_map) {
// AnimationList that drives this Transformation
const COLLADAFW::UniqueId& anim_list_id = tm->getAnimationList();
-
+
// store this so later we can link animation data with ob
Animation anim = {ob, node, tm};
(*animation_map)[anim_list_id] = anim;
diff --git a/source/blender/collada/TransformReader.h b/source/blender/collada/TransformReader.h
index 08bb17ccac1..6544aa1c040 100644
--- a/source/blender/collada/TransformReader.h
+++ b/source/blender/collada/TransformReader.h
@@ -19,7 +19,7 @@
*
* ***** END GPL LICENSE BLOCK *****
*/
-
+
/** \file TransformReader.h
* \ingroup collada
*/
diff --git a/source/blender/collada/TransformWriter.cpp b/source/blender/collada/TransformWriter.cpp
index 1fb54e99147..9f75a604f96 100644
--- a/source/blender/collada/TransformWriter.cpp
+++ b/source/blender/collada/TransformWriter.cpp
@@ -119,7 +119,7 @@ void TransformWriter::add_node_transform_ob(COLLADASW::Node& node, Object *ob,
{
float loc[3], rot[3], scale[3];
bc_decompose(f_obmat, loc, rot, NULL, scale);
- add_transform(node, loc, rot, scale);
+ add_transform(node, loc, rot, scale);
break;
}
}
diff --git a/source/blender/collada/collada_internal.h b/source/blender/collada/collada_internal.h
index 299e13326ce..1eac5b68778 100644
--- a/source/blender/collada/collada_internal.h
+++ b/source/blender/collada/collada_internal.h
@@ -51,7 +51,7 @@ private:
float y_up_mat4[4][4];
float z_up_mat4[4][4];
float scale_mat4[4][4];
-
+
public:
enum UnitSystem {
@@ -66,11 +66,11 @@ public:
void read_asset(const COLLADAFW::FileInfo *asset);
void convertVector3(COLLADABU::Math::Vector3 &vec, float *v);
-
+
UnitConverter::UnitSystem isMetricSystem(void);
-
+
float getLinearMeter(void);
-
+
// TODO need also for angle conversion, time conversion...
void dae_matrix_to_mat4_(float out[4][4], const COLLADABU::Math::Matrix4& in);
diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp
index bf441effc81..945dda68745 100644
--- a/source/blender/collada/collada_utils.cpp
+++ b/source/blender/collada/collada_utils.cpp
@@ -77,7 +77,7 @@ float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned in
if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT)
return array.getFloatValues()->getData()[index];
- else
+ else
return array.getDoubleValues()->getData()[index];
}
@@ -85,10 +85,10 @@ float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned in
int bc_test_parent_loop(Object *par, Object *ob)
{
/* test if 'ob' is a parent somewhere in par's parents */
-
+
if (par == NULL) return 0;
if (ob == par) return 1;
-
+
return bc_test_parent_loop(par->parent, ob);
}
@@ -117,7 +117,7 @@ int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space)
mul_m4_m4m4(mat, par->obmat, ob->obmat);
copy_m4_m4(ob->obmat, mat);
}
-
+
// apply child obmat (i.e. decompose it into rot/loc/size)
BKE_object_apply_mat4(ob, ob->obmat, 0, 0);
@@ -231,7 +231,7 @@ Object *bc_get_assigned_armature(Object *ob)
// IMPORTANT: This function expects that
// all exported objects have set:
// ob->id.tag & LIB_TAG_DOIT
-Object *bc_get_highest_selected_ancestor_or_self(LinkNode *export_set, Object *ob)
+Object *bc_get_highest_selected_ancestor_or_self(LinkNode *export_set, Object *ob)
{
Object *ancestor = ob;
while (ob->parent && bc_is_marked(ob->parent)) {
@@ -256,7 +256,7 @@ bool bc_is_in_Export_set(LinkNode *export_set, Object *ob)
bool bc_has_object_type(LinkNode *export_set, short obtype)
{
LinkNode *node;
-
+
for (node = export_set; node; node = node->next) {
Object *ob = (Object *)node->link;
/* XXX - why is this checking for ob->data? - we could be looking for empties */
@@ -290,7 +290,7 @@ void bc_bubble_sort_by_Object_name(LinkNode *export_set)
for (node = export_set; node->next && !sorted; node = node->next) {
sorted = true;
-
+
LinkNode *current;
for (current = export_set; current->next; current = current->next) {
Object *a = (Object *)current->link;
@@ -301,12 +301,12 @@ void bc_bubble_sort_by_Object_name(LinkNode *export_set)
current->next->link = a;
sorted = false;
}
-
+
}
}
}
-/* Check if a bone is the top most exportable bone in the bone hierarchy.
+/* Check if a bone is the top most exportable bone in the bone hierarchy.
* When deform_bones_only == false, then only bones with NO parent
* can be root bones. Otherwise the top most deform bones in the hierarchy
* are root bones.
@@ -367,13 +367,13 @@ void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene)
BKE_object_apply_mat4(ob, ob->obmat, 0, 0);
}
-void bc_match_scale(std::vector<Object *> *objects_done,
+void bc_match_scale(std::vector<Object *> *objects_done,
UnitConverter &bc_unit,
bool scale_to_scene)
{
for (std::vector<Object *>::iterator it = objects_done->begin();
it != objects_done->end();
- ++it)
+ ++it)
{
Object *ob = *it;
if (ob -> parent == NULL) {
@@ -781,7 +781,7 @@ float bc_get_property(Bone *bone, std::string key, float def)
/**
* Read a custom bone property and convert to matrix
* Return true if conversion was succesfull
-*
+*
* Return false if:
* - the property does not exist
* - is not an array of size 16
diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h
index 89765375afb..20c569834d8 100644
--- a/source/blender/collada/collada_utils.h
+++ b/source/blender/collada/collada_utils.h
@@ -92,8 +92,8 @@ extern void bc_bubble_sort_by_Object_name(LinkNode *export_set);
extern bool bc_is_root_bone(Bone *aBone, bool deform_bones_only);
extern int bc_get_active_UVLayer(Object *ob);
-extern std::string bc_replace_string(std::string data, const std::string& pattern, const std::string& replacement);
-extern std::string bc_url_encode(std::string data);
+extern std::string bc_replace_string(std::string data, const std::string& pattern, const std::string& replacement);
+extern std::string bc_url_encode(std::string data);
extern void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene);
extern void bc_match_scale(std::vector<Object *> *objects_done, UnitConverter &unit_converter, bool scale_to_scene);
@@ -135,8 +135,8 @@ class BCPolygonNormalsIndices
normal_indices.push_back(index);
}
- unsigned int operator[](unsigned int i) {
- return normal_indices[i];
+ unsigned int operator[](unsigned int i) {
+ return normal_indices[i];
}
};
diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c
index 92048f32a28..e5f82ae09c8 100644
--- a/source/blender/datatoc/datatoc_icon.c
+++ b/source/blender/datatoc/datatoc_icon.c
@@ -386,7 +386,7 @@ int main(int argc, char **argv)
{
const char *path_src;
const char *file_dst;
-
+
if (argc < 3) {
printf("Usage: datatoc_icon <dir_icons> <data_icon_to.png>\n");
diff --git a/source/blender/ikplugin/CMakeLists.txt b/source/blender/ikplugin/CMakeLists.txt
index 8991e113410..eb34e50715e 100644
--- a/source/blender/ikplugin/CMakeLists.txt
+++ b/source/blender/ikplugin/CMakeLists.txt
@@ -25,7 +25,7 @@
remove_extra_strict_flags()
-set(INC
+set(INC
.
../blenkernel
../blenlib
diff --git a/source/blender/ikplugin/intern/ikplugin_api.c b/source/blender/ikplugin/intern/ikplugin_api.c
index 216e60f5c5c..791b74e4bc9 100644
--- a/source/blender/ikplugin/intern/ikplugin_api.c
+++ b/source/blender/ikplugin/intern/ikplugin_api.c
@@ -105,7 +105,7 @@ void BIK_execute_tree(struct Depsgraph *depsgraph, struct Scene *scene, Object *
plugin->execute_tree_func(depsgraph, scene, ob, pchan, ctime);
}
-void BIK_release_tree(struct Scene *scene, Object *ob, float ctime)
+void BIK_release_tree(struct Scene *scene, Object *ob, float ctime)
{
IKPlugin *plugin = get_plugin(ob->pose);
diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp
index af303556090..9ad98755be1 100644
--- a/source/blender/ikplugin/intern/itasc_plugin.cpp
+++ b/source/blender/ikplugin/intern/itasc_plugin.cpp
@@ -881,7 +881,7 @@ static int convert_channels(struct Depsgraph *depsgraph, IK_Scene *ikscene, Pose
ikchan->owner = ikscene->blArmature;
// the constraint and channels must be applied before we build the iTaSC scene,
- // this is because some of the pose data (e.g. pose head) don't have corresponding
+ // this is because some of the pose data (e.g. pose head) don't have corresponding
// joint angles and can't be applied to the iTaSC armature dynamically
if (!(pchan->flag & POSE_DONE))
BKE_pose_where_is_bone(depsgraph, ikscene->blscene, ikscene->blArmature, pchan, ctime, 1);
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index 87b60543853..2c123fdbf7e 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -59,7 +59,7 @@
static void composite_get_from_context(const bContext *C, bNodeTreeType *UNUSED(treetype), bNodeTree **r_ntree, ID **r_id, ID **r_from)
{
Scene *scene = CTX_data_scene(C);
-
+
*r_from = NULL;
*r_id = &scene->id;
*r_ntree = scene->nodetree;
@@ -83,7 +83,7 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
static void free_node_cache(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock;
-
+
for (sock = node->outputs.first; sock; sock = sock->next) {
if (sock->cache) {
sock->cache = NULL;
@@ -103,15 +103,15 @@ static void localize(bNodeTree *UNUSED(localtree), bNodeTree *ntree)
{
bNode *node;
bNodeSocket *sock;
-
+
for (node = ntree->nodes.first; node; node = node->next) {
/* ensure new user input gets handled ok */
node->need_exec = 0;
node->new_node->original = node;
-
+
/* move over the compbufs */
/* right after ntreeCopyTree() oldsock pointers are valid */
-
+
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (node->id) {
if (node->flag & NODE_DO_OUTPUT)
@@ -120,7 +120,7 @@ static void localize(bNodeTree *UNUSED(localtree), bNodeTree *ntree)
node->new_node->id = NULL;
}
}
-
+
for (sock = node->outputs.first; sock; sock = sock->next) {
sock->new_sock->cache = sock->cache;
sock->cache = NULL;
@@ -138,10 +138,10 @@ static void local_merge(bNodeTree *localtree, bNodeTree *ntree)
{
bNode *lnode;
bNodeSocket *lsock;
-
+
/* move over the compbufs and previews */
BKE_node_preview_merge_tree(ntree, localtree, true);
-
+
for (lnode = localtree->nodes.first; lnode; lnode = lnode->next) {
if (ntreeNodeExists(ntree, lnode->new_node)) {
if (ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
@@ -161,7 +161,7 @@ static void local_merge(bNodeTree *localtree, bNodeTree *ntree)
lnode->new_node->storage = BKE_tracking_distortion_copy(lnode->storage);
}
}
-
+
for (lsock = lnode->outputs.first; lsock; lsock = lsock->next) {
if (ntreeOutputExists(lnode->new_node, lsock->new_sock)) {
lsock->new_sock->cache = lsock->cache;
@@ -176,9 +176,9 @@ static void local_merge(bNodeTree *localtree, bNodeTree *ntree)
static void update(bNodeTree *ntree)
{
ntreeSetOutput(ntree);
-
+
ntree_update_reroute_nodes(ntree);
-
+
if (ntree->update & NTREE_UPDATE_NODES) {
/* clean up preview cache, in case nodes have been removed */
BKE_node_preview_remove_unused(ntree);
@@ -187,12 +187,12 @@ static void update(bNodeTree *ntree)
static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode)
{
- /* Composite node will only show previews for input classes
- * by default, other will be hidden
+ /* Composite node will only show previews for input classes
+ * by default, other will be hidden
* but can be made visible with the show_preview option */
if (bnode->typeinfo->nclass != NODE_CLASS_INPUT) {
bnode->flag &= ~NODE_PREVIEW;
- }
+ }
}
bNodeTreeType *ntreeType_Composite;
@@ -200,13 +200,13 @@ bNodeTreeType *ntreeType_Composite;
void register_node_tree_type_cmp(void)
{
bNodeTreeType *tt = ntreeType_Composite = MEM_callocN(sizeof(bNodeTreeType), "compositor node tree type");
-
+
tt->type = NTREE_COMPOSIT;
strcpy(tt->idname, "CompositorNodeTree");
strcpy(tt->ui_name, "Compositing");
tt->ui_icon = 0; /* defined in drawnode.c */
strcpy(tt->ui_description, "Compositing nodes");
-
+
tt->free_cache = free_cache;
tt->free_node_cache = free_node_cache;
tt->foreach_nodeclass = foreach_nodeclass;
@@ -216,9 +216,9 @@ void register_node_tree_type_cmp(void)
tt->update = update;
tt->get_from_context = composite_get_from_context;
tt->node_add_init = composite_node_add_init;
-
+
tt->ext.srna = &RNA_CompositorNodeTree;
-
+
ntreeTypeAdd(tt);
}
diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.c
index faf3d994bf9..022794e8d42 100644
--- a/source/blender/nodes/composite/node_composite_util.c
+++ b/source/blender/nodes/composite/node_composite_util.c
@@ -52,7 +52,7 @@ void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node)
void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
-
+
ntype->poll = cmp_node_poll_default;
ntype->updatefunc = cmp_node_update_default;
ntype->insert_link = node_insert_link_default;
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c b/source/blender/nodes/composite/nodes/node_composite_bokehblur.c
index 503d1ad9a9d..acb5d100b0a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.c
@@ -60,6 +60,6 @@ void register_node_type_cmp_bokehblur(void)
cmp_node_type_base(&ntype, CMP_NODE_BOKEHBLUR, "Bokeh Blur", NODE_CLASS_OP_FILTER, 0);
node_type_socket_templates(&ntype, cmp_node_bokehblur_in, cmp_node_bokehblur_out);
node_type_init(&ntype, node_composit_init_bokehblur);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c b/source/blender/nodes/composite/nodes/node_composite_bokehimage.c
index 1473212f8e1..04cdd3367e6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.c
@@ -54,7 +54,7 @@ static void node_composit_init_bokehimage(bNodeTree *UNUSED(ntree), bNode *node)
void register_node_type_cmp_bokehimage(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_BOKEHIMAGE, "Bokeh Image", NODE_CLASS_INPUT, NODE_PREVIEW);
node_type_socket_templates(&ntype, NULL, cmp_node_bokehimage_out);
node_type_init(&ntype, node_composit_init_bokehimage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.c b/source/blender/nodes/composite/nodes/node_composite_boxmask.c
index 2a47c58c33f..611cf323873 100644
--- a/source/blender/nodes/composite/nodes/node_composite_boxmask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.c
@@ -32,7 +32,7 @@
#include "../node_composite_util.h"
-/* **************** SCALAR MATH ******************** */
+/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate cmp_node_boxmask_in[] = {
{ SOCK_FLOAT, 1, N_("Mask"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_FLOAT, 1, N_("Value"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.c b/source/blender/nodes/composite/nodes/node_composite_brightness.c
index 845e9e20c81..d44b8cd63cb 100644
--- a/source/blender/nodes/composite/nodes/node_composite_brightness.c
+++ b/source/blender/nodes/composite/nodes/node_composite_brightness.c
@@ -54,7 +54,7 @@ static void node_composit_init_brightcontrast(bNodeTree *UNUSED(ntree), bNode *n
void register_node_type_cmp_brightcontrast(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, cmp_node_brightcontrast_in, cmp_node_brightcontrast_out);
node_type_init(&ntype, node_composit_init_brightcontrast);
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c b/source/blender/nodes/composite/nodes/node_composite_colorSpill.c
index a6a7f3aa5a3..641026c3aad 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c
+++ b/source/blender/nodes/composite/nodes/node_composite_colorSpill.c
@@ -57,7 +57,7 @@ static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node
void register_node_type_cmp_color_spill(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_COLOR_SPILL, "Color Spill", NODE_CLASS_MATTE, 0);
node_type_socket_templates(&ntype, cmp_node_color_spill_in, cmp_node_color_spill_out);
node_type_init(&ntype, node_composit_init_color_spill);
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c b/source/blender/nodes/composite/nodes/node_composite_colorbalance.c
index 8370ace8cb3..f9ad8c0ea5f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.c
@@ -52,7 +52,7 @@ void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeColorBalance *n = node->storage;
int c;
-
+
for (c = 0; c < 3; ++c) {
n->slope[c] = (2.0f - n->lift[c]) * n->gain[c];
n->offset[c] = (n->lift[c] - 1.0f) * n->gain[c];
@@ -64,7 +64,7 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeColorBalance *n = node->storage;
int c;
-
+
for (c = 0; c < 3; ++c) {
float d = n->slope[c] + n->offset[c];
n->lift[c] = (d != 0.0f ? n->slope[c] + 2.0f * n->offset[c] / d : 0.0f);
diff --git a/source/blender/nodes/composite/nodes/node_composite_common.c b/source/blender/nodes/composite/nodes/node_composite_common.c
index 57ec0f1c7ad..de97a5beac3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_common.c
+++ b/source/blender/nodes/composite/nodes/node_composite_common.c
@@ -43,7 +43,7 @@
void register_node_type_cmp_group(void)
{
static bNodeType ntype;
-
+
/* NB: cannot use sh_node_type_base for node group, because it would map the node type
* to the shared NODE_GROUP integer type id.
*/
@@ -56,7 +56,7 @@ void register_node_type_cmp_group(void)
ntype.ext.srna = RNA_struct_find("CompositorNodeGroup");
BLI_assert(ntype.ext.srna != NULL);
RNA_struct_blender_type_set(ntype.ext.srna, &ntype);
-
+
node_type_socket_templates(&ntype, NULL, NULL);
node_type_size(&ntype, 140, 60, 400);
node_type_label(&ntype, node_group_label);
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.c b/source/blender/nodes/composite/nodes/node_composite_dilate.c
index 4efee112438..72d12e0a643 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.c
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.c
@@ -54,7 +54,7 @@ static void node_composit_init_dilateerode(bNodeTree *UNUSED(ntree), bNode *node
void register_node_type_cmp_dilateerode(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_DILATEERODE, "Dilate/Erode", NODE_CLASS_OP_FILTER, 0);
node_type_socket_templates(&ntype, cmp_node_dilateerode_in, cmp_node_dilateerode_out);
node_type_init(&ntype, node_composit_init_dilateerode);
diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c
index df8ae4931e5..b72a0fb82ae 100644
--- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c
+++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c
@@ -32,7 +32,7 @@
#include "../node_composite_util.h"
-/* **************** SCALAR MATH ******************** */
+/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate cmp_node_ellipsemask_in[] = {
{ SOCK_FLOAT, 1, N_("Mask"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_FLOAT, 1, N_("Value"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.c b/source/blender/nodes/composite/nodes/node_composite_gamma.c
index 81764c97aac..30d399d3f73 100644
--- a/source/blender/nodes/composite/nodes/node_composite_gamma.c
+++ b/source/blender/nodes/composite/nodes/node_composite_gamma.c
@@ -47,9 +47,9 @@ static bNodeSocketTemplate cmp_node_gamma_out[] = {
void register_node_type_cmp_gamma(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, cmp_node_gamma_in, cmp_node_gamma_out);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c b/source/blender/nodes/composite/nodes/node_composite_huecorrect.c
index 4ee1bccd726..70c73bcb46a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.c
@@ -47,14 +47,14 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node)
{
CurveMapping *cumapping = node->storage = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
int c;
-
+
cumapping->preset = CURVE_PRESET_MID9;
-
+
for (c = 0; c < 3; c++) {
CurveMap *cuma = &cumapping->cm[c];
curvemap_reset(cuma, &cumapping->clipr, cumapping->preset, CURVEMAP_SLOPE_POSITIVE);
}
-
+
/* default to showing Saturation */
cumapping->cur = 1;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c
index 6a5458d6045..124b2fa72b9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.c
+++ b/source/blender/nodes/composite/nodes/node_composite_image.c
@@ -150,10 +150,10 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, bNode *node, LinkNod
/* make sure ima->type is correct */
ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, NULL);
-
+
if (ima->rr) {
RenderLayer *rl = BLI_findlink(&ima->rr->layers, iuser->layer);
-
+
if (rl) {
RenderPass *rpass;
for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
@@ -251,7 +251,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl
bNodeSocket *sock, *sock_next;
LinkNodePair available_sockets = {NULL, NULL};
int sock_index;
-
+
/* XXX make callback */
if (rlayer)
cmp_node_rlayer_create_outputs(ntree, node, &available_sockets);
@@ -307,7 +307,7 @@ static void node_composit_init_image(bNodeTree *ntree, bNode *node)
iuser->fie_ima = 2;
iuser->ok = 1;
iuser->flag |= IMA_ANIM_ALWAYS;
-
+
/* setup initial outputs */
cmp_node_image_verify_outputs(ntree, node, false);
}
@@ -315,20 +315,20 @@ static void node_composit_init_image(bNodeTree *ntree, bNode *node)
static void node_composit_free_image(bNode *node)
{
bNodeSocket *sock;
-
+
/* free extra socket info */
for (sock = node->outputs.first; sock; sock = sock->next)
MEM_freeN(sock->storage);
-
+
MEM_freeN(node->storage);
}
static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, bNode *src_node)
{
bNodeSocket *sock;
-
+
dest_node->storage = MEM_dupallocN(src_node->storage);
-
+
/* copy extra socket info */
for (sock = src_node->outputs.first; sock; sock = sock->next)
sock->new_sock->storage = MEM_dupallocN(sock->storage);
@@ -394,7 +394,7 @@ static int node_composit_poll_rlayers(bNodeType *UNUSED(ntype), bNodeTree *ntree
{
if (STREQ(ntree->idname, "CompositorNodeTree")) {
Scene *scene;
-
+
/* XXX ugly: check if ntree is a local scene node tree.
* Render layers node can only be used in local scene->nodetree,
* since it directly links to the scene.
@@ -402,7 +402,7 @@ static int node_composit_poll_rlayers(bNodeType *UNUSED(ntype), bNodeTree *ntree
for (scene = G.main->scene.first; scene; scene = scene->id.next)
if (scene->nodetree == ntree)
break;
-
+
return (scene != NULL);
}
return false;
diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.c b/source/blender/nodes/composite/nodes/node_composite_inpaint.c
index a0d3531dabf..f97f366d0c4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_inpaint.c
+++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.c
@@ -47,7 +47,7 @@ static bNodeSocketTemplate cmp_node_inpaint_out[] = {
void register_node_type_cmp_inpaint(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER, 0);
node_type_socket_templates(&ntype, cmp_node_inpaint_in, cmp_node_inpaint_out);
diff --git a/source/blender/nodes/composite/nodes/node_composite_math.c b/source/blender/nodes/composite/nodes/node_composite_math.c
index 276dab27f76..e40621d3210 100644
--- a/source/blender/nodes/composite/nodes/node_composite_math.c
+++ b/source/blender/nodes/composite/nodes/node_composite_math.c
@@ -32,7 +32,7 @@
#include "node_composite_util.h"
-/* **************** SCALAR MATH ******************** */
+/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate cmp_node_math_in[] = {
{ SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
{ SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c
index 9c54009d2f1..4149a0eec9d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c
+++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c
@@ -58,7 +58,7 @@ static void init(const bContext *C, PointerRNA *ptr)
{
bNode *node = ptr->data;
Scene *scene = CTX_data_scene(C);
-
+
node->id = (ID *)scene->clip;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.c b/source/blender/nodes/composite/nodes/node_composite_normalize.c
index d93c62f8229..9a5cc84797e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normalize.c
+++ b/source/blender/nodes/composite/nodes/node_composite_normalize.c
@@ -46,9 +46,9 @@ static bNodeSocketTemplate cmp_node_normalize_out[] = {
void register_node_type_cmp_normalize(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR, 0);
node_type_socket_templates(&ntype, cmp_node_normalize_in, cmp_node_normalize_out);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.c
index 8b349ea8779..a2a25d5c515 100644
--- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c
+++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.c
@@ -107,16 +107,16 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, con
{
NodeImageMultiFile *nimf = node->storage;
bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name);
-
+
/* create format data for the input socket */
NodeImageMultiFileSocket *sockdata = MEM_callocN(sizeof(NodeImageMultiFileSocket), "socket image format");
sock->storage = sockdata;
-
+
BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
-
+
if (im_format) {
sockdata->format = *im_format;
if (BKE_imtype_is_movie(sockdata->format.imtype)) {
@@ -129,7 +129,7 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, con
sockdata->use_node_format = true;
nimf->active_input = BLI_findindex(&node->inputs, sock);
-
+
return sock;
}
@@ -138,16 +138,16 @@ int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
NodeImageMultiFile *nimf = node->storage;
bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
int totinputs = BLI_listbase_count(&node->inputs);
-
+
if (!sock)
return 0;
-
+
if (nimf->active_input == totinputs - 1)
--nimf->active_input;
-
+
/* free format data */
MEM_freeN(sock->storage);
-
+
nodeRemoveSocket(ntree, node, sock);
return 1;
}
@@ -175,7 +175,7 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
NodeImageMultiFile *nimf = MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file");
ImageFormatData *format = NULL;
node->storage = nimf;
-
+
if (scene) {
RenderData *rd = &scene->r;
@@ -184,7 +184,7 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
if (BKE_imtype_is_movie(nimf->format.imtype)) {
nimf->format.imtype = R_IMF_IMTYPE_OPENEXR;
}
-
+
format = &nimf->format;
}
else
@@ -197,21 +197,21 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
static void free_output_file(bNode *node)
{
bNodeSocket *sock;
-
+
/* free storage data in sockets */
for (sock = node->inputs.first; sock; sock = sock->next) {
MEM_freeN(sock->storage);
}
-
+
MEM_freeN(node->storage);
}
static void copy_output_file(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, bNode *src_node)
{
bNodeSocket *src_sock, *dest_sock;
-
+
dest_node->storage = MEM_dupallocN(src_node->storage);
-
+
/* duplicate storage data in sockets */
for (src_sock = src_node->inputs.first, dest_sock = dest_node->inputs.first; src_sock && dest_sock; src_sock = src_sock->next, dest_sock = dest_sock->next) {
dest_sock->storage = MEM_dupallocN(src_sock->storage);
@@ -222,7 +222,7 @@ static void update_output_file(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock, *sock_next;
PointerRNA ptr;
-
+
/* XXX fix for #36706: remove invalid sockets added with bpy API.
* This is not ideal, but prevents crashes from missing storage.
* FileOutput node needs a redesign to support this properly.
@@ -237,9 +237,9 @@ static void update_output_file(bNodeTree *ntree, bNode *node)
sock_next = sock->next;
nodeRemoveSocket(ntree, node, sock);
}
-
+
cmp_node_update_default(ntree, node);
-
+
/* automatically update the socket type based on linked input */
for (sock = node->inputs.first; sock; sock = sock->next) {
if (sock->link) {
diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c b/source/blender/nodes/composite/nodes/node_composite_splitViewer.c
index 780ec1592bf..dbe47066f66 100644
--- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c
+++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.c
@@ -49,7 +49,7 @@ static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node
iuser->fie_ima = 2;
iuser->ok = 1;
node->custom1 = 50; /* default 50% split */
-
+
node->id = (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c
index 93ae6b6c4a4..16f5d340ddd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c
+++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c
@@ -50,9 +50,9 @@ static void init(const bContext *C, PointerRNA *ptr)
{
bNode *node = ptr->data;
Scene *scene = CTX_data_scene(C);
-
+
node->id = (ID *)scene->clip;
-
+
/* default to bilinear, see node_sampler_type_items in rna_nodetree.c */
node->custom1 = 1;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c b/source/blender/nodes/composite/nodes/node_composite_valToRgb.c
index 1b62f4e77d4..16fc98a5e85 100644
--- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c
+++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.c
@@ -77,10 +77,10 @@ static bNodeSocketTemplate cmp_node_rgbtobw_out[] = {
void register_node_type_cmp_rgbtobw(void)
{
static bNodeType ntype;
-
+
cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, cmp_node_rgbtobw_in, cmp_node_rgbtobw_out);
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.c b/source/blender/nodes/composite/nodes/node_composite_viewer.c
index 66f2e2bacbc..a10ba02b38e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_viewer.c
+++ b/source/blender/nodes/composite/nodes/node_composite_viewer.c
@@ -52,7 +52,7 @@ static void node_composit_init_viewer(bNodeTree *UNUSED(ntree), bNode *node)
iuser->ok = 1;
node->custom3 = 0.5f;
node->custom4 = 0.5f;
-
+
node->id = (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
}
diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.c
index c465f7a9be5..4a47bf7035c 100644
--- a/source/blender/nodes/intern/node_common.c
+++ b/source/blender/nodes/intern/node_common.c
@@ -98,16 +98,16 @@ int nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree)
{
bNode *node;
int valid = 1;
-
+
/* unspecified node group, generally allowed
* (if anything, should be avoided on operator level)
*/
if (grouptree == NULL)
return 1;
-
+
if (nodetree == grouptree)
return 0;
-
+
for (node = grouptree->nodes.first; node; node = node->next) {
if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, nodetree)) {
valid = 0;
@@ -121,27 +121,27 @@ int nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree)
static bNodeSocket *group_verify_socket(bNodeTree *ntree, bNode *gnode, bNodeSocket *iosock, ListBase *verify_lb, int in_out)
{
bNodeSocket *sock;
-
+
for (sock = verify_lb->first; sock; sock = sock->next) {
if (STREQ(sock->identifier, iosock->identifier))
break;
}
if (sock) {
strcpy(sock->name, iosock->name);
-
+
if (iosock->typeinfo->interface_verify_socket)
iosock->typeinfo->interface_verify_socket(ntree, iosock, gnode, sock, "interface");
}
else {
sock = nodeAddSocket(ntree, gnode, in_out, iosock->idname, iosock->identifier, iosock->name);
-
+
if (iosock->typeinfo->interface_init_socket)
iosock->typeinfo->interface_init_socket(ntree, iosock, gnode, sock, "interface");
}
-
+
/* remove from list temporarily, to distinguish from orphaned sockets */
BLI_remlink(verify_lb, sock);
-
+
return sock;
}
@@ -150,9 +150,9 @@ static void group_verify_socket_list(bNodeTree *ntree, bNode *gnode,
ListBase *iosock_lb, ListBase *verify_lb, int in_out)
{
bNodeSocket *iosock, *sock, *nextsock;
-
+
/* step by step compare */
-
+
iosock = iosock_lb->first;
for (; iosock; iosock = iosock->next) {
/* abusing new_sock pointer for verification here! only used inside this function */
@@ -195,9 +195,9 @@ static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeFrame *data = (NodeFrame *)MEM_callocN(sizeof(NodeFrame), "frame node storage");
node->storage = data;
-
+
data->flag |= NODE_FRAME_SHRINK;
-
+
data->label_size = 20;
}
@@ -211,7 +211,7 @@ void register_node_type_frame(void)
node_type_storage(ntype, "NodeFrame", node_free_standard_storage, node_copy_standard_storage);
node_type_size(ntype, 150, 100, 0);
node_type_compatibility(ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
-
+
ntype->needs_free = 1;
nodeRegisterType(ntype);
}
@@ -251,11 +251,11 @@ void register_node_type_reroute(void)
{
/* frame type is used for all tree types, needs dynamic allocation */
bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type");
-
+
node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT, 0);
node_type_init(ntype, node_reroute_init);
node_type_internal_links(ntype, node_reroute_update_internal_links);
-
+
ntype->needs_free = 1;
nodeRegisterType(ntype);
}
@@ -267,14 +267,14 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
bNodeLink *link;
int type = SOCK_FLOAT;
const char *type_idname = nodeStaticSocketType(type, PROP_NONE);
-
+
/* XXX it would be a little bit more efficient to restrict actual updates
* to rerout nodes connected to an updated node, but there's no reliable flag
* to indicate updated nodes (node->update is not set on linking).
*/
-
+
node->done = 1;
-
+
/* recursive update */
for (link = ntree->links.first; link; link = link->next) {
bNode *fromnode = link->fromnode;
@@ -283,7 +283,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
continue;
if (nodeLinkIsHidden(link))
continue;
-
+
if (flag & REFINE_FORWARD) {
if (tonode == node && fromnode->type == NODE_REROUTE && !fromnode->done)
node_reroute_inherit_type_recursive(ntree, fromnode, REFINE_FORWARD);
@@ -293,7 +293,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
node_reroute_inherit_type_recursive(ntree, tonode, REFINE_BACKWARD);
}
}
-
+
/* determine socket type from unambiguous input/output connection if possible */
if (input->limit == 1 && input->link) {
type = input->link->fromsock->type;
@@ -303,7 +303,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
type = output->link->tosock->type;
type_idname = nodeStaticSocketType(type, PROP_NONE);
}
-
+
if (input->type != type) {
bNodeSocket *ninput = nodeAddSocket(ntree, node, SOCK_IN, type_idname, "input", "Input");
for (link = ntree->links.first; link; link = link->next) {
@@ -314,7 +314,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
}
nodeRemoveSocket(ntree, node, input);
}
-
+
if (output->type != type) {
bNodeSocket *noutput = nodeAddSocket(ntree, node, SOCK_OUT, type_idname, "output", "Output");
for (link = ntree->links.first; link; link = link->next) {
@@ -324,7 +324,7 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
}
nodeRemoveSocket(ntree, node, output);
}
-
+
nodeUpdateInternalLinks(ntree, node);
}
@@ -334,11 +334,11 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i
void ntree_update_reroute_nodes(bNodeTree *ntree)
{
bNode *node;
-
+
/* clear tags */
for (node = ntree->nodes.first; node; node = node->next)
node->done = 0;
-
+
for (node = ntree->nodes.first; node; node = node->next)
if (node->type == NODE_REROUTE && !node->done)
node_reroute_inherit_type_recursive(ntree, node, REFINE_FORWARD | REFINE_BACKWARD);
@@ -411,7 +411,7 @@ void node_group_input_verify(bNodeTree *ntree, bNode *node, ID *id)
if (id == (ID *)ntree) {
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->inputs, &node->outputs, SOCK_OUT);
-
+
/* add virtual extension socket */
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketVirtual", "__extend__", "");
}
@@ -426,23 +426,23 @@ static void node_group_input_update(bNodeTree *ntree, bNode *node)
* so they can be recreated after verification.
*/
ListBase tmplinks;
-
+
/* find links from the extension socket and store them */
BLI_listbase_clear(&tmplinks);
for (link = ntree->links.first; link; link = linknext) {
linknext = link->next;
if (nodeLinkIsHidden(link))
continue;
-
+
if (link->fromsock == extsock) {
bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link");
*tlink = *link;
BLI_addtail(&tmplinks, tlink);
-
+
nodeRemLink(ntree, link);
}
}
-
+
/* find valid link to expose */
exposelink = NULL;
for (link = tmplinks.first; link; link = link->next) {
@@ -456,22 +456,22 @@ static void node_group_input_update(bNodeTree *ntree, bNode *node)
break;
}
}
-
+
if (exposelink) {
bNodeSocket *gsock, *newsock;
-
+
gsock = ntreeAddSocketInterfaceFromSocket(ntree, exposelink->tonode, exposelink->tosock);
-
+
node_group_input_verify(ntree, node, (ID *)ntree);
newsock = node_group_input_find_socket(node, gsock->identifier);
-
+
/* redirect links from the extension socket */
for (link = tmplinks.first; link; link = link->next) {
nodeAddLink(ntree, node, newsock, link->tonode, link->tosock);
}
-
+
}
-
+
BLI_freelistN(&tmplinks);
}
@@ -479,13 +479,13 @@ void register_node_type_group_input(void)
{
/* used for all tree types, needs dynamic allocation */
bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type");
-
+
node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE, 0);
node_type_size(ntype, 140, 80, 400);
node_type_init(ntype, node_group_input_init);
node_type_update(ntype, node_group_input_update, node_group_input_verify);
node_type_compatibility(ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
-
+
ntype->needs_free = 1;
nodeRegisterType(ntype);
}
@@ -510,7 +510,7 @@ void node_group_output_verify(bNodeTree *ntree, bNode *node, ID *id)
if (id == (ID *)ntree) {
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->outputs, &node->inputs, SOCK_IN);
-
+
/* add virtual extension socket */
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketVirtual", "__extend__", "");
}
@@ -525,23 +525,23 @@ static void node_group_output_update(bNodeTree *ntree, bNode *node)
* so they can be recreated after verification.
*/
ListBase tmplinks;
-
+
/* find links to the extension socket and store them */
BLI_listbase_clear(&tmplinks);
for (link = ntree->links.first; link; link = linknext) {
linknext = link->next;
if (nodeLinkIsHidden(link))
continue;
-
+
if (link->tosock == extsock) {
bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link");
*tlink = *link;
BLI_addtail(&tmplinks, tlink);
-
+
nodeRemLink(ntree, link);
}
}
-
+
/* find valid link to expose */
exposelink = NULL;
for (link = tmplinks.first; link; link = link->next) {
@@ -555,22 +555,22 @@ static void node_group_output_update(bNodeTree *ntree, bNode *node)
break;
}
}
-
+
if (exposelink) {
bNodeSocket *gsock, *newsock;
-
+
/* XXX what if connecting virtual to virtual socket?? */
gsock = ntreeAddSocketInterfaceFromSocket(ntree, exposelink->fromnode, exposelink->fromsock);
-
+
node_group_output_verify(ntree, node, (ID *)ntree);
newsock = node_group_output_find_socket(node, gsock->identifier);
-
+
/* redirect links to the extension socket */
for (link = tmplinks.first; link; link = link->next) {
nodeAddLink(ntree, link->fromnode, link->fromsock, node, newsock);
}
}
-
+
BLI_freelistN(&tmplinks);
}
@@ -578,13 +578,13 @@ void register_node_type_group_output(void)
{
/* used for all tree types, needs dynamic allocation */
bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type");
-
+
node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE, 0);
node_type_size(ntype, 140, 80, 400);
node_type_init(ntype, node_group_output_init);
node_type_update(ntype, node_group_output_update, node_group_output_verify);
node_type_compatibility(ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
-
+
ntype->needs_free = 1;
nodeRegisterType(ntype);
}
diff --git a/source/blender/nodes/intern/node_exec.c b/source/blender/nodes/intern/node_exec.c
index 5b97685fc54..3708a7663ed 100644
--- a/source/blender/nodes/intern/node_exec.c
+++ b/source/blender/nodes/intern/node_exec.c
@@ -61,14 +61,14 @@ bNodeStack *node_get_socket_stack(bNodeStack *stack, bNodeSocket *sock)
void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out)
{
bNodeSocket *sock;
-
+
/* build pointer stack */
if (in) {
for (sock = node->inputs.first; sock; sock = sock->next) {
*(in++) = node_get_socket_stack(stack, sock);
}
}
-
+
if (out) {
for (sock = node->outputs.first; sock; sock = sock->next) {
*(out++) = node_get_socket_stack(stack, sock);
@@ -127,13 +127,13 @@ static struct bNodeStack *setup_stack(bNodeStack *stack, bNodeTree *ntree, bNode
bNodeStack *ns = node_get_socket_stack(stack, sock);
if (!ns)
return NULL;
-
+
/* don't mess with remote socket stacks, these are initialized by other nodes! */
if (sock->link)
return ns;
-
+
ns->sockettype = sock->type;
-
+
switch (sock->type) {
case SOCK_FLOAT:
ns->vec[0] = node_socket_get_float(ntree, node, sock);
@@ -145,7 +145,7 @@ static struct bNodeStack *setup_stack(bNodeStack *stack, bNodeTree *ntree, bNode
node_socket_get_color(ntree, node, sock, ns->vec);
break;
}
-
+
return ns;
}
@@ -161,29 +161,29 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context, bNodeTree *ntree, bNo
bNode **nodelist;
int totnodes, n;
/* XXX texnodes have threading issues with muting, have to disable it there ... */
-
+
/* ensure all sock->link pointers and node levels are correct */
ntreeUpdateTree(G.main, ntree);
-
+
/* get a dependency-sorted list of nodes */
ntreeGetDependencyList(ntree, &nodelist, &totnodes);
-
+
/* XXX could let callbacks do this for specialized data */
exec = MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data");
/* backpointer to node tree */
exec->nodetree = ntree;
-
+
/* set stack indices */
index = 0;
for (n = 0; n < totnodes; ++n) {
node = nodelist[n];
-
+
node->stack_index = index;
-
+
/* init node socket stack indexes */
for (sock = node->inputs.first; sock; sock = sock->next)
node_init_input_index(sock, &index);
-
+
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
for (sock = node->outputs.first; sock; sock = sock->next)
node_init_output_index(sock, &index, &node->internal_links);
@@ -193,48 +193,48 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context, bNodeTree *ntree, bNo
node_init_output_index(sock, &index, NULL);
}
}
-
+
/* allocated exec data pointers for nodes */
exec->totnodes = totnodes;
exec->nodeexec = MEM_callocN(exec->totnodes * sizeof(bNodeExec), "node execution data");
/* allocate data pointer for node stack */
exec->stacksize = index;
exec->stack = MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
-
+
/* all non-const results are considered inputs */
for (n = 0; n < exec->stacksize; ++n)
exec->stack[n].hasinput = 1;
-
+
/* prepare all nodes for execution */
for (n = 0, nodeexec = exec->nodeexec; n < totnodes; ++n, ++nodeexec) {
node = nodeexec->node = nodelist[n];
nodeexec->freeexecfunc = node->typeinfo->freeexecfunc;
-
+
/* tag inputs */
for (sock = node->inputs.first; sock; sock = sock->next) {
/* disable the node if an input link is invalid */
if (sock->link && !(sock->link->flag & NODE_LINK_VALID))
node->need_exec = 0;
-
+
ns = setup_stack(exec->stack, ntree, node, sock);
if (ns)
ns->hasoutput = 1;
}
-
+
/* tag all outputs */
for (sock = node->outputs.first; sock; sock = sock->next) {
/* ns = */ setup_stack(exec->stack, ntree, node, sock);
}
-
+
nodekey = BKE_node_instance_key(parent_key, ntree, node);
nodeexec->data.preview = context->previews ? BKE_node_instance_hash_lookup(context->previews, nodekey) : NULL;
if (node->typeinfo->initexecfunc)
nodeexec->data.data = node->typeinfo->initexecfunc(context, node, nodekey);
}
-
+
if (nodelist)
MEM_freeN(nodelist);
-
+
return exec;
}
@@ -242,18 +242,18 @@ void ntree_exec_end(bNodeTreeExec *exec)
{
bNodeExec *nodeexec;
int n;
-
+
if (exec->stack)
MEM_freeN(exec->stack);
-
+
for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) {
if (nodeexec->freeexecfunc)
nodeexec->freeexecfunc(nodeexec->data.data);
}
-
+
if (exec->nodeexec)
MEM_freeN(exec->nodeexec);
-
+
MEM_freeN(exec);
}
@@ -263,14 +263,14 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
{
ListBase *lb = &exec->threadstack[thread];
bNodeThreadStack *nts;
-
+
for (nts = lb->first; nts; nts = nts->next) {
if (!nts->used) {
nts->used = true;
break;
}
}
-
+
if (!nts) {
nts = MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
nts->stack = MEM_dupallocN(exec->stack);
@@ -293,9 +293,9 @@ bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *call
bNodeExec *nodeexec;
bNode *node;
int n;
-
+
/* nodes are presorted, so exec is in order of list */
-
+
for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) {
node = nodeexec->node;
if (node->need_exec) {
@@ -308,7 +308,7 @@ bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *call
node->typeinfo->execfunc(callerdata, thread, node, &nodeexec->data, nsin, nsout);
}
}
-
+
/* signal to that all went OK, for render */
return true;
}
diff --git a/source/blender/nodes/intern/node_exec.h b/source/blender/nodes/intern/node_exec.h
index bf4c29bad8e..6771df76bf9 100644
--- a/source/blender/nodes/intern/node_exec.h
+++ b/source/blender/nodes/intern/node_exec.h
@@ -51,17 +51,17 @@ struct bNodeStack;
typedef struct bNodeExec {
struct bNode *node; /* backpointer to node */
bNodeExecData data;
-
+
NodeFreeExecFunction freeexecfunc; /* free function, stored in exec itself to avoid dangling node pointer access */
} bNodeExec;
/* Execution Data for each instance of node tree execution */
typedef struct bNodeTreeExec {
struct bNodeTree *nodetree; /* backpointer to node tree */
-
+
int totnodes; /* total node count */
struct bNodeExec *nodeexec; /* per-node execution data */
-
+
int stacksize;
struct bNodeStack *stack; /* socket data stack */
/* only used by material and texture trees to keep one stack for each thread */
diff --git a/source/blender/nodes/intern/node_socket.c b/source/blender/nodes/intern/node_socket.c
index e1e1e2f0854..23ba51bbe10 100644
--- a/source/blender/nodes/intern/node_socket.c
+++ b/source/blender/nodes/intern/node_socket.c
@@ -52,9 +52,9 @@ struct Main;
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocketTemplate *stemp, int in_out)
{
bNodeSocket *sock = nodeAddStaticSocket(ntree, node, in_out, stemp->type, stemp->subtype, stemp->identifier, stemp->name);
-
+
sock->flag |= stemp->flag;
-
+
/* initialize default_value */
switch (stemp->type) {
case SOCK_FLOAT:
@@ -99,14 +99,14 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, struc
break;
}
}
-
+
return sock;
}
static bNodeSocket *verify_socket_template(bNodeTree *ntree, bNode *node, int in_out, ListBase *socklist, bNodeSocketTemplate *stemp)
{
bNodeSocket *sock;
-
+
for (sock = socklist->first; sock; sock = sock->next) {
if (STREQLEN(sock->name, stemp->name, NODE_MAXSTR))
break;
@@ -127,7 +127,7 @@ static bNodeSocket *verify_socket_template(bNodeTree *ntree, bNode *node, int in
/* remove the new socket from the node socket list first,
* will be added back after verification. */
BLI_remlink(socklist, sock);
-
+
return sock;
}
@@ -135,7 +135,7 @@ static void verify_socket_template_list(bNodeTree *ntree, bNode *node, int in_ou
{
bNodeSocket *sock, *nextsock;
bNodeSocketTemplate *stemp;
-
+
/* no inputs anymore? */
if (stemp_first == NULL) {
for (sock = (bNodeSocket *)socklist->first; sock; sock = nextsock) {
@@ -155,7 +155,7 @@ static void verify_socket_template_list(bNodeTree *ntree, bNode *node, int in_ou
nextsock = sock->next;
nodeRemoveSocket(ntree, node, sock);
}
-
+
/* and we put back the verified sockets */
stemp = stemp_first;
if (socklist->first) {
@@ -199,10 +199,10 @@ void node_socket_init_default_value(bNodeSocket *sock)
{
int type = sock->typeinfo->type;
int subtype = sock->typeinfo->subtype;
-
+
if (sock->default_value)
return; /* already initialized */
-
+
switch (type) {
case SOCK_FLOAT:
{
@@ -211,7 +211,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
dval->value = 0.0f;
dval->min = -FLT_MAX;
dval->max = FLT_MAX;
-
+
sock->default_value = dval;
break;
}
@@ -222,7 +222,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
dval->value = 0;
dval->min = INT_MIN;
dval->max = INT_MAX;
-
+
sock->default_value = dval;
break;
}
@@ -230,7 +230,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
{
bNodeSocketValueBoolean *dval = MEM_callocN(sizeof(bNodeSocketValueBoolean), "node socket value bool");
dval->value = false;
-
+
sock->default_value = dval;
break;
}
@@ -242,7 +242,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
copy_v3_v3(dval->value, default_value);
dval->min = -FLT_MAX;
dval->max = FLT_MAX;
-
+
sock->default_value = dval;
break;
}
@@ -251,7 +251,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
static float default_value[] = { 0.0f, 0.0f, 0.0f, 1.0f };
bNodeSocketValueRGBA *dval = MEM_callocN(sizeof(bNodeSocketValueRGBA), "node socket value color");
copy_v4_v4(dval->value, default_value);
-
+
sock->default_value = dval;
break;
}
@@ -260,7 +260,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
bNodeSocketValueString *dval = MEM_callocN(sizeof(bNodeSocketValueString), "node socket value string");
dval->subtype = subtype;
dval->value[0] = '\0';
-
+
sock->default_value = dval;
break;
}
@@ -272,12 +272,12 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
/* sanity check */
if (to->type != from->type)
return;
-
+
/* make sure both exist */
if (!from->default_value)
return;
node_socket_init_default_value(to);
-
+
switch (from->typeinfo->type) {
case SOCK_FLOAT:
{
@@ -330,7 +330,7 @@ static void standard_node_socket_interface_init_socket(bNodeTree *UNUSED(ntree),
{
/* initialize the type value */
sock->type = sock->typeinfo->type;
-
+
/* XXX socket interface 'type' value is not used really,
* but has to match or the copy function will bail out
*/
@@ -345,12 +345,12 @@ static void standard_node_socket_interface_verify_socket(bNodeTree *UNUSED(ntree
/* sanity check */
if (sock->type != stemp->typeinfo->type)
return;
-
+
/* make sure both exist */
if (!stemp->default_value)
return;
node_socket_init_default_value(sock);
-
+
switch (stemp->typeinfo->type) {
case SOCK_FLOAT:
{
@@ -389,65 +389,65 @@ static void standard_node_socket_interface_from_socket(bNodeTree *UNUSED(ntree),
static bNodeSocketType *make_standard_socket_type(int type, int subtype)
{
extern void ED_init_standard_node_socket_type(bNodeSocketType *);
-
+
const char *socket_idname = nodeStaticSocketType(type, subtype);
const char *interface_idname = nodeStaticSocketInterfaceType(type, subtype);
bNodeSocketType *stype;
StructRNA *srna;
-
+
stype = MEM_callocN(sizeof(bNodeSocketType), "node socket C type");
BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname));
-
+
/* set the RNA type
* uses the exact same identifier as the socket type idname */
srna = stype->ext_socket.srna = RNA_struct_find(socket_idname);
BLI_assert(srna != NULL);
/* associate the RNA type with the socket type */
RNA_struct_blender_type_set(srna, stype);
-
+
/* set the interface RNA type */
srna = stype->ext_interface.srna = RNA_struct_find(interface_idname);
BLI_assert(srna != NULL);
/* associate the RNA type with the socket type */
RNA_struct_blender_type_set(srna, stype);
-
+
/* extra type info for standard socket types */
stype->type = type;
stype->subtype = subtype;
-
+
/* XXX bad-level call! needed for setting draw callbacks */
ED_init_standard_node_socket_type(stype);
-
+
stype->interface_init_socket = standard_node_socket_interface_init_socket;
stype->interface_from_socket = standard_node_socket_interface_from_socket;
stype->interface_verify_socket = standard_node_socket_interface_verify_socket;
-
+
return stype;
}
static bNodeSocketType *make_socket_type_virtual(void)
{
extern void ED_init_node_socket_type_virtual(bNodeSocketType *);
-
+
const char *socket_idname = "NodeSocketVirtual";
bNodeSocketType *stype;
StructRNA *srna;
-
+
stype = MEM_callocN(sizeof(bNodeSocketType), "node socket C type");
BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname));
-
+
/* set the RNA type
* uses the exact same identifier as the socket type idname */
srna = stype->ext_socket.srna = RNA_struct_find(socket_idname);
BLI_assert(srna != NULL);
/* associate the RNA type with the socket type */
RNA_struct_blender_type_set(srna, stype);
-
+
/* extra type info for standard socket types */
stype->type = SOCK_CUSTOM;
-
+
ED_init_node_socket_type_virtual(stype);
-
+
return stype;
}
@@ -455,21 +455,21 @@ static bNodeSocketType *make_socket_type_virtual(void)
void register_standard_node_socket_types(void)
{
/* draw callbacks are set in drawnode.c to avoid bad-level calls */
-
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_NONE));
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_UNSIGNED));
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_PERCENTAGE));
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_FACTOR));
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_ANGLE));
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_TIME));
-
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_NONE));
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_UNSIGNED));
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_PERCENTAGE));
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_FACTOR));
-
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE));
-
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_NONE));
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_TRANSLATION));
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_DIRECTION));
@@ -477,12 +477,12 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_ACCELERATION));
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_EULER));
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_XYZ));
-
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_RGBA, PROP_NONE));
-
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_STRING, PROP_NONE));
-
+
nodeRegisterSocketType(make_standard_socket_type(SOCK_SHADER, PROP_NONE));
-
+
nodeRegisterSocketType(make_socket_type_virtual());
}
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 45409a2dfad..19529794c7c 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -165,10 +165,10 @@ static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur)
{
/* link swapping: try to find a free slot with a matching name */
-
+
bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
bNodeSocket *sock;
-
+
sock = cur->next ? cur->next : first; /* wrap around the list end */
while (sock != cur) {
if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
@@ -177,7 +177,7 @@ static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNo
if (link_count + 1 <= sock->limit)
return sock; /* found a valid free socket we can swap to */
}
-
+
sock = sock->next ? sock->next : first; /* wrap around the list end */
}
return NULL;
@@ -187,18 +187,18 @@ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
bNodeSocket *sock = link->tosock;
bNodeLink *tlink, *tlink_next;
-
+
/* inputs can have one link only, outputs can have unlimited links */
if (node != link->tonode)
return;
-
+
for (tlink = ntree->links.first; tlink; tlink = tlink_next) {
bNodeSocket *new_sock;
tlink_next = tlink->next;
-
+
if (sock != tlink->tosock)
continue;
-
+
new_sock = node_find_linkable_socket(ntree, node, sock);
if (new_sock && new_sock != sock) {
/* redirect existing link */
@@ -287,12 +287,12 @@ static bNodeSocket *select_internal_link_input(bNode *node, bNodeSocket *output)
int i;
int sel_priority = -1;
bool sel_is_linked = false;
-
+
for (input = node->inputs.first, i = 0; input; input = input->next, ++i) {
int priority = node_datatype_priority(input->type, output->type);
bool is_linked = (input->link != NULL);
bool preferred;
-
+
if (nodeSocketIsHidden(input) || /* ignore hidden sockets */
input->flag & SOCK_NO_INTERNAL_LINK || /* ignore if input is not allowed for internal connections */
priority < 0 || /* ignore incompatible types */
@@ -300,18 +300,18 @@ static bNodeSocket *select_internal_link_input(bNode *node, bNodeSocket *output)
{
continue;
}
-
+
/* determine if this input is preferred over the currently selected */
preferred = (priority > sel_priority) || /* prefer higher datatype priority */
(is_linked && !sel_is_linked); /* prefer linked over unlinked */
-
+
if (preferred) {
selected = input;
sel_is_linked = is_linked;
sel_priority = priority;
}
}
-
+
return selected;
}
@@ -319,29 +319,29 @@ void node_update_internal_links_default(bNodeTree *ntree, bNode *node)
{
bNodeLink *link;
bNodeSocket *output, *input;
-
+
/* sanity check */
if (!ntree)
return;
-
+
/* use link pointer as a tag for handled sockets (for outputs is unused anyway) */
for (output = node->outputs.first; output; output = output->next)
output->link = NULL;
-
+
for (link = ntree->links.first; link; link = link->next) {
if (nodeLinkIsHidden(link))
continue;
-
+
output = link->fromsock;
if (link->fromnode != node || output->link)
continue;
if (nodeSocketIsHidden(output) || output->flag & SOCK_NO_INTERNAL_LINK)
continue;
output->link = link; /* not really used, just for tagging handled sockets */
-
+
/* look for suitable input */
input = select_internal_link_input(node, output);
-
+
if (input) {
bNodeLink *ilink = MEM_callocN(sizeof(bNodeLink), "internal node link");
ilink->fromnode = node;
@@ -353,7 +353,7 @@ void node_update_internal_links_default(bNodeTree *ntree, bNode *node)
BLI_addtail(&node->internal_links, ilink);
}
}
-
+
/* clean up */
for (output = node->outputs.first; output; output = output->next)
output->link = NULL;
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index a5d8031f2f3..fcb21982661 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -82,7 +82,7 @@ static void shader_get_from_context(const bContext *C, bNodeTreeType *UNUSED(tre
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = OBACT(view_layer);
-
+
if (snode->shaderfrom == SNODE_SHADER_OBJECT) {
if (ob) {
*r_from = &ob->id;
@@ -136,11 +136,11 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
static void localize(bNodeTree *localtree, bNodeTree *UNUSED(ntree))
{
bNode *node, *node_next;
-
+
/* replace muted nodes and reroute nodes by internal links */
for (node = localtree->nodes.first; node; node = node_next) {
node_next = node->next;
-
+
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
nodeInternalRelink(localtree, node);
nodeFreeNode(localtree, node);
@@ -161,9 +161,9 @@ static void local_merge(bNodeTree *localtree, bNodeTree *ntree)
static void update(bNodeTree *ntree)
{
ntreeSetOutput(ntree);
-
+
ntree_update_reroute_nodes(ntree);
-
+
if (ntree->update & NTREE_UPDATE_NODES) {
/* clean up preview cache, in case nodes have been removed */
BKE_node_preview_remove_unused(ntree);
@@ -175,13 +175,13 @@ bNodeTreeType *ntreeType_Shader;
void register_node_tree_type_sh(void)
{
bNodeTreeType *tt = ntreeType_Shader = MEM_callocN(sizeof(bNodeTreeType), "shader node tree type");
-
+
tt->type = NTREE_SHADER;
strcpy(tt->idname, "ShaderNodeTree");
strcpy(tt->ui_name, "Shader Editor");
tt->ui_icon = 0; /* defined in drawnode.c */
strcpy(tt->ui_description, "Shader nodes");
-
+
tt->foreach_nodeclass = foreach_nodeclass;
tt->localize = localize;
tt->local_sync = local_sync;
@@ -189,9 +189,9 @@ void register_node_tree_type_sh(void)
tt->update = update;
tt->poll = shader_tree_poll;
tt->get_from_context = shader_get_from_context;
-
+
tt->ext.srna = &RNA_ShaderNodeTree;
-
+
ntreeTypeAdd(tt);
}
@@ -622,19 +622,19 @@ bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context, bNod
{
bNodeTreeExec *exec;
bNode *node;
-
+
/* ensures only a single output node is enabled */
ntreeSetOutput(ntree);
-
+
/* common base initialization */
exec = ntree_exec_begin(context, ntree, parent_key);
-
+
/* allocate the thread stack listbase array */
exec->threadstack = MEM_callocN(BLENDER_MAX_THREADS * sizeof(ListBase), "thread stack array");
-
+
for (node = exec->nodetree->nodes.first; node; node = node->next)
node->need_exec = 1;
-
+
return exec;
}
@@ -642,22 +642,22 @@ bNodeTreeExec *ntreeShaderBeginExecTree(bNodeTree *ntree)
{
bNodeExecContext context;
bNodeTreeExec *exec;
-
+
/* XXX hack: prevent exec data from being generated twice.
* this should be handled by the renderer!
*/
if (ntree->execdata)
return ntree->execdata;
-
+
context.previews = ntree->previews;
-
+
exec = ntreeShaderBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE);
-
+
/* XXX this should not be necessary, but is still used for cmp/sha/tex nodes,
* which only store the ntree pointer. Should be fixed at some point!
*/
ntree->execdata = exec;
-
+
return exec;
}
@@ -665,18 +665,18 @@ void ntreeShaderEndExecTree_internal(bNodeTreeExec *exec)
{
bNodeThreadStack *nts;
int a;
-
+
if (exec->threadstack) {
for (a = 0; a < BLENDER_MAX_THREADS; a++) {
for (nts = exec->threadstack[a].first; nts; nts = nts->next)
if (nts->stack) MEM_freeN(nts->stack);
BLI_freelistN(&exec->threadstack[a]);
}
-
+
MEM_freeN(exec->threadstack);
exec->threadstack = NULL;
}
-
+
ntree_exec_end(exec);
}
@@ -686,7 +686,7 @@ void ntreeShaderEndExecTree(bNodeTreeExec *exec)
/* exec may get freed, so assign ntree */
bNodeTree *ntree = exec->nodetree;
ntreeShaderEndExecTree_internal(exec);
-
+
/* XXX clear nodetree backpointer to exec data, same problem as noted in ntreeBeginExecTree */
ntree->execdata = NULL;
}
@@ -699,7 +699,7 @@ bool ntreeShaderExecTree(bNodeTree *ntree, int thread)
bNodeThreadStack *nts = NULL;
bNodeTreeExec *exec = ntree->execdata;
int compat;
-
+
/* ensure execdata is only initialized once */
if (!exec) {
BLI_thread_lock(LOCK_NODES);
@@ -709,11 +709,11 @@ bool ntreeShaderExecTree(bNodeTree *ntree, int thread)
exec = ntree->execdata;
}
-
+
nts = ntreeGetThreadStack(exec, thread);
compat = ntreeExecThreadNodes(exec, nts, &scd, thread);
ntreeReleaseThreadStack(nts);
-
+
/* if compat is zero, it has been using non-compatible nodes */
return compat;
}
diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c
index 43e940d6d0a..a4b2c155675 100644
--- a/source/blender/nodes/shader/node_shader_util.c
+++ b/source/blender/nodes/shader/node_shader_util.c
@@ -45,7 +45,7 @@ int sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
void sh_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
-
+
ntype->poll = sh_node_poll_default;
ntype->insert_link = node_insert_link_default;
ntype->update_internal_links = node_update_internal_links_default;
@@ -56,11 +56,11 @@ void sh_node_type_base(struct bNodeType *ntype, int type, const char *name, shor
void nodestack_get_vec(float *in, short type_in, bNodeStack *ns)
{
const float *from = ns->vec;
-
+
if (type_in == SOCK_FLOAT) {
if (ns->sockettype == SOCK_FLOAT)
*in = *from;
- else
+ else
*in = (from[0] + from[1] + from[2]) / 3.0f;
}
else if (type_in == SOCK_VECTOR) {
@@ -94,7 +94,7 @@ void nodestack_get_vec(float *in, short type_in, bNodeStack *ns)
void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, bNodeStack *ns)
{
memset(gs, 0, sizeof(*gs));
-
+
if (ns == NULL) {
/* node_get_stack() will generate NULL bNodeStack pointers for unknown/unsuported types of sockets... */
zero_v4(gs->vec);
@@ -107,7 +107,7 @@ void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, bNodeStack *ns)
else {
nodestack_get_vec(gs->vec, type, ns);
gs->link = ns->data;
-
+
if (type == SOCK_FLOAT)
gs->type = GPU_FLOAT;
else if (type == SOCK_VECTOR)
@@ -140,10 +140,10 @@ static void gpu_stack_from_data_list(GPUNodeStack *gs, ListBase *sockets, bNodeS
{
bNodeSocket *sock;
int i;
-
+
for (sock = sockets->first, i = 0; sock; sock = sock->next, i++)
node_gpu_stack_from_data(&gs[i], sock->type, ns[i]);
-
+
gs[i].end = true;
}
@@ -192,7 +192,7 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree)
if (activetexnode)
return activetexnode;
-
+
if (hasgroup) {
/* node active texture node in this tree, look inside groups */
for (node = ntree->nodes.first; node; node = node->next) {
@@ -203,7 +203,7 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree)
}
}
}
-
+
return inactivenode;
}
@@ -222,7 +222,7 @@ void ntreeExecGPUNodes(bNodeTreeExec *exec, GPUMaterial *mat, int do_outputs, sh
for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) {
node = nodeexec->node;
-
+
do_it = false;
/* for groups, only execute outputs for edited group */
if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) {
diff --git a/source/blender/nodes/shader/nodes/node_shader_brightness.c b/source/blender/nodes/shader/nodes/node_shader_brightness.c
index d795575c86a..bb95ed2d32c 100644
--- a/source/blender/nodes/shader/nodes/node_shader_brightness.c
+++ b/source/blender/nodes/shader/nodes/node_shader_brightness.c
@@ -50,13 +50,13 @@ static int gpu_shader_brightcontrast(GPUMaterial *mat, bNode *node, bNodeExecDat
void register_node_type_sh_brightcontrast(void)
{
static bNodeType ntype;
-
+
sh_node_type_base(&ntype, SH_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR, 0);
node_type_compatibility(&ntype, NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_brightcontrast_in, sh_node_brightcontrast_out);
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, gpu_shader_brightcontrast);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.c b/source/blender/nodes/shader/nodes/node_shader_bump.c
index 6098aefc5e1..6274d132bc7 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bump.c
+++ b/source/blender/nodes/shader/nodes/node_shader_bump.c
@@ -31,7 +31,7 @@
#include "node_shader_util.h"
-/* **************** BUMP ******************** */
+/* **************** BUMP ******************** */
static bNodeSocketTemplate sh_node_bump_in[] = {
{ SOCK_FLOAT, 1, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{ SOCK_FLOAT, 1, N_("Distance"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f},
diff --git a/source/blender/nodes/shader/nodes/node_shader_common.c b/source/blender/nodes/shader/nodes/node_shader_common.c
index 134319cb352..24de03dbda4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_common.c
+++ b/source/blender/nodes/shader/nodes/node_shader_common.c
@@ -50,7 +50,7 @@ static void copy_stack(bNodeStack *to, bNodeStack *from)
copy_v4_v4(to->vec, from->vec);
to->data = from->data;
to->datatype = from->datatype;
-
+
/* tag as copy to prevent freeing */
to->is_copy = 1;
}
@@ -63,7 +63,7 @@ static void move_stack(bNodeStack *to, bNodeStack *from)
to->data = from->data;
to->datatype = from->datatype;
to->is_copy = from->is_copy;
-
+
from->data = NULL;
from->is_copy = 0;
}
@@ -75,20 +75,20 @@ static void *group_initexec(bNodeExecContext *context, bNode *node, bNodeInstanc
{
bNodeTree *ngroup = (bNodeTree *)node->id;
bNodeTreeExec *exec;
-
+
if (!ngroup)
return NULL;
-
+
/* initialize the internal node tree execution */
exec = ntreeShaderBeginExecTree_internal(context, ngroup, key);
-
+
return exec;
}
static void group_freeexec(void *nodedata)
{
bNodeTreeExec *gexec = (bNodeTreeExec *)nodedata;
-
+
if (gexec)
ntreeShaderEndExecTree_internal(gexec);
}
@@ -102,7 +102,7 @@ static void group_copy_inputs(bNode *gnode, bNodeStack **in, bNodeStack *gstack)
bNodeSocket *sock;
bNodeStack *ns;
int a;
-
+
for (node = ngroup->nodes.first; node; node = node->next) {
if (node->type == NODE_GROUP_INPUT) {
for (sock = node->outputs.first, a = 0; sock; sock = sock->next, ++a) {
@@ -123,7 +123,7 @@ static void group_move_outputs(bNode *gnode, bNodeStack **out, bNodeStack *gstac
bNodeSocket *sock;
bNodeStack *ns;
int a;
-
+
for (node = ngroup->nodes.first; node; node = node->next) {
if (node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT)) {
for (sock = node->inputs.first, a = 0; sock; sock = sock->next, ++a) {
@@ -140,10 +140,10 @@ static void group_execute(void *data, int thread, struct bNode *node, bNodeExecD
{
bNodeTreeExec *exec = execdata->data;
bNodeThreadStack *nts;
-
+
if (!exec)
return;
-
+
/* XXX same behavior as trunk: all nodes inside group are executed.
* it's stupid, but just makes it work. compo redesign will do this better.
*/
@@ -152,13 +152,13 @@ static void group_execute(void *data, int thread, struct bNode *node, bNodeExecD
for (inode = exec->nodetree->nodes.first; inode; inode = inode->next)
inode->need_exec = 1;
}
-
+
nts = ntreeGetThreadStack(exec, thread);
-
+
group_copy_inputs(node, in, nts->stack);
ntreeExecThreadNodes(exec, nts, data, thread);
group_move_outputs(node, out, nts->stack);
-
+
ntreeReleaseThreadStack(nts);
}
@@ -169,7 +169,7 @@ static void group_gpu_copy_inputs(bNode *gnode, GPUNodeStack *in, bNodeStack *gs
bNodeSocket *sock;
bNodeStack *ns;
int a;
-
+
for (node = ngroup->nodes.first; node; node = node->next) {
if (node->type == NODE_GROUP_INPUT) {
for (sock = node->outputs.first, a = 0; sock; sock = sock->next, ++a) {
@@ -192,7 +192,7 @@ static void group_gpu_move_outputs(bNode *gnode, GPUNodeStack *out, bNodeStack *
bNodeSocket *sock;
bNodeStack *ns;
int a;
-
+
for (node = ngroup->nodes.first; node; node = node->next) {
if (node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT)) {
for (sock = node->inputs.first, a = 0; sock; sock = sock->next, ++a) {
@@ -210,10 +210,10 @@ static void group_gpu_move_outputs(bNode *gnode, GPUNodeStack *out, bNodeStack *
static int gpu_group_execute(GPUMaterial *mat, bNode *node, bNodeExecData *execdata, GPUNodeStack *in, GPUNodeStack *out)
{
bNodeTreeExec *exec = execdata->data;
-
+
if (!node->id)
return 0;
-
+
group_gpu_copy_inputs(node, in, exec->stack);
#if 0 /* XXX NODE_GROUP_EDIT is deprecated, depends on node space */
ntreeExecGPUNodes(exec, mat, (node->flag & NODE_GROUP_EDIT));
@@ -221,14 +221,14 @@ static int gpu_group_execute(GPUMaterial *mat, bNode *node, bNodeExecData *execd
ntreeExecGPUNodes(exec, mat, 0, NODE_NEW_SHADING | NODE_OLD_SHADING);
#endif
group_gpu_move_outputs(node, out, exec->stack);
-
+
return 1;
}
void register_node_type_sh_group(void)
{
static bNodeType ntype;
-
+
/* NB: cannot use sh_node_type_base for node group, because it would map the node type
* to the shared NODE_GROUP integer type id.
*/
@@ -241,7 +241,7 @@ void register_node_type_sh_group(void)
ntype.ext.srna = RNA_struct_find("ShaderNodeGroup");
BLI_assert(ntype.ext.srna != NULL);
RNA_struct_blender_type_set(ntype.ext.srna, &ntype);
-
+
node_type_compatibility(&ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
node_type_socket_templates(&ntype, NULL, NULL);
node_type_size(&ntype, 140, 60, 400);
@@ -249,6 +249,6 @@ void register_node_type_sh_group(void)
node_type_update(&ntype, NULL, node_group_verify);
node_type_exec(&ntype, group_initexec, group_freeexec, group_execute);
node_type_gpu(&ntype, gpu_group_execute);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.c
index 5dfeb883915..4f3dc92ad02 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.c
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.c
@@ -48,7 +48,7 @@ static bNodeSocketTemplate sh_node_curve_vec_out[] = {
static void node_shader_exec_curve_vec(void *UNUSED(data), int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out)
{
float vec[3];
-
+
/* stack order input: vec */
/* stack order output: vec */
nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
@@ -102,7 +102,7 @@ static void node_shader_exec_curve_rgb(void *UNUSED(data), int UNUSED(thread), b
{
float vec[3];
float fac;
-
+
/* stack order input: vec */
/* stack order output: vec */
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
diff --git a/source/blender/nodes/shader/nodes/node_shader_fresnel.c b/source/blender/nodes/shader/nodes/node_shader_fresnel.c
index 599d0533c27..072abed6c16 100644
--- a/source/blender/nodes/shader/nodes/node_shader_fresnel.c
+++ b/source/blender/nodes/shader/nodes/node_shader_fresnel.c
@@ -47,7 +47,7 @@ static int node_shader_gpu_fresnel(GPUMaterial *mat, bNode *node, bNodeExecData
else {
GPU_link(mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link);
}
-
+
return GPU_stack_link(mat, node, "node_fresnel", in, out, GPU_builtin(GPU_VIEW_POSITION));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_gamma.c b/source/blender/nodes/shader/nodes/node_shader_gamma.c
index 2aedba58f2c..e536d198ed0 100644
--- a/source/blender/nodes/shader/nodes/node_shader_gamma.c
+++ b/source/blender/nodes/shader/nodes/node_shader_gamma.c
@@ -60,7 +60,7 @@ static int node_shader_gpu_gamma(GPUMaterial *mat, bNode *node, bNodeExecData *U
void register_node_type_sh_gamma(void)
{
static bNodeType ntype;
-
+
sh_node_type_base(&ntype, SH_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR, 0);
node_type_compatibility(&ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_gamma_in, sh_node_gamma_out);
@@ -68,6 +68,6 @@ void register_node_type_sh_gamma(void)
node_type_storage(&ntype, "", NULL, NULL);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_gamma);
node_type_gpu(&ntype, node_shader_gpu_gamma);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_geom.c b/source/blender/nodes/shader/nodes/node_shader_geom.c
new file mode 100644
index 00000000000..0a51ee8dc68
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_geom.c
@@ -0,0 +1,163 @@
+/*
+ * ***** 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) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/shader/nodes/node_shader_geom.c
+ * \ingroup shdnodes
+ */
+
+
+#include "node_shader_util.h"
+
+#include "DNA_customdata_types.h"
+
+/* **************** GEOMETRY ******************** */
+
+/* output socket type definition */
+static bNodeSocketTemplate sh_node_geom_out[] = {
+ { SOCK_VECTOR, 0, N_("Global")},
+ { SOCK_VECTOR, 0, N_("Local")},
+ { SOCK_VECTOR, 0, N_("View")},
+ { SOCK_VECTOR, 0, N_("Orco")},
+ { SOCK_VECTOR, 0, N_("UV")},
+ { SOCK_VECTOR, 0, N_("Normal")},
+ { SOCK_RGBA, 0, N_("Vertex Color")},
+ { SOCK_FLOAT, 0, N_("Vertex Alpha")},
+ { SOCK_FLOAT, 0, N_("Front/Back")},
+ { -1, 0, "" }
+};
+
+/* node execute callback */
+static void node_shader_exec_geom(void *data, int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **UNUSED(in), bNodeStack **out)
+{
+ if (data) {
+ ShadeInput *shi = ((ShaderCallData *)data)->shi;
+ NodeGeometry *ngeo = (NodeGeometry *)node->storage;
+ ShadeInputUV *suv = &shi->uv[shi->actuv];
+ static float defaultvcol[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ int i;
+
+ if (ngeo->uvname[0]) {
+ /* find uv map by name */
+ for (i = 0; i < shi->totuv; i++) {
+ if (STREQ(shi->uv[i].name, ngeo->uvname)) {
+ suv = &shi->uv[i];
+ break;
+ }
+ }
+ }
+
+ /* out: global, local, view, orco, uv, normal, vertex color */
+ copy_v3_v3(out[GEOM_OUT_GLOB]->vec, shi->gl);
+ copy_v3_v3(out[GEOM_OUT_LOCAL]->vec, shi->co);
+ copy_v3_v3(out[GEOM_OUT_VIEW]->vec, shi->view);
+ copy_v3_v3(out[GEOM_OUT_ORCO]->vec, shi->lo);
+ copy_v3_v3(out[GEOM_OUT_UV]->vec, suv->uv);
+ copy_v3_v3(out[GEOM_OUT_NORMAL]->vec, shi->vno);
+
+ if (shi->use_world_space_shading) {
+ negate_v3(out[GEOM_OUT_NORMAL]->vec);
+ mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[GEOM_OUT_NORMAL]->vec);
+ }
+ if (shi->totcol) {
+ /* find vertex color layer by name */
+ ShadeInputCol *scol = &shi->col[0];
+
+ if (ngeo->colname[0]) {
+ for (i = 0; i < shi->totcol; i++) {
+ if (STREQ(shi->col[i].name, ngeo->colname)) {
+ scol = &shi->col[i];
+ break;
+ }
+ }
+ }
+
+ srgb_to_linearrgb_v3_v3(out[GEOM_OUT_VCOL]->vec, scol->col);
+ out[GEOM_OUT_VCOL]->vec[3] = scol->col[3];
+ out[GEOM_OUT_VCOL_ALPHA]->vec[0] = scol->col[3];
+ }
+ else {
+ memcpy(out[GEOM_OUT_VCOL]->vec, defaultvcol, sizeof(defaultvcol));
+ out[GEOM_OUT_VCOL_ALPHA]->vec[0] = 1.0f;
+ }
+
+ if (shi->osatex) {
+ out[GEOM_OUT_GLOB]->data = shi->dxgl;
+ out[GEOM_OUT_GLOB]->datatype = NS_OSA_VECTORS;
+ out[GEOM_OUT_LOCAL]->data = shi->dxco;
+ out[GEOM_OUT_LOCAL]->datatype = NS_OSA_VECTORS;
+ out[GEOM_OUT_VIEW]->data = &shi->dxview;
+ out[GEOM_OUT_VIEW]->datatype = NS_OSA_VALUES;
+ out[GEOM_OUT_ORCO]->data = shi->dxlo;
+ out[GEOM_OUT_ORCO]->datatype = NS_OSA_VECTORS;
+ out[GEOM_OUT_UV]->data = suv->dxuv;
+ out[GEOM_OUT_UV]->datatype = NS_OSA_VECTORS;
+ out[GEOM_OUT_NORMAL]->data = shi->dxno;
+ out[GEOM_OUT_NORMAL]->datatype = NS_OSA_VECTORS;
+ }
+
+ /* front/back, normal flipping was stored */
+ out[GEOM_OUT_FRONTBACK]->vec[0] = (shi->flippednor) ? 0.0f : 1.0f;
+ }
+}
+
+static void node_shader_init_geometry(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->storage = MEM_callocN(sizeof(NodeGeometry), "NodeGeometry");
+}
+
+static int gpu_shader_geom(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
+{
+ NodeGeometry *ngeo = (NodeGeometry *)node->storage;
+ GPUNodeLink *orco = GPU_attribute(CD_ORCO, "");
+ GPUNodeLink *mtface = GPU_attribute(CD_MTFACE, ngeo->uvname);
+ GPUNodeLink *mcol = GPU_attribute(CD_MCOL, ngeo->colname);
+
+ bool ret = GPU_stack_link(mat, "geom", in, out,
+ GPU_builtin(GPU_VIEW_POSITION), GPU_builtin(GPU_VIEW_NORMAL),
+ GPU_builtin(GPU_INVERSE_VIEW_MATRIX), orco, mtface, mcol);
+ if (GPU_material_use_world_space_shading(mat)) {
+ GPU_link(mat, "vec_math_negate", out[5].link, &out[5].link);
+ ret &= GPU_link(mat, "direction_transform_m4v3", out[5].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[5].link);
+ }
+ return ret;
+}
+
+/* node type definition */
+void register_node_type_sh_geom(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_GEOMETRY, "Geometry", NODE_CLASS_INPUT, 0);
+ node_type_compatibility(&ntype, NODE_OLD_SHADING);
+ node_type_socket_templates(&ntype, NULL, sh_node_geom_out);
+ node_type_init(&ntype, node_shader_init_geometry);
+ node_type_storage(&ntype, "NodeGeometry", node_free_standard_storage, node_copy_standard_storage);
+ node_type_exec(&ntype, NULL, NULL, node_shader_exec_geom);
+ node_type_gpu(&ntype, gpu_shader_geom);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c b/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c
index 0e82c962f53..07f1e9e3233 100644
--- a/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c
+++ b/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c
@@ -52,7 +52,7 @@ static void do_hue_sat_fac(bNode *UNUSED(node), float *out, float hue, float sat
{
if (fac != 0.0f && (hue != 0.5f || sat != 1.0f || val != 1.0f)) {
float col[3], hsv[3], mfac = 1.0f - fac;
-
+
rgb_to_hsv(in[0], in[1], in[2], hsv, hsv + 1, hsv + 2);
hsv[0] += (hue - 0.5f);
if (hsv[0] > 1.0f) hsv[0] -= 1.0f; else if (hsv[0] < 0.0f) hsv[0] += 1.0f;
diff --git a/source/blender/nodes/shader/nodes/node_shader_invert.c b/source/blender/nodes/shader/nodes/node_shader_invert.c
index c5765ae492b..b1805946f65 100644
--- a/source/blender/nodes/shader/nodes/node_shader_invert.c
+++ b/source/blender/nodes/shader/nodes/node_shader_invert.c
@@ -34,7 +34,7 @@
-/* **************** INVERT ******************** */
+/* **************** INVERT ******************** */
static bNodeSocketTemplate sh_node_invert_in[] = {
{ SOCK_FLOAT, 1, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{ SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
@@ -46,18 +46,18 @@ static bNodeSocketTemplate sh_node_invert_out[] = {
{ -1, 0, "" }
};
-static void node_shader_exec_invert(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), bNodeStack **in,
+static void node_shader_exec_invert(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), bNodeStack **in,
bNodeStack **out)
{
float col[3], icol[3], fac;
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
nodestack_get_vec(col, SOCK_VECTOR, in[1]);
-
+
icol[0] = 1.0f - col[0];
icol[1] = 1.0f - col[1];
icol[2] = 1.0f - col[2];
-
+
/* if fac, blend result against original input */
if (fac < 1.0f)
interp_v3_v3v3(out[0]->vec, col, icol, fac);
diff --git a/source/blender/nodes/shader/nodes/node_shader_mapping.c b/source/blender/nodes/shader/nodes/node_shader_mapping.c
index 42d957977e3..fdbf23618ef 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mapping.c
+++ b/source/blender/nodes/shader/nodes/node_shader_mapping.c
@@ -56,7 +56,7 @@ static void node_shader_exec_mapping(void *UNUSED(data), int UNUSED(thread), bNo
{
TexMapping *texmap = node->storage;
float *vec = out[0]->vec;
-
+
/* stack order input: vector */
/* stack order output: vector */
nodestack_get_vec(vec, SOCK_VECTOR, in[0]);
@@ -105,7 +105,7 @@ static int gpu_shader_mapping(GPUMaterial *mat, bNode *node, bNodeExecData *UNUS
void register_node_type_sh_mapping(void)
{
static bNodeType ntype;
-
+
sh_node_type_base(&ntype, SH_NODE_MAPPING, "Mapping", NODE_CLASS_OP_VECTOR, 0);
node_type_compatibility(&ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_mapping_in, sh_node_mapping_out);
@@ -114,6 +114,6 @@ void register_node_type_sh_mapping(void)
node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage);
node_type_exec(&ntype, node_shader_initexec_mapping, NULL, node_shader_exec_mapping);
node_type_gpu(&ntype, gpu_shader_mapping);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_material.c b/source/blender/nodes/shader/nodes/node_shader_material.c
new file mode 100644
index 00000000000..8a73ddc1194
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_material.c
@@ -0,0 +1,374 @@
+/*
+ * ***** 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) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/shader/nodes/node_shader_material.c
+ * \ingroup shdnodes
+ */
+
+#include "node_shader_util.h"
+
+/* **************** MATERIAL ******************** */
+
+static bNodeSocketTemplate sh_node_material_in[] = {
+ { SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
+ { SOCK_RGBA, 1, N_("Spec"), 0.0f, 0.0f, 0.0f, 1.0f},
+ { SOCK_FLOAT, 1, N_("DiffuseIntensity"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
+ { SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_DIRECTION},
+ { -1, 0, "" }
+};
+
+static bNodeSocketTemplate sh_node_material_out[] = {
+ { SOCK_RGBA, 0, N_("Color")},
+ { SOCK_FLOAT, 0, N_("Alpha")},
+ { SOCK_VECTOR, 0, N_("Normal")},
+ { -1, 0, "" }
+};
+
+/* **************** EXTENDED MATERIAL ******************** */
+
+static bNodeSocketTemplate sh_node_material_ext_in[] = {
+ { SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
+ { SOCK_RGBA, 1, N_("Spec"), 0.0f, 0.0f, 0.0f, 1.0f},
+ { SOCK_FLOAT, 1, N_("DiffuseIntensity"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
+ { SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_DIRECTION},
+ { SOCK_RGBA, 1, N_("Mirror"), 0.0f, 0.0f, 0.0f, 1.0f},
+ { SOCK_FLOAT, 1, N_("Ambient"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
+ { SOCK_FLOAT, 1, N_("Emit"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED},
+ { SOCK_FLOAT, 1, N_("SpecTra"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
+ { SOCK_FLOAT, 1, N_("Reflectivity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
+ { SOCK_FLOAT, 1, N_("Alpha"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED},
+ { SOCK_FLOAT, 1, N_("Translucency"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
+ { -1, 0, "" }
+};
+
+static bNodeSocketTemplate sh_node_material_ext_out[] = {
+ { SOCK_RGBA, 0, N_("Color")},
+ { SOCK_FLOAT, 0, N_("Alpha")},
+ { SOCK_VECTOR, 0, N_("Normal")},
+ { SOCK_RGBA, 0, N_("Diffuse")},
+ { SOCK_RGBA, 0, N_("Spec")},
+ { SOCK_RGBA, 0, N_("AO")},
+ { -1, 0, "" }
+};
+
+static void node_shader_exec_material(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
+{
+ if (data && node->id) {
+ ShadeResult shrnode;
+ ShadeInput *shi;
+ ShaderCallData *shcd = data;
+ float col[4];
+ bNodeSocket *sock;
+ char hasinput[NUM_MAT_IN] = {'\0'};
+ int i, mode;
+
+ /* note: cannot use the in[]->hasinput flags directly, as these are not necessarily
+ * the constant input stack values (e.g. in case material node is inside a group).
+ * we just want to know if a node input uses external data or the material setting.
+ * this is an ugly hack, but so is this node as a whole.
+ */
+ for (sock = node->inputs.first, i = 0; sock; sock = sock->next, ++i)
+ hasinput[i] = (sock->link != NULL);
+
+ shi = shcd->shi;
+ shi->mat = (Material *)node->id;
+
+ /* copy all relevant material vars, note, keep this synced with render_types.h */
+ memcpy(&shi->r, &shi->mat->r, 23 * sizeof(float));
+ shi->har = shi->mat->har;
+
+ /* write values */
+ if (hasinput[MAT_IN_COLOR])
+ nodestack_get_vec(&shi->r, SOCK_VECTOR, in[MAT_IN_COLOR]);
+
+ if (hasinput[MAT_IN_SPEC])
+ nodestack_get_vec(&shi->specr, SOCK_VECTOR, in[MAT_IN_SPEC]);
+
+ if (hasinput[MAT_IN_REFL])
+ nodestack_get_vec(&shi->refl, SOCK_FLOAT, in[MAT_IN_REFL]);
+
+ /* retrieve normal */
+ if (hasinput[MAT_IN_NORMAL]) {
+ nodestack_get_vec(shi->vn, SOCK_VECTOR, in[MAT_IN_NORMAL]);
+ if (shi->use_world_space_shading) {
+ negate_v3(shi->vn);
+ mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEW_MATRIX), shi->vn);
+ }
+ normalize_v3(shi->vn);
+ }
+ else
+ copy_v3_v3(shi->vn, shi->vno);
+
+ /* custom option to flip normal */
+ if (node->custom1 & SH_NODE_MAT_NEG) {
+ negate_v3(shi->vn);
+ }
+
+ if (node->type == SH_NODE_MATERIAL_EXT) {
+ if (hasinput[MAT_IN_MIR])
+ nodestack_get_vec(&shi->mirr, SOCK_VECTOR, in[MAT_IN_MIR]);
+ if (hasinput[MAT_IN_AMB])
+ nodestack_get_vec(&shi->amb, SOCK_FLOAT, in[MAT_IN_AMB]);
+ if (hasinput[MAT_IN_EMIT])
+ nodestack_get_vec(&shi->emit, SOCK_FLOAT, in[MAT_IN_EMIT]);
+ if (hasinput[MAT_IN_SPECTRA])
+ nodestack_get_vec(&shi->spectra, SOCK_FLOAT, in[MAT_IN_SPECTRA]);
+ if (hasinput[MAT_IN_RAY_MIRROR])
+ nodestack_get_vec(&shi->ray_mirror, SOCK_FLOAT, in[MAT_IN_RAY_MIRROR]);
+ if (hasinput[MAT_IN_ALPHA])
+ nodestack_get_vec(&shi->alpha, SOCK_FLOAT, in[MAT_IN_ALPHA]);
+ if (hasinput[MAT_IN_TRANSLUCENCY])
+ nodestack_get_vec(&shi->translucency, SOCK_FLOAT, in[MAT_IN_TRANSLUCENCY]);
+ }
+
+ /* make alpha output give results even if transparency is only enabled on
+ * the material linked in this not and not on the parent material */
+ mode = shi->mode;
+ if (shi->mat->mode & MA_TRANSP)
+ shi->mode |= MA_TRANSP;
+
+ shi->nodes = 1; /* temp hack to prevent trashadow recursion */
+ node_shader_lamp_loop(shi, &shrnode); /* clears shrnode */
+ shi->nodes = 0;
+
+ shi->mode = mode;
+
+ /* write to outputs */
+ if (node->custom1 & SH_NODE_MAT_DIFF) {
+ copy_v3_v3(col, shrnode.combined);
+ if (!(node->custom1 & SH_NODE_MAT_SPEC)) {
+ sub_v3_v3(col, shrnode.spec);
+ }
+ }
+ else if (node->custom1 & SH_NODE_MAT_SPEC) {
+ copy_v3_v3(col, shrnode.spec);
+ }
+ else
+ col[0] = col[1] = col[2] = 0.0f;
+
+ col[3] = shrnode.alpha;
+
+ if (shi->do_preview)
+ BKE_node_preview_set_pixel(execdata->preview, col, shi->xs, shi->ys, shi->do_manage);
+
+ copy_v3_v3(out[MAT_OUT_COLOR]->vec, col);
+ out[MAT_OUT_ALPHA]->vec[0] = shrnode.alpha;
+
+ if (node->custom1 & SH_NODE_MAT_NEG) {
+ shi->vn[0] = -shi->vn[0];
+ shi->vn[1] = -shi->vn[1];
+ shi->vn[2] = -shi->vn[2];
+ }
+
+ copy_v3_v3(out[MAT_OUT_NORMAL]->vec, shi->vn);
+
+ if (shi->use_world_space_shading) {
+ negate_v3(out[MAT_OUT_NORMAL]->vec);
+ mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[MAT_OUT_NORMAL]->vec);
+ }
+ /* Extended material options */
+ if (node->type == SH_NODE_MATERIAL_EXT) {
+ /* Shadow, Reflect, Refract, Radiosity, Speed seem to cause problems inside
+ * a node tree :( */
+ copy_v3_v3(out[MAT_OUT_DIFFUSE]->vec, shrnode.diffshad);
+ copy_v3_v3(out[MAT_OUT_SPEC]->vec, shrnode.spec);
+ copy_v3_v3(out[MAT_OUT_AO]->vec, shrnode.ao);
+ }
+
+ /* copy passes, now just active node */
+ if (node->flag & NODE_ACTIVE_ID) {
+ float combined[4], alpha;
+
+ copy_v4_v4(combined, shcd->shr->combined);
+ alpha = shcd->shr->alpha;
+
+ *(shcd->shr) = shrnode;
+
+ copy_v4_v4(shcd->shr->combined, combined);
+ shcd->shr->alpha = alpha;
+ }
+ }
+}
+
+
+static void node_shader_init_material(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = SH_NODE_MAT_DIFF | SH_NODE_MAT_SPEC;
+}
+
+/* XXX this is also done as a local static function in gpu_codegen.c,
+ * but we need this to hack around the crappy material node.
+ */
+static GPUNodeLink *gpu_get_input_link(GPUMaterial *mat, GPUNodeStack *in)
+{
+ if (in->link) {
+ return in->link;
+ }
+ else {
+ GPUNodeLink *result = NULL;
+
+ /* note GPU_uniform() is only intended to be used as a parameter to
+ * GPU_link(), returning it directly results in leaks or double frees */
+ if (in->type == GPU_FLOAT)
+ GPU_link(mat, "set_value", GPU_uniform(in->vec), &result);
+ else if (in->type == GPU_VEC3)
+ GPU_link(mat, "set_rgb", GPU_uniform(in->vec), &result);
+ else if (in->type == GPU_VEC4)
+ GPU_link(mat, "set_rgba", GPU_uniform(in->vec), &result);
+ else
+ BLI_assert(0);
+
+ return result;
+ }
+}
+
+static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
+{
+ if (node->id) {
+ GPUShadeInput shi;
+ GPUShadeResult shr;
+ bNodeSocket *sock;
+ char hasinput[NUM_MAT_IN] = {'\0'};
+ int i;
+
+ /* note: cannot use the in[]->hasinput flags directly, as these are not necessarily
+ * the constant input stack values (e.g. in case material node is inside a group).
+ * we just want to know if a node input uses external data or the material setting.
+ */
+ for (sock = node->inputs.first, i = 0; sock; sock = sock->next, ++i)
+ hasinput[i] = (sock->link != NULL);
+
+ GPU_shadeinput_set(mat, (Material *)node->id, &shi);
+
+ /* write values */
+ if (hasinput[MAT_IN_COLOR])
+ shi.rgb = gpu_get_input_link(mat, &in[MAT_IN_COLOR]);
+
+ if (hasinput[MAT_IN_SPEC])
+ shi.specrgb = gpu_get_input_link(mat, &in[MAT_IN_SPEC]);
+
+ if (hasinput[MAT_IN_REFL])
+ shi.refl = gpu_get_input_link(mat, &in[MAT_IN_REFL]);
+
+ /* retrieve normal */
+ if (hasinput[MAT_IN_NORMAL]) {
+ GPUNodeLink *tmp;
+ shi.vn = gpu_get_input_link(mat, &in[MAT_IN_NORMAL]);
+ if (GPU_material_use_world_space_shading(mat)) {
+ GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
+ GPU_link(mat, "direction_transform_m4v3", shi.vn, GPU_builtin(GPU_VIEW_MATRIX), &shi.vn);
+ }
+ GPU_link(mat, "vec_math_normalize", shi.vn, &shi.vn, &tmp);
+ }
+
+ /* custom option to flip normal */
+ if (node->custom1 & SH_NODE_MAT_NEG)
+ GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
+
+ if (node->type == SH_NODE_MATERIAL_EXT) {
+ if (hasinput[MAT_IN_MIR])
+ shi.mir = gpu_get_input_link(mat, &in[MAT_IN_MIR]);
+ if (hasinput[MAT_IN_AMB])
+ shi.amb = gpu_get_input_link(mat, &in[MAT_IN_AMB]);
+ if (hasinput[MAT_IN_EMIT])
+ shi.emit = gpu_get_input_link(mat, &in[MAT_IN_EMIT]);
+ if (hasinput[MAT_IN_SPECTRA])
+ shi.spectra = gpu_get_input_link(mat, &in[MAT_IN_SPECTRA]);
+ if (hasinput[MAT_IN_ALPHA])
+ shi.alpha = gpu_get_input_link(mat, &in[MAT_IN_ALPHA]);
+ }
+
+ GPU_shaderesult_set(&shi, &shr); /* clears shr */
+
+ /* write to outputs */
+ if (node->custom1 & SH_NODE_MAT_DIFF) {
+ out[MAT_OUT_COLOR].link = shr.combined;
+
+ if (!(node->custom1 & SH_NODE_MAT_SPEC)) {
+ GPUNodeLink *link;
+ GPU_link(mat, "vec_math_sub", shr.combined, shr.spec, &out[MAT_OUT_COLOR].link, &link);
+ }
+ }
+ else if (node->custom1 & SH_NODE_MAT_SPEC) {
+ out[MAT_OUT_COLOR].link = shr.spec;
+ }
+ else
+ GPU_link(mat, "set_rgb_zero", &out[MAT_OUT_COLOR].link);
+
+ GPU_link(mat, "mtex_alpha_to_col", out[MAT_OUT_COLOR].link, shr.alpha, &out[MAT_OUT_COLOR].link);
+
+ out[MAT_OUT_ALPHA].link = shr.alpha; //
+
+ if (node->custom1 & SH_NODE_MAT_NEG)
+ GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
+ out[MAT_OUT_NORMAL].link = shi.vn;
+ if (GPU_material_use_world_space_shading(mat)) {
+ GPU_link(mat, "vec_math_negate", out[MAT_OUT_NORMAL].link, &out[MAT_OUT_NORMAL].link);
+ GPU_link(mat, "direction_transform_m4v3", out[MAT_OUT_NORMAL].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[MAT_OUT_NORMAL].link);
+ }
+
+ if (node->type == SH_NODE_MATERIAL_EXT) {
+ out[MAT_OUT_DIFFUSE].link = shr.diff;
+ out[MAT_OUT_SPEC].link = shr.spec;
+ GPU_link(mat, "set_rgb_one", &out[MAT_OUT_AO].link);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void register_node_type_sh_material(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_MATERIAL, "Material", NODE_CLASS_INPUT, NODE_PREVIEW);
+ node_type_compatibility(&ntype, NODE_OLD_SHADING);
+ node_type_socket_templates(&ntype, sh_node_material_in, sh_node_material_out);
+ node_type_init(&ntype, node_shader_init_material);
+ node_type_exec(&ntype, NULL, NULL, node_shader_exec_material);
+ node_type_gpu(&ntype, gpu_shader_material);
+
+ nodeRegisterType(&ntype);
+}
+
+
+void register_node_type_sh_material_ext(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_MATERIAL_EXT, "Extended Material", NODE_CLASS_INPUT, NODE_PREVIEW);
+ node_type_compatibility(&ntype, NODE_OLD_SHADING);
+ node_type_socket_templates(&ntype, sh_node_material_ext_in, sh_node_material_ext_out);
+ node_type_init(&ntype, node_shader_init_material);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ node_type_exec(&ntype, NULL, NULL, node_shader_exec_material);
+ node_type_gpu(&ntype, gpu_shader_material);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.c b/source/blender/nodes/shader/nodes/node_shader_math.c
index 2be70b66b36..bf594325119 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.c
+++ b/source/blender/nodes/shader/nodes/node_shader_math.c
@@ -33,7 +33,7 @@
#include "node_shader_util.h"
-/* **************** SCALAR MATH ******************** */
+/* **************** SCALAR MATH ******************** */
static bNodeSocketTemplate sh_node_math_in[] = {
{ SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
{ SOCK_FLOAT, 1, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
@@ -45,15 +45,15 @@ static bNodeSocketTemplate sh_node_math_out[] = {
{ -1, 0, "" }
};
-static void node_shader_exec_math(void *UNUSED(data), int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out)
+static void node_shader_exec_math(void *UNUSED(data), int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out)
{
float a, b, r = 0.0f;
-
+
nodestack_get_vec(&a, SOCK_FLOAT, in[0]);
nodestack_get_vec(&b, SOCK_FLOAT, in[1]);
-
+
switch (node->custom1) {
-
+
case NODE_MATH_ADD:
r = a + b;
break;
@@ -147,7 +147,7 @@ static void node_shader_exec_math(void *UNUSED(data), int UNUSED(thread), bNode
}
else {
float y_mod_1 = fabsf(fmodf(b, 1.0f));
-
+
/* if input value is not nearly an integer, fall back to zero, nicer than straight rounding */
if (y_mod_1 > 0.999f || y_mod_1 < 0.001f) {
r = powf(a, floorf(b + 0.5f));
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c b/source/blender/nodes/shader/nodes/node_shader_mixRgb.c
index 37ec4d46226..054b02b220d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.c
@@ -54,7 +54,7 @@ static void node_shader_exec_mix_rgb(void *UNUSED(data), int UNUSED(thread), bNo
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
CLAMP(fac, 0.0f, 1.0f);
-
+
nodestack_get_vec(col, SOCK_VECTOR, in[1]);
nodestack_get_vec(vec, SOCK_VECTOR, in[2]);
diff --git a/source/blender/nodes/shader/nodes/node_shader_normal.c b/source/blender/nodes/shader/nodes/node_shader_normal.c
index 4bca5add106..265f6ac6fab 100644
--- a/source/blender/nodes/shader/nodes/node_shader_normal.c
+++ b/source/blender/nodes/shader/nodes/node_shader_normal.c
@@ -48,12 +48,12 @@ static bNodeSocketTemplate sh_node_normal_out[] = {
static void node_shader_exec_normal(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out)
{
float vec[3];
-
+
/* stack order input: normal */
/* stack order output: normal, value */
-
+
nodestack_get_vec(vec, SOCK_VECTOR, in[0]);
-
+
/* render normals point inside... the widget points outside */
out[1]->vec[0] = -dot_v3v3(vec, out[0]->vec);
}
@@ -67,12 +67,12 @@ static int gpu_shader_normal(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSE
void register_node_type_sh_normal(void)
{
static bNodeType ntype;
-
+
sh_node_type_base(&ntype, SH_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR, 0);
node_type_compatibility(&ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_normal_in, sh_node_normal_out);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_normal);
node_type_gpu(&ntype, gpu_shader_normal);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_output.c b/source/blender/nodes/shader/nodes/node_shader_output.c
new file mode 100644
index 00000000000..5b1a68b4bf9
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_output.c
@@ -0,0 +1,97 @@
+/*
+ * ***** 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) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/shader/nodes/node_shader_output.c
+ * \ingroup shdnodes
+ */
+
+
+#include "node_shader_util.h"
+
+/* **************** OUTPUT ******************** */
+static bNodeSocketTemplate sh_node_output_in[] = {
+ { SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
+ { SOCK_FLOAT, 1, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
+ { -1, 0, "" }
+};
+
+static void node_shader_exec_output(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **UNUSED(out))
+{
+ if (data) {
+ ShadeInput *shi = ((ShaderCallData *)data)->shi;
+ float col[4];
+
+ /* stack order input sockets: col, alpha, normal */
+ nodestack_get_vec(col, SOCK_VECTOR, in[0]);
+ nodestack_get_vec(col + 3, SOCK_FLOAT, in[1]);
+
+ if (shi->do_preview) {
+ BKE_node_preview_set_pixel(execdata->preview, col, shi->xs, shi->ys, shi->do_manage);
+ node->lasty = shi->ys;
+ }
+
+ if (node->flag & NODE_DO_OUTPUT) {
+ ShadeResult *shr = ((ShaderCallData *)data)->shr;
+
+ copy_v4_v4(shr->combined, col);
+ shr->alpha = col[3];
+
+ // copy_v3_v3(shr->nor, in[3]->vec);
+ }
+ }
+}
+
+static int gpu_shader_output(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
+{
+ GPUNodeLink *outlink;
+
+#if 0
+ if (in[1].hasinput)
+ GPU_material_enable_alpha(mat);
+#endif
+
+ GPU_stack_link(mat, "output_node", in, out, &outlink);
+ GPU_material_output_link(mat, outlink);
+
+ return 1;
+}
+
+void register_node_type_sh_output(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_OUTPUT, "Output", NODE_CLASS_OUTPUT, NODE_PREVIEW);
+ node_type_compatibility(&ntype, NODE_OLD_SHADING);
+ node_type_socket_templates(&ntype, sh_node_output_in, NULL);
+ node_type_exec(&ntype, NULL, NULL, node_shader_exec_output);
+ node_type_gpu(&ntype, gpu_shader_output);
+
+ /* Do not allow muting output node. */
+ node_type_internal_links(&ntype, NULL);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_world.c b/source/blender/nodes/shader/nodes/node_shader_output_world.c
index 17b4bfb6de6..2c115bdda20 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_world.c
+++ b/source/blender/nodes/shader/nodes/node_shader_output_world.c
@@ -56,7 +56,7 @@ void register_node_type_sh_output_world(void)
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, node_shader_gpu_output_world);
-
+
/* Do not allow muting output node. */
node_type_internal_links(&ntype, NULL);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c b/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c
index 19f31f77989..148f8e99c8f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c
@@ -48,7 +48,7 @@ static void node_shader_exec_sephsv(void *UNUSED(data), int UNUSED(thread), bNod
{
float col[3];
nodestack_get_vec(col, SOCK_VECTOR, in[0]);
-
+
rgb_to_hsv(col[0], col[1], col[2],
&out[0]->vec[0], &out[1]->vec[0], &out[2]->vec[0]);
}
@@ -90,7 +90,7 @@ static void node_shader_exec_combhsv(void *UNUSED(data), int UNUSED(thread), bNo
nodestack_get_vec(&h, SOCK_FLOAT, in[0]);
nodestack_get_vec(&s, SOCK_FLOAT, in[1]);
nodestack_get_vec(&v, SOCK_FLOAT, in[2]);
-
+
hsv_to_rgb(h, s, v, &out[0]->vec[0], &out[0]->vec[1], &out[0]->vec[2]);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c
index 3f866af5de4..bd914399a28 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c
@@ -48,7 +48,7 @@ static void node_shader_exec_seprgb(void *UNUSED(data), int UNUSED(thread), bNod
{
float col[3];
nodestack_get_vec(col, SOCK_VECTOR, in[0]);
-
+
out[0]->vec[0] = col[0];
out[1]->vec[0] = col[1];
out[2]->vec[0] = col[2];
@@ -92,7 +92,7 @@ static void node_shader_exec_combrgb(void *UNUSED(data), int UNUSED(thread), bNo
nodestack_get_vec(&r, SOCK_FLOAT, in[0]);
nodestack_get_vec(&g, SOCK_FLOAT, in[1]);
nodestack_get_vec(&b, SOCK_FLOAT, in[2]);
-
+
out[0]->vec[0] = r;
out[0]->vec[1] = g;
out[0]->vec[2] = b;
diff --git a/source/blender/nodes/shader/nodes/node_shader_squeeze.c b/source/blender/nodes/shader/nodes/node_shader_squeeze.c
index 2175a7c564f..e46494efd34 100644
--- a/source/blender/nodes/shader/nodes/node_shader_squeeze.c
+++ b/source/blender/nodes/shader/nodes/node_shader_squeeze.c
@@ -32,7 +32,7 @@
#include "node_shader_util.h"
-/* **************** VALUE SQUEEZE ******************** */
+/* **************** VALUE SQUEEZE ******************** */
static bNodeSocketTemplate sh_node_squeeze_in[] = {
{ SOCK_FLOAT, 1, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE},
{ SOCK_FLOAT, 1, N_("Width"), 1.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE},
@@ -48,7 +48,7 @@ static bNodeSocketTemplate sh_node_squeeze_out[] = {
static void node_shader_exec_squeeze(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out)
{
float vec[3];
-
+
nodestack_get_vec(vec, SOCK_FLOAT, in[0]);
nodestack_get_vec(vec + 1, SOCK_FLOAT, in[1]);
nodestack_get_vec(vec + 2, SOCK_FLOAT, in[2]);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c
index c919f8efa4e..67fe6d08ffd 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c
@@ -54,7 +54,7 @@ static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node)
NodeTexBrick *tex = MEM_callocN(sizeof(NodeTexBrick), "NodeTexBrick");
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
BKE_texture_colormapping_default(&tex->base.color_mapping);
-
+
tex->offset = 0.5f;
tex->squash = 1.0f;
tex->offset_freq = 2;
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.c b/source/blender/nodes/shader/nodes/node_shader_tex_coord.c
index 111a9cbfbc0..360b28d768a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.c
@@ -49,7 +49,7 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, bNode *node, bNodeExecDat
GPUMatType type = GPU_Material_get_type(mat);
GPU_link(mat, "generated_from_orco", orco, &orco);
-
+
if (type == GPU_MATERIAL_TYPE_WORLD) {
return GPU_stack_link(mat, node, "node_tex_coord_background", in, out,
GPU_builtin(GPU_VIEW_POSITION), GPU_builtin(GPU_VIEW_NORMAL),
@@ -59,7 +59,7 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, bNode *node, bNodeExecDat
else {
return GPU_stack_link(mat, node, "node_tex_coord", in, out,
GPU_builtin(GPU_VIEW_POSITION), GPU_builtin(GPU_VIEW_NORMAL),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
+ GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
GPU_builtin(GPU_CAMERA_TEXCO_FACTORS), orco, mtface);
}
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
index 378da163f64..d441a674838 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
@@ -66,20 +66,20 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, bNode *node, bNodeE
if (!in[0].link) {
GPUMatType type = GPU_Material_get_type(mat);
-
+
if (type == GPU_MATERIAL_TYPE_MESH)
in[0].link = GPU_builtin(GPU_VIEW_POSITION);
else
GPU_link(mat, "background_transform_to_world", GPU_builtin(GPU_VIEW_POSITION), &in[0].link);
}
-
+
node_shader_gpu_tex_mapping(mat, node, in, out);
if (tex->projection == SHD_PROJ_EQUIRECTANGULAR)
GPU_stack_link(mat, node, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata));
else
GPU_stack_link(mat, node, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata));
-
+
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
GPU_material_do_color_management(mat))
diff --git a/source/blender/nodes/shader/nodes/node_shader_texture.c b/source/blender/nodes/shader/nodes/node_shader_texture.c
new file mode 100644
index 00000000000..737ec7d1c4b
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_texture.c
@@ -0,0 +1,166 @@
+/*
+ * ***** 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) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/shader/nodes/node_shader_texture.c
+ * \ingroup shdnodes
+ */
+
+#include "DNA_texture_types.h"
+
+#include "node_shader_util.h"
+
+#include "GPU_material.h"
+
+/* **************** TEXTURE ******************** */
+static bNodeSocketTemplate sh_node_texture_in[] = {
+ { SOCK_VECTOR, 1, "Vector", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, /* no limit */
+ { -1, 0, "" }
+};
+static bNodeSocketTemplate sh_node_texture_out[] = {
+ { SOCK_FLOAT, 0, N_("Value"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_NO_INTERNAL_LINK},
+ { SOCK_RGBA, 0, N_("Color"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_NO_INTERNAL_LINK},
+ { SOCK_VECTOR, 0, N_("Normal"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_NO_INTERNAL_LINK},
+ { -1, 0, "" }
+};
+
+static void node_shader_exec_texture(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
+{
+ if (data && node->id) {
+ ShadeInput *shi = ((ShaderCallData *)data)->shi;
+ TexResult texres;
+ bNodeSocket *sock_vector = node->inputs.first;
+ float vec[3], nor[3] = {0.0f, 0.0f, 0.0f};
+ int retval;
+ short which_output = node->custom1;
+
+ short thread = shi->thread;
+
+ /* out: value, color, normal */
+
+ /* we should find out if a normal as output is needed, for now we do all */
+ texres.nor = nor;
+ texres.tr = texres.tg = texres.tb = 0.0f;
+
+ /* don't use in[0]->hasinput, see material node for explanation */
+ if (sock_vector->link) {
+ nodestack_get_vec(vec, SOCK_VECTOR, in[0]);
+
+ if (in[0]->datatype == NS_OSA_VECTORS) {
+ float *fp = in[0]->data;
+ retval = multitex_nodes((Tex *)node->id, vec, fp, fp + 3, shi->osatex, &texres, thread, which_output, NULL, NULL, NULL);
+ }
+ else if (in[0]->datatype == NS_OSA_VALUES) {
+ const float *fp = in[0]->data;
+ float dxt[3], dyt[3];
+
+ dxt[0] = fp[0]; dxt[1] = dxt[2] = 0.0f;
+ dyt[0] = fp[1]; dyt[1] = dyt[2] = 0.0f;
+ retval = multitex_nodes((Tex *)node->id, vec, dxt, dyt, shi->osatex, &texres, thread, which_output, NULL, NULL, NULL);
+ }
+ else
+ retval = multitex_nodes((Tex *)node->id, vec, NULL, NULL, 0, &texres, thread, which_output, NULL, NULL, NULL);
+ }
+ else {
+ copy_v3_v3(vec, shi->lo);
+ retval = multitex_nodes((Tex *)node->id, vec, NULL, NULL, 0, &texres, thread, which_output, NULL, NULL, NULL);
+ }
+
+ /* stupid exception */
+ if ( ((Tex *)node->id)->type == TEX_STUCCI) {
+ texres.tin = 0.5f + 0.7f * texres.nor[0];
+ CLAMP(texres.tin, 0.0f, 1.0f);
+ }
+
+ /* intensity and color need some handling */
+ if (texres.talpha)
+ out[0]->vec[0] = texres.ta;
+ else
+ out[0]->vec[0] = texres.tin;
+
+ if ((retval & TEX_RGB) == 0) {
+ copy_v3_fl(out[1]->vec, out[0]->vec[0]);
+ out[1]->vec[3] = 1.0f;
+ }
+ else {
+ copy_v3_v3(out[1]->vec, &texres.tr);
+ out[1]->vec[3] = 1.0f;
+ }
+
+ copy_v3_v3(out[2]->vec, nor);
+
+ if (shi->do_preview) {
+ BKE_node_preview_set_pixel(execdata->preview, out[1]->vec, shi->xs, shi->ys, shi->do_manage);
+ }
+
+ }
+}
+
+static int gpu_shader_texture(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
+{
+ Tex *tex = (Tex *)node->id;
+
+ if (tex && tex->ima && (tex->type == TEX_IMAGE || tex->type == TEX_ENVMAP)) {
+ if (tex->type == TEX_IMAGE) {
+ GPUNodeLink *texlink = GPU_image(tex->ima, &tex->iuser, false);
+ GPU_stack_link(mat, "texture_image", in, out, texlink);
+ }
+ else { /* TEX_ENVMAP */
+ if (!in[0].link)
+ in[0].link = GPU_uniform(in[0].vec);
+ if (!GPU_material_use_world_space_shading(mat))
+ GPU_link(mat, "direction_transform_m4v3", in[0].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &in[0].link);
+ GPU_link(mat, "mtex_cube_map_refl_from_refldir",
+ GPU_cube_map(tex->ima, &tex->iuser, false), in[0].link, &out[0].link, &out[1].link);
+ GPU_link(mat, "color_to_normal", out[1].link, &out[2].link);
+ }
+
+ ImBuf *ibuf = BKE_image_acquire_ibuf(tex->ima, &tex->iuser, NULL);
+ if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
+ GPU_material_do_color_management(mat))
+ {
+ GPU_link(mat, "srgb_to_linearrgb", out[1].link, &out[1].link);
+ }
+ BKE_image_release_ibuf(tex->ima, ibuf, NULL);
+
+ return true;
+ }
+
+ return false;
+}
+
+void register_node_type_sh_texture(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW);
+ node_type_compatibility(&ntype, NODE_OLD_SHADING);
+ node_type_socket_templates(&ntype, sh_node_texture_in, sh_node_texture_out);
+ node_type_exec(&ntype, NULL, NULL, node_shader_exec_texture);
+ node_type_gpu(&ntype, gpu_shader_texture);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.c b/source/blender/nodes/shader/nodes/node_shader_valToRgb.c
index 5041741beb1..a1879df3a18 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.c
@@ -48,7 +48,7 @@ static void node_shader_exec_valtorgb(void *UNUSED(data), int UNUSED(thread), bN
{
/* stack order in: fac */
/* stack order out: col, alpha */
-
+
if (node->storage) {
float fac;
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
diff --git a/source/blender/nodes/shader/nodes/node_shader_vectMath.c b/source/blender/nodes/shader/nodes/node_shader_vectMath.c
index 2b060806819..ca5291e6041 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vectMath.c
+++ b/source/blender/nodes/shader/nodes/node_shader_vectMath.c
@@ -31,7 +31,7 @@
#include "node_shader_util.h"
-/* **************** VECTOR MATH ******************** */
+/* **************** VECTOR MATH ******************** */
static bNodeSocketTemplate sh_node_vect_math_in[] = {
{ SOCK_VECTOR, 1, N_("Vector"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
{ SOCK_VECTOR, 1, N_("Vector"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
@@ -44,32 +44,32 @@ static bNodeSocketTemplate sh_node_vect_math_out[] = {
{ -1, 0, "" }
};
-static void node_shader_exec_vect_math(void *UNUSED(data), int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out)
-{
+static void node_shader_exec_vect_math(void *UNUSED(data), int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out)
+{
float vec1[3], vec2[3];
-
+
nodestack_get_vec(vec1, SOCK_VECTOR, in[0]);
nodestack_get_vec(vec2, SOCK_VECTOR, in[1]);
-
+
if (node->custom1 == 0) { /* Add */
out[0]->vec[0] = vec1[0] + vec2[0];
out[0]->vec[1] = vec1[1] + vec2[1];
out[0]->vec[2] = vec1[2] + vec2[2];
-
+
out[1]->vec[0] = (fabsf(out[0]->vec[0]) + fabsf(out[0]->vec[1]) + fabsf(out[0]->vec[2])) / 3.0f;
}
else if (node->custom1 == 1) { /* Subtract */
out[0]->vec[0] = vec1[0] - vec2[0];
out[0]->vec[1] = vec1[1] - vec2[1];
out[0]->vec[2] = vec1[2] - vec2[2];
-
+
out[1]->vec[0] = (fabsf(out[0]->vec[0]) + fabsf(out[0]->vec[1]) + fabsf(out[0]->vec[2])) / 3.0f;
}
else if (node->custom1 == 2) { /* Average */
out[0]->vec[0] = vec1[0] + vec2[0];
out[0]->vec[1] = vec1[1] + vec2[1];
out[0]->vec[2] = vec1[2] + vec2[2];
-
+
out[1]->vec[0] = normalize_v3(out[0]->vec);
}
else if (node->custom1 == 3) { /* Dot product */
@@ -79,7 +79,7 @@ static void node_shader_exec_vect_math(void *UNUSED(data), int UNUSED(thread), b
out[0]->vec[0] = (vec1[1] * vec2[2]) - (vec1[2] * vec2[1]);
out[0]->vec[1] = (vec1[2] * vec2[0]) - (vec1[0] * vec2[2]);
out[0]->vec[2] = (vec1[0] * vec2[1]) - (vec1[1] * vec2[0]);
-
+
out[1]->vec[0] = normalize_v3(out[0]->vec);
}
else if (node->custom1 == 5) { /* Normalize */
@@ -93,10 +93,10 @@ static void node_shader_exec_vect_math(void *UNUSED(data), int UNUSED(thread), b
out[0]->vec[1] = vec2[1];
out[0]->vec[2] = vec2[2];
}
-
+
out[1]->vec[0] = normalize_v3(out[0]->vec);
}
-
+
}
static int gpu_shader_vect_math(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
@@ -132,7 +132,7 @@ static int gpu_shader_vect_math(GPUMaterial *mat, bNode *node, bNodeExecData *UN
default:
return false;
}
-
+
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vectTransform.c b/source/blender/nodes/shader/nodes/node_shader_vectTransform.c
index 8183b6c45cb..d0b16dd5886 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vectTransform.c
+++ b/source/blender/nodes/shader/nodes/node_shader_vectTransform.c
@@ -28,10 +28,10 @@
/** \file blender/nodes/shader/nodes/node_shader_vectTransform.c
* \ingroup shdnodes
*/
-
+
#include "../node_shader_util.h"
-/* **************** Vector Transform ******************** */
+/* **************** Vector Transform ******************** */
static bNodeSocketTemplate sh_node_vect_transform_in[] = {
{ SOCK_VECTOR, 1, N_("Vector"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
{ -1, 0, "" }
@@ -45,10 +45,10 @@ static bNodeSocketTemplate sh_node_vect_transform_out[] = {
static void node_shader_init_vect_transform(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeShaderVectTransform *vect = MEM_callocN(sizeof(NodeShaderVectTransform), "NodeShaderVectTransform");
-
+
/* Convert World into Object Space per default */
vect->convert_to = 1;
-
+
node->storage = vect;
}
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 0d3d3f261de..5dbcece0a84 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -70,7 +70,7 @@ static void texture_get_from_context(
if (snode->texfrom == SNODE_TEX_BRUSH) {
struct Brush *brush = NULL;
-
+
if (ob && (ob->mode & OB_MODE_SCULPT))
brush = BKE_paint_brush(&scene->toolsettings->sculpt->paint);
else
@@ -119,11 +119,11 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
static void localize(bNodeTree *localtree, bNodeTree *UNUSED(ntree))
{
bNode *node, *node_next;
-
+
/* replace muted nodes and reroute nodes by internal links */
for (node = localtree->nodes.first; node; node = node_next) {
node_next = node->next;
-
+
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
nodeInternalRelink(localtree, node);
nodeFreeNode(localtree, node);
@@ -149,7 +149,7 @@ static void local_merge(bNodeTree *localtree, bNodeTree *ntree)
static void update(bNodeTree *ntree)
{
ntree_update_reroute_nodes(ntree);
-
+
if (ntree->update & NTREE_UPDATE_NODES) {
/* clean up preview cache, in case nodes have been removed */
BKE_node_preview_remove_unused(ntree);
@@ -161,31 +161,31 @@ bNodeTreeType *ntreeType_Texture;
void register_node_tree_type_tex(void)
{
bNodeTreeType *tt = ntreeType_Texture = MEM_callocN(sizeof(bNodeTreeType), "texture node tree type");
-
+
tt->type = NTREE_TEXTURE;
strcpy(tt->idname, "TextureNodeTree");
strcpy(tt->ui_name, "Texture Editor");
tt->ui_icon = 0; /* defined in drawnode.c */
strcpy(tt->ui_description, "Texture nodes");
-
+
tt->foreach_nodeclass = foreach_nodeclass;
tt->update = update;
tt->localize = localize;
tt->local_sync = local_sync;
tt->local_merge = local_merge;
tt->get_from_context = texture_get_from_context;
-
+
tt->ext.srna = &RNA_TextureNodeTree;
-
+
ntreeTypeAdd(tt);
}
int ntreeTexTagAnimated(bNodeTree *ntree)
{
bNode *node;
-
+
if (ntree == NULL) return 0;
-
+
for (node = ntree->nodes.first; node; node = node->next) {
if (node->type == TEX_NODE_CURVE_TIME) {
nodeUpdate(ntree, node);
@@ -197,7 +197,7 @@ int ntreeTexTagAnimated(bNodeTree *ntree)
}
}
}
-
+
return 0;
}
@@ -205,16 +205,16 @@ bNodeTreeExec *ntreeTexBeginExecTree_internal(bNodeExecContext *context, bNodeTr
{
bNodeTreeExec *exec;
bNode *node;
-
+
/* common base initialization */
exec = ntree_exec_begin(context, ntree, parent_key);
-
+
/* allocate the thread stack listbase array */
exec->threadstack = MEM_callocN(BLENDER_MAX_THREADS * sizeof(ListBase), "thread stack array");
-
+
for (node = exec->nodetree->nodes.first; node; node = node->next)
node->need_exec = 1;
-
+
return exec;
}
@@ -222,22 +222,22 @@ bNodeTreeExec *ntreeTexBeginExecTree(bNodeTree *ntree)
{
bNodeExecContext context;
bNodeTreeExec *exec;
-
+
/* XXX hack: prevent exec data from being generated twice.
* this should be handled by the renderer!
*/
if (ntree->execdata)
return ntree->execdata;
-
+
context.previews = ntree->previews;
-
+
exec = ntreeTexBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE);
-
+
/* XXX this should not be necessary, but is still used for cmp/sha/tex nodes,
* which only store the ntree pointer. Should be fixed at some point!
*/
ntree->execdata = exec;
-
+
return exec;
}
@@ -247,7 +247,7 @@ static void tex_free_delegates(bNodeTreeExec *exec)
bNodeThreadStack *nts;
bNodeStack *ns;
int th, a;
-
+
for (th = 0; th < BLENDER_MAX_THREADS; th++)
for (nts = exec->threadstack[th].first; nts; nts = nts->next)
for (ns = nts->stack, a = 0; a < exec->stacksize; a++, ns++)
@@ -259,20 +259,20 @@ void ntreeTexEndExecTree_internal(bNodeTreeExec *exec)
{
bNodeThreadStack *nts;
int a;
-
+
if (exec->threadstack) {
tex_free_delegates(exec);
-
+
for (a = 0; a < BLENDER_MAX_THREADS; a++) {
for (nts = exec->threadstack[a].first; nts; nts = nts->next)
if (nts->stack) MEM_freeN(nts->stack);
BLI_freelistN(&exec->threadstack[a]);
}
-
+
MEM_freeN(exec->threadstack);
exec->threadstack = NULL;
}
-
+
ntree_exec_end(exec);
}
@@ -282,7 +282,7 @@ void ntreeTexEndExecTree(bNodeTreeExec *exec)
/* exec may get freed, so assign ntree */
bNodeTree *ntree = exec->nodetree;
ntreeTexEndExecTree_internal(exec);
-
+
/* XXX clear nodetree backpointer to exec data, same problem as noted in ntreeBeginExecTree */
ntree->execdata = NULL;
}
@@ -318,7 +318,7 @@ int ntreeTexExecTree(
data.which_output = which_output;
data.cfra = cfra;
data.mtex = mtex;
-
+
/* ensure execdata is only initialized once */
if (!exec) {
BLI_thread_lock(LOCK_NODES);
@@ -328,7 +328,7 @@ int ntreeTexExecTree(
exec = nodes->execdata;
}
-
+
nts = ntreeGetThreadStack(exec, thread);
ntreeExecThreadNodes(exec, nts, &data, thread);
ntreeReleaseThreadStack(nts);
diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c
index 8cb61478c41..a6b0d060d93 100644
--- a/source/blender/nodes/texture/node_texture_util.c
+++ b/source/blender/nodes/texture/node_texture_util.c
@@ -58,7 +58,7 @@ int tex_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
void tex_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
-
+
ntype->poll = tex_node_poll_default;
ntype->insert_link = node_insert_link_default;
ntype->update_internal_links = node_update_internal_links_default;
@@ -80,7 +80,7 @@ static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, sho
TexDelegate *dg = in->data;
if (dg) {
tex_call_delegate(dg, in->vec, params, thread);
-
+
if (in->hasoutput && in->sockettype == SOCK_FLOAT)
in->vec[1] = in->vec[2] = in->vec[0];
}
@@ -95,12 +95,12 @@ void tex_input_vec(float *out, bNodeStack *in, TexParams *params, short thread)
void tex_input_rgba(float *out, bNodeStack *in, TexParams *params, short thread)
{
tex_input(out, 4, in, params, thread);
-
+
if (in->hasoutput && in->sockettype == SOCK_FLOAT) {
out[1] = out[2] = out[0];
out[3] = 1;
}
-
+
if (in->hasoutput && in->sockettype == SOCK_VECTOR) {
out[0] = out[0] * 0.5f + 0.5f;
out[1] = out[1] * 0.5f + 0.5f;
@@ -132,7 +132,7 @@ void tex_do_preview(bNodePreview *preview, const float coord[2], const float col
if (preview) {
int xs = ((coord[0] + 1.0f) * 0.5f) * preview->xsize;
int ys = ((coord[1] + 1.0f) * 0.5f) * preview->ysize;
-
+
BKE_node_preview_set_pixel(preview, col, xs, ys, do_manage);
}
}
@@ -140,7 +140,7 @@ void tex_do_preview(bNodePreview *preview, const float coord[2], const float col
void tex_output(bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack *out, TexFn texfn, TexCallData *cdata)
{
TexDelegate *dg;
-
+
if (node->flag & NODE_MUTED) {
/* do not add a delegate if the node is muted */
return;
@@ -175,9 +175,9 @@ void ntreeTexCheckCyclics(struct bNodeTree *ntree)
}
else {
Tex *tex = (Tex *)node->id;
-
+
node->custom2 = 0;
-
+
node->custom1 = 1;
if (tex->use_nodes && tex->nodetree) {
ntreeTexCheckCyclics(tex->nodetree);
diff --git a/source/blender/nodes/texture/nodes/node_texture_at.c b/source/blender/nodes/texture/nodes/node_texture_at.c
index bd37a73c776..690d87b42a9 100644
--- a/source/blender/nodes/texture/nodes/node_texture_at.c
+++ b/source/blender/nodes/texture/nodes/node_texture_at.c
@@ -48,7 +48,7 @@ static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **
TexParams np = *p;
float new_co[3];
np.co = new_co;
-
+
tex_input_vec(new_co, in[1], p, thread);
tex_input_rgba(out, in[0], &np, thread);
}
@@ -61,11 +61,11 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_at(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_AT, "At", NODE_CLASS_DISTORT, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_size(&ntype, 140, 100, 320);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_bricks.c b/source/blender/nodes/texture/nodes/node_texture_bricks.c
index 802cfb97533..43af02acdf2 100644
--- a/source/blender/nodes/texture/nodes/node_texture_bricks.c
+++ b/source/blender/nodes/texture/nodes/node_texture_bricks.c
@@ -67,43 +67,43 @@ static float noise(int n) /* fast integer noise */
static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread)
{
const float *co = p->co;
-
+
float x = co[0];
float y = co[1];
-
+
int bricknum, rownum;
float offset = 0;
float ins_x, ins_y;
float tint;
-
+
float bricks1[4];
float bricks2[4];
float mortar[4];
-
+
float mortar_thickness = tex_input_value(in[3], p, thread);
float bias = tex_input_value(in[4], p, thread);
float brick_width = tex_input_value(in[5], p, thread);
float row_height = tex_input_value(in[6], p, thread);
-
+
tex_input_rgba(bricks1, in[0], p, thread);
tex_input_rgba(bricks2, in[1], p, thread);
tex_input_rgba(mortar, in[2], p, thread);
-
+
rownum = (int)floor(y / row_height);
-
+
if (node->custom1 && node->custom2) {
brick_width *= ((int)(rownum) % node->custom2) ? 1.0f : node->custom4; /* squash */
offset = ((int)(rownum) % node->custom1) ? 0 : (brick_width * node->custom3); /* offset */
}
-
+
bricknum = (int)floor((x + offset) / brick_width);
-
+
ins_x = (x + offset) - brick_width * bricknum;
ins_y = y - row_height * rownum;
-
+
tint = noise((rownum << 16) + (bricknum & 0xFFFF)) + bias;
CLAMP(tint, 0.0f, 1.0f);
-
+
if (ins_x < mortar_thickness || ins_y < mortar_thickness ||
ins_x > (brick_width - mortar_thickness) ||
ins_y > (row_height - mortar_thickness))
@@ -124,12 +124,12 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_bricks(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_BRICKS, "Bricks", NODE_CLASS_PATTERN, NODE_PREVIEW);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_init(&ntype, init);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_checker.c b/source/blender/nodes/texture/nodes/node_texture_checker.c
index b38c883e3b8..d7ad642d474 100644
--- a/source/blender/nodes/texture/nodes/node_texture_checker.c
+++ b/source/blender/nodes/texture/nodes/node_texture_checker.c
@@ -51,12 +51,12 @@ static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **
float y = p->co[1];
float z = p->co[2];
float sz = tex_input_value(in[2], p, thread);
-
+
/* 0.00001 because of unit sized stuff */
int xi = (int)fabs(floor(0.00001f + x / sz));
int yi = (int)fabs(floor(0.00001f + y / sz));
int zi = (int)fabs(floor(0.00001f + z / sz));
-
+
if ( (xi % 2 == yi % 2) == (zi % 2) ) {
tex_input_rgba(out, in[0], p, thread);
}
@@ -73,10 +73,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_checker(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_CHECKER, "Checker", NODE_CLASS_PATTERN, NODE_PREVIEW);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_common.c b/source/blender/nodes/texture/nodes/node_texture_common.c
index 6f9a39d7524..20b1815e436 100644
--- a/source/blender/nodes/texture/nodes/node_texture_common.c
+++ b/source/blender/nodes/texture/nodes/node_texture_common.c
@@ -50,7 +50,7 @@ static void copy_stack(bNodeStack *to, bNodeStack *from)
copy_v4_v4(to->vec, from->vec);
to->data = from->data;
to->datatype = from->datatype;
-
+
/* tag as copy to prevent freeing */
to->is_copy = 1;
}
@@ -62,20 +62,20 @@ static void *group_initexec(bNodeExecContext *context, bNode *node, bNodeInstanc
{
bNodeTree *ngroup = (bNodeTree *)node->id;
void *exec;
-
+
if (!ngroup)
return NULL;
-
+
/* initialize the internal node tree execution */
exec = ntreeTexBeginExecTree_internal(context, ngroup, key);
-
+
return exec;
}
static void group_freeexec(void *nodedata)
{
bNodeTreeExec *gexec = (bNodeTreeExec *)nodedata;
-
+
ntreeTexEndExecTree_internal(gexec);
}
@@ -89,7 +89,7 @@ static void group_copy_inputs(bNode *gnode, bNodeStack **in, bNodeStack *gstack)
bNodeSocket *sock;
bNodeStack *ns;
int a;
-
+
for (node = ngroup->nodes.first; node; node = node->next) {
if (node->type == NODE_GROUP_INPUT) {
for (sock = node->outputs.first, a = 0; sock; sock = sock->next, ++a) {
@@ -113,7 +113,7 @@ static void group_copy_outputs(bNode *gnode, bNodeStack **out, bNodeStack *gstac
bNodeSocket *sock;
bNodeStack *ns;
int a;
-
+
for (node = ngroup->nodes.first; node; node = node->next) {
if (node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT)) {
for (sock = node->inputs.first, a = 0; sock; sock = sock->next, ++a) {
@@ -133,10 +133,10 @@ static void group_execute(void *data, int thread, struct bNode *node, bNodeExecD
{
bNodeTreeExec *exec = execdata->data;
bNodeThreadStack *nts;
-
+
if (!exec)
return;
-
+
/* XXX same behavior as trunk: all nodes inside group are executed.
* it's stupid, but just makes it work. compo redesign will do this better.
*/
@@ -145,13 +145,13 @@ static void group_execute(void *data, int thread, struct bNode *node, bNodeExecD
for (inode = exec->nodetree->nodes.first; inode; inode = inode->next)
inode->need_exec = 1;
}
-
+
nts = ntreeGetThreadStack(exec, thread);
-
+
group_copy_inputs(node, in, nts->stack);
ntreeExecThreadNodes(exec, nts, data, thread);
group_copy_outputs(node, out, nts->stack);
-
+
ntreeReleaseThreadStack(nts);
}
@@ -171,12 +171,12 @@ void register_node_type_tex_group(void)
ntype.ext.srna = RNA_struct_find("TextureNodeGroup");
BLI_assert(ntype.ext.srna != NULL);
RNA_struct_blender_type_set(ntype.ext.srna, &ntype);
-
+
node_type_socket_templates(&ntype, NULL, NULL);
node_type_size(&ntype, 140, 60, 400);
node_type_label(&ntype, node_group_label);
node_type_update(&ntype, NULL, node_group_verify);
node_type_exec(&ntype, group_initexec, group_freeexec, group_execute);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_compose.c b/source/blender/nodes/texture/nodes/node_texture_compose.c
index 092bf919a67..002da4428cc 100644
--- a/source/blender/nodes/texture/nodes/node_texture_compose.c
+++ b/source/blender/nodes/texture/nodes/node_texture_compose.c
@@ -30,7 +30,7 @@
*/
-#include "node_texture_util.h"
+#include "node_texture_util.h"
#include "NOD_texture.h"
static bNodeSocketTemplate inputs[] = {
@@ -60,10 +60,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_compose(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_COMPOSE, "Combine RGBA", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_coord.c b/source/blender/nodes/texture/nodes/node_texture_coord.c
index e76987da61b..e698ffd0a54 100644
--- a/source/blender/nodes/texture/nodes/node_texture_coord.c
+++ b/source/blender/nodes/texture/nodes/node_texture_coord.c
@@ -51,11 +51,11 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_coord(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_COORD, "Coordinates", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, NULL, outputs);
node_type_storage(&ntype, "", NULL, NULL);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_curves.c b/source/blender/nodes/texture/nodes/node_texture_curves.c
index c25c312626e..cc734df9586 100644
--- a/source/blender/nodes/texture/nodes/node_texture_curves.c
+++ b/source/blender/nodes/texture/nodes/node_texture_curves.c
@@ -45,10 +45,10 @@ static void time_colorfn(float *out, TexParams *p, bNode *node, bNodeStack **UNU
{
/* stack order output: fac */
float fac = 0.0f;
-
+
if (node->custom1 < node->custom2)
fac = (p->cfra - node->custom1) / (float)(node->custom2 - node->custom1);
-
+
curvemapping_initialize(node->storage);
fac = curvemapping_evaluateF(node->storage, 0, fac);
out[0] = CLAMPIS(fac, 0.0f, 1.0f);
@@ -70,14 +70,14 @@ static void time_init(bNodeTree *UNUSED(ntree), bNode *node)
void register_node_type_tex_curve_time(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_CURVE_TIME, "Time", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, NULL, time_outputs);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_init(&ntype, time_init);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
node_type_exec(&ntype, node_initexec_curves, NULL, time_exec);
-
+
nodeRegisterType(&ntype);
}
@@ -96,7 +96,7 @@ static void rgb_colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in,
{
float cin[4];
tex_input_rgba(cin, in[0], p, thread);
-
+
curvemapping_evaluateRGBF(node->storage, out, cin);
out[3] = cin[3];
}
@@ -114,13 +114,13 @@ static void rgb_init(bNodeTree *UNUSED(ntree), bNode *node)
void register_node_type_tex_curve_rgb(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, rgb_inputs, rgb_outputs);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_init(&ntype, rgb_init);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
node_type_exec(&ntype, node_initexec_curves, NULL, rgb_exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_decompose.c b/source/blender/nodes/texture/nodes/node_texture_decompose.c
index 16938bee8e4..392cff970e7 100644
--- a/source/blender/nodes/texture/nodes/node_texture_decompose.c
+++ b/source/blender/nodes/texture/nodes/node_texture_decompose.c
@@ -30,7 +30,7 @@
*/
-#include "node_texture_util.h"
+#include "node_texture_util.h"
#include "NOD_texture.h"
#include <math.h>
@@ -81,10 +81,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_decompose(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE, "Separate RGBA", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_distance.c b/source/blender/nodes/texture/nodes/node_texture_distance.c
index 3fdcbe870a9..7cd032f7d59 100644
--- a/source/blender/nodes/texture/nodes/node_texture_distance.c
+++ b/source/blender/nodes/texture/nodes/node_texture_distance.c
@@ -64,11 +64,11 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_distance(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_DISTANCE, "Distance", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_storage(&ntype, "", NULL, NULL);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c b/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c
index 9c86f2d0a4c..8316579af93 100644
--- a/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c
+++ b/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c
@@ -51,7 +51,7 @@ static void do_hue_sat_fac(bNode *UNUSED(node), float *out, float hue, float sat
{
if (fac != 0 && (hue != 0.5f || sat != 1 || val != 1)) {
float col[3], hsv[3], mfac = 1.0f - fac;
-
+
rgb_to_hsv(in[0], in[1], in[2], hsv, hsv + 1, hsv + 2);
hsv[0] += (hue - 0.5f);
if (hsv[0] > 1.0f) hsv[0] -= 1.0f; else if (hsv[0] < 0.0f) hsv[0] += 1.0f;
@@ -71,19 +71,19 @@ static void do_hue_sat_fac(bNode *UNUSED(node), float *out, float hue, float sat
}
static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread)
-{
+{
float hue = tex_input_value(in[0], p, thread);
float sat = tex_input_value(in[1], p, thread);
float val = tex_input_value(in[2], p, thread);
float fac = tex_input_value(in[3], p, thread);
-
+
float col[4];
tex_input_rgba(col, in[4], p, thread);
-
+
hue += 0.5f; /* [-0.5, 0.5] -> [0, 1] */
-
+
do_hue_sat_fac(node, out, hue, sat, val, col, fac);
-
+
out[3] = col[3];
}
@@ -95,11 +95,11 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_hue_sat(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_image.c b/source/blender/nodes/texture/nodes/node_texture_image.c
index e36df01f489..0d70eff15e3 100644
--- a/source/blender/nodes/texture/nodes/node_texture_image.c
+++ b/source/blender/nodes/texture/nodes/node_texture_image.c
@@ -44,37 +44,37 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **UNUSED(i
float y = p->co[1];
Image *ima = (Image *)node->id;
ImageUser *iuser = (ImageUser *)node->storage;
-
+
if (ima) {
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (ibuf) {
float xsize, ysize;
float xoff, yoff;
int px, py;
-
+
const float *result;
xsize = ibuf->x / 2;
ysize = ibuf->y / 2;
xoff = yoff = -1;
-
+
px = (int)( (x - xoff) * xsize);
py = (int)( (y - yoff) * ysize);
-
+
if ( (!xsize) || (!ysize) ) return;
-
+
if (!ibuf->rect_float) {
BLI_thread_lock(LOCK_IMAGE);
if (!ibuf->rect_float)
IMB_float_from_rect(ibuf);
BLI_thread_unlock(LOCK_IMAGE);
}
-
+
while (px < 0) px += ibuf->x;
while (py < 0) py += ibuf->y;
while (px >= ibuf->x) px -= ibuf->x;
while (py >= ibuf->y) py -= ibuf->y;
-
+
result = ibuf->rect_float + py * ibuf->x * 4 + px * 4;
copy_v4_v4(out, result);
@@ -101,7 +101,7 @@ static void init(bNodeTree *UNUSED(ntree), bNode *node)
void register_node_type_tex_image(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_IMAGE, "Image", NODE_CLASS_INPUT, NODE_PREVIEW);
node_type_socket_templates(&ntype, NULL, outputs);
node_type_init(&ntype, init);
diff --git a/source/blender/nodes/texture/nodes/node_texture_invert.c b/source/blender/nodes/texture/nodes/node_texture_invert.c
index 35f5072d8a2..8ef7a7a6ad2 100644
--- a/source/blender/nodes/texture/nodes/node_texture_invert.c
+++ b/source/blender/nodes/texture/nodes/node_texture_invert.c
@@ -33,7 +33,7 @@
#include "node_texture_util.h"
#include "NOD_texture.h"
-/* **************** INVERT ******************** */
+/* **************** INVERT ******************** */
static bNodeSocketTemplate inputs[] = {
{ SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
@@ -47,13 +47,13 @@ static bNodeSocketTemplate outputs[] = {
static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **in, short thread)
{
float col[4];
-
+
tex_input_rgba(col, in[0], p, thread);
col[0] = 1.0f - col[0];
col[1] = 1.0f - col[1];
col[2] = 1.0f - col[2];
-
+
copy_v3_v3(out, col);
out[3] = col[3];
}
@@ -66,10 +66,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_invert(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_mixRgb.c b/source/blender/nodes/texture/nodes/node_texture_mixRgb.c
index 835a49c24d4..9fb8332c61a 100644
--- a/source/blender/nodes/texture/nodes/node_texture_mixRgb.c
+++ b/source/blender/nodes/texture/nodes/node_texture_mixRgb.c
@@ -49,16 +49,16 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
{
float fac = tex_input_value(in[0], p, thread);
float col1[4], col2[4];
-
+
tex_input_rgba(col1, in[1], p, thread);
tex_input_rgba(col2, in[2], p, thread);
/* use alpha */
if (node->custom2 & 1)
fac *= col2[3];
-
+
CLAMP(fac, 0.0f, 1.0f);
-
+
copy_v4_v4(out, col1);
ramp_blend(node->custom1, out, fac, col2);
}
@@ -71,11 +71,11 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_mix_rgb(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_label(&ntype, node_blend_label);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_output.c b/source/blender/nodes/texture/nodes/node_texture_output.c
index 664b7e4f507..412e3ffb56c 100644
--- a/source/blender/nodes/texture/nodes/node_texture_output.c
+++ b/source/blender/nodes/texture/nodes/node_texture_output.c
@@ -45,7 +45,7 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
{
TexCallData *cdata = (TexCallData *)data;
TexResult *target = cdata->target;
-
+
if (cdata->do_preview) {
TexParams params;
params_from_cdata(&params, cdata);
@@ -61,12 +61,12 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
if (cdata->which_output == node->custom1 || (cdata->which_output == 0 && node->custom1 == 1)) {
TexParams params;
params_from_cdata(&params, cdata);
-
+
tex_input_rgba(&target->tr, in[0], &params, cdata->thread);
-
+
target->tin = (target->tr + target->tg + target->tb) / 3.0f;
target->talpha = true;
-
+
if (target->nor) {
if (in[1] && in[1]->hasinput)
tex_input_vec(target->nor, in[1], &params, cdata->thread);
@@ -85,7 +85,7 @@ static void unique_name(bNode *node)
int suffix;
bNode *i;
const char *name = tno->name;
-
+
new_name[0] = '\0';
i = node;
while (i->prev) i = i->prev;
@@ -114,7 +114,7 @@ static void unique_name(bNode *node)
}
sprintf(new_name + new_len - 4, ".%03d", ++suffix);
}
-
+
if (new_name[0] != '\0') {
BLI_strncpy(tno->name, new_name, sizeof(tno->name));
}
@@ -124,11 +124,11 @@ static void assign_index(struct bNode *node)
{
bNode *tnode;
int index = 1;
-
+
tnode = node;
while (tnode->prev)
tnode = tnode->prev;
-
+
check_index:
for (; tnode; tnode = tnode->next)
if (tnode->type == TEX_NODE_OUTPUT && tnode != node)
@@ -136,7 +136,7 @@ check_index:
index++;
goto check_index;
}
-
+
node->custom1 = index;
}
@@ -144,7 +144,7 @@ static void init(bNodeTree *UNUSED(ntree), bNode *node)
{
TexNodeOutput *tno = MEM_callocN(sizeof(TexNodeOutput), "TEX_output");
node->storage = tno;
-
+
strcpy(tno->name, "Default");
unique_name(node);
assign_index(node);
@@ -160,16 +160,16 @@ static void copy(bNodeTree *dest_ntree, bNode *dest_node, bNode *src_node)
void register_node_type_tex_output(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_OUTPUT, "Output", NODE_CLASS_OUTPUT, NODE_PREVIEW);
node_type_socket_templates(&ntype, inputs, NULL);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_init(&ntype, init);
node_type_storage(&ntype, "TexNodeOutput", node_free_standard_storage, copy);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
/* Do not allow muting output. */
node_type_internal_links(&ntype, NULL);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_proc.c b/source/blender/nodes/texture/nodes/node_texture_proc.c
index 2461a53ca2a..47f4683549f 100644
--- a/source/blender/nodes/texture/nodes/node_texture_proc.c
+++ b/source/blender/nodes/texture/nodes/node_texture_proc.c
@@ -35,7 +35,7 @@
#include "RE_shader_ext.h"
-/*
+/*
* In this file: wrappers to use procedural textures as nodes
*/
@@ -61,19 +61,19 @@ static void do_proc(float *result, TexParams *p, const float col1[4], const floa
{
TexResult texres;
int textype;
-
+
if (is_normal) {
texres.nor = result;
}
else
texres.nor = NULL;
-
+
textype = multitex_nodes(tex, p->co, p->dxt, p->dyt, p->osatex,
&texres, thread, 0, p->mtex, NULL);
-
+
if (is_normal)
return;
-
+
if (textype & TEX_RGB) {
copy_v4_v4(result, &texres.tr);
}
@@ -86,11 +86,11 @@ static void do_proc(float *result, TexParams *p, const float col1[4], const floa
typedef void (*MapFn) (Tex *tex, bNodeStack **in, TexParams *p, const short thread);
static void texfn(
- float *result,
+ float *result,
TexParams *p,
- bNode *node,
+ bNode *node,
bNodeStack **in,
- char is_normal,
+ char is_normal,
MapFn map_inputs,
short thread)
{
@@ -98,9 +98,9 @@ static void texfn(
float col1[4], col2[4];
tex_input_rgba(col1, in[0], p, thread);
tex_input_rgba(col2, in[1], p, thread);
-
+
map_inputs(&tex, in, p, thread);
-
+
do_proc(result, p, col1, col2, is_normal, &tex, thread);
}
@@ -144,10 +144,10 @@ static bNodeSocketTemplate voronoi_inputs[] = {
{ SOCK_FLOAT, 1, N_("W2"), 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f, PROP_NONE },
{ SOCK_FLOAT, 1, N_("W3"), 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f, PROP_NONE },
{ SOCK_FLOAT, 1, N_("W4"), 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f, PROP_NONE },
-
+
{ SOCK_FLOAT, 1, N_("iScale"), 1.0f, 0.0f, 0.0f, 0.0f, 0.01f, 10.0f, PROP_UNSIGNED },
{ SOCK_FLOAT, 1, N_("Size"), 0.25f, 0.0f, 0.0f, 0.0f, 0.0001f, 4.0f, PROP_UNSIGNED },
-
+
{ -1, 0, "" }
};
static void voronoi_map_inputs(Tex *tex, bNodeStack **in, TexParams *p, short thread)
@@ -156,7 +156,7 @@ static void voronoi_map_inputs(Tex *tex, bNodeStack **in, TexParams *p, short th
tex->vn_w2 = tex_input_value(in[I + 1], p, thread);
tex->vn_w3 = tex_input_value(in[I + 2], p, thread);
tex->vn_w4 = tex_input_value(in[I + 3], p, thread);
-
+
tex->ns_outscale = tex_input_value(in[I + 4], p, thread);
tex->noisesize = tex_input_value(in[I + 5], p, thread);
}
@@ -242,7 +242,7 @@ static bNodeSocketTemplate musgrave_inputs[] = {
{ SOCK_FLOAT, 1, N_("H"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0001f, 2.0f, PROP_UNSIGNED },
{ SOCK_FLOAT, 1, N_("Lacunarity"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 6.0f, PROP_UNSIGNED },
{ SOCK_FLOAT, 1, N_("Octaves"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 8.0f, PROP_UNSIGNED },
-
+
{ SOCK_FLOAT, 1, N_("iScale"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 10.0f, PROP_UNSIGNED },
{ SOCK_FLOAT, 1, N_("Size"), 0.25f, 0.0f, 0.0f, 0.0f, 0.0001f, 2.0f, PROP_UNSIGNED },
{ -1, 0, "" }
@@ -285,13 +285,13 @@ static void init(bNodeTree *UNUSED(ntree), bNode *node)
{
Tex *tex = MEM_callocN(sizeof(Tex), "Tex");
node->storage = tex;
-
+
BKE_texture_default(tex);
tex->type = node->type - TEX_NODE_PROC;
-
+
if (tex->type == TEX_WOOD)
tex->stype = TEX_BANDNOISE;
-
+
}
/* Node type definitions */
@@ -309,10 +309,10 @@ void register_node_type_tex_proc_##name(void) \
\
nodeRegisterType(&ntype); \
}
-
+
#define C outputs_color_only
#define CV outputs_both
-
+
TexDef(TEX_VORONOI, CV, voronoi, "Voronoi" )
TexDef(TEX_BLEND, C, blend, "Blend" )
TexDef(TEX_MAGIC, C, magic, "Magic" )
diff --git a/source/blender/nodes/texture/nodes/node_texture_rotate.c b/source/blender/nodes/texture/nodes/node_texture_rotate.c
index 99648ec323f..bc4e56ea81d 100644
--- a/source/blender/nodes/texture/nodes/node_texture_rotate.c
+++ b/source/blender/nodes/texture/nodes/node_texture_rotate.c
@@ -52,18 +52,18 @@ static void rotate(float new_co[3], float a, float ax[3], const float co[3])
float para[3];
float perp[3];
float cp[3];
-
+
float cos_a = cosf(a * (float)(2 * M_PI));
float sin_a = sinf(a * (float)(2 * M_PI));
-
+
// x' = xcosa + n(n.x)(1-cosa) + (x*n)sina
-
+
mul_v3_v3fl(perp, co, cos_a);
mul_v3_v3fl(para, ax, dot_v3v3(co, ax) * (1 - cos_a));
-
+
cross_v3_v3v3(cp, ax, co);
mul_v3_fl(cp, sin_a);
-
+
new_co[0] = para[0] + perp[0] + cp[0];
new_co[1] = para[1] + perp[1] + cp[1];
new_co[2] = para[2] + perp[2] + cp[2];
@@ -72,7 +72,7 @@ static void rotate(float new_co[3], float a, float ax[3], const float co[3])
static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **in, short thread)
{
float new_co[3], new_dxt[3], new_dyt[3], a, ax[3];
-
+
a = tex_input_value(in[1], p, thread);
tex_input_vec(ax, in[2], p, thread);
@@ -81,7 +81,7 @@ static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **
rotate(new_dxt, a, ax, p->dxt);
rotate(new_dyt, a, ax, p->dyt);
}
-
+
{
TexParams np = *p;
np.co = new_co;
@@ -90,7 +90,7 @@ static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **
tex_input_rgba(out, in[0], &np, thread);
}
}
-static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
+static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
{
tex_output(node, execdata, in, out[0], &colorfn, data);
}
@@ -98,10 +98,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_rotate(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_ROTATE, "Rotate", NODE_CLASS_DISTORT, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_scale.c b/source/blender/nodes/texture/nodes/node_texture_scale.c
index d623e8e159a..94ccfd01357 100644
--- a/source/blender/nodes/texture/nodes/node_texture_scale.c
+++ b/source/blender/nodes/texture/nodes/node_texture_scale.c
@@ -52,7 +52,7 @@ static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **
np.co = new_co;
np.dxt = new_dxt;
np.dyt = new_dyt;
-
+
tex_input_vec(scale, in[1], p, thread);
mul_v3_v3v3(new_co, p->co, scale);
@@ -60,10 +60,10 @@ static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **
mul_v3_v3v3(new_dxt, p->dxt, scale);
mul_v3_v3v3(new_dyt, p->dyt, scale);
}
-
+
tex_input_rgba(out, in[0], &np, thread);
}
-static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
+static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
{
tex_output(node, execdata, in, out[0], &colorfn, data);
}
@@ -71,10 +71,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_scale(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_SCALE, "Scale", NODE_CLASS_DISTORT, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_texture.c b/source/blender/nodes/texture/nodes/node_texture_texture.c
index f77234ae1ae..ff405cc9f3e 100644
--- a/source/blender/nodes/texture/nodes/node_texture_texture.c
+++ b/source/blender/nodes/texture/nodes/node_texture_texture.c
@@ -52,7 +52,7 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
static float red[] = {1, 0, 0, 1};
static float white[] = {1, 1, 1, 1};
float co[3], dxt[3], dyt[3];
-
+
copy_v3_v3(co, p->co);
if (p->osatex) {
copy_v3_v3(dxt, p->dxt);
@@ -62,7 +62,7 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
zero_v3(dxt);
zero_v3(dyt);
}
-
+
if (node->custom2 || node->need_exec == 0) {
/* this node refers to its own texture tree! */
copy_v4_v4(out, (fabsf(co[0] - co[1]) < 0.01f) ? white : red);
@@ -72,14 +72,14 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
int textype;
float nor[] = {0, 0, 0};
float col1[4], col2[4];
-
+
tex_input_rgba(col1, in[0], p, thread);
tex_input_rgba(col2, in[1], p, thread);
-
+
texres.nor = nor;
textype = multitex_nodes(nodetex, co, dxt, dyt, p->osatex,
&texres, thread, 0, p->mtex, NULL);
-
+
if (textype & TEX_RGB) {
copy_v4_v4(out, &texres.tr);
}
@@ -98,10 +98,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_texture(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_translate.c b/source/blender/nodes/texture/nodes/node_texture_translate.c
index ec90029c0bf..fa8319d077b 100644
--- a/source/blender/nodes/texture/nodes/node_texture_translate.c
+++ b/source/blender/nodes/texture/nodes/node_texture_translate.c
@@ -50,16 +50,16 @@ static void colorfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **
float offset[3], new_co[3];
TexParams np = *p;
np.co = new_co;
-
+
tex_input_vec(offset, in[1], p, thread);
-
+
new_co[0] = p->co[0] + offset[0];
new_co[1] = p->co[1] + offset[1];
new_co[2] = p->co[2] + offset[2];
-
+
tex_input_rgba(out, in[0], &np, thread);
}
-static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
+static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
{
tex_output(node, execdata, in, out[0], &colorfn, data);
}
@@ -67,10 +67,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_translate(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_TRANSLATE, "Translate", NODE_CLASS_DISTORT, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_valToNor.c b/source/blender/nodes/texture/nodes/node_texture_valToNor.c
index 995fb6c1782..b7d57b4d4cf 100644
--- a/source/blender/nodes/texture/nodes/node_texture_valToNor.c
+++ b/source/blender/nodes/texture/nodes/node_texture_valToNor.c
@@ -52,7 +52,7 @@ static void normalfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack *
float nabla = tex_input_value(in[1], p, thread);
float val;
float nor[3];
-
+
TexParams np = *p;
np.co = new_co;
@@ -66,7 +66,7 @@ static void normalfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack *
new_co[0] = co[0];
new_co[1] = co[1] + nabla;
nor[1] = tex_input_value(in[0], &np, thread);
-
+
new_co[1] = co[1];
new_co[2] = co[2] + nabla;
nor[2] = tex_input_value(in[0], &np, thread);
@@ -75,7 +75,7 @@ static void normalfn(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack *
out[1] = val - nor[1];
out[2] = val - nor[2];
}
-static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
+static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
{
tex_output(node, execdata, in, out[0], &normalfn, data);
}
@@ -83,10 +83,10 @@ static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *exe
void register_node_type_tex_valtonor(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_VALTONOR, "Value to Normal", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c
index 4041d666811..ad3c4344f34 100644
--- a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c
+++ b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c
@@ -53,7 +53,7 @@ static void valtorgb_colorfn(float *out, TexParams *p, bNode *node, bNodeStack *
}
}
-static void valtorgb_exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
+static void valtorgb_exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
{
tex_output(node, execdata, in, out[0], &valtorgb_colorfn, data);
}
@@ -66,14 +66,14 @@ static void valtorgb_init(bNodeTree *UNUSED(ntree), bNode *node)
void register_node_type_tex_valtorgb(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, valtorgb_in, valtorgb_out);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_init(&ntype, valtorgb_init);
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
node_type_exec(&ntype, NULL, NULL, valtorgb_exec);
-
+
nodeRegisterType(&ntype);
}
@@ -103,10 +103,10 @@ static void rgbtobw_exec(void *data, int UNUSED(thread), bNode *node, bNodeExecD
void register_node_type_tex_rgbtobw(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, rgbtobw_in, rgbtobw_out);
node_type_exec(&ntype, NULL, NULL, rgbtobw_exec);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_viewer.c b/source/blender/nodes/texture/nodes/node_texture_viewer.c
index fefa06b0078..69f4d3ca086 100644
--- a/source/blender/nodes/texture/nodes/node_texture_viewer.c
+++ b/source/blender/nodes/texture/nodes/node_texture_viewer.c
@@ -59,13 +59,13 @@ static void exec(void *data, int UNUSED(thread), bNode *UNUSED(node), bNodeExecD
void register_node_type_tex_viewer(void)
{
static bNodeType ntype;
-
+
tex_node_type_base(&ntype, TEX_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
-
+
/* Do not allow muting viewer node. */
node_type_internal_links(&ntype, NULL);
-
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp
index e5724c97bbc..c8932045c52 100644
--- a/source/blender/physics/intern/BPH_mass_spring.cpp
+++ b/source/blender/physics/intern/BPH_mass_spring.cpp
@@ -60,7 +60,7 @@ static int cloth_count_nondiag_blocks(Cloth *cloth)
{
LinkNode *link;
int nondiag = 0;
-
+
for (link = cloth->springs; link; link = link->next) {
ClothSpring *spring = (ClothSpring *)link->link;
switch (spring->type) {
@@ -68,14 +68,14 @@ static int cloth_count_nondiag_blocks(Cloth *cloth)
/* angular bending combines 3 vertices */
nondiag += 3;
break;
-
+
default:
/* all other springs depend on 2 vertices only */
nondiag += 1;
break;
}
}
-
+
return nondiag;
}
@@ -86,25 +86,25 @@ int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
const float ZERO[3] = {0.0f, 0.0f, 0.0f};
Implicit_Data *id;
unsigned int i, nondiag;
-
+
nondiag = cloth_count_nondiag_blocks(cloth);
cloth->implicit = id = BPH_mass_spring_solver_create(cloth->mvert_num, nondiag);
-
+
for (i = 0; i < cloth->mvert_num; i++) {
BPH_mass_spring_set_vertex_mass(id, i, verts[i].mass);
}
-
+
for (i = 0; i < cloth->mvert_num; i++) {
BPH_mass_spring_set_motion_state(id, i, verts[i].x, ZERO);
}
-
+
return 1;
}
void BPH_cloth_solver_free(ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
-
+
if (cloth->implicit) {
BPH_mass_spring_solver_free(cloth->implicit);
cloth->implicit = NULL;
@@ -118,7 +118,7 @@ void BKE_cloth_solver_set_positions(ClothModifierData *clmd)
unsigned int mvert_num = cloth->mvert_num, i;
ClothHairData *cloth_hairdata = clmd->hairdata;
Implicit_Data *id = cloth->implicit;
-
+
for (i = 0; i < mvert_num; i++) {
if (cloth_hairdata) {
ClothHairData *root = &cloth_hairdata[i];
@@ -126,7 +126,7 @@ void BKE_cloth_solver_set_positions(ClothModifierData *clmd)
}
else
BPH_mass_spring_set_rest_transform(id, i, I3);
-
+
BPH_mass_spring_set_motion_state(id, i, verts[i].x, verts[i].v);
}
}
@@ -136,22 +136,22 @@ static bool collision_response(ClothModifierData *clmd, CollisionModifierData *c
Cloth *cloth = clmd->clothObject;
int index = collpair->ap1;
bool result = false;
-
+
float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3];
float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
float margin_distance = (float)collpair->distance - epsilon2;
float mag_v_rel;
-
+
zero_v3(r_impulse);
-
+
if (margin_distance > 0.0f)
return false; /* XXX tested before already? */
-
+
/* only handle static collisions here */
if ( collpair->flag & COLLISION_IN_FUTURE )
return false;
-
+
/* velocity */
copy_v3_v3(v1, cloth->verts[index].v);
collision_get_collider_velocity(v2_old, v2_new, collmd, collpair);
@@ -160,32 +160,32 @@ static bool collision_response(ClothModifierData *clmd, CollisionModifierData *c
sub_v3_v3v3(v_rel_new, v1, v2_new);
/* normal component of the relative velocity */
mag_v_rel = dot_v3v3(v_rel_old, collpair->normal);
-
+
/* only valid when moving toward the collider */
if (mag_v_rel < -ALMOST_ZERO) {
float v_nor_old, v_nor_new;
float v_tan_old[3], v_tan_new[3];
float bounce, repulse;
-
+
/* Collision response based on
* "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005)
* http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf
*/
-
+
v_nor_old = mag_v_rel;
v_nor_new = dot_v3v3(v_rel_new, collpair->normal);
-
+
madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old);
madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new);
-
+
bounce = -v_nor_old * restitution;
-
+
repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */
/* XXX this clamping factor is quite arbitrary ...
* not sure if there is a more scientific approach, but seems to give good results
*/
CLAMP(repulse, 0.0f, 4.0f * bounce);
-
+
if (margin_distance < -epsilon2) {
mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new);
}
@@ -193,10 +193,10 @@ static bool collision_response(ClothModifierData *clmd, CollisionModifierData *c
bounce = 0.0f;
mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new);
}
-
+
result = true;
}
-
+
return result;
}
@@ -211,17 +211,17 @@ static void cloth_setup_constraints(ClothModifierData *clmd, ColliderContacts *c
ClothVertex *verts = cloth->verts;
int mvert_num = cloth->mvert_num;
int i, j, v;
-
+
const float ZERO[3] = {0.0f, 0.0f, 0.0f};
-
+
BPH_mass_spring_clear_constraints(data);
-
+
for (v = 0; v < mvert_num; v++) {
if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) {
/* pinned vertex constraints */
BPH_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */
}
-
+
verts[v].impulse_count = 0;
}
@@ -233,21 +233,21 @@ static void cloth_setup_constraints(ClothModifierData *clmd, ColliderContacts *c
float restitution = 0.0f;
int v = collpair->face1;
float impulse[3];
-
+
/* pinned verts handled separately */
if (verts[v].flags & CLOTH_VERT_FLAG_PINNED)
continue;
-
+
/* XXX cheap way of avoiding instability from multiple collisions in the same step
* this should eventually be supported ...
*/
if (verts[v].impulse_count > 0)
continue;
-
+
/* calculate collision response */
if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse))
continue;
-
+
BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse);
++verts[v].impulse_count;
}
@@ -267,11 +267,11 @@ static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothMo
ClothSpring *spring;
ClothVertex *cv;
int i, steps;
-
+
cv = cloth->verts;
for (i = 0; i < cloth->mvert_num; i++, cv++) {
copy_v3_v3(cos[i], cv->tx);
-
+
if (cv->goal == 1.0f || len_squared_v3v3(initial_cos[i], cv->tx) != 0.0f) {
masses[i] = 1e+10;
}
@@ -279,57 +279,57 @@ static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothMo
masses[i] = cv->mass;
}
}
-
+
steps = 55;
for (i=0; i<steps; i++) {
for (node=cloth->springs; node; node=node->next) {
/* ClothVertex *cv1, *cv2; */ /* UNUSED */
int v1, v2;
float len, c, l, vec[3];
-
+
spring = (ClothSpring *)node->link;
- if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR)
+ if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR)
continue;
-
+
v1 = spring->ij; v2 = spring->kl;
/* cv1 = cloth->verts + v1; */ /* UNUSED */
/* cv2 = cloth->verts + v2; */ /* UNUSED */
len = len_v3v3(cos[v1], cos[v2]);
-
+
sub_v3_v3v3(vec, cos[v1], cos[v2]);
normalize_v3(vec);
-
+
c = (len - spring->restlen);
if (c == 0.0f)
continue;
-
+
l = c / ((1.0f / masses[v1]) + (1.0f / masses[v2]));
-
+
mul_v3_fl(vec, -(1.0f / masses[v1]) * l);
add_v3_v3(cos[v1], vec);
-
+
sub_v3_v3v3(vec, cos[v2], cos[v1]);
normalize_v3(vec);
-
+
mul_v3_fl(vec, -(1.0f / masses[v2]) * l);
add_v3_v3(cos[v2], vec);
}
}
-
+
cv = cloth->verts;
for (i = 0; i < cloth->mvert_num; i++, cv++) {
float vec[3];
-
+
/*compute forces*/
sub_v3_v3v3(vec, cos[i], cv->tx);
mul_v3_fl(vec, cv->mass*dt*20.0f);
add_v3_v3(cv->tv, vec);
//copy_v3_v3(cv->tx, cos[i]);
}
-
+
MEM_freeN(cos);
MEM_freeN(masses);
-
+
return 1;
}
@@ -338,21 +338,21 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
Cloth *cloth = clmd->clothObject;
ClothSimSettings *parms = clmd->sim_parms;
Implicit_Data *data = cloth->implicit;
-
+
bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
-
+
s->flags &= ~CLOTH_SPRING_FLAG_NEEDED;
-
+
// calculate force of structural + shear springs
if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) {
#ifdef CLOTH_FORCE_SPRING_STRUCTURAL
float k, scaling;
-
+
s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
+
scaling = parms->structural + s->stiffness * fabsf(parms->max_struct - parms->structural);
k = scaling / (parms->avg_spring_len + FLT_EPSILON);
-
+
if (s->type & CLOTH_SPRING_TYPE_SEWING) {
// TODO: verify, half verified (couldn't see error)
// sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects
@@ -366,50 +366,50 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
else if (s->type & CLOTH_SPRING_TYPE_BENDING) { /* calculate force of bending springs */
#ifdef CLOTH_FORCE_SPRING_BEND
float kb, cb, scaling;
-
+
s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
+
scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending);
kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
+
// Fix for [#45084] for cloth stiffness must have cb proportional to kb
cb = kb * parms->bending_damping;
-
+
BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb);
#endif
}
else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) {
#ifdef CLOTH_FORCE_SPRING_BEND
float kb, cb, scaling;
-
+
s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
+
/* XXX WARNING: angular bending springs for hair apply stiffness factor as an overall factor, unlike cloth springs!
* this is crap, but needed due to cloth/hair mixing ...
* max_bend factor is not even used for hair, so ...
*/
scaling = s->stiffness * parms->bending;
kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
+
// Fix for [#45084] for cloth stiffness must have cb proportional to kb
cb = kb * parms->bending_damping;
-
+
/* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */
BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb);
-
+
#if 0
{
float x_kl[3], x_mn[3], v[3], d[3];
-
+
BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v);
BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v);
-
+
BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl);
BKE_sim_debug_data_add_line(clmd->debug_data, x_kl, x_mn, 0.8, 0.8, 0.8, "target", 7981, s->kl);
-
+
copy_v3_v3(d, s->target);
BKE_sim_debug_data_add_vector(clmd->debug_data, x_kl, d, 0.8, 0.8, 0.2, "target", 7982, s->kl);
-
+
// copy_v3_v3(d, s->target_ij);
// BKE_sim_debug_data_add_vector(clmd->debug_data, x, d, 1, 0.4, 0.4, "target", 7983, s->kl);
}
@@ -424,7 +424,7 @@ static void hair_get_boundbox(ClothModifierData *clmd, float gmin[3], float gmax
Implicit_Data *data = cloth->implicit;
unsigned int mvert_num = cloth->mvert_num;
int i;
-
+
INIT_MINMAX(gmin, gmax);
for (i = 0; i < mvert_num; i++) {
float x[3];
@@ -444,7 +444,7 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB
const MVertTri *tri = cloth->tri;
unsigned int mvert_num = cloth->mvert_num;
ClothVertex *vert;
-
+
#ifdef CLOTH_FORCE_GRAVITY
/* global acceleration (gravitation) */
if (clmd->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
@@ -477,7 +477,7 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB
#ifdef CLOTH_FORCE_DRAG
BPH_mass_spring_force_drag(data, drag);
#endif
-
+
/* handle external forces like wind */
if (effectors) {
/* cache per-vertex forces to avoid redundant calculation */
@@ -485,12 +485,12 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB
for (i = 0; i < cloth->mvert_num; i++) {
float x[3], v[3];
EffectedPoint epoint;
-
+
BPH_mass_spring_get_motion_state(data, i, x, v);
pd_point_from_loc(clmd->scene, x, v, i, &epoint);
pdDoEffectors(effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
}
-
+
for (i = 0; i < cloth->tri_num; i++) {
const MVertTri *vt = &tri[i];
BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec);
@@ -501,7 +501,7 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB
#if 0
ClothHairData *hairdata = clmd->hairdata;
ClothHairData *hair_ij, *hair_kl;
-
+
for (LinkNode *link = cloth->springs; link; link = link->next) {
ClothSpring *spring = (ClothSpring *)link->link;
if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) {
@@ -516,7 +516,7 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB
}
#else
ClothHairData *hairdata = clmd->hairdata;
-
+
vert = cloth->verts;
for (i = 0; i < cloth->mvert_num; i++, vert++) {
if (hairdata) {
@@ -531,7 +531,7 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListB
MEM_freeN(winvec);
}
-
+
// calculate spring forces
for (LinkNode *link = cloth->springs; link; link = link->next) {
ClothSpring *spring = (ClothSpring *)link->link;
@@ -548,7 +548,7 @@ BLI_INLINE void cloth_get_grid_location(Implicit_Data *data, float cell_scale, c
{
BPH_mass_spring_get_position(data, index, x);
BPH_mass_spring_get_new_velocity(data, index, v);
-
+
mul_v3_fl(x, cell_scale);
add_v3_v3(x, cell_offset);
}
@@ -582,41 +582,41 @@ static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid, const float c
// ClothVertex *vert3, *vert4;
float x1[3], v1[3], x2[3], v2[3], x3[3], v3[3], x4[3], v4[3];
float dir1[3], dir2[3], dir3[3];
-
+
spring1 = NULL;
spring2 = NULL;
spring3 = (ClothSpring *)spring_link->link;
-
+
zero_v3(x1); zero_v3(v1);
zero_v3(dir1);
zero_v3(x2); zero_v3(v2);
zero_v3(dir2);
-
+
// vert3 = &verts[spring3->kl];
cloth_get_grid_location(data, cell_scale, cell_offset, spring3->kl, x3, v3);
// vert4 = &verts[spring3->ij];
cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
sub_v3_v3v3(dir3, x4, x3);
normalize_v3(dir3);
-
+
while (spring_link) {
/* move on */
spring1 = spring2;
spring2 = spring3;
-
+
// vert3 = vert4;
-
+
copy_v3_v3(x1, x2); copy_v3_v3(v1, v2);
copy_v3_v3(x2, x3); copy_v3_v3(v2, v3);
copy_v3_v3(x3, x4); copy_v3_v3(v3, v4);
-
+
copy_v3_v3(dir1, dir2);
copy_v3_v3(dir2, dir3);
-
+
/* read next segment */
next_spring_link = spring_link->next;
spring_link = hair_spring_next(spring_link);
-
+
if (spring_link) {
spring3 = (ClothSpring *)spring_link->link;
// vert4 = &verts[spring3->ij];
@@ -630,13 +630,13 @@ static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid, const float c
zero_v3(x4); zero_v3(v4);
zero_v3(dir3);
}
-
+
BPH_hair_volume_add_segment(grid, x1, v1, x2, v2, x3, v3, x4, v4,
spring1 ? dir1 : NULL,
dir2,
spring3 ? dir3 : NULL);
}
-
+
return next_spring_link;
}
@@ -647,17 +647,17 @@ static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
int mvert_num = cloth->mvert_num;
ClothVertex *vert;
int i;
-
+
for (i = 0, vert = cloth->verts; i < mvert_num; i++, vert++) {
float x[3], v[3];
-
+
cloth_get_vertex_motion_state(data, vert, x, v);
BPH_hair_volume_add_vertex(grid, x, v);
}
#else
LinkNode *link;
float cellsize, gmin[3], cell_scale, cell_offset[3];
-
+
/* scale and offset for transforming vertex locations into grid space
* (cell size is 0..1, gmin becomes origin)
*/
@@ -665,7 +665,7 @@ static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
cell_scale = cellsize > 0.0f ? 1.0f / cellsize : 0.0f;
mul_v3_v3fl(cell_offset, gmin, cell_scale);
negate_v3(cell_offset);
-
+
link = cloth->springs;
while (link) {
ClothSpring *spring = (ClothSpring *)link->link;
@@ -685,7 +685,7 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
Implicit_Data *data = cloth->implicit;
int mvert_num = cloth->mvert_num;
ClothVertex *vert;
-
+
const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */
float smoothfac = parms->velocity_smooth;
/* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead,
@@ -695,42 +695,42 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
float density_strength = parms->density_strength;
float gmin[3], gmax[3];
int i;
-
+
/* clear grid info */
zero_v3_int(clmd->hair_grid_res);
zero_v3(clmd->hair_grid_min);
zero_v3(clmd->hair_grid_max);
clmd->hair_grid_cellsize = 0.0f;
-
+
hair_get_boundbox(clmd, gmin, gmax);
-
+
/* gather velocities & density */
if (smoothfac > 0.0f || density_strength > 0.0f) {
HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
-
+
cloth_continuum_fill_grid(grid, cloth);
-
+
/* main hair continuum solver */
BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
-
+
for (i = 0, vert = cloth->verts; i < mvert_num; i++, vert++) {
float x[3], v[3], nv[3];
-
+
/* calculate volumetric velocity influence */
BPH_mass_spring_get_position(data, i, x);
BPH_mass_spring_get_new_velocity(data, i, v);
-
+
BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
-
+
interp_v3_v3v3(nv, v, nv, smoothfac);
-
+
/* apply on hair data */
BPH_mass_spring_set_new_velocity(data, i, nv);
}
-
+
/* store basic grid info in the modifier data */
BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max);
-
+
#if 0 /* DEBUG hair velocity vector field */
{
const int size = 64;
@@ -738,26 +738,26 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
float offset[3], a[3], b[3];
const int axis = 0;
const float shift = 0.0f;
-
+
copy_v3_v3(offset, clmd->hair_grid_min);
zero_v3(a);
zero_v3(b);
-
+
offset[axis] = shift * clmd->hair_grid_cellsize;
a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3];
b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3];
-
+
BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity");
for (j = 0; j < size; ++j) {
for (i = 0; i < size; ++i) {
float x[3], v[3], gvel[3], gvel_smooth[3], gdensity;
-
+
madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1));
madd_v3_v3fl(x, b, (float)j / (float)(size-1));
zero_v3(v);
-
+
BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
-
+
// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111);
if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) {
float dvel[3];
@@ -770,7 +770,7 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
float col0[3] = {0.0, 0.0, 0.0};
float col1[3] = {0.0, 1.0, 0.0};
float col[3];
-
+
interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0));
// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115);
// BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115);
@@ -782,7 +782,7 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
}
}
#endif
-
+
BPH_hair_volume_free_vertex_grid(grid);
}
}
@@ -795,7 +795,7 @@ static void cloth_calc_volume_force(ClothModifierData *clmd)
Implicit_Data *data = cloth->implicit;
int mvert_num = cloth->mvert_num;
ClothVertex *vert;
-
+
/* 2.0f is an experimental value that seems to give good results */
float smoothfac = 2.0f * parms->velocity_smooth;
float collfac = 2.0f * parms->collider_friction;
@@ -803,17 +803,17 @@ static void cloth_calc_volume_force(ClothModifierData *clmd)
float minpress = parms->pressure_threshold;
float gmin[3], gmax[3];
int i;
-
+
hair_get_boundbox(clmd, gmin, gmax);
-
+
/* gather velocities & density */
if (smoothfac > 0.0f || pressfac > 0.0f) {
HairVertexGrid *vertex_grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_res, gmin, gmax);
-
+
vert = cloth->verts;
for (i = 0; i < mvert_num; i++, vert++) {
float x[3], v[3];
-
+
if (vert->solver_index < 0) {
copy_v3_v3(x, vert->x);
copy_v3_v3(v, vert->v);
@@ -824,21 +824,21 @@ static void cloth_calc_volume_force(ClothModifierData *clmd)
BPH_hair_volume_add_vertex(vertex_grid, x, v);
}
BPH_hair_volume_normalize_vertex_grid(vertex_grid);
-
+
vert = cloth->verts;
for (i = 0; i < mvert_num; i++, vert++) {
float x[3], v[3], f[3], dfdx[3][3], dfdv[3][3];
-
+
if (vert->solver_index < 0)
continue;
-
+
/* calculate volumetric forces */
BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
BPH_hair_volume_vertex_grid_forces(vertex_grid, x, v, smoothfac, pressfac, minpress, f, dfdx, dfdv);
/* apply on hair data */
BPH_mass_spring_force_extern(data, vert->solver_index, f, dfdx, dfdv);
}
-
+
BPH_hair_volume_free_vertex_grid(vertex_grid);
}
}
@@ -854,33 +854,33 @@ static void cloth_collision_solve_extra(Object *ob, ClothModifierData *clmd, Lis
ClothVertex *verts = cloth->verts;
int mvert_num = cloth->mvert_num;
const float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;
-
+
bool do_extra_solve;
int i;
-
+
if (!(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED))
return;
if (!clmd->clothObject->bvhtree)
return;
-
+
// update verts to current positions
for (i = 0; i < mvert_num; i++) {
BPH_mass_spring_get_new_position(id, i, verts[i].tx);
-
+
sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold);
copy_v3_v3(verts[i].v, verts[i].tv);
}
-
+
#if 0 /* unused */
for (i=0, cv=cloth->verts; i<cloth->mvert_num; i++, cv++) {
copy_v3_v3(initial_cos[i], cv->tx);
}
#endif
-
+
// call collision function
// TODO: check if "step" or "step+dt" is correct - dg
do_extra_solve = cloth_bvh_objcollision(ob, clmd, step / clmd->sim_parms->timescale, dt / clmd->sim_parms->timescale);
-
+
// copy corrected positions back to simulation
for (i = 0; i < mvert_num; i++) {
float curx[3];
@@ -888,41 +888,41 @@ static void cloth_collision_solve_extra(Object *ob, ClothModifierData *clmd, Lis
// correct velocity again, just to be sure we had to change it due to adaptive collisions
sub_v3_v3v3(verts[i].tv, verts[i].tx, curx);
}
-
+
if (do_extra_solve) {
// cloth_calc_helper_forces(ob, clmd, initial_cos, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale);
-
+
for (i = 0; i < mvert_num; i++) {
-
+
float newv[3];
-
+
if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED))
continue;
-
+
BPH_mass_spring_set_new_position(id, i, verts[i].tx);
mul_v3_v3fl(newv, verts[i].tv, spf);
BPH_mass_spring_set_new_velocity(id, i, newv);
}
}
-
+
// X = Xnew;
BPH_mass_spring_apply_result(id);
-
+
if (do_extra_solve) {
ImplicitSolverResult result;
-
+
/* initialize forces to zero */
BPH_mass_spring_clear_forces(id);
-
+
// calculate forces
cloth_calc_force(clmd, frame, effectors, step);
-
+
// calculate new velocity and position
BPH_mass_spring_solve_velocities(id, dt, &result);
// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
-
+
/* note: positions are advanced only once in the main solver step! */
-
+
BPH_mass_spring_apply_result(id);
}
}
@@ -930,7 +930,7 @@ static void cloth_collision_solve_extra(Object *ob, ClothModifierData *clmd, Lis
static void cloth_clear_result(ClothModifierData *clmd)
{
ClothSolverResult *sres = clmd->solver_result;
-
+
sres->status = 0;
sres->max_error = sres->min_error = sres->avg_error = 0.0f;
sres->max_iterations = sres->min_iterations = 0;
@@ -940,7 +940,7 @@ static void cloth_clear_result(ClothModifierData *clmd)
static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps)
{
ClothSolverResult *sres = clmd->solver_result;
-
+
if (sres->status) { /* already initialized ? */
/* error only makes sense for successful iterations */
if (result->status == BPH_SOLVER_SUCCESS) {
@@ -948,7 +948,7 @@ static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *r
sres->max_error = max_ff(sres->max_error, result->error);
sres->avg_error += result->error / (float)steps;
}
-
+
sres->min_iterations = min_ii(sres->min_iterations, result->iterations);
sres->max_iterations = max_ii(sres->max_iterations, result->iterations);
sres->avg_iterations += (float)result->iterations / (float)steps;
@@ -959,11 +959,11 @@ static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *r
sres->min_error = sres->max_error = result->error;
sres->avg_error += result->error / (float)steps;
}
-
+
sres->min_iterations = sres->max_iterations = result->iterations;
sres->avg_iterations += (float)result->iterations / (float)steps;
}
-
+
sres->status |= result->status;
}
@@ -974,7 +974,7 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
* Bad design, TODO
*/
const bool is_hair = (clmd->hairdata != NULL);
-
+
unsigned int i=0;
float step=0.0f, tf=clmd->sim_parms->timescale;
Cloth *cloth = clmd->clothObject;
@@ -984,13 +984,13 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
Implicit_Data *id = cloth->implicit;
ColliderContacts *contacts = NULL;
int totcolliders = 0;
-
+
BKE_sim_debug_data_clear_category("collision");
-
+
if (!clmd->solver_result)
clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
cloth_clear_result(clmd);
-
+
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
for (i = 0; i < mvert_num; i++) {
// update velocities with constrained velocities from pinned verts
@@ -1004,22 +1004,22 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
}
}
}
-
+
while (step < tf) {
ImplicitSolverResult result;
-
+
/* copy velocities for collision */
for (i = 0; i < mvert_num; i++) {
BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv);
copy_v3_v3(verts[i].v, verts[i].tv);
}
-
+
if (is_hair) {
/* determine contact points */
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders);
}
-
+
/* setup vertex constraints for pinned vertices and contacts */
cloth_setup_constraints(clmd, contacts, totcolliders, dt);
}
@@ -1027,10 +1027,10 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
/* setup vertex constraints for pinned vertices */
cloth_setup_constraints(clmd, NULL, 0, dt);
}
-
+
/* initialize forces to zero */
BPH_mass_spring_clear_forces(id);
-
+
// damping velocity for artistic reasons
// this is a bad way to do it, should be removed imo - lukas_t
if (clmd->sim_parms->vel_damping != 1.0f) {
@@ -1041,26 +1041,26 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
BPH_mass_spring_set_velocity(id, i, v);
}
}
-
+
// calculate forces
cloth_calc_force(clmd, frame, effectors, step);
-
+
// calculate new velocity and position
BPH_mass_spring_solve_velocities(id, dt, &result);
cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
-
+
if (is_hair) {
cloth_continuum_step(clmd, dt);
}
-
+
BPH_mass_spring_solve_positions(id, dt);
-
+
if (!is_hair) {
cloth_collision_solve_extra(ob, clmd, effectors, frame, step, dt);
}
-
+
BPH_mass_spring_apply_result(id);
-
+
/* move pinned verts to correct position */
for (i = 0; i < mvert_num; i++) {
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) {
@@ -1071,23 +1071,23 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *
BPH_mass_spring_set_position(id, i, x);
}
}
-
+
BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
}
-
+
/* free contact points */
if (contacts) {
cloth_free_contacts(contacts, totcolliders);
}
-
+
step += dt;
}
-
+
/* copy results back to cloth data */
for (i = 0; i < mvert_num; i++) {
BPH_mass_spring_get_motion_state(id, i, verts[i].x, verts[i].v);
copy_v3_v3(verts[i].txold, verts[i].x);
}
-
+
return 1;
}
diff --git a/source/blender/physics/intern/ConstrainedConjugateGradient.h b/source/blender/physics/intern/ConstrainedConjugateGradient.h
index f9c6931fe8c..2d5fb41cc73 100644
--- a/source/blender/physics/intern/ConstrainedConjugateGradient.h
+++ b/source/blender/physics/intern/ConstrainedConjugateGradient.h
@@ -4,7 +4,7 @@
#include <Eigen/Core>
-namespace Eigen {
+namespace Eigen {
namespace internal {
@@ -29,16 +29,16 @@ void constrained_conjugate_gradient(const MatrixType& mat, const Rhs& rhs, Dest&
typedef typename Dest::RealScalar RealScalar;
typedef typename Dest::Scalar Scalar;
typedef Matrix<Scalar,Dynamic,1> VectorType;
-
+
RealScalar tol = tol_error;
int maxIters = iters;
-
+
int n = mat.cols();
VectorType residual = filter * (rhs - mat * x); //initial residual
RealScalar rhsNorm2 = (filter * rhs).squaredNorm();
- if(rhsNorm2 == 0)
+ if(rhsNorm2 == 0)
{
/* XXX TODO set constrained result here */
x.setZero();
@@ -54,7 +54,7 @@ void constrained_conjugate_gradient(const MatrixType& mat, const Rhs& rhs, Dest&
tol_error = sqrt(residualNorm2 / rhsNorm2);
return;
}
-
+
VectorType p(n);
p = filter * precond.solve(residual); //initial search direction
@@ -68,11 +68,11 @@ void constrained_conjugate_gradient(const MatrixType& mat, const Rhs& rhs, Dest&
Scalar alpha = absNew / p.dot(tmp); // the amount we travel on dir
x += alpha * p; // update solution
residual -= alpha * tmp; // update residue
-
+
residualNorm2 = residual.squaredNorm();
if(residualNorm2 < threshold)
break;
-
+
z = precond.solve(residual); // approximately solve for "A z = residual"
RealScalar absOld = absNew;
@@ -95,20 +95,20 @@ struct MatrixFilter
m_cmat(NULL)
{
}
-
+
MatrixFilter(const MatrixType &cmat) :
m_cmat(&cmat)
{
}
-
+
void setMatrix(const MatrixType &cmat) { m_cmat = &cmat; }
-
+
template <typename VectorType>
void apply(VectorType v) const
{
v = (*m_cmat) * v;
}
-
+
protected:
const MatrixType *m_cmat;
};
@@ -145,7 +145,7 @@ struct traits<ConstrainedConjugateGradient<_MatrixType,_UpLo,_FilterMatrixType,_
* The maximal number of iterations and tolerance value can be controlled via the setMaxIterations()
* and setTolerance() methods. The defaults are the size of the problem for the maximal number of iterations
* and NumTraits<Scalar>::epsilon() for the tolerance.
- *
+ *
* This class can be used as the direct solver classes. Here is a typical usage example:
* \code
* int n = 10000;
@@ -160,7 +160,7 @@ struct traits<ConstrainedConjugateGradient<_MatrixType,_UpLo,_FilterMatrixType,_
* // update b, and solve again
* x = cg.solve(b);
* \endcode
- *
+ *
* By default the iterations start with x=0 as an initial guess of the solution.
* One can control the start using the solveWithGuess() method. Here is a step by
* step execution example starting with a random guess and printing the evolution
@@ -176,7 +176,7 @@ struct traits<ConstrainedConjugateGradient<_MatrixType,_UpLo,_FilterMatrixType,_
* } while (cg.info()!=Success && i<100);
* \endcode
* Note that such a step by step excution is slightly slower.
- *
+ *
* \sa class SimplicialCholesky, DiagonalPreconditioner, IdentityPreconditioner
*/
template< typename _MatrixType, int _UpLo, typename _FilterMatrixType, typename _Preconditioner>
@@ -206,10 +206,10 @@ public:
ConstrainedConjugateGradient() : Base() {}
/** Initialize the solver with matrix \a A for further \c Ax=b solving.
- *
+ *
* This constructor is a shortcut for the default constructor followed
* by a call to compute().
- *
+ *
* \warning this class stores a reference to the matrix A as well as some
* precomputed values that depend on it. Therefore, if \a A is changed
* this class becomes invalid. Call compute() to update it with the new
@@ -258,7 +258,7 @@ public:
m_isInitialized = true;
m_info = m_error <= Base::m_tolerance ? Success : NoConvergence;
}
-
+
/** \internal */
template<typename Rhs,typename Dest>
void _solve(const Rhs& b, Dest& x) const
diff --git a/source/blender/physics/intern/eigen_utils.h b/source/blender/physics/intern/eigen_utils.h
index 8a5a9dbf5e9..4598d3ad3a7 100644
--- a/source/blender/physics/intern/eigen_utils.h
+++ b/source/blender/physics/intern/eigen_utils.h
@@ -56,24 +56,24 @@ typedef float Scalar;
class Vector3 : public Eigen::Vector3f {
public:
typedef float *ctype;
-
+
Vector3()
{
}
-
+
Vector3(const ctype &v)
{
for (int k = 0; k < 3; ++k)
coeffRef(k) = v[k];
}
-
+
Vector3& operator = (const ctype &v)
{
for (int k = 0; k < 3; ++k)
coeffRef(k) = v[k];
return *this;
}
-
+
operator ctype()
{
return data();
@@ -86,18 +86,18 @@ public:
class Matrix3 : public Eigen::Matrix3f {
public:
typedef float (*ctype)[3];
-
+
Matrix3()
{
}
-
+
Matrix3(const ctype &v)
{
for (int k = 0; k < 3; ++k)
for (int l = 0; l < 3; ++l)
coeffRef(l, k) = v[k][l];
}
-
+
Matrix3& operator = (const ctype &v)
{
for (int k = 0; k < 3; ++k)
@@ -105,7 +105,7 @@ public:
coeffRef(l, k) = v[k][l];
return *this;
}
-
+
operator ctype()
{
return (ctype)data();
@@ -120,23 +120,23 @@ typedef Eigen::VectorXf lVector;
class lVector3f : public Eigen::VectorXf {
public:
typedef Eigen::VectorXf base_t;
-
+
lVector3f()
{
}
-
+
template <typename T>
lVector3f& operator = (T rhs)
{
base_t::operator=(rhs);
return *this;
}
-
+
float* v3(int vertex)
{
return &coeffRef(3 * vertex);
}
-
+
const float* v3(int vertex) const
{
return &coeffRef(3 * vertex);
@@ -157,18 +157,18 @@ struct lMatrix3fCtor {
lMatrix3fCtor()
{
}
-
+
void reset()
{
m_trips.clear();
}
-
+
void reserve(int numverts)
{
/* reserve for diagonal entries */
m_trips.reserve(numverts * 9);
}
-
+
void add(int i, int j, const Matrix3 &m)
{
i *= 3;
@@ -177,7 +177,7 @@ struct lMatrix3fCtor {
for (int l = 0; l < 3; ++l)
m_trips.push_back(Triplet(i + k, j + l, m.coeff(l, k)));
}
-
+
void sub(int i, int j, const Matrix3 &m)
{
i *= 3;
@@ -186,13 +186,13 @@ struct lMatrix3fCtor {
for (int l = 0; l < 3; ++l)
m_trips.push_back(Triplet(i + k, j + l, -m.coeff(l, k)));
}
-
+
inline void construct(lMatrix &m)
{
m.setFromTriplets(m_trips.begin(), m_trips.end());
m_trips.clear();
}
-
+
private:
TripletList m_trips;
};
@@ -206,7 +206,7 @@ BLI_INLINE void print_lvector(const lVector3f &v)
for (int i = 0; i < v.rows(); ++i) {
if (i > 0 && i % 3 == 0)
printf("\n");
-
+
printf("%f,\n", v[i]);
}
}
@@ -216,11 +216,11 @@ BLI_INLINE void print_lmatrix(const lMatrix &m)
for (int j = 0; j < m.rows(); ++j) {
if (j > 0 && j % 3 == 0)
printf("\n");
-
+
for (int i = 0; i < m.cols(); ++i) {
if (i > 0 && i % 3 == 0)
printf(" ");
-
+
implicit_print_matrix_elem(m.coeff(j, i));
}
printf("\n");
diff --git a/source/blender/physics/intern/hair_volume.cpp b/source/blender/physics/intern/hair_volume.cpp
index 6ab69a0050c..b59ac46abbc 100644
--- a/source/blender/physics/intern/hair_volume.cpp
+++ b/source/blender/physics/intern/hair_volume.cpp
@@ -82,7 +82,7 @@ typedef struct HairGridVert {
int samples;
float velocity[3];
float density;
-
+
float velocity_smooth[3];
} HairGridVert;
@@ -107,20 +107,20 @@ BLI_INLINE int hair_grid_offset(const float vec[3], const int res[3], const floa
BLI_INLINE int hair_grid_interp_weights(const int res[3], const float gmin[3], float scale, const float vec[3], float uvw[3])
{
int i, j, k, offset;
-
+
i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0);
j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1);
k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2);
offset = i + (j + k*res[1])*res[0];
-
+
uvw[0] = (vec[0] - gmin[0]) * scale - (float)i;
uvw[1] = (vec[1] - gmin[1]) * scale - (float)j;
uvw[2] = (vec[2] - gmin[2]) * scale - (float)k;
-
+
// BLI_assert(0.0f <= uvw[0] && uvw[0] <= 1.0001f);
// BLI_assert(0.0f <= uvw[1] && uvw[1] <= 1.0001f);
// BLI_assert(0.0f <= uvw[2] && uvw[2] <= 1.0001f);
-
+
return offset;
}
@@ -131,12 +131,12 @@ BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, const int res[3]
float uvw[3], muvw[3];
int res2 = res[1] * res[0];
int offset;
-
+
offset = hair_grid_interp_weights(res, gmin, scale, vec, uvw);
muvw[0] = 1.0f - uvw[0];
muvw[1] = 1.0f - uvw[1];
muvw[2] = 1.0f - uvw[2];
-
+
data[0] = grid[offset ];
data[1] = grid[offset +1];
data[2] = grid[offset +res[0] ];
@@ -145,14 +145,14 @@ BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, const int res[3]
data[5] = grid[offset+res2 +1];
data[6] = grid[offset+res2+res[0] ];
data[7] = grid[offset+res2+res[0]+1];
-
+
if (density) {
*density = muvw[2]*( muvw[1]*( muvw[0]*data[0].density + uvw[0]*data[1].density ) +
uvw[1]*( muvw[0]*data[2].density + uvw[0]*data[3].density ) ) +
uvw[2]*( muvw[1]*( muvw[0]*data[4].density + uvw[0]*data[5].density ) +
uvw[1]*( muvw[0]*data[6].density + uvw[0]*data[7].density ) );
}
-
+
if (velocity) {
int k;
for (k = 0; k < 3; ++k) {
@@ -162,7 +162,7 @@ BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, const int res[3]
uvw[1]*( muvw[0]*data[6].velocity[k] + uvw[0]*data[7].velocity[k] ) );
}
}
-
+
if (vel_smooth) {
int k;
for (k = 0; k < 3; ++k) {
@@ -172,24 +172,24 @@ BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, const int res[3]
uvw[1]*( muvw[0]*data[6].velocity_smooth[k] + uvw[0]*data[7].velocity_smooth[k] ) );
}
}
-
+
if (density_gradient) {
density_gradient[0] = muvw[1] * muvw[2] * ( data[0].density - data[1].density ) +
uvw[1] * muvw[2] * ( data[2].density - data[3].density ) +
muvw[1] * uvw[2] * ( data[4].density - data[5].density ) +
uvw[1] * uvw[2] * ( data[6].density - data[7].density );
-
+
density_gradient[1] = muvw[2] * muvw[0] * ( data[0].density - data[2].density ) +
uvw[2] * muvw[0] * ( data[4].density - data[6].density ) +
muvw[2] * uvw[0] * ( data[1].density - data[3].density ) +
uvw[2] * uvw[0] * ( data[5].density - data[7].density );
-
+
density_gradient[2] = muvw[2] * muvw[0] * ( data[0].density - data[4].density ) +
uvw[2] * muvw[0] * ( data[1].density - data[5].density ) +
muvw[2] * uvw[0] * ( data[2].density - data[6].density ) +
uvw[2] * uvw[0] * ( data[3].density - data[7].density );
}
-
+
if (velocity_gradient) {
/* XXX TODO */
zero_m3(velocity_gradient);
@@ -201,21 +201,21 @@ void BPH_hair_volume_vertex_grid_forces(HairGrid *grid, const float x[3], const
float f[3], float dfdx[3][3], float dfdv[3][3])
{
float gdensity, gvelocity[3], ggrad[3], gvelgrad[3][3], gradlen;
-
+
hair_grid_interpolate(grid->verts, grid->res, grid->gmin, grid->inv_cellsize, x, &gdensity, gvelocity, NULL, ggrad, gvelgrad);
-
+
zero_v3(f);
sub_v3_v3(gvelocity, v);
mul_v3_v3fl(f, gvelocity, smoothfac);
-
+
gradlen = normalize_v3(ggrad) - minpressure;
if (gradlen > 0.0f) {
mul_v3_fl(ggrad, gradlen);
madd_v3_v3fl(f, ggrad, pressurefac);
}
-
+
zero_m3(dfdx);
-
+
sub_m3_m3m3(dfdv, gvelgrad, I);
mul_m3_fl(dfdv, smoothfac);
}
@@ -232,16 +232,16 @@ void BPH_hair_volume_grid_velocity(HairGrid *grid, const float x[3], const float
{
float gdensity, gvelocity[3], gvel_smooth[3], ggrad[3], gvelgrad[3][3];
float v_pic[3], v_flip[3];
-
+
hair_grid_interpolate(grid->verts, grid->res, grid->gmin, grid->inv_cellsize, x, &gdensity, gvelocity, gvel_smooth, ggrad, gvelgrad);
-
+
/* velocity according to PIC method (Particle-in-Cell) */
copy_v3_v3(v_pic, gvel_smooth);
-
+
/* velocity according to FLIP method (Fluid-Implicit-Particle) */
sub_v3_v3v3(v_flip, gvel_smooth, gvelocity);
add_v3_v3(v_flip, v);
-
+
interp_v3_v3v3(r_v, v_pic, v_flip, fluid_factor);
}
@@ -283,16 +283,16 @@ BLI_INLINE int hair_grid_weights(const int res[3], const float gmin[3], float sc
{
int i, j, k, offset;
float uvw[3];
-
+
i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0);
j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1);
k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2);
offset = i + (j + k*res[1])*res[0];
-
+
uvw[0] = (vec[0] - gmin[0]) * scale;
uvw[1] = (vec[1] - gmin[1]) * scale;
uvw[2] = (vec[2] - gmin[2]) * scale;
-
+
weights[0] = dist_tent_v3f3(uvw, (float)i , (float)j , (float)k );
weights[1] = dist_tent_v3f3(uvw, (float)(i+1), (float)j , (float)k );
weights[2] = dist_tent_v3f3(uvw, (float)i , (float)(j+1), (float)k );
@@ -301,9 +301,9 @@ BLI_INLINE int hair_grid_weights(const int res[3], const float gmin[3], float sc
weights[5] = dist_tent_v3f3(uvw, (float)(i+1), (float)j , (float)(k+1));
weights[6] = dist_tent_v3f3(uvw, (float)i , (float)(j+1), (float)(k+1));
weights[7] = dist_tent_v3f3(uvw, (float)(i+1), (float)(j+1), (float)(k+1));
-
+
// BLI_assert(fabsf(weights_sum(weights) - 1.0f) < 0.0001f);
-
+
return offset;
}
@@ -320,18 +320,18 @@ void BPH_hair_volume_add_vertex(HairGrid *grid, const float x[3], const float v[
float weights[8];
int di, dj, dk;
int offset;
-
+
if (!hair_grid_point_valid(x, grid->gmin, grid->gmax))
return;
-
+
offset = hair_grid_weights(res, grid->gmin, grid->inv_cellsize, x, weights);
-
+
for (di = 0; di < 2; ++di) {
for (dj = 0; dj < 2; ++dj) {
for (dk = 0; dk < 2; ++dk) {
int voffset = offset + di + (dj + dk*res[1])*res[0];
int iw = di + dj*2 + dk*4;
-
+
grid->verts[voffset].density += weights[iw];
madd_v3_v3fl(grid->verts[voffset].velocity, v, weights[iw]);
}
@@ -344,15 +344,15 @@ BLI_INLINE void hair_volume_eval_grid_vertex(HairGridVert *vert, const float loc
const float x2[3], const float v2[3], const float x3[3], const float v3[3])
{
float closest[3], lambda, dist, weight;
-
+
lambda = closest_to_line_v3(closest, loc, x2, x3);
dist = len_v3v3(closest, loc);
-
+
weight = (radius - dist) * dist_scale;
-
+
if (weight > 0.0f) {
float vel[3];
-
+
interp_v3_v3v3(vel, v2, v3, lambda);
madd_v3_v3fl(vert->velocity, vel, weight);
vert->density += weight;
@@ -378,37 +378,37 @@ BLI_INLINE void hair_volume_add_segment_2D(HairGrid *grid,
{
const float radius = 1.5f;
const float dist_scale = grid->inv_cellsize;
-
+
int j, k;
-
+
/* boundary checks to be safe */
CLAMP_MIN(jmin, 0);
CLAMP_MAX(jmax, resj-1);
CLAMP_MIN(kmin, 0);
CLAMP_MAX(kmax, resk-1);
-
+
HairGridVert *vert_j = vert + jmin * stride_j;
float loc_j[3] = { loc[0], loc[1], loc[2] };
loc_j[axis_j] += (float)jmin;
for (j = jmin; j <= jmax; ++j, vert_j += stride_j, loc_j[axis_j] += 1.0f) {
-
+
HairGridVert *vert_k = vert_j + kmin * stride_k;
float loc_k[3] = { loc_j[0], loc_j[1], loc_j[2] };
loc_k[axis_k] += (float)kmin;
for (k = kmin; k <= kmax; ++k, vert_k += stride_k, loc_k[axis_k] += 1.0f) {
-
+
hair_volume_eval_grid_vertex(vert_k, loc_k, radius, dist_scale, x2, v2, x3, v3);
-
+
#if 0
{
float wloc[3], x2w[3], x3w[3];
grid_to_world(grid, wloc, loc_k);
grid_to_world(grid, x2w, x2);
grid_to_world(grid, x3w, x3);
-
+
if (vert_k->samples > 0)
BKE_sim_debug_data_add_circle(wloc, 0.01f, 1.0, 1.0, 0.3, "grid", 2525, debug_i, j, k);
-
+
if (grid->debug_value) {
BKE_sim_debug_data_add_dot(wloc, 1, 0, 0, "grid", 93, debug_i, j, k);
BKE_sim_debug_data_add_dot(x2w, 0.1, 0.1, 0.7, "grid", 649, debug_i, j, k);
@@ -435,55 +435,55 @@ void BPH_hair_volume_add_segment(HairGrid *grid,
const float dir1[3], const float dir2[3], const float dir3[3])
{
const int res[3] = { grid->res[0], grid->res[1], grid->res[2] };
-
+
/* find the primary direction from the major axis of the direction vector */
const int axis0 = major_axis_v3(dir2);
const int axis1 = (axis0 + 1) % 3;
const int axis2 = (axis0 + 2) % 3;
-
+
/* vertex buffer offset factors along cardinal axes */
const int strides[3] = { 1, res[0], res[0] * res[1] };
const int stride0 = strides[axis0];
const int stride1 = strides[axis1];
const int stride2 = strides[axis2];
-
+
/* increment of secondary directions per step in the primary direction
* note: we always go in the positive direction along axis0, so the sign can be inverted
*/
const float inc1 = dir2[axis1] / dir2[axis0];
const float inc2 = dir2[axis2] / dir2[axis0];
-
+
/* start/end points, so increment along axis0 is always positive */
const float *start = x2[axis0] < x3[axis0] ? x2 : x3;
const float *end = x2[axis0] < x3[axis0] ? x3 : x2;
const float start0 = start[axis0], start1 = start[axis1], start2 = start[axis2];
const float end0 = end[axis0];
-
+
/* range along primary direction */
const int imin = max_ii(floor_int(start[axis0]) - 1, 0);
const int imax = min_ii(floor_int(end[axis0]) + 2, res[axis0]-1);
-
+
float h = 0.0f;
HairGridVert *vert0;
float loc0[3];
int j0, k0, j0_prev, k0_prev;
int i;
-
+
for (i = imin; i <= imax; ++i) {
float shift1, shift2; /* fraction of a full cell shift [0.0, 1.0) */
int jmin, jmax, kmin, kmax;
-
+
h = CLAMPIS((float)i, start0, end0);
-
+
shift1 = start1 + (h - start0) * inc1;
shift2 = start2 + (h - start0) * inc2;
-
+
j0_prev = j0;
j0 = floor_int(shift1);
-
+
k0_prev = k0;
k0 = floor_int(shift2);
-
+
if (i > imin) {
jmin = min_ii(j0, j0_prev);
jmax = max_ii(j0, j0_prev);
@@ -494,12 +494,12 @@ void BPH_hair_volume_add_segment(HairGrid *grid,
jmin = jmax = j0;
kmin = kmax = k0;
}
-
+
vert0 = grid->verts + i * stride0;
loc0[axis0] = (float)i;
loc0[axis1] = 0.0f;
loc0[axis2] = 0.0f;
-
+
hair_volume_add_segment_2D(grid, x1, v1, x2, v2, x3, v3, x4, v4, dir1, dir2, dir3,
res[axis1], res[axis2], jmin-1, jmax+2, kmin-1, kmax+2,
vert0, stride1, stride2, loc0, axis1, axis2,
@@ -511,11 +511,11 @@ BLI_INLINE void hair_volume_eval_grid_vertex_sample(HairGridVert *vert, const fl
const float x[3], const float v[3])
{
float dist, weight;
-
+
dist = len_v3v3(x, loc);
-
+
weight = (radius - dist) * dist_scale;
-
+
if (weight > 0.0f) {
madd_v3_v3fl(vert->velocity, v, weight);
vert->density += weight;
@@ -533,34 +533,34 @@ void BPH_hair_volume_add_segment(HairGrid *grid,
{
const float radius = 1.5f;
const float dist_scale = grid->inv_cellsize;
-
+
const int res[3] = { grid->res[0], grid->res[1], grid->res[2] };
const int stride[3] = { 1, res[0], res[0] * res[1] };
const int num_samples = 10;
-
+
int s;
-
+
for (s = 0; s < num_samples; ++s) {
float x[3], v[3];
int i, j, k;
-
+
float f = (float)s / (float)(num_samples-1);
interp_v3_v3v3(x, x2, x3, f);
interp_v3_v3v3(v, v2, v3, f);
-
+
int imin = max_ii(floor_int(x[0]) - 2, 0);
int imax = min_ii(floor_int(x[0]) + 2, res[0]-1);
int jmin = max_ii(floor_int(x[1]) - 2, 0);
int jmax = min_ii(floor_int(x[1]) + 2, res[1]-1);
int kmin = max_ii(floor_int(x[2]) - 2, 0);
int kmax = min_ii(floor_int(x[2]) + 2, res[2]-1);
-
+
for (k = kmin; k <= kmax; ++k) {
for (j = jmin; j <= jmax; ++j) {
for (i = imin; i <= imax; ++i) {
float loc[3] = { (float)i, (float)j, (float)k };
HairGridVert *vert = grid->verts + i * stride[0] + j * stride[1] + k * stride[2];
-
+
hair_volume_eval_grid_vertex_sample(vert, loc, radius, dist_scale, x, v);
}
}
@@ -598,25 +598,25 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
{
const float flowfac = grid->cellsize;
const float inv_flowfac = 1.0f / grid->cellsize;
-
+
/*const int num_cells = hair_grid_size(grid->res);*/
const int res[3] = { grid->res[0], grid->res[1], grid->res[2] };
const int resA[3] = { grid->res[0] + 2, grid->res[1] + 2, grid->res[2] + 2 };
-
+
const int stride0 = 1;
const int stride1 = grid->res[0];
const int stride2 = grid->res[1] * grid->res[0];
const int strideA0 = 1;
const int strideA1 = grid->res[0] + 2;
const int strideA2 = (grid->res[1] + 2) * (grid->res[0] + 2);
-
+
const int num_cells = res[0] * res[1] * res[2];
const int num_cellsA = (res[0] + 2) * (res[1] + 2) * (res[2] + 2);
-
+
HairGridVert *vert_start = grid->verts - (stride0 + stride1 + stride2);
HairGridVert *vert;
int i, j, k;
-
+
#define MARGIN_i0 (i < 1)
#define MARGIN_j0 (j < 1)
#define MARGIN_k0 (k < 1)
@@ -630,9 +630,9 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
#define NEIGHBOR_MARGIN_i1 (i >= resA[0]-2)
#define NEIGHBOR_MARGIN_j1 (j >= resA[1]-2)
#define NEIGHBOR_MARGIN_k1 (k >= resA[2]-2)
-
+
BLI_assert(num_cells >= 1);
-
+
/* Calculate divergence */
lVector B(num_cellsA);
for (k = 0; k < resA[2]; ++k) {
@@ -640,14 +640,14 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
for (i = 0; i < resA[0]; ++i) {
int u = i * strideA0 + j * strideA1 + k * strideA2;
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1;
-
+
if (is_margin) {
B[u] = 0.0f;
continue;
}
-
+
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
-
+
const float *v0 = vert->velocity;
float dx = 0.0f, dy = 0.0f, dz = 0.0f;
if (!NEIGHBOR_MARGIN_i0)
@@ -662,19 +662,19 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
dz += v0[2] - (vert - stride2)->velocity[2];
if (!NEIGHBOR_MARGIN_k1)
dz += (vert + stride2)->velocity[2] - v0[2];
-
+
float divergence = -0.5f * flowfac * (dx + dy + dz);
-
+
/* adjustment term for target density */
float target = hair_volume_density_divergence(vert->density, target_density, target_strength);
-
+
/* B vector contains the finite difference approximation of the velocity divergence.
* Note: according to the discretized Navier-Stokes equation the rhs vector
* and resulting pressure gradient should be multiplied by the (inverse) density;
* however, this is already included in the weighting of hair velocities on the grid!
*/
B[u] = divergence - target;
-
+
#if 0
{
float wloc[3], loc[3];
@@ -683,12 +683,12 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
float coln[3] = {1.0, 0.0, 1.0};
float col[3];
float fac;
-
+
loc[0] = (float)(i - 1);
loc[1] = (float)(j - 1);
loc[2] = (float)(k - 1);
grid_to_world(grid, wloc, loc);
-
+
if (divergence > 0.0f) {
fac = CLAMPIS(divergence * target_strength, 0.0, 1.0);
interp_v3_v3v3(col, col0, colp, fac);
@@ -704,11 +704,11 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
}
}
}
-
+
/* Main Poisson equation system:
* This is derived from the discretezation of the Poisson equation
* div(grad(p)) = div(v)
- *
+ *
* The finite difference approximation yields the linear equation system described here:
* https://en.wikipedia.org/wiki/Discrete_Poisson_equation
*/
@@ -718,13 +718,13 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
* and up to 6 factors -1 on other places.
*/
A.reserve(Eigen::VectorXi::Constant(num_cellsA, 7));
-
+
for (k = 0; k < resA[2]; ++k) {
for (j = 0; j < resA[1]; ++j) {
for (i = 0; i < resA[0]; ++i) {
int u = i * strideA0 + j * strideA1 + k * strideA2;
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1;
-
+
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
if (!is_margin && vert->density > density_threshold) {
int neighbors_lo = 0;
@@ -733,7 +733,7 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
int neighbor_lo_index[3];
int neighbor_hi_index[3];
int n;
-
+
/* check for upper bounds in advance
* to get the correct number of neighbors,
* needed for the diagonal element
@@ -750,10 +750,10 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
neighbor_hi_index[neighbors_hi++] = u + strideA1;
if (!NEIGHBOR_MARGIN_k1 && (vert + stride2)->density > density_threshold)
neighbor_hi_index[neighbors_hi++] = u + strideA2;
-
+
/*int liquid_neighbors = neighbors_lo + neighbors_hi;*/
non_solid_neighbors = 6;
-
+
for (n = 0; n < neighbors_lo; ++n)
A.insert(neighbor_lo_index[n], u) = -1.0f;
A.insert(u, u) = (float)non_solid_neighbors;
@@ -766,15 +766,15 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
}
}
}
-
+
ConjugateGradient cg;
cg.setMaxIterations(100);
cg.setTolerance(0.01f);
-
+
cg.compute(A);
-
+
lVector p = cg.solve(B);
-
+
if (cg.info() == Eigen::Success) {
/* Calculate velocity = grad(p) */
for (k = 0; k < resA[2]; ++k) {
@@ -784,7 +784,7 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1;
if (is_margin)
continue;
-
+
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
if (vert->density > density_threshold) {
float p_left = p[u - strideA0];
@@ -793,14 +793,14 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
float p_up = p[u + strideA1];
float p_bottom = p[u - strideA2];
float p_top = p[u + strideA2];
-
+
/* finite difference estimate of pressure gradient */
float dvel[3];
dvel[0] = p_right - p_left;
dvel[1] = p_up - p_down;
dvel[2] = p_top - p_bottom;
mul_v3_fl(dvel, -0.5f * inv_flowfac);
-
+
/* pressure gradient describes velocity delta */
add_v3_v3v3(vert->velocity_smooth, vert->velocity, dvel);
}
@@ -810,14 +810,14 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
}
}
}
-
+
#if 0
{
int axis = 0;
float offset = 0.0f;
-
+
int slice = (offset - grid->gmin[axis]) / grid->cellsize;
-
+
for (k = 0; k < resA[2]; ++k) {
for (j = 0; j < resA[1]; ++j) {
for (i = 0; i < resA[0]; ++i) {
@@ -825,21 +825,21 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1;
if (i != slice)
continue;
-
+
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
-
+
float wloc[3], loc[3];
float col0[3] = {0.0, 0.0, 0.0};
float colp[3] = {0.0, 1.0, 1.0};
float coln[3] = {1.0, 0.0, 1.0};
float col[3];
float fac;
-
+
loc[0] = (float)(i - 1);
loc[1] = (float)(j - 1);
loc[2] = (float)(k - 1);
grid_to_world(grid, wloc, loc);
-
+
float pressure = p[u];
if (pressure > 0.0f) {
fac = CLAMPIS(pressure * grid->debug1, 0.0, 1.0);
@@ -851,19 +851,19 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
}
if (fac > 0.05f)
BKE_sim_debug_data_add_circle(grid->debug_data, wloc, 0.01f, col[0], col[1], col[2], "grid", 5533, i, j, k);
-
+
if (!is_margin) {
float dvel[3];
sub_v3_v3v3(dvel, vert->velocity_smooth, vert->velocity);
// BKE_sim_debug_data_add_vector(grid->debug_data, wloc, dvel, 1, 1, 1, "grid", 5566, i, j, k);
}
-
+
if (!is_margin) {
float d = CLAMPIS(vert->density * grid->debug2, 0.0f, 1.0f);
float col0[3] = {0.3, 0.3, 0.3};
float colp[3] = {0.0, 0.0, 1.0};
float col[3];
-
+
interp_v3_v3v3(col, col0, colp, d);
// if (d > 0.05f)
// BKE_sim_debug_data_add_dot(grid->debug_data, wloc, col[0], col[1], col[2], "grid", 5544, i, j, k);
@@ -873,7 +873,7 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
}
}
#endif
-
+
return true;
}
else {
@@ -881,7 +881,7 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target
for (i = 0, vert = grid->verts; i < num_cells; ++i, ++vert) {
zero_v3(vert->velocity_smooth);
}
-
+
return false;
}
}
@@ -901,20 +901,20 @@ BLI_INLINE void hair_volume_filter_box_convolute(HairVertexGrid *grid, float inv
int offset, kernel_offset, kernel_dq, kernel_dr;
HairGridVert *verts;
float *vel_smooth;
-
+
offset = i + (j + k*res)*res;
verts = grid->verts;
vel_smooth = verts[offset].velocity_smooth;
-
+
kernel_offset = minp + (minq + minr*res)*res;
kernel_dq = res;
kernel_dr = res * res;
for (r = minr; r <= maxr; ++r) {
for (q = minq; q <= maxq; ++q) {
for (p = minp; p <= maxp; ++p) {
-
+
madd_v3_v3fl(vel_smooth, verts[kernel_offset].velocity, invD);
-
+
kernel_offset += 1;
}
kernel_offset += kernel_dq;
@@ -930,18 +930,18 @@ void BPH_hair_volume_vertex_grid_filter_box(HairVertexGrid *grid, int kernel_siz
int tot;
float invD;
int i, j, k;
-
+
if (kernel_size <= 0)
return;
-
+
tot = kernel_size * 2 + 1;
invD = 1.0f / (float)(tot*tot*tot);
-
+
/* clear values for convolution */
for (i = 0; i < size; ++i) {
zero_v3(grid->verts[i].velocity_smooth);
}
-
+
for (i = 0; i < grid->res; ++i) {
for (j = 0; j < grid->res; ++j) {
for (k = 0; k < grid->res; ++k) {
@@ -949,7 +949,7 @@ void BPH_hair_volume_vertex_grid_filter_box(HairVertexGrid *grid, int kernel_siz
}
}
}
-
+
/* apply as new velocity */
for (i = 0; i < size; ++i) {
copy_v3_v3(grid->verts[i].velocity, grid->verts[i].velocity_smooth);
@@ -966,21 +966,21 @@ HairGrid *BPH_hair_volume_create_vertex_grid(float cellsize, const float gmin[3]
int size;
HairGrid *grid;
int i;
-
+
/* sanity check */
if (cellsize <= 0.0f)
cellsize = 1.0f;
scale = 1.0f / cellsize;
-
+
sub_v3_v3v3(extent, gmax, gmin);
for (i = 0; i < 3; ++i) {
resmin[i] = floor_int(gmin[i] * scale);
resmax[i] = floor_int(gmax[i] * scale) + 1;
-
+
/* add margin of 1 cell */
resmin[i] -= 1;
resmax[i] += 1;
-
+
res[i] = resmax[i] - resmin[i] + 1;
/* sanity check: avoid null-sized grid */
if (res[i] < 4) {
@@ -992,12 +992,12 @@ HairGrid *BPH_hair_volume_create_vertex_grid(float cellsize, const float gmin[3]
res[i] = MAX_HAIR_GRID_RES;
resmax[i] = resmin[i] + MAX_HAIR_GRID_RES;
}
-
+
gmin_margin[i] = (float)resmin[i] * cellsize;
gmax_margin[i] = (float)resmax[i] * cellsize;
}
size = hair_grid_size(res);
-
+
grid = (HairGrid *)MEM_callocN(sizeof(HairGrid), "hair grid");
grid->res[0] = res[0];
grid->res[1] = res[1];
@@ -1062,23 +1062,23 @@ static HairGridVert *hair_volume_create_collision_grid(ClothModifierData *clmd,
float vel[3];
float weights[8];
int di, dj, dk;
-
+
for (v=0; v < col->collmd->numverts; v++, loc0++, loc1++) {
int offset;
-
+
if (!hair_grid_point_valid(loc1->co, gmin, gmax))
continue;
-
+
offset = hair_grid_weights(res, gmin, scale, lX[v], weights);
-
+
sub_v3_v3v3(vel, loc1->co, loc0->co);
-
+
for (di = 0; di < 2; ++di) {
for (dj = 0; dj < 2; ++dj) {
for (dk = 0; dk < 2; ++dk) {
int voffset = offset + di + (dj + dk*res)*res;
int iw = di + dj*2 + dk*4;
-
+
collgrid[voffset].density += weights[iw];
madd_v3_v3fl(collgrid[voffset].velocity, vel, weights[iw]);
}
@@ -1095,7 +1095,7 @@ static HairGridVert *hair_volume_create_collision_grid(ClothModifierData *clmd,
if (density > 0.0f)
mul_v3_fl(collgrid[i].velocity, 1.0f/density);
}
-
+
return collgrid;
}
#endif
diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h
index d6bf5c6b7bf..2eadd3171b0 100644
--- a/source/blender/physics/intern/implicit.h
+++ b/source/blender/physics/intern/implicit.h
@@ -62,7 +62,7 @@ struct Implicit_Data;
typedef struct ImplicitSolverResult {
int status;
-
+
int iterations;
float error;
} ImplicitSolverResult;
diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c
index 49798081cff..5fd9c6b50de 100644
--- a/source/blender/physics/intern/implicit_blender.c
+++ b/source/blender/physics/intern/implicit_blender.c
@@ -71,9 +71,9 @@ static float ZERO[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
/*
#define C99
#ifdef C99
-#defineDO_INLINE inline
-#else
-#defineDO_INLINE static
+#defineDO_INLINE inline
+#else
+#defineDO_INLINE static
#endif
*/
struct Cloth;
@@ -90,7 +90,7 @@ typedef struct fmatrix3x3 {
/* int pinned; // is this vertex allowed to move? */
float n1, n2, n3; /* three normal vectors for collision constrains */
unsigned int vcount; /* vertex count */
- unsigned int scount; /* spring count */
+ unsigned int scount; /* spring count */
} fmatrix3x3;
///////////////////////////
@@ -115,9 +115,9 @@ DO_INLINE void mul_fvectorT_fvector(float to[3][3], float vectorA[3], float vect
/* simple v^T * v product with scalar ("outer product") */
/* STATUS: HAS TO BE verified (*should* work) */
DO_INLINE void mul_fvectorT_fvectorS(float to[3][3], float vectorA[3], float vectorB[3], float aS)
-{
+{
mul_fvectorT_fvector(to, vectorA, vectorB);
-
+
mul_fvector_S(to[0], to[0], aS);
mul_fvector_S(to[1], to[1], aS);
mul_fvector_S(to[2], to[2], aS);
@@ -288,7 +288,7 @@ static void print_lvector(lfVector *v, int numverts)
for (i = 0; i < numverts; ++i) {
if (i > 0)
printf("\n");
-
+
printf("%f,\n", v[i][0]);
printf("%f,\n", v[i][1]);
printf("%f,\n", v[i][2]);
@@ -303,11 +303,11 @@ static void print_bfmatrix(fmatrix3x3 *m)
int size = m[0].vcount * 3;
float *t = MEM_callocN(sizeof(float) * size*size, "bfmatrix");
int q, i, j;
-
+
for (q = 0; q < tot; ++q) {
int k = 3 * m[q].r;
int l = 3 * m[q].c;
-
+
for (j = 0; j < 3; ++j) {
for (i = 0; i < 3; ++i) {
// if (t[k + i + (l + j) * size] != 0.0f) {
@@ -323,20 +323,20 @@ static void print_bfmatrix(fmatrix3x3 *m)
}
}
}
-
+
for (j = 0; j < size; ++j) {
if (j > 0 && j % 3 == 0)
printf("\n");
-
+
for (i = 0; i < size; ++i) {
if (i > 0 && i % 3 == 0)
printf(" ");
-
+
implicit_print_matrix_elem(t[i + j * size]);
}
printf("\n");
}
-
+
MEM_freeN(t);
}
#endif
@@ -354,7 +354,7 @@ DO_INLINE void cp_fmatrix(float to[3][3], float from[3][3])
DO_INLINE void initdiag_fmatrixS(float to[3][3], float aS)
{
cp_fmatrix(to, ZERO);
-
+
to[0][0] = aS;
to[1][1] = aS;
to[2][2] = aS;
@@ -532,15 +532,15 @@ DO_INLINE fmatrix3x3 *create_bfmatrix(unsigned int verts, unsigned int springs)
// TODO: check if memory allocation was successful */
fmatrix3x3 *temp = (fmatrix3x3 *)MEM_callocN(sizeof(fmatrix3x3) * (verts + springs), "cloth_implicit_alloc_matrix");
int i;
-
+
temp[0].vcount = verts;
temp[0].scount = springs;
-
+
/* vertex part of the matrix is diagonal blocks */
for (i = 0; i < verts; ++i) {
init_fmatrix(temp + i, i, i);
}
-
+
return temp;
}
/* delete big matrix */
@@ -565,7 +565,7 @@ DO_INLINE void init_bfmatrix(fmatrix3x3 *matrix, float m3[3][3])
unsigned int i;
for (i = 0; i < matrix[0].vcount+matrix[0].scount; i++) {
- cp_fmatrix(matrix[i].m, m3);
+ cp_fmatrix(matrix[i].m, m3);
}
}
@@ -577,10 +577,10 @@ DO_INLINE void initdiag_bfmatrix(fmatrix3x3 *matrix, float m3[3][3])
float tmatrix[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (i = 0; i < matrix[0].vcount; i++) {
- cp_fmatrix(matrix[i].m, m3);
+ cp_fmatrix(matrix[i].m, m3);
}
for (j = matrix[0].vcount; j < matrix[0].vcount+matrix[0].scount; j++) {
- cp_fmatrix(matrix[j].m, tmatrix);
+ cp_fmatrix(matrix[j].m, tmatrix);
}
}
@@ -591,7 +591,7 @@ DO_INLINE void mul_bfmatrix_lfvector( float (*to)[3], fmatrix3x3 *from, lfVector
unsigned int i = 0;
unsigned int vcount = from[0].vcount;
lfVector *temp = create_lfvector(vcount);
-
+
zero_lfvector(to, vcount);
#pragma omp parallel sections private(i) if (vcount > CLOTH_OPENMP_LIMIT)
@@ -610,10 +610,10 @@ DO_INLINE void mul_bfmatrix_lfvector( float (*to)[3], fmatrix3x3 *from, lfVector
}
}
add_lfvector_lfvector(to, to, temp, from[0].vcount);
-
+
del_lfvector(temp);
-
-
+
+
}
/* SPARSE SYMMETRIC sub big matrix with big matrix*/
@@ -642,15 +642,15 @@ typedef struct Implicit_Data {
lfVector *F; /* forces */
fmatrix3x3 *dFdV, *dFdX; /* force jacobians */
int num_blocks; /* number of off-diagonal blocks (springs) */
-
+
/* motion state data */
lfVector *X, *Xnew; /* positions */
lfVector *V, *Vnew; /* velocities */
-
+
/* internal solver data */
lfVector *B; /* B for A*dV = B */
fmatrix3x3 *A; /* A for A*dV = B */
-
+
lfVector *dV; /* velocity change (solution of A*dV = B) */
lfVector *z; /* target velocity in constrained directions */
fmatrix3x3 *S; /* filtering matrix for constraints */
@@ -660,7 +660,7 @@ typedef struct Implicit_Data {
Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings)
{
Implicit_Data *id = (Implicit_Data *)MEM_callocN(sizeof(Implicit_Data), "implicit vecmat");
-
+
/* process diagonal elements */
id->tfm = create_bfmatrix(numverts, 0);
id->A = create_bfmatrix(numverts, numsprings);
@@ -696,7 +696,7 @@ void BPH_mass_spring_solver_free(Implicit_Data *id)
del_bfmatrix(id->Pinv);
del_bfmatrix(id->bigI);
del_bfmatrix(id->M);
-
+
del_lfvector(id->X);
del_lfvector(id->Xnew);
del_lfvector(id->V);
@@ -705,7 +705,7 @@ void BPH_mass_spring_solver_free(Implicit_Data *id)
del_lfvector(id->B);
del_lfvector(id->dV);
del_lfvector(id->z);
-
+
MEM_freeN(id);
}
@@ -752,7 +752,7 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z
// Solves for unknown X in equation AX=B
unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100;
float conjgrad_epsilon=0.0001f /* , conjgrad_lasterror=0 */ /* UNUSED */;
- lfVector *q, *d, *tmp, *r;
+ lfVector *q, *d, *tmp, *r;
float s, starget, a, s_prev;
unsigned int numverts = lA[0].vcount;
q = create_lfvector(numverts);
@@ -818,7 +818,7 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z,
// Solves for unknown X in equation AX=B
unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100;
float conjgrad_epsilon=0.01f;
-
+
unsigned int numverts = lA[0].vcount;
lfVector *fB = create_lfvector(numverts);
lfVector *AdV = create_lfvector(numverts);
@@ -827,27 +827,27 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z,
lfVector *q = create_lfvector(numverts);
lfVector *s = create_lfvector(numverts);
float bnorm2, delta_new, delta_old, delta_target, alpha;
-
+
cp_lfvector(ldV, z, numverts);
-
+
/* d0 = filter(B)^T * P * filter(B) */
cp_lfvector(fB, lB, numverts);
filter(fB, S);
bnorm2 = dot_lfvector(fB, fB, numverts);
delta_target = conjgrad_epsilon*conjgrad_epsilon * bnorm2;
-
+
/* r = filter(B - A * dV) */
mul_bfmatrix_lfvector(AdV, lA, ldV);
sub_lfvector_lfvector(r, lB, AdV, numverts);
filter(r, S);
-
+
/* c = filter(P^-1 * r) */
cp_lfvector(c, r, numverts);
filter(c, S);
-
+
/* delta = r^T * c */
delta_new = dot_lfvector(r, c, numverts);
-
+
#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT
printf("==== A ====\n");
print_bfmatrix(lA);
@@ -858,25 +858,25 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z,
printf("==== S ====\n");
print_bfmatrix(S);
#endif
-
+
while (delta_new > delta_target && conjgrad_loopcount < conjgrad_looplimit) {
mul_bfmatrix_lfvector(q, lA, c);
filter(q, S);
-
+
alpha = delta_new / dot_lfvector(c, q, numverts);
-
+
add_lfvector_lfvectorS(ldV, ldV, c, alpha, numverts);
-
+
add_lfvector_lfvectorS(r, r, q, -alpha, numverts);
-
+
/* s = P^-1 * r */
cp_lfvector(s, r, numverts);
delta_old = delta_new;
delta_new = dot_lfvector(r, s, numverts);
-
+
add_lfvector_lfvectorS(c, s, c, delta_new / delta_old, numverts);
filter(c, S);
-
+
conjgrad_loopcount++;
}
@@ -885,7 +885,7 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z,
print_lvector(ldV, numverts);
printf("========\n");
#endif
-
+
del_lfvector(fB);
del_lfvector(AdV);
del_lfvector(r);
@@ -906,14 +906,14 @@ static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z,
DO_INLINE void BuildPPinv(fmatrix3x3 *lA, fmatrix3x3 *P, fmatrix3x3 *Pinv)
{
unsigned int i = 0;
-
+
// Take only the diagonal blocks of A
// #pragma omp parallel for private(i) if (lA[0].vcount > CLOTH_OPENMP_LIMIT)
for (i = 0; i<lA[0].vcount; i++) {
// block diagonalizer
cp_fmatrix(P[i].m, lA[i].m);
inverse_fmatrix(Pinv[i].m, P[i].m);
-
+
}
}
/*
@@ -927,65 +927,65 @@ static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector
lfVector *p = create_lfvector(numverts);
lfVector *s = create_lfvector(numverts);
lfVector *h = create_lfvector(numverts);
-
+
BuildPPinv(lA, P, Pinv);
-
+
filter(dv, S);
add_lfvector_lfvector(dv, dv, z, numverts);
-
+
mul_bfmatrix_lfvector(r, lA, dv);
sub_lfvector_lfvector(r, lB, r, numverts);
filter(r, S);
-
+
mul_prevfmatrix_lfvector(p, Pinv, r);
filter(p, S);
-
+
deltaNew = dot_lfvector(r, p, numverts);
-
+
delta0 = deltaNew * sqrt(conjgrad_epsilon);
-
+
#ifdef DEBUG_TIME
double start = PIL_check_seconds_timer();
#endif
-
+
while ((deltaNew > delta0) && (iterations < conjgrad_looplimit))
{
iterations++;
-
+
mul_bfmatrix_lfvector(s, lA, p);
filter(s, S);
-
+
alpha = deltaNew / dot_lfvector(p, s, numverts);
-
+
add_lfvector_lfvectorS(dv, dv, p, alpha, numverts);
-
+
add_lfvector_lfvectorS(r, r, s, -alpha, numverts);
-
+
mul_prevfmatrix_lfvector(h, Pinv, r);
filter(h, S);
-
+
deltaOld = deltaNew;
-
+
deltaNew = dot_lfvector(r, h, numverts);
-
+
add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts);
-
+
filter(p, S);
-
+
}
#ifdef DEBUG_TIME
double end = PIL_check_seconds_timer();
printf("cg_filtered_pre time: %f\n", (float)(end - start));
#endif
-
+
del_lfvector(h);
del_lfvector(s);
del_lfvector(p);
del_lfvector(r);
-
+
printf("iterations: %d\n", iterations);
-
+
return iterations<conjgrad_looplimit;
}
*/
@@ -1000,83 +1000,83 @@ static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector
lfVector *h = create_lfvector(numverts);
lfVector *bhat = create_lfvector(numverts);
lfVector *btemp = create_lfvector(numverts);
-
+
BuildPPinv(lA, P, Pinv);
-
+
initdiag_bfmatrix(bigI, I);
sub_bfmatrix_Smatrix(bigI, bigI, S);
-
+
// x = Sx_0+(I-S)z
filter(dv, S);
add_lfvector_lfvector(dv, dv, z, numverts);
-
+
// b_hat = S(b-A(I-S)z)
mul_bfmatrix_lfvector(r, lA, z);
mul_bfmatrix_lfvector(bhat, bigI, r);
sub_lfvector_lfvector(bhat, lB, bhat, numverts);
-
+
// r = S(b-Ax)
mul_bfmatrix_lfvector(r, lA, dv);
sub_lfvector_lfvector(r, lB, r, numverts);
filter(r, S);
-
+
// p = SP^-1r
mul_prevfmatrix_lfvector(p, Pinv, r);
filter(p, S);
-
+
// delta0 = bhat^TP^-1bhat
mul_prevfmatrix_lfvector(btemp, Pinv, bhat);
delta0 = dot_lfvector(bhat, btemp, numverts);
-
+
// deltaNew = r^TP
deltaNew = dot_lfvector(r, p, numverts);
-
+
/*
filter(dv, S);
add_lfvector_lfvector(dv, dv, z, numverts);
-
+
mul_bfmatrix_lfvector(r, lA, dv);
sub_lfvector_lfvector(r, lB, r, numverts);
filter(r, S);
-
+
mul_prevfmatrix_lfvector(p, Pinv, r);
filter(p, S);
-
+
deltaNew = dot_lfvector(r, p, numverts);
-
+
delta0 = deltaNew * sqrt(conjgrad_epsilon);
*/
#ifdef DEBUG_TIME
double start = PIL_check_seconds_timer();
#endif
-
+
tol = (0.01*0.2);
-
+
while ((deltaNew > delta0*tol*tol) && (iterations < conjgrad_looplimit))
{
iterations++;
-
+
mul_bfmatrix_lfvector(s, lA, p);
filter(s, S);
-
+
alpha = deltaNew / dot_lfvector(p, s, numverts);
-
+
add_lfvector_lfvectorS(dv, dv, p, alpha, numverts);
-
+
add_lfvector_lfvectorS(r, r, s, -alpha, numverts);
-
+
mul_prevfmatrix_lfvector(h, Pinv, r);
filter(h, S);
-
+
deltaOld = deltaNew;
-
+
deltaNew = dot_lfvector(r, h, numverts);
-
+
add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts);
-
+
filter(p, S);
-
+
}
#ifdef DEBUG_TIME
@@ -1090,9 +1090,9 @@ static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector
del_lfvector(s);
del_lfvector(p);
del_lfvector(r);
-
+
// printf("iterations: %d\n", iterations);
-
+
return iterations<conjgrad_looplimit;
}
#endif
@@ -1128,17 +1128,17 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol
add_lfvector_lfvector(data->Vnew, data->V, data->dV, numverts);
del_lfvector(dFdXmV);
-
+
return result->status == BPH_SOLVER_SUCCESS;
}
bool BPH_mass_spring_solve_positions(Implicit_Data *data, float dt)
{
int numverts = data->M[0].vcount;
-
+
// advance positions
add_lfvector_lfvectorS(data->Xnew, data->X, data->Vnew, dt, numverts);
-
+
return true;
}
@@ -1219,7 +1219,7 @@ static int BPH_mass_spring_add_block(Implicit_Data *data, int v1, int v2)
int s = data->M[0].vcount + data->num_blocks; /* index from array start */
BLI_assert(s < data->M[0].vcount + data->M[0].scount);
++data->num_blocks;
-
+
/* tfm and S don't have spring entries (diagonal blocks only) */
init_fmatrix(data->bigI + s, v1, v2);
init_fmatrix(data->M + s, v1, v2);
@@ -1228,7 +1228,7 @@ static int BPH_mass_spring_add_block(Implicit_Data *data, int v1, int v2)
init_fmatrix(data->A + s, v1, v2);
init_fmatrix(data->P + s, v1, v2);
init_fmatrix(data->Pinv + s, v1, v2);
-
+
return s;
}
@@ -1244,26 +1244,26 @@ void BPH_mass_spring_clear_constraints(Implicit_Data *data)
void BPH_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3])
{
zero_m3(data->S[index].m);
-
+
world_to_root_v3(data, index, data->z[index], dV);
}
void BPH_mass_spring_add_constraint_ndof1(Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3])
{
float m[3][3], p[3], q[3], u[3], cmat[3][3];
-
+
world_to_root_v3(data, index, p, c1);
mul_fvectorT_fvector(cmat, p, p);
sub_m3_m3m3(m, I, cmat);
-
+
world_to_root_v3(data, index, q, c2);
mul_fvectorT_fvector(cmat, q, q);
sub_m3_m3m3(m, m, cmat);
-
+
/* XXX not sure but multiplication should work here */
copy_m3_m3(data->S[index].m, m);
// mul_m3_m3m3(data->S[index].m, data->S[index].m, m);
-
+
world_to_root_v3(data, index, u, dV);
add_v3_v3(data->z[index], u);
}
@@ -1271,14 +1271,14 @@ void BPH_mass_spring_add_constraint_ndof1(Implicit_Data *data, int index, const
void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data, int index, const float c1[3], const float dV[3])
{
float m[3][3], p[3], u[3], cmat[3][3];
-
+
world_to_root_v3(data, index, p, c1);
mul_fvectorT_fvector(cmat, p, p);
sub_m3_m3m3(m, I, cmat);
-
+
copy_m3_m3(data->S[index].m, m);
// mul_m3_m3m3(data->S[index].m, data->S[index].m, m);
-
+
world_to_root_v3(data, index, u, dV);
add_v3_v3(data->z[index], u);
}
@@ -1289,7 +1289,7 @@ void BPH_mass_spring_clear_forces(Implicit_Data *data)
zero_lfvector(data->F, numverts);
init_bfmatrix(data->dFdX, ZERO);
init_bfmatrix(data->dFdV, ZERO);
-
+
data->num_blocks = 0;
}
@@ -1300,37 +1300,37 @@ void BPH_mass_spring_force_reference_frame(Implicit_Data *data, int index, const
float f[3], dfdx[3][3], dfdv[3][3];
float euler[3], coriolis[3], centrifugal[3], rotvel[3];
float deuler[3][3], dcoriolis[3][3], dcentrifugal[3][3], drotvel[3][3];
-
+
world_to_root_v3(data, index, acc, acceleration);
world_to_root_v3(data, index, w, omega);
world_to_root_v3(data, index, dwdt, domega_dt);
-
+
cross_v3_v3v3(euler, dwdt, data->X[index]);
cross_v3_v3v3(coriolis, w, data->V[index]);
mul_v3_fl(coriolis, 2.0f);
cross_v3_v3v3(rotvel, w, data->X[index]);
cross_v3_v3v3(centrifugal, w, rotvel);
-
+
sub_v3_v3v3(f, acc, euler);
sub_v3_v3(f, coriolis);
sub_v3_v3(f, centrifugal);
-
+
mul_v3_fl(f, mass); /* F = m * a */
-
+
cross_v3_identity(deuler, dwdt);
cross_v3_identity(dcoriolis, w);
mul_m3_fl(dcoriolis, 2.0f);
cross_v3_identity(drotvel, w);
cross_m3_v3m3(dcentrifugal, w, drotvel);
-
+
add_m3_m3m3(dfdx, deuler, dcentrifugal);
negate_m3(dfdx);
mul_m3_fl(dfdx, mass);
-
+
copy_m3_m3(dfdv, dcoriolis);
negate_m3(dfdv);
mul_m3_fl(dfdv, mass);
-
+
add_v3_v3(data->F[index], f);
add_m3_m3m3(data->dFdX[index].m, data->dFdX[index].m, dfdx);
add_m3_m3m3(data->dFdV[index].m, data->dFdV[index].m, dfdv);
@@ -1349,7 +1349,7 @@ void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, c
float f[3];
world_to_root_v3(data, index, f, g);
mul_v3_fl(f, mass);
-
+
add_v3_v3(data->F[index], f);
}
@@ -1358,10 +1358,10 @@ void BPH_mass_spring_force_drag(Implicit_Data *data, float drag)
int i, numverts = data->M[0].vcount;
for (i = 0; i < numverts; i++) {
float tmp[3][3];
-
+
/* NB: uses root space velocity, no need to transform */
madd_v3_v3fl(data->F[i], data->V[i], -drag);
-
+
copy_m3_m3(tmp, I);
mul_m3_fl(tmp, -drag);
add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, tmp);
@@ -1374,7 +1374,7 @@ void BPH_mass_spring_force_extern(struct Implicit_Data *data, int i, const float
world_to_root_v3(data, i, tf, f);
world_to_root_m3(data, i, tdfdx, dfdx);
world_to_root_m3(data, i, tdfdv, dfdv);
-
+
add_v3_v3(data->F[i], tf);
add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, tdfdx);
add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, tdfdv);
@@ -1383,10 +1383,10 @@ void BPH_mass_spring_force_extern(struct Implicit_Data *data, int i, const float
static float calc_nor_area_tri(float nor[3], const float v1[3], const float v2[3], const float v3[3])
{
float n1[3], n2[3];
-
+
sub_v3_v3v3(n1, v1, v2);
sub_v3_v3v3(n2, v2, v3);
-
+
cross_v3_v3v3(nor, n1, n2);
return normalize_v3(nor);
}
@@ -1397,17 +1397,17 @@ void BPH_mass_spring_force_face_wind(Implicit_Data *data, int v1, int v2, int v3
const float effector_scale = 0.02f;
float win[3], nor[3], area;
float factor;
-
+
/* calculate face normal and area */
area = calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]);
factor = effector_scale * area / 3.0f;
-
+
world_to_root_v3(data, v1, win, winvec[v1]);
madd_v3_v3fl(data->F[v1], nor, factor * dot_v3v3(win, nor));
-
+
world_to_root_v3(data, v2, win, winvec[v2]);
madd_v3_v3fl(data->F[v2], nor, factor * dot_v3v3(win, nor));
-
+
world_to_root_v3(data, v3, win, winvec[v3]);
madd_v3_v3fl(data->F[v3], nor, factor * dot_v3v3(win, nor));
}
@@ -1417,17 +1417,17 @@ static void edge_wind_vertex(const float dir[3], float length, float radius, con
const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */
float cos_alpha, sin_alpha, cross_section;
float windlen = len_v3(wind);
-
+
if (windlen == 0.0f) {
zero_v3(f);
return;
}
-
+
/* angle of wind direction to edge */
cos_alpha = dot_v3v3(wind, dir) / windlen;
sin_alpha = sqrtf(1.0f - cos_alpha * cos_alpha);
cross_section = radius * ((float)M_PI * radius * sin_alpha + length * cos_alpha);
-
+
mul_v3_v3fl(f, wind, density * cross_section);
}
@@ -1435,14 +1435,14 @@ void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, float
{
float win[3], dir[3], length;
float f[3], dfdx[3][3], dfdv[3][3];
-
+
sub_v3_v3v3(dir, data->X[v1], data->X[v2]);
length = normalize_v3(dir);
-
+
world_to_root_v3(data, v1, win, winvec[v1]);
edge_wind_vertex(dir, length, radius1, win, f, dfdx, dfdv);
add_v3_v3(data->F[v1], f);
-
+
world_to_root_v3(data, v2, win, winvec[v2]);
edge_wind_vertex(dir, length, radius2, win, f, dfdx, dfdv);
add_v3_v3(data->F[v2], f);
@@ -1451,10 +1451,10 @@ void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, float
void BPH_mass_spring_force_vertex_wind(Implicit_Data *data, int v, float UNUSED(radius), const float (*winvec)[3])
{
const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */
-
+
float wind[3];
float f[3];
-
+
world_to_root_v3(data, v, wind, winvec[v]);
mul_v3_v3fl(f, wind, density);
add_v3_v3(data->F[v], f);
@@ -1466,8 +1466,8 @@ BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, fl
//return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k;
outerproduct(to, dir, dir);
sub_m3_m3m3(to, I, to);
-
- mul_m3_fl(to, (L/length));
+
+ mul_m3_fl(to, (L/length));
sub_m3_m3m3(to, to, I);
mul_m3_fl(to, k);
}
@@ -1476,7 +1476,7 @@ BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, fl
#if 0
BLI_INLINE void dfdx_damp(float to[3][3], const float dir[3], float length, const float vel[3], float rest, float damping)
{
- // inner spring damping vel is the relative velocity of the endpoints.
+ // inner spring damping vel is the relative velocity of the endpoints.
// return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest)));
mul_fvectorT_fvector(to, dir, dir);
sub_fmatrix_fmatrix(to, I, to);
@@ -1512,7 +1512,7 @@ BLI_INLINE float fbstar(float length, float L, float kb, float cb)
{
float tempfb_fl = kb * fb(length, L);
float fbstar_fl = cb * (length - L);
-
+
if (tempfb_fl < fbstar_fl)
return fbstar_fl;
else
@@ -1539,7 +1539,7 @@ BLI_INLINE bool spring_length(Implicit_Data *data, int i, int j, float r_extent[
sub_v3_v3v3(r_extent, data->X[j], data->X[i]);
sub_v3_v3v3(r_vel, data->V[j], data->V[i]);
*r_length = len_v3(r_extent);
-
+
if (*r_length > ALMOST_ZERO) {
/*
if (length>L) {
@@ -1557,21 +1557,21 @@ BLI_INLINE bool spring_length(Implicit_Data *data, int i, int j, float r_extent[
else {
zero_v3(r_dir);
}
-
+
return true;
}
BLI_INLINE void apply_spring(Implicit_Data *data, int i, int j, const float f[3], float dfdx[3][3], float dfdv[3][3])
{
int block_ij = BPH_mass_spring_add_block(data, i, j);
-
+
add_v3_v3(data->F[i], f);
sub_v3_v3(data->F[j], f);
-
+
add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfdx);
add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfdx);
sub_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfdx);
-
+
add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, dfdv);
add_m3_m3m3(data->dFdV[j].m, data->dFdV[j].m, dfdv);
sub_m3_m3m3(data->dFdV[block_ij].m, data->dFdV[block_ij].m, dfdv);
@@ -1581,7 +1581,7 @@ bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, floa
float stiffness, float damping, bool no_compress, float clamp_force)
{
float extent[3], length, dir[3], vel[3];
-
+
// calculate elonglation
spring_length(data, i, j, extent, dir, &length, vel);
@@ -1590,22 +1590,22 @@ bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, floa
Thus length > restlen makes cloth unconstrained at the start of simulation. */
if ((length >= restlen && length > 0) || no_compress) {
float stretch_force, f[3], dfdx[3][3], dfdv[3][3];
-
+
stretch_force = stiffness * (length - restlen);
if (clamp_force > 0.0f && stretch_force > clamp_force) {
stretch_force = clamp_force;
}
mul_v3_v3fl(f, dir, stretch_force);
-
+
// Ascher & Boxman, p.21: Damping only during elonglation
// something wrong with it...
madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir));
-
+
dfdx_spring(dfdx, dir, length, restlen, stiffness);
dfdv_damp(dfdv, dir, damping);
-
+
apply_spring(data, i, j, f, dfdx, dfdv);
-
+
return true;
}
else {
@@ -1617,23 +1617,23 @@ bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, floa
bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, float restlen, float kb, float cb)
{
float extent[3], length, dir[3], vel[3];
-
+
// calculate elonglation
spring_length(data, i, j, extent, dir, &length, vel);
-
+
if (length < restlen) {
float f[3], dfdx[3][3], dfdv[3][3];
-
+
mul_v3_v3fl(f, dir, fbstar(length, restlen, kb, cb));
-
+
outerproduct(dfdx, dir, dir);
mul_m3_fl(dfdx, fbstar_jacobi(length, restlen, kb, cb));
-
+
/* XXX damping not supported */
zero_m3(dfdv);
-
+
apply_spring(data, i, j, f, dfdx, dfdv);
-
+
return true;
}
else {
@@ -1650,10 +1650,10 @@ bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, flo
BLI_INLINE void spring_grad_dir(Implicit_Data *data, int i, int j, float edge[3], float dir[3], float grad_dir[3][3])
{
float length;
-
+
sub_v3_v3v3(edge, data->X[j], data->X[i]);
length = normalize_v3_v3(dir, edge);
-
+
if (length > ALMOST_ZERO) {
outerproduct(grad_dir, dir, dir);
sub_m3_m3m3(grad_dir, I, grad_dir);
@@ -1676,39 +1676,39 @@ BLI_INLINE void spring_angbend_forces(Implicit_Data *data, int i, int j, int k,
float f_bend[3], f_damp[3];
float fk[3];
float dist[3];
-
+
zero_v3(fk);
-
+
sub_v3_v3v3(edge_ij, data->X[j], data->X[i]);
if (q == i) sub_v3_v3(edge_ij, dx);
if (q == j) add_v3_v3(edge_ij, dx);
normalize_v3_v3(dir_ij, edge_ij);
-
+
sub_v3_v3v3(edge_jk, data->X[k], data->X[j]);
if (q == j) sub_v3_v3(edge_jk, dx);
if (q == k) add_v3_v3(edge_jk, dx);
normalize_v3_v3(dir_jk, edge_jk);
-
+
sub_v3_v3v3(vel_ij, data->V[j], data->V[i]);
if (q == i) sub_v3_v3(vel_ij, dv);
if (q == j) add_v3_v3(vel_ij, dv);
-
+
sub_v3_v3v3(vel_jk, data->V[k], data->V[j]);
if (q == j) sub_v3_v3(vel_jk, dv);
if (q == k) add_v3_v3(vel_jk, dv);
-
+
/* bending force */
sub_v3_v3v3(dist, goal, edge_jk);
mul_v3_v3fl(f_bend, dist, stiffness);
-
+
add_v3_v3(fk, f_bend);
-
+
/* damping force */
madd_v3_v3v3fl(vel_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk));
mul_v3_v3fl(f_damp, vel_ortho, damping);
-
+
sub_v3_v3(fk, f_damp);
-
+
copy_v3_v3(r_f, fk);
}
@@ -1722,24 +1722,24 @@ BLI_INLINE void spring_angbend_estimate_dfdx(Implicit_Data *data, int i, int j,
float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3];
float f[3];
int a, b;
-
+
zero_m3(dvec_null);
unit_m3(dvec_pos);
mul_m3_fl(dvec_pos, delta * 0.5f);
copy_m3_m3(dvec_neg, dvec_pos);
negate_m3(dvec_neg);
-
+
/* XXX TODO offset targets to account for position dependency */
-
+
for (a = 0; a < 3; ++a) {
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_pos[a], dvec_null[a], f);
copy_v3_v3(dfdx[a], f);
-
+
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_neg[a], dvec_null[a], f);
sub_v3_v3(dfdx[a], f);
-
+
for (b = 0; b < 3; ++b) {
dfdx[a][b] /= delta;
}
@@ -1756,24 +1756,24 @@ BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j,
float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3];
float f[3];
int a, b;
-
+
zero_m3(dvec_null);
unit_m3(dvec_pos);
mul_m3_fl(dvec_pos, delta * 0.5f);
copy_m3_m3(dvec_neg, dvec_pos);
negate_m3(dvec_neg);
-
+
/* XXX TODO offset targets to account for position dependency */
-
+
for (a = 0; a < 3; ++a) {
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_null[a], dvec_pos[a], f);
copy_v3_v3(dfdv[a], f);
-
+
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_null[a], dvec_neg[a], f);
sub_v3_v3(dfdv[a], f);
-
+
for (b = 0; b < 3; ++b) {
dfdv[a][b] /= delta;
}
@@ -1790,45 +1790,45 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
float fj[3], fk[3];
float dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3];
float dfj_dvi[3][3], dfj_dvj[3][3], dfk_dvi[3][3], dfk_dvj[3][3], dfk_dvk[3][3];
-
+
const float vecnull[3] = {0.0f, 0.0f, 0.0f};
-
+
int block_ij = BPH_mass_spring_add_block(data, i, j);
int block_jk = BPH_mass_spring_add_block(data, j, k);
int block_ik = BPH_mass_spring_add_block(data, i, k);
-
+
world_to_root_v3(data, j, goal, target);
-
+
spring_angbend_forces(data, i, j, k, goal, stiffness, damping, k, vecnull, vecnull, fk);
negate_v3_v3(fj, fk); /* counterforce */
-
+
spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, i, dfk_dxi);
spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, j, dfk_dxj);
spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, k, dfk_dxk);
copy_m3_m3(dfj_dxi, dfk_dxi); negate_m3(dfj_dxi);
copy_m3_m3(dfj_dxj, dfk_dxj); negate_m3(dfj_dxj);
-
+
spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, i, dfk_dvi);
spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, j, dfk_dvj);
spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, k, dfk_dvk);
copy_m3_m3(dfj_dvi, dfk_dvi); negate_m3(dfj_dvi);
copy_m3_m3(dfj_dvj, dfk_dvj); negate_m3(dfj_dvj);
-
+
/* add forces and jacobians to the solver data */
-
+
add_v3_v3(data->F[j], fj);
add_v3_v3(data->F[k], fk);
-
+
add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfj_dxj);
add_m3_m3m3(data->dFdX[k].m, data->dFdX[k].m, dfk_dxk);
-
+
add_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfj_dxi);
add_m3_m3m3(data->dFdX[block_jk].m, data->dFdX[block_jk].m, dfk_dxj);
add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi);
-
+
add_m3_m3m3(data->dFdV[j].m, data->dFdV[j].m, dfj_dvj);
add_m3_m3m3(data->dFdV[k].m, data->dFdV[k].m, dfk_dvk);
-
+
add_m3_m3m3(data->dFdV[block_ij].m, data->dFdV[block_ij].m, dfj_dvi);
add_m3_m3m3(data->dFdV[block_jk].m, data->dFdV[block_jk].m, dfk_dvj);
add_m3_m3m3(data->dFdV[block_ik].m, data->dFdV[block_ik].m, dfk_dvi);
@@ -1847,10 +1847,10 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
float fi[3], fj[3], fk[3];
float dfi_dxi[3][3], dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3];
float dfdvi[3][3];
-
+
// TESTING
damping = 0.0f;
-
+
zero_v3(fi);
zero_v3(fj);
zero_v3(fk);
@@ -1859,68 +1859,68 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
zero_m3(dfk_dxi);
zero_m3(dfk_dxj);
zero_m3(dfk_dxk);
-
+
/* jacobian of direction vectors */
spring_grad_dir(data, i, j, edge_ij, dir_ij, grad_dir_ij);
spring_grad_dir(data, j, k, edge_jk, dir_jk, grad_dir_jk);
-
+
sub_v3_v3v3(vel_jk, data->V[k], data->V[j]);
-
+
/* bending force */
mul_v3_v3fl(target, dir_ij, restlen);
sub_v3_v3v3(dist, target, edge_jk);
mul_v3_v3fl(fk, dist, stiffness);
-
+
/* damping force */
madd_v3_v3v3fl(vel_jk_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk));
madd_v3_v3fl(fk, vel_jk_ortho, damping);
-
+
/* XXX this only holds true as long as we assume straight rest shape!
* eventually will become a bit more involved since the opposite segment
* gets its own target, under condition of having equal torque on both sides.
*/
copy_v3_v3(fi, fk);
-
+
/* counterforce on the middle point */
sub_v3_v3(fj, fi);
sub_v3_v3(fj, fk);
-
+
/* === derivatives === */
-
+
madd_m3_m3fl(dfk_dxi, grad_dir_ij, stiffness * restlen);
-
+
madd_m3_m3fl(dfk_dxj, grad_dir_ij, -stiffness * restlen);
madd_m3_m3fl(dfk_dxj, I, stiffness);
-
+
madd_m3_m3fl(dfk_dxk, I, -stiffness);
-
+
copy_m3_m3(dfi_dxi, dfk_dxk);
negate_m3(dfi_dxi);
-
+
/* dfj_dfi == dfi_dfj due to symmetry,
* dfi_dfj == dfk_dfj due to fi == fk
* XXX see comment above on future bent rest shapes
*/
copy_m3_m3(dfj_dxi, dfk_dxj);
-
+
/* dfj_dxj == -(dfi_dxj + dfk_dxj) due to fj == -(fi + fk) */
sub_m3_m3m3(dfj_dxj, dfj_dxj, dfj_dxi);
sub_m3_m3m3(dfj_dxj, dfj_dxj, dfk_dxj);
-
+
/* add forces and jacobians to the solver data */
add_v3_v3(data->F[i], fi);
add_v3_v3(data->F[j], fj);
add_v3_v3(data->F[k], fk);
-
+
add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfi_dxi);
add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfj_dxj);
add_m3_m3m3(data->dFdX[k].m, data->dFdX[k].m, dfk_dxk);
-
+
add_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfj_dxi);
add_m3_m3m3(data->dFdX[block_jk].m, data->dFdX[block_jk].m, dfk_dxj);
add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi);
#endif
-
+
return true;
}
@@ -1929,29 +1929,29 @@ bool BPH_mass_spring_force_spring_goal(Implicit_Data *data, int i, const float g
{
float root_goal_x[3], root_goal_v[3], extent[3], length, dir[3], vel[3];
float f[3], dfdx[3][3], dfdv[3][3];
-
+
/* goal is in world space */
world_to_root_v3(data, i, root_goal_x, goal_x);
world_to_root_v3(data, i, root_goal_v, goal_v);
-
+
sub_v3_v3v3(extent, root_goal_x, data->X[i]);
sub_v3_v3v3(vel, root_goal_v, data->V[i]);
length = normalize_v3_v3(dir, extent);
-
+
if (length > ALMOST_ZERO) {
mul_v3_v3fl(f, dir, stiffness * length);
-
+
// Ascher & Boxman, p.21: Damping only during elonglation
// something wrong with it...
madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir));
-
+
dfdx_spring(dfdx, dir, length, 0.0f, stiffness);
dfdv_damp(dfdv, dir, damping);
-
+
add_v3_v3(data->F[i], f);
add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfdx);
add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, dfdv);
-
+
return true;
}
else {
diff --git a/source/blender/physics/intern/implicit_eigen.cpp b/source/blender/physics/intern/implicit_eigen.cpp
index d56525f2e93..eaac63893a6 100644
--- a/source/blender/physics/intern/implicit_eigen.cpp
+++ b/source/blender/physics/intern/implicit_eigen.cpp
@@ -99,24 +99,24 @@ static float I[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
class fVector : public Eigen::Vector3f {
public:
typedef float *ctype;
-
+
fVector()
{
}
-
+
fVector(const ctype &v)
{
for (int k = 0; k < 3; ++k)
coeffRef(k) = v[k];
}
-
+
fVector& operator = (const ctype &v)
{
for (int k = 0; k < 3; ++k)
coeffRef(k) = v[k];
return *this;
}
-
+
operator ctype()
{
return data();
@@ -129,18 +129,18 @@ public:
class fMatrix : public Eigen::Matrix3f {
public:
typedef float (*ctype)[3];
-
+
fMatrix()
{
}
-
+
fMatrix(const ctype &v)
{
for (int k = 0; k < 3; ++k)
for (int l = 0; l < 3; ++l)
coeffRef(l, k) = v[k][l];
}
-
+
fMatrix& operator = (const ctype &v)
{
for (int k = 0; k < 3; ++k)
@@ -148,7 +148,7 @@ public:
coeffRef(l, k) = v[k][l];
return *this;
}
-
+
operator ctype()
{
return (ctype)data();
@@ -161,23 +161,23 @@ public:
class lVector : public Eigen::VectorXf {
public:
typedef Eigen::VectorXf base_t;
-
+
lVector()
{
}
-
+
template <typename T>
lVector& operator = (T rhs)
{
base_t::operator=(rhs);
return *this;
}
-
+
float* v3(int vertex)
{
return &coeffRef(3 * vertex);
}
-
+
const float* v3(int vertex) const
{
return &coeffRef(3 * vertex);
@@ -198,18 +198,18 @@ struct lMatrixCtor {
lMatrixCtor()
{
}
-
+
void reset()
{
m_trips.clear();
}
-
+
void reserve(int numverts)
{
/* reserve for diagonal entries */
m_trips.reserve(numverts * 9);
}
-
+
void add(int i, int j, const fMatrix &m)
{
i *= 3;
@@ -218,7 +218,7 @@ struct lMatrixCtor {
for (int l = 0; l < 3; ++l)
m_trips.push_back(Triplet(i + k, j + l, m.coeff(l, k)));
}
-
+
void sub(int i, int j, const fMatrix &m)
{
i *= 3;
@@ -227,13 +227,13 @@ struct lMatrixCtor {
for (int l = 0; l < 3; ++l)
m_trips.push_back(Triplet(i + k, j + l, -m.coeff(l, k)));
}
-
+
inline void construct(lMatrix &m)
{
m.setFromTriplets(m_trips.begin(), m_trips.end());
m_trips.clear();
}
-
+
private:
TripletList m_trips;
};
@@ -253,7 +253,7 @@ static void print_lvector(const lVector &v)
for (int i = 0; i < v.rows(); ++i) {
if (i > 0 && i % 3 == 0)
printf("\n");
-
+
printf("%f,\n", v[i]);
}
}
@@ -263,11 +263,11 @@ static void print_lmatrix(const lMatrix &m)
for (int j = 0; j < m.rows(); ++j) {
if (j > 0 && j % 3 == 0)
printf("\n");
-
+
for (int i = 0; i < m.cols(); ++i) {
if (i > 0 && i % 3 == 0)
printf(" ");
-
+
implicit_print_matrix_elem(m.coeff(j, i));
}
printf("\n");
@@ -383,63 +383,63 @@ BLI_INLINE void madd_m3_m3m3fl(float r[3][3], float a[3][3], float b[3][3], floa
struct Implicit_Data {
typedef std::vector<fMatrix> fMatrixVector;
-
+
Implicit_Data(int numverts)
{
resize(numverts);
}
-
+
void resize(int numverts)
{
this->numverts = numverts;
int tot = 3 * numverts;
-
+
M.resize(tot, tot);
F.resize(tot);
dFdX.resize(tot, tot);
dFdV.resize(tot, tot);
-
+
tfm.resize(numverts, I);
-
+
X.resize(tot);
Xnew.resize(tot);
V.resize(tot);
Vnew.resize(tot);
-
+
A.resize(tot, tot);
B.resize(tot);
-
+
dV.resize(tot);
z.resize(tot);
S.resize(tot, tot);
-
+
iM.reserve(numverts);
idFdX.reserve(numverts);
idFdV.reserve(numverts);
iS.reserve(numverts);
}
-
+
int numverts;
-
+
/* inputs */
lMatrix M; /* masses */
lVector F; /* forces */
lMatrix dFdX, dFdV; /* force jacobians */
-
+
fMatrixVector tfm; /* local coordinate transform */
-
+
/* motion state data */
lVector X, Xnew; /* positions */
lVector V, Vnew; /* velocities */
-
+
/* internal solver data */
lVector B; /* B for A*dV = B */
lMatrix A; /* A for A*dV = B */
-
+
lVector dV; /* velocity change (solution of A*dV = B) */
lVector z; /* target velocity in constrained directions */
lMatrix S; /* filtering matrix for constraints */
-
+
/* temporary constructors */
lMatrixCtor iM; /* masses */
lMatrixCtor idFdX, idFdV; /* force jacobians */
@@ -502,25 +502,25 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol
#ifdef USE_EIGEN_CONSTRAINED_CG
typedef ConstraintConjGrad solver_t;
#endif
-
+
data->iM.construct(data->M);
data->idFdX.construct(data->dFdX);
data->idFdV.construct(data->dFdV);
data->iS.construct(data->S);
-
+
solver_t cg;
cg.setMaxIterations(100);
cg.setTolerance(0.01f);
-
+
#ifdef USE_EIGEN_CONSTRAINED_CG
cg.filter() = data->S;
#endif
-
+
data->A = data->M - dt * data->dFdV - dt*dt * data->dFdX;
cg.compute(data->A);
-
+
data->B = dt * data->F + dt*dt * data->dFdX * data->V;
-
+
#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT
printf("==== A ====\n");
print_lmatrix(id->A);
@@ -531,22 +531,22 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol
printf("==== S ====\n");
print_lmatrix(id->S);
#endif
-
+
#ifdef USE_EIGEN_CORE
data->dV = cg.solve(data->B);
#endif
#ifdef USE_EIGEN_CONSTRAINED_CG
data->dV = cg.solveWithGuess(data->B, data->z);
#endif
-
+
#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT
printf("==== dV ====\n");
print_lvector(id->dV);
printf("========\n");
#endif
-
+
data->Vnew = data->V + data->dV;
-
+
switch (cg.info()) {
case Eigen::Success: result->status = BPH_SOLVER_SUCCESS; break;
case Eigen::NoConvergence: result->status = BPH_SOLVER_NO_CONVERGENCE; break;
@@ -556,7 +556,7 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol
result->iterations = cg.iterations();
result->error = cg.error();
-
+
return cg.info() == Eigen::Success;
}
@@ -641,26 +641,26 @@ void BPH_mass_spring_clear_constraints(Implicit_Data *data)
void BPH_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3])
{
data->iS.sub(index, index, I);
-
+
world_to_root_v3(data, index, data->z.v3(index), dV);
}
void BPH_mass_spring_add_constraint_ndof1(Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3])
{
float m[3][3], p[3], q[3], u[3], cmat[3][3];
-
+
world_to_root_v3(data, index, p, c1);
outerproduct(cmat, p, p);
copy_m3_m3(m, cmat);
-
+
world_to_root_v3(data, index, q, c2);
outerproduct(cmat, q, q);
add_m3_m3m3(m, m, cmat);
-
+
/* XXX not sure but multiplication should work here */
data->iS.sub(index, index, m);
// mul_m3_m3m3(data->S[index].m, data->S[index].m, m);
-
+
world_to_root_v3(data, index, u, dV);
add_v3_v3(data->z.v3(index), u);
}
@@ -668,14 +668,14 @@ void BPH_mass_spring_add_constraint_ndof1(Implicit_Data *data, int index, const
void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data, int index, const float c1[3], const float dV[3])
{
float m[3][3], p[3], u[3], cmat[3][3];
-
+
world_to_root_v3(data, index, p, c1);
outerproduct(cmat, p, p);
copy_m3_m3(m, cmat);
-
+
data->iS.sub(index, index, m);
// mul_m3_m3m3(data->S[index].m, data->S[index].m, m);
-
+
world_to_root_v3(data, index, u, dV);
add_v3_v3(data->z.v3(index), u);
}
@@ -694,37 +694,37 @@ void BPH_mass_spring_force_reference_frame(Implicit_Data *data, int index, const
float f[3], dfdx[3][3], dfdv[3][3];
float euler[3], coriolis[3], centrifugal[3], rotvel[3];
float deuler[3][3], dcoriolis[3][3], dcentrifugal[3][3], drotvel[3][3];
-
+
world_to_root_v3(data, index, acc, acceleration);
world_to_root_v3(data, index, w, omega);
world_to_root_v3(data, index, dwdt, domega_dt);
-
+
cross_v3_v3v3(euler, dwdt, data->X.v3(index));
cross_v3_v3v3(coriolis, w, data->V.v3(index));
mul_v3_fl(coriolis, 2.0f);
cross_v3_v3v3(rotvel, w, data->X.v3(index));
cross_v3_v3v3(centrifugal, w, rotvel);
-
+
sub_v3_v3v3(f, acc, euler);
sub_v3_v3(f, coriolis);
sub_v3_v3(f, centrifugal);
-
+
mul_v3_fl(f, mass); /* F = m * a */
-
+
cross_v3_identity(deuler, dwdt);
cross_v3_identity(dcoriolis, w);
mul_m3_fl(dcoriolis, 2.0f);
cross_v3_identity(drotvel, w);
cross_m3_v3m3(dcentrifugal, w, drotvel);
-
+
add_m3_m3m3(dfdx, deuler, dcentrifugal);
negate_m3(dfdx);
mul_m3_fl(dfdx, mass);
-
+
copy_m3_m3(dfdv, dcoriolis);
negate_m3(dfdv);
mul_m3_fl(dfdv, mass);
-
+
add_v3_v3(data->F.v3(index), f);
data->idFdX.add(index, index, dfdx);
data->idFdV.add(index, index, dfdv);
@@ -743,7 +743,7 @@ void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, c
float f[3];
world_to_root_v3(data, index, f, g);
mul_v3_fl(f, mass);
-
+
add_v3_v3(data->F.v3(index), f);
}
@@ -752,10 +752,10 @@ void BPH_mass_spring_force_drag(Implicit_Data *data, float drag)
int numverts = data->numverts;
for (int i = 0; i < numverts; i++) {
float tmp[3][3];
-
+
/* NB: uses root space velocity, no need to transform */
madd_v3_v3fl(data->F.v3(i), data->V.v3(i), -drag);
-
+
copy_m3_m3(tmp, I);
mul_m3_fl(tmp, -drag);
data->idFdV.add(i, i, tmp);
@@ -768,7 +768,7 @@ void BPH_mass_spring_force_extern(struct Implicit_Data *data, int i, const float
world_to_root_v3(data, i, tf, f);
world_to_root_m3(data, i, tdfdx, dfdx);
world_to_root_m3(data, i, tdfdv, dfdv);
-
+
add_v3_v3(data->F.v3(i), tf);
data->idFdX.add(i, i, tdfdx);
data->idFdV.add(i, i, tdfdv);
@@ -777,10 +777,10 @@ void BPH_mass_spring_force_extern(struct Implicit_Data *data, int i, const float
static float calc_nor_area_tri(float nor[3], const float v1[3], const float v2[3], const float v3[3])
{
float n1[3], n2[3];
-
+
sub_v3_v3v3(n1, v1, v2);
sub_v3_v3v3(n2, v2, v3);
-
+
cross_v3_v3v3(nor, n1, n2);
return normalize_v3(nor);
}
@@ -791,17 +791,17 @@ void BPH_mass_spring_force_face_wind(Implicit_Data *data, int v1, int v2, int v3
const float effector_scale = 0.02f;
float win[3], nor[3], area;
float factor;
-
+
// calculate face normal and area
area = calc_nor_area_tri(nor, data->X.v3(v1), data->X.v3(v2), data->X.v3(v3));
factor = effector_scale * area / 3.0f;
-
+
world_to_root_v3(data, v1, win, winvec[v1]);
madd_v3_v3fl(data->F.v3(v1), nor, factor * dot_v3v3(win, nor));
-
+
world_to_root_v3(data, v2, win, winvec[v2]);
madd_v3_v3fl(data->F.v3(v2), nor, factor * dot_v3v3(win, nor));
-
+
world_to_root_v3(data, v3, win, winvec[v3]);
madd_v3_v3fl(data->F.v3(v3), nor, factor * dot_v3v3(win, nor));
}
@@ -810,14 +810,14 @@ void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, const
{
const float effector_scale = 0.01;
float win[3], dir[3], nor[3], length;
-
+
sub_v3_v3v3(dir, data->X.v3(v1), data->X.v3(v2));
length = normalize_v3(dir);
-
+
world_to_root_v3(data, v1, win, winvec[v1]);
madd_v3_v3v3fl(nor, win, dir, -dot_v3v3(win, dir));
madd_v3_v3fl(data->F.v3(v1), nor, effector_scale * length);
-
+
world_to_root_v3(data, v2, win, winvec[v2]);
madd_v3_v3v3fl(nor, win, dir, -dot_v3v3(win, dir));
madd_v3_v3fl(data->F.v3(v2), nor, effector_scale * length);
@@ -829,8 +829,8 @@ BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, fl
//return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k;
outerproduct(to, dir, dir);
sub_m3_m3m3(to, I, to);
-
- mul_m3_fl(to, (L/length));
+
+ mul_m3_fl(to, (L/length));
sub_m3_m3m3(to, to, I);
mul_m3_fl(to, k);
}
@@ -839,7 +839,7 @@ BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, fl
#if 0
BLI_INLINE void dfdx_damp(float to[3][3], const float dir[3], float length, const float vel[3], float rest, float damping)
{
- // inner spring damping vel is the relative velocity of the endpoints.
+ // inner spring damping vel is the relative velocity of the endpoints.
// return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest)));
mul_fvectorT_fvector(to, dir, dir);
sub_fmatrix_fmatrix(to, I, to);
@@ -871,7 +871,7 @@ BLI_INLINE float fbstar(float length, float L, float kb, float cb)
{
float tempfb_fl = kb * fb(length, L);
float fbstar_fl = cb * (length - L);
-
+
if (tempfb_fl < fbstar_fl)
return fbstar_fl;
else
@@ -898,7 +898,7 @@ BLI_INLINE bool spring_length(Implicit_Data *data, int i, int j, float r_extent[
sub_v3_v3v3(r_extent, data->X.v3(j), data->X.v3(i));
sub_v3_v3v3(r_vel, data->V.v3(j), data->V.v3(i));
*r_length = len_v3(r_extent);
-
+
if (*r_length > ALMOST_ZERO) {
/*
if (length>L) {
@@ -916,7 +916,7 @@ BLI_INLINE bool spring_length(Implicit_Data *data, int i, int j, float r_extent[
else {
zero_v3(r_dir);
}
-
+
return true;
}
@@ -924,12 +924,12 @@ BLI_INLINE void apply_spring(Implicit_Data *data, int i, int j, const float f[3]
{
add_v3_v3(data->F.v3(i), f);
sub_v3_v3(data->F.v3(j), f);
-
+
data->idFdX.add(i, i, dfdx);
data->idFdX.add(j, j, dfdx);
data->idFdX.sub(i, j, dfdx);
data->idFdX.sub(j, i, dfdx);
-
+
data->idFdV.add(i, i, dfdv);
data->idFdV.add(j, j, dfdv);
data->idFdV.sub(i, j, dfdv);
@@ -941,39 +941,39 @@ bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, floa
float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3])
{
float extent[3], length, dir[3], vel[3];
-
+
// calculate elonglation
spring_length(data, i, j, extent, dir, &length, vel);
-
+
if (length > restlen || no_compress) {
float stretch_force, f[3], dfdx[3][3], dfdv[3][3];
-
+
stretch_force = stiffness * (length - restlen);
if (clamp_force > 0.0f && stretch_force > clamp_force) {
stretch_force = clamp_force;
}
mul_v3_v3fl(f, dir, stretch_force);
-
+
// Ascher & Boxman, p.21: Damping only during elonglation
// something wrong with it...
madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir));
-
+
dfdx_spring(dfdx, dir, length, restlen, stiffness);
dfdv_damp(dfdv, dir, damping);
-
+
apply_spring(data, i, j, f, dfdx, dfdv);
-
+
if (r_f) copy_v3_v3(r_f, f);
if (r_dfdx) copy_m3_m3(r_dfdx, dfdx);
if (r_dfdv) copy_m3_m3(r_dfdv, dfdv);
-
+
return true;
}
else {
if (r_f) zero_v3(r_f);
if (r_dfdx) zero_m3(r_dfdx);
if (r_dfdv) zero_m3(r_dfdv);
-
+
return false;
}
}
@@ -984,34 +984,34 @@ bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, flo
float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3])
{
float extent[3], length, dir[3], vel[3];
-
+
// calculate elonglation
spring_length(data, i, j, extent, dir, &length, vel);
-
+
if (length < restlen) {
float f[3], dfdx[3][3], dfdv[3][3];
-
+
mul_v3_v3fl(f, dir, fbstar(length, restlen, kb, cb));
-
+
outerproduct(dfdx, dir, dir);
mul_m3_fl(dfdx, fbstar_jacobi(length, restlen, kb, cb));
-
+
/* XXX damping not supported */
zero_m3(dfdv);
-
+
apply_spring(data, i, j, f, dfdx, dfdv);
-
+
if (r_f) copy_v3_v3(r_f, f);
if (r_dfdx) copy_m3_m3(r_dfdx, dfdx);
if (r_dfdv) copy_m3_m3(r_dfdv, dfdv);
-
+
return true;
}
else {
if (r_f) zero_v3(r_f);
if (r_dfdx) zero_m3(r_dfdx);
if (r_dfdv) zero_m3(r_dfdv);
-
+
return false;
}
}
@@ -1025,10 +1025,10 @@ bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, flo
BLI_INLINE void spring_grad_dir(Implicit_Data *data, int i, int j, float edge[3], float dir[3], float grad_dir[3][3])
{
float length;
-
+
sub_v3_v3v3(edge, data->X.v3(j), data->X.v3(i));
length = normalize_v3_v3(dir, edge);
-
+
if (length > ALMOST_ZERO) {
outerproduct(grad_dir, dir, dir);
sub_m3_m3m3(grad_dir, I, grad_dir);
@@ -1051,39 +1051,39 @@ BLI_INLINE void spring_angbend_forces(Implicit_Data *data, int i, int j, int k,
float f_bend[3], f_damp[3];
float fk[3];
float dist[3];
-
+
zero_v3(fk);
-
+
sub_v3_v3v3(edge_ij, data->X.v3(j), data->X.v3(i));
if (q == i) sub_v3_v3(edge_ij, dx);
if (q == j) add_v3_v3(edge_ij, dx);
normalize_v3_v3(dir_ij, edge_ij);
-
+
sub_v3_v3v3(edge_jk, data->X.v3(k), data->X.v3(j));
if (q == j) sub_v3_v3(edge_jk, dx);
if (q == k) add_v3_v3(edge_jk, dx);
normalize_v3_v3(dir_jk, edge_jk);
-
+
sub_v3_v3v3(vel_ij, data->V.v3(j), data->V.v3(i));
if (q == i) sub_v3_v3(vel_ij, dv);
if (q == j) add_v3_v3(vel_ij, dv);
-
+
sub_v3_v3v3(vel_jk, data->V.v3(k), data->V.v3(j));
if (q == j) sub_v3_v3(vel_jk, dv);
if (q == k) add_v3_v3(vel_jk, dv);
-
+
/* bending force */
sub_v3_v3v3(dist, goal, edge_jk);
mul_v3_v3fl(f_bend, dist, stiffness);
-
+
add_v3_v3(fk, f_bend);
-
+
/* damping force */
madd_v3_v3v3fl(vel_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk));
mul_v3_v3fl(f_damp, vel_ortho, damping);
-
+
sub_v3_v3(fk, f_damp);
-
+
copy_v3_v3(r_f, fk);
}
@@ -1097,24 +1097,24 @@ BLI_INLINE void spring_angbend_estimate_dfdx(Implicit_Data *data, int i, int j,
float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3];
float f[3];
int a, b;
-
+
zero_m3(dvec_null);
unit_m3(dvec_pos);
mul_m3_fl(dvec_pos, delta * 0.5f);
copy_m3_m3(dvec_neg, dvec_pos);
negate_m3(dvec_neg);
-
+
/* XXX TODO offset targets to account for position dependency */
-
+
for (a = 0; a < 3; ++a) {
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_pos[a], dvec_null[a], f);
copy_v3_v3(dfdx[a], f);
-
+
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_neg[a], dvec_null[a], f);
sub_v3_v3(dfdx[a], f);
-
+
for (b = 0; b < 3; ++b) {
dfdx[a][b] /= delta;
}
@@ -1131,24 +1131,24 @@ BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j,
float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3];
float f[3];
int a, b;
-
+
zero_m3(dvec_null);
unit_m3(dvec_pos);
mul_m3_fl(dvec_pos, delta * 0.5f);
copy_m3_m3(dvec_neg, dvec_pos);
negate_m3(dvec_neg);
-
+
/* XXX TODO offset targets to account for position dependency */
-
+
for (a = 0; a < 3; ++a) {
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_null[a], dvec_pos[a], f);
copy_v3_v3(dfdv[a], f);
-
+
spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_null[a], dvec_neg[a], f);
sub_v3_v3(dfdv[a], f);
-
+
for (b = 0; b < 3; ++b) {
dfdv[a][b] /= delta;
}
@@ -1165,44 +1165,44 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
float fj[3], fk[3];
float dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3];
float dfj_dvi[3][3], dfj_dvj[3][3], dfk_dvi[3][3], dfk_dvj[3][3], dfk_dvk[3][3];
-
+
const float vecnull[3] = {0.0f, 0.0f, 0.0f};
-
+
world_to_root_v3(data, j, goal, target);
-
+
spring_angbend_forces(data, i, j, k, goal, stiffness, damping, k, vecnull, vecnull, fk);
negate_v3_v3(fj, fk); /* counterforce */
-
+
spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, i, dfk_dxi);
spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, j, dfk_dxj);
spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, k, dfk_dxk);
copy_m3_m3(dfj_dxi, dfk_dxi); negate_m3(dfj_dxi);
copy_m3_m3(dfj_dxj, dfk_dxj); negate_m3(dfj_dxj);
-
+
spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, i, dfk_dvi);
spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, j, dfk_dvj);
spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, k, dfk_dvk);
copy_m3_m3(dfj_dvi, dfk_dvi); negate_m3(dfj_dvi);
copy_m3_m3(dfj_dvj, dfk_dvj); negate_m3(dfj_dvj);
-
+
/* add forces and jacobians to the solver data */
-
+
add_v3_v3(data->F.v3(j), fj);
add_v3_v3(data->F.v3(k), fk);
-
+
data->idFdX.add(j, j, dfj_dxj);
data->idFdX.add(k, k, dfk_dxk);
-
+
data->idFdX.add(i, j, dfj_dxi);
data->idFdX.add(j, i, dfj_dxi);
data->idFdX.add(j, k, dfk_dxj);
data->idFdX.add(k, j, dfk_dxj);
data->idFdX.add(i, k, dfk_dxi);
data->idFdX.add(k, i, dfk_dxi);
-
+
data->idFdV.add(j, j, dfj_dvj);
data->idFdV.add(k, k, dfk_dvk);
-
+
data->idFdV.add(i, j, dfj_dvi);
data->idFdV.add(j, i, dfj_dvi);
data->idFdV.add(j, k, dfk_dvj);
@@ -1223,10 +1223,10 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
float fi[3], fj[3], fk[3];
float dfi_dxi[3][3], dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3];
float dfdvi[3][3];
-
+
// TESTING
damping = 0.0f;
-
+
zero_v3(fi);
zero_v3(fj);
zero_v3(fk);
@@ -1235,68 +1235,68 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
zero_m3(dfk_dxi);
zero_m3(dfk_dxj);
zero_m3(dfk_dxk);
-
+
/* jacobian of direction vectors */
spring_grad_dir(data, i, j, edge_ij, dir_ij, grad_dir_ij);
spring_grad_dir(data, j, k, edge_jk, dir_jk, grad_dir_jk);
-
+
sub_v3_v3v3(vel_jk, data->V[k], data->V[j]);
-
+
/* bending force */
mul_v3_v3fl(target, dir_ij, restlen);
sub_v3_v3v3(dist, target, edge_jk);
mul_v3_v3fl(fk, dist, stiffness);
-
+
/* damping force */
madd_v3_v3v3fl(vel_jk_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk));
madd_v3_v3fl(fk, vel_jk_ortho, damping);
-
+
/* XXX this only holds true as long as we assume straight rest shape!
* eventually will become a bit more involved since the opposite segment
* gets its own target, under condition of having equal torque on both sides.
*/
copy_v3_v3(fi, fk);
-
+
/* counterforce on the middle point */
sub_v3_v3(fj, fi);
sub_v3_v3(fj, fk);
-
+
/* === derivatives === */
-
+
madd_m3_m3fl(dfk_dxi, grad_dir_ij, stiffness * restlen);
-
+
madd_m3_m3fl(dfk_dxj, grad_dir_ij, -stiffness * restlen);
madd_m3_m3fl(dfk_dxj, I, stiffness);
-
+
madd_m3_m3fl(dfk_dxk, I, -stiffness);
-
+
copy_m3_m3(dfi_dxi, dfk_dxk);
negate_m3(dfi_dxi);
-
+
/* dfj_dfi == dfi_dfj due to symmetry,
* dfi_dfj == dfk_dfj due to fi == fk
* XXX see comment above on future bent rest shapes
*/
copy_m3_m3(dfj_dxi, dfk_dxj);
-
+
/* dfj_dxj == -(dfi_dxj + dfk_dxj) due to fj == -(fi + fk) */
sub_m3_m3m3(dfj_dxj, dfj_dxj, dfj_dxi);
sub_m3_m3m3(dfj_dxj, dfj_dxj, dfk_dxj);
-
+
/* add forces and jacobians to the solver data */
add_v3_v3(data->F[i], fi);
add_v3_v3(data->F[j], fj);
add_v3_v3(data->F[k], fk);
-
+
add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfi_dxi);
add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfj_dxj);
add_m3_m3m3(data->dFdX[k].m, data->dFdX[k].m, dfk_dxk);
-
+
add_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfj_dxi);
add_m3_m3m3(data->dFdX[block_jk].m, data->dFdX[block_jk].m, dfk_dxj);
add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi);
#endif
-
+
return true;
}
@@ -1306,40 +1306,40 @@ bool BPH_mass_spring_force_spring_goal(Implicit_Data *data, int i, const float g
{
float root_goal_x[3], root_goal_v[3], extent[3], length, dir[3], vel[3];
float f[3], dfdx[3][3], dfdv[3][3];
-
+
/* goal is in world space */
world_to_root_v3(data, i, root_goal_x, goal_x);
world_to_root_v3(data, i, root_goal_v, goal_v);
-
+
sub_v3_v3v3(extent, root_goal_x, data->X.v3(i));
sub_v3_v3v3(vel, root_goal_v, data->V.v3(i));
length = normalize_v3_v3(dir, extent);
-
+
if (length > ALMOST_ZERO) {
mul_v3_v3fl(f, dir, stiffness * length);
-
+
// Ascher & Boxman, p.21: Damping only during elonglation
// something wrong with it...
madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir));
-
+
dfdx_spring(dfdx, dir, length, 0.0f, stiffness);
dfdv_damp(dfdv, dir, damping);
-
+
add_v3_v3(data->F.v3(i), f);
data->idFdX.add(i, i, dfdx);
data->idFdV.add(i, i, dfdv);
-
+
if (r_f) copy_v3_v3(r_f, f);
if (r_dfdx) copy_m3_m3(r_dfdx, dfdx);
if (r_dfdv) copy_m3_m3(r_dfdv, dfdv);
-
+
return true;
}
else {
if (r_f) zero_v3(r_f);
if (r_dfdx) zero_m3(r_dfdx);
if (r_dfdv) zero_m3(r_dfdv);
-
+
return false;
}
}
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index 0f0060c7578..359369228f8 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -24,7 +24,7 @@
# ***** END GPL LICENSE BLOCK *****
-set(INC
+set(INC
extern/include
intern/include
../blenkernel
diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h
index 1b0707bafc0..660e81eb022 100644
--- a/source/blender/render/extern/include/RE_pipeline.h
+++ b/source/blender/render/extern/include/RE_pipeline.h
@@ -103,11 +103,11 @@ typedef struct RenderPass {
/* after render, the Combined pass is in combined, for renderlayers read from files it is a real pass */
typedef struct RenderLayer {
struct RenderLayer *next, *prev;
-
+
/* copy of RenderData */
char name[RE_MAXNAME];
int layflag, passflag, pass_xor;
-
+
/* MULTIVIEW_TODO: acolrect and scolrect are not supported by multiview at the moment.
* If they are really required they should be in RenderView instead */
@@ -121,16 +121,16 @@ typedef struct RenderLayer {
void *exrhandle;
ListBase passes;
-
+
} RenderLayer;
typedef struct RenderResult {
struct RenderResult *next, *prev;
-
+
/* target image size */
int rectx, recty;
short crop, sample_nr;
-
+
/* the following rect32, rectf and rectz buffers are for temporary storage only, for RenderResult structs
* created in #RE_AcquireResultImage - which do not have RenderView */
@@ -140,25 +140,25 @@ typedef struct RenderResult {
float *rectf;
/* if this exists, a copy of one of layers, or result of composited layers */
float *rectz;
-
+
/* coordinates within final image (after cropping) */
rcti tilerect;
/* offset to apply to get a border render in full image */
int xof, yof;
-
+
/* the main buffers */
ListBase layers;
-
+
/* multiView maps to a StringVector in OpenEXR */
ListBase views; /* RenderView */
/* allowing live updates: */
volatile rcti renrect;
volatile RenderLayer *renlay;
-
+
/* optional saved endresult on disk */
int do_exr_tile;
-
+
/* for render results in Image, verify validity for sequences */
int framenr;
diff --git a/source/blender/render/intern/include/envmap.h b/source/blender/render/intern/include/envmap.h
new file mode 100644
index 00000000000..c66427ae788
--- /dev/null
+++ b/source/blender/render/intern/include/envmap.h
@@ -0,0 +1,54 @@
+/*
+ * envmap_ext.h
+ *
+ *
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/envmap.h
+ * \ingroup render
+ */
+
+
+#ifndef __ENVMAP_H__
+#define __ENVMAP_H__
+
+/**
+ * Make environment maps for all objects in the scene that have an
+ * environment map as texture.
+ * (initrender.c)
+ */
+
+struct Render;
+struct TexResult;
+struct ImagePool;
+
+void make_envmaps(struct Render *re);
+int envmaptex(struct Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool, const bool skip_image_load);
+void env_rotate_scene(struct Render *re, float mat[4][4], int do_rotate);
+
+#endif /* __ENVMAP_H__ */
+
diff --git a/source/blender/render/intern/include/initrender.h b/source/blender/render/intern/include/initrender.h
index e7ff3c7097c..b8732e7cc5c 100644
--- a/source/blender/render/intern/include/initrender.h
+++ b/source/blender/render/intern/include/initrender.h
@@ -31,7 +31,7 @@
#ifndef __INITRENDER_H__
-#define __INITRENDER_H__
+#define __INITRENDER_H__
/* Functions */
diff --git a/source/blender/render/intern/include/pixelblending.h b/source/blender/render/intern/include/pixelblending.h
new file mode 100644
index 00000000000..022510c7132
--- /dev/null
+++ b/source/blender/render/intern/include/pixelblending.h
@@ -0,0 +1,65 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): 2004-2006 Blender Foundation, full recode
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/pixelblending.h
+ * \ingroup render
+ */
+
+
+#ifndef __PIXELBLENDING_H__
+#define __PIXELBLENDING_H__
+
+
+/**
+ * add 1 pixel to into filtered three lines
+ * (float vecs to float vec)
+ */
+void add_filt_fmask(unsigned int mask, const float col[4], float *rowbuf, int row_w);
+void add_filt_fmask_pixsize(unsigned int mask, float *in, float *rowbuf, int row_w, int pixsize);
+void add_filt_fmask_coord(float filt[3][3], const float col[4], float *rowbuf, int row_stride, int x, int y, rcti *mask);
+void mask_array(unsigned int mask, float filt[3][3]);
+
+/**
+ * Alpha-over blending for floats.
+ */
+void addAlphaOverFloat(float dest[4], const float source[4]);
+
+/**
+ * Alpha-under blending for floats.
+ */
+void addAlphaUnderFloat(float dest[4], const float source[4]);
+
+
+/**
+ * Same for floats
+ */
+void addalphaAddfacFloat(float dest[4], const float source[4], char addfac);
+
+/**
+ * dest = dest + source
+ */
+void addalphaAddFloat(float dest[4], const float source[4]);
+
+#endif /* __PIXELBLENDING_H__ */
diff --git a/source/blender/render/intern/include/pixelshading.h b/source/blender/render/intern/include/pixelshading.h
new file mode 100644
index 00000000000..0e630eda475
--- /dev/null
+++ b/source/blender/render/intern/include/pixelshading.h
@@ -0,0 +1,62 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): 2004-2006, Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/pixelshading.h
+ * \ingroup render
+ *
+ * These functions determine what actual color a pixel will have.
+ */
+
+#ifndef __PIXELSHADING_H__
+#define __PIXELSHADING_H__
+
+
+/**
+ * Render the pixel at (x,y) for object ap. Apply the jitter mask.
+ * Output is given in float collector[4]. The type vector:
+ * t[0] - min. distance
+ * t[1] - face/halo index
+ * t[2] - jitter mask
+ * t[3] - type ZB_POLY or ZB_HALO
+ * t[4] - max. distance
+ * mask is pixel coverage in bits
+ * \return pointer to the object
+ */
+int shadeHaloFloat(HaloRen *har,
+ float *col, int zz,
+ float dist, float xn,
+ float yn, short flarec);
+
+/**
+ * Render the sky at pixel (x, y).
+ */
+void shadeSkyPixel(float collector[4], float fx, float fy, short thread);
+void shadeSkyView(float col_r[3], const float rco[3], const float view[3], const float dxyview[2], short thread);
+void shadeAtmPixel(struct SunSky *sunsky, float *collector, float fx, float fy, float distance);
+void shadeSunView(float col_r[3], const float view[3]);
+/* ------------------------------------------------------------------------- */
+
+#endif
+
diff --git a/source/blender/render/intern/include/pointdensity.h b/source/blender/render/intern/include/pointdensity.h
new file mode 100644
index 00000000000..eadf714c1ba
--- /dev/null
+++ b/source/blender/render/intern/include/pointdensity.h
@@ -0,0 +1,51 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Matt Ebb
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/pointdensity.h
+ * \ingroup render
+ */
+
+
+#ifndef __POINTDENSITY_H__
+#define __POINTDENSITY_H__
+
+/**
+ * Make point density kd-trees for all point density textures in the scene
+ */
+
+struct PointDensity;
+struct Render;
+struct TexResult;
+
+void free_pointdensity(struct PointDensity *pd);
+void cache_pointdensity(struct Render *re, struct PointDensity *pd);
+void make_pointdensities(struct Render *re);
+void free_pointdensities(struct Render *re);
+int pointdensitytex(struct Tex *tex, const float texvec[3], struct TexResult *texres);
+
+#endif /* __POINTDENSITY_H__ */
+
diff --git a/source/blender/render/intern/include/raycounter.h b/source/blender/render/intern/include/raycounter.h
new file mode 100644
index 00000000000..e16c6e13c7e
--- /dev/null
+++ b/source/blender/render/intern/include/raycounter.h
@@ -0,0 +1,74 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/raycounter.h
+ * \ingroup render
+ */
+
+
+#ifndef __RAYCOUNTER_H__
+#define __RAYCOUNTER_H__
+
+//#define RE_RAYCOUNTER /* enable counters per ray, useful for measuring raytrace structures performance */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef RE_RAYCOUNTER
+
+/* ray counter functions */
+
+typedef struct RayCounter {
+ struct {
+ unsigned long long test, hit;
+ } faces, bb, simd_bb, raycast, raytrace_hint, rayshadow_last_hit;
+} RayCounter;
+
+#define RE_RC_INIT(isec, shi) (isec).raycounter = &((shi).shading.raycounter)
+void RE_RC_INFO(RayCounter *rc);
+void RE_RC_MERGE(RayCounter *rc, RayCounter *tmp);
+#define RE_RC_COUNT(var) (var)++
+
+extern RayCounter re_rc_counter[];
+
+#else
+
+/* ray counter stubs */
+
+#define RE_RC_INIT(isec,shi)
+#define RE_RC_INFO(rc)
+#define RE_RC_MERGE(dest,src)
+#define RE_RC_COUNT(var)
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/blender/render/intern/include/rayintersection.h b/source/blender/render/intern/include/rayintersection.h
new file mode 100644
index 00000000000..a303301ad3b
--- /dev/null
+++ b/source/blender/render/intern/include/rayintersection.h
@@ -0,0 +1,136 @@
+/*
+ * ***** 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) 2007 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ * RE_raytrace.h: ray tracing api, can be used independently from the renderer.
+ */
+
+/** \file blender/render/intern/include/rayintersection.h
+ * \ingroup render
+ */
+
+
+#ifndef __RAYINTERSECTION_H__
+#define __RAYINTERSECTION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "BLI_math_geom.h"
+
+struct RayObject;
+
+/* Ray Hints */
+
+#define RE_RAY_LCTS_MAX_SIZE 256
+#define RT_USE_LAST_HIT /* last shadow hit is reused before raycasting on whole tree */
+//#define RT_USE_HINT /* last hit object is reused before raycasting on whole tree */
+
+typedef struct LCTSHint {
+ int size;
+ struct RayObject *stack[RE_RAY_LCTS_MAX_SIZE];
+} LCTSHint;
+
+typedef struct RayHint {
+ union { LCTSHint lcts; } data;
+} RayHint;
+
+/* Ray Intersection */
+
+typedef struct Isect {
+ /* ray start, direction (normalized vector), and max distance. on hit,
+ * the distance is modified to be the distance to the hit point. */
+ float start[3];
+ float dir[3];
+ float dist;
+
+ /* for envmap and incremental view update renders */
+ float origstart[3];
+ float origdir[3];
+
+ /* precomputed values to accelerate bounding box intersection */
+ int bv_index[6];
+ float idot_axis[3];
+
+ /* intersection options */
+ int mode; /* RE_RAY_SHADOW, RE_RAY_MIRROR, RE_RAY_SHADOW_TRA */
+ int lay; /* -1 default, set for layer lamps */
+ int skip; /* skip flags */
+ int check; /* check flags */
+ void *userdata; /* used by bake check */
+
+ /* hit information */
+ float u, v;
+ int isect; /* which half of quad */
+
+ struct {
+ void *ob;
+ void *face;
+ } hit, orig;
+
+ /* last hit optimization */
+ struct RayObject *last_hit;
+
+ /* hints */
+#ifdef RT_USE_HINT
+ RayTraceHint *hint, *hit_hint;
+#endif
+ RayHint *hint;
+
+ /* ray counter */
+#ifdef RE_RAYCOUNTER
+ RayCounter *raycounter;
+#endif
+
+ /* Precalculated coefficients for watertight intersection check. */
+ struct IsectRayPrecalc isect_precalc;
+} Isect;
+
+/* ray types */
+#define RE_RAY_SHADOW 0
+#define RE_RAY_MIRROR 1
+#define RE_RAY_SHADOW_TRA 2
+
+/* skip options */
+#define RE_SKIP_CULLFACE (1 << 0)
+/* if using this flag then *face should be a pointer to a VlakRen */
+#define RE_SKIP_VLR_NEIGHBOUR (1 << 1)
+
+/* check options */
+#define RE_CHECK_VLR_NONE 0
+#define RE_CHECK_VLR_RENDER 1
+#define RE_CHECK_VLR_NON_SOLID_MATERIAL 2
+#define RE_CHECK_VLR_BAKE 3
+
+/* arbitrary, but can't use e.g. FLT_MAX because of precision issues */
+#define RE_RAYTRACE_MAXDIST 1e15f
+#define RE_RAYTRACE_EPSILON 0.0f
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RAYINTERSECTION_H__ */
+
diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h
index 8308b5e76e4..fd24f4eb053 100644
--- a/source/blender/render/intern/include/render_types.h
+++ b/source/blender/render/intern/include/render_types.h
@@ -52,10 +52,10 @@ struct Main;
/* this is handed over to threaded hiding/passes/shading engine */
typedef struct RenderPart {
struct RenderPart *next, *prev;
-
+
RenderResult *result; /* result of part rendering */
ListBase fullresult; /* optional full sample buffers */
-
+
rcti disprect; /* part coordinates within total picture */
int rectx, recty; /* the size */
int nr; /* nr is partnr */
@@ -74,10 +74,10 @@ struct Render {
struct Render *next, *prev;
char name[RE_MAXNAME];
int slot;
-
+
/* state settings */
short flag, ok, result_ok;
-
+
/* result of rendering */
RenderResult *result;
/* if render with single-layer option, other rendered layers are stored here */
@@ -88,29 +88,29 @@ struct Render {
* write lock, all external code must use a read lock. internal code is assumed
* to not conflict with writes, so no lock used for that */
ThreadRWMutex resultmutex;
-
+
/* window size, display rect, viewplane */
int winx, winy; /* buffer width and height with percentage applied
* without border & crop. convert to long before multiplying together to avoid overflow. */
rcti disprect; /* part within winx winy */
rctf viewplane; /* mapped on winx winy */
-
+
/* final picture width and height (within disprect) */
int rectx, recty;
-
- /* real maximum size of parts after correction for minimum
+
+ /* real maximum size of parts after correction for minimum
* partx*xparts can be larger than rectx, in that case last part is smaller */
int partx, party;
-
+
/* Camera transform, only used by Freestyle. */
float viewmat[4][4], viewinv[4][4];
float viewmat_orig[4][4]; /* for incremental render */
float winmat[4][4];
-
+
/* clippping */
float clipsta;
float clipend;
-
+
/* main, scene, and its full copy of renderdata and world */
struct Main *main;
Scene *scene;
@@ -119,13 +119,13 @@ struct Render {
int active_view_layer;
struct Object *camera_override;
unsigned int lay, layer_override;
-
+
ThreadRWMutex partsmutex;
ListBase parts;
-
+
/* render engine */
struct RenderEngine *engine;
-
+
#ifdef WITH_FREESTYLE
struct Main *freestyle_bmain;
ListBase freestyle_renders;
@@ -140,17 +140,17 @@ struct Render {
void *duh;
void (*current_scene_update)(void *handle, struct Scene *scene);
void *suh;
-
+
void (*stats_draw)(void *handle, RenderStats *ri);
void *sdh;
void (*progress)(void *handle, float i);
void *prh;
-
+
void (*draw_lock)(void *handle, int i);
void *dlh;
int (*test_break)(void *handle);
void *tbh;
-
+
RenderStats i;
struct ReportList *reports;
diff --git a/source/blender/render/intern/include/rendercore.h b/source/blender/render/intern/include/rendercore.h
new file mode 100644
index 00000000000..aa3efca9e5b
--- /dev/null
+++ b/source/blender/render/intern/include/rendercore.h
@@ -0,0 +1,105 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __RENDERCORE_H__
+#define __RENDERCORE_H__
+
+/** \file blender/render/intern/include/rendercore.h
+ * \ingroup render
+ */
+
+#include "render_types.h"
+
+#include "RE_engine.h"
+
+#include "DNA_node_types.h"
+
+#include "NOD_composite.h"
+
+struct ShadeInput;
+struct ShadeResult;
+struct World;
+struct RenderPart;
+struct RenderLayer;
+struct RayObject;
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct PixStr {
+ struct PixStr *next;
+ int obi, facenr, z, maskz;
+ unsigned short mask;
+ short shadfac;
+} PixStr;
+
+typedef struct PixStrMain {
+ struct PixStrMain *next, *prev;
+ struct PixStr *ps;
+ int counter;
+} PixStrMain;
+
+/* ------------------------------------------------------------------------- */
+
+
+void calc_view_vector(float view[3], float x, float y);
+float mistfactor(float zcor, const float co[3]); /* dist and height, return alpha */
+
+void renderspothalo(struct ShadeInput *shi, float col[4], float alpha);
+void add_halo_flare(Render *re);
+
+void calc_renderco_zbuf(float co[3], const float view[3], int z);
+void calc_renderco_ortho(float co[3], float x, float y, int z);
+
+int count_mask(unsigned short mask);
+
+void zbufshade_tile(struct RenderPart *pa);
+void zbufshadeDA_tile(struct RenderPart *pa);
+
+void zbufshade_sss_tile(struct RenderPart *pa);
+
+int get_sample_layers(struct RenderPart *pa, struct RenderLayer *rl, struct RenderLayer **rlpp);
+
+void render_internal_update_passes(struct RenderEngine *engine, struct Scene *scene, struct SceneRenderLayer *srl);
+
+
+/* -------- ray.c ------- */
+
+struct RayObject *RE_rayobject_create(int type, int size, int octree_resolution);
+
+extern void freeraytree(Render *re);
+extern void makeraytree(Render *re);
+struct RayObject* makeraytree_object(Render *re, ObjectInstanceRen *obi);
+
+extern void ray_shadow(ShadeInput *shi, LampRen *lar, float shadfac[4]);
+extern void ray_trace(ShadeInput *shi, ShadeResult *);
+extern void ray_ao(ShadeInput *shi, float ao[3], float env[3]);
+extern void init_jitter_plane(LampRen *lar);
+extern void init_ao_sphere(Render *re, struct World *wrld);
+extern void init_render_qmcsampler(Render *re);
+extern void free_render_qmcsampler(Render *re);
+
+#endif /* __RENDERCORE_H__ */
diff --git a/source/blender/render/intern/include/shading.h b/source/blender/render/intern/include/shading.h
new file mode 100644
index 00000000000..e306c3c075c
--- /dev/null
+++ b/source/blender/render/intern/include/shading.h
@@ -0,0 +1,105 @@
+/*
+ * ***** 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) 2006 Blender Foundation
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/shading.h
+ * \ingroup render
+ */
+
+
+struct ShadeInput;
+struct ShadeResult;
+struct RenderPart;
+struct RenderLayer;
+struct PixStr;
+struct LampRen;
+struct VlakRen;
+struct StrandPoint;
+struct ObjectInstanceRen;
+struct Isect;
+
+/* shadeinput.c */
+
+#define RE_MAX_OSA 16
+
+/* needed to calculate shadow and AO for an entire pixel */
+typedef struct ShadeSample {
+ int tot; /* amount of shi in use, can be 1 for not FULL_OSA */
+
+ RenderLayer *rlpp[RE_MAX_OSA]; /* fast lookup from sample to renderlayer (fullsample buf) */
+
+ /* could be malloced once */
+ ShadeInput shi[RE_MAX_OSA];
+ ShadeResult shr[RE_MAX_OSA];
+} ShadeSample;
+
+
+ /* also the node shader callback */
+void shade_material_loop(struct ShadeInput *shi, struct ShadeResult *shr);
+
+void shade_input_set_triangle_i(struct ShadeInput *shi, struct ObjectInstanceRen *obi, struct VlakRen *vlr, short i1, short i2, short i3);
+void shade_input_set_triangle(struct ShadeInput *shi, int obi, int facenr, int normal_flip);
+void shade_input_copy_triangle(struct ShadeInput *shi, struct ShadeInput *from);
+void shade_input_calc_viewco(struct ShadeInput *shi, float x, float y, float z, float view[3], float dxyview[2], float co[3], float dxco[3], float dyco[3]);
+void shade_input_set_viewco(struct ShadeInput *shi, float x, float y, float sx, float sy, float z);
+void shade_input_set_uv(struct ShadeInput *shi);
+void shade_input_set_normals(struct ShadeInput *shi);
+void shade_input_set_vertex_normals(struct ShadeInput *shi);
+void shade_input_flip_normals(struct ShadeInput *shi);
+void shade_input_set_shade_texco(struct ShadeInput *shi);
+void shade_input_set_strand(struct ShadeInput *shi, struct StrandRen *strand, struct StrandPoint *spoint);
+void shade_input_set_strand_texco(struct ShadeInput *shi, struct StrandRen *strand, struct StrandVert *svert, struct StrandPoint *spoint);
+void shade_input_do_shade(struct ShadeInput *shi, struct ShadeResult *shr);
+
+void shade_input_init_material(struct ShadeInput *shi);
+void shade_input_initialize(struct ShadeInput *shi, struct RenderPart *pa, struct RenderLayer *rl, int sample);
+
+void shade_sample_initialize(struct ShadeSample *ssamp, struct RenderPart *pa, struct RenderLayer *rl);
+void shade_samples_do_AO(struct ShadeSample *ssamp);
+void shade_samples_fill_with_ps(struct ShadeSample *ssamp, struct PixStr *ps, int x, int y);
+int shade_samples(struct ShadeSample *ssamp, struct PixStr *ps, int x, int y);
+
+void vlr_set_uv_indices(struct VlakRen *vlr, int *i1, int *i2, int *i3);
+
+void calc_R_ref(struct ShadeInput *shi);
+
+void barycentric_differentials_from_position(
+ const float co[3], const float v1[3], const float v2[3], const float v3[3],
+ const float dxco[3], const float dyco[3], const float facenor[3], const bool differentials,
+ float *u, float *v, float *dx_u, float *dx_v, float *dy_u, float *dy_v);
+
+/* shadeoutput. */
+void shade_lamp_loop(struct ShadeInput *shi, struct ShadeResult *shr);
+
+void shade_color(struct ShadeInput *shi, ShadeResult *shr);
+
+void ambient_occlusion(struct ShadeInput *shi);
+void environment_lighting_apply(struct ShadeInput *shi, struct ShadeResult *shr);
+
+ListBase *get_lights(struct ShadeInput *shi);
+float lamp_get_visibility(struct LampRen *lar, const float co[3], float lv[3], float *dist);
+void lamp_get_shadow(struct LampRen *lar, ShadeInput *shi, float inp, float shadfac[4], int do_real);
+
+float fresnel_fac(const float view[3], const float vn[3], float fresnel, float fac);
+
+/* rayshade.c */
+extern void shade_ray(struct Isect *is, struct ShadeInput *shi, struct ShadeResult *shr);
diff --git a/source/blender/render/intern/include/strand.h b/source/blender/render/intern/include/strand.h
new file mode 100644
index 00000000000..f4e22c78b42
--- /dev/null
+++ b/source/blender/render/intern/include/strand.h
@@ -0,0 +1,99 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Brecht Van Lommel.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/strand.h
+ * \ingroup render
+ */
+
+
+#ifndef __STRAND_H__
+#define __STRAND_H__
+
+struct StrandVert;
+struct StrandRen;
+struct StrandBuffer;
+struct ShadeSample;
+struct StrandPart;
+struct Render;
+struct ZSpan;
+struct ObjectInstanceRen;
+struct StrandSurface;
+struct DerivedMesh;
+struct ObjectRen;
+
+typedef struct StrandPoint {
+ /* position within segment */
+ float t;
+
+ /* camera space */
+ float co[3];
+ float nor[3];
+ float tan[3];
+ float strandco;
+ float width;
+
+ /* derivatives */
+ float dtco[3], dsco[3];
+ float dtstrandco;
+
+ /* outer points */
+ float co1[3], co2[3];
+ float hoco1[4], hoco2[4];
+ float zco1[3], zco2[3];
+ int clip1, clip2;
+
+ /* screen space */
+ float hoco[4];
+ float x, y;
+
+ /* simplification */
+ float alpha;
+} StrandPoint;
+
+typedef struct StrandSegment {
+ struct StrandVert *v[4];
+ struct StrandRen *strand;
+ struct StrandBuffer *buffer;
+ struct ObjectInstanceRen *obi;
+ float sqadaptcos;
+
+ StrandPoint point1, point2;
+ int shaded;
+} StrandSegment;
+
+struct StrandShadeCache;
+typedef struct StrandShadeCache StrandShadeCache;
+
+void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint);
+void render_strand_segment(struct Render *re, float winmat[4][4], struct StrandPart *spart, struct ZSpan *zspan, int totzspan, StrandSegment *sseg);
+void strand_minmax(struct StrandRen *strand, float min[3], float max[3], const float width);
+
+struct StrandSurface *cache_strand_surface(struct Render *re, struct ObjectRen *obr, struct DerivedMesh *dm, float mat[4][4], int timeoffset);
+void free_strand_surface(struct Render *re);
+
+struct StrandShadeCache *strand_shade_cache_create(void);
+void strand_shade_cache_free(struct StrandShadeCache *cache);
+void strand_shade_segment(struct Render *re, struct StrandShadeCache *cache, struct StrandSegment *sseg, struct ShadeSample *ssamp, float t, float s, int addpassflag);
+void strand_shade_unref(struct StrandShadeCache *cache, struct ObjectInstanceRen *obi, struct StrandVert *svert);
+
+#endif
+
diff --git a/source/blender/render/intern/include/sunsky.h b/source/blender/render/intern/include/sunsky.h
new file mode 100644
index 00000000000..c608f9fc48c
--- /dev/null
+++ b/source/blender/render/intern/include/sunsky.h
@@ -0,0 +1,81 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): zaghaghi
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/sunsky.h
+ * \ingroup render
+ */
+
+#ifndef __SUNSKY_H__
+#define __SUNSKY_H__
+
+// #define SPECTRUM_MAX_COMPONENTS 100
+
+typedef struct SunSky {
+ short effect_type, skyblendtype, sky_colorspace;
+ float turbidity;
+ float theta, phi;
+
+ float toSun[3];
+
+ /*float sunSpectralRaddata[SPECTRUM_MAX_COMPONENTS];*/
+ float sunSolidAngle;
+
+ float zenith_Y, zenith_x, zenith_y;
+
+ float perez_Y[5], perez_x[5], perez_y[5];
+
+ /* suggested by glome in patch [#8063] */
+ float horizon_brightness;
+ float spread;
+ float sun_brightness;
+ float sun_size;
+ float backscattered_light;
+ float skyblendfac;
+ float sky_exposure;
+
+ float atm_HGg;
+
+ float atm_SunIntensity;
+ float atm_InscatteringMultiplier;
+ float atm_ExtinctionMultiplier;
+ float atm_BetaRayMultiplier;
+ float atm_BetaMieMultiplier;
+ float atm_DistanceMultiplier;
+
+ float atm_BetaRay[3];
+ float atm_BetaDashRay[3];
+ float atm_BetaMie[3];
+ float atm_BetaDashMie[3];
+ float atm_BetaRM[3];
+} SunSky;
+
+void InitSunSky(struct SunSky *sunsky, float turb, const float toSun[3], float horizon_brightness,
+ float spread, float sun_brightness, float sun_size, float back_scatter,
+ float skyblendfac, short skyblendtype, float sky_exposure, float sky_colorspace);
+
+void GetSkyXYZRadiance(struct SunSky *sunsky, float theta, float phi, float color_out[3]);
+void GetSkyXYZRadiancef(struct SunSky *sunsky, const float varg[3], float color_out[3]);
+void InitAtmosphere(struct SunSky *sunSky, float sun_intens, float mief, float rayf, float inscattf, float extincf, float disf);
+void AtmospherePixleShader(struct SunSky *sunSky, float view[3], float s, float rgb[3]);
+void ClipColor(float c[3]);
+
+#endif /*__SUNSKY_H__*/
diff --git a/source/blender/render/intern/include/texture_ocean.h b/source/blender/render/intern/include/texture_ocean.h
new file mode 100644
index 00000000000..6d7bc6fe7b0
--- /dev/null
+++ b/source/blender/render/intern/include/texture_ocean.h
@@ -0,0 +1,35 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributors: Matt Ebb
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __TEXTURE_OCEAN_H__
+#define __TEXTURE_OCEAN_H__
+
+/** \file blender/render/intern/include/texture_ocean.h
+ * \ingroup render
+ */
+
+int ocean_texture(struct Tex *tex, const float texvec[2], struct TexResult *texres);
+
+#endif /* __TEXTURE_OCEAN_H__ */
diff --git a/source/blender/render/intern/include/voxeldata.h b/source/blender/render/intern/include/voxeldata.h
new file mode 100644
index 00000000000..041ca78a799
--- /dev/null
+++ b/source/blender/render/intern/include/voxeldata.h
@@ -0,0 +1,47 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Raul Fernandez Hernandez (Farsthary), Matt Ebb.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/include/voxeldata.h
+ * \ingroup render
+ */
+
+#ifndef __VOXELDATA_H__
+#define __VOXELDATA_H__
+
+struct Render;
+struct TexResult;
+
+typedef struct VoxelDataHeader {
+ int resolX, resolY, resolZ;
+ int frames;
+} VoxelDataHeader;
+
+void cache_voxeldata(Tex *tex, int scene_frame);
+void make_voxeldata(struct Render *re);
+int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texres);
+
+#endif /* __VOXELDATA_H__ */
diff --git a/source/blender/render/intern/include/zbuf.h b/source/blender/render/intern/include/zbuf.h
index 3dfcbc355c4..0654a4f8df6 100644
--- a/source/blender/render/intern/include/zbuf.h
+++ b/source/blender/render/intern/include/zbuf.h
@@ -36,7 +36,7 @@
/* span fill in method, is also used to localize data for zbuffering */
typedef struct ZSpan {
int rectx, recty; /* range for clipping */
-
+
int miny1, maxy1, miny2, maxy2; /* actual filled in range */
const float *minp1, *maxp1, *minp2, *maxp2; /* vertex pointers detect min/max range in */
float *span1, *span2;
diff --git a/source/blender/render/intern/raytrace/bvh.h b/source/blender/render/intern/raytrace/bvh.h
new file mode 100644
index 00000000000..0f9a506762b
--- /dev/null
+++ b/source/blender/render/intern/raytrace/bvh.h
@@ -0,0 +1,407 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/bvh.h
+ * \ingroup render
+ */
+
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "raycounter.h"
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "rayobject_hint.h"
+#include "rayobject_rtbuild.h"
+
+#include <assert.h>
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
+
+#ifndef __BVH_H__
+#define __BVH_H__
+
+#ifdef __SSE__
+inline int test_bb_group4(__m128 *bb_group, const Isect *isec)
+{
+ const __m128 tmin0 = _mm_setzero_ps();
+ const __m128 tmax0 = _mm_set_ps1(isec->dist);
+
+ float start[3], idot_axis[3];
+ copy_v3_v3(start, isec->start);
+ copy_v3_v3(idot_axis, isec->idot_axis);
+
+ const __m128 tmin1 = _mm_max_ps(tmin0, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[0]], _mm_set_ps1(start[0]) ), _mm_set_ps1(idot_axis[0])) );
+ const __m128 tmax1 = _mm_min_ps(tmax0, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[1]], _mm_set_ps1(start[0]) ), _mm_set_ps1(idot_axis[0])) );
+ const __m128 tmin2 = _mm_max_ps(tmin1, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[2]], _mm_set_ps1(start[1]) ), _mm_set_ps1(idot_axis[1])) );
+ const __m128 tmax2 = _mm_min_ps(tmax1, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[3]], _mm_set_ps1(start[1]) ), _mm_set_ps1(idot_axis[1])) );
+ const __m128 tmin3 = _mm_max_ps(tmin2, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[4]], _mm_set_ps1(start[2]) ), _mm_set_ps1(idot_axis[2])) );
+ const __m128 tmax3 = _mm_min_ps(tmax2, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[5]], _mm_set_ps1(start[2]) ), _mm_set_ps1(idot_axis[2])) );
+
+ return _mm_movemask_ps(_mm_cmpge_ps(tmax3, tmin3));
+}
+#endif
+
+/*
+ * Determines the distance that the ray must travel to hit the bounding volume of the given node
+ * Based on Tactical Optimization of Ray/Box Intersection, by Graham Fyffe
+ * [http://tog.acm.org/resources/RTNews/html/rtnv21n1.html#art9]
+ */
+static inline int rayobject_bb_intersect_test(const Isect *isec, const float *_bb)
+{
+ const float *bb = _bb;
+
+ float t1x = (bb[isec->bv_index[0]] - isec->start[0]) * isec->idot_axis[0];
+ float t2x = (bb[isec->bv_index[1]] - isec->start[0]) * isec->idot_axis[0];
+ float t1y = (bb[isec->bv_index[2]] - isec->start[1]) * isec->idot_axis[1];
+ float t2y = (bb[isec->bv_index[3]] - isec->start[1]) * isec->idot_axis[1];
+ float t1z = (bb[isec->bv_index[4]] - isec->start[2]) * isec->idot_axis[2];
+ float t2z = (bb[isec->bv_index[5]] - isec->start[2]) * isec->idot_axis[2];
+
+ RE_RC_COUNT(isec->raycounter->bb.test);
+
+ if (t1x > t2y || t2x < t1y || t1x > t2z || t2x < t1z || t1y > t2z || t2y < t1z) return 0;
+ if (t2x < 0.0f || t2y < 0.0f || t2z < 0.0f) return 0;
+ if (t1x > isec->dist || t1y > isec->dist || t1z > isec->dist) return 0;
+ RE_RC_COUNT(isec->raycounter->bb.hit);
+
+ return 1;
+}
+
+/* bvh tree generics */
+template<class Tree> static void bvh_add(Tree *obj, RayObject *ob)
+{
+ rtbuild_add(obj->builder, ob);
+}
+
+template<class Node>
+inline bool is_leaf(Node *node)
+{
+ return !RE_rayobject_isAligned(node);
+}
+
+template<class Tree> static void bvh_done(Tree *obj);
+
+template<class Tree>
+static void bvh_free(Tree *obj)
+{
+ if (obj->builder)
+ rtbuild_free(obj->builder);
+
+ if (obj->node_arena)
+ BLI_memarena_free(obj->node_arena);
+
+ MEM_freeN(obj);
+}
+
+template<class Tree>
+static void bvh_bb(Tree *obj, float *min, float *max)
+{
+ if (obj->root)
+ bvh_node_merge_bb(obj->root, min, max);
+}
+
+
+template<class Tree>
+static float bvh_cost(Tree *obj)
+{
+ assert(obj->cost >= 0.0f);
+ return obj->cost;
+}
+
+
+
+/* bvh tree nodes generics */
+template<class Node> static inline int bvh_node_hit_test(Node *node, Isect *isec)
+{
+ return rayobject_bb_intersect_test(isec, (const float *)node->bb);
+}
+
+
+template<class Node>
+static inline void bvh_node_merge_bb(Node *node, float min[3], float max[3])
+{
+ if (is_leaf(node)) {
+ RE_rayobject_merge_bb((RayObject *)node, min, max);
+ }
+ else {
+ DO_MIN(node->bb, min);
+ DO_MAX(node->bb + 3, max);
+ }
+}
+
+
+
+/*
+ * recursively transverse a BVH looking for a rayhit using a local stack
+ */
+template<class Node> static inline void bvh_node_push_childs(Node *node, Isect *isec, Node **stack, int &stack_pos);
+
+template<class Node, int MAX_STACK_SIZE, bool TEST_ROOT, bool SHADOW>
+static int bvh_node_stack_raycast(Node *root, Isect *isec)
+{
+ Node *stack[MAX_STACK_SIZE];
+ int hit = 0, stack_pos = 0;
+
+ if (!TEST_ROOT && !is_leaf(root))
+ bvh_node_push_childs(root, isec, stack, stack_pos);
+ else
+ stack[stack_pos++] = root;
+
+ while (stack_pos) {
+ Node *node = stack[--stack_pos];
+ if (!is_leaf(node)) {
+ if (bvh_node_hit_test(node, isec)) {
+ bvh_node_push_childs(node, isec, stack, stack_pos);
+ assert(stack_pos <= MAX_STACK_SIZE);
+ }
+ }
+ else {
+ hit |= RE_rayobject_intersect( (RayObject *)node, isec);
+ if (SHADOW && hit) return hit;
+ }
+ }
+ return hit;
+}
+
+
+#ifdef __SSE__
+/*
+ * Generic SIMD bvh recursion
+ * this was created to be able to use any simd (with the cost of some memmoves)
+ * it can take advantage of any SIMD width and doens't needs any special tree care
+ */
+template<class Node, int MAX_STACK_SIZE, bool TEST_ROOT>
+static int bvh_node_stack_raycast_simd(Node *root, Isect *isec)
+{
+ Node *stack[MAX_STACK_SIZE];
+
+ int hit = 0, stack_pos = 0;
+
+ if (!TEST_ROOT) {
+ if (!is_leaf(root)) {
+ if (!is_leaf(root->child))
+ bvh_node_push_childs(root, isec, stack, stack_pos);
+ else
+ return RE_rayobject_intersect( (RayObject *)root->child, isec);
+ }
+ else
+ return RE_rayobject_intersect( (RayObject *)root, isec);
+ }
+ else {
+ if (!is_leaf(root))
+ stack[stack_pos++] = root;
+ else
+ return RE_rayobject_intersect( (RayObject *)root, isec);
+ }
+
+ while (true) {
+ //Use SIMD 4
+ if (stack_pos >= 4) {
+ __m128 t_bb[6];
+ Node *t_node[4];
+
+ stack_pos -= 4;
+
+ /* prepare the 4BB for SIMD */
+ t_node[0] = stack[stack_pos + 0]->child;
+ t_node[1] = stack[stack_pos + 1]->child;
+ t_node[2] = stack[stack_pos + 2]->child;
+ t_node[3] = stack[stack_pos + 3]->child;
+
+ const float *bb0 = stack[stack_pos + 0]->bb;
+ const float *bb1 = stack[stack_pos + 1]->bb;
+ const float *bb2 = stack[stack_pos + 2]->bb;
+ const float *bb3 = stack[stack_pos + 3]->bb;
+
+ const __m128 x0y0x1y1 = _mm_shuffle_ps(_mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(1, 0, 1, 0) );
+ const __m128 x2y2x3y3 = _mm_shuffle_ps(_mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(1, 0, 1, 0) );
+ t_bb[0] = _mm_shuffle_ps(x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(2, 0, 2, 0) );
+ t_bb[1] = _mm_shuffle_ps(x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(3, 1, 3, 1) );
+
+ const __m128 z0X0z1X1 = _mm_shuffle_ps(_mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(3, 2, 3, 2) );
+ const __m128 z2X2z3X3 = _mm_shuffle_ps(_mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(3, 2, 3, 2) );
+ t_bb[2] = _mm_shuffle_ps(z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(2, 0, 2, 0) );
+ t_bb[3] = _mm_shuffle_ps(z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(3, 1, 3, 1) );
+
+ const __m128 Y0Z0Y1Z1 = _mm_shuffle_ps(_mm_load_ps(bb0 + 4), _mm_load_ps(bb1 + 4), _MM_SHUFFLE(1, 0, 1, 0) );
+ const __m128 Y2Z2Y3Z3 = _mm_shuffle_ps(_mm_load_ps(bb2 + 4), _mm_load_ps(bb3 + 4), _MM_SHUFFLE(1, 0, 1, 0) );
+ t_bb[4] = _mm_shuffle_ps(Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(2, 0, 2, 0) );
+ t_bb[5] = _mm_shuffle_ps(Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(3, 1, 3, 1) );
+#if 0
+ for (int i = 0; i < 4; i++)
+ {
+ Node *t = stack[stack_pos + i];
+ assert(!is_leaf(t));
+
+ float *bb = ((float *)t_bb) + i;
+ bb[4 * 0] = t->bb[0];
+ bb[4 * 1] = t->bb[1];
+ bb[4 * 2] = t->bb[2];
+ bb[4 * 3] = t->bb[3];
+ bb[4 * 4] = t->bb[4];
+ bb[4 * 5] = t->bb[5];
+ t_node[i] = t->child;
+ }
+#endif
+ RE_RC_COUNT(isec->raycounter->simd_bb.test);
+ int res = test_bb_group4(t_bb, isec);
+
+ for (int i = 0; i < 4; i++)
+ if (res & (1 << i)) {
+ RE_RC_COUNT(isec->raycounter->simd_bb.hit);
+ if (!is_leaf(t_node[i])) {
+ for (Node *t = t_node[i]; t; t = t->sibling) {
+ assert(stack_pos < MAX_STACK_SIZE);
+ stack[stack_pos++] = t;
+ }
+ }
+ else {
+ hit |= RE_rayobject_intersect( (RayObject *)t_node[i], isec);
+ if (hit && isec->mode == RE_RAY_SHADOW) return hit;
+ }
+ }
+ }
+ else if (stack_pos > 0) {
+ Node *node = stack[--stack_pos];
+ assert(!is_leaf(node));
+
+ if (bvh_node_hit_test(node, isec)) {
+ if (!is_leaf(node->child)) {
+ bvh_node_push_childs(node, isec, stack, stack_pos);
+ assert(stack_pos <= MAX_STACK_SIZE);
+ }
+ else {
+ hit |= RE_rayobject_intersect( (RayObject *)node->child, isec);
+ if (hit && isec->mode == RE_RAY_SHADOW) return hit;
+ }
+ }
+ }
+ else break;
+ }
+ return hit;
+}
+#endif
+
+/*
+ * recursively transverse a BVH looking for a rayhit using system stack
+ */
+#if 0
+template<class Node>
+static int bvh_node_raycast(Node *node, Isect *isec)
+{
+ int hit = 0;
+ if (bvh_test_node(node, isec))
+ {
+ if (isec->idot_axis[node->split_axis] > 0.0f)
+ {
+ int i;
+ for (i = 0; i < BVH_NCHILDS; i++)
+ if (!is_leaf(node->child[i]))
+ {
+ if (node->child[i] == 0) break;
+
+ hit |= bvh_node_raycast(node->child[i], isec);
+ if (hit && isec->mode == RE_RAY_SHADOW) return hit;
+ }
+ else {
+ hit |= RE_rayobject_intersect( (RayObject *)node->child[i], isec);
+ if (hit && isec->mode == RE_RAY_SHADOW) return hit;
+ }
+ }
+ else {
+ int i;
+ for (i = BVH_NCHILDS - 1; i >= 0; i--)
+ if (!is_leaf(node->child[i]))
+ {
+ if (node->child[i])
+ {
+ hit |= dfs_raycast(node->child[i], isec);
+ if (hit && isec->mode == RE_RAY_SHADOW) return hit;
+ }
+ }
+ else {
+ hit |= RE_rayobject_intersect( (RayObject *)node->child[i], isec);
+ if (hit && isec->mode == RE_RAY_SHADOW) return hit;
+ }
+ }
+ }
+ return hit;
+}
+#endif
+
+template<class Node, class HintObject>
+static void bvh_dfs_make_hint(Node *node, LCTSHint *hint, int reserve_space, HintObject *hintObject)
+{
+ assert(hint->size + reserve_space + 1 <= RE_RAY_LCTS_MAX_SIZE);
+
+ if (is_leaf(node)) {
+ hint->stack[hint->size++] = (RayObject *)node;
+ }
+ else {
+ int childs = count_childs(node);
+ if (hint->size + reserve_space + childs <= RE_RAY_LCTS_MAX_SIZE) {
+ int result = hint_test_bb(hintObject, node->bb, node->bb + 3);
+ if (result == HINT_RECURSE) {
+ /* We are 100% sure the ray will be pass inside this node */
+ bvh_dfs_make_hint_push_siblings(node->child, hint, reserve_space, hintObject);
+ }
+ else if (result == HINT_ACCEPT) {
+ hint->stack[hint->size++] = (RayObject *)node;
+ }
+ }
+ else {
+ hint->stack[hint->size++] = (RayObject *)node;
+ }
+ }
+}
+
+
+template<class Tree>
+static RayObjectAPI *bvh_get_api(int maxstacksize);
+
+
+template<class Tree, int DFS_STACK_SIZE>
+static inline RayObject *bvh_create_tree(int size)
+{
+ Tree *obj = (Tree *)MEM_callocN(sizeof(Tree), "BVHTree");
+ assert(RE_rayobject_isAligned(obj)); /* RayObject API assumes real data to be 4-byte aligned */
+
+ obj->rayobj.api = bvh_get_api<Tree>(DFS_STACK_SIZE);
+ obj->root = NULL;
+
+ obj->node_arena = NULL;
+ obj->builder = rtbuild_create(size);
+
+ return RE_rayobject_unalignRayAPI((RayObject *) obj);
+}
+
+#endif
diff --git a/source/blender/render/intern/raytrace/rayobject.cpp b/source/blender/render/intern/raytrace/rayobject.cpp
new file mode 100644
index 00000000000..fee877b311d
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject.cpp
@@ -0,0 +1,534 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject.cpp
+ * \ingroup render
+ */
+
+
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_material_types.h"
+
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "raycounter.h"
+#include "render_types.h"
+#include "renderdatabase.h"
+
+/* RayFace
+ *
+ * note we force always inline here, because compiler refuses to otherwise
+ * because function is too long. Since this is code that is called billions
+ * of times we really do want to inline. */
+
+MALWAYS_INLINE RayObject *rayface_from_coords(RayFace *rayface, void *ob, void *face,
+ float *v1, float *v2, float *v3, float *v4)
+{
+ rayface->ob = ob;
+ rayface->face = face;
+
+ copy_v3_v3(rayface->v1, v1);
+ copy_v3_v3(rayface->v2, v2);
+ copy_v3_v3(rayface->v3, v3);
+
+ if (v4) {
+ copy_v3_v3(rayface->v4, v4);
+ rayface->quad = 1;
+ }
+ else {
+ rayface->quad = 0;
+ }
+
+ return RE_rayobject_unalignRayFace(rayface);
+}
+
+MALWAYS_INLINE void rayface_from_vlak(RayFace *rayface, ObjectInstanceRen *obi, VlakRen *vlr)
+{
+ rayface_from_coords(rayface, obi, vlr, vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->v4 ? vlr->v4->co : NULL);
+
+ if (obi->transform_primitives) {
+ mul_m4_v3(obi->mat, rayface->v1);
+ mul_m4_v3(obi->mat, rayface->v2);
+ mul_m4_v3(obi->mat, rayface->v3);
+
+ if (RE_rayface_isQuad(rayface))
+ mul_m4_v3(obi->mat, rayface->v4);
+ }
+}
+
+RayObject *RE_rayface_from_vlak(RayFace *rayface, ObjectInstanceRen *obi, VlakRen *vlr)
+{
+ return rayface_from_coords(rayface, obi, vlr, vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->v4 ? vlr->v4->co : NULL);
+}
+
+RayObject *RE_rayface_from_coords(RayFace *rayface, void *ob, void *face, float *v1, float *v2, float *v3, float *v4)
+{
+ return rayface_from_coords(rayface, ob, face, v1, v2, v3, v4);
+}
+
+/* VlakPrimitive */
+
+RayObject *RE_vlakprimitive_from_vlak(VlakPrimitive *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr)
+{
+ face->ob = obi;
+ face->face = vlr;
+
+ return RE_rayobject_unalignVlakPrimitive(face);
+}
+
+/* Checks for ignoring faces or materials */
+
+MALWAYS_INLINE int vlr_check_intersect(Isect *is, ObjectInstanceRen *obi, VlakRen *vlr)
+{
+ /* for baking selected to active non-traceable materials might still
+ * be in the raytree */
+ if (!(vlr->flag & R_TRACEBLE))
+ return 0;
+
+ /* I know... cpu cycle waste, might do smarter once */
+ if (is->mode == RE_RAY_MIRROR)
+ return !(vlr->mat->mode & MA_ONLYCAST);
+ else
+ return (vlr->mat->mode2 & MA_CASTSHADOW) && (is->lay & obi->lay);
+}
+
+MALWAYS_INLINE int vlr_check_intersect_solid(Isect *UNUSED(is), ObjectInstanceRen *UNUSED(obi), VlakRen *vlr)
+{
+ /* solid material types only */
+ if (vlr->mat->material_type == MA_TYPE_SURFACE)
+ return 1;
+ else
+ return 0;
+}
+
+MALWAYS_INLINE int vlr_check_bake(Isect *is, ObjectInstanceRen *obi, VlakRen *UNUSED(vlr))
+{
+ return (obi->obr->ob != is->userdata) && (obi->obr->ob->flag & SELECT);
+}
+
+/* Ray Triangle/Quad Intersection */
+
+static bool isect_ray_tri_watertight_no_sign_check_v3(
+ const float ray_origin[3], const struct IsectRayPrecalc *isect_precalc,
+ const float v0[3], const float v1[3], const float v2[3],
+ float *r_lambda, float r_uv[2])
+{
+ const int kx = isect_precalc->kx;
+ const int ky = isect_precalc->ky;
+ const int kz = isect_precalc->kz;
+ const float sx = isect_precalc->sx;
+ const float sy = isect_precalc->sy;
+ const float sz = isect_precalc->sz;
+
+ /* Calculate vertices relative to ray origin. */
+ const float a[3] = {v0[0] - ray_origin[0], v0[1] - ray_origin[1], v0[2] - ray_origin[2]};
+ const float b[3] = {v1[0] - ray_origin[0], v1[1] - ray_origin[1], v1[2] - ray_origin[2]};
+ const float c[3] = {v2[0] - ray_origin[0], v2[1] - ray_origin[1], v2[2] - ray_origin[2]};
+
+ const float a_kx = a[kx], a_ky = a[ky], a_kz = a[kz];
+ const float b_kx = b[kx], b_ky = b[ky], b_kz = b[kz];
+ const float c_kx = c[kx], c_ky = c[ky], c_kz = c[kz];
+
+ /* Perform shear and scale of vertices. */
+ const float ax = a_kx - sx * a_kz;
+ const float ay = a_ky - sy * a_kz;
+ const float bx = b_kx - sx * b_kz;
+ const float by = b_ky - sy * b_kz;
+ const float cx = c_kx - sx * c_kz;
+ const float cy = c_ky - sy * c_kz;
+
+ /* Calculate scaled barycentric coordinates. */
+ const float u = cx * by - cy * bx;
+ const float v = ax * cy - ay * cx;
+ const float w = bx * ay - by * ax;
+ float det;
+
+ if ((u < 0.0f || v < 0.0f || w < 0.0f) &&
+ (u > 0.0f || v > 0.0f || w > 0.0f))
+ {
+ return false;
+ }
+
+ /* Calculate determinant. */
+ det = u + v + w;
+ if (UNLIKELY(det == 0.0f)) {
+ return false;
+ }
+ else {
+ /* Calculate scaled z-coordinates of vertices and use them to calculate
+ * the hit distance.
+ */
+ const float t = (u * a_kz + v * b_kz + w * c_kz) * sz;
+ /* Normalize u, v and t. */
+ const float inv_det = 1.0f / det;
+ if (r_uv) {
+ r_uv[0] = u * inv_det;
+ r_uv[1] = v * inv_det;
+ }
+ *r_lambda = t * inv_det;
+ return true;
+ }
+}
+
+MALWAYS_INLINE int isec_tri_quad(const float start[3],
+ const struct IsectRayPrecalc *isect_precalc,
+ const RayFace *face,
+ float r_uv[2], float *r_lambda)
+{
+ float uv[2], l;
+
+ if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v2, face->v3, &l, uv)) {
+ /* check if intersection is within ray length */
+ if (l > -RE_RAYTRACE_EPSILON && l < *r_lambda) {
+ r_uv[0] = -uv[0];
+ r_uv[1] = -uv[1];
+ *r_lambda = l;
+ return 1;
+ }
+ }
+
+ /* intersect second triangle in quad */
+ if (RE_rayface_isQuad(face)) {
+ if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v3, face->v4, &l, uv)) {
+ /* check if intersection is within ray length */
+ if (l > -RE_RAYTRACE_EPSILON && l < *r_lambda) {
+ r_uv[0] = -uv[0];
+ r_uv[1] = -uv[1];
+ *r_lambda = l;
+ return 2;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Simpler yes/no Ray Triangle/Quad Intersection */
+
+MALWAYS_INLINE int isec_tri_quad_neighbour(const float start[3],
+ const float dir[3],
+ const RayFace *face)
+{
+ float r[3];
+ struct IsectRayPrecalc isect_precalc;
+ float uv[2], l;
+
+ negate_v3_v3(r, dir); /* note, different than above function */
+
+ isect_ray_tri_watertight_v3_precalc(&isect_precalc, r);
+
+ if (isect_ray_tri_watertight_no_sign_check_v3(start, &isect_precalc, face->v1, face->v2, face->v3, &l, uv)) {
+ return 1;
+ }
+
+ /* intersect second triangle in quad */
+ if (RE_rayface_isQuad(face)) {
+ if (isect_ray_tri_watertight_no_sign_check_v3(start, &isect_precalc, face->v1, face->v3, face->v4, &l, uv)) {
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+/* RayFace intersection with checks and neighbor verifaction included,
+ * Isect is modified if the face is hit. */
+
+MALWAYS_INLINE int intersect_rayface(RayObject *hit_obj, RayFace *face, Isect *is)
+{
+ float dist, uv[2];
+ int ok = 0;
+
+ /* avoid self-intersection */
+ if (is->orig.ob == face->ob && is->orig.face == face->face)
+ return 0;
+
+ /* check if we should intersect this face */
+ if (is->check == RE_CHECK_VLR_RENDER) {
+ if (vlr_check_intersect(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
+ return 0;
+ }
+ else if (is->check == RE_CHECK_VLR_NON_SOLID_MATERIAL) {
+ if (vlr_check_intersect(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
+ return 0;
+ if (vlr_check_intersect_solid(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
+ return 0;
+ }
+ else if (is->check == RE_CHECK_VLR_BAKE) {
+ if (vlr_check_bake(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
+ return 0;
+ }
+
+ /* ray counter */
+ RE_RC_COUNT(is->raycounter->faces.test);
+
+ dist = is->dist;
+ ok = isec_tri_quad(is->start, &is->isect_precalc, face, uv, &dist);
+
+ if (ok) {
+
+ /* when a shadow ray leaves a face, it can be little outside the edges
+ * of it, causing intersection to be detected in its neighbor face */
+ if (is->skip & RE_SKIP_VLR_NEIGHBOUR) {
+ if (dist < 0.1f && is->orig.ob == face->ob) {
+ VlakRen *a = (VlakRen *)is->orig.face;
+ VlakRen *b = (VlakRen *)face->face;
+ ObjectRen *obr = ((ObjectInstanceRen *)face->ob)->obr;
+
+ VertRen **va, **vb;
+ int *org_idx_a, *org_idx_b;
+ int i, j;
+ bool is_neighbor = false;
+
+ /* "same" vertex means either the actual same VertRen, or the same 'final org index', if available
+ * (autosmooth only, currently). */
+ for (i = 0, va = &a->v1; !is_neighbor && i < 4 && *va; ++i, ++va) {
+ org_idx_a = RE_vertren_get_origindex(obr, *va, false);
+ for (j = 0, vb = &b->v1; !is_neighbor && j < 4 && *vb; ++j, ++vb) {
+ if (*va == *vb) {
+ is_neighbor = true;
+ }
+ else if (org_idx_a) {
+ org_idx_b = RE_vertren_get_origindex(obr, *vb, 0);
+ if (org_idx_b && *org_idx_a == *org_idx_b) {
+ is_neighbor = true;
+ }
+ }
+ }
+ }
+
+ /* So there's a shared edge or vertex, let's intersect ray with self, if that's true
+ * we can safely return 1, otherwise we assume the intersection is invalid, 0 */
+ if (is_neighbor) {
+ /* create RayFace from original face, transformed if necessary */
+ RayFace origface;
+ ObjectInstanceRen *ob = (ObjectInstanceRen *)is->orig.ob;
+ rayface_from_vlak(&origface, ob, (VlakRen *)is->orig.face);
+
+ if (!isec_tri_quad_neighbour(is->start, is->dir, &origface)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ RE_RC_COUNT(is->raycounter->faces.hit);
+
+ is->isect = ok; // which half of the quad
+ is->dist = dist;
+ is->u = uv[0]; is->v = uv[1];
+
+ is->hit.ob = face->ob;
+ is->hit.face = face->face;
+#ifdef RT_USE_LAST_HIT
+ is->last_hit = hit_obj;
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Intersection */
+
+int RE_rayobject_raycast(RayObject *r, Isect *isec)
+{
+ int i;
+
+ /* Pre-calculate orientation for watertight intersection checks. */
+ isect_ray_tri_watertight_v3_precalc(&isec->isect_precalc, isec->dir);
+
+ RE_RC_COUNT(isec->raycounter->raycast.test);
+
+ /* setup vars used on raycast */
+ for (i = 0; i < 3; i++) {
+ isec->idot_axis[i] = 1.0f / isec->dir[i];
+
+ isec->bv_index[2 * i] = isec->idot_axis[i] < 0.0f ? 1 : 0;
+ isec->bv_index[2 * i + 1] = 1 - isec->bv_index[2 * i];
+
+ isec->bv_index[2 * i] = i + 3 * isec->bv_index[2 * i];
+ isec->bv_index[2 * i + 1] = i + 3 * isec->bv_index[2 * i + 1];
+ }
+
+#ifdef RT_USE_LAST_HIT
+ /* last hit heuristic */
+ if (isec->mode == RE_RAY_SHADOW && isec->last_hit) {
+ RE_RC_COUNT(isec->raycounter->rayshadow_last_hit.test);
+
+ if (RE_rayobject_intersect(isec->last_hit, isec)) {
+ RE_RC_COUNT(isec->raycounter->raycast.hit);
+ RE_RC_COUNT(isec->raycounter->rayshadow_last_hit.hit);
+ return 1;
+ }
+ }
+#endif
+
+#ifdef RT_USE_HINT
+ isec->hit_hint = 0;
+#endif
+
+ if (RE_rayobject_intersect(r, isec)) {
+ RE_RC_COUNT(isec->raycounter->raycast.hit);
+
+#ifdef RT_USE_HINT
+ isec->hint = isec->hit_hint;
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+int RE_rayobject_intersect(RayObject *r, Isect *i)
+{
+ if (RE_rayobject_isRayFace(r)) {
+ return intersect_rayface(r, (RayFace *) RE_rayobject_align(r), i);
+ }
+ else if (RE_rayobject_isVlakPrimitive(r)) {
+ //TODO optimize (useless copy to RayFace to avoid duplicate code)
+ VlakPrimitive *face = (VlakPrimitive *) RE_rayobject_align(r);
+ RayFace nface;
+ rayface_from_vlak(&nface, face->ob, face->face);
+
+ return intersect_rayface(r, &nface, i);
+ }
+ else if (RE_rayobject_isRayAPI(r)) {
+ r = RE_rayobject_align(r);
+ return r->api->raycast(r, i);
+ }
+ else {
+ assert(0);
+ return 0;
+ }
+}
+
+/* Building */
+
+void RE_rayobject_add(RayObject *r, RayObject *o)
+{
+ r = RE_rayobject_align(r);
+ return r->api->add(r, o);
+}
+
+void RE_rayobject_done(RayObject *r)
+{
+ r = RE_rayobject_align(r);
+ r->api->done(r);
+}
+
+void RE_rayobject_free(RayObject *r)
+{
+ r = RE_rayobject_align(r);
+ r->api->free(r);
+}
+
+float RE_rayobject_cost(RayObject *r)
+{
+ if (RE_rayobject_isRayFace(r) || RE_rayobject_isVlakPrimitive(r)) {
+ return 1.0f;
+ }
+ else if (RE_rayobject_isRayAPI(r)) {
+ r = RE_rayobject_align(r);
+ return r->api->cost(r);
+ }
+ else {
+ assert(0);
+ return 1.0f;
+ }
+}
+
+/* Bounding Boxes */
+
+void RE_rayobject_merge_bb(RayObject *r, float min[3], float max[3])
+{
+ if (RE_rayobject_isRayFace(r)) {
+ RayFace *face = (RayFace *) RE_rayobject_align(r);
+
+ DO_MINMAX(face->v1, min, max);
+ DO_MINMAX(face->v2, min, max);
+ DO_MINMAX(face->v3, min, max);
+ if (RE_rayface_isQuad(face)) DO_MINMAX(face->v4, min, max);
+ }
+ else if (RE_rayobject_isVlakPrimitive(r)) {
+ VlakPrimitive *face = (VlakPrimitive *) RE_rayobject_align(r);
+ RayFace nface;
+ rayface_from_vlak(&nface, face->ob, face->face);
+
+ DO_MINMAX(nface.v1, min, max);
+ DO_MINMAX(nface.v2, min, max);
+ DO_MINMAX(nface.v3, min, max);
+ if (RE_rayface_isQuad(&nface)) DO_MINMAX(nface.v4, min, max);
+ }
+ else if (RE_rayobject_isRayAPI(r)) {
+ r = RE_rayobject_align(r);
+ r->api->bb(r, min, max);
+ }
+ else
+ assert(0);
+}
+
+/* Hints */
+
+void RE_rayobject_hint_bb(RayObject *r, RayHint *hint, float *min, float *max)
+{
+ if (RE_rayobject_isRayFace(r) || RE_rayobject_isVlakPrimitive(r)) {
+ return;
+ }
+ else if (RE_rayobject_isRayAPI(r)) {
+ r = RE_rayobject_align(r);
+ return r->api->hint_bb(r, hint, min, max);
+ }
+ else
+ assert(0);
+}
+
+/* RayObjectControl */
+
+int RE_rayobjectcontrol_test_break(RayObjectControl *control)
+{
+ if (control->test_break)
+ return control->test_break(control->data);
+
+ return 0;
+}
+
+void RE_rayobject_set_control(RayObject *r, void *data, RE_rayobjectcontrol_test_break_callback test_break)
+{
+ if (RE_rayobject_isRayAPI(r)) {
+ r = RE_rayobject_align(r);
+ r->control.data = data;
+ r->control.test_break = test_break;
+ }
+}
+
diff --git a/source/blender/render/intern/raytrace/rayobject_hint.h b/source/blender/render/intern/raytrace/rayobject_hint.h
new file mode 100644
index 00000000000..88a32819bd2
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_hint.h
@@ -0,0 +1,72 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_hint.h
+ * \ingroup render
+ */
+
+
+#ifndef __RAYOBJECT_HINT_H__
+#define __RAYOBJECT_HINT_H__
+
+#define HINT_RECURSE 1
+#define HINT_ACCEPT 0
+#define HINT_DISCARD -1
+
+struct HintBB {
+ float bb[6];
+};
+
+inline int hint_test_bb(HintBB *obj, float *Nmin, float *Nmax)
+{
+ if (bb_fits_inside(Nmin, Nmax, obj->bb, obj->bb + 3) )
+ return HINT_RECURSE;
+ else
+ return HINT_ACCEPT;
+}
+#if 0
+struct HintFrustum {
+ float co[3];
+ float no[4][3];
+};
+
+inline int hint_test_bb(HintFrustum &obj, float *Nmin, float *Nmax)
+{
+ //if frustum inside BB
+ {
+ return HINT_RECURSE;
+ }
+ //if BB outside frustum
+ {
+ return HINT_DISCARD;
+ }
+
+ return HINT_ACCEPT;
+}
+#endif
+
+#endif /* __RAYOBJECT_HINT_H__ */
diff --git a/source/blender/render/intern/raytrace/rayobject_instance.cpp b/source/blender/render/intern/raytrace/rayobject_instance.cpp
new file mode 100644
index 00000000000..361e7963d96
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_instance.cpp
@@ -0,0 +1,211 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_instance.cpp
+ * \ingroup render
+ */
+
+
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "rayintersection.h"
+#include "rayobject.h"
+
+#define RE_COST_INSTANCE (1.0f)
+
+static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec);
+static void RE_rayobject_instance_free(RayObject *o);
+static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max);
+static float RE_rayobject_instance_cost(RayObject *o);
+
+static void RE_rayobject_instance_hint_bb(RayObject *UNUSED(o), RayHint *UNUSED(hint),
+ float *UNUSED(min), float *UNUSED(max))
+{}
+
+static RayObjectAPI instance_api =
+{
+ RE_rayobject_instance_intersect,
+ NULL, //static void RE_rayobject_instance_add(RayObject *o, RayObject *ob);
+ NULL, //static void RE_rayobject_instance_done(RayObject *o);
+ RE_rayobject_instance_free,
+ RE_rayobject_instance_bb,
+ RE_rayobject_instance_cost,
+ RE_rayobject_instance_hint_bb
+};
+
+typedef struct InstanceRayObject {
+ RayObject rayobj;
+ RayObject *target;
+
+ void *ob; //Object represented by this instance
+ void *target_ob; //Object represented by the inner RayObject, needed to handle self-intersection
+
+ float global2target[4][4];
+ float target2global[4][4];
+
+} InstanceRayObject;
+
+
+RayObject *RE_rayobject_instance_create(RayObject *target, float transform[4][4], void *ob, void *target_ob)
+{
+ InstanceRayObject *obj = (InstanceRayObject *)MEM_callocN(sizeof(InstanceRayObject), "InstanceRayObject");
+ assert(RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */
+
+ obj->rayobj.api = &instance_api;
+ obj->target = target;
+ obj->ob = ob;
+ obj->target_ob = target_ob;
+
+ copy_m4_m4(obj->target2global, transform);
+ invert_m4_m4(obj->global2target, obj->target2global);
+
+ return RE_rayobject_unalignRayAPI((RayObject *) obj);
+}
+
+static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec)
+{
+ InstanceRayObject *obj = (InstanceRayObject *)o;
+ float start[3], dir[3], idot_axis[3], dist;
+ int changed = 0, i, res;
+
+ // TODO - this is disabling self intersection on instances
+ if (isec->orig.ob == obj->ob && obj->ob) {
+ changed = 1;
+ isec->orig.ob = obj->target_ob;
+ }
+
+ // backup old values
+ copy_v3_v3(start, isec->start);
+ copy_v3_v3(dir, isec->dir);
+ copy_v3_v3(idot_axis, isec->idot_axis);
+ dist = isec->dist;
+
+ // transform to target coordinates system
+ mul_m4_v3(obj->global2target, isec->start);
+ mul_mat3_m4_v3(obj->global2target, isec->dir);
+ isec->dist *= normalize_v3(isec->dir);
+
+ // update idot_axis and bv_index
+ for (i = 0; i < 3; i++) {
+ isec->idot_axis[i] = 1.0f / isec->dir[i];
+
+ isec->bv_index[2 * i] = isec->idot_axis[i] < 0.0f ? 1 : 0;
+ isec->bv_index[2 * i + 1] = 1 - isec->bv_index[2 * i];
+
+ isec->bv_index[2 * i] = i + 3 * isec->bv_index[2 * i];
+ isec->bv_index[2 * i + 1] = i + 3 * isec->bv_index[2 * i + 1];
+ }
+
+ // Pre-calculate orientation for watertight intersection checks.
+ isect_ray_tri_watertight_v3_precalc(&isec->isect_precalc, isec->dir);
+
+ // raycast
+ res = RE_rayobject_intersect(obj->target, isec);
+
+ // map dist into original coordinate space
+ if (res == 0) {
+ isec->dist = dist;
+ }
+ else {
+ // note we don't just multiply dist, because of possible
+ // non-uniform scaling in the transform matrix
+ float vec[3];
+
+ mul_v3_v3fl(vec, isec->dir, isec->dist);
+ mul_mat3_m4_v3(obj->target2global, vec);
+
+ isec->dist = len_v3(vec);
+ isec->hit.ob = obj->ob;
+
+#ifdef RT_USE_LAST_HIT
+ // TODO support for last hit optimization in instances that can jump
+ // directly to the last hit face.
+ // For now it jumps directly to the last-hit instance root node.
+ isec->last_hit = RE_rayobject_unalignRayAPI((RayObject *) obj);
+#endif
+ }
+
+ // restore values
+ copy_v3_v3(isec->start, start);
+ copy_v3_v3(isec->dir, dir);
+ copy_v3_v3(isec->idot_axis, idot_axis);
+
+ if (changed)
+ isec->orig.ob = obj->ob;
+
+ // restore bv_index
+ for (i = 0; i < 3; i++) {
+ isec->bv_index[2 * i] = isec->idot_axis[i] < 0.0f ? 1 : 0;
+ isec->bv_index[2 * i + 1] = 1 - isec->bv_index[2 * i];
+
+ isec->bv_index[2 * i] = i + 3 * isec->bv_index[2 * i];
+ isec->bv_index[2 * i + 1] = i + 3 * isec->bv_index[2 * i + 1];
+ }
+
+ // Pre-calculate orientation for watertight intersection checks.
+ isect_ray_tri_watertight_v3_precalc(&isec->isect_precalc, isec->dir);
+
+ return res;
+}
+
+static void RE_rayobject_instance_free(RayObject *o)
+{
+ InstanceRayObject *obj = (InstanceRayObject *)o;
+ MEM_freeN(obj);
+}
+
+static float RE_rayobject_instance_cost(RayObject *o)
+{
+ InstanceRayObject *obj = (InstanceRayObject *)o;
+ return RE_rayobject_cost(obj->target) + RE_COST_INSTANCE;
+}
+
+static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max)
+{
+ //TODO:
+ // *better bb.. calculated without rotations of bb
+ // *maybe cache that better-fitted-BB at the InstanceRayObject
+ InstanceRayObject *obj = (InstanceRayObject *)o;
+
+ float m[3], M[3], t[3];
+ int i, j;
+ INIT_MINMAX(m, M);
+ RE_rayobject_merge_bb(obj->target, m, M);
+
+ //There must be a faster way than rotating all the 8 vertexs of the BB
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 3; j++) t[j] = (i & (1 << j)) ? M[j] : m[j];
+ mul_m4_v3(obj->target2global, t);
+ DO_MINMAX(t, min, max);
+ }
+}
+
diff --git a/source/blender/render/intern/raytrace/rayobject_octree.cpp b/source/blender/render/intern/raytrace/rayobject_octree.cpp
new file mode 100644
index 00000000000..4b73e64ca45
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_octree.cpp
@@ -0,0 +1,1101 @@
+/*
+ * ***** 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) 1990-1998 NeoGeo BV.
+ * All rights reserved.
+ *
+ * Contributors: 2004/2005 Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_octree.cpp
+ * \ingroup render
+ */
+
+
+/* IMPORTANT NOTE: this code must be independent of any other render code
+ * to use it outside the renderer! */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <float.h>
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_material_types.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "rayintersection.h"
+#include "rayobject.h"
+
+/* ********** structs *************** */
+#define BRANCH_ARRAY 1024
+#define NODE_ARRAY 4096
+
+typedef struct Branch {
+ struct Branch *b[8];
+} Branch;
+
+typedef struct OcVal {
+ short ocx, ocy, ocz;
+} OcVal;
+
+typedef struct Node {
+ struct RayFace *v[8];
+ struct OcVal ov[8];
+ struct Node *next;
+} Node;
+
+typedef struct Octree {
+ RayObject rayobj;
+
+ struct Branch **adrbranch;
+ struct Node **adrnode;
+ float ocsize; /* ocsize: mult factor, max size octree */
+ float ocfacx, ocfacy, ocfacz;
+ float min[3], max[3];
+ int ocres;
+ int branchcount, nodecount;
+
+ /* during building only */
+ char *ocface;
+
+ RayFace **ro_nodes;
+ int ro_nodes_size, ro_nodes_used;
+
+} Octree;
+
+static int RE_rayobject_octree_intersect(RayObject *o, Isect *isec);
+static void RE_rayobject_octree_add(RayObject *o, RayObject *ob);
+static void RE_rayobject_octree_done(RayObject *o);
+static void RE_rayobject_octree_free(RayObject *o);
+static void RE_rayobject_octree_bb(RayObject *o, float *min, float *max);
+
+/*
+ * This function is not expected to be called by current code state.
+ */
+static float RE_rayobject_octree_cost(RayObject *UNUSED(o))
+{
+ return 1.0;
+}
+
+static void RE_rayobject_octree_hint_bb(RayObject *UNUSED(o), RayHint *UNUSED(hint),
+ float *UNUSED(min), float *UNUSED(max))
+{
+ return;
+}
+
+static RayObjectAPI octree_api =
+{
+ RE_rayobject_octree_intersect,
+ RE_rayobject_octree_add,
+ RE_rayobject_octree_done,
+ RE_rayobject_octree_free,
+ RE_rayobject_octree_bb,
+ RE_rayobject_octree_cost,
+ RE_rayobject_octree_hint_bb
+};
+
+/* **************** ocval method ******************* */
+/* within one octree node, a set of 3x15 bits defines a 'boundbox' to OR with */
+
+#define OCVALRES 15
+#define BROW16(min, max) \
+ (((max) >= OCVALRES ? 0xFFFF : (1 << ((max) + 1)) - 1) - (((min) > 0) ? ((1 << (min)) - 1) : 0))
+
+static void calc_ocval_face(float *v1, float *v2, float *v3, float *v4, short x, short y, short z, OcVal *ov)
+{
+ float min[3], max[3];
+ int ocmin, ocmax;
+
+ copy_v3_v3(min, v1);
+ copy_v3_v3(max, v1);
+ DO_MINMAX(v2, min, max);
+ DO_MINMAX(v3, min, max);
+ if (v4) {
+ DO_MINMAX(v4, min, max);
+ }
+
+ ocmin = OCVALRES * (min[0] - x);
+ ocmax = OCVALRES * (max[0] - x);
+ ov->ocx = BROW16(ocmin, ocmax);
+
+ ocmin = OCVALRES * (min[1] - y);
+ ocmax = OCVALRES * (max[1] - y);
+ ov->ocy = BROW16(ocmin, ocmax);
+
+ ocmin = OCVALRES * (min[2] - z);
+ ocmax = OCVALRES * (max[2] - z);
+ ov->ocz = BROW16(ocmin, ocmax);
+
+}
+
+static void calc_ocval_ray(OcVal *ov, float xo, float yo, float zo, float *vec1, float *vec2)
+{
+ int ocmin, ocmax;
+
+ if (vec1[0] < vec2[0]) {
+ ocmin = OCVALRES * (vec1[0] - xo);
+ ocmax = OCVALRES * (vec2[0] - xo);
+ }
+ else {
+ ocmin = OCVALRES * (vec2[0] - xo);
+ ocmax = OCVALRES * (vec1[0] - xo);
+ }
+ ov->ocx = BROW16(ocmin, ocmax);
+
+ if (vec1[1] < vec2[1]) {
+ ocmin = OCVALRES * (vec1[1] - yo);
+ ocmax = OCVALRES * (vec2[1] - yo);
+ }
+ else {
+ ocmin = OCVALRES * (vec2[1] - yo);
+ ocmax = OCVALRES * (vec1[1] - yo);
+ }
+ ov->ocy = BROW16(ocmin, ocmax);
+
+ if (vec1[2] < vec2[2]) {
+ ocmin = OCVALRES * (vec1[2] - zo);
+ ocmax = OCVALRES * (vec2[2] - zo);
+ }
+ else {
+ ocmin = OCVALRES * (vec2[2] - zo);
+ ocmax = OCVALRES * (vec1[2] - zo);
+ }
+ ov->ocz = BROW16(ocmin, ocmax);
+}
+
+/* ************* octree ************** */
+
+static Branch *addbranch(Octree *oc, Branch *br, short ocb)
+{
+ int index;
+
+ if (br->b[ocb]) return br->b[ocb];
+
+ oc->branchcount++;
+ index = oc->branchcount >> 12;
+
+ if (oc->adrbranch[index] == NULL)
+ oc->adrbranch[index] = (Branch *)MEM_callocN(4096 * sizeof(Branch), "new oc branch");
+
+ if (oc->branchcount >= BRANCH_ARRAY * 4096) {
+ printf("error; octree branches full\n");
+ oc->branchcount = 0;
+ }
+
+ return br->b[ocb] = oc->adrbranch[index] + (oc->branchcount & 4095);
+}
+
+static Node *addnode(Octree *oc)
+{
+ int index;
+
+ oc->nodecount++;
+ index = oc->nodecount >> 12;
+
+ if (oc->adrnode[index] == NULL)
+ oc->adrnode[index] = (Node *)MEM_callocN(4096 * sizeof(Node), "addnode");
+
+ if (oc->nodecount > NODE_ARRAY * NODE_ARRAY) {
+ printf("error; octree nodes full\n");
+ oc->nodecount = 0;
+ }
+
+ return oc->adrnode[index] + (oc->nodecount & 4095);
+}
+
+static bool face_in_node(RayFace *face, short x, short y, short z, float rtf[4][3])
+{
+ static float nor[3], d;
+ float fx, fy, fz;
+
+ // init static vars
+ if (face) {
+ normal_tri_v3(nor, rtf[0], rtf[1], rtf[2]);
+ d = -nor[0] * rtf[0][0] - nor[1] * rtf[0][1] - nor[2] * rtf[0][2];
+ return 0;
+ }
+
+ fx = x;
+ fy = y;
+ fz = z;
+
+ if ((fx) * nor[0] + (fy) * nor[1] + (fz) * nor[2] + d > 0.0f) {
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz ) * nor[2] + d < 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d < 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d < 0.0f) return 1;
+
+ if ((fx ) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ }
+ else {
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz ) * nor[2] + d > 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d > 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d > 0.0f) return 1;
+
+ if ((fx ) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ }
+
+ return 0;
+}
+
+static void ocwrite(Octree *oc, RayFace *face, int quad, short x, short y, short z, float rtf[4][3])
+{
+ Branch *br;
+ Node *no;
+ short a, oc0, oc1, oc2, oc3, oc4, oc5;
+
+ x <<= 2;
+ y <<= 1;
+
+ br = oc->adrbranch[0];
+
+ if (oc->ocres == 512) {
+ oc0 = ((x & 1024) + (y & 512) + (z & 256)) >> 8;
+ br = addbranch(oc, br, oc0);
+ }
+ if (oc->ocres >= 256) {
+ oc0 = ((x & 512) + (y & 256) + (z & 128)) >> 7;
+ br = addbranch(oc, br, oc0);
+ }
+ if (oc->ocres >= 128) {
+ oc0 = ((x & 256) + (y & 128) + (z & 64)) >> 6;
+ br = addbranch(oc, br, oc0);
+ }
+
+ oc0 = ((x & 128) + (y & 64) + (z & 32)) >> 5;
+ oc1 = ((x & 64) + (y & 32) + (z & 16)) >> 4;
+ oc2 = ((x & 32) + (y & 16) + (z & 8)) >> 3;
+ oc3 = ((x & 16) + (y & 8) + (z & 4)) >> 2;
+ oc4 = ((x & 8) + (y & 4) + (z & 2)) >> 1;
+ oc5 = ((x & 4) + (y & 2) + (z & 1));
+
+ br = addbranch(oc, br, oc0);
+ br = addbranch(oc, br, oc1);
+ br = addbranch(oc, br, oc2);
+ br = addbranch(oc, br, oc3);
+ br = addbranch(oc, br, oc4);
+ no = (Node *)br->b[oc5];
+ if (no == NULL) br->b[oc5] = (Branch *)(no = addnode(oc));
+
+ while (no->next) no = no->next;
+
+ a = 0;
+ if (no->v[7]) { /* node full */
+ no->next = addnode(oc);
+ no = no->next;
+ }
+ else {
+ while (no->v[a] != NULL) a++;
+ }
+
+ no->v[a] = (RayFace *) RE_rayobject_align(face);
+
+ if (quad)
+ calc_ocval_face(rtf[0], rtf[1], rtf[2], rtf[3], x >> 2, y >> 1, z, &no->ov[a]);
+ else
+ calc_ocval_face(rtf[0], rtf[1], rtf[2], NULL, x >> 2, y >> 1, z, &no->ov[a]);
+}
+
+static void d2dda(Octree *oc, short b1, short b2, short c1, short c2, char *ocface, short rts[4][3], float rtf[4][3])
+{
+ int ocx1, ocx2, ocy1, ocy2;
+ int x, y, dx = 0, dy = 0;
+ float ox1, ox2, oy1, oy2;
+ float lambda, lambda_o, lambda_x, lambda_y, ldx, ldy;
+
+ ocx1 = rts[b1][c1];
+ ocy1 = rts[b1][c2];
+ ocx2 = rts[b2][c1];
+ ocy2 = rts[b2][c2];
+
+ if (ocx1 == ocx2 && ocy1 == ocy2) {
+ ocface[oc->ocres * ocx1 + ocy1] = 1;
+ return;
+ }
+
+ ox1 = rtf[b1][c1];
+ oy1 = rtf[b1][c2];
+ ox2 = rtf[b2][c1];
+ oy2 = rtf[b2][c2];
+
+ if (ox1 != ox2) {
+ if (ox2 - ox1 > 0.0f) {
+ lambda_x = (ox1 - ocx1 - 1.0f) / (ox1 - ox2);
+ ldx = -1.0f / (ox1 - ox2);
+ dx = 1;
+ }
+ else {
+ lambda_x = (ox1 - ocx1) / (ox1 - ox2);
+ ldx = 1.0f / (ox1 - ox2);
+ dx = -1;
+ }
+ }
+ else {
+ lambda_x = 1.0f;
+ ldx = 0;
+ }
+
+ if (oy1 != oy2) {
+ if (oy2 - oy1 > 0.0f) {
+ lambda_y = (oy1 - ocy1 - 1.0f) / (oy1 - oy2);
+ ldy = -1.0f / (oy1 - oy2);
+ dy = 1;
+ }
+ else {
+ lambda_y = (oy1 - ocy1) / (oy1 - oy2);
+ ldy = 1.0f / (oy1 - oy2);
+ dy = -1;
+ }
+ }
+ else {
+ lambda_y = 1.0f;
+ ldy = 0;
+ }
+
+ x = ocx1; y = ocy1;
+ lambda = MIN2(lambda_x, lambda_y);
+
+ while (true) {
+
+ if (x < 0 || y < 0 || x >= oc->ocres || y >= oc->ocres) {
+ /* pass*/
+ }
+ else {
+ ocface[oc->ocres * x + y] = 1;
+ }
+
+ lambda_o = lambda;
+ if (lambda_x == lambda_y) {
+ lambda_x += ldx;
+ x += dx;
+ lambda_y += ldy;
+ y += dy;
+ }
+ else {
+ if (lambda_x < lambda_y) {
+ lambda_x += ldx;
+ x += dx;
+ }
+ else {
+ lambda_y += ldy;
+ y += dy;
+ }
+ }
+ lambda = MIN2(lambda_x, lambda_y);
+ if (lambda == lambda_o) break;
+ if (lambda >= 1.0f) break;
+ }
+ ocface[oc->ocres * ocx2 + ocy2] = 1;
+}
+
+static void filltriangle(Octree *oc, short c1, short c2, char *ocface, short *ocmin, short *ocmax)
+{
+ int a, x, y, y1, y2;
+
+ for (x = ocmin[c1]; x <= ocmax[c1]; x++) {
+ a = oc->ocres * x;
+ for (y = ocmin[c2]; y <= ocmax[c2]; y++) {
+ if (ocface[a + y]) {
+ y++;
+ while (ocface[a + y] && y != ocmax[c2]) y++;
+ for (y1 = ocmax[c2]; y1 > y; y1--) {
+ if (ocface[a + y1]) {
+ for (y2 = y; y2 <= y1; y2++) ocface[a + y2] = 1;
+ y1 = 0;
+ }
+ }
+ y = ocmax[c2];
+ }
+ }
+ }
+}
+
+static void RE_rayobject_octree_free(RayObject *tree)
+{
+ Octree *oc = (Octree *)tree;
+
+#if 0
+ printf("branches %d nodes %d\n", oc->branchcount, oc->nodecount);
+ printf("raycount %d\n", raycount);
+ printf("ray coherent %d\n", coherent_ray);
+ printf("accepted %d rejected %d\n", accepted, rejected);
+#endif
+ if (oc->ocface)
+ MEM_freeN(oc->ocface);
+
+ if (oc->adrbranch) {
+ int a = 0;
+ while (oc->adrbranch[a]) {
+ MEM_freeN(oc->adrbranch[a]);
+ oc->adrbranch[a] = NULL;
+ a++;
+ }
+ MEM_freeN(oc->adrbranch);
+ oc->adrbranch = NULL;
+ }
+ oc->branchcount = 0;
+
+ if (oc->adrnode) {
+ int a = 0;
+ while (oc->adrnode[a]) {
+ MEM_freeN(oc->adrnode[a]);
+ oc->adrnode[a] = NULL;
+ a++;
+ }
+ MEM_freeN(oc->adrnode);
+ oc->adrnode = NULL;
+ }
+ oc->nodecount = 0;
+
+ MEM_freeN(oc);
+}
+
+
+RayObject *RE_rayobject_octree_create(int ocres, int size)
+{
+ Octree *oc = (Octree *)MEM_callocN(sizeof(Octree), "Octree");
+ assert(RE_rayobject_isAligned(oc) ); /* RayObject API assumes real data to be 4-byte aligned */
+
+ oc->rayobj.api = &octree_api;
+
+ oc->ocres = ocres;
+
+ oc->ro_nodes = (RayFace **)MEM_callocN(sizeof(RayFace *) * size, "octree rayobject nodes");
+ oc->ro_nodes_size = size;
+ oc->ro_nodes_used = 0;
+
+
+ return RE_rayobject_unalignRayAPI((RayObject *) oc);
+}
+
+
+static void RE_rayobject_octree_add(RayObject *tree, RayObject *node)
+{
+ Octree *oc = (Octree *)tree;
+
+ assert(RE_rayobject_isRayFace(node) );
+ assert(oc->ro_nodes_used < oc->ro_nodes_size);
+ oc->ro_nodes[oc->ro_nodes_used++] = (RayFace *)RE_rayobject_align(node);
+}
+
+static void octree_fill_rayface(Octree *oc, RayFace *face)
+{
+ float ocfac[3], rtf[4][3];
+ float co1[3], co2[3], co3[3], co4[3];
+ short rts[4][3];
+ short ocmin[3], ocmax[3];
+ char *ocface = oc->ocface; // front, top, size view of face, to fill in
+ int a, b, c, oc1, oc2, oc3, oc4, x, y, z, ocres2;
+
+ ocfac[0] = oc->ocfacx;
+ ocfac[1] = oc->ocfacy;
+ ocfac[2] = oc->ocfacz;
+
+ ocres2 = oc->ocres * oc->ocres;
+
+ copy_v3_v3(co1, face->v1);
+ copy_v3_v3(co2, face->v2);
+ copy_v3_v3(co3, face->v3);
+ if (RE_rayface_isQuad(face))
+ copy_v3_v3(co4, face->v4);
+
+ for (c = 0; c < 3; c++) {
+ rtf[0][c] = (co1[c] - oc->min[c]) * ocfac[c];
+ rts[0][c] = (short)rtf[0][c];
+ rtf[1][c] = (co2[c] - oc->min[c]) * ocfac[c];
+ rts[1][c] = (short)rtf[1][c];
+ rtf[2][c] = (co3[c] - oc->min[c]) * ocfac[c];
+ rts[2][c] = (short)rtf[2][c];
+ if (RE_rayface_isQuad(face)) {
+ rtf[3][c] = (co4[c] - oc->min[c]) * ocfac[c];
+ rts[3][c] = (short)rtf[3][c];
+ }
+ }
+
+ for (c = 0; c < 3; c++) {
+ oc1 = rts[0][c];
+ oc2 = rts[1][c];
+ oc3 = rts[2][c];
+ if (!RE_rayface_isQuad(face)) {
+ ocmin[c] = min_iii(oc1, oc2, oc3);
+ ocmax[c] = max_iii(oc1, oc2, oc3);
+ }
+ else {
+ oc4 = rts[3][c];
+ ocmin[c] = min_iiii(oc1, oc2, oc3, oc4);
+ ocmax[c] = max_iiii(oc1, oc2, oc3, oc4);
+ }
+ if (ocmax[c] > oc->ocres - 1) ocmax[c] = oc->ocres - 1;
+ if (ocmin[c] < 0) ocmin[c] = 0;
+ }
+
+ if (ocmin[0] == ocmax[0] && ocmin[1] == ocmax[1] && ocmin[2] == ocmax[2]) {
+ ocwrite(oc, face, RE_rayface_isQuad(face), ocmin[0], ocmin[1], ocmin[2], rtf);
+ }
+ else {
+
+ d2dda(oc, 0, 1, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 0, 1, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 0, 1, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ d2dda(oc, 1, 2, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 1, 2, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 1, 2, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ if (!RE_rayface_isQuad(face)) {
+ d2dda(oc, 2, 0, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 2, 0, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 2, 0, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ }
+ else {
+ d2dda(oc, 2, 3, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 2, 3, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 2, 3, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ d2dda(oc, 3, 0, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 3, 0, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 3, 0, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ }
+ /* nothing todo with triangle..., just fills :) */
+ filltriangle(oc, 0, 1, ocface + ocres2, ocmin, ocmax);
+ filltriangle(oc, 0, 2, ocface, ocmin, ocmax);
+ filltriangle(oc, 1, 2, ocface + 2 * ocres2, ocmin, ocmax);
+
+ /* init static vars here */
+ face_in_node(face, 0, 0, 0, rtf);
+
+ for (x = ocmin[0]; x <= ocmax[0]; x++) {
+ a = oc->ocres * x;
+ for (y = ocmin[1]; y <= ocmax[1]; y++) {
+ if (ocface[a + y + ocres2]) {
+ b = oc->ocres * y + 2 * ocres2;
+ for (z = ocmin[2]; z <= ocmax[2]; z++) {
+ if (ocface[b + z] && ocface[a + z]) {
+ if (face_in_node(NULL, x, y, z, rtf))
+ ocwrite(oc, face, RE_rayface_isQuad(face), x, y, z, rtf);
+ }
+ }
+ }
+ }
+ }
+
+ /* same loops to clear octree, doubt it can be done smarter */
+ for (x = ocmin[0]; x <= ocmax[0]; x++) {
+ a = oc->ocres * x;
+ for (y = ocmin[1]; y <= ocmax[1]; y++) {
+ /* x-y */
+ ocface[a + y + ocres2] = 0;
+
+ b = oc->ocres * y + 2 * ocres2;
+ for (z = ocmin[2]; z <= ocmax[2]; z++) {
+ /* y-z */
+ ocface[b + z] = 0;
+ /* x-z */
+ ocface[a + z] = 0;
+ }
+ }
+ }
+ }
+}
+
+static void RE_rayobject_octree_done(RayObject *tree)
+{
+ Octree *oc = (Octree *)tree;
+ int c;
+ float t00, t01, t02;
+ int ocres2 = oc->ocres * oc->ocres;
+
+ INIT_MINMAX(oc->min, oc->max);
+
+ /* Calculate Bounding Box */
+ for (c = 0; c < oc->ro_nodes_used; c++)
+ RE_rayobject_merge_bb(RE_rayobject_unalignRayFace(oc->ro_nodes[c]), oc->min, oc->max);
+
+ /* Alloc memory */
+ oc->adrbranch = (Branch **)MEM_callocN(sizeof(void *) * BRANCH_ARRAY, "octree branches");
+ oc->adrnode = (Node **)MEM_callocN(sizeof(void *) * NODE_ARRAY, "octree nodes");
+
+ oc->adrbranch[0] = (Branch *)MEM_callocN(4096 * sizeof(Branch), "makeoctree");
+
+ /* the lookup table, per face, for which nodes to fill in */
+ oc->ocface = (char *)MEM_callocN(3 * ocres2 + 8, "ocface");
+ memset(oc->ocface, 0, 3 * ocres2);
+
+ for (c = 0; c < 3; c++) { /* octree enlarge, still needed? */
+ oc->min[c] -= 0.01f;
+ oc->max[c] += 0.01f;
+ }
+
+ t00 = oc->max[0] - oc->min[0];
+ t01 = oc->max[1] - oc->min[1];
+ t02 = oc->max[2] - oc->min[2];
+
+ /* this minus 0.1 is old safety... seems to be needed? */
+ oc->ocfacx = (oc->ocres - 0.1f) / t00;
+ oc->ocfacy = (oc->ocres - 0.1f) / t01;
+ oc->ocfacz = (oc->ocres - 0.1f) / t02;
+
+ oc->ocsize = sqrtf(t00 * t00 + t01 * t01 + t02 * t02); /* global, max size octree */
+
+ for (c = 0; c < oc->ro_nodes_used; c++) {
+ octree_fill_rayface(oc, oc->ro_nodes[c]);
+ }
+
+ MEM_freeN(oc->ocface);
+ oc->ocface = NULL;
+ MEM_freeN(oc->ro_nodes);
+ oc->ro_nodes = NULL;
+
+#if 0
+ printf("%f %f - %f\n", oc->min[0], oc->max[0], oc->ocfacx);
+ printf("%f %f - %f\n", oc->min[1], oc->max[1], oc->ocfacy);
+ printf("%f %f - %f\n", oc->min[2], oc->max[2], oc->ocfacz);
+#endif
+}
+
+static void RE_rayobject_octree_bb(RayObject *tree, float *min, float *max)
+{
+ Octree *oc = (Octree *)tree;
+ DO_MINMAX(oc->min, min, max);
+ DO_MINMAX(oc->max, min, max);
+}
+
+/* check all faces in this node */
+static int testnode(Octree *UNUSED(oc), Isect *is, Node *no, OcVal ocval)
+{
+ short nr = 0;
+
+ /* return on any first hit */
+ if (is->mode == RE_RAY_SHADOW) {
+
+ for (; no; no = no->next) {
+ for (nr = 0; nr < 8; nr++) {
+ RayFace *face = no->v[nr];
+ OcVal *ov = no->ov + nr;
+
+ if (!face) break;
+
+ if ( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) {
+ if (RE_rayobject_intersect(RE_rayobject_unalignRayFace(face), is) )
+ return 1;
+ }
+ }
+ }
+ }
+ else {
+ /* else mirror or glass or shadowtra, return closest face */
+ int found = 0;
+
+ for (; no; no = no->next) {
+ for (nr = 0; nr < 8; nr++) {
+ RayFace *face = no->v[nr];
+ OcVal *ov = no->ov + nr;
+
+ if (!face) break;
+
+ if ( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) {
+ if (RE_rayobject_intersect(RE_rayobject_unalignRayFace(face), is) ) {
+ found = 1;
+ }
+ }
+ }
+ }
+
+ return found;
+ }
+
+ return 0;
+}
+
+/* find the Node for the octree coord x y z */
+static Node *ocread(Octree *oc, int x, int y, int z)
+{
+ Branch *br;
+ int oc1;
+
+ x <<= 2;
+ y <<= 1;
+
+ br = oc->adrbranch[0];
+
+ if (oc->ocres == 512) {
+ oc1 = ((x & 1024) + (y & 512) + (z & 256)) >> 8;
+ br = br->b[oc1];
+ if (br == NULL) {
+ return NULL;
+ }
+ }
+ if (oc->ocres >= 256) {
+ oc1 = ((x & 512) + (y & 256) + (z & 128)) >> 7;
+ br = br->b[oc1];
+ if (br == NULL) {
+ return NULL;
+ }
+ }
+ if (oc->ocres >= 128) {
+ oc1 = ((x & 256) + (y & 128) + (z & 64)) >> 6;
+ br = br->b[oc1];
+ if (br == NULL) {
+ return NULL;
+ }
+ }
+
+ oc1 = ((x & 128) + (y & 64) + (z & 32)) >> 5;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 64) + (y & 32) + (z & 16)) >> 4;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 32) + (y & 16) + (z & 8)) >> 3;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 16) + (y & 8) + (z & 4)) >> 2;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 8) + (y & 4) + (z & 2)) >> 1;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 4) + (y & 2) + (z & 1));
+ return (Node *)br->b[oc1];
+ }
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int cliptest(float p, float q, float *u1, float *u2)
+{
+ float r;
+
+ if (p < 0.0f) {
+ if (q < p) return 0;
+ else if (q < 0.0f) {
+ r = q / p;
+ if (r > *u2) return 0;
+ else if (r > *u1) *u1 = r;
+ }
+ }
+ else {
+ if (p > 0.0f) {
+ if (q < 0.0f) return 0;
+ else if (q < p) {
+ r = q / p;
+ if (r < *u1) return 0;
+ else if (r < *u2) *u2 = r;
+ }
+ }
+ else if (q < 0.0f) return 0;
+ }
+ return 1;
+}
+
+/* extensive coherence checks/storage cancels out the benefit of it, and gives errors... we
+ * need better methods, sample code commented out below (ton) */
+
+#if 0
+
+in top : static int coh_nodes[16 * 16 * 16][6];
+in makeoctree : memset(coh_nodes, 0, sizeof(coh_nodes));
+
+static void add_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2)
+{
+ short *sp;
+
+ sp = coh_nodes[(ocx2 & 15) + 16 * (ocy2 & 15) + 256 * (ocz2 & 15)];
+ sp[0] = ocx1; sp[1] = ocy1; sp[2] = ocz1;
+ sp[3] = ocx2; sp[4] = ocy2; sp[5] = ocz2;
+
+}
+
+static int do_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2)
+{
+ short *sp;
+
+ sp = coh_nodes[(ocx2 & 15) + 16 * (ocy2 & 15) + 256 * (ocz2 & 15)];
+ if (sp[0] == ocx1 && sp[1] == ocy1 && sp[2] == ocz1 &&
+ sp[3] == ocx2 && sp[4] == ocy2 && sp[5] == ocz2) return 1;
+ return 0;
+}
+
+#endif
+
+/* return 1: found valid intersection */
+/* starts with is->orig.face */
+static int RE_rayobject_octree_intersect(RayObject *tree, Isect *is)
+{
+ Octree *oc = (Octree *)tree;
+ Node *no;
+ OcVal ocval;
+ float vec1[3], vec2[3], start[3], end[3];
+ float u1, u2, ox1, ox2, oy1, oy2, oz1, oz2;
+ float lambda_o, lambda_x, ldx, lambda_y, ldy, lambda_z, ldz, dda_lambda;
+ float o_lambda = 0;
+ int dx, dy, dz;
+ int xo, yo, zo, c1 = 0;
+ int ocx1, ocx2, ocy1, ocy2, ocz1, ocz2;
+
+ /* clip with octree */
+ if (oc->branchcount == 0) return 0;
+
+ /* do this before intersect calls */
+#if 0
+ is->facecontr = NULL; /* to check shared edge */
+ is->obcontr = 0;
+ is->faceisect = is->isect = 0; /* shared edge, quad half flag */
+ is->userdata = oc->userdata;
+#endif
+
+ copy_v3_v3(start, is->start);
+ madd_v3_v3v3fl(end, is->start, is->dir, is->dist);
+ ldx = is->dir[0] * is->dist;
+ o_lambda = is->dist;
+ u1 = 0.0f;
+ u2 = 1.0f;
+
+ /* clip with octree cube */
+ if (cliptest(-ldx, start[0] - oc->min[0], &u1, &u2)) {
+ if (cliptest(ldx, oc->max[0] - start[0], &u1, &u2)) {
+ ldy = is->dir[1] * is->dist;
+ if (cliptest(-ldy, start[1] - oc->min[1], &u1, &u2)) {
+ if (cliptest(ldy, oc->max[1] - start[1], &u1, &u2)) {
+ ldz = is->dir[2] * is->dist;
+ if (cliptest(-ldz, start[2] - oc->min[2], &u1, &u2)) {
+ if (cliptest(ldz, oc->max[2] - start[2], &u1, &u2)) {
+ c1 = 1;
+ if (u2 < 1.0f) {
+ end[0] = start[0] + u2 * ldx;
+ end[1] = start[1] + u2 * ldy;
+ end[2] = start[2] + u2 * ldz;
+ }
+
+ if (u1 > 0.0f) {
+ start[0] += u1 * ldx;
+ start[1] += u1 * ldy;
+ start[2] += u1 * ldz;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (c1 == 0) return 0;
+
+ /* reset static variables in ocread */
+ //ocread(oc, oc->ocres, 0, 0);
+
+ /* setup 3dda to traverse octree */
+ ox1 = (start[0] - oc->min[0]) * oc->ocfacx;
+ oy1 = (start[1] - oc->min[1]) * oc->ocfacy;
+ oz1 = (start[2] - oc->min[2]) * oc->ocfacz;
+ ox2 = (end[0] - oc->min[0]) * oc->ocfacx;
+ oy2 = (end[1] - oc->min[1]) * oc->ocfacy;
+ oz2 = (end[2] - oc->min[2]) * oc->ocfacz;
+
+ ocx1 = (int)ox1;
+ ocy1 = (int)oy1;
+ ocz1 = (int)oz1;
+ ocx2 = (int)ox2;
+ ocy2 = (int)oy2;
+ ocz2 = (int)oz2;
+
+ if (ocx1 == ocx2 && ocy1 == ocy2 && ocz1 == ocz2) {
+ no = ocread(oc, ocx1, ocy1, ocz1);
+ if (no) {
+ /* exact intersection with node */
+ vec1[0] = ox1; vec1[1] = oy1; vec1[2] = oz1;
+ vec2[0] = ox2; vec2[1] = oy2; vec2[2] = oz2;
+ calc_ocval_ray(&ocval, (float)ocx1, (float)ocy1, (float)ocz1, vec1, vec2);
+ if (testnode(oc, is, no, ocval) ) return 1;
+ }
+ }
+ else {
+ int found = 0;
+ //static int coh_ocx1, coh_ocx2, coh_ocy1, coh_ocy2, coh_ocz1, coh_ocz2;
+ float dox, doy, doz;
+ int eqval;
+
+ /* calc lambda en ld */
+ dox = ox1 - ox2;
+ doy = oy1 - oy2;
+ doz = oz1 - oz2;
+
+ if (dox < -FLT_EPSILON) {
+ ldx = -1.0f / dox;
+ lambda_x = (ocx1 - ox1 + 1.0f) * ldx;
+ dx = 1;
+ }
+ else if (dox > FLT_EPSILON) {
+ ldx = 1.0f / dox;
+ lambda_x = (ox1 - ocx1) * ldx;
+ dx = -1;
+ }
+ else {
+ lambda_x = 1.0f;
+ ldx = 0;
+ dx = 0;
+ }
+
+ if (doy < -FLT_EPSILON) {
+ ldy = -1.0f / doy;
+ lambda_y = (ocy1 - oy1 + 1.0f) * ldy;
+ dy = 1;
+ }
+ else if (doy > FLT_EPSILON) {
+ ldy = 1.0f / doy;
+ lambda_y = (oy1 - ocy1) * ldy;
+ dy = -1;
+ }
+ else {
+ lambda_y = 1.0f;
+ ldy = 0;
+ dy = 0;
+ }
+
+ if (doz < -FLT_EPSILON) {
+ ldz = -1.0f / doz;
+ lambda_z = (ocz1 - oz1 + 1.0f) * ldz;
+ dz = 1;
+ }
+ else if (doz > FLT_EPSILON) {
+ ldz = 1.0f / doz;
+ lambda_z = (oz1 - ocz1) * ldz;
+ dz = -1;
+ }
+ else {
+ lambda_z = 1.0f;
+ ldz = 0;
+ dz = 0;
+ }
+
+ xo = ocx1; yo = ocy1; zo = ocz1;
+ dda_lambda = min_fff(lambda_x, lambda_y, lambda_z);
+
+ vec2[0] = ox1;
+ vec2[1] = oy1;
+ vec2[2] = oz1;
+
+ /* this loop has been constructed to make sure the first and last node of ray
+ * are always included, even when dda_lambda==1.0f or larger */
+
+ while (true) {
+
+ no = ocread(oc, xo, yo, zo);
+ if (no) {
+
+ /* calculate ray intersection with octree node */
+ copy_v3_v3(vec1, vec2);
+ // dox, y, z is negative
+ vec2[0] = ox1 - dda_lambda * dox;
+ vec2[1] = oy1 - dda_lambda * doy;
+ vec2[2] = oz1 - dda_lambda * doz;
+ calc_ocval_ray(&ocval, (float)xo, (float)yo, (float)zo, vec1, vec2);
+
+ //is->dist = (u1 + dda_lambda * (u2 - u1)) * o_lambda;
+ if (testnode(oc, is, no, ocval) )
+ found = 1;
+
+ if (is->dist < (u1 + dda_lambda * (u2 - u1)) * o_lambda)
+ return found;
+ }
+
+
+ lambda_o = dda_lambda;
+
+ /* traversing octree nodes need careful detection of smallest values, with proper
+ * exceptions for equal lambdas */
+ eqval = (lambda_x == lambda_y);
+ if (lambda_y == lambda_z) eqval += 2;
+ if (lambda_x == lambda_z) eqval += 4;
+
+ if (eqval) { // only 4 cases exist!
+ if (eqval == 7) { // x=y=z
+ xo += dx; lambda_x += ldx;
+ yo += dy; lambda_y += ldy;
+ zo += dz; lambda_z += ldz;
+ }
+ else if (eqval == 1) { // x=y
+ if (lambda_y < lambda_z) {
+ xo += dx; lambda_x += ldx;
+ yo += dy; lambda_y += ldy;
+ }
+ else {
+ zo += dz; lambda_z += ldz;
+ }
+ }
+ else if (eqval == 2) { // y=z
+ if (lambda_x < lambda_y) {
+ xo += dx; lambda_x += ldx;
+ }
+ else {
+ yo += dy; lambda_y += ldy;
+ zo += dz; lambda_z += ldz;
+ }
+ }
+ else { // x=z
+ if (lambda_y < lambda_x) {
+ yo += dy; lambda_y += ldy;
+ }
+ else {
+ xo += dx; lambda_x += ldx;
+ zo += dz; lambda_z += ldz;
+ }
+ }
+ }
+ else { // all three different, just three cases exist
+ eqval = (lambda_x < lambda_y);
+ if (lambda_y < lambda_z) eqval += 2;
+ if (lambda_x < lambda_z) eqval += 4;
+
+ if (eqval == 7 || eqval == 5) { // x smallest
+ xo += dx; lambda_x += ldx;
+ }
+ else if (eqval == 2 || eqval == 6) { // y smallest
+ yo += dy; lambda_y += ldy;
+ }
+ else { // z smallest
+ zo += dz; lambda_z += ldz;
+ }
+
+ }
+
+ dda_lambda = min_fff(lambda_x, lambda_y, lambda_z);
+ if (dda_lambda == lambda_o) break;
+ /* to make sure the last node is always checked */
+ if (lambda_o >= 1.0f) break;
+ }
+ }
+
+ /* reached end, no intersections found */
+ return 0;
+}
+
+
+
diff --git a/source/blender/render/intern/raytrace/rayobject_qbvh.cpp b/source/blender/render/intern/raytrace/rayobject_qbvh.cpp
new file mode 100644
index 00000000000..8e3dd87efd1
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_qbvh.cpp
@@ -0,0 +1,160 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_qbvh.cpp
+ * \ingroup render
+ */
+
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "vbvh.h"
+#include "svbvh.h"
+#include "reorganize.h"
+
+#ifdef __SSE__
+
+#define DFS_STACK_SIZE 256
+
+struct QBVHTree {
+ RayObject rayobj;
+
+ SVBVHNode *root;
+ MemArena *node_arena;
+
+ float cost;
+ RTBuilder *builder;
+};
+
+
+template<>
+void bvh_done<QBVHTree>(QBVHTree *obj)
+{
+ rtbuild_done(obj->builder, &obj->rayobj.control);
+
+ //TODO find a away to exactly calculate the needed memory
+ MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "qbvh arena");
+ BLI_memarena_use_malloc(arena1);
+
+ MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "qbvh arena 2");
+ BLI_memarena_use_malloc(arena2);
+ BLI_memarena_use_align(arena2, 16);
+
+ //Build and optimize the tree
+ //TODO do this in 1 pass (half memory usage during building)
+ VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
+
+ if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
+ BLI_memarena_free(arena1);
+ BLI_memarena_free(arena2);
+ return;
+ }
+
+ if (root) {
+ pushup_simd<VBVHNode, 4>(root);
+ obj->root = Reorganize_SVBVH<VBVHNode>(arena2).transform(root);
+ }
+ else
+ obj->root = NULL;
+
+ //Free data
+ BLI_memarena_free(arena1);
+
+ obj->node_arena = arena2;
+ obj->cost = 1.0;
+
+ rtbuild_free(obj->builder);
+ obj->builder = NULL;
+}
+
+template<int StackSize>
+static int intersect(QBVHTree *obj, Isect *isec)
+{
+ //TODO renable hint support
+ if (RE_rayobject_isAligned(obj->root)) {
+ if (isec->mode == RE_RAY_SHADOW)
+ return svbvh_node_stack_raycast<StackSize, true>(obj->root, isec);
+ else
+ return svbvh_node_stack_raycast<StackSize, false>(obj->root, isec);
+ }
+ else
+ return RE_rayobject_intersect((RayObject *)obj->root, isec);
+}
+
+template<class Tree>
+static void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *UNUSED(min), float *UNUSED(max))
+{
+ //TODO renable hint support
+ {
+ hint->size = 0;
+ hint->stack[hint->size++] = (RayObject *)tree->root;
+ }
+}
+/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
+template<class Tree, int STACK_SIZE>
+static RayObjectAPI make_api()
+{
+ static RayObjectAPI api =
+ {
+ (RE_rayobject_raycast_callback) ((int (*)(Tree *, Isect *)) & intersect<STACK_SIZE>),
+ (RE_rayobject_add_callback) ((void (*)(Tree *, RayObject *)) & bvh_add<Tree>),
+ (RE_rayobject_done_callback) ((void (*)(Tree *)) & bvh_done<Tree>),
+ (RE_rayobject_free_callback) ((void (*)(Tree *)) & bvh_free<Tree>),
+ (RE_rayobject_merge_bb_callback)((void (*)(Tree *, float *, float *)) & bvh_bb<Tree>),
+ (RE_rayobject_cost_callback) ((float (*)(Tree *)) & bvh_cost<Tree>),
+ (RE_rayobject_hint_bb_callback) ((void (*)(Tree *, LCTSHint *, float *, float *)) & bvh_hint_bb<Tree>)
+ };
+
+ return api;
+}
+
+template<class Tree>
+RayObjectAPI *bvh_get_api(int maxstacksize)
+{
+ static RayObjectAPI bvh_api256 = make_api<Tree, 1024>();
+
+ if (maxstacksize <= 1024) return &bvh_api256;
+ assert(maxstacksize <= 256);
+ return NULL;
+}
+
+RayObject *RE_rayobject_qbvh_create(int size)
+{
+ return bvh_create_tree<QBVHTree, DFS_STACK_SIZE>(size);
+}
+
+#else
+
+RayObject *RE_rayobject_qbvh_create(int UNUSED(size))
+{
+ puts("WARNING: SSE disabled at compile time\n");
+ return NULL;
+}
+
+#endif
diff --git a/source/blender/render/intern/raytrace/rayobject_raycounter.cpp b/source/blender/render/intern/raytrace/rayobject_raycounter.cpp
new file mode 100644
index 00000000000..429c47f1c0f
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_raycounter.cpp
@@ -0,0 +1,91 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_raycounter.cpp
+ * \ingroup render
+ */
+
+
+#include "rayobject.h"
+#include "raycounter.h"
+
+#ifdef RE_RAYCOUNTER
+
+void RE_RC_INFO(RayCounter *info)
+{
+ printf("----------- Raycast counter --------\n");
+ printf("Rays total: %llu\n", info->raycast.test );
+ printf("Rays hit: %llu\n", info->raycast.hit );
+ printf("\n");
+ printf("BB tests: %llu\n", info->bb.test );
+ printf("BB hits: %llu\n", info->bb.hit );
+ printf("\n");
+ printf("SIMD BB tests: %llu\n", info->simd_bb.test );
+ printf("SIMD BB hits: %llu\n", info->simd_bb.hit );
+ printf("\n");
+ printf("Primitives tests: %llu\n", info->faces.test );
+ printf("Primitives hits: %llu\n", info->faces.hit );
+ printf("------------------------------------\n");
+ printf("Shadow last-hit tests per ray: %f\n", info->rayshadow_last_hit.test / ((float)info->raycast.test) );
+ printf("Shadow last-hit hits per ray: %f\n", info->rayshadow_last_hit.hit / ((float)info->raycast.test) );
+ printf("\n");
+ printf("Hint tests per ray: %f\n", info->raytrace_hint.test / ((float)info->raycast.test) );
+ printf("Hint hits per ray: %f\n", info->raytrace_hint.hit / ((float)info->raycast.test) );
+ printf("\n");
+ printf("BB tests per ray: %f\n", info->bb.test / ((float)info->raycast.test) );
+ printf("BB hits per ray: %f\n", info->bb.hit / ((float)info->raycast.test) );
+ printf("\n");
+ printf("SIMD tests per ray: %f\n", info->simd_bb.test / ((float)info->raycast.test) );
+ printf("SIMD hits per ray: %f\n", info->simd_bb.hit / ((float)info->raycast.test) );
+ printf("\n");
+ printf("Primitives tests per ray: %f\n", info->faces.test / ((float)info->raycast.test) );
+ printf("Primitives hits per ray: %f\n", info->faces.hit / ((float)info->raycast.test) );
+ printf("------------------------------------\n");
+}
+
+void RE_RC_MERGE(RayCounter *dest, RayCounter *tmp)
+{
+ dest->faces.test += tmp->faces.test;
+ dest->faces.hit += tmp->faces.hit;
+
+ dest->bb.test += tmp->bb.test;
+ dest->bb.hit += tmp->bb.hit;
+
+ dest->simd_bb.test += tmp->simd_bb.test;
+ dest->simd_bb.hit += tmp->simd_bb.hit;
+
+ dest->raycast.test += tmp->raycast.test;
+ dest->raycast.hit += tmp->raycast.hit;
+
+ dest->rayshadow_last_hit.test += tmp->rayshadow_last_hit.test;
+ dest->rayshadow_last_hit.hit += tmp->rayshadow_last_hit.hit;
+
+ dest->raytrace_hint.test += tmp->raytrace_hint.test;
+ dest->raytrace_hint.hit += tmp->raytrace_hint.hit;
+}
+
+#endif
diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp
new file mode 100644
index 00000000000..51f89784674
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp
@@ -0,0 +1,531 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_rtbuild.cpp
+ * \ingroup render
+ */
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <algorithm>
+
+#if __cplusplus >= 201103L
+#include <cmath>
+using std::isfinite;
+#else
+#include <math.h>
+#endif
+
+#include "rayobject_rtbuild.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+static bool selected_node(RTBuilder::Object *node)
+{
+ return node->selected;
+}
+
+static void rtbuild_init(RTBuilder *b)
+{
+ b->split_axis = -1;
+ b->primitives.begin = NULL;
+ b->primitives.end = NULL;
+ b->primitives.maxsize = 0;
+ b->depth = 0;
+
+ for (int i = 0; i < RTBUILD_MAX_CHILDS; i++)
+ b->child_offset[i] = 0;
+
+ for (int i = 0; i < 3; i++)
+ b->sorted_begin[i] = b->sorted_end[i] = NULL;
+
+ INIT_MINMAX(b->bb, b->bb + 3);
+}
+
+RTBuilder *rtbuild_create(int size)
+{
+ RTBuilder *builder = (RTBuilder *) MEM_mallocN(sizeof(RTBuilder), "RTBuilder");
+ RTBuilder::Object *memblock = (RTBuilder::Object *)MEM_mallocN(sizeof(RTBuilder::Object) * size, "RTBuilder.objects");
+
+
+ rtbuild_init(builder);
+
+ builder->primitives.begin = builder->primitives.end = memblock;
+ builder->primitives.maxsize = size;
+
+ for (int i = 0; i < 3; i++) {
+ builder->sorted_begin[i] = (RTBuilder::Object **)MEM_mallocN(sizeof(RTBuilder::Object *) * size, "RTBuilder.sorted_objects");
+ builder->sorted_end[i] = builder->sorted_begin[i];
+ }
+
+
+ return builder;
+}
+
+void rtbuild_free(RTBuilder *b)
+{
+ if (b->primitives.begin) MEM_freeN(b->primitives.begin);
+
+ for (int i = 0; i < 3; i++)
+ if (b->sorted_begin[i])
+ MEM_freeN(b->sorted_begin[i]);
+
+ MEM_freeN(b);
+}
+
+void rtbuild_add(RTBuilder *b, RayObject *o)
+{
+ float bb[6];
+
+ assert(b->primitives.begin + b->primitives.maxsize != b->primitives.end);
+
+ INIT_MINMAX(bb, bb + 3);
+ RE_rayobject_merge_bb(o, bb, bb + 3);
+
+ /* skip objects with invalid bounding boxes, nan causes DO_MINMAX
+ * to do nothing, so we get these invalid values. this shouldn't
+ * happen usually, but bugs earlier in the pipeline can cause it. */
+ if (bb[0] > bb[3] || bb[1] > bb[4] || bb[2] > bb[5])
+ return;
+ /* skip objects with inf bounding boxes */
+ if (!isfinite(bb[0]) || !isfinite(bb[1]) || !isfinite(bb[2]))
+ return;
+ if (!isfinite(bb[3]) || !isfinite(bb[4]) || !isfinite(bb[5]))
+ return;
+ /* skip objects with zero bounding box, they are of no use, and
+ * will give problems in rtbuild_heuristic_object_split later */
+ if (bb[0] == bb[3] && bb[1] == bb[4] && bb[2] == bb[5])
+ return;
+
+ copy_v3_v3(b->primitives.end->bb, bb);
+ copy_v3_v3(b->primitives.end->bb + 3, bb + 3);
+ b->primitives.end->obj = o;
+ b->primitives.end->cost = RE_rayobject_cost(o);
+
+ for (int i = 0; i < 3; i++) {
+ *(b->sorted_end[i]) = b->primitives.end;
+ b->sorted_end[i]++;
+ }
+ b->primitives.end++;
+}
+
+int rtbuild_size(RTBuilder *b)
+{
+ return b->sorted_end[0] - b->sorted_begin[0];
+}
+
+
+template<class Obj, int Axis>
+static bool obj_bb_compare(const Obj &a, const Obj &b)
+{
+ if (a->bb[Axis] != b->bb[Axis])
+ return a->bb[Axis] < b->bb[Axis];
+ return a->obj < b->obj;
+}
+
+template<class Item>
+static void object_sort(Item *begin, Item *end, int axis)
+{
+ if (axis == 0) return std::sort(begin, end, obj_bb_compare<Item, 0> );
+ if (axis == 1) return std::sort(begin, end, obj_bb_compare<Item, 1> );
+ if (axis == 2) return std::sort(begin, end, obj_bb_compare<Item, 2> );
+ assert(false);
+}
+
+void rtbuild_done(RTBuilder *b, RayObjectControl *ctrl)
+{
+ for (int i = 0; i < 3; i++) {
+ if (b->sorted_begin[i]) {
+ if (RE_rayobjectcontrol_test_break(ctrl)) break;
+ object_sort(b->sorted_begin[i], b->sorted_end[i], i);
+ }
+ }
+}
+
+RayObject *rtbuild_get_primitive(RTBuilder *b, int index)
+{
+ return b->sorted_begin[0][index]->obj;
+}
+
+RTBuilder *rtbuild_get_child(RTBuilder *b, int child, RTBuilder *tmp)
+{
+ rtbuild_init(tmp);
+
+ tmp->depth = b->depth + 1;
+
+ for (int i = 0; i < 3; i++)
+ if (b->sorted_begin[i]) {
+ tmp->sorted_begin[i] = b->sorted_begin[i] + b->child_offset[child];
+ tmp->sorted_end[i] = b->sorted_begin[i] + b->child_offset[child + 1];
+ }
+ else {
+ tmp->sorted_begin[i] = NULL;
+ tmp->sorted_end[i] = NULL;
+ }
+
+ return tmp;
+}
+
+static void rtbuild_calc_bb(RTBuilder *b)
+{
+ if (b->bb[0] == 1.0e30f) {
+ for (RTBuilder::Object **index = b->sorted_begin[0]; index != b->sorted_end[0]; index++)
+ RE_rayobject_merge_bb( (*index)->obj, b->bb, b->bb + 3);
+ }
+}
+
+void rtbuild_merge_bb(RTBuilder *b, float min[3], float max[3])
+{
+ rtbuild_calc_bb(b);
+ DO_MIN(b->bb, min);
+ DO_MAX(b->bb + 3, max);
+}
+
+#if 0
+int rtbuild_get_largest_axis(RTBuilder *b)
+{
+ rtbuild_calc_bb(b);
+ return bb_largest_axis(b->bb, b->bb + 3);
+}
+
+//Left balanced tree
+int rtbuild_mean_split(RTBuilder *b, int nchilds, int axis)
+{
+ int i;
+ int mleafs_per_child, Mleafs_per_child;
+ int tot_leafs = rtbuild_size(b);
+ int missing_leafs;
+
+ long long s;
+
+ assert(nchilds <= RTBUILD_MAX_CHILDS);
+
+ //TODO optimize calc of leafs_per_child
+ for (s = nchilds; s < tot_leafs; s *= nchilds) ;
+ Mleafs_per_child = s / nchilds;
+ mleafs_per_child = Mleafs_per_child / nchilds;
+
+ //split min leafs per child
+ b->child_offset[0] = 0;
+ for (i = 1; i <= nchilds; i++)
+ b->child_offset[i] = mleafs_per_child;
+
+ //split remaining leafs
+ missing_leafs = tot_leafs - mleafs_per_child * nchilds;
+ for (i = 1; i <= nchilds; i++)
+ {
+ if (missing_leafs > Mleafs_per_child - mleafs_per_child)
+ {
+ b->child_offset[i] += Mleafs_per_child - mleafs_per_child;
+ missing_leafs -= Mleafs_per_child - mleafs_per_child;
+ }
+ else {
+ b->child_offset[i] += missing_leafs;
+ missing_leafs = 0;
+ break;
+ }
+ }
+
+ //adjust for accumulative offsets
+ for (i = 1; i <= nchilds; i++)
+ b->child_offset[i] += b->child_offset[i - 1];
+
+ //Count created childs
+ for (i = nchilds; b->child_offset[i] == b->child_offset[i - 1]; i--) ;
+ split_leafs(b, b->child_offset, i, axis);
+
+ assert(b->child_offset[0] == 0 && b->child_offset[i] == tot_leafs);
+ return i;
+}
+
+
+int rtbuild_mean_split_largest_axis(RTBuilder *b, int nchilds)
+{
+ int axis = rtbuild_get_largest_axis(b);
+ return rtbuild_mean_split(b, nchilds, axis);
+}
+#endif
+
+/*
+ * "separators" is an array of dim NCHILDS-1
+ * and indicates where to cut the childs
+ */
+#if 0
+int rtbuild_median_split(RTBuilder *b, float *separators, int nchilds, int axis)
+{
+ int size = rtbuild_size(b);
+
+ assert(nchilds <= RTBUILD_MAX_CHILDS);
+ if (size <= nchilds)
+ {
+ return rtbuild_mean_split(b, nchilds, axis);
+ }
+ else {
+ int i;
+
+ b->split_axis = axis;
+
+ //Calculate child offsets
+ b->child_offset[0] = 0;
+ for (i = 0; i < nchilds - 1; i++)
+ b->child_offset[i + 1] = split_leafs_by_plane(b, b->child_offset[i], size, separators[i]);
+ b->child_offset[nchilds] = size;
+
+ for (i = 0; i < nchilds; i++)
+ if (b->child_offset[i + 1] - b->child_offset[i] == size)
+ return rtbuild_mean_split(b, nchilds, axis);
+
+ return nchilds;
+ }
+}
+
+int rtbuild_median_split_largest_axis(RTBuilder *b, int nchilds)
+{
+ int la, i;
+ float separators[RTBUILD_MAX_CHILDS];
+
+ rtbuild_calc_bb(b);
+
+ la = bb_largest_axis(b->bb, b->bb + 3);
+ for (i = 1; i < nchilds; i++)
+ separators[i - 1] = (b->bb[la + 3] - b->bb[la]) * i / nchilds;
+
+ return rtbuild_median_split(b, separators, nchilds, la);
+}
+#endif
+
+//Heuristics Object Splitter
+
+
+struct SweepCost {
+ float bb[6];
+ float cost;
+};
+
+/* Object Surface Area Heuristic splitter */
+int rtbuild_heuristic_object_split(RTBuilder *b, int nchilds)
+{
+ int size = rtbuild_size(b);
+ assert(nchilds == 2);
+ assert(size > 1);
+ int baxis = -1, boffset = 0;
+
+ if (size > nchilds) {
+ if (b->depth > RTBUILD_MAX_SAH_DEPTH) {
+ // for degenerate cases we avoid running out of stack space
+ // by simply splitting the children in the middle
+ b->child_offset[0] = 0;
+ b->child_offset[1] = (size+1)/2;
+ b->child_offset[2] = size;
+ return 2;
+ }
+
+ float bcost = FLT_MAX;
+ baxis = -1;
+ boffset = size / 2;
+
+ SweepCost *sweep = (SweepCost *)MEM_mallocN(sizeof(SweepCost) * size, "RTBuilder.HeuristicSweep");
+
+ for (int axis = 0; axis < 3; axis++) {
+ SweepCost sweep_left;
+
+ RTBuilder::Object **obj = b->sorted_begin[axis];
+
+// float right_cost = 0;
+ for (int i = size - 1; i >= 0; i--) {
+ if (i == size - 1) {
+ copy_v3_v3(sweep[i].bb, obj[i]->bb);
+ copy_v3_v3(sweep[i].bb + 3, obj[i]->bb + 3);
+ sweep[i].cost = obj[i]->cost;
+ }
+ else {
+ sweep[i].bb[0] = min_ff(obj[i]->bb[0], sweep[i + 1].bb[0]);
+ sweep[i].bb[1] = min_ff(obj[i]->bb[1], sweep[i + 1].bb[1]);
+ sweep[i].bb[2] = min_ff(obj[i]->bb[2], sweep[i + 1].bb[2]);
+ sweep[i].bb[3] = max_ff(obj[i]->bb[3], sweep[i + 1].bb[3]);
+ sweep[i].bb[4] = max_ff(obj[i]->bb[4], sweep[i + 1].bb[4]);
+ sweep[i].bb[5] = max_ff(obj[i]->bb[5], sweep[i + 1].bb[5]);
+ sweep[i].cost = obj[i]->cost + sweep[i + 1].cost;
+ }
+// right_cost += obj[i]->cost;
+ }
+
+ sweep_left.bb[0] = obj[0]->bb[0];
+ sweep_left.bb[1] = obj[0]->bb[1];
+ sweep_left.bb[2] = obj[0]->bb[2];
+ sweep_left.bb[3] = obj[0]->bb[3];
+ sweep_left.bb[4] = obj[0]->bb[4];
+ sweep_left.bb[5] = obj[0]->bb[5];
+ sweep_left.cost = obj[0]->cost;
+
+// right_cost -= obj[0]->cost; if (right_cost < 0) right_cost = 0;
+
+ for (int i = 1; i < size; i++) {
+ //Worst case heuristic (cost of each child is linear)
+ float hcost, left_side, right_side;
+
+ // not using log seems to have no impact on raytracing perf, but
+ // makes tree construction quicker, left out for now to test (brecht)
+ // left_side = bb_area(sweep_left.bb, sweep_left.bb + 3) * (sweep_left.cost + logf((float)i));
+ // right_side = bb_area(sweep[i].bb, sweep[i].bb + 3) * (sweep[i].cost + logf((float)size - i));
+ left_side = bb_area(sweep_left.bb, sweep_left.bb + 3) * (sweep_left.cost);
+ right_side = bb_area(sweep[i].bb, sweep[i].bb + 3) * (sweep[i].cost);
+ hcost = left_side + right_side;
+
+ assert(left_side >= 0);
+ assert(right_side >= 0);
+
+ if (left_side > bcost) break; //No way we can find a better heuristic in this axis
+
+ assert(hcost >= 0);
+ // this makes sure the tree built is the same whatever is the order of the sorting axis
+ if (hcost < bcost || (hcost == bcost && axis < baxis)) {
+ bcost = hcost;
+ baxis = axis;
+ boffset = i;
+ }
+ DO_MIN(obj[i]->bb, sweep_left.bb);
+ DO_MAX(obj[i]->bb + 3, sweep_left.bb + 3);
+
+ sweep_left.cost += obj[i]->cost;
+// right_cost -= obj[i]->cost; if (right_cost < 0) right_cost = 0;
+ }
+
+ //assert(baxis >= 0 && baxis < 3);
+ if (!(baxis >= 0 && baxis < 3))
+ baxis = 0;
+ }
+
+
+ MEM_freeN(sweep);
+ }
+ else if (size == 2) {
+ baxis = 0;
+ boffset = 1;
+ }
+ else if (size == 1) {
+ b->child_offset[0] = 0;
+ b->child_offset[1] = 1;
+ return 1;
+ }
+
+ b->child_offset[0] = 0;
+ b->child_offset[1] = boffset;
+ b->child_offset[2] = size;
+
+
+ /* Adjust sorted arrays for childs */
+ for (int i = 0; i < boffset; i++) b->sorted_begin[baxis][i]->selected = true;
+ for (int i = boffset; i < size; i++) b->sorted_begin[baxis][i]->selected = false;
+ for (int i = 0; i < 3; i++)
+ std::stable_partition(b->sorted_begin[i], b->sorted_end[i], selected_node);
+
+ return nchilds;
+}
+
+/*
+ * Helper code
+ * PARTITION code / used on mean-split
+ * basically this a std::nth_element (like on C++ STL algorithm)
+ */
+#if 0
+static void split_leafs(RTBuilder *b, int *nth, int partitions, int split_axis)
+{
+ int i;
+ b->split_axis = split_axis;
+
+ for (i = 0; i < partitions - 1; i++)
+ {
+ assert(nth[i] < nth[i + 1] && nth[i + 1] < nth[partitions]);
+
+ if (split_axis == 0) std::nth_element(b, nth[i], nth[i + 1], nth[partitions], obj_bb_compare<RTBuilder::Object, 0>);
+ if (split_axis == 1) std::nth_element(b, nth[i], nth[i + 1], nth[partitions], obj_bb_compare<RTBuilder::Object, 1>);
+ if (split_axis == 2) std::nth_element(b, nth[i], nth[i + 1], nth[partitions], obj_bb_compare<RTBuilder::Object, 2>);
+ }
+}
+#endif
+
+/*
+ * Bounding Box utils
+ */
+float bb_volume(const float min[3], const float max[3])
+{
+ return (max[0] - min[0]) * (max[1] - min[1]) * (max[2] - min[2]);
+}
+
+float bb_area(const float min[3], const float max[3])
+{
+ float sub[3], a;
+ sub[0] = max[0] - min[0];
+ sub[1] = max[1] - min[1];
+ sub[2] = max[2] - min[2];
+
+ a = (sub[0] * sub[1] + sub[0] * sub[2] + sub[1] * sub[2]) * 2.0f;
+ /* used to have an assert() here on negative results
+ * however, in this case its likely some overflow or ffast math error.
+ * so just return 0.0f instead. */
+ return a < 0.0f ? 0.0f : a;
+}
+
+int bb_largest_axis(const float min[3], const float max[3])
+{
+ float sub[3];
+
+ sub[0] = max[0] - min[0];
+ sub[1] = max[1] - min[1];
+ sub[2] = max[2] - min[2];
+ if (sub[0] > sub[1]) {
+ if (sub[0] > sub[2])
+ return 0;
+ else
+ return 2;
+ }
+ else {
+ if (sub[1] > sub[2])
+ return 1;
+ else
+ return 2;
+ }
+}
+
+/* only returns 0 if merging inner and outerbox would create a box larger than outer box */
+int bb_fits_inside(const float outer_min[3], const float outer_max[3],
+ const float inner_min[3], const float inner_max[3])
+{
+ int i;
+ for (i = 0; i < 3; i++)
+ if (outer_min[i] > inner_min[i]) return 0;
+
+ for (i = 0; i < 3; i++)
+ if (outer_max[i] < inner_max[i]) return 0;
+
+ return 1;
+}
diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.h b/source/blender/render/intern/raytrace/rayobject_rtbuild.h
new file mode 100644
index 00000000000..fc42bc36d92
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.h
@@ -0,0 +1,125 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_rtbuild.h
+ * \ingroup render
+ */
+
+#ifndef __RAYOBJECT_RTBUILD_H__
+#define __RAYOBJECT_RTBUILD_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "rayobject.h"
+
+
+/*
+ * Ray Tree Builder
+ * this structs helps building any type of tree
+ * it contains several methods to organize/split nodes
+ * allowing to create a given tree on the fly.
+ *
+ * Idea is that other trees BVH, BIH can use this code to
+ * generate with simple calls, and then convert to the theirs
+ * specific structure on the fly.
+ */
+#define RTBUILD_MAX_CHILDS 32
+#define RTBUILD_MAX_SAH_DEPTH 256
+
+
+typedef struct RTBuilder {
+ struct Object {
+ RayObject *obj;
+ float cost;
+ float bb[6];
+ int selected;
+ };
+
+ /* list to all primitives added in this tree */
+ struct {
+ Object *begin, *end;
+ int maxsize;
+ } primitives;
+
+ /* sorted list of rayobjects */
+ struct Object **sorted_begin[3], **sorted_end[3];
+
+ /* axis used (if any) on the split method */
+ int split_axis;
+
+ /* child partitions calculated during splitting */
+ int child_offset[RTBUILD_MAX_CHILDS + 1];
+
+// int child_sorted_axis; /* -1 if not sorted */
+
+ float bb[6];
+
+ /* current depth */
+ int depth;
+} RTBuilder;
+
+/* used during creation */
+RTBuilder *rtbuild_create(int size);
+void rtbuild_free(RTBuilder *b);
+void rtbuild_add(RTBuilder *b, RayObject *o);
+void rtbuild_done(RTBuilder *b, RayObjectControl *c);
+void rtbuild_merge_bb(RTBuilder *b, float min[3], float max[3]);
+int rtbuild_size(RTBuilder *b);
+
+RayObject *rtbuild_get_primitive(RTBuilder *b, int offset);
+
+/* used during tree reorganization */
+RTBuilder *rtbuild_get_child(RTBuilder *b, int child, RTBuilder *tmp);
+
+/* Calculates child partitions and returns number of efectively needed partitions */
+int rtbuild_get_largest_axis(RTBuilder *b);
+
+//Object partition
+int rtbuild_mean_split(RTBuilder *b, int nchilds, int axis);
+int rtbuild_mean_split_largest_axis(RTBuilder *b, int nchilds);
+
+int rtbuild_heuristic_object_split(RTBuilder *b, int nchilds);
+
+//Space partition
+int rtbuild_median_split(RTBuilder *b, float *separators, int nchilds, int axis);
+int rtbuild_median_split_largest_axis(RTBuilder *b, int nchilds);
+
+
+/* bb utils */
+float bb_area(const float min[3], const float max[3]);
+float bb_volume(const float min[3], const float max[3]);
+int bb_largest_axis(const float min[3], const float max[3]);
+int bb_fits_inside(const float outer_min[3], const float outer_max[3],
+ const float inner_min[3], const float inner_max[3]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RAYOBJECT_RTBUILD_H__ */
diff --git a/source/blender/render/intern/raytrace/rayobject_svbvh.cpp b/source/blender/render/intern/raytrace/rayobject_svbvh.cpp
new file mode 100644
index 00000000000..fcd692fac02
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_svbvh.cpp
@@ -0,0 +1,192 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_svbvh.cpp
+ * \ingroup render
+ */
+
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "vbvh.h"
+#include "svbvh.h"
+#include "reorganize.h"
+
+#ifdef __SSE__
+
+#define DFS_STACK_SIZE 256
+
+struct SVBVHTree {
+ RayObject rayobj;
+
+ SVBVHNode *root;
+ MemArena *node_arena;
+
+ float cost;
+ RTBuilder *builder;
+};
+
+/*
+ * Cost to test N childs
+ */
+struct PackCost {
+ float operator()(int n)
+ {
+ return (n / 4) + ((n % 4) > 2 ? 1 : n % 4);
+ }
+};
+
+
+template<>
+void bvh_done<SVBVHTree>(SVBVHTree *obj)
+{
+ rtbuild_done(obj->builder, &obj->rayobj.control);
+
+ //TODO find a away to exactly calculate the needed memory
+ MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "svbvh arena");
+ BLI_memarena_use_malloc(arena1);
+
+ MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "svbvh arena2");
+ BLI_memarena_use_malloc(arena2);
+ BLI_memarena_use_align(arena2, 16);
+
+ //Build and optimize the tree
+ if (0) {
+ VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
+
+ if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
+ BLI_memarena_free(arena1);
+ BLI_memarena_free(arena2);
+ return;
+ }
+
+ reorganize(root);
+ remove_useless(root, &root);
+ bvh_refit(root);
+
+ pushup(root);
+ pushdown(root);
+ pushup_simd<VBVHNode, 4>(root);
+
+ obj->root = Reorganize_SVBVH<VBVHNode>(arena2).transform(root);
+ }
+ else {
+ //Finds the optimal packing of this tree using a given cost model
+ //TODO this uses quite a lot of memory, find ways to reduce memory usage during building
+ OVBVHNode *root = BuildBinaryVBVH<OVBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
+
+ if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
+ BLI_memarena_free(arena1);
+ BLI_memarena_free(arena2);
+ return;
+ }
+
+ if (root) {
+ VBVH_optimalPackSIMD<OVBVHNode, PackCost>(PackCost()).transform(root);
+ obj->root = Reorganize_SVBVH<OVBVHNode>(arena2).transform(root);
+ }
+ else
+ obj->root = NULL;
+ }
+
+ //Free data
+ BLI_memarena_free(arena1);
+
+ obj->node_arena = arena2;
+ obj->cost = 1.0;
+
+ rtbuild_free(obj->builder);
+ obj->builder = NULL;
+}
+
+template<int StackSize>
+static int intersect(SVBVHTree *obj, Isect *isec)
+{
+ //TODO renable hint support
+ if (RE_rayobject_isAligned(obj->root)) {
+ if (isec->mode == RE_RAY_SHADOW)
+ return svbvh_node_stack_raycast<StackSize, true>(obj->root, isec);
+ else
+ return svbvh_node_stack_raycast<StackSize, false>(obj->root, isec);
+ }
+ else
+ return RE_rayobject_intersect( (RayObject *) obj->root, isec);
+}
+
+template<class Tree>
+static void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *UNUSED(min), float *UNUSED(max))
+{
+ //TODO renable hint support
+ {
+ hint->size = 0;
+ hint->stack[hint->size++] = (RayObject *)tree->root;
+ }
+}
+/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
+template<class Tree, int STACK_SIZE>
+static RayObjectAPI make_api()
+{
+ static RayObjectAPI api =
+ {
+ (RE_rayobject_raycast_callback) ((int (*)(Tree *, Isect *)) & intersect<STACK_SIZE>),
+ (RE_rayobject_add_callback) ((void (*)(Tree *, RayObject *)) & bvh_add<Tree>),
+ (RE_rayobject_done_callback) ((void (*)(Tree *)) & bvh_done<Tree>),
+ (RE_rayobject_free_callback) ((void (*)(Tree *)) & bvh_free<Tree>),
+ (RE_rayobject_merge_bb_callback)((void (*)(Tree *, float *, float *)) & bvh_bb<Tree>),
+ (RE_rayobject_cost_callback) ((float (*)(Tree *)) & bvh_cost<Tree>),
+ (RE_rayobject_hint_bb_callback) ((void (*)(Tree *, LCTSHint *, float *, float *)) & bvh_hint_bb<Tree>)
+ };
+
+ return api;
+}
+
+template<class Tree>
+static RayObjectAPI *bvh_get_api(int maxstacksize)
+{
+ static RayObjectAPI bvh_api256 = make_api<Tree, 1024>();
+
+ if (maxstacksize <= 1024) return &bvh_api256;
+ assert(maxstacksize <= 256);
+ return NULL;
+}
+
+RayObject *RE_rayobject_svbvh_create(int size)
+{
+ return bvh_create_tree<SVBVHTree, DFS_STACK_SIZE>(size);
+}
+
+#else
+
+RayObject *RE_rayobject_svbvh_create(int UNUSED(size))
+{
+ puts("WARNING: SSE disabled at compile time\n");
+ return NULL;
+}
+
+#endif
diff --git a/source/blender/render/intern/raytrace/rayobject_vbvh.cpp b/source/blender/render/intern/raytrace/rayobject_vbvh.cpp
new file mode 100644
index 00000000000..b63a11047dd
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_vbvh.cpp
@@ -0,0 +1,206 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_vbvh.cpp
+ * \ingroup render
+ */
+
+
+int tot_pushup = 0;
+int tot_pushdown = 0;
+int tot_hints = 0;
+
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_global.h"
+
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "rayobject_rtbuild.h"
+
+#include "reorganize.h"
+#include "bvh.h"
+#include "vbvh.h"
+
+#include <queue>
+#include <algorithm>
+
+#define DFS_STACK_SIZE 256
+
+struct VBVHTree {
+ RayObject rayobj;
+ VBVHNode *root;
+ MemArena *node_arena;
+ float cost;
+ RTBuilder *builder;
+};
+
+/*
+ * Cost to test N childs
+ */
+struct PackCost {
+ float operator()(int n)
+ {
+ return n;
+ }
+};
+
+template<>
+void bvh_done<VBVHTree>(VBVHTree *obj)
+{
+ rtbuild_done(obj->builder, &obj->rayobj.control);
+
+ //TODO find a away to exactly calculate the needed memory
+ MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "vbvh arena");
+ BLI_memarena_use_malloc(arena1);
+
+ //Build and optimize the tree
+ if (1) {
+ VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
+ if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
+ BLI_memarena_free(arena1);
+ return;
+ }
+
+ if (root) {
+ reorganize(root);
+ remove_useless(root, &root);
+ bvh_refit(root);
+
+ pushup(root);
+ pushdown(root);
+ obj->root = root;
+ }
+ else
+ obj->root = NULL;
+ }
+ else {
+ /* TODO */
+#if 0
+ MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "vbvh arena2");
+ BLI_memarena_use_malloc(arena2);
+
+ //Finds the optimal packing of this tree using a given cost model
+ //TODO this uses quite a lot of memory, find ways to reduce memory usage during building
+ OVBVHNode *root = BuildBinaryVBVH<OVBVHNode>(arena2).transform(obj->builder);
+ VBVH_optimalPackSIMD<OVBVHNode, PackCost>(PackCost()).transform(root);
+ obj->root = Reorganize_VBVH<OVBVHNode>(arena1).transform(root);
+
+ BLI_memarena_free(arena2);
+#endif
+ }
+
+ //Cleanup
+ rtbuild_free(obj->builder);
+ obj->builder = NULL;
+
+ obj->node_arena = arena1;
+ obj->cost = 1.0;
+}
+
+template<int StackSize>
+static int intersect(VBVHTree *obj, Isect *isec)
+{
+ //TODO renable hint support
+ if (RE_rayobject_isAligned(obj->root)) {
+ if (isec->mode == RE_RAY_SHADOW)
+ return bvh_node_stack_raycast<VBVHNode, StackSize, false, true>(obj->root, isec);
+ else
+ return bvh_node_stack_raycast<VBVHNode, StackSize, false, false>(obj->root, isec);
+ }
+ else
+ return RE_rayobject_intersect( (RayObject *) obj->root, isec);
+}
+
+template<class Tree>
+static void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *UNUSED(min), float *UNUSED(max))
+{
+ //TODO renable hint support
+ {
+ hint->size = 0;
+ hint->stack[hint->size++] = (RayObject *)tree->root;
+ }
+}
+
+#if 0 /* UNUSED */
+static void bfree(VBVHTree *tree)
+{
+ if (tot_pushup + tot_pushdown + tot_hints + tot_moves) {
+ if (G.debug & G_DEBUG) {
+ printf("tot pushups: %d\n", tot_pushup);
+ printf("tot pushdowns: %d\n", tot_pushdown);
+ printf("tot moves: %d\n", tot_moves);
+ printf("tot hints created: %d\n", tot_hints);
+ }
+
+ tot_pushup = 0;
+ tot_pushdown = 0;
+ tot_hints = 0;
+ tot_moves = 0;
+ }
+ bvh_free(tree);
+}
+#endif
+
+/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
+template<class Tree, int STACK_SIZE>
+static RayObjectAPI make_api()
+{
+ static RayObjectAPI api =
+ {
+ (RE_rayobject_raycast_callback) ((int (*)(Tree *, Isect *)) & intersect<STACK_SIZE>),
+ (RE_rayobject_add_callback) ((void (*)(Tree *, RayObject *)) & bvh_add<Tree>),
+ (RE_rayobject_done_callback) ((void (*)(Tree *)) & bvh_done<Tree>),
+ (RE_rayobject_free_callback) ((void (*)(Tree *)) & bvh_free<Tree>),
+ (RE_rayobject_merge_bb_callback)((void (*)(Tree *, float *, float *)) & bvh_bb<Tree>),
+ (RE_rayobject_cost_callback) ((float (*)(Tree *)) & bvh_cost<Tree>),
+ (RE_rayobject_hint_bb_callback) ((void (*)(Tree *, LCTSHint *, float *, float *)) & bvh_hint_bb<Tree>)
+ };
+
+ return api;
+}
+
+template<class Tree>
+RayObjectAPI *bvh_get_api(int maxstacksize)
+{
+ static RayObjectAPI bvh_api256 = make_api<Tree, 1024>();
+
+ if (maxstacksize <= 1024) return &bvh_api256;
+ assert(maxstacksize <= 256);
+ return 0;
+}
+
+RayObject *RE_rayobject_vbvh_create(int size)
+{
+ return bvh_create_tree<VBVHTree, DFS_STACK_SIZE>(size);
+}
diff --git a/source/blender/render/intern/raytrace/reorganize.h b/source/blender/render/intern/raytrace/reorganize.h
new file mode 100644
index 00000000000..3fdd3363edb
--- /dev/null
+++ b/source/blender/render/intern/raytrace/reorganize.h
@@ -0,0 +1,513 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/reorganize.h
+ * \ingroup render
+ */
+
+
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <queue>
+#include <vector>
+
+#include "BKE_global.h"
+
+#ifdef _WIN32
+# ifdef INFINITY
+# undef INFINITY
+# endif
+# define INFINITY FLT_MAX // in mingw math.h: (1.0F/0.0F). This generates compile error, though.
+#endif
+
+extern int tot_pushup;
+extern int tot_pushdown;
+
+#if !defined(INFINITY) && defined(HUGE_VAL)
+#define INFINITY HUGE_VAL
+#endif
+
+template<class Node>
+static bool node_fits_inside(Node *a, Node *b)
+{
+ return bb_fits_inside(b->bb, b->bb + 3, a->bb, a->bb + 3);
+}
+
+template<class Node>
+static void reorganize_find_fittest_parent(Node *tree, Node *node, std::pair<float, Node *> &cost)
+{
+ std::queue<Node *> q;
+ q.push(tree);
+
+ while (!q.empty()) {
+ Node *parent = q.front();
+ q.pop();
+
+ if (parent == node) continue;
+ if (node_fits_inside(node, parent) && RE_rayobject_isAligned(parent->child) ) {
+ float pcost = bb_area(parent->bb, parent->bb + 3);
+ cost = std::min(cost, std::make_pair(pcost, parent) );
+ for (Node *child = parent->child; child; child = child->sibling)
+ q.push(child);
+ }
+ }
+}
+
+template<class Node>
+static void reorganize(Node *root)
+{
+ std::queue<Node *> q;
+
+ q.push(root);
+ while (!q.empty()) {
+ Node *node = q.front();
+ q.pop();
+
+ if (RE_rayobject_isAligned(node->child)) {
+ for (Node **prev = &node->child; *prev; ) {
+ assert(RE_rayobject_isAligned(*prev));
+ q.push(*prev);
+
+ std::pair<float, Node *> best(FLT_MAX, root);
+ reorganize_find_fittest_parent(root, *prev, best);
+
+ if (best.second == node) {
+ //Already inside the fitnest BB
+ prev = &(*prev)->sibling;
+ }
+ else {
+ Node *tmp = *prev;
+ *prev = (*prev)->sibling;
+
+ tmp->sibling = best.second->child;
+ best.second->child = tmp;
+ }
+
+
+ }
+ }
+ if (node != root) {
+ }
+ }
+}
+
+/*
+ * Prunes useless nodes from trees:
+ * erases nodes with total amount of primitives = 0
+ * prunes nodes with only one child (except if that child is a primitive)
+ */
+template<class Node>
+static void remove_useless(Node *node, Node **new_node)
+{
+ if (RE_rayobject_isAligned(node->child) ) {
+
+ for (Node **prev = &node->child; *prev; ) {
+ Node *next = (*prev)->sibling;
+ remove_useless(*prev, prev);
+ if (*prev == NULL)
+ *prev = next;
+ else {
+ (*prev)->sibling = next;
+ prev = &((*prev)->sibling);
+ }
+ }
+ }
+ if (node->child) {
+ if (RE_rayobject_isAligned(node->child) && node->child->sibling == 0)
+ *new_node = node->child;
+ }
+ else if (node->child == NULL) {
+ *new_node = NULL;
+ }
+}
+
+/*
+ * Minimizes expected number of BBtest by colapsing nodes
+ * it uses surface area heuristic for determining whether a node should be colapsed
+ */
+template<class Node>
+static void pushup(Node *parent)
+{
+ if (is_leaf(parent)) return;
+
+ float p_area = bb_area(parent->bb, parent->bb + 3);
+ Node **prev = &parent->child;
+ for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; ) {
+ const float c_area = bb_area(child->bb, child->bb + 3);
+ const int nchilds = count_childs(child);
+ float original_cost = ((p_area != 0.0f) ? (c_area / p_area) * nchilds : 1.0f) + 1;
+ float flatten_cost = nchilds;
+ if (flatten_cost < original_cost && nchilds >= 2) {
+ append_sibling(child, child->child);
+ child = child->sibling;
+ *prev = child;
+
+// *prev = child->child;
+// append_sibling( *prev, child->sibling );
+// child = *prev;
+ tot_pushup++;
+ }
+ else {
+ *prev = child;
+ prev = &(*prev)->sibling;
+ child = *prev;
+ }
+ }
+
+ for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
+ pushup(child);
+}
+
+/*
+ * try to optimize number of childs to be a multiple of SSize
+ */
+template<class Node, int SSize>
+static void pushup_simd(Node *parent)
+{
+ if (is_leaf(parent)) return;
+
+ int n = count_childs(parent);
+
+ Node **prev = &parent->child;
+ for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; ) {
+ int cn = count_childs(child);
+ if (cn - 1 <= (SSize - (n % SSize) ) % SSize && RE_rayobject_isAligned(child->child) ) {
+ n += (cn - 1);
+ append_sibling(child, child->child);
+ child = child->sibling;
+ *prev = child;
+ }
+ else {
+ *prev = child;
+ prev = &(*prev)->sibling;
+ child = *prev;
+ }
+ }
+
+ for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
+ pushup_simd<Node, SSize>(child);
+}
+
+
+/*
+ * Pushdown
+ * makes sure no child fits inside any of its sibling
+ */
+template<class Node>
+static void pushdown(Node *parent)
+{
+ Node **s_child = &parent->child;
+ Node *child = parent->child;
+
+ while (child && RE_rayobject_isAligned(child)) {
+ Node *next = child->sibling;
+ Node **next_s_child = &child->sibling;
+
+ //assert(bb_fits_inside(parent->bb, parent->bb+3, child->bb, child->bb+3));
+
+ for (Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling)
+ if (child != i && bb_fits_inside(i->bb, i->bb + 3, child->bb, child->bb + 3) && RE_rayobject_isAligned(i->child)) {
+// todo optimize (should the one with the smallest area?)
+// float ia = bb_area(i->bb, i->bb+3)
+// if (child->i)
+ *s_child = child->sibling;
+ child->sibling = i->child;
+ i->child = child;
+ next_s_child = s_child;
+
+ tot_pushdown++;
+ break;
+ }
+ child = next;
+ s_child = next_s_child;
+ }
+
+ for (Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling) {
+ pushdown(i);
+ }
+}
+
+
+/*
+ * BVH refit
+ * readjust nodes BB (useful if nodes childs where modified)
+ */
+template<class Node>
+static float bvh_refit(Node *node)
+{
+ if (is_leaf(node)) return 0;
+ if (is_leaf(node->child)) return 0;
+
+ float total = 0;
+
+ for (Node *child = node->child; child; child = child->sibling)
+ total += bvh_refit(child);
+
+ float old_area = bb_area(node->bb, node->bb + 3);
+ INIT_MINMAX(node->bb, node->bb + 3);
+ for (Node *child = node->child; child; child = child->sibling) {
+ DO_MIN(child->bb, node->bb);
+ DO_MAX(child->bb + 3, node->bb + 3);
+ }
+ total += old_area - bb_area(node->bb, node->bb + 3);
+ return total;
+}
+
+
+/*
+ * this finds the best way to packing a tree according to a given test cost function
+ * with the purpose to reduce the expected cost (eg.: number of BB tests).
+ */
+#include <vector>
+#define MAX_CUT_SIZE 4 /* svbvh assumes max 4 children! */
+#define MAX_OPTIMIZE_CHILDS MAX_CUT_SIZE
+
+#define CUT_SIZE_IS_VALID(cut_size) ((cut_size) < MAX_CUT_SIZE && (cut_size) >= 0)
+#define CUT_SIZE_INVALID -1
+
+
+struct OVBVHNode {
+ float bb[6];
+
+ OVBVHNode *child;
+ OVBVHNode *sibling;
+
+ /*
+ * Returns min cost to represent the subtree starting at the given node,
+ * allowing it to have a given cutsize
+ */
+ float cut_cost[MAX_CUT_SIZE];
+ float get_cost(int cutsize)
+ {
+ assert(CUT_SIZE_IS_VALID(cutsize - 1));
+ return cut_cost[cutsize - 1];
+ }
+
+ /*
+ * This saves the cut size of this child, when parent is reaching
+ * its minimum cut with the given cut size
+ */
+ int cut_size[MAX_CUT_SIZE];
+ int get_cut_size(int parent_cut_size)
+ {
+ assert(CUT_SIZE_IS_VALID(parent_cut_size - 1));
+ return cut_size[parent_cut_size - 1];
+ }
+
+ /*
+ * Reorganize the node based on calculated cut costs
+ */
+ int best_cutsize;
+ void set_cut(int cutsize, OVBVHNode ***cut)
+ {
+ if (cutsize == 1) {
+ **cut = this;
+ *cut = &(**cut)->sibling;
+ }
+ else {
+ if (cutsize > MAX_CUT_SIZE) {
+ for (OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
+ child->set_cut(1, cut);
+ cutsize--;
+ }
+ assert(cutsize == 0);
+ }
+ else {
+ for (OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
+ child->set_cut(child->get_cut_size(cutsize), cut);
+ }
+ }
+ }
+ }
+
+ void optimize()
+ {
+ if (RE_rayobject_isAligned(this->child)) {
+ //Calc new childs
+ if (this->best_cutsize != CUT_SIZE_INVALID) {
+ OVBVHNode **cut = &(this->child);
+ set_cut(this->best_cutsize, &cut);
+ *cut = NULL;
+ }
+
+ //Optimize new childs
+ for (OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
+ child->optimize();
+ }
+ }
+};
+
+/*
+ * Calculates an optimal SIMD packing
+ *
+ */
+template<class Node, class TestCost>
+struct VBVH_optimalPackSIMD {
+ TestCost testcost;
+
+ VBVH_optimalPackSIMD(TestCost testcost)
+ {
+ this->testcost = testcost;
+ }
+
+ /*
+ * calc best cut on a node
+ */
+ struct calc_best {
+ Node *child[MAX_OPTIMIZE_CHILDS];
+ float child_hit_prob[MAX_OPTIMIZE_CHILDS];
+
+ calc_best(Node *node)
+ {
+ int nchilds = 0;
+ //Fetch childs and needed data
+ {
+ float parent_area = bb_area(node->bb, node->bb + 3);
+ for (Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
+ this->child[nchilds] = child;
+ this->child_hit_prob[nchilds] = (parent_area != 0.0f) ? bb_area(child->bb, child->bb + 3) / parent_area : 1.0f;
+ nchilds++;
+ }
+
+ assert(nchilds >= 2 && nchilds <= MAX_OPTIMIZE_CHILDS);
+ }
+
+
+ //Build DP table to find minimum cost to represent this node with a given cutsize
+ int bt[MAX_OPTIMIZE_CHILDS + 1][MAX_CUT_SIZE + 1]; //backtrace table
+ float cost[MAX_OPTIMIZE_CHILDS + 1][MAX_CUT_SIZE + 1]; //cost table (can be reduced to float[2][MAX_CUT_COST])
+
+ for (int i = 0; i <= nchilds; i++) {
+ for (int j = 0; j <= MAX_CUT_SIZE; j++) {
+ cost[i][j] = INFINITY;
+ }
+ }
+
+ cost[0][0] = 0;
+
+ for (int i = 1; i <= nchilds; i++) {
+ for (int size = i - 1; size /*+(nchilds-i)*/ <= MAX_CUT_SIZE; size++) {
+ for (int cut = 1; cut + size /*+(nchilds-i)*/ <= MAX_CUT_SIZE; cut++) {
+ float new_cost = cost[i - 1][size] + child_hit_prob[i - 1] * child[i - 1]->get_cost(cut);
+ if (new_cost < cost[i][size + cut]) {
+ cost[i][size + cut] = new_cost;
+ bt[i][size + cut] = cut;
+ }
+ }
+ }
+ }
+
+ /* Save the ways to archive the minimum cost with a given cutsize */
+ for (int i = nchilds; i <= MAX_CUT_SIZE; i++) {
+ node->cut_cost[i - 1] = cost[nchilds][i];
+ if (cost[nchilds][i] < INFINITY) {
+ int current_size = i;
+ for (int j = nchilds; j > 0; j--) {
+ child[j - 1]->cut_size[i - 1] = bt[j][current_size];
+ current_size -= bt[j][current_size];
+ }
+ }
+ }
+ }
+ };
+
+ void calc_costs(Node *node)
+ {
+
+ if (RE_rayobject_isAligned(node->child) ) {
+ int nchilds = 0;
+ for (Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
+ calc_costs(child);
+ nchilds++;
+ }
+
+ for (int i = 0; i < MAX_CUT_SIZE; i++)
+ node->cut_cost[i] = INFINITY;
+
+ //We are not allowed to look on nodes with with so many childs
+ if (nchilds > MAX_CUT_SIZE) {
+ float cost = 0;
+
+ float parent_area = bb_area(node->bb, node->bb + 3);
+ for (Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
+ cost += ((parent_area != 0.0f) ? (bb_area(child->bb, child->bb + 3) / parent_area) : 1.0f) * child->get_cost(1);
+ }
+
+ cost += testcost(nchilds);
+ node->cut_cost[0] = cost;
+ node->best_cutsize = nchilds;
+ }
+ else {
+ calc_best calc(node);
+
+ //calc expected cost if we optimaly pack this node
+ for (int cutsize = nchilds; cutsize <= MAX_CUT_SIZE; cutsize++) {
+ float m = node->get_cost(cutsize) + testcost(cutsize);
+ if (m < node->cut_cost[0]) {
+ node->cut_cost[0] = m;
+ node->best_cutsize = cutsize;
+ }
+ }
+ }
+
+ if (node->cut_cost[0] == INFINITY) {
+ node->best_cutsize = CUT_SIZE_INVALID;
+ }
+ }
+ else {
+ node->cut_cost[0] = 1.0f;
+ for (int i = 1; i < MAX_CUT_SIZE; i++)
+ node->cut_cost[i] = INFINITY;
+
+ /* node->best_cutsize can remain unset here */
+ }
+ }
+
+ Node *transform(Node *node)
+ {
+ if (RE_rayobject_isAligned(node->child)) {
+#ifdef DEBUG
+ static int num = 0;
+ bool first = false;
+ if (num == 0) { num++; first = true; }
+#endif
+
+ calc_costs(node);
+
+#ifdef DEBUG
+ if (first && G.debug) {
+ printf("expected cost = %f (%d)\n", node->cut_cost[0], node->best_cutsize);
+ }
+#endif
+ node->optimize();
+ }
+ return node;
+ }
+};
diff --git a/source/blender/render/intern/raytrace/svbvh.h b/source/blender/render/intern/raytrace/svbvh.h
new file mode 100644
index 00000000000..0a5690deb46
--- /dev/null
+++ b/source/blender/render/intern/raytrace/svbvh.h
@@ -0,0 +1,317 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/svbvh.h
+ * \ingroup render
+ */
+
+#ifndef __SVBVH_H__
+#define __SVBVH_H__
+
+#ifdef __SSE__
+
+#include "bvh.h"
+#include "BLI_memarena.h"
+#include <algorithm>
+
+struct SVBVHNode {
+ float child_bb[24];
+ SVBVHNode *child[4];
+ int nchilds;
+};
+
+static int svbvh_bb_intersect_test_simd4(const Isect *isec, const __m128 *bb_group)
+{
+ const __m128 tmin0 = _mm_setzero_ps();
+ const __m128 tmax0 = _mm_set_ps1(isec->dist);
+
+ const __m128 start0 = _mm_set_ps1(isec->start[0]);
+ const __m128 start1 = _mm_set_ps1(isec->start[1]);
+ const __m128 start2 = _mm_set_ps1(isec->start[2]);
+ const __m128 sub0 = _mm_sub_ps(bb_group[isec->bv_index[0]], start0);
+ const __m128 sub1 = _mm_sub_ps(bb_group[isec->bv_index[1]], start0);
+ const __m128 sub2 = _mm_sub_ps(bb_group[isec->bv_index[2]], start1);
+ const __m128 sub3 = _mm_sub_ps(bb_group[isec->bv_index[3]], start1);
+ const __m128 sub4 = _mm_sub_ps(bb_group[isec->bv_index[4]], start2);
+ const __m128 sub5 = _mm_sub_ps(bb_group[isec->bv_index[5]], start2);
+ const __m128 idot_axis0 = _mm_set_ps1(isec->idot_axis[0]);
+ const __m128 idot_axis1 = _mm_set_ps1(isec->idot_axis[1]);
+ const __m128 idot_axis2 = _mm_set_ps1(isec->idot_axis[2]);
+ const __m128 mul0 = _mm_mul_ps(sub0, idot_axis0);
+ const __m128 mul1 = _mm_mul_ps(sub1, idot_axis0);
+ const __m128 mul2 = _mm_mul_ps(sub2, idot_axis1);
+ const __m128 mul3 = _mm_mul_ps(sub3, idot_axis1);
+ const __m128 mul4 = _mm_mul_ps(sub4, idot_axis2);
+ const __m128 mul5 = _mm_mul_ps(sub5, idot_axis2);
+ const __m128 tmin1 = _mm_max_ps(tmin0, mul0);
+ const __m128 tmax1 = _mm_min_ps(tmax0, mul1);
+ const __m128 tmin2 = _mm_max_ps(tmin1, mul2);
+ const __m128 tmax2 = _mm_min_ps(tmax1, mul3);
+ const __m128 tmin3 = _mm_max_ps(tmin2, mul4);
+ const __m128 tmax3 = _mm_min_ps(tmax2, mul5);
+
+ return _mm_movemask_ps(_mm_cmpge_ps(tmax3, tmin3));
+}
+
+static int svbvh_bb_intersect_test(const Isect *isec, const float *_bb)
+{
+ const float *bb = _bb;
+
+ float t1x = (bb[isec->bv_index[0]] - isec->start[0]) * isec->idot_axis[0];
+ float t2x = (bb[isec->bv_index[1]] - isec->start[0]) * isec->idot_axis[0];
+ float t1y = (bb[isec->bv_index[2]] - isec->start[1]) * isec->idot_axis[1];
+ float t2y = (bb[isec->bv_index[3]] - isec->start[1]) * isec->idot_axis[1];
+ float t1z = (bb[isec->bv_index[4]] - isec->start[2]) * isec->idot_axis[2];
+ float t2z = (bb[isec->bv_index[5]] - isec->start[2]) * isec->idot_axis[2];
+
+ RE_RC_COUNT(isec->raycounter->bb.test);
+
+ if (t1x > t2y || t2x < t1y || t1x > t2z || t2x < t1z || t1y > t2z || t2y < t1z) return 0;
+ if (t2x < 0.0f || t2y < 0.0f || t2z < 0.0f) return 0;
+ if (t1x > isec->dist || t1y > isec->dist || t1z > isec->dist) return 0;
+
+ RE_RC_COUNT(isec->raycounter->bb.hit);
+
+ return 1;
+}
+
+static bool svbvh_node_is_leaf(const SVBVHNode *node)
+{
+ return !RE_rayobject_isAligned(node);
+}
+
+template<int MAX_STACK_SIZE, bool SHADOW>
+static int svbvh_node_stack_raycast(SVBVHNode *root, Isect *isec)
+{
+ SVBVHNode *stack[MAX_STACK_SIZE], *node;
+ int hit = 0, stack_pos = 0;
+
+ stack[stack_pos++] = root;
+
+ while (stack_pos) {
+ node = stack[--stack_pos];
+
+ if (!svbvh_node_is_leaf(node)) {
+ int nchilds = node->nchilds;
+
+ if (nchilds == 4) {
+ float *child_bb = node->child_bb;
+ int res = svbvh_bb_intersect_test_simd4(isec, ((__m128 *) (child_bb)));
+ SVBVHNode **child = node->child;
+
+ RE_RC_COUNT(isec->raycounter->simd_bb.test);
+
+ if (res & 1) { stack[stack_pos++] = child[0]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
+ if (res & 2) { stack[stack_pos++] = child[1]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
+ if (res & 4) { stack[stack_pos++] = child[2]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
+ if (res & 8) { stack[stack_pos++] = child[3]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
+ }
+ else {
+ float *child_bb = node->child_bb;
+ SVBVHNode **child = node->child;
+ int i;
+
+ for (i = 0; i < nchilds; i++) {
+ if (svbvh_bb_intersect_test(isec, (float *)child_bb + 6 * i)) {
+ stack[stack_pos++] = child[i];
+ }
+ }
+ }
+ }
+ else {
+ hit |= RE_rayobject_intersect((RayObject *)node, isec);
+ if (SHADOW && hit) break;
+ }
+ }
+
+ return hit;
+}
+
+
+template<>
+inline void bvh_node_merge_bb<SVBVHNode>(SVBVHNode *node, float min[3], float max[3])
+{
+ if (is_leaf(node)) {
+ RE_rayobject_merge_bb((RayObject *)node, min, max);
+ }
+ else {
+ int i;
+ for (i = 0; i + 4 <= node->nchilds; i += 4) {
+ float *res = node->child_bb + 6 * i;
+ for (int j = 0; j < 3; j++) {
+ min[j] = min_ff(min[j],
+ min_ffff(res[4 * j + 0],
+ res[4 * j + 1],
+ res[4 * j + 2],
+ res[4 * j + 3]));
+ }
+ for (int j = 0; j < 3; j++) {
+ max[j] = max_ff(max[j],
+ max_ffff(res[4 * (j + 3) + 0],
+ res[4 * (j + 3) + 1],
+ res[4 * (j + 3) + 2],
+ res[4 * (j + 3) + 3]));
+ }
+ }
+
+ for (; i < node->nchilds; i++) {
+ DO_MIN(node->child_bb + 6 * i, min);
+ DO_MAX(node->child_bb + 3 + 6 * i, max);
+ }
+ }
+}
+
+
+
+/*
+ * Builds a SVBVH tree form a VBVHTree
+ */
+template<class OldNode>
+struct Reorganize_SVBVH {
+ MemArena *arena;
+
+ float childs_per_node;
+ int nodes_with_childs[16];
+ int useless_bb;
+ int nodes;
+
+ Reorganize_SVBVH(MemArena *a)
+ {
+ arena = a;
+ nodes = 0;
+ childs_per_node = 0;
+ useless_bb = 0;
+
+ for (int i = 0; i < 16; i++) {
+ nodes_with_childs[i] = 0;
+ }
+ }
+
+ ~Reorganize_SVBVH()
+ {
+#if 0
+ {
+ printf("%f childs per node\n", childs_per_node / nodes);
+ printf("%d childs BB are useless\n", useless_bb);
+ for (int i = 0; i < 16; i++) {
+ printf("%i childs per node: %d/%d = %f\n", i, nodes_with_childs[i], nodes, nodes_with_childs[i] / float(nodes));
+ }
+ }
+#endif
+ }
+
+ SVBVHNode *create_node(int nchilds)
+ {
+ SVBVHNode *node = (SVBVHNode *)BLI_memarena_alloc(arena, sizeof(SVBVHNode));
+ node->nchilds = nchilds;
+
+ return node;
+ }
+
+ void copy_bb(float bb[6], const float old_bb[6])
+ {
+ std::copy(old_bb, old_bb + 6, bb);
+ }
+
+ void prepare_for_simd(SVBVHNode *node)
+ {
+ int i = 0;
+ while (i + 4 <= node->nchilds) {
+ float vec_tmp[4 * 6];
+ float *res = node->child_bb + 6 * i;
+ std::copy(res, res + 6 * 4, vec_tmp);
+
+ for (int j = 0; j < 6; j++) {
+ res[4 * j + 0] = vec_tmp[6 * 0 + j];
+ res[4 * j + 1] = vec_tmp[6 * 1 + j];
+ res[4 * j + 2] = vec_tmp[6 * 2 + j];
+ res[4 * j + 3] = vec_tmp[6 * 3 + j];
+ }
+
+ i += 4;
+ }
+ }
+
+ /* amt must be power of two */
+ inline int padup(int num, int amt)
+ {
+ return ((num + (amt - 1)) & ~(amt - 1));
+ }
+
+ SVBVHNode *transform(OldNode *old)
+ {
+ if (is_leaf(old))
+ return (SVBVHNode *)old;
+ if (is_leaf(old->child))
+ return (SVBVHNode *)old->child;
+
+ int nchilds = count_childs(old);
+ int alloc_childs = nchilds;
+ if (nchilds % 4 > 2)
+ alloc_childs = padup(nchilds, 4);
+
+ SVBVHNode *node = create_node(alloc_childs);
+
+ childs_per_node += nchilds;
+ nodes++;
+ if (nchilds < 16)
+ nodes_with_childs[nchilds]++;
+
+ useless_bb += alloc_childs - nchilds;
+ while (alloc_childs > nchilds) {
+ const static float def_bb[6] = {FLT_MAX, FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX};
+ alloc_childs--;
+ node->child[alloc_childs] = NULL;
+ copy_bb(node->child_bb + alloc_childs * 6, def_bb);
+ }
+
+ int i = nchilds;
+ for (OldNode *o_child = old->child; o_child; o_child = o_child->sibling) {
+ i--;
+ node->child[i] = transform(o_child);
+ if (is_leaf(o_child)) {
+ float bb[6];
+ INIT_MINMAX(bb, bb + 3);
+ RE_rayobject_merge_bb((RayObject *)o_child, bb, bb + 3);
+ copy_bb(node->child_bb + i * 6, bb);
+ break;
+ }
+ else {
+ copy_bb(node->child_bb + i * 6, o_child->bb);
+ }
+ }
+ assert(i == 0);
+
+ prepare_for_simd(node);
+
+ return node;
+ }
+};
+
+#endif /* __SSE__ */
+
+#endif /* __SVBVH_H__ */
diff --git a/source/blender/render/intern/raytrace/vbvh.h b/source/blender/render/intern/raytrace/vbvh.h
new file mode 100644
index 00000000000..0b0bbd19116
--- /dev/null
+++ b/source/blender/render/intern/raytrace/vbvh.h
@@ -0,0 +1,238 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): André Pinto.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/vbvh.h
+ * \ingroup render
+ */
+
+
+#include <assert.h>
+#include <algorithm>
+
+#include "BLI_memarena.h"
+
+#include "rayobject_rtbuild.h"
+
+/*
+ * VBVHNode represents a BVHNode with support for a variable number of childrens
+ */
+struct VBVHNode {
+ float bb[6];
+
+ VBVHNode *child;
+ VBVHNode *sibling;
+};
+
+
+/*
+ * Push nodes (used on dfs)
+ */
+template<class Node>
+inline static void bvh_node_push_childs(Node *node, Isect *UNUSED(isec), Node **stack, int &stack_pos)
+{
+ Node *child = node->child;
+
+ if (is_leaf(child)) {
+ stack[stack_pos++] = child;
+ }
+ else {
+ while (child) {
+ /* Skips BB tests on primitives */
+#if 0
+ if (is_leaf(child->child)) {
+ stack[stack_pos++] = child->child;
+ }
+ else
+#endif
+ {
+ stack[stack_pos++] = child;
+ }
+
+ child = child->sibling;
+ }
+ }
+}
+
+
+template<class Node>
+static int count_childs(Node *parent)
+{
+ int n = 0;
+ for (Node *i = parent->child; i; i = i->sibling) {
+ n++;
+ if (is_leaf(i))
+ break;
+ }
+
+ return n;
+}
+
+
+template<class Node>
+static void append_sibling(Node *node, Node *sibling)
+{
+ while (node->sibling)
+ node = node->sibling;
+
+ node->sibling = sibling;
+}
+
+
+/*
+ * Builds a binary VBVH from a rtbuild
+ */
+template<class Node>
+struct BuildBinaryVBVH {
+ MemArena *arena;
+ RayObjectControl *control;
+
+ void test_break()
+ {
+ if (RE_rayobjectcontrol_test_break(control))
+ throw "Stop";
+ }
+
+ BuildBinaryVBVH(MemArena *a, RayObjectControl *c)
+ {
+ arena = a;
+ control = c;
+ }
+
+ Node *create_node()
+ {
+ Node *node = (Node *)BLI_memarena_alloc(arena, sizeof(Node) );
+ assert(RE_rayobject_isAligned(node));
+
+ node->sibling = NULL;
+ node->child = NULL;
+
+ return node;
+ }
+
+ int rtbuild_split(RTBuilder *builder)
+ {
+ return ::rtbuild_heuristic_object_split(builder, 2);
+ }
+
+ Node *transform(RTBuilder *builder)
+ {
+ try
+ {
+ return _transform(builder);
+
+ } catch (...)
+ {
+ }
+ return NULL;
+ }
+
+ Node *_transform(RTBuilder *builder)
+ {
+ int size = rtbuild_size(builder);
+
+ if (size == 0) {
+ return NULL;
+ }
+ else if (size == 1) {
+ Node *node = create_node();
+ INIT_MINMAX(node->bb, node->bb + 3);
+ rtbuild_merge_bb(builder, node->bb, node->bb + 3);
+ node->child = (Node *) rtbuild_get_primitive(builder, 0);
+ return node;
+ }
+ else {
+ test_break();
+
+ Node *node = create_node();
+
+ Node **child = &node->child;
+
+ int nc = rtbuild_split(builder);
+ INIT_MINMAX(node->bb, node->bb + 3);
+
+ assert(nc == 2);
+ for (int i = 0; i < nc; i++) {
+ RTBuilder tmp;
+ rtbuild_get_child(builder, i, &tmp);
+
+ *child = _transform(&tmp);
+ DO_MIN((*child)->bb, node->bb);
+ DO_MAX((*child)->bb + 3, node->bb + 3);
+ child = &((*child)->sibling);
+ }
+
+ *child = NULL;
+ return node;
+ }
+ }
+};
+
+#if 0
+template<class Tree, class OldNode>
+struct Reorganize_VBVH {
+ Tree *tree;
+
+ Reorganize_VBVH(Tree *t)
+ {
+ tree = t;
+ }
+
+ VBVHNode *create_node()
+ {
+ VBVHNode *node = (VBVHNode *)BLI_memarena_alloc(tree->node_arena, sizeof(VBVHNode));
+ return node;
+ }
+
+ void copy_bb(VBVHNode *node, OldNode *old)
+ {
+ std::copy(old->bb, old->bb + 6, node->bb);
+ }
+
+ VBVHNode *transform(OldNode *old)
+ {
+ if (is_leaf(old))
+ return (VBVHNode *)old;
+
+ VBVHNode *node = create_node();
+ VBVHNode **child_ptr = &node->child;
+ node->sibling = 0;
+
+ copy_bb(node, old);
+
+ for (OldNode *o_child = old->child; o_child; o_child = o_child->sibling)
+ {
+ VBVHNode *n_child = transform(o_child);
+ *child_ptr = n_child;
+ if (is_leaf(n_child)) return node;
+ child_ptr = &n_child->sibling;
+ }
+ *child_ptr = 0;
+
+ return node;
+ }
+};
+#endif
diff --git a/source/blender/render/intern/source/bake.c b/source/blender/render/intern/source/bake.c
new file mode 100644
index 00000000000..4a7962b1776
--- /dev/null
+++ b/source/blender/render/intern/source/bake.c
@@ -0,0 +1,1342 @@
+/*
+ * ***** 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.
+ *
+ * Contributors: 2004/2005/2006 Blender Foundation, full recode
+ * Contributors: Vertex color baking, Copyright 2011 AutoCRC
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/bake.c
+ * \ingroup render
+ */
+
+
+/* system includes */
+#include <stdio.h>
+#include <string.h>
+
+/* External modules: */
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_image_types.h"
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_scene.h"
+#include "BKE_library.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+#include "IMB_colormanagement.h"
+
+/* local include */
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "render_types.h"
+#include "renderdatabase.h"
+#include "shading.h"
+#include "zbuf.h"
+
+#include "PIL_time.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+
+/* ************************* bake ************************ */
+
+
+typedef struct BakeShade {
+ int thread;
+
+ ShadeSample ssamp;
+ ObjectInstanceRen *obi;
+ VlakRen *vlr;
+
+ ZSpan *zspan;
+ Image *ima;
+ ImBuf *ibuf;
+
+ int rectx, recty, quad, type, vdone;
+ bool ready;
+
+ float dir[3];
+ Object *actob;
+
+ /* Output: vertex color or image data. If vcol is not NULL, rect and
+ * rect_float should be NULL. */
+ MPoly *mpoly;
+ MLoop *mloop;
+ MLoopCol *vcol;
+
+ unsigned int *rect;
+ float *rect_float;
+
+ /* displacement buffer used for normalization with unknown maximal distance */
+ bool use_displacement_buffer;
+ float *displacement_buffer;
+ float displacement_min, displacement_max;
+
+ bool use_mask;
+ char *rect_mask; /* bake pixel mask */
+
+ float dxco[3], dyco[3];
+
+ short *do_update;
+
+ struct ColorSpace *rect_colorspace;
+} BakeShade;
+
+static void bake_set_shade_input(ObjectInstanceRen *obi, VlakRen *vlr, ShadeInput *shi, int quad, int UNUSED(isect), int x, int y, float u, float v)
+{
+ if (quad)
+ shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3);
+ else
+ shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2);
+
+ /* cache for shadow */
+ shi->samplenr = R.shadowsamplenr[shi->thread]++;
+
+ shi->mask = 0xFFFF; /* all samples */
+
+ shi->u = -u;
+ shi->v = -v;
+ shi->xs = x;
+ shi->ys = y;
+
+ shade_input_set_uv(shi);
+ shade_input_set_normals(shi);
+
+ /* no normal flip */
+ if (shi->flippednor)
+ shade_input_flip_normals(shi);
+
+ /* set up view vector to look right at the surface (note that the normal
+ * is negated in the renderer so it does not need to be done here) */
+ shi->view[0] = shi->vn[0];
+ shi->view[1] = shi->vn[1];
+ shi->view[2] = shi->vn[2];
+}
+
+static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(quad), int x, int y, float UNUSED(u), float UNUSED(v), float *tvn, float *ttang)
+{
+ BakeShade *bs = handle;
+ ShadeSample *ssamp = &bs->ssamp;
+ ShadeResult shr;
+ VlakRen *vlr = shi->vlr;
+
+ shade_input_init_material(shi);
+
+ if (bs->type == RE_BAKE_AO) {
+ ambient_occlusion(shi);
+
+ if (R.r.bake_flag & R_BAKE_NORMALIZE) {
+ copy_v3_v3(shr.combined, shi->ao);
+ }
+ else {
+ zero_v3(shr.combined);
+ environment_lighting_apply(shi, &shr);
+ }
+ }
+ else {
+ if (bs->type == RE_BAKE_SHADOW) /* Why do shadows set the color anyhow?, ignore material color for baking */
+ shi->r = shi->g = shi->b = 1.0f;
+
+ shade_input_set_shade_texco(shi);
+
+ /* only do AO for a full bake (and obviously AO bakes)
+ * AO for light bakes is a leftover and might not be needed */
+ if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_AO, RE_BAKE_LIGHT))
+ shade_samples_do_AO(ssamp);
+
+ if (shi->mat->nodetree && shi->mat->use_nodes) {
+ ntreeShaderExecTree(shi->mat->nodetree, shi, &shr);
+ shi->mat = vlr->mat; /* shi->mat is being set in nodetree */
+ }
+ else
+ shade_material_loop(shi, &shr);
+
+ if (bs->type == RE_BAKE_NORMALS) {
+ float nor[3];
+
+ copy_v3_v3(nor, shi->vn);
+
+ if (R.r.bake_normal_space == R_BAKE_SPACE_CAMERA) {
+ /* pass */
+ }
+ else if (R.r.bake_normal_space == R_BAKE_SPACE_TANGENT) {
+ float mat[3][3], imat[3][3];
+
+ /* bitangent */
+ if (tvn && ttang) {
+ copy_v3_v3(mat[0], ttang);
+ cross_v3_v3v3(mat[1], tvn, ttang);
+ mul_v3_fl(mat[1], ttang[3]);
+ copy_v3_v3(mat[2], tvn);
+ }
+ else {
+ copy_v3_v3(mat[0], shi->nmaptang);
+ cross_v3_v3v3(mat[1], shi->nmapnorm, shi->nmaptang);
+ mul_v3_fl(mat[1], shi->nmaptang[3]);
+ copy_v3_v3(mat[2], shi->nmapnorm);
+ }
+
+ invert_m3_m3(imat, mat);
+ mul_m3_v3(imat, nor);
+ }
+ else if (R.r.bake_normal_space == R_BAKE_SPACE_OBJECT)
+ mul_mat3_m4_v3(ob->imat_ren, nor); /* ob->imat_ren includes viewinv! */
+ else if (R.r.bake_normal_space == R_BAKE_SPACE_WORLD)
+ mul_mat3_m4_v3(R.viewinv, nor);
+
+ normalize_v3(nor); /* in case object has scaling */
+
+ /* The invert of the red channel is to make
+ * the normal map compliant with the outside world.
+ * It needs to be done because in Blender
+ * the normal used in the renderer points inward. It is generated
+ * this way in calc_vertexnormals(). Should this ever change
+ * this negate must be removed.
+ *
+ * there is also a small 1e-5f bias for precision issues. otherwise
+ * we randomly get 127 or 128 for neutral colors. we choose 128
+ * because it is the convention flat color. * */
+ shr.combined[0] = (-nor[0]) / 2.0f + 0.5f + 1e-5f;
+ shr.combined[1] = nor[1] / 2.0f + 0.5f + 1e-5f;
+ shr.combined[2] = nor[2] / 2.0f + 0.5f + 1e-5f;
+ }
+ else if (bs->type == RE_BAKE_TEXTURE) {
+ copy_v3_v3(shr.combined, &shi->r);
+ shr.alpha = shi->alpha;
+ }
+ else if (bs->type == RE_BAKE_SHADOW) {
+ copy_v3_v3(shr.combined, shr.shad);
+ shr.alpha = shi->alpha;
+ }
+ else if (bs->type == RE_BAKE_SPEC_COLOR) {
+ copy_v3_v3(shr.combined, &shi->specr);
+ shr.alpha = 1.0f;
+ }
+ else if (bs->type == RE_BAKE_SPEC_INTENSITY) {
+ copy_v3_fl(shr.combined, shi->spec);
+ shr.alpha = 1.0f;
+ }
+ else if (bs->type == RE_BAKE_MIRROR_COLOR) {
+ copy_v3_v3(shr.combined, &shi->mirr);
+ shr.alpha = 1.0f;
+ }
+ else if (bs->type == RE_BAKE_MIRROR_INTENSITY) {
+ copy_v3_fl(shr.combined, shi->ray_mirror);
+ shr.alpha = 1.0f;
+ }
+ else if (bs->type == RE_BAKE_ALPHA) {
+ copy_v3_fl(shr.combined, shi->alpha);
+ shr.alpha = 1.0f;
+ }
+ else if (bs->type == RE_BAKE_EMIT) {
+ copy_v3_fl(shr.combined, shi->emit);
+ shr.alpha = 1.0f;
+ }
+ else if (bs->type == RE_BAKE_VERTEX_COLORS) {
+ copy_v3_v3(shr.combined, shi->vcol);
+ shr.alpha = shi->vcol[3];
+ }
+ }
+
+ if (bs->rect_float && !bs->vcol) {
+ float *col = bs->rect_float + 4 * (bs->rectx * y + x);
+ copy_v3_v3(col, shr.combined);
+ if (bs->type == RE_BAKE_ALL || bs->type == RE_BAKE_TEXTURE || bs->type == RE_BAKE_VERTEX_COLORS) {
+ col[3] = shr.alpha;
+ }
+ else {
+ col[3] = 1.0;
+ }
+ }
+ else {
+ /* Target is char (LDR). */
+ unsigned char col[4];
+
+ if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) {
+ float rgb[3];
+
+ copy_v3_v3(rgb, shr.combined);
+ if (R.scene_color_manage) {
+ /* Vertex colors have no way to specify color space, so they
+ * default to sRGB. */
+ if (!bs->vcol)
+ IMB_colormanagement_scene_linear_to_colorspace_v3(rgb, bs->rect_colorspace);
+ else
+ linearrgb_to_srgb_v3_v3(rgb, rgb);
+ }
+ rgb_float_to_uchar(col, rgb);
+ }
+ else {
+ rgb_float_to_uchar(col, shr.combined);
+ }
+
+ if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE, RE_BAKE_VERTEX_COLORS)) {
+ col[3] = unit_float_to_uchar_clamp(shr.alpha);
+ }
+ else {
+ col[3] = 255;
+ }
+
+ if (bs->vcol) {
+ /* Vertex color baking. Vcol has no useful alpha channel (it exists
+ * but is used only for vertex painting). */
+ bs->vcol->r = col[0];
+ bs->vcol->g = col[1];
+ bs->vcol->b = col[2];
+ }
+ else {
+ unsigned char *imcol = (unsigned char *)(bs->rect + bs->rectx * y + x);
+ copy_v4_v4_uchar(imcol, col);
+ }
+
+ }
+
+ if (bs->rect_mask) {
+ bs->rect_mask[bs->rectx * y + x] = FILTER_MASK_USED;
+ }
+
+ if (bs->do_update) {
+ *bs->do_update = true;
+ }
+}
+
+static void bake_displacement(void *handle, ShadeInput *UNUSED(shi), float dist, int x, int y)
+{
+ BakeShade *bs = handle;
+ float disp;
+
+ if (R.r.bake_flag & R_BAKE_NORMALIZE) {
+ if (R.r.bake_maxdist)
+ disp = (dist + R.r.bake_maxdist) / (R.r.bake_maxdist * 2); /* alter the range from [-bake_maxdist, bake_maxdist] to [0, 1]*/
+ else
+ disp = dist;
+ }
+ else {
+ disp = 0.5f + dist; /* alter the range from [-0.5,0.5] to [0,1]*/
+ }
+
+ if (bs->displacement_buffer) {
+ float *displacement = bs->displacement_buffer + (bs->rectx * y + x);
+ *displacement = disp;
+ bs->displacement_min = min_ff(bs->displacement_min, disp);
+ bs->displacement_max = max_ff(bs->displacement_max, disp);
+ }
+
+ if (bs->rect_float && !bs->vcol) {
+ float *col = bs->rect_float + 4 * (bs->rectx * y + x);
+ col[0] = col[1] = col[2] = disp;
+ col[3] = 1.0f;
+ }
+ else {
+ /* Target is char (LDR). */
+ unsigned char col[4];
+ col[0] = col[1] = col[2] = unit_float_to_uchar_clamp(disp);
+ col[3] = 255;
+
+ if (bs->vcol) {
+ /* Vertex color baking. Vcol has no useful alpha channel (it exists
+ * but is used only for vertex painting). */
+ bs->vcol->r = col[0];
+ bs->vcol->g = col[1];
+ bs->vcol->b = col[2];
+ }
+ else {
+ unsigned char *imcol = (unsigned char *)(bs->rect + bs->rectx * y + x);
+ copy_v4_v4_uchar(imcol, col);
+ }
+ }
+ if (bs->rect_mask) {
+ bs->rect_mask[bs->rectx * y + x] = FILTER_MASK_USED;
+ }
+}
+
+static int bake_intersect_tree(RayObject *raytree, Isect *isect, float *start, float *dir, float sign, float *hitco, float *dist)
+{
+ float maxdist;
+ int hit;
+
+ /* might be useful to make a user setting for maxsize*/
+ if (R.r.bake_maxdist > 0.0f)
+ maxdist = R.r.bake_maxdist;
+ else
+ maxdist = RE_RAYTRACE_MAXDIST + R.r.bake_biasdist;
+
+ /* 'dir' is always normalized */
+ madd_v3_v3v3fl(isect->start, start, dir, -R.r.bake_biasdist);
+
+ mul_v3_v3fl(isect->dir, dir, sign);
+
+ isect->dist = maxdist;
+
+ hit = RE_rayobject_raycast(raytree, isect);
+ if (hit) {
+ madd_v3_v3v3fl(hitco, isect->start, isect->dir, isect->dist);
+
+ *dist = isect->dist;
+ }
+
+ return hit;
+}
+
+static void bake_set_vlr_dxyco(BakeShade *bs, float *uv1, float *uv2, float *uv3)
+{
+ VlakRen *vlr = bs->vlr;
+ float A, d1, d2, d3, *v1, *v2, *v3;
+
+ if (bs->quad) {
+ v1 = vlr->v1->co;
+ v2 = vlr->v3->co;
+ v3 = vlr->v4->co;
+ }
+ else {
+ v1 = vlr->v1->co;
+ v2 = vlr->v2->co;
+ v3 = vlr->v3->co;
+ }
+
+ /* formula derived from barycentric coordinates:
+ * (uvArea1*v1 + uvArea2*v2 + uvArea3*v3)/uvArea
+ * then taking u and v partial derivatives to get dxco and dyco */
+ A = (uv2[0] - uv1[0]) * (uv3[1] - uv1[1]) - (uv3[0] - uv1[0]) * (uv2[1] - uv1[1]);
+
+ if (fabsf(A) > FLT_EPSILON) {
+ A = 0.5f / A;
+
+ d1 = uv2[1] - uv3[1];
+ d2 = uv3[1] - uv1[1];
+ d3 = uv1[1] - uv2[1];
+ bs->dxco[0] = (v1[0] * d1 + v2[0] * d2 + v3[0] * d3) * A;
+ bs->dxco[1] = (v1[1] * d1 + v2[1] * d2 + v3[1] * d3) * A;
+ bs->dxco[2] = (v1[2] * d1 + v2[2] * d2 + v3[2] * d3) * A;
+
+ d1 = uv3[0] - uv2[0];
+ d2 = uv1[0] - uv3[0];
+ d3 = uv2[0] - uv1[0];
+ bs->dyco[0] = (v1[0] * d1 + v2[0] * d2 + v3[0] * d3) * A;
+ bs->dyco[1] = (v1[1] * d1 + v2[1] * d2 + v3[1] * d3) * A;
+ bs->dyco[2] = (v1[2] * d1 + v2[2] * d2 + v3[2] * d3) * A;
+ }
+ else {
+ bs->dxco[0] = bs->dxco[1] = bs->dxco[2] = 0.0f;
+ bs->dyco[0] = bs->dyco[1] = bs->dyco[2] = 0.0f;
+ }
+
+ if (bs->obi->flag & R_TRANSFORMED) {
+ mul_m3_v3(bs->obi->nmat, bs->dxco);
+ mul_m3_v3(bs->obi->nmat, bs->dyco);
+ }
+}
+
+static void do_bake_shade(void *handle, int x, int y, float u, float v)
+{
+ BakeShade *bs = handle;
+ VlakRen *vlr = bs->vlr;
+ ObjectInstanceRen *obi = bs->obi;
+ Object *ob = obi->obr->ob;
+ float l, *v1, *v2, *v3, tvn[3], ttang[4];
+ int quad;
+ ShadeSample *ssamp = &bs->ssamp;
+ ShadeInput *shi = ssamp->shi;
+
+ /* fast threadsafe break test */
+ if (R.test_break(R.tbh))
+ return;
+
+ /* setup render coordinates */
+ if (bs->quad) {
+ v1 = vlr->v1->co;
+ v2 = vlr->v3->co;
+ v3 = vlr->v4->co;
+ }
+ else {
+ v1 = vlr->v1->co;
+ v2 = vlr->v2->co;
+ v3 = vlr->v3->co;
+ }
+
+ l = 1.0f - u - v;
+
+ /* shrink barycentric coordinates inwards slightly to avoid some issues
+ * where baking selected to active might just miss the other face at the
+ * near the edge of a face */
+ if (bs->actob) {
+ const float eps = 1.0f - 1e-4f;
+ float invsum;
+
+ u = (u - 0.5f) * eps + 0.5f;
+ v = (v - 0.5f) * eps + 0.5f;
+ l = (l - 0.5f) * eps + 0.5f;
+
+ invsum = 1.0f / (u + v + l);
+
+ u *= invsum;
+ v *= invsum;
+ l *= invsum;
+ }
+
+ /* renderco */
+ shi->co[0] = l * v3[0] + u * v1[0] + v * v2[0];
+ shi->co[1] = l * v3[1] + u * v1[1] + v * v2[1];
+ shi->co[2] = l * v3[2] + u * v1[2] + v * v2[2];
+
+ /* avoid self shadow with vertex bake from adjacent faces [#33729] */
+ if ((bs->vcol != NULL) && (bs->actob == NULL)) {
+ madd_v3_v3fl(shi->co, vlr->n, 0.0001f);
+ }
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_v3(obi->mat, shi->co);
+
+ copy_v3_v3(shi->dxco, bs->dxco);
+ copy_v3_v3(shi->dyco, bs->dyco);
+
+ quad = bs->quad;
+ bake_set_shade_input(obi, vlr, shi, quad, 0, x, y, u, v);
+
+ if (bs->type == RE_BAKE_NORMALS && R.r.bake_normal_space == R_BAKE_SPACE_TANGENT) {
+ shade_input_set_shade_texco(shi);
+ copy_v3_v3(tvn, shi->nmapnorm);
+ copy_v4_v4(ttang, shi->nmaptang);
+ }
+
+ /* if we are doing selected to active baking, find point on other face */
+ if (bs->actob) {
+ Isect isec, minisec;
+ float co[3], minco[3], dist, mindist = 0.0f;
+ int hit, sign, dir = 1;
+
+ /* intersect with ray going forward and backward*/
+ hit = 0;
+ memset(&minisec, 0, sizeof(minisec));
+ minco[0] = minco[1] = minco[2] = 0.0f;
+
+ copy_v3_v3(bs->dir, shi->vn);
+
+ for (sign = -1; sign <= 1; sign += 2) {
+ memset(&isec, 0, sizeof(isec));
+ isec.mode = RE_RAY_MIRROR;
+
+ isec.orig.ob = obi;
+ isec.orig.face = vlr;
+ isec.userdata = bs->actob;
+ isec.check = RE_CHECK_VLR_BAKE;
+ isec.skip = RE_SKIP_VLR_NEIGHBOUR;
+
+ if (bake_intersect_tree(R.raytree, &isec, shi->co, shi->vn, sign, co, &dist)) {
+ if (!hit || len_squared_v3v3(shi->co, co) < len_squared_v3v3(shi->co, minco)) {
+ minisec = isec;
+ mindist = dist;
+ copy_v3_v3(minco, co);
+ hit = 1;
+ dir = sign;
+ }
+ }
+ }
+
+ if (ELEM(bs->type, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
+ if (hit)
+ bake_displacement(handle, shi, (dir == -1) ? mindist : -mindist, x, y);
+ else
+ bake_displacement(handle, shi, 0.0f, x, y);
+ return;
+ }
+
+ /* if hit, we shade from the new point, otherwise from point one starting face */
+ if (hit) {
+ obi = (ObjectInstanceRen *)minisec.hit.ob;
+ vlr = (VlakRen *)minisec.hit.face;
+ quad = (minisec.isect == 2);
+ copy_v3_v3(shi->co, minco);
+
+ u = -minisec.u;
+ v = -minisec.v;
+ bake_set_shade_input(obi, vlr, shi, quad, 1, x, y, u, v);
+ }
+ }
+
+ if (bs->type == RE_BAKE_NORMALS && R.r.bake_normal_space == R_BAKE_SPACE_TANGENT)
+ bake_shade(handle, ob, shi, quad, x, y, u, v, tvn, ttang);
+ else
+ bake_shade(handle, ob, shi, quad, x, y, u, v, NULL, NULL);
+}
+
+static int get_next_bake_face(BakeShade *bs)
+{
+ ObjectRen *obr;
+ VlakRen *vlr;
+ MTFace *tface;
+ static int v = 0, vdone = false;
+ static ObjectInstanceRen *obi = NULL;
+
+ if (bs == NULL) {
+ vlr = NULL;
+ v = vdone = false;
+ obi = R.instancetable.first;
+ return 0;
+ }
+
+ BLI_thread_lock(LOCK_CUSTOM1);
+
+ for (; obi; obi = obi->next, v = 0) {
+ obr = obi->obr;
+
+ /* only allow non instances here */
+ if (obr->flag & R_INSTANCEABLE)
+ continue;
+
+ for (; v < obr->totvlak; v++) {
+ vlr = RE_findOrAddVlak(obr, v);
+
+ if ((bs->actob && bs->actob == obr->ob) || (!bs->actob && (obr->ob->flag & SELECT))) {
+ if (R.r.bake_flag & R_BAKE_VCOL) {
+ /* Gather face data for vertex color bake */
+ Mesh *me;
+ int *origindex, vcollayer;
+ CustomDataLayer *cdl;
+
+ if (obr->ob->type != OB_MESH)
+ continue;
+ me = obr->ob->data;
+
+ origindex = RE_vlakren_get_origindex(obr, vlr, 0);
+ if (origindex == NULL)
+ continue;
+ if (*origindex >= me->totpoly) {
+ /* Small hack for Array modifier, which gives false
+ * original indices - z0r */
+ continue;
+ }
+#if 0
+ /* Only shade selected faces. */
+ if ((me->mface[*origindex].flag & ME_FACE_SEL) == 0)
+ continue;
+#endif
+
+ vcollayer = CustomData_get_render_layer_index(&me->ldata, CD_MLOOPCOL);
+ if (vcollayer == -1)
+ continue;
+
+ cdl = &me->ldata.layers[vcollayer];
+ bs->mpoly = me->mpoly + *origindex;
+ bs->vcol = ((MLoopCol *)cdl->data) + bs->mpoly->loopstart;
+ bs->mloop = me->mloop + bs->mpoly->loopstart;
+
+ /* Tag mesh for reevaluation. */
+ me->id.tag |= LIB_TAG_DOIT;
+ }
+ else {
+ Image *ima = NULL;
+ ImBuf *ibuf = NULL;
+ const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f};
+ const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f};
+ const float disp_alpha[4] = {0.5f, 0.5f, 0.5f, 0.0f};
+ const float disp_solid[4] = {0.5f, 0.5f, 0.5f, 1.0f};
+
+ tface = RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0);
+
+ if (!tface || !tface->tpage)
+ continue;
+
+ ima = tface->tpage;
+ ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+
+ if (ibuf == NULL)
+ continue;
+
+ if (ibuf->rect == NULL && ibuf->rect_float == NULL) {
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ continue;
+ }
+
+ if (ibuf->rect_float && !(ibuf->channels == 0 || ibuf->channels == 4)) {
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ continue;
+ }
+
+ if (ima->flag & IMA_USED_FOR_RENDER) {
+ ima->id.tag &= ~LIB_TAG_DOIT;
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ continue;
+ }
+
+ /* find the image for the first time? */
+ if (ima->id.tag & LIB_TAG_DOIT) {
+ ima->id.tag &= ~LIB_TAG_DOIT;
+
+ /* we either fill in float or char, this ensures things go fine */
+ if (ibuf->rect_float)
+ imb_freerectImBuf(ibuf);
+ /* clear image */
+ if (R.r.bake_flag & R_BAKE_CLEAR) {
+ if (R.r.bake_mode == RE_BAKE_NORMALS && R.r.bake_normal_space == R_BAKE_SPACE_TANGENT)
+ IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
+ else if (ELEM(R.r.bake_mode, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE))
+ IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid);
+ else
+ IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
+ }
+ /* might be read by UI to set active image for display */
+ R.bakebuf = ima;
+ }
+
+ /* Tag image for redraw. */
+ ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ }
+
+ bs->obi = obi;
+ bs->vlr = vlr;
+ bs->vdone++; /* only for error message if nothing was rendered */
+ v++;
+ BLI_thread_unlock(LOCK_CUSTOM1);
+ return 1;
+ }
+ }
+ }
+
+ BLI_thread_unlock(LOCK_CUSTOM1);
+ return 0;
+}
+
+static void bake_single_vertex(BakeShade *bs, VertRen *vert, float u, float v)
+{
+ int *origindex, i;
+ MLoopCol *basevcol;
+ MLoop *mloop;
+
+ /* per vertex fixed seed */
+ BLI_thread_srandom(bs->thread, vert->index);
+
+ origindex = RE_vertren_get_origindex(bs->obi->obr, vert, 0);
+ if (!origindex || *origindex == ORIGINDEX_NONE)
+ return;
+
+ /* Search for matching vertex index and apply shading. */
+ for (i = 0; i < bs->mpoly->totloop; i++) {
+ mloop = bs->mloop + i;
+ if (mloop->v != *origindex)
+ continue;
+ basevcol = bs->vcol;
+ bs->vcol = basevcol + i;
+ do_bake_shade(bs, 0, 0, u, v);
+ bs->vcol = basevcol;
+ break;
+ }
+}
+
+/* Bake all vertices of a face. Actually, this still works on a face-by-face
+ * basis, and each vertex on each face is shaded. Vertex colors are a property
+ * of loops, not vertices. */
+static void shade_verts(BakeShade *bs)
+{
+ VlakRen *vlr = bs->vlr;
+
+ /* Disable baking to image; write to vcol instead. vcol pointer is set in
+ * bake_single_vertex. */
+ bs->ima = NULL;
+ bs->rect = NULL;
+ bs->rect_float = NULL;
+ bs->displacement_buffer = NULL;
+ bs->displacement_min = FLT_MAX;
+ bs->displacement_max = -FLT_MAX;
+
+ bs->quad = 0;
+
+ /* No anti-aliasing for vertices. */
+ zero_v3(bs->dxco);
+ zero_v3(bs->dyco);
+
+ /* Shade each vertex of the face. u and v are barycentric coordinates; since
+ * we're only interested in vertices, these will be 0 or 1. */
+ if ((vlr->flag & R_FACE_SPLIT) == 0) {
+ /* Processing triangle face, whole quad, or first half of split quad. */
+
+ bake_single_vertex(bs, bs->vlr->v1, 1.0f, 0.0f);
+ bake_single_vertex(bs, bs->vlr->v2, 0.0f, 1.0f);
+ bake_single_vertex(bs, bs->vlr->v3, 0.0f, 0.0f);
+
+ if (vlr->v4) {
+ bs->quad = 1;
+ bake_single_vertex(bs, bs->vlr->v4, 0.0f, 0.0f);
+ }
+ }
+ else {
+ /* Processing second half of split quad. Only one vertex to go. */
+ if (vlr->flag & R_DIVIDE_24) {
+ bake_single_vertex(bs, bs->vlr->v2, 0.0f, 1.0f);
+ }
+ else {
+ bake_single_vertex(bs, bs->vlr->v3, 0.0f, 0.0f);
+ }
+ }
+}
+
+/* already have tested for tface and ima and zspan */
+static void shade_tface(BakeShade *bs)
+{
+ VlakRen *vlr = bs->vlr;
+ ObjectInstanceRen *obi = bs->obi;
+ ObjectRen *obr = obi->obr;
+ MTFace *tface = RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0);
+ Image *ima = tface->tpage;
+ float vec[4][2];
+ int a, i1, i2, i3;
+
+ /* per face fixed seed */
+ BLI_thread_srandom(bs->thread, vlr->index);
+
+ /* check valid zspan */
+ if (ima != bs->ima) {
+ BKE_image_release_ibuf(bs->ima, bs->ibuf, NULL);
+
+ bs->ima = ima;
+ bs->ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ /* note, these calls only free/fill contents of zspan struct, not zspan itself */
+ zbuf_free_span(bs->zspan);
+ zbuf_alloc_span(bs->zspan, bs->ibuf->x, bs->ibuf->y, R.clipcrop);
+ }
+
+ bs->rectx = bs->ibuf->x;
+ bs->recty = bs->ibuf->y;
+ bs->rect = bs->ibuf->rect;
+ bs->rect_colorspace = bs->ibuf->rect_colorspace;
+ bs->rect_float = bs->ibuf->rect_float;
+ bs->vcol = NULL;
+ bs->quad = 0;
+ bs->rect_mask = NULL;
+ bs->displacement_buffer = NULL;
+
+ if (bs->use_mask || bs->use_displacement_buffer) {
+ BakeImBufuserData *userdata = bs->ibuf->userdata;
+ if (userdata == NULL) {
+ BLI_thread_lock(LOCK_CUSTOM1);
+ userdata = bs->ibuf->userdata;
+ if (userdata == NULL) /* since the thread was locked, its possible another thread alloced the value */
+ userdata = MEM_callocN(sizeof(BakeImBufuserData), "BakeImBufuserData");
+
+ if (bs->use_mask) {
+ if (userdata->mask_buffer == NULL) {
+ userdata->mask_buffer = MEM_callocN(sizeof(char) * bs->rectx * bs->recty, "BakeMask");
+ }
+ }
+
+ if (bs->use_displacement_buffer) {
+ if (userdata->displacement_buffer == NULL) {
+ userdata->displacement_buffer = MEM_callocN(sizeof(float) * bs->rectx * bs->recty, "BakeDisp");
+ }
+ }
+
+ bs->ibuf->userdata = userdata;
+
+ BLI_thread_unlock(LOCK_CUSTOM1);
+ }
+
+ bs->rect_mask = userdata->mask_buffer;
+ bs->displacement_buffer = userdata->displacement_buffer;
+ }
+
+ /* get pixel level vertex coordinates */
+ for (a = 0; a < 4; a++) {
+ /* Note, workaround for pixel aligned UVs which are common and can screw up our intersection tests
+ * where a pixel gets in between 2 faces or the middle of a quad,
+ * camera aligned quads also have this problem but they are less common.
+ * Add a small offset to the UVs, fixes bug #18685 - Campbell */
+ vec[a][0] = tface->uv[a][0] * (float)bs->rectx - (0.5f + 0.001f);
+ vec[a][1] = tface->uv[a][1] * (float)bs->recty - (0.5f + 0.002f);
+ }
+
+ /* UV indices have to be corrected for possible quad->tria splits */
+ i1 = 0; i2 = 1; i3 = 2;
+ vlr_set_uv_indices(vlr, &i1, &i2, &i3);
+ bake_set_vlr_dxyco(bs, vec[i1], vec[i2], vec[i3]);
+ zspan_scanconvert(bs->zspan, bs, vec[i1], vec[i2], vec[i3], do_bake_shade);
+
+ if (vlr->v4) {
+ bs->quad = 1;
+ bake_set_vlr_dxyco(bs, vec[0], vec[2], vec[3]);
+ zspan_scanconvert(bs->zspan, bs, vec[0], vec[2], vec[3], do_bake_shade);
+ }
+}
+
+static void *do_bake_thread(void *bs_v)
+{
+ BakeShade *bs = bs_v;
+
+ while (get_next_bake_face(bs)) {
+ if (R.r.bake_flag & R_BAKE_VCOL) {
+ shade_verts(bs);
+ }
+ else {
+ shade_tface(bs);
+ }
+
+ /* fast threadsafe break test */
+ if (R.test_break(R.tbh))
+ break;
+
+ /* access is not threadsafe but since its just true/false probably ok
+ * only used for interactive baking */
+ if (bs->do_update) {
+ *bs->do_update = true;
+ }
+ }
+ bs->ready = true;
+
+ BKE_image_release_ibuf(bs->ima, bs->ibuf, NULL);
+
+ return NULL;
+}
+
+void RE_bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter)
+{
+ /* must check before filtering */
+ const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf);
+
+ /* Margin */
+ if (filter) {
+ IMB_filter_extend(ibuf, mask, filter);
+ }
+
+ /* if the bake results in new alpha then change the image setting */
+ if (is_new_alpha) {
+ ibuf->planes = R_IMF_PLANES_RGBA;
+ }
+ else {
+ if (filter && ibuf->planes != R_IMF_PLANES_RGBA) {
+ /* clear alpha added by filtering */
+ IMB_rectfill_alpha(ibuf, 1.0f);
+ }
+ }
+}
+
+void RE_bake_ibuf_normalize_displacement(ImBuf *ibuf, float *displacement, char *mask, float displacement_min, float displacement_max)
+{
+ int i;
+ const float *current_displacement = displacement;
+ const char *current_mask = mask;
+ float max_distance;
+
+ max_distance = max_ff(fabsf(displacement_min), fabsf(displacement_max));
+
+ for (i = 0; i < ibuf->x * ibuf->y; i++) {
+ if (*current_mask == FILTER_MASK_USED) {
+ float normalized_displacement;
+
+ if (max_distance > 1e-5f)
+ normalized_displacement = (*current_displacement + max_distance) / (max_distance * 2);
+ else
+ normalized_displacement = 0.5f;
+
+ if (ibuf->rect_float) {
+ /* currently baking happens to RGBA only */
+ float *fp = ibuf->rect_float + i * 4;
+ fp[0] = fp[1] = fp[2] = normalized_displacement;
+ fp[3] = 1.0f;
+ }
+
+ if (ibuf->rect) {
+ unsigned char *cp = (unsigned char *) (ibuf->rect + i);
+ cp[0] = cp[1] = cp[2] = unit_float_to_uchar_clamp(normalized_displacement);
+ cp[3] = 255;
+ }
+ }
+
+ current_displacement++;
+ current_mask++;
+ }
+}
+
+/* using object selection tags, the faces with UV maps get baked */
+/* render should have been setup */
+/* returns 0 if nothing was handled */
+int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_update, float *progress)
+{
+ BakeShade *handles;
+ ListBase threads;
+ Image *ima;
+ int a, vdone = false, result = BAKE_RESULT_OK;
+ bool use_mask = false;
+ bool use_displacement_buffer = false;
+ bool do_manage = false;
+
+ if (ELEM(type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) {
+ do_manage = BKE_scene_check_color_management_enabled(re->scene);
+ }
+
+ re->scene_color_manage = BKE_scene_check_color_management_enabled(re->scene);
+
+ /* initialize render global */
+ R = *re;
+ R.bakebuf = NULL;
+
+ /* initialize static vars */
+ get_next_bake_face(NULL);
+
+ /* do we need a mask? */
+ if (re->r.bake_filter)
+ use_mask = true;
+
+ /* do we need buffer to store displacements */
+ if (ELEM(type, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
+ if (((R.r.bake_flag & R_BAKE_NORMALIZE) && R.r.bake_maxdist == 0.0f) ||
+ (type == RE_BAKE_DERIVATIVE))
+ {
+ use_displacement_buffer = true;
+ use_mask = true;
+ }
+ }
+
+ /* baker uses this flag to detect if image was initialized */
+ if ((R.r.bake_flag & R_BAKE_VCOL) == 0) {
+ for (ima = G.main->image.first; ima; ima = ima->id.next) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ ima->id.tag |= LIB_TAG_DOIT;
+ ima->flag &= ~IMA_USED_FOR_RENDER;
+ if (ibuf) {
+ ibuf->userdata = NULL; /* use for masking if needed */
+ }
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ }
+ }
+
+ if (R.r.bake_flag & R_BAKE_VCOL) {
+ /* untag all meshes */
+ BKE_main_id_tag_listbase(&G.main->mesh, LIB_TAG_DOIT, false);
+ }
+
+ BLI_threadpool_init(&threads, do_bake_thread, re->r.threads);
+
+ handles = MEM_callocN(sizeof(BakeShade) * re->r.threads, "BakeShade");
+
+ /* get the threads running */
+ for (a = 0; a < re->r.threads; a++) {
+ handles[a].thread = a;
+
+ /* set defaults in handles */
+ handles[a].ssamp.shi[0].lay = re->lay;
+
+ if (type == RE_BAKE_SHADOW) {
+ handles[a].ssamp.shi[0].passflag = SCE_PASS_SHADOW;
+ }
+ else {
+ handles[a].ssamp.shi[0].passflag = SCE_PASS_COMBINED;
+ }
+ handles[a].ssamp.shi[0].combinedflag = ~(SCE_PASS_SPEC);
+ handles[a].ssamp.shi[0].thread = a;
+ handles[a].ssamp.shi[0].do_manage = do_manage;
+ handles[a].ssamp.tot = 1;
+
+ handles[a].type = type;
+ handles[a].actob = actob;
+ if (R.r.bake_flag & R_BAKE_VCOL)
+ handles[a].zspan = NULL;
+ else
+ handles[a].zspan = MEM_callocN(sizeof(ZSpan), "zspan for bake");
+
+ handles[a].use_mask = use_mask;
+ handles[a].use_displacement_buffer = use_displacement_buffer;
+
+ handles[a].do_update = do_update; /* use to tell the view to update */
+
+ handles[a].displacement_min = FLT_MAX;
+ handles[a].displacement_max = -FLT_MAX;
+
+ BLI_threadpool_insert(&threads, &handles[a]);
+ }
+
+ /* wait for everything to be done */
+ a = 0;
+ while (a != re->r.threads) {
+ PIL_sleep_ms(50);
+
+ /* calculate progress */
+ for (vdone = false, a = 0; a < re->r.threads; a++)
+ vdone += handles[a].vdone;
+ if (progress)
+ *progress = (float)(vdone / (float)re->totvlak);
+
+ for (a = 0; a < re->r.threads; a++) {
+ if (handles[a].ready == false) {
+ break;
+ }
+ }
+ }
+
+ /* filter and refresh images */
+ if ((R.r.bake_flag & R_BAKE_VCOL) == 0) {
+ float displacement_min = FLT_MAX, displacement_max = -FLT_MAX;
+
+ if (use_displacement_buffer) {
+ for (a = 0; a < re->r.threads; a++) {
+ displacement_min = min_ff(displacement_min, handles[a].displacement_min);
+ displacement_max = max_ff(displacement_max, handles[a].displacement_max);
+ }
+ }
+
+ for (ima = G.main->image.first; ima; ima = ima->id.next) {
+ if ((ima->id.tag & LIB_TAG_DOIT) == 0) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ BakeImBufuserData *userdata;
+
+ if (ima->flag & IMA_USED_FOR_RENDER)
+ result = BAKE_RESULT_FEEDBACK_LOOP;
+
+ if (!ibuf)
+ continue;
+
+ userdata = (BakeImBufuserData *)ibuf->userdata;
+ if (userdata) {
+ if (use_displacement_buffer) {
+ if (type == RE_BAKE_DERIVATIVE) {
+ float user_scale = (R.r.bake_flag & R_BAKE_USERSCALE) ? R.r.bake_user_scale : -1.0f;
+ RE_bake_make_derivative(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
+ displacement_min, displacement_max, user_scale);
+ }
+ else {
+ RE_bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
+ displacement_min, displacement_max);
+ }
+ }
+
+ RE_bake_ibuf_filter(ibuf, userdata->mask_buffer, re->r.bake_filter);
+ }
+
+ ibuf->userflags |= IB_BITMAPDIRTY;
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ }
+ }
+
+ /* calculate return value */
+ for (a = 0; a < re->r.threads; a++) {
+ zbuf_free_span(handles[a].zspan);
+ MEM_freeN(handles[a].zspan);
+ }
+ }
+
+ MEM_freeN(handles);
+
+ BLI_threadpool_end(&threads);
+
+ if (vdone == 0) {
+ result = BAKE_RESULT_NO_OBJECTS;
+ }
+
+ return result;
+}
+
+struct Image *RE_bake_shade_get_image(void)
+{
+ return R.bakebuf;
+}
+
+/* **************** Derivative Maps Baker **************** */
+
+static void add_single_heights_margin(const ImBuf *ibuf, const char *mask, float *heights_buffer)
+{
+ int x, y;
+
+ for (y = 0; y < ibuf->y; y++) {
+ for (x = 0; x < ibuf->x; x++) {
+ int index = ibuf->x * y + x;
+
+ /* If unassigned pixel, look for neighbors. */
+ if (mask[index] != FILTER_MASK_USED) {
+ float height_acc = 0;
+ int denom = 0;
+ int i, j;
+
+ for (j = -1; j <= 1; j++)
+ for (i = -1; i <= 1; i++) {
+ int w = (i == 0 ? 1 : 0) + (j == 0 ? 1 : 0) + 1;
+
+ if (i != 0 || j != 0) {
+ int index2 = 0;
+ int x0 = x + i;
+ int y0 = y + j;
+
+ CLAMP(x0, 0, ibuf->x - 1);
+ CLAMP(y0, 0, ibuf->y - 1);
+
+ index2 = ibuf->x * y0 + x0;
+
+ if (mask[index2] == FILTER_MASK_USED) {
+ height_acc += w * heights_buffer[index2];
+ denom += w;
+ }
+ }
+ }
+
+ /* Insert final value. */
+ if (denom > 0) {
+ heights_buffer[index] = height_acc / denom;
+ }
+ }
+ }
+ }
+}
+
+/* returns user-scale */
+float RE_bake_make_derivative(ImBuf *ibuf, float *heights_buffer, const char *mask,
+ const float height_min, const float height_max,
+ const float fmult)
+{
+ const float delta_height = height_max - height_min;
+ const float denom = delta_height > 0.0f ? (8 * delta_height) : 1.0f;
+ bool auto_range_fit = fmult <= 0.0f;
+ float max_num_deriv = -1.0f;
+ int x, y, index;
+
+ /* Need a single margin to calculate good derivatives. */
+ add_single_heights_margin(ibuf, mask, heights_buffer);
+
+ if (auto_range_fit) {
+ /* If automatic range fitting is enabled. */
+ for (y = 0; y < ibuf->y; y++) {
+ const int Yu = y == (ibuf->y - 1) ? (ibuf->y - 1) : (y + 1);
+ const int Yc = y;
+ const int Yd = y == 0 ? 0 : (y - 1);
+
+ for (x = 0; x < ibuf->x; x++) {
+ const int Xl = x == 0 ? 0 : (x - 1);
+ const int Xc = x;
+ const int Xr = x == (ibuf->x - 1) ? (ibuf->x - 1) : (x + 1);
+
+ const float Hcy = heights_buffer[Yc * ibuf->x + Xr] - heights_buffer[Yc * ibuf->x + Xl];
+ const float Hu = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yu * ibuf->x + Xl];
+ const float Hd = heights_buffer[Yd * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xl];
+
+ const float Hl = heights_buffer[Yu * ibuf->x + Xl] - heights_buffer[Yd * ibuf->x + Xl];
+ const float Hcx = heights_buffer[Yu * ibuf->x + Xc] - heights_buffer[Yd * ibuf->x + Xc];
+ const float Hr = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xr];
+
+ /* This corresponds to using the sobel kernel on the heights buffer
+ * to obtain the derivative multiplied by 8.
+ */
+ const float deriv_x = Hu + 2 * Hcy + Hd;
+ const float deriv_y = Hr + 2 * Hcx + Hl;
+
+ /* early out */
+ index = ibuf->x * y + x;
+ if (mask[index] != FILTER_MASK_USED) {
+ continue;
+ }
+
+ /* Widen bound. */
+ if (fabsf(deriv_x) > max_num_deriv) {
+ max_num_deriv = fabsf(deriv_x);
+ }
+
+ if (fabsf(deriv_y) > max_num_deriv) {
+ max_num_deriv = fabsf(deriv_y);
+ }
+ }
+ }
+ }
+
+ /* Output derivatives. */
+ auto_range_fit &= (max_num_deriv > 0);
+ for (y = 0; y < ibuf->y; y++) {
+ const int Yu = y == (ibuf->y - 1) ? (ibuf->y - 1) : (y + 1);
+ const int Yc = y;
+ const int Yd = y == 0 ? 0 : (y - 1);
+
+ for (x = 0; x < ibuf->x; x++) {
+ const int Xl = x == 0 ? 0 : (x - 1);
+ const int Xc = x;
+ const int Xr = x == (ibuf->x - 1) ? (ibuf->x - 1) : (x + 1);
+
+ const float Hcy = heights_buffer[Yc * ibuf->x + Xr] - heights_buffer[Yc * ibuf->x + Xl];
+ const float Hu = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yu * ibuf->x + Xl];
+ const float Hd = heights_buffer[Yd * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xl];
+
+ const float Hl = heights_buffer[Yu * ibuf->x + Xl] - heights_buffer[Yd * ibuf->x + Xl];
+ const float Hcx = heights_buffer[Yu * ibuf->x + Xc] - heights_buffer[Yd * ibuf->x + Xc];
+ const float Hr = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xr];
+
+ /* This corresponds to using the sobel kernel on the heights buffer
+ * to obtain the derivative multiplied by 8.
+ */
+ float deriv_x = Hu + 2 * Hcy + Hd;
+ float deriv_y = Hr + 2 * Hcx + Hl;
+
+ /* Early out. */
+ index = ibuf->x * y + x;
+ if (mask[index] != FILTER_MASK_USED) {
+ continue;
+ }
+
+ if (auto_range_fit) {
+ deriv_x /= max_num_deriv;
+ deriv_y /= max_num_deriv;
+ }
+ else {
+ deriv_x *= (fmult / denom);
+ deriv_y *= (fmult / denom);
+ }
+
+ deriv_x = deriv_x * 0.5f + 0.5f;
+ deriv_y = deriv_y * 0.5f + 0.5f;
+
+ /* Clamp. */
+ CLAMP(deriv_x, 0.0f, 1.0f);
+ CLAMP(deriv_y, 0.0f, 1.0f);
+
+ /* Write out derivatives. */
+ if (ibuf->rect_float) {
+ float *rrgbf = ibuf->rect_float + index * 4;
+
+ rrgbf[0] = deriv_x;
+ rrgbf[1] = deriv_y;
+ rrgbf[2] = 0.0f;
+ rrgbf[3] = 1.0f;
+ }
+ else {
+ char *rrgb = (char *)ibuf->rect + index * 4;
+
+ rrgb[0] = unit_float_to_uchar_clamp(deriv_x);
+ rrgb[1] = unit_float_to_uchar_clamp(deriv_y);
+ rrgb[2] = 0;
+ rrgb[3] = 255;
+ }
+ }
+ }
+
+ /* Eeturn user-scale (for rendering). */
+ return auto_range_fit ? (max_num_deriv / denom) : (fmult > 0.0f ? (1.0f / fmult) : 0.0f);
+}
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c
new file mode 100644
index 00000000000..8675ffec313
--- /dev/null
+++ b/source/blender/render/intern/source/convertblender.c
@@ -0,0 +1,6014 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributors: 2004/2005/2006 Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/convertblender.c
+ * \ingroup render
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_rand.h"
+#include "BLI_memarena.h"
+#ifdef WITH_FREESTYLE
+# include "BLI_edgehash.h"
+#endif
+
+#include "BLT_translation.h"
+
+#include "DNA_material_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_group_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_image_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_object_fluidsim_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_texture_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_curve.h"
+#include "BKE_customdata.h"
+#include "BKE_colortools.h"
+#include "BKE_displist.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_key.h"
+#include "BKE_image.h"
+#include "BKE_lattice.h"
+#include "BKE_material.h"
+#include "BKE_main.h"
+#include "BKE_mball.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_node.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_scene.h"
+
+#include "PIL_time.h"
+
+#include "envmap.h"
+#include "occlusion.h"
+#include "pointdensity.h"
+#include "voxeldata.h"
+#include "render_types.h"
+#include "rendercore.h"
+#include "renderdatabase.h"
+#include "renderpipeline.h"
+#include "shadbuf.h"
+#include "shading.h"
+#include "strand.h"
+#include "texture.h"
+#include "volume_precache.h"
+#include "sss.h"
+#include "zbuf.h"
+#include "sunsky.h"
+
+/* 10 times larger than normal epsilon, test it on default nurbs sphere with ray_transp (for quad detection) */
+/* or for checking vertex normal flips */
+#define FLT_EPSILON10 1.19209290e-06F
+
+/* could enable at some point but for now there are far too many conversions */
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wdouble-promotion"
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* tool functions/defines for ad hoc simplification and possible future
+ * cleanup */
+/* ------------------------------------------------------------------------- */
+
+#define UVTOINDEX(u, v) (startvlak + (u) * sizev + (v))
+/*
+ *
+ * NOTE THAT U/V COORDINATES ARE SOMETIMES SWAPPED !!
+ *
+ * ^ ()----p4----p3----()
+ * | | | | |
+ * u | | F1 | F2 |
+ * | | | |
+ * ()----p1----p2----()
+ * v ->
+ */
+
+/* ------------------------------------------------------------------------- */
+
+#define CD_MASK_RENDER_INTERNAL \
+ (CD_MASK_BAREMESH | CD_MASK_MFACE | CD_MASK_MTFACE | CD_MASK_MCOL)
+
+static void split_v_renderfaces(ObjectRen *obr, int startvlak, int UNUSED(startvert), int UNUSED(usize), int vsize, int uIndex, int UNUSED(cyclu), int cyclv)
+{
+ int vLen = vsize-1+(!!cyclv);
+ int v;
+
+ for (v=0; v<vLen; v++) {
+ VlakRen *vlr = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + v);
+ VlakRen *vlr_other;
+ VertRen *vert = RE_vertren_copy(obr, vlr->v2);
+
+ if (cyclv) {
+ vlr->v2 = vert;
+
+ if (v == vLen - 1) {
+ vlr_other = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + 0);
+ vlr_other->v1 = vert;
+ }
+ else {
+ vlr_other = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + v+1);
+ vlr_other->v1 = vert;
+ }
+ }
+ else {
+ vlr->v2 = vert;
+
+ if (v < vLen - 1) {
+ vlr_other = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + v+1);
+ vlr_other->v1 = vert;
+ }
+
+ if (v == 0) {
+ vlr->v1 = RE_vertren_copy(obr, vlr->v1);
+ }
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* Stress, tangents and normals */
+/* ------------------------------------------------------------------------- */
+
+static void calc_edge_stress_add(float *accum, VertRen *v1, VertRen *v2)
+{
+ float len= len_v3v3(v1->co, v2->co)/len_v3v3(v1->orco, v2->orco);
+ float *acc;
+
+ acc= accum + 2*v1->index;
+ acc[0]+= len;
+ acc[1]+= 1.0f;
+
+ acc= accum + 2*v2->index;
+ acc[0]+= len;
+ acc[1]+= 1.0f;
+}
+
+static void calc_edge_stress(Render *UNUSED(re), ObjectRen *obr, Mesh *me)
+{
+ float loc[3], size[3], *accum, *acc, *accumoffs, *stress;
+ int a;
+
+ if (obr->totvert==0) return;
+
+ BKE_mesh_texspace_get(me, loc, NULL, size);
+
+ accum= MEM_callocN(2*sizeof(float)*obr->totvert, "temp accum for stress");
+
+ /* de-normalize orco */
+ for (a=0; a<obr->totvert; a++) {
+ VertRen *ver= RE_findOrAddVert(obr, a);
+ if (ver->orco) {
+ ver->orco[0]= ver->orco[0]*size[0] +loc[0];
+ ver->orco[1]= ver->orco[1]*size[1] +loc[1];
+ ver->orco[2]= ver->orco[2]*size[2] +loc[2];
+ }
+ }
+
+ /* add stress values */
+ accumoffs= accum; /* so we can use vertex index */
+ for (a=0; a<obr->totvlak; a++) {
+ VlakRen *vlr= RE_findOrAddVlak(obr, a);
+
+ if (vlr->v1->orco && vlr->v4) {
+ calc_edge_stress_add(accumoffs, vlr->v1, vlr->v2);
+ calc_edge_stress_add(accumoffs, vlr->v2, vlr->v3);
+ calc_edge_stress_add(accumoffs, vlr->v3, vlr->v1);
+ if (vlr->v4) {
+ calc_edge_stress_add(accumoffs, vlr->v3, vlr->v4);
+ calc_edge_stress_add(accumoffs, vlr->v4, vlr->v1);
+ calc_edge_stress_add(accumoffs, vlr->v2, vlr->v4);
+ }
+ }
+ }
+
+ for (a=0; a<obr->totvert; a++) {
+ VertRen *ver= RE_findOrAddVert(obr, a);
+ if (ver->orco) {
+ /* find stress value */
+ acc= accumoffs + 2*ver->index;
+ if (acc[1]!=0.0f)
+ acc[0]/= acc[1];
+ stress= RE_vertren_get_stress(obr, ver, 1);
+ *stress= *acc;
+
+ /* restore orcos */
+ ver->orco[0] = (ver->orco[0]-loc[0])/size[0];
+ ver->orco[1] = (ver->orco[1]-loc[1])/size[1];
+ ver->orco[2] = (ver->orco[2]-loc[2])/size[2];
+ }
+ }
+
+ MEM_freeN(accum);
+}
+
+/* gets tangent from tface or orco */
+static void calc_tangent_vector(ObjectRen *obr, VlakRen *vlr, int do_tangent)
+{
+ MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0);
+ VertRen *v1=vlr->v1, *v2=vlr->v2, *v3=vlr->v3, *v4=vlr->v4;
+ float tang[3], *tav;
+ float *uv1, *uv2, *uv3, *uv4;
+ float uv[4][2];
+
+ if (tface) {
+ uv1= tface->uv[0];
+ uv2= tface->uv[1];
+ uv3= tface->uv[2];
+ uv4= tface->uv[3];
+ }
+ else if (v1->orco) {
+ uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3];
+ map_to_sphere(&uv[0][0], &uv[0][1], v1->orco[0], v1->orco[1], v1->orco[2]);
+ map_to_sphere(&uv[1][0], &uv[1][1], v2->orco[0], v2->orco[1], v2->orco[2]);
+ map_to_sphere(&uv[2][0], &uv[2][1], v3->orco[0], v3->orco[1], v3->orco[2]);
+ if (v4)
+ map_to_sphere(&uv[3][0], &uv[3][1], v4->orco[0], v4->orco[1], v4->orco[2]);
+ }
+ else return;
+
+ tangent_from_uv_v3(uv1, uv2, uv3, v1->co, v2->co, v3->co, vlr->n, tang);
+
+ if (do_tangent) {
+ tav= RE_vertren_get_tangent(obr, v1, 1);
+ add_v3_v3(tav, tang);
+ tav= RE_vertren_get_tangent(obr, v2, 1);
+ add_v3_v3(tav, tang);
+ tav= RE_vertren_get_tangent(obr, v3, 1);
+ add_v3_v3(tav, tang);
+ }
+
+ if (v4) {
+ tangent_from_uv_v3(uv1, uv3, uv4, v1->co, v3->co, v4->co, vlr->n, tang);
+
+ if (do_tangent) {
+ tav= RE_vertren_get_tangent(obr, v1, 1);
+ add_v3_v3(tav, tang);
+ tav= RE_vertren_get_tangent(obr, v3, 1);
+ add_v3_v3(tav, tang);
+ tav= RE_vertren_get_tangent(obr, v4, 1);
+ add_v3_v3(tav, tang);
+ }
+ }
+}
+
+
+
+/****************************************************************
+ ************ tangent space generation interface ****************
+ ****************************************************************/
+
+typedef struct {
+ ObjectRen *obr;
+ int mtface_index;
+} SRenderMeshToTangent;
+
+/* interface */
+#include "mikktspace.h"
+
+static int GetNumFaces(const SMikkTSpaceContext *pContext)
+{
+ SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
+ return pMesh->obr->totvlak;
+}
+
+static int GetNumVertsOfFace(const SMikkTSpaceContext *pContext, const int face_num)
+{
+ SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
+ VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
+ return vlr->v4!=NULL ? 4 : 3;
+}
+
+static void GetPosition(const SMikkTSpaceContext *pContext, float r_co[3], const int face_num, const int vert_index)
+{
+ //assert(vert_index>=0 && vert_index<4);
+ SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
+ VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
+ const float *co = (&vlr->v1)[vert_index]->co;
+ copy_v3_v3(r_co, co);
+}
+
+static void GetTextureCoordinate(const SMikkTSpaceContext *pContext, float r_uv[2], const int face_num, const int vert_index)
+{
+ //assert(vert_index>=0 && vert_index<4);
+ SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
+ VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
+ MTFace *tface= RE_vlakren_get_tface(pMesh->obr, vlr, pMesh->mtface_index, NULL, 0);
+ const float *coord;
+
+ if (tface != NULL) {
+ coord= tface->uv[vert_index];
+ copy_v2_v2(r_uv, coord);
+ }
+ else if ((coord = (&vlr->v1)[vert_index]->orco)) {
+ map_to_sphere(&r_uv[0], &r_uv[1], coord[0], coord[1], coord[2]);
+ }
+ else { /* else we get un-initialized value, 0.0 ok default? */
+ zero_v2(r_uv);
+ }
+}
+
+static void GetNormal(const SMikkTSpaceContext *pContext, float r_no[3], const int face_num, const int vert_index)
+{
+ //assert(vert_index>=0 && vert_index<4);
+ SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
+ VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
+
+ if (vlr->flag & ME_SMOOTH) {
+ const float *n = (&vlr->v1)[vert_index]->n;
+ copy_v3_v3(r_no, n);
+ }
+ else {
+ negate_v3_v3(r_no, vlr->n);
+ }
+}
+static void SetTSpace(const SMikkTSpaceContext *pContext, const float fvTangent[3], const float fSign, const int face_num, const int iVert)
+{
+ //assert(vert_index>=0 && vert_index<4);
+ SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
+ VlakRen *vlr = RE_findOrAddVlak(pMesh->obr, face_num);
+ float *ftang = RE_vlakren_get_nmap_tangent(pMesh->obr, vlr, pMesh->mtface_index, true);
+ if (ftang!=NULL) {
+ copy_v3_v3(&ftang[iVert*4+0], fvTangent);
+ ftang[iVert*4+3]=fSign;
+ }
+}
+
+static void calc_vertexnormals(Render *UNUSED(re), ObjectRen *obr, bool do_vertex_normal, bool do_tangent, bool do_nmap_tangent)
+{
+ int a;
+
+ /* clear all vertex normals */
+ if (do_vertex_normal) {
+ for (a=0; a<obr->totvert; a++) {
+ VertRen *ver= RE_findOrAddVert(obr, a);
+ ver->n[0]=ver->n[1]=ver->n[2]= 0.0f;
+ }
+ }
+
+ /* calculate cos of angles and point-masses, use as weight factor to
+ * add face normal to vertex */
+ for (a=0; a<obr->totvlak; a++) {
+ VlakRen *vlr= RE_findOrAddVlak(obr, a);
+ if (do_vertex_normal && vlr->flag & ME_SMOOTH) {
+ float *n4= (vlr->v4)? vlr->v4->n: NULL;
+ const float *c4= (vlr->v4)? vlr->v4->co: NULL;
+
+ accumulate_vertex_normals_v3(vlr->v1->n, vlr->v2->n, vlr->v3->n, n4,
+ vlr->n, vlr->v1->co, vlr->v2->co, vlr->v3->co, c4);
+ }
+ if (do_tangent) {
+ /* tangents still need to be calculated for flat faces too */
+ /* weighting removed, they are not vertexnormals */
+ calc_tangent_vector(obr, vlr, do_tangent);
+ }
+ }
+
+ /* do solid faces */
+ for (a=0; a<obr->totvlak; a++) {
+ VlakRen *vlr= RE_findOrAddVlak(obr, a);
+
+ if (do_vertex_normal && (vlr->flag & ME_SMOOTH)==0) {
+ if (is_zero_v3(vlr->v1->n)) copy_v3_v3(vlr->v1->n, vlr->n);
+ if (is_zero_v3(vlr->v2->n)) copy_v3_v3(vlr->v2->n, vlr->n);
+ if (is_zero_v3(vlr->v3->n)) copy_v3_v3(vlr->v3->n, vlr->n);
+ if (vlr->v4 && is_zero_v3(vlr->v4->n)) copy_v3_v3(vlr->v4->n, vlr->n);
+ }
+ }
+
+ /* normalize vertex normals */
+ for (a=0; a<obr->totvert; a++) {
+ VertRen *ver= RE_findOrAddVert(obr, a);
+ normalize_v3(ver->n);
+ if (do_tangent) {
+ float *tav= RE_vertren_get_tangent(obr, ver, 0);
+ if (tav) {
+ /* orthonorm. */
+ const float tdn = dot_v3v3(tav, ver->n);
+ tav[0] -= ver->n[0]*tdn;
+ tav[1] -= ver->n[1]*tdn;
+ tav[2] -= ver->n[2]*tdn;
+ normalize_v3(tav);
+ }
+ }
+ }
+
+ /* normal mapping tangent with mikktspace */
+ if (do_nmap_tangent != false) {
+ SRenderMeshToTangent mesh2tangent;
+ SMikkTSpaceContext sContext;
+ SMikkTSpaceInterface sInterface;
+ memset(&mesh2tangent, 0, sizeof(SRenderMeshToTangent));
+ memset(&sContext, 0, sizeof(SMikkTSpaceContext));
+ memset(&sInterface, 0, sizeof(SMikkTSpaceInterface));
+
+ mesh2tangent.obr = obr;
+
+ sContext.m_pUserData = &mesh2tangent;
+ sContext.m_pInterface = &sInterface;
+ sInterface.m_getNumFaces = GetNumFaces;
+ sInterface.m_getNumVerticesOfFace = GetNumVertsOfFace;
+ sInterface.m_getPosition = GetPosition;
+ sInterface.m_getTexCoord = GetTextureCoordinate;
+ sInterface.m_getNormal = GetNormal;
+ sInterface.m_setTSpaceBasic = SetTSpace;
+
+ for (a = 0; a < MAX_MTFACE; a++) {
+ if (obr->tangent_mask & 1 << a) {
+ mesh2tangent.mtface_index = a;
+ genTangSpaceDefault(&sContext);
+ }
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* Autosmoothing: */
+/* ------------------------------------------------------------------------- */
+
+typedef struct ASvert {
+ int totface;
+ ListBase faces;
+} ASvert;
+
+typedef struct ASface {
+ struct ASface *next, *prev;
+ VlakRen *vlr[4];
+ VertRen *nver[4];
+} ASface;
+
+static int as_addvert(ASvert *asv, VertRen *v1, VlakRen *vlr)
+{
+ ASface *asf;
+ int a = -1;
+
+ if (v1 == NULL)
+ return a;
+
+ asf = asv->faces.last;
+ if (asf) {
+ for (a = 0; a < 4 && asf->vlr[a]; a++) {
+ }
+ }
+ else {
+ a = 4;
+ }
+
+ /* new face struct */
+ if (a == 4) {
+ a = 0;
+ asf = MEM_callocN(sizeof(ASface), "asface");
+ BLI_addtail(&asv->faces, asf);
+ }
+
+ asf->vlr[a] = vlr;
+ asv->totface++;
+
+ return a;
+}
+
+static VertRen *as_findvertex_lnor(VlakRen *vlr, VertRen *ver, ASvert *asv, const float lnor[3])
+{
+ /* return when new vertex already was made, or existing one is OK */
+ ASface *asf;
+ int a;
+
+ /* First face, we can use existing vert and assign it current lnor! */
+ if (asv->totface == 1) {
+ copy_v3_v3(ver->n, lnor);
+ return ver;
+ }
+
+ /* In case existing ver has same normal as current lnor, we can simply use it! */
+ if (equals_v3v3(lnor, ver->n)) {
+ return ver;
+ }
+
+ asf = asv->faces.first;
+ while (asf) {
+ for (a = 0; a < 4; a++) {
+ if (asf->vlr[a] && asf->vlr[a] != vlr) {
+ /* this face already made a copy for this vertex! */
+ if (asf->nver[a]) {
+ if (equals_v3v3(lnor, asf->nver[a]->n)) {
+ return asf->nver[a];
+ }
+ }
+ }
+ }
+ asf = asf->next;
+ }
+
+ return NULL;
+}
+
+static void as_addvert_lnor(ObjectRen *obr, ASvert *asv, VertRen *ver, VlakRen *vlr, const short _lnor[3])
+{
+ VertRen *v1;
+ ASface *asf;
+ int asf_idx;
+ float lnor[3];
+
+ normal_short_to_float_v3(lnor, _lnor);
+
+ asf_idx = as_addvert(asv, ver, vlr);
+ if (asf_idx < 0) {
+ return;
+ }
+ asf = asv->faces.last;
+
+ /* already made a new vertex within threshold? */
+ v1 = as_findvertex_lnor(vlr, ver, asv, lnor);
+ if (v1 == NULL) {
+ /* make a new vertex */
+ v1 = RE_vertren_copy(obr, ver);
+ copy_v3_v3(v1->n, lnor);
+ }
+ if (v1 != ver) {
+ asf->nver[asf_idx] = v1;
+ if (vlr->v1 == ver) vlr->v1 = v1;
+ if (vlr->v2 == ver) vlr->v2 = v1;
+ if (vlr->v3 == ver) vlr->v3 = v1;
+ if (vlr->v4 == ver) vlr->v4 = v1;
+ }
+}
+
+/* note; autosmooth happens in object space still, after applying autosmooth we rotate */
+/* note2; actually, when original mesh and displist are equal sized, face normals are from original mesh */
+static void autosmooth(Render *UNUSED(re), ObjectRen *obr, float mat[4][4], short (*lnors)[4][3])
+{
+ ASvert *asverts;
+ VertRen *ver;
+ VlakRen *vlr;
+ int a, totvert;
+
+ float rot[3][3];
+
+ /* Note: For normals, we only want rotation, not scaling component.
+ * Negative scales (aka mirroring) give wrong results, see T44102. */
+ if (lnors) {
+ float mat3[3][3], size[3];
+
+ copy_m3_m4(mat3, mat);
+ mat3_to_rot_size(rot, size, mat3);
+ }
+
+ if (obr->totvert == 0)
+ return;
+
+ totvert = obr->totvert;
+ asverts = MEM_callocN(sizeof(ASvert) * totvert, "all smooth verts");
+
+ if (lnors) {
+ /* We construct listbase of all vertices and pointers to faces, and add new verts when needed
+ * (i.e. when existing ones do not share the same (loop)normal).
+ */
+ for (a = 0; a < obr->totvlak; a++, lnors++) {
+ vlr = RE_findOrAddVlak(obr, a);
+ /* skip wire faces */
+ if (vlr->v2 != vlr->v3) {
+ as_addvert_lnor(obr, asverts+vlr->v1->index, vlr->v1, vlr, (const short*)lnors[0][0]);
+ as_addvert_lnor(obr, asverts+vlr->v2->index, vlr->v2, vlr, (const short*)lnors[0][1]);
+ as_addvert_lnor(obr, asverts+vlr->v3->index, vlr->v3, vlr, (const short*)lnors[0][2]);
+ if (vlr->v4)
+ as_addvert_lnor(obr, asverts+vlr->v4->index, vlr->v4, vlr, (const short*)lnors[0][3]);
+ }
+ }
+ }
+
+ /* free */
+ for (a = 0; a < totvert; a++) {
+ BLI_freelistN(&asverts[a].faces);
+ }
+ MEM_freeN(asverts);
+
+ /* rotate vertices and calculate normal of faces */
+ for (a = 0; a < obr->totvert; a++) {
+ ver = RE_findOrAddVert(obr, a);
+ mul_m4_v3(mat, ver->co);
+ if (lnors) {
+ mul_m3_v3(rot, ver->n);
+ negate_v3(ver->n);
+ }
+ }
+ for (a = 0; a < obr->totvlak; a++) {
+ vlr = RE_findOrAddVlak(obr, a);
+
+ /* skip wire faces */
+ if (vlr->v2 != vlr->v3) {
+ if (vlr->v4)
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ else
+ normal_tri_v3(vlr->n, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* Orco hash and Materials */
+/* ------------------------------------------------------------------------- */
+
+static float *get_object_orco(Render *re, void *ob)
+{
+ if (!re->orco_hash) {
+ return NULL;
+ }
+
+ return BLI_ghash_lookup(re->orco_hash, ob);
+}
+
+static void set_object_orco(Render *re, void *ob, float *orco)
+{
+ if (!re->orco_hash)
+ re->orco_hash = BLI_ghash_ptr_new("set_object_orco gh");
+
+ BLI_ghash_insert(re->orco_hash, ob, orco);
+}
+
+static void free_mesh_orco_hash(Render *re)
+{
+ if (re->orco_hash) {
+ BLI_ghash_free(re->orco_hash, NULL, MEM_freeN);
+ re->orco_hash = NULL;
+ }
+}
+
+static void check_material_mapto(Material *ma)
+{
+ int a;
+ ma->mapto_textured = 0;
+
+ /* cache which inputs are actually textured.
+ * this can avoid a bit of time spent iterating through all the texture slots, map inputs and map tos
+ * every time a property which may or may not be textured is accessed */
+
+ for (a=0; a<MAX_MTEX; a++) {
+ if (ma->mtex[a] && ma->mtex[a]->tex) {
+ /* currently used only in volume render, so we'll check for those flags */
+ if (ma->mtex[a]->mapto & MAP_DENSITY) ma->mapto_textured |= MAP_DENSITY;
+ if (ma->mtex[a]->mapto & MAP_EMISSION) ma->mapto_textured |= MAP_EMISSION;
+ if (ma->mtex[a]->mapto & MAP_EMISSION_COL) ma->mapto_textured |= MAP_EMISSION_COL;
+ if (ma->mtex[a]->mapto & MAP_SCATTERING) ma->mapto_textured |= MAP_SCATTERING;
+ if (ma->mtex[a]->mapto & MAP_TRANSMISSION_COL) ma->mapto_textured |= MAP_TRANSMISSION_COL;
+ if (ma->mtex[a]->mapto & MAP_REFLECTION) ma->mapto_textured |= MAP_REFLECTION;
+ if (ma->mtex[a]->mapto & MAP_REFLECTION_COL) ma->mapto_textured |= MAP_REFLECTION_COL;
+ }
+ }
+}
+static void flag_render_node_material(Render *re, bNodeTree *ntree)
+{
+ bNode *node;
+
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (node->id) {
+ if (GS(node->id->name)==ID_MA) {
+ Material *ma= (Material *)node->id;
+
+ if ((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP))
+ re->flag |= R_ZTRA;
+
+ ma->flag |= MA_IS_USED;
+ }
+ else if (node->type==NODE_GROUP)
+ flag_render_node_material(re, (bNodeTree *)node->id);
+ }
+ }
+}
+
+static Material *give_render_material(Render *re, Object *ob, short nr)
+{
+ extern Material defmaterial; /* material.c */
+ Material *ma;
+
+ ma= give_current_material(ob, nr);
+ if (ma==NULL)
+ ma= &defmaterial;
+
+ if (re->r.mode & R_SPEED) ma->texco |= NEED_UV;
+
+ if (ma->material_type == MA_TYPE_VOLUME) {
+ ma->mode |= MA_TRANSP;
+ ma->mode &= ~MA_SHADBUF;
+ }
+ if ((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP))
+ re->flag |= R_ZTRA;
+
+ /* for light groups and SSS */
+ ma->flag |= MA_IS_USED;
+
+ if (ma->nodetree && ma->use_nodes)
+ flag_render_node_material(re, ma->nodetree);
+
+ check_material_mapto(ma);
+
+ return ma;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Particles */
+/* ------------------------------------------------------------------------- */
+typedef struct ParticleStrandData {
+ struct MCol *mcol;
+ float *orco, *uvco, *surfnor;
+ float time, adapt_angle, adapt_pix, size;
+ int totuv, totcol;
+ int first, line, adapt, override_uv;
+}
+ParticleStrandData;
+/* future thread problem... */
+static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, ParticleStrandData *sd, const float vec[3], const float vec1[3])
+{
+ static VertRen *v1= NULL, *v2= NULL;
+ VlakRen *vlr= NULL;
+ float nor[3], cross[3], crosslen, w, dx, dy, width;
+ static float anor[3], avec[3];
+ int flag, i;
+ static int second=0;
+
+ sub_v3_v3v3(nor, vec, vec1);
+ normalize_v3(nor); /* nor needed as tangent */
+ cross_v3_v3v3(cross, vec, nor);
+
+ /* turn cross in pixelsize */
+ w= vec[2]*re->winmat[2][3] + re->winmat[3][3];
+ dx= re->winx*cross[0]*re->winmat[0][0];
+ dy= re->winy*cross[1]*re->winmat[1][1];
+ w = sqrtf(dx * dx + dy * dy) / w;
+
+ if (w!=0.0f) {
+ float fac;
+ if (ma->strand_ease!=0.0f) {
+ if (ma->strand_ease<0.0f)
+ fac= pow(sd->time, 1.0f+ma->strand_ease);
+ else
+ fac= pow(sd->time, 1.0f/(1.0f-ma->strand_ease));
+ }
+ else fac= sd->time;
+
+ width= ((1.0f-fac)*ma->strand_sta + (fac)*ma->strand_end);
+
+ /* use actual Blender units for strand width and fall back to minimum width */
+ if (ma->mode & MA_STR_B_UNITS) {
+ crosslen= len_v3(cross);
+ w= 2.0f*crosslen*ma->strand_min/w;
+
+ if (width < w)
+ width= w;
+
+ /*cross is the radius of the strand so we want it to be half of full width */
+ mul_v3_fl(cross, 0.5f/crosslen);
+ }
+ else
+ width/=w;
+
+ mul_v3_fl(cross, width);
+ }
+
+ if (ma->mode & MA_TANGENT_STR)
+ flag= R_SMOOTH|R_TANGENT;
+ else
+ flag= R_SMOOTH;
+
+ /* only 1 pixel wide strands filled in as quads now, otherwise zbuf errors */
+ if (ma->strand_sta==1.0f)
+ flag |= R_STRAND;
+
+ /* single face line */
+ if (sd->line) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->flag= flag;
+ vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ copy_v3_v3(vlr->v1->co, vec);
+ add_v3_v3(vlr->v1->co, cross);
+ copy_v3_v3(vlr->v1->n, nor);
+ vlr->v1->orco= sd->orco;
+ vlr->v1->accum = -1.0f; /* accum abuse for strand texco */
+
+ copy_v3_v3(vlr->v2->co, vec);
+ sub_v3_v3v3(vlr->v2->co, vlr->v2->co, cross);
+ copy_v3_v3(vlr->v2->n, nor);
+ vlr->v2->orco= sd->orco;
+ vlr->v2->accum= vlr->v1->accum;
+
+ copy_v3_v3(vlr->v4->co, vec1);
+ add_v3_v3(vlr->v4->co, cross);
+ copy_v3_v3(vlr->v4->n, nor);
+ vlr->v4->orco= sd->orco;
+ vlr->v4->accum = 1.0f; /* accum abuse for strand texco */
+
+ copy_v3_v3(vlr->v3->co, vec1);
+ sub_v3_v3v3(vlr->v3->co, vlr->v3->co, cross);
+ copy_v3_v3(vlr->v3->n, nor);
+ vlr->v3->orco= sd->orco;
+ vlr->v3->accum= vlr->v4->accum;
+
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V2V3;
+
+ if (sd->surfnor) {
+ float *snor= RE_vlakren_get_surfnor(obr, vlr, 1);
+ copy_v3_v3(snor, sd->surfnor);
+ }
+
+ if (sd->uvco) {
+ for (i=0; i<sd->totuv; i++) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, i, NULL, 1);
+ mtf->uv[0][0]=mtf->uv[1][0]=
+ mtf->uv[2][0]=mtf->uv[3][0]=(sd->uvco+2*i)[0];
+ mtf->uv[0][1]=mtf->uv[1][1]=
+ mtf->uv[2][1]=mtf->uv[3][1]=(sd->uvco+2*i)[1];
+ }
+ if (sd->override_uv>=0) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, sd->override_uv, NULL, 0);
+
+ mtf->uv[0][0]=mtf->uv[3][0]=0.0f;
+ mtf->uv[1][0]=mtf->uv[2][0]=1.0f;
+
+ mtf->uv[0][1]=mtf->uv[1][1]=0.0f;
+ mtf->uv[2][1]=mtf->uv[3][1]=1.0f;
+ }
+ }
+ if (sd->mcol) {
+ for (i=0; i<sd->totcol; i++) {
+ MCol *mc;
+ mc=RE_vlakren_get_mcol(obr, vlr, i, NULL, 1);
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ }
+ }
+ }
+ /* first two vertices of a strand */
+ else if (sd->first) {
+ if (sd->adapt) {
+ copy_v3_v3(anor, nor);
+ copy_v3_v3(avec, vec);
+ second=1;
+ }
+
+ v1= RE_findOrAddVert(obr, obr->totvert++);
+ v2= RE_findOrAddVert(obr, obr->totvert++);
+
+ copy_v3_v3(v1->co, vec);
+ add_v3_v3(v1->co, cross);
+ copy_v3_v3(v1->n, nor);
+ v1->orco= sd->orco;
+ v1->accum = -1.0f; /* accum abuse for strand texco */
+
+ copy_v3_v3(v2->co, vec);
+ sub_v3_v3v3(v2->co, v2->co, cross);
+ copy_v3_v3(v2->n, nor);
+ v2->orco= sd->orco;
+ v2->accum= v1->accum;
+ }
+ /* more vertices & faces to strand */
+ else {
+ if (sd->adapt==0 || second) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->flag= flag;
+ vlr->v1= v1;
+ vlr->v2= v2;
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ v1= vlr->v4; /* cycle */
+ v2= vlr->v3; /* cycle */
+
+
+ if (sd->adapt) {
+ second=0;
+ copy_v3_v3(anor, nor);
+ copy_v3_v3(avec, vec);
+ }
+
+ }
+ else if (sd->adapt) {
+ float dvec[3], pvec[3];
+ sub_v3_v3v3(dvec, avec, vec);
+ project_v3_v3v3(pvec, dvec, vec);
+ sub_v3_v3v3(dvec, dvec, pvec);
+
+ w= vec[2]*re->winmat[2][3] + re->winmat[3][3];
+ dx= re->winx*dvec[0]*re->winmat[0][0]/w;
+ dy= re->winy*dvec[1]*re->winmat[1][1]/w;
+ w = sqrtf(dx * dx + dy * dy);
+ if (dot_v3v3(anor, nor)<sd->adapt_angle && w>sd->adapt_pix) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->flag= flag;
+ vlr->v1= v1;
+ vlr->v2= v2;
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ v1= vlr->v4; /* cycle */
+ v2= vlr->v3; /* cycle */
+
+ copy_v3_v3(anor, nor);
+ copy_v3_v3(avec, vec);
+ }
+ else {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak-1);
+ }
+ }
+
+ copy_v3_v3(vlr->v4->co, vec);
+ add_v3_v3(vlr->v4->co, cross);
+ copy_v3_v3(vlr->v4->n, nor);
+ vlr->v4->orco= sd->orco;
+ vlr->v4->accum= -1.0f + 2.0f * sd->time; /* accum abuse for strand texco */
+
+ copy_v3_v3(vlr->v3->co, vec);
+ sub_v3_v3v3(vlr->v3->co, vlr->v3->co, cross);
+ copy_v3_v3(vlr->v3->n, nor);
+ vlr->v3->orco= sd->orco;
+ vlr->v3->accum= vlr->v4->accum;
+
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V2V3;
+
+ if (sd->surfnor) {
+ float *snor= RE_vlakren_get_surfnor(obr, vlr, 1);
+ copy_v3_v3(snor, sd->surfnor);
+ }
+
+ if (sd->uvco) {
+ for (i=0; i<sd->totuv; i++) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, i, NULL, 1);
+ mtf->uv[0][0]=mtf->uv[1][0]=
+ mtf->uv[2][0]=mtf->uv[3][0]=(sd->uvco+2*i)[0];
+ mtf->uv[0][1]=mtf->uv[1][1]=
+ mtf->uv[2][1]=mtf->uv[3][1]=(sd->uvco+2*i)[1];
+ }
+ if (sd->override_uv>=0) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, sd->override_uv, NULL, 0);
+
+ mtf->uv[0][0]=mtf->uv[3][0]=0.0f;
+ mtf->uv[1][0]=mtf->uv[2][0]=1.0f;
+
+ mtf->uv[0][1]=mtf->uv[1][1]=(vlr->v1->accum+1.0f)/2.0f;
+ mtf->uv[2][1]=mtf->uv[3][1]=(vlr->v3->accum+1.0f)/2.0f;
+ }
+ }
+ if (sd->mcol) {
+ for (i=0; i<sd->totcol; i++) {
+ MCol *mc;
+ mc=RE_vlakren_get_mcol(obr, vlr, i, NULL, 1);
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ }
+ }
+ }
+}
+
+static void static_particle_wire(ObjectRen *obr, Material *ma, const float vec[3], const float vec1[3], int first, int line)
+{
+ VlakRen *vlr;
+ static VertRen *v1;
+
+ if (line) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= vlr->v2;
+ vlr->v4= NULL;
+
+ copy_v3_v3(vlr->v1->co, vec);
+ copy_v3_v3(vlr->v2->co, vec1);
+
+ sub_v3_v3v3(vlr->n, vec, vec1);
+ normalize_v3(vlr->n);
+ copy_v3_v3(vlr->v1->n, vlr->n);
+ copy_v3_v3(vlr->v2->n, vlr->n);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V1V2;
+
+ }
+ else if (first) {
+ v1= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(v1->co, vec);
+ }
+ else {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= v1;
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= vlr->v2;
+ vlr->v4= NULL;
+
+ v1= vlr->v2; /* cycle */
+ copy_v3_v3(v1->co, vec);
+
+ sub_v3_v3v3(vlr->n, vec, vec1);
+ normalize_v3(vlr->n);
+ copy_v3_v3(v1->n, vlr->n);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V1V2;
+ }
+
+}
+
+static void particle_curve(Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma, ParticleStrandData *sd,
+ const float loc[3], const float loc1[3], int seed, float *pa_co)
+{
+ HaloRen *har = NULL;
+
+ if (ma->material_type == MA_TYPE_WIRE)
+ static_particle_wire(obr, ma, loc, loc1, sd->first, sd->line);
+ else if (ma->material_type == MA_TYPE_HALO) {
+ har= RE_inithalo_particle(re, obr, dm, ma, loc, loc1, sd->orco, sd->uvco, sd->size, 1.0, seed, pa_co);
+ if (har) har->lay= obr->ob->lay;
+ }
+ else
+ static_particle_strand(re, obr, ma, sd, loc, loc1);
+}
+static void particle_billboard(Render *re, ObjectRen *obr, Material *ma, ParticleBillboardData *bb)
+{
+ VlakRen *vlr;
+ MTFace *mtf;
+ float xvec[3], yvec[3], zvec[3], bb_center[3];
+ /* Number of tiles */
+ int totsplit = bb->uv_split * bb->uv_split;
+ int tile, x, y;
+ /* Tile offsets */
+ float uvx = 0.0f, uvy = 0.0f, uvdx = 1.0f, uvdy = 1.0f, time = 0.0f;
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ psys_make_billboard(bb, xvec, yvec, zvec, bb_center);
+
+ add_v3_v3v3(vlr->v1->co, bb_center, xvec);
+ add_v3_v3(vlr->v1->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v1->co);
+
+ sub_v3_v3v3(vlr->v2->co, bb_center, xvec);
+ add_v3_v3(vlr->v2->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v2->co);
+
+ sub_v3_v3v3(vlr->v3->co, bb_center, xvec);
+ sub_v3_v3v3(vlr->v3->co, vlr->v3->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v3->co);
+
+ add_v3_v3v3(vlr->v4->co, bb_center, xvec);
+ sub_v3_v3(vlr->v4->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v4->co);
+
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ copy_v3_v3(vlr->v1->n, vlr->n);
+ copy_v3_v3(vlr->v2->n, vlr->n);
+ copy_v3_v3(vlr->v3->n, vlr->n);
+ copy_v3_v3(vlr->v4->n, vlr->n);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V2V3;
+
+ if (bb->uv_split > 1) {
+ uvdx = uvdy = 1.0f / (float)bb->uv_split;
+
+ if (ELEM(bb->anim, PART_BB_ANIM_AGE, PART_BB_ANIM_FRAME)) {
+ if (bb->anim == PART_BB_ANIM_FRAME)
+ time = ((int)(bb->time * bb->lifetime) % totsplit)/(float)totsplit;
+ else
+ time = bb->time;
+ }
+ else if (bb->anim == PART_BB_ANIM_ANGLE) {
+ if (bb->align == PART_BB_VIEW) {
+ time = (float)fmod((bb->tilt + 1.0f) / 2.0f, 1.0);
+ }
+ else {
+ float axis1[3] = {0.0f, 0.0f, 0.0f};
+ float axis2[3] = {0.0f, 0.0f, 0.0f};
+
+ axis1[(bb->align + 1) % 3] = 1.0f;
+ axis2[(bb->align + 2) % 3] = 1.0f;
+
+ if (bb->lock == 0) {
+ zvec[bb->align] = 0.0f;
+ normalize_v3(zvec);
+ }
+
+ time = saacos(dot_v3v3(zvec, axis1)) / (float)M_PI;
+
+ if (dot_v3v3(zvec, axis2) < 0.0f)
+ time = 1.0f - time / 2.0f;
+ else
+ time /= 2.0f;
+ }
+ }
+
+ if (bb->split_offset == PART_BB_OFF_LINEAR)
+ time = (float)fmod(time + (float)bb->num / (float)totsplit, 1.0f);
+ else if (bb->split_offset==PART_BB_OFF_RANDOM)
+ time = (float)fmod(time + bb->random, 1.0f);
+
+ /* Find the coordinates in tile space (integer), then convert to UV
+ * space (float). Note that Y is flipped. */
+ tile = (int)((time + FLT_EPSILON10) * totsplit);
+ x = tile % bb->uv_split;
+ y = tile / bb->uv_split;
+ y = (bb->uv_split - 1) - y;
+ uvx = uvdx * x;
+ uvy = uvdy * y;
+ }
+
+ /* normal UVs */
+ if (bb->uv[0] >= 0) {
+ mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[0], NULL, 1);
+ mtf->uv[0][0] = 1.0f;
+ mtf->uv[0][1] = 1.0f;
+ mtf->uv[1][0] = 0.0f;
+ mtf->uv[1][1] = 1.0f;
+ mtf->uv[2][0] = 0.0f;
+ mtf->uv[2][1] = 0.0f;
+ mtf->uv[3][0] = 1.0f;
+ mtf->uv[3][1] = 0.0f;
+ }
+
+ /* time-index UVs */
+ if (bb->uv[1] >= 0) {
+ mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[1], NULL, 1);
+ mtf->uv[0][0] = mtf->uv[1][0] = mtf->uv[2][0] = mtf->uv[3][0] = bb->time;
+ mtf->uv[0][1] = mtf->uv[1][1] = mtf->uv[2][1] = mtf->uv[3][1] = (float)bb->num/(float)bb->totnum;
+ }
+
+ /* split UVs */
+ if (bb->uv_split > 1 && bb->uv[2] >= 0) {
+ mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[2], NULL, 1);
+ mtf->uv[0][0] = uvx + uvdx;
+ mtf->uv[0][1] = uvy + uvdy;
+ mtf->uv[1][0] = uvx;
+ mtf->uv[1][1] = uvy + uvdy;
+ mtf->uv[2][0] = uvx;
+ mtf->uv[2][1] = uvy;
+ mtf->uv[3][0] = uvx + uvdx;
+ mtf->uv[3][1] = uvy;
+ }
+}
+static void particle_normal_ren(short ren_as, ParticleSettings *part, Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma, ParticleStrandData *sd, ParticleBillboardData *bb, ParticleKey *state, int seed, float hasize, float *pa_co)
+{
+ float loc[3], loc0[3], loc1[3], vel[3];
+
+ copy_v3_v3(loc, state->co);
+
+ if (ren_as != PART_DRAW_BB)
+ mul_m4_v3(re->viewmat, loc);
+
+ switch (ren_as) {
+ case PART_DRAW_LINE:
+ sd->line = 1;
+ sd->time = 0.0f;
+ sd->size = hasize;
+
+ mul_v3_mat3_m4v3(vel, re->viewmat, state->vel);
+ normalize_v3(vel);
+
+ if (part->draw & PART_DRAW_VEL_LENGTH)
+ mul_v3_fl(vel, len_v3(state->vel));
+
+ madd_v3_v3v3fl(loc0, loc, vel, -part->draw_line[0]);
+ madd_v3_v3v3fl(loc1, loc, vel, part->draw_line[1]);
+
+ particle_curve(re, obr, dm, ma, sd, loc0, loc1, seed, pa_co);
+
+ break;
+
+ case PART_DRAW_BB:
+
+ copy_v3_v3(bb->vec, loc);
+ copy_v3_v3(bb->vel, state->vel);
+
+ particle_billboard(re, obr, ma, bb);
+
+ break;
+
+ default:
+ {
+ HaloRen *har = NULL;
+
+ har = RE_inithalo_particle(re, obr, dm, ma, loc, NULL, sd->orco, sd->uvco, hasize, 0.0, seed, pa_co);
+
+ if (har) har->lay= obr->ob->lay;
+
+ break;
+ }
+ }
+}
+static void get_particle_uvco_mcol(short from, DerivedMesh *dm, float *fuv, int num, ParticleStrandData *sd)
+{
+ int i;
+
+ /* get uvco */
+ if (sd->uvco && ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+ for (i=0; i<sd->totuv; i++) {
+ if (!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
+ MFace *mface = dm->getTessFaceData(dm, num, CD_MFACE);
+ MTFace *mtface = (MTFace*)CustomData_get_layer_n(&dm->faceData, CD_MTFACE, i);
+ mtface += num;
+
+ psys_interpolate_uvs(mtface, mface->v4, fuv, sd->uvco + 2 * i);
+ }
+ else {
+ sd->uvco[2*i] = 0.0f;
+ sd->uvco[2*i + 1] = 0.0f;
+ }
+ }
+ }
+
+ /* get mcol */
+ if (sd->mcol && ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+ for (i=0; i<sd->totcol; i++) {
+ if (!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
+ MFace *mface = dm->getTessFaceData(dm, num, CD_MFACE);
+ MCol *mc = (MCol*)CustomData_get_layer_n(&dm->faceData, CD_MCOL, i);
+ mc += num * 4;
+
+ psys_interpolate_mcol(mc, mface->v4, fuv, sd->mcol + i);
+ }
+ else
+ memset(&sd->mcol[i], 0, sizeof(MCol));
+ }
+ }
+}
+static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem *psys, int timeoffset)
+{
+ Object *ob= obr->ob;
+// Object *tob=0;
+ Material *ma = NULL;
+ ParticleSystemModifierData *psmd;
+ ParticleSystem *tpsys = NULL;
+ ParticleSettings *part, *tpart = NULL;
+ ParticleData *pars, *pa = NULL, *tpa = NULL;
+ ParticleKey *states = NULL;
+ ParticleKey state;
+ ParticleCacheKey *cache = NULL;
+ ParticleBillboardData bb;
+ ParticleSimulationData sim = {NULL};
+ ParticleStrandData sd;
+ StrandBuffer *strandbuf = NULL;
+ StrandVert *svert = NULL;
+ StrandBound *sbound = NULL;
+ StrandRen *strand = NULL;
+ RNG *rng = NULL;
+ float loc[3], loc1[3], loc0[3], mat[4][4], nmat[3][3], co[3], nor[3], duplimat[4][4];
+ float strandlen=0.0f, curlen=0.0f;
+ float hasize, pa_size, r_tilt, r_length;
+ float pa_time, pa_birthtime, pa_dietime;
+ float random, simplify[2], pa_co[3];
+ const float cfra= BKE_scene_frame_get(re->scene);
+ int i, a, k, max_k=0, totpart;
+ bool do_simplify = false, do_surfacecache = false, use_duplimat = false;
+ int totchild=0, step_nbr;
+ int seed, path_nbr=0, orco1=0, num;
+ int totface;
+
+ const int *index_mf_to_mpoly = NULL;
+ const int *index_mp_to_orig = NULL;
+
+/* 1. check that everything is ok & updated */
+ if (psys==NULL)
+ return 0;
+
+ part=psys->part;
+ pars=psys->particles;
+
+ if (part==NULL || pars==NULL || !psys_check_enabled(ob, psys, G.is_rendering))
+ return 0;
+
+ if (part->ren_as==PART_DRAW_OB || part->ren_as==PART_DRAW_GR || part->ren_as==PART_DRAW_NOT)
+ return 1;
+
+ if ((re->r.scemode & R_VIEWPORT_PREVIEW) && (ob->mode & OB_MODE_PARTICLE_EDIT))
+ return 0;
+
+ if (part->ren_as == PART_DRAW_BB && part->bb_ob == NULL && RE_GetCamera(re) == NULL)
+ return 0;
+
+/* 2. start initializing things */
+
+ /* last possibility to bail out! */
+ psmd = psys_get_modifier(ob, psys);
+ if (!(psmd->modifier.mode & eModifierMode_Render))
+ return 0;
+
+ sim.scene= re->scene;
+ sim.ob= ob;
+ sim.psys= psys;
+ sim.psmd= psmd;
+
+ if (part->phystype==PART_PHYS_KEYED)
+ psys_count_keyed_targets(&sim);
+
+ totchild=psys->totchild;
+
+ /* can happen for disconnected/global hair */
+ if (part->type==PART_HAIR && !psys->childcache)
+ totchild= 0;
+
+ if (re->r.scemode & R_VIEWPORT_PREVIEW) { /* preview render */
+ totchild = (int)((float)totchild * (float)part->disp / 100.0f);
+ step_nbr = 1 << part->draw_step;
+ }
+ else {
+ step_nbr = 1 << part->ren_step;
+ }
+ if (ELEM(part->kink, PART_KINK_SPIRAL))
+ step_nbr += part->kink_extra_steps;
+
+ psys->flag |= PSYS_DRAWING;
+
+ rng= BLI_rng_new(psys->seed);
+
+ totpart=psys->totpart;
+
+ memset(&sd, 0, sizeof(ParticleStrandData));
+ sd.override_uv = -1;
+
+/* 2.1 setup material stff */
+ ma= give_render_material(re, ob, part->omat);
+
+#if 0 /* XXX old animation system */
+ if (ma->ipo) {
+ calc_ipo(ma->ipo, cfra);
+ execute_ipo((ID *)ma, ma->ipo);
+ }
+#endif /* XXX old animation system */
+
+ hasize = ma->hasize;
+ seed = ma->seed1;
+
+ re->flag |= R_HALO;
+
+ RE_set_customdata_names(obr, &psmd->dm_final->faceData);
+ sd.totuv = CustomData_number_of_layers(&psmd->dm_final->faceData, CD_MTFACE);
+ sd.totcol = CustomData_number_of_layers(&psmd->dm_final->faceData, CD_MCOL);
+
+ if (ma->texco & TEXCO_UV && sd.totuv) {
+ sd.uvco = MEM_callocN(sd.totuv * 2 * sizeof(float), "particle_uvs");
+
+ if (ma->strand_uvname[0]) {
+ sd.override_uv = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, ma->strand_uvname);
+ sd.override_uv -= CustomData_get_layer_index(&psmd->dm_final->faceData, CD_MTFACE);
+ }
+ }
+ else
+ sd.uvco = NULL;
+
+ if (sd.totcol)
+ sd.mcol = MEM_callocN(sd.totcol * sizeof(MCol), "particle_mcols");
+
+/* 2.2 setup billboards */
+ if (part->ren_as == PART_DRAW_BB) {
+ int first_uv = CustomData_get_layer_index(&psmd->dm_final->faceData, CD_MTFACE);
+
+ bb.uv[0] = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, psys->bb_uvname[0]);
+ if (bb.uv[0] < 0)
+ bb.uv[0] = CustomData_get_active_layer_index(&psmd->dm_final->faceData, CD_MTFACE);
+
+ bb.uv[1] = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, psys->bb_uvname[1]);
+
+ bb.uv[2] = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, psys->bb_uvname[2]);
+
+ if (first_uv >= 0) {
+ bb.uv[0] -= first_uv;
+ bb.uv[1] -= first_uv;
+ bb.uv[2] -= first_uv;
+ }
+
+ bb.align = part->bb_align;
+ bb.anim = part->bb_anim;
+ bb.lock = part->draw & PART_DRAW_BB_LOCK;
+ bb.ob = (part->bb_ob ? part->bb_ob : RE_GetCamera(re));
+ bb.split_offset = part->bb_split_offset;
+ bb.totnum = totpart+totchild;
+ bb.uv_split = part->bb_uv_split;
+ }
+
+/* 2.5 setup matrices */
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat); /* need to be that way, for imat texture */
+ transpose_m3_m4(nmat, ob->imat);
+
+ if (psys->flag & PSYS_USE_IMAT) {
+ /* psys->imat is the original emitter's inverse matrix, ob->obmat is the duplicated object's matrix */
+ mul_m4_m4m4(duplimat, ob->obmat, psys->imat);
+ use_duplimat = true;
+ }
+
+/* 2.6 setup strand rendering */
+ if (part->ren_as == PART_DRAW_PATH && psys->pathcache) {
+ path_nbr = step_nbr;
+
+ if (path_nbr) {
+ if (!ELEM(ma->material_type, MA_TYPE_HALO, MA_TYPE_WIRE)) {
+ sd.orco = get_object_orco(re, psys);
+ if (!sd.orco) {
+ sd.orco = MEM_mallocN(3*sizeof(float)*(totpart+totchild), "particle orcos");
+ set_object_orco(re, psys, sd.orco);
+ }
+ }
+ }
+
+ if (part->draw & PART_DRAW_REN_ADAPT) {
+ sd.adapt = 1;
+ sd.adapt_pix = (float)part->adapt_pix;
+ sd.adapt_angle = cosf(DEG2RADF((float)part->adapt_angle));
+ }
+
+ if (part->draw & PART_DRAW_REN_STRAND) {
+ strandbuf= RE_addStrandBuffer(obr, (totpart+totchild)*(path_nbr+1));
+ strandbuf->ma= ma;
+ strandbuf->lay= ob->lay;
+ copy_m4_m4(strandbuf->winmat, re->winmat);
+ strandbuf->winx= re->winx;
+ strandbuf->winy= re->winy;
+ strandbuf->maxdepth= 2;
+ strandbuf->adaptcos= cosf(DEG2RADF((float)part->adapt_angle));
+ strandbuf->overrideuv= sd.override_uv;
+ strandbuf->minwidth= ma->strand_min;
+
+ if (ma->strand_widthfade == 0.0f)
+ strandbuf->widthfade= -1.0f;
+ else if (ma->strand_widthfade >= 1.0f)
+ strandbuf->widthfade= 2.0f - ma->strand_widthfade;
+ else
+ strandbuf->widthfade= 1.0f/MAX2(ma->strand_widthfade, 1e-5f);
+
+ if (part->flag & PART_HAIR_BSPLINE)
+ strandbuf->flag |= R_STRAND_BSPLINE;
+ if (ma->mode & MA_STR_B_UNITS)
+ strandbuf->flag |= R_STRAND_B_UNITS;
+
+ svert= strandbuf->vert;
+
+ if (re->r.mode & R_SPEED)
+ do_surfacecache = true;
+ else if ((re->wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT)) && (re->wrld.ao_gather_method == WO_AOGATHER_APPROX))
+ if (ma->amb != 0.0f)
+ do_surfacecache = true;
+
+ totface= psmd->dm_final->getNumTessFaces(psmd->dm_final);
+ index_mf_to_mpoly = psmd->dm_final->getTessFaceDataArray(psmd->dm_final, CD_ORIGINDEX);
+ index_mp_to_orig = psmd->dm_final->getPolyDataArray(psmd->dm_final, CD_ORIGINDEX);
+ if (index_mf_to_mpoly == NULL) {
+ index_mp_to_orig = NULL;
+ }
+ for (a=0; a<totface; a++)
+ strandbuf->totbound = max_ii(strandbuf->totbound, (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a): a);
+
+ strandbuf->totbound++;
+ strandbuf->bound= MEM_callocN(sizeof(StrandBound)*strandbuf->totbound, "StrandBound");
+ sbound= strandbuf->bound;
+ sbound->start= sbound->end= 0;
+ }
+ }
+
+ if (sd.orco == NULL) {
+ sd.orco = MEM_mallocN(3 * sizeof(float), "particle orco");
+ orco1 = 1;
+ }
+
+ if (path_nbr == 0)
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+/* 3. start creating renderable things */
+ for (a=0, pa=pars; a<totpart+totchild; a++, pa++, seed++) {
+ random = BLI_rng_get_float(rng);
+ /* setup per particle individual stuff */
+ if (a<totpart) {
+ if (pa->flag & PARS_UNEXIST) continue;
+
+ pa_time=(cfra-pa->time)/pa->lifetime;
+ pa_birthtime = pa->time;
+ pa_dietime = pa->dietime;
+
+ hasize = ma->hasize;
+
+ /* XXX 'tpsys' is alwyas NULL, this code won't run! */
+ /* get orco */
+ if (tpsys && part->phystype == PART_PHYS_NO) {
+ tpa = tpsys->particles + pa->num;
+ psys_particle_on_emitter(
+ psmd,
+ tpart->from, tpa->num, pa->num_dmcache, tpa->fuv,
+ tpa->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+ else {
+ psys_particle_on_emitter(
+ psmd,
+ part->from, pa->num, pa->num_dmcache,
+ pa->fuv, pa->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+
+ /* get uvco & mcol */
+ num= pa->num_dmcache;
+
+ if (num == DMCACHE_NOTFOUND)
+ if (pa->num < psmd->dm_final->getNumTessFaces(psmd->dm_final))
+ num= pa->num;
+
+ get_particle_uvco_mcol(part->from, psmd->dm_final, pa->fuv, num, &sd);
+
+ pa_size = pa->size;
+
+ r_tilt = 2.0f*(psys_frand(psys, a) - 0.5f);
+ r_length = psys_frand(psys, a+1);
+
+ if (path_nbr) {
+ cache = psys->pathcache[a];
+ max_k = (int)cache->segments;
+ }
+
+ if (totchild && (part->draw&PART_DRAW_PARENT)==0) continue;
+ }
+ else {
+ ChildParticle *cpa= psys->child+a-totpart;
+
+ if (path_nbr) {
+ cache = psys->childcache[a-totpart];
+
+ if (cache->segments < 0)
+ continue;
+
+ max_k = (int)cache->segments;
+ }
+
+ pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime);
+ pa_size = psys_get_child_size(psys, cpa, cfra, &pa_time);
+
+ r_tilt = 2.0f*(psys_frand(psys, a + 21) - 0.5f);
+ r_length = psys_frand(psys, a + 22);
+
+ num = cpa->num;
+
+ /* get orco */
+ if (part->childtype == PART_CHILD_FACES) {
+ psys_particle_on_emitter(
+ psmd,
+ PART_FROM_FACE, cpa->num, DMCACHE_ISCHILD,
+ cpa->fuv, cpa->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+ else {
+ ParticleData *par = psys->particles + cpa->parent;
+ psys_particle_on_emitter(
+ psmd,
+ part->from, par->num, DMCACHE_ISCHILD, par->fuv,
+ par->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+
+ /* get uvco & mcol */
+ if (part->childtype==PART_CHILD_FACES) {
+ get_particle_uvco_mcol(PART_FROM_FACE, psmd->dm_final, cpa->fuv, cpa->num, &sd);
+ }
+ else {
+ ParticleData *parent = psys->particles + cpa->parent;
+ num = parent->num_dmcache;
+
+ if (num == DMCACHE_NOTFOUND)
+ if (parent->num < psmd->dm_final->getNumTessFaces(psmd->dm_final))
+ num = parent->num;
+
+ get_particle_uvco_mcol(part->from, psmd->dm_final, parent->fuv, num, &sd);
+ }
+
+ do_simplify = psys_render_simplify_params(psys, cpa, simplify);
+
+ if (strandbuf) {
+ int orignum = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, cpa->num) : cpa->num;
+
+ if ((orignum > sbound - strandbuf->bound) &&
+ (orignum < strandbuf->totbound))
+ {
+ sbound = &strandbuf->bound[orignum];
+ sbound->start = sbound->end = obr->totstrand;
+ }
+ }
+ }
+
+ /* TEXCO_PARTICLE */
+ pa_co[0] = pa_time;
+ pa_co[1] = 0.f;
+ pa_co[2] = 0.f;
+
+ /* surface normal shading setup */
+ if (ma->mode_l & MA_STR_SURFDIFF) {
+ mul_m3_v3(nmat, nor);
+ sd.surfnor= nor;
+ }
+ else
+ sd.surfnor= NULL;
+
+ /* strand render setup */
+ if (strandbuf) {
+ strand= RE_findOrAddStrand(obr, obr->totstrand++);
+ strand->buffer= strandbuf;
+ strand->vert= svert;
+ copy_v3_v3(strand->orco, sd.orco);
+
+ if (do_simplify) {
+ float *ssimplify= RE_strandren_get_simplify(obr, strand, 1);
+ ssimplify[0]= simplify[0];
+ ssimplify[1]= simplify[1];
+ }
+
+ if (sd.surfnor) {
+ float *snor= RE_strandren_get_surfnor(obr, strand, 1);
+ copy_v3_v3(snor, sd.surfnor);
+ }
+
+ if (do_surfacecache && num >= 0) {
+ int *facenum= RE_strandren_get_face(obr, strand, 1);
+ *facenum= num;
+ }
+
+ if (sd.uvco) {
+ for (i=0; i<sd.totuv; i++) {
+ if (i != sd.override_uv) {
+ float *uv= RE_strandren_get_uv(obr, strand, i, NULL, 1);
+
+ uv[0]= sd.uvco[2*i];
+ uv[1]= sd.uvco[2*i+1];
+ }
+ }
+ }
+ if (sd.mcol) {
+ for (i=0; i<sd.totcol; i++) {
+ MCol *mc= RE_strandren_get_mcol(obr, strand, i, NULL, 1);
+ *mc = sd.mcol[i];
+ }
+ }
+
+ sbound->end++;
+ }
+
+ /* strandco computation setup */
+ if (path_nbr) {
+ strandlen= 0.0f;
+ curlen= 0.0f;
+ for (k=1; k<=path_nbr; k++)
+ if (k<=max_k)
+ strandlen += len_v3v3((cache+k-1)->co, (cache+k)->co);
+ }
+
+ if (path_nbr) {
+ /* render strands */
+ for (k=0; k<=path_nbr; k++) {
+ float time;
+
+ if (k<=max_k) {
+ copy_v3_v3(state.co, (cache+k)->co);
+ copy_v3_v3(state.vel, (cache+k)->vel);
+ }
+ else
+ continue;
+
+ if (k > 0)
+ curlen += len_v3v3((cache+k-1)->co, (cache+k)->co);
+ time= curlen/strandlen;
+
+ copy_v3_v3(loc, state.co);
+ mul_m4_v3(re->viewmat, loc);
+
+ if (strandbuf) {
+ copy_v3_v3(svert->co, loc);
+ svert->strandco= -1.0f + 2.0f*time;
+ svert++;
+ strand->totvert++;
+ }
+ else {
+ sd.size = hasize;
+
+ if (k==1) {
+ sd.first = 1;
+ sd.time = 0.0f;
+ sub_v3_v3v3(loc0, loc1, loc);
+ add_v3_v3v3(loc0, loc1, loc0);
+
+ particle_curve(re, obr, psmd->dm_final, ma, &sd, loc1, loc0, seed, pa_co);
+ }
+
+ sd.first = 0;
+ sd.time = time;
+
+ if (k)
+ particle_curve(re, obr, psmd->dm_final, ma, &sd, loc, loc1, seed, pa_co);
+
+ copy_v3_v3(loc1, loc);
+ }
+ }
+
+ }
+ else {
+ /* render normal particles */
+ if (part->trail_count > 1) {
+ float length = part->path_end * (1.0f - part->randlength * r_length);
+ int trail_count = part->trail_count * (1.0f - part->randlength * r_length);
+ float ct = (part->draw & PART_ABS_PATH_TIME) ? cfra : pa_time;
+ float dt = length / (trail_count ? (float)trail_count : 1.0f);
+
+ /* make sure we have pointcache in memory before getting particle on path */
+ psys_make_temp_pointcache(ob, psys);
+
+ for (i=0; i < trail_count; i++, ct -= dt) {
+ if (part->draw & PART_ABS_PATH_TIME) {
+ if (ct < pa_birthtime || ct > pa_dietime)
+ continue;
+ }
+ else if (ct < 0.0f || ct > 1.0f)
+ continue;
+
+ state.time = (part->draw & PART_ABS_PATH_TIME) ? -ct : ct;
+ psys_get_particle_on_path(&sim, a, &state, 1);
+
+ if (psys->parent)
+ mul_m4_v3(psys->parent->obmat, state.co);
+
+ if (use_duplimat)
+ mul_m4_v4(duplimat, state.co);
+
+ if (part->ren_as == PART_DRAW_BB) {
+ bb.random = random;
+ bb.offset[0] = part->bb_offset[0];
+ bb.offset[1] = part->bb_offset[1];
+ bb.size[0] = part->bb_size[0] * pa_size;
+ if (part->bb_align==PART_BB_VEL) {
+ float pa_vel = len_v3(state.vel);
+ float head = part->bb_vel_head*pa_vel;
+ float tail = part->bb_vel_tail*pa_vel;
+ bb.size[1] = part->bb_size[1]*pa_size + head + tail;
+ /* use offset to adjust the particle center. this is relative to size, so need to divide! */
+ if (bb.size[1] > 0.0f)
+ bb.offset[1] += (head-tail) / bb.size[1];
+ }
+ else
+ bb.size[1] = part->bb_size[1] * pa_size;
+ bb.tilt = part->bb_tilt * (1.0f - part->bb_rand_tilt * r_tilt);
+ bb.time = ct;
+ bb.num = a;
+ }
+
+ pa_co[0] = (part->draw & PART_ABS_PATH_TIME) ? (ct-pa_birthtime)/(pa_dietime-pa_birthtime) : ct;
+ pa_co[1] = (float)i/(float)(trail_count-1);
+
+ particle_normal_ren(part->ren_as, part, re, obr, psmd->dm_final, ma, &sd, &bb, &state, seed, hasize, pa_co);
+ }
+ }
+ else {
+ state.time=cfra;
+ if (psys_get_particle_state(&sim, a, &state, 0)==0)
+ continue;
+
+ if (psys->parent)
+ mul_m4_v3(psys->parent->obmat, state.co);
+
+ if (use_duplimat)
+ mul_m4_v3(duplimat, state.co);
+
+ if (part->ren_as == PART_DRAW_BB) {
+ bb.random = random;
+ bb.offset[0] = part->bb_offset[0];
+ bb.offset[1] = part->bb_offset[1];
+ bb.size[0] = part->bb_size[0] * pa_size;
+ if (part->bb_align==PART_BB_VEL) {
+ float pa_vel = len_v3(state.vel);
+ float head = part->bb_vel_head*pa_vel;
+ float tail = part->bb_vel_tail*pa_vel;
+ bb.size[1] = part->bb_size[1]*pa_size + head + tail;
+ /* use offset to adjust the particle center. this is relative to size, so need to divide! */
+ if (bb.size[1] > 0.0f)
+ bb.offset[1] += (head-tail) / bb.size[1];
+ }
+ else
+ bb.size[1] = part->bb_size[1] * pa_size;
+ bb.tilt = part->bb_tilt * (1.0f - part->bb_rand_tilt * r_tilt);
+ bb.time = pa_time;
+ bb.num = a;
+ bb.lifetime = pa_dietime-pa_birthtime;
+ }
+
+ particle_normal_ren(part->ren_as, part, re, obr, psmd->dm_final, ma, &sd, &bb, &state, seed, hasize, pa_co);
+ }
+ }
+
+ if (orco1==0)
+ sd.orco+=3;
+
+ if (re->test_break(re->tbh))
+ break;
+ }
+
+ if (do_surfacecache)
+ strandbuf->surface= cache_strand_surface(re, obr, psmd->dm_final, mat, timeoffset);
+
+/* 4. clean up */
+#if 0 /* XXX old animation system */
+ if (ma) do_mat_ipo(re->scene, ma);
+#endif /* XXX old animation system */
+
+ if (orco1)
+ MEM_freeN(sd.orco);
+
+ if (sd.uvco)
+ MEM_freeN(sd.uvco);
+
+ if (sd.mcol)
+ MEM_freeN(sd.mcol);
+
+ if (states)
+ MEM_freeN(states);
+
+ BLI_rng_free(rng);
+
+ psys->flag &= ~PSYS_DRAWING;
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (path_nbr && (ma->mode_l & MA_TANGENT_STR)==0)
+ calc_vertexnormals(re, obr, 1, 0, 0);
+
+ return 1;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Halo's */
+/* ------------------------------------------------------------------------- */
+
+static void make_render_halos(Render *re, ObjectRen *obr, Mesh *UNUSED(me), int totvert, MVert *mvert, Material *ma, float *orco)
+{
+ Object *ob= obr->ob;
+ HaloRen *har;
+ float xn, yn, zn, nor[3], view[3];
+ float vec[3], hasize, mat[4][4], imat[3][3];
+ int a, ok, seed= ma->seed1;
+
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ copy_m3_m4(imat, ob->imat);
+
+ re->flag |= R_HALO;
+
+ for (a=0; a<totvert; a++, mvert++) {
+ ok= 1;
+
+ if (ok) {
+ hasize= ma->hasize;
+
+ copy_v3_v3(vec, mvert->co);
+ mul_m4_v3(mat, vec);
+
+ if (ma->mode & MA_HALOPUNO) {
+ xn= mvert->no[0];
+ yn= mvert->no[1];
+ zn= mvert->no[2];
+
+ /* transpose ! */
+ nor[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn;
+ nor[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn;
+ nor[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn;
+ normalize_v3(nor);
+
+ copy_v3_v3(view, vec);
+ normalize_v3(view);
+
+ zn = dot_v3v3(nor, view);
+ if (zn>=0.0f) hasize= 0.0f;
+ else hasize*= zn*zn*zn*zn;
+ }
+
+ if (orco) har= RE_inithalo(re, obr, ma, vec, NULL, orco, hasize, 0.0, seed);
+ else har= RE_inithalo(re, obr, ma, vec, NULL, mvert->co, hasize, 0.0, seed);
+ if (har) har->lay= ob->lay;
+ }
+ if (orco) orco+= 3;
+ seed++;
+ }
+}
+
+static int verghalo(const void *a1, const void *a2)
+{
+ const HaloRen *har1= *(const HaloRen**)a1;
+ const HaloRen *har2= *(const HaloRen**)a2;
+
+ if (har1->zs < har2->zs) return 1;
+ else if (har1->zs > har2->zs) return -1;
+ return 0;
+}
+
+static void sort_halos(Render *re, int totsort)
+{
+ ObjectRen *obr;
+ HaloRen *har= NULL, **haso;
+ int a;
+
+ if (re->tothalo==0) return;
+
+ re->sortedhalos= MEM_callocN(sizeof(HaloRen*)*re->tothalo, "sorthalos");
+ haso= re->sortedhalos;
+
+ for (obr=re->objecttable.first; obr; obr=obr->next) {
+ for (a=0; a<obr->tothalo; a++) {
+ if ((a & 255)==0) har= obr->bloha[a>>8];
+ else har++;
+
+ *(haso++)= har;
+ }
+ }
+
+ qsort(re->sortedhalos, totsort, sizeof(HaloRen*), verghalo);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Displacement Mapping */
+/* ------------------------------------------------------------------------- */
+
+static short test_for_displace(Render *re, Object *ob)
+{
+ /* return 1 when this object uses displacement textures. */
+ Material *ma;
+ int i;
+
+ for (i=1; i<=ob->totcol; i++) {
+ ma=give_render_material(re, ob, i);
+ /* ma->mapto is ORed total of all mapto channels */
+ if (ma && (ma->mapto & MAP_DISPLACE)) return 1;
+ }
+ return 0;
+}
+
+static void displace_render_vert(Render *re, ObjectRen *obr, ShadeInput *shi, VertRen *vr, int vindex, float *scale)
+{
+ MTFace *tface;
+ short texco= shi->mat->texco;
+ float sample=0, displace[3];
+ char *name;
+ int i;
+
+ /* shi->co is current render coord, just make sure at least some vector is here */
+ copy_v3_v3(shi->co, vr->co);
+ /* vertex normal is used for textures type 'col' and 'var' */
+ copy_v3_v3(shi->vn, vr->n);
+
+ if (texco & TEXCO_UV) {
+ shi->totuv= 0;
+ shi->actuv= obr->actmtface;
+
+ for (i=0; (tface=RE_vlakren_get_tface(obr, shi->vlr, i, &name, 0)); i++) {
+ ShadeInputUV *suv= &shi->uv[i];
+
+ /* shi.uv needs scale correction from tface uv */
+ suv->uv[0]= 2*tface->uv[vindex][0]-1.0f;
+ suv->uv[1]= 2*tface->uv[vindex][1]-1.0f;
+ suv->uv[2]= 0.0f;
+ suv->name= name;
+ shi->totuv++;
+ }
+ }
+
+ /* set all rendercoords, 'texco' is an ORed value for all textures needed */
+ if ((texco & TEXCO_ORCO) && (vr->orco)) {
+ copy_v3_v3(shi->lo, vr->orco);
+ }
+ if (texco & TEXCO_GLOB) {
+ copy_v3_v3(shi->gl, shi->co);
+ mul_m4_v3(re->viewinv, shi->gl);
+ }
+ if (texco & TEXCO_NORM) {
+ copy_v3_v3(shi->orn, shi->vn);
+ }
+ if (texco & TEXCO_REFL) {
+ /* not (yet?) */
+ }
+ if (texco & TEXCO_STRESS) {
+ const float *s= RE_vertren_get_stress(obr, vr, 0);
+
+ if (s) {
+ shi->stress= *s;
+ if (shi->stress<1.0f) shi->stress-= 1.0f;
+ else shi->stress= (shi->stress-1.0f)/shi->stress;
+ }
+ else
+ shi->stress= 0.0f;
+ }
+
+ shi->displace[0]= shi->displace[1]= shi->displace[2]= 0.0;
+
+ do_material_tex(shi, re);
+
+ //printf("no=%f, %f, %f\nbefore co=%f, %f, %f\n", vr->n[0], vr->n[1], vr->n[2],
+ //vr->co[0], vr->co[1], vr->co[2]);
+
+ displace[0]= shi->displace[0] * scale[0];
+ displace[1]= shi->displace[1] * scale[1];
+ displace[2]= shi->displace[2] * scale[2];
+
+ /* 0.5 could become button once? */
+ vr->co[0] += displace[0];
+ vr->co[1] += displace[1];
+ vr->co[2] += displace[2];
+
+ //printf("after co=%f, %f, %f\n", vr->co[0], vr->co[1], vr->co[2]);
+
+ /* we just don't do this vertex again, bad luck for other face using same vertex with
+ * different material... */
+ vr->flag |= 1;
+
+ /* Pass sample back so displace_face can decide which way to split the quad */
+ sample = shi->displace[0]*shi->displace[0];
+ sample += shi->displace[1]*shi->displace[1];
+ sample += shi->displace[2]*shi->displace[2];
+
+ vr->accum=sample;
+ /* Should be sqrt(sample), but I'm only looking for "bigger". Save the cycles. */
+ return;
+}
+
+static void displace_render_face(Render *re, ObjectRen *obr, VlakRen *vlr, float *scale)
+{
+ ShadeInput shi;
+
+ /* Warning, This is not that nice, and possibly a bit slow,
+ * however some variables were not initialized properly in, unless using shade_input_initialize(...), we need to do a memset */
+ memset(&shi, 0, sizeof(ShadeInput));
+ /* end warning! - Campbell */
+
+ /* set up shadeinput struct for multitex() */
+
+ /* memset above means we don't need this */
+ /*shi.osatex= 0;*/ /* signal not to use dx[] and dy[] texture AA vectors */
+
+ shi.obr= obr;
+ shi.vlr= vlr; /* current render face */
+ shi.mat= vlr->mat; /* current input material */
+ shi.thread= 0;
+
+ /* TODO, assign these, displacement with new bumpmap is skipped without - campbell */
+#if 0
+ /* order is not known ? */
+ shi.v1= vlr->v1;
+ shi.v2= vlr->v2;
+ shi.v3= vlr->v3;
+#endif
+
+ /* Displace the verts, flag is set when done */
+ if (!vlr->v1->flag)
+ displace_render_vert(re, obr, &shi, vlr->v1, 0, scale);
+
+ if (!vlr->v2->flag)
+ displace_render_vert(re, obr, &shi, vlr->v2, 1, scale);
+
+ if (!vlr->v3->flag)
+ displace_render_vert(re, obr, &shi, vlr->v3, 2, scale);
+
+ if (vlr->v4) {
+ if (!vlr->v4->flag)
+ displace_render_vert(re, obr, &shi, vlr->v4, 3, scale);
+
+ /* closest in displace value. This will help smooth edges. */
+ if (fabsf(vlr->v1->accum - vlr->v3->accum) > fabsf(vlr->v2->accum - vlr->v4->accum)) vlr->flag |= R_DIVIDE_24;
+ else vlr->flag &= ~R_DIVIDE_24;
+ }
+
+ /* Recalculate the face normal - if flipped before, flip now */
+ if (vlr->v4) {
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ }
+ else {
+ normal_tri_v3(vlr->n, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ }
+}
+
+static void displace(Render *re, ObjectRen *obr)
+{
+ VertRen *vr;
+ VlakRen *vlr;
+// float min[3]={1e30, 1e30, 1e30}, max[3]={-1e30, -1e30, -1e30};
+ float scale[3]={1.0f, 1.0f, 1.0f}, temp[3];//, xn
+ int i; //, texflag=0;
+ Object *obt;
+
+ /* Object Size with parenting */
+ obt=obr->ob;
+ while (obt) {
+ mul_v3_v3v3(temp, obt->size, obt->dscale);
+ scale[0]*=temp[0]; scale[1]*=temp[1]; scale[2]*=temp[2];
+ obt=obt->parent;
+ }
+
+ /* Clear all flags */
+ for (i=0; i<obr->totvert; i++) {
+ vr= RE_findOrAddVert(obr, i);
+ vr->flag= 0;
+ }
+
+ for (i=0; i<obr->totvlak; i++) {
+ vlr=RE_findOrAddVlak(obr, i);
+ displace_render_face(re, obr, vlr, scale);
+ }
+
+ /* Recalc vertex normals */
+ calc_vertexnormals(re, obr, 1, 0, 0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Metaball */
+/* ------------------------------------------------------------------------- */
+
+static void init_render_mball(Render *re, ObjectRen *obr)
+{
+ Object *ob= obr->ob;
+ DispList *dl;
+ VertRen *ver;
+ VlakRen *vlr, *vlr1;
+ Material *ma;
+ float *data, *nors, *orco=NULL, mat[4][4], imat[3][3], xn, yn, zn;
+ int a, need_orco, vlakindex, *index, negative_scale;
+ ListBase dispbase= {NULL, NULL};
+
+ if (ob!=BKE_mball_basis_find(re->eval_ctx, re->scene, ob))
+ return;
+
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat);
+ copy_m3_m4(imat, ob->imat);
+ negative_scale = is_negative_m4(mat);
+
+ ma= give_render_material(re, ob, 1);
+
+ need_orco= 0;
+ if (ma->texco & TEXCO_ORCO) {
+ need_orco= 1;
+ }
+
+ BKE_displist_make_mball_forRender(re->eval_ctx, re->scene, ob, &dispbase);
+ dl= dispbase.first;
+ if (dl == NULL) return;
+
+ data= dl->verts;
+ nors= dl->nors;
+ if (need_orco) {
+ orco= get_object_orco(re, ob);
+
+ if (!orco) {
+ /* orco hasn't been found in cache - create new one and add to cache */
+ orco= BKE_mball_make_orco(ob, &dispbase);
+ set_object_orco(re, ob, orco);
+ }
+ }
+
+ for (a=0; a<dl->nr; a++, data+=3, nors+=3) {
+
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, data);
+ mul_m4_v3(mat, ver->co);
+
+ /* render normals are inverted */
+ xn= -nors[0];
+ yn= -nors[1];
+ zn= -nors[2];
+
+ /* transpose ! */
+ ver->n[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn;
+ ver->n[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn;
+ ver->n[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn;
+ normalize_v3(ver->n);
+ //if (ob->transflag & OB_NEG_SCALE) negate_v3(ver->n);
+
+ if (need_orco) {
+ ver->orco= orco;
+ orco+=3;
+ }
+ }
+
+ index= dl->index;
+ for (a=0; a<dl->parts; a++, index+=4) {
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, index[0]);
+ vlr->v2= RE_findOrAddVert(obr, index[1]);
+ vlr->v3= RE_findOrAddVert(obr, index[2]);
+ vlr->v4 = NULL;
+
+ if (negative_scale)
+ normal_tri_v3(vlr->n, vlr->v1->co, vlr->v2->co, vlr->v3->co);
+ else
+ normal_tri_v3(vlr->n, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+
+ vlr->mat= ma;
+ vlr->flag= ME_SMOOTH;
+ vlr->ec= 0;
+
+ /* mball -too bad- always has triangles, because quads can be non-planar */
+ if (index[3] && index[3]!=index[2]) {
+ vlr1= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlakindex= vlr1->index;
+ *vlr1= *vlr;
+ vlr1->index= vlakindex;
+ vlr1->v2= vlr1->v3;
+ vlr1->v3= RE_findOrAddVert(obr, index[3]);
+ if (negative_scale)
+ normal_tri_v3(vlr1->n, vlr1->v1->co, vlr1->v2->co, vlr1->v3->co);
+ else
+ normal_tri_v3(vlr1->n, vlr1->v3->co, vlr1->v2->co, vlr1->v1->co);
+ }
+ }
+
+ /* enforce display lists remade */
+ BKE_displist_free(&dispbase);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Surfaces and Curves */
+/* ------------------------------------------------------------------------- */
+
+/* returns amount of vertices added for orco */
+static int dl_surf_to_renderdata(ObjectRen *obr, DispList *dl, Material **matar, float *orco, float mat[4][4])
+{
+ VertRen *v1, *v2, *v3, *v4, *ver;
+ VlakRen *vlr, *vlr1, *vlr2, *vlr3;
+ float *data, n1[3];
+ int u, v, orcoret= 0;
+ int p1, p2, p3, p4, a;
+ int sizeu, nsizeu, sizev, nsizev;
+ int startvert, startvlak;
+
+ startvert= obr->totvert;
+ nsizeu = sizeu = dl->parts; nsizev = sizev = dl->nr;
+
+ data= dl->verts;
+ for (u = 0; u < sizeu; u++) {
+ v1 = RE_findOrAddVert(obr, obr->totvert++); /* save this for possible V wrapping */
+ copy_v3_v3(v1->co, data); data += 3;
+ if (orco) {
+ v1->orco= orco; orco+= 3; orcoret++;
+ }
+ mul_m4_v3(mat, v1->co);
+
+ for (v = 1; v < sizev; v++) {
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, data); data += 3;
+ if (orco) {
+ ver->orco= orco; orco+= 3; orcoret++;
+ }
+ mul_m4_v3(mat, ver->co);
+ }
+ /* if V-cyclic, add extra vertices at end of the row */
+ if (dl->flag & DL_CYCL_U) {
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, v1->co);
+ if (orco) {
+ ver->orco= orco; orco+=3; orcoret++; //orcobase + 3*(u*sizev + 0);
+ }
+ }
+ }
+
+ /* Done before next loop to get corner vert */
+ if (dl->flag & DL_CYCL_U) nsizev++;
+ if (dl->flag & DL_CYCL_V) nsizeu++;
+
+ /* if U cyclic, add extra row at end of column */
+ if (dl->flag & DL_CYCL_V) {
+ for (v = 0; v < nsizev; v++) {
+ v1= RE_findOrAddVert(obr, startvert + v);
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, v1->co);
+ if (orco) {
+ ver->orco= orco; orco+=3; orcoret++; //ver->orco= orcobase + 3*(0*sizev + v);
+ }
+ }
+ }
+
+ sizeu = nsizeu;
+ sizev = nsizev;
+
+ startvlak= obr->totvlak;
+
+ for (u = 0; u < sizeu - 1; u++) {
+ p1 = startvert + u * sizev; /* walk through face list */
+ p2 = p1 + 1;
+ p3 = p2 + sizev;
+ p4 = p3 - 1;
+
+ for (v = 0; v < sizev - 1; v++) {
+ v1= RE_findOrAddVert(obr, p1);
+ v2= RE_findOrAddVert(obr, p2);
+ v3= RE_findOrAddVert(obr, p3);
+ v4= RE_findOrAddVert(obr, p4);
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= v1; vlr->v2= v2; vlr->v3= v3; vlr->v4= v4;
+
+ normal_quad_v3(n1, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+
+ copy_v3_v3(vlr->n, n1);
+
+ vlr->mat= matar[ dl->col];
+ vlr->ec= ME_V1V2+ME_V2V3;
+ vlr->flag= dl->rt;
+
+ add_v3_v3(v1->n, n1);
+ add_v3_v3(v2->n, n1);
+ add_v3_v3(v3->n, n1);
+ add_v3_v3(v4->n, n1);
+
+ p1++; p2++; p3++; p4++;
+ }
+ }
+ /* fix normals for U resp. V cyclic faces */
+ sizeu--; sizev--; /* dec size for face array */
+ if (dl->flag & DL_CYCL_V) {
+
+ for (v = 0; v < sizev; v++) {
+ /* optimize! :*/
+ vlr= RE_findOrAddVlak(obr, UVTOINDEX(sizeu - 1, v));
+ vlr1= RE_findOrAddVlak(obr, UVTOINDEX(0, v));
+ add_v3_v3(vlr1->v1->n, vlr->n);
+ add_v3_v3(vlr1->v2->n, vlr->n);
+ add_v3_v3(vlr->v3->n, vlr1->n);
+ add_v3_v3(vlr->v4->n, vlr1->n);
+ }
+ }
+ if (dl->flag & DL_CYCL_U) {
+
+ for (u = 0; u < sizeu; u++) {
+ /* optimize! :*/
+ vlr= RE_findOrAddVlak(obr, UVTOINDEX(u, 0));
+ vlr1= RE_findOrAddVlak(obr, UVTOINDEX(u, sizev-1));
+ add_v3_v3(vlr1->v2->n, vlr->n);
+ add_v3_v3(vlr1->v3->n, vlr->n);
+ add_v3_v3(vlr->v1->n, vlr1->n);
+ add_v3_v3(vlr->v4->n, vlr1->n);
+ }
+ }
+
+ /* last vertex is an extra case:
+ *
+ * ^ ()----()----()----()
+ * | | | || |
+ * u | |(0,n)||(0,0)|
+ * | | || |
+ * ()====()====[]====()
+ * | | || |
+ * | |(m,n)||(m,0)|
+ * | | || |
+ * ()----()----()----()
+ * v ->
+ *
+ * vertex [] is no longer shared, therefore distribute
+ * normals of the surrounding faces to all of the duplicates of []
+ */
+
+ if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U)) {
+ vlr= RE_findOrAddVlak(obr, UVTOINDEX(sizeu - 1, sizev - 1)); /* (m, n) */
+ vlr1= RE_findOrAddVlak(obr, UVTOINDEX(0, 0)); /* (0, 0) */
+ add_v3_v3v3(n1, vlr->n, vlr1->n);
+ vlr2= RE_findOrAddVlak(obr, UVTOINDEX(0, sizev-1)); /* (0, n) */
+ add_v3_v3(n1, vlr2->n);
+ vlr3= RE_findOrAddVlak(obr, UVTOINDEX(sizeu-1, 0)); /* (m, 0) */
+ add_v3_v3(n1, vlr3->n);
+ copy_v3_v3(vlr->v3->n, n1);
+ copy_v3_v3(vlr1->v1->n, n1);
+ copy_v3_v3(vlr2->v2->n, n1);
+ copy_v3_v3(vlr3->v4->n, n1);
+ }
+ for (a = startvert; a < obr->totvert; a++) {
+ ver= RE_findOrAddVert(obr, a);
+ normalize_v3(ver->n);
+ }
+
+
+ return orcoret;
+}
+
+static void init_render_dm(DerivedMesh *dm, Render *re, ObjectRen *obr,
+ int timeoffset, float *orco, float mat[4][4])
+{
+ Object *ob= obr->ob;
+ int a, end, totvert, vertofs;
+ short mat_iter;
+ VertRen *ver;
+ VlakRen *vlr;
+ MVert *mvert = NULL;
+ MFace *mface;
+ Material *ma;
+#ifdef WITH_FREESTYLE
+ const int *index_mf_to_mpoly = NULL;
+ const int *index_mp_to_orig = NULL;
+ FreestyleFace *ffa = NULL;
+#endif
+ /* Curve *cu= ELEM(ob->type, OB_FONT, OB_CURVE) ? ob->data : NULL; */
+
+ mvert= dm->getVertArray(dm);
+ totvert= dm->getNumVerts(dm);
+
+ for (a=0; a<totvert; a++, mvert++) {
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, mvert->co);
+ mul_m4_v3(mat, ver->co);
+
+ if (orco) {
+ ver->orco= orco;
+ orco+=3;
+ }
+ }
+
+ if (!timeoffset) {
+ /* store customdata names, because DerivedMesh is freed */
+ RE_set_customdata_names(obr, &dm->faceData);
+
+ /* still to do for keys: the correct local texture coordinate */
+
+ /* faces in order of color blocks */
+ vertofs= obr->totvert - totvert;
+ for (mat_iter= 0; (mat_iter < ob->totcol || (mat_iter==0 && ob->totcol==0)); mat_iter++) {
+
+ ma= give_render_material(re, ob, mat_iter+1);
+ end= dm->getNumTessFaces(dm);
+ mface= dm->getTessFaceArray(dm);
+
+#ifdef WITH_FREESTYLE
+ if (ob->type == OB_MESH) {
+ Mesh *me= ob->data;
+ index_mf_to_mpoly= dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
+ index_mp_to_orig= dm->getPolyDataArray(dm, CD_ORIGINDEX);
+ ffa= CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE);
+ }
+#endif
+
+ for (a=0; a<end; a++, mface++) {
+ int v1, v2, v3, v4, flag;
+
+ if (mface->mat_nr == mat_iter) {
+ float len;
+
+ v1= mface->v1;
+ v2= mface->v2;
+ v3= mface->v3;
+ v4= mface->v4;
+ flag= mface->flag & ME_SMOOTH;
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, vertofs+v1);
+ vlr->v2= RE_findOrAddVert(obr, vertofs+v2);
+ vlr->v3= RE_findOrAddVert(obr, vertofs+v3);
+ if (v4) vlr->v4= RE_findOrAddVert(obr, vertofs+v4);
+ else vlr->v4 = NULL;
+
+ /* render normals are inverted in render */
+ if (vlr->v4)
+ len= normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ else
+ len= normal_tri_v3(vlr->n, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+
+ vlr->mat= ma;
+ vlr->flag= flag;
+ vlr->ec= 0; /* mesh edges rendered separately */
+#ifdef WITH_FREESTYLE
+ if (ffa) {
+ int index = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a;
+ vlr->freestyle_face_mark= (ffa[index].flag & FREESTYLE_FACE_MARK) ? 1 : 0;
+ }
+ else {
+ vlr->freestyle_face_mark= 0;
+ }
+#endif
+
+ if (len==0) obr->totvlak--;
+ else {
+ CustomDataLayer *layer;
+ MTFace *mtface, *mtf;
+ MCol *mcol, *mc;
+ int index, mtfn= 0, mcn= 0;
+ char *name;
+
+ for (index=0; index<dm->faceData.totlayer; index++) {
+ layer= &dm->faceData.layers[index];
+ name= layer->name;
+
+ if (layer->type == CD_MTFACE && mtfn < MAX_MTFACE) {
+ mtf= RE_vlakren_get_tface(obr, vlr, mtfn++, &name, 1);
+ mtface= (MTFace*)layer->data;
+ *mtf= mtface[a];
+ }
+ else if (layer->type == CD_MCOL && mcn < MAX_MCOL) {
+ mc= RE_vlakren_get_mcol(obr, vlr, mcn++, &name, 1);
+ mcol= (MCol*)layer->data;
+ memcpy(mc, &mcol[a*4], sizeof(MCol)*4);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Normals */
+ calc_vertexnormals(re, obr, 1, 0, 0);
+ }
+
+}
+
+static void init_render_surf(Render *re, ObjectRen *obr, int timeoffset)
+{
+ Object *ob= obr->ob;
+ Nurb *nu = NULL;
+ Curve *cu;
+ ListBase displist= {NULL, NULL};
+ DispList *dl;
+ Material **matar;
+ float *orco=NULL, mat[4][4];
+ int a, totmat;
+ bool need_orco = false;
+ DerivedMesh *dm= NULL;
+
+ cu= ob->data;
+ nu= cu->nurb.first;
+ if (nu == NULL) return;
+
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat);
+
+ /* material array */
+ totmat= ob->totcol+1;
+ matar= MEM_callocN(sizeof(Material*)*totmat, "init_render_surf matar");
+
+ for (a=0; a<totmat; a++) {
+ matar[a]= give_render_material(re, ob, a+1);
+
+ if (matar[a] && matar[a]->texco & TEXCO_ORCO)
+ need_orco= 1;
+ }
+
+ if (ob->parent && (ob->parent->type==OB_LATTICE)) need_orco= 1;
+
+ BKE_displist_make_surf(re->scene, ob, &displist, &dm, 1, 0, 1);
+
+ if (dm) {
+ if (need_orco) {
+ orco = get_object_orco(re, ob);
+ if (!orco) {
+ orco= BKE_displist_make_orco(re->scene, ob, dm, true, true);
+ if (orco) {
+ set_object_orco(re, ob, orco);
+ }
+ }
+ }
+
+ init_render_dm(dm, re, obr, timeoffset, orco, mat);
+ dm->release(dm);
+ }
+ else {
+ if (need_orco) {
+ orco = get_object_orco(re, ob);
+ if (!orco) {
+ orco = BKE_curve_surf_make_orco(ob);
+ set_object_orco(re, ob, orco);
+ }
+ }
+
+ /* walk along displaylist and create rendervertices/-faces */
+ for (dl=displist.first; dl; dl=dl->next) {
+ /* watch out: u ^= y, v ^= x !! */
+ if (dl->type==DL_SURF)
+ orco+= 3*dl_surf_to_renderdata(obr, dl, matar, orco, mat);
+ }
+ }
+
+ BKE_displist_free(&displist);
+
+ MEM_freeN(matar);
+}
+
+static void init_render_curve(Render *re, ObjectRen *obr, int timeoffset)
+{
+ Object *ob= obr->ob;
+ Curve *cu;
+ VertRen *ver;
+ VlakRen *vlr;
+ DispList *dl;
+ DerivedMesh *dm = NULL;
+ ListBase disp={NULL, NULL};
+ Material **matar;
+ float *data, *fp, *orco=NULL;
+ float n[3], mat[4][4], nmat[4][4];
+ int nr, startvert, a, b, negative_scale;
+ bool need_orco = false;
+ int totmat;
+
+ cu= ob->data;
+ if (ob->type==OB_FONT && cu->str==NULL) return;
+ else if (ob->type==OB_CURVE && cu->nurb.first==NULL) return;
+
+ BKE_displist_make_curveTypes_forRender(re->scene, ob, &disp, &dm, false, true);
+ dl= disp.first;
+ if (dl==NULL) return;
+
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat);
+ negative_scale = is_negative_m4(mat);
+
+ /* local object -> world space transform for normals */
+ transpose_m4_m4(nmat, mat);
+ invert_m4(nmat);
+
+ /* material array */
+ totmat= ob->totcol+1;
+ matar= MEM_callocN(sizeof(Material*)*totmat, "init_render_surf matar");
+
+ for (a=0; a<totmat; a++) {
+ matar[a]= give_render_material(re, ob, a+1);
+
+ if (matar[a] && matar[a]->texco & TEXCO_ORCO)
+ need_orco= 1;
+ }
+
+ if (dm) {
+ if (need_orco) {
+ orco = get_object_orco(re, ob);
+ if (!orco) {
+ orco = BKE_displist_make_orco(re->scene, ob, dm, true, true);
+ if (orco) {
+ set_object_orco(re, ob, orco);
+ }
+ }
+ }
+
+ init_render_dm(dm, re, obr, timeoffset, orco, mat);
+ dm->release(dm);
+ }
+ else {
+ if (need_orco) {
+ orco = get_object_orco(re, ob);
+ if (!orco) {
+ orco = BKE_curve_make_orco(re->scene, ob, NULL);
+ set_object_orco(re, ob, orco);
+ }
+ }
+
+ while (dl) {
+ if (dl->col > ob->totcol) {
+ /* pass */
+ }
+ else if (dl->type==DL_INDEX3) {
+ const int *index;
+
+ startvert= obr->totvert;
+ data= dl->verts;
+
+ for (a=0; a<dl->nr; a++, data+=3) {
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, data);
+
+ mul_m4_v3(mat, ver->co);
+
+ if (orco) {
+ ver->orco = orco;
+ orco += 3;
+ }
+ }
+
+ if (timeoffset==0) {
+ float tmp[3];
+ const int startvlak= obr->totvlak;
+
+ zero_v3(n);
+ index= dl->index;
+ for (a=0; a<dl->parts; a++, index+=3) {
+ int v1 = index[0], v2 = index[2], v3 = index[1];
+ float *co1 = &dl->verts[v1 * 3],
+ *co2 = &dl->verts[v2 * 3],
+ *co3 = &dl->verts[v3 * 3];
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, startvert + v1);
+ vlr->v2= RE_findOrAddVert(obr, startvert + v2);
+ vlr->v3= RE_findOrAddVert(obr, startvert + v3);
+ vlr->v4= NULL;
+
+ /* to prevent float accuracy issues, we calculate normal in local object space (not world) */
+ if (normal_tri_v3(tmp, co1, co2, co3) > FLT_EPSILON) {
+ if (negative_scale == false) {
+ add_v3_v3(n, tmp);
+ }
+ else {
+ sub_v3_v3(n, tmp);
+ }
+ }
+
+ vlr->mat= matar[ dl->col ];
+ vlr->flag= 0;
+ vlr->ec= 0;
+ }
+
+ /* transform normal to world space */
+ mul_m4_v3(nmat, n);
+ normalize_v3(n);
+
+ /* vertex normals */
+ for (a= startvlak; a<obr->totvlak; a++) {
+ vlr= RE_findOrAddVlak(obr, a);
+
+ copy_v3_v3(vlr->n, n);
+ add_v3_v3(vlr->v1->n, vlr->n);
+ add_v3_v3(vlr->v3->n, vlr->n);
+ add_v3_v3(vlr->v2->n, vlr->n);
+ }
+ for (a=startvert; a<obr->totvert; a++) {
+ ver= RE_findOrAddVert(obr, a);
+ normalize_v3(ver->n);
+ }
+ }
+ }
+ else if (dl->type==DL_SURF) {
+
+ /* cyclic U means an extruded full circular curve, we skip bevel splitting then */
+ if (dl->flag & DL_CYCL_U) {
+ orco+= 3*dl_surf_to_renderdata(obr, dl, matar, orco, mat);
+ }
+ else {
+ int p1, p2, p3, p4;
+
+ fp= dl->verts;
+ startvert= obr->totvert;
+ nr= dl->nr*dl->parts;
+
+ while (nr--) {
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+
+ copy_v3_v3(ver->co, fp);
+ mul_m4_v3(mat, ver->co);
+ fp+= 3;
+
+ if (orco) {
+ ver->orco = orco;
+ orco += 3;
+ }
+ }
+
+ if (dl->flag & DL_CYCL_V && orco) {
+ fp = dl->verts;
+ nr = dl->nr;
+ while (nr--) {
+ ver = RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, fp);
+ mul_m4_v3(mat, ver->co);
+ ver->orco = orco;
+ fp += 3;
+ orco += 3;
+ }
+ }
+
+ if (dl->bevel_split || timeoffset == 0) {
+ const int startvlak= obr->totvlak;
+
+ for (a=0; a<dl->parts; a++) {
+
+ if (BKE_displist_surfindex_get(dl, a, &b, &p1, &p2, &p3, &p4)==0)
+ break;
+
+ p1+= startvert;
+ p2+= startvert;
+ p3+= startvert;
+ p4+= startvert;
+
+ if (dl->flag & DL_CYCL_V && orco && a == dl->parts - 1) {
+ p3 = p1 + dl->nr;
+ p4 = p2 + dl->nr;
+ }
+
+ for (; b<dl->nr; b++) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ /* important 1 offset in order is kept [#24913] */
+ vlr->v1= RE_findOrAddVert(obr, p2);
+ vlr->v2= RE_findOrAddVert(obr, p1);
+ vlr->v3= RE_findOrAddVert(obr, p3);
+ vlr->v4= RE_findOrAddVert(obr, p4);
+ vlr->ec= ME_V2V3+ME_V3V4;
+ if (a==0) vlr->ec+= ME_V1V2;
+
+ vlr->flag= dl->rt;
+
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ vlr->mat= matar[ dl->col ];
+
+ p4= p3;
+ p3++;
+ p2= p1;
+ p1++;
+ }
+ }
+
+ if (dl->bevel_split) {
+ for (a = 0; a < dl->parts - 1 + !!(dl->flag & DL_CYCL_V); a++) {
+ if (BLI_BITMAP_TEST(dl->bevel_split, a)) {
+ split_v_renderfaces(
+ obr, startvlak, startvert, dl->parts, dl->nr, a,
+ /* intentionally swap (v, u) --> (u, v) */
+ dl->flag & DL_CYCL_V, dl->flag & DL_CYCL_U);
+ }
+ }
+ }
+
+ /* vertex normals */
+ for (a= startvlak; a<obr->totvlak; a++) {
+ vlr= RE_findOrAddVlak(obr, a);
+
+ add_v3_v3(vlr->v1->n, vlr->n);
+ add_v3_v3(vlr->v3->n, vlr->n);
+ add_v3_v3(vlr->v2->n, vlr->n);
+ add_v3_v3(vlr->v4->n, vlr->n);
+ }
+ for (a=startvert; a<obr->totvert; a++) {
+ ver= RE_findOrAddVert(obr, a);
+ normalize_v3(ver->n);
+ }
+ }
+ }
+ }
+
+ dl= dl->next;
+ }
+ }
+
+ BKE_displist_free(&disp);
+
+ MEM_freeN(matar);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Mesh */
+/* ------------------------------------------------------------------------- */
+
+struct edgesort {
+ unsigned int v1, v2;
+ int f;
+ unsigned int i1, i2;
+};
+
+/* edges have to be added with lowest index first for sorting */
+static void to_edgesort(struct edgesort *ed,
+ unsigned int i1, unsigned int i2,
+ unsigned int v1, unsigned int v2, int f)
+{
+ if (v1 > v2) {
+ SWAP(unsigned int, v1, v2);
+ SWAP(unsigned int, i1, i2);
+ }
+
+ ed->v1= v1;
+ ed->v2= v2;
+ ed->i1= i1;
+ ed->i2= i2;
+ ed->f = f;
+}
+
+static int vergedgesort(const void *v1, const void *v2)
+{
+ const struct edgesort *x1=v1, *x2=v2;
+
+ if ( x1->v1 > x2->v1) return 1;
+ else if ( x1->v1 < x2->v1) return -1;
+ else if ( x1->v2 > x2->v2) return 1;
+ else if ( x1->v2 < x2->v2) return -1;
+
+ return 0;
+}
+
+static struct edgesort *make_mesh_edge_lookup(DerivedMesh *dm, int *totedgesort)
+{
+ MFace *mf, *mface;
+ MTFace *tface=NULL;
+ struct edgesort *edsort, *ed;
+ unsigned int *mcol=NULL;
+ int a, totedge=0, totface;
+
+ mface= dm->getTessFaceArray(dm);
+ totface= dm->getNumTessFaces(dm);
+ tface= dm->getTessFaceDataArray(dm, CD_MTFACE);
+ mcol= dm->getTessFaceDataArray(dm, CD_MCOL);
+
+ if (mcol==NULL && tface==NULL) return NULL;
+
+ /* make sorted table with edges and face indices in it */
+ for (a= totface, mf= mface; a>0; a--, mf++) {
+ totedge += mf->v4 ? 4 : 3;
+ }
+
+ if (totedge==0)
+ return NULL;
+
+ ed= edsort= MEM_callocN(totedge*sizeof(struct edgesort), "edgesort");
+
+ for (a=0, mf=mface; a<totface; a++, mf++) {
+ to_edgesort(ed++, 0, 1, mf->v1, mf->v2, a);
+ to_edgesort(ed++, 1, 2, mf->v2, mf->v3, a);
+ if (mf->v4) {
+ to_edgesort(ed++, 2, 3, mf->v3, mf->v4, a);
+ to_edgesort(ed++, 3, 0, mf->v4, mf->v1, a);
+ }
+ else {
+ to_edgesort(ed++, 2, 3, mf->v3, mf->v1, a);
+ }
+ }
+
+ qsort(edsort, totedge, sizeof(struct edgesort), vergedgesort);
+
+ *totedgesort= totedge;
+
+ return edsort;
+}
+
+static void use_mesh_edge_lookup(ObjectRen *obr, DerivedMesh *dm, MEdge *medge, VlakRen *vlr, struct edgesort *edgetable, int totedge)
+{
+ struct edgesort ed, *edp;
+ CustomDataLayer *layer;
+ MTFace *mtface, *mtf;
+ MCol *mcol, *mc;
+ int index, mtfn, mcn;
+ char *name;
+
+ if (medge->v1 < medge->v2) {
+ ed.v1= medge->v1;
+ ed.v2= medge->v2;
+ }
+ else {
+ ed.v1= medge->v2;
+ ed.v2= medge->v1;
+ }
+
+ edp= bsearch(&ed, edgetable, totedge, sizeof(struct edgesort), vergedgesort);
+
+ /* since edges have different index ordering, we have to duplicate mcol and tface */
+ if (edp) {
+ mtfn= mcn= 0;
+
+ for (index=0; index<dm->faceData.totlayer; index++) {
+ layer= &dm->faceData.layers[index];
+ name= layer->name;
+
+ if (layer->type == CD_MTFACE && mtfn < MAX_MTFACE) {
+ mtface= &((MTFace*)layer->data)[edp->f];
+ mtf= RE_vlakren_get_tface(obr, vlr, mtfn++, &name, 1);
+
+ *mtf= *mtface;
+
+ memcpy(mtf->uv[0], mtface->uv[edp->i1], sizeof(float)*2);
+ memcpy(mtf->uv[1], mtface->uv[edp->i2], sizeof(float)*2);
+ memcpy(mtf->uv[2], mtface->uv[1], sizeof(float)*2);
+ memcpy(mtf->uv[3], mtface->uv[1], sizeof(float)*2);
+ }
+ else if (layer->type == CD_MCOL && mcn < MAX_MCOL) {
+ mcol= &((MCol*)layer->data)[edp->f*4];
+ mc= RE_vlakren_get_mcol(obr, vlr, mcn++, &name, 1);
+
+ mc[0]= mcol[edp->i1];
+ mc[1]= mc[2]= mc[3]= mcol[edp->i2];
+ }
+ }
+ }
+}
+
+static void free_camera_inside_volumes(Render *re)
+{
+ BLI_freelistN(&re->render_volumes_inside);
+}
+
+static void init_camera_inside_volumes(Render *re)
+{
+ ObjectInstanceRen *obi;
+ VolumeOb *vo;
+ /* coordinates are all in camera space, so camera coordinate is zero. we also
+ * add an offset for the clip start, however note that with clip start it's
+ * actually impossible to do a single 'inside' test, since there will not be
+ * a single point where all camera rays start from, though for small clip start
+ * they will be close together. */
+ float co[3] = {0.f, 0.f, -re->clipsta};
+
+ for (vo= re->volumes.first; vo; vo= vo->next) {
+ for (obi= re->instancetable.first; obi; obi= obi->next) {
+ if (obi->obr == vo->obr) {
+ if (point_inside_volume_objectinstance(re, obi, co)) {
+ MatInside *mi;
+
+ mi = MEM_mallocN(sizeof(MatInside), "camera inside material");
+ mi->ma = vo->ma;
+ mi->obi = obi;
+
+ BLI_addtail(&(re->render_volumes_inside), mi);
+ }
+ }
+ }
+ }
+
+
+#if 0 /* debug */
+ {
+ MatInside *m;
+ for (m = re->render_volumes_inside.first; m; m = m->next) {
+ printf("matinside: ma: %s\n", m->ma->id.name + 2);
+ }
+ }
+#endif
+}
+
+static void add_volume(Render *re, ObjectRen *obr, Material *ma)
+{
+ struct VolumeOb *vo;
+
+ vo = MEM_mallocN(sizeof(VolumeOb), "volume object");
+
+ vo->ma = ma;
+ vo->obr = obr;
+
+ BLI_addtail(&re->volumes, vo);
+}
+
+#ifdef WITH_FREESTYLE
+static EdgeHash *make_freestyle_edge_mark_hash(DerivedMesh *dm)
+{
+ EdgeHash *edge_hash= NULL;
+ FreestyleEdge *fed;
+ MEdge *medge;
+ int totedge, a;
+
+ medge = dm->getEdgeArray(dm);
+ totedge = dm->getNumEdges(dm);
+ fed = dm->getEdgeDataArray(dm, CD_FREESTYLE_EDGE);
+ if (fed) {
+ edge_hash = BLI_edgehash_new(__func__);
+ for (a = 0; a < totedge; a++) {
+ if (fed[a].flag & FREESTYLE_EDGE_MARK)
+ BLI_edgehash_insert(edge_hash, medge[a].v1, medge[a].v2, medge+a);
+ }
+ }
+ return edge_hash;
+}
+
+static bool has_freestyle_edge_mark(EdgeHash *edge_hash, int v1, int v2)
+{
+ MEdge *medge= BLI_edgehash_lookup(edge_hash, v1, v2);
+ return (!medge) ? 0 : 1;
+}
+#endif
+
+static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset)
+{
+ Object *ob= obr->ob;
+ Mesh *me;
+ MVert *mvert = NULL;
+ MFace *mface;
+ VlakRen *vlr; //, *vlr1;
+ VertRen *ver;
+ Material *ma;
+ DerivedMesh *dm;
+ CustomDataMask mask;
+ float xn, yn, zn, imat[3][3], mat[4][4]; //nor[3],
+ float *orco = NULL;
+ short (*loop_nors)[4][3] = NULL;
+ bool need_orco = false, need_stress = false, need_tangent = false, need_origindex = false;
+ bool need_nmap_tangent_concrete = false;
+ int a, a1, ok, vertofs;
+ int end, totvert = 0;
+ bool do_autosmooth = false, do_displace = false;
+ bool use_original_normals = false;
+ int recalc_normals = 0; /* false by default */
+ int negative_scale;
+#ifdef WITH_FREESTYLE
+ FreestyleFace *ffa;
+#endif
+
+ me= ob->data;
+
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat);
+ copy_m3_m4(imat, ob->imat);
+ negative_scale= is_negative_m4(mat);
+
+ need_orco= 0;
+ for (a=1; a<=ob->totcol; a++) {
+ ma= give_render_material(re, ob, a);
+ if (ma) {
+ if (ma->texco & (TEXCO_ORCO|TEXCO_STRESS))
+ need_orco= 1;
+ if (ma->texco & TEXCO_STRESS)
+ need_stress= 1;
+ /* normalmaps, test if tangents needed, separated from shading */
+ if (ma->mode_l & MA_TANGENT_V) {
+ need_tangent= 1;
+ if (me->mtpoly==NULL)
+ need_orco= 1;
+ }
+ if (ma->mode_l & MA_NORMAP_TANG) {
+ if (me->mtpoly==NULL) {
+ need_orco= 1;
+ }
+ need_tangent= 1;
+ }
+ if (ma->mode2_l & MA_TANGENT_CONCRETE) {
+ need_nmap_tangent_concrete = true;
+ }
+ }
+ }
+
+ if (re->flag & R_NEED_TANGENT) {
+ /* exception for tangent space baking */
+ if (me->mtpoly==NULL) {
+ need_orco= 1;
+ }
+ need_tangent= 1;
+ }
+
+ /* check autosmooth and displacement, we then have to skip only-verts optimize
+ * Note: not sure what we want to give higher priority, currently do_displace
+ * takes precedence over do_autosmooth.
+ */
+ do_displace = test_for_displace(re, ob);
+ do_autosmooth = ((me->flag & ME_AUTOSMOOTH) != 0) && !do_displace;
+ if (do_autosmooth || do_displace)
+ timeoffset = 0;
+
+ /* origindex currently used when using autosmooth, or baking to vertex colors. */
+ need_origindex = (do_autosmooth || ((re->flag & R_BAKING) && (re->r.bake_flag & R_BAKE_VCOL)));
+
+ mask = CD_MASK_RENDER_INTERNAL;
+ if (!timeoffset)
+ if (need_orco)
+ mask |= CD_MASK_ORCO;
+
+#ifdef WITH_FREESTYLE
+ mask |= CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
+#endif
+
+ if (re->r.scemode & R_VIEWPORT_PREVIEW)
+ dm= mesh_create_derived_view(re->scene, ob, mask);
+ else
+ dm= mesh_create_derived_render(re->scene, ob, mask);
+ if (dm==NULL) return; /* in case duplicated object fails? */
+
+ mvert= dm->getVertArray(dm);
+ totvert= dm->getNumVerts(dm);
+
+ if (totvert == 0) {
+ dm->release(dm);
+ return;
+ }
+
+ if (mask & CD_MASK_ORCO) {
+ orco = get_object_orco(re, ob);
+ if (!orco) {
+ orco= dm->getVertDataArray(dm, CD_ORCO);
+ if (orco) {
+ orco= MEM_dupallocN(orco);
+ set_object_orco(re, ob, orco);
+ }
+ }
+ }
+
+ /* attempt to autsmooth on original mesh, only without subsurf */
+ if (do_autosmooth && me->totvert==totvert && me->totface==dm->getNumTessFaces(dm))
+ use_original_normals= true;
+
+ ma= give_render_material(re, ob, 1);
+
+
+ if (ma->material_type == MA_TYPE_HALO) {
+ make_render_halos(re, obr, me, totvert, mvert, ma, orco);
+ }
+ else {
+ const int *index_vert_orig = NULL;
+ const int *index_mf_to_mpoly = NULL;
+ const int *index_mp_to_orig = NULL;
+ if (need_origindex) {
+ index_vert_orig = dm->getVertDataArray(dm, CD_ORIGINDEX);
+ /* double lookup for faces -> polys */
+#ifdef WITH_FREESTYLE
+ index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
+ index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX);
+#endif
+ }
+
+ for (a=0; a<totvert; a++, mvert++) {
+ ver= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(ver->co, mvert->co);
+ if (do_autosmooth == false) { /* autosmooth on original unrotated data to prevent differences between frames */
+ normal_short_to_float_v3(ver->n, mvert->no);
+ mul_m4_v3(mat, ver->co);
+ mul_transposed_m3_v3(imat, ver->n);
+ normalize_v3(ver->n);
+ negate_v3(ver->n);
+ }
+
+ if (orco) {
+ ver->orco= orco;
+ orco+=3;
+ }
+
+ if (need_origindex) {
+ int *origindex;
+ origindex = RE_vertren_get_origindex(obr, ver, 1);
+
+ /* Use orig index array if it's available (e.g. in the presence
+ * of modifiers). */
+ if (index_vert_orig)
+ *origindex = index_vert_orig[a];
+ else
+ *origindex = a;
+ }
+ }
+
+ if (!timeoffset) {
+ short (*lnp)[4][3] = NULL;
+#ifdef WITH_FREESTYLE
+ EdgeHash *edge_hash;
+
+ /* create a hash table of Freestyle edge marks */
+ edge_hash = make_freestyle_edge_mark_hash(dm);
+#endif
+
+ /* store customdata names, because DerivedMesh is freed */
+ RE_set_customdata_names(obr, &dm->faceData);
+
+ /* add tangent layers if we need */
+ if ((ma->nmap_tangent_names_count && need_nmap_tangent_concrete) || need_tangent) {
+ dm->calcLoopTangents(
+ dm, need_tangent,
+ (const char (*)[MAX_NAME])ma->nmap_tangent_names, ma->nmap_tangent_names_count);
+ obr->tangent_mask = dm->tangent_mask;
+ DM_generate_tangent_tessface_data(dm, need_nmap_tangent_concrete || need_tangent);
+ }
+
+ /* still to do for keys: the correct local texture coordinate */
+
+ /* faces in order of color blocks */
+ vertofs= obr->totvert - totvert;
+ for (a1=0; (a1<ob->totcol || (a1==0 && ob->totcol==0)); a1++) {
+
+ ma= give_render_material(re, ob, a1+1);
+
+ /* test for 100% transparent */
+ ok = 1;
+ if ((ma->alpha == 0.0f) &&
+ (ma->spectra == 0.0f) &&
+ /* No need to test filter here, it's only active with MA_RAYTRANSP and we check against it below. */
+ /* (ma->filter == 0.0f) && */
+ (ma->mode & MA_TRANSP) &&
+ (ma->mode & (MA_RAYTRANSP | MA_RAYMIRROR)) == 0)
+ {
+ ok = 0;
+ /* texture on transparency? */
+ for (a=0; a<MAX_MTEX; a++) {
+ if (ma->mtex[a] && ma->mtex[a]->tex) {
+ if (ma->mtex[a]->mapto & MAP_ALPHA) ok= 1;
+ }
+ }
+ }
+
+ /* if wire material, and we got edges, don't do the faces */
+ if (ma->material_type == MA_TYPE_WIRE) {
+ end= dm->getNumEdges(dm);
+ if (end) ok= 0;
+ }
+
+ if (ok) {
+ end= dm->getNumTessFaces(dm);
+ mface= dm->getTessFaceArray(dm);
+ if (!loop_nors && do_autosmooth &&
+ (dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL) != NULL))
+ {
+ lnp = loop_nors = MEM_mallocN(sizeof(*loop_nors) * end, __func__);
+ }
+#ifdef WITH_FREESTYLE
+ index_mf_to_mpoly= dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
+ index_mp_to_orig= dm->getPolyDataArray(dm, CD_ORIGINDEX);
+ ffa= CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE);
+#endif
+
+ for (a=0; a<end; a++, mface++) {
+ int v1, v2, v3, v4, flag;
+
+ if ( mface->mat_nr==a1 ) {
+ float len;
+ bool reverse_verts = (negative_scale != 0 && do_autosmooth == false);
+ int rev_tab[] = {reverse_verts==0 ? 0 : 2, 1, reverse_verts==0 ? 2 : 0, 3};
+ v1= reverse_verts==0 ? mface->v1 : mface->v3;
+ v2= mface->v2;
+ v3= reverse_verts==0 ? mface->v3 : mface->v1;
+ v4= mface->v4;
+ flag = do_autosmooth ? ME_SMOOTH : mface->flag & ME_SMOOTH;
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, vertofs+v1);
+ vlr->v2= RE_findOrAddVert(obr, vertofs+v2);
+ vlr->v3= RE_findOrAddVert(obr, vertofs+v3);
+ if (v4) vlr->v4 = RE_findOrAddVert(obr, vertofs+v4);
+ else vlr->v4 = NULL;
+
+#ifdef WITH_FREESTYLE
+ /* Freestyle edge/face marks */
+ if (edge_hash) {
+ int edge_mark = 0;
+
+ if (has_freestyle_edge_mark(edge_hash, v1, v2)) edge_mark |= R_EDGE_V1V2;
+ if (has_freestyle_edge_mark(edge_hash, v2, v3)) edge_mark |= R_EDGE_V2V3;
+ if (!v4) {
+ if (has_freestyle_edge_mark(edge_hash, v3, v1)) edge_mark |= R_EDGE_V3V1;
+ }
+ else {
+ if (has_freestyle_edge_mark(edge_hash, v3, v4)) edge_mark |= R_EDGE_V3V4;
+ if (has_freestyle_edge_mark(edge_hash, v4, v1)) edge_mark |= R_EDGE_V4V1;
+ }
+ vlr->freestyle_edge_mark= edge_mark;
+ }
+ if (ffa) {
+ int index = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a;
+ vlr->freestyle_face_mark= (ffa[index].flag & FREESTYLE_FACE_MARK) ? 1 : 0;
+ }
+ else {
+ vlr->freestyle_face_mark= 0;
+ }
+#endif
+
+ /* render normals are inverted in render */
+ if (use_original_normals) {
+ MFace *mf= me->mface+a;
+ MVert *mv= me->mvert;
+
+ if (vlr->v4)
+ len= normal_quad_v3(vlr->n, mv[mf->v4].co, mv[mf->v3].co, mv[mf->v2].co, mv[mf->v1].co);
+ else
+ len= normal_tri_v3(vlr->n, mv[mf->v3].co, mv[mf->v2].co, mv[mf->v1].co);
+ }
+ else {
+ if (vlr->v4)
+ len= normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ else
+ len= normal_tri_v3(vlr->n, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ }
+
+ vlr->mat= ma;
+ vlr->flag= flag;
+ vlr->ec= 0; /* mesh edges rendered separately */
+
+ if (len==0) obr->totvlak--;
+ else {
+ CustomDataLayer *layer;
+ MTFace *mtface, *mtf;
+ MCol *mcol, *mc;
+ int index, mtfn= 0, mcn= 0, mln = 0, vindex;
+ char *name;
+ int nr_verts = v4!=0 ? 4 : 3;
+
+ for (index=0; index<dm->faceData.totlayer; index++) {
+ layer= &dm->faceData.layers[index];
+ name= layer->name;
+
+ if (layer->type == CD_MTFACE && mtfn < MAX_MTFACE) {
+ int t;
+ mtf= RE_vlakren_get_tface(obr, vlr, mtfn++, &name, 1);
+ mtface= (MTFace*)layer->data;
+ *mtf = mtface[a]; /* copy face info */
+ for (vindex=0; vindex<nr_verts; vindex++)
+ for (t=0; t<2; t++)
+ mtf->uv[vindex][t]=mtface[a].uv[rev_tab[vindex]][t];
+ }
+ else if (layer->type == CD_MCOL && mcn < MAX_MCOL) {
+ mc= RE_vlakren_get_mcol(obr, vlr, mcn++, &name, 1);
+ mcol= (MCol*)layer->data;
+ for (vindex=0; vindex<nr_verts; vindex++)
+ mc[vindex]=mcol[a*4+rev_tab[vindex]];
+ }
+ else if (layer->type == CD_TANGENT) {
+ if (need_nmap_tangent_concrete || need_tangent) {
+ int uv_start = CustomData_get_layer_index(&dm->faceData, CD_MTFACE);
+ int uv_index = CustomData_get_named_layer_index(&dm->faceData, CD_MTFACE, layer->name);
+
+ /* if there are no UVs, orco tangents are in first slot */
+ int n = (uv_start >= 0 && uv_index >= 0) ? uv_index - uv_start : 0;
+
+ const float *tangent = (const float *) layer->data;
+ float *ftang = RE_vlakren_get_nmap_tangent(obr, vlr, n, true);
+
+ for (vindex=0; vindex<nr_verts; vindex++) {
+ copy_v4_v4(ftang+vindex*4, tangent+a*16+rev_tab[vindex]*4);
+ mul_mat3_m4_v3(mat, ftang+vindex*4);
+ normalize_v3(ftang+vindex*4);
+ }
+ }
+ }
+ else if (layer->type == CD_TESSLOOPNORMAL && mln < 1) {
+ if (loop_nors) {
+ const short (*lnors)[4][3] = (const short (*)[4][3])layer->data;
+ for (vindex = 0; vindex < 4; vindex++) {
+ //print_v3("lnors[a][rev_tab[vindex]]", lnors[a][rev_tab[vindex]]);
+ copy_v3_v3_short((short *)lnp[0][vindex], lnors[a][rev_tab[vindex]]);
+ /* If we copy loop normals, we are doing autosmooth, so we are still
+ * in object space, no need to multiply with mat!
+ */
+ }
+ lnp++;
+ }
+ mln++;
+ }
+ }
+
+ if (need_origindex) {
+ /* Find original index of mpoly for this tessface. Options:
+ * - Modified mesh; two-step look up from tessface -> modified mpoly -> original mpoly
+ * - OR Tesselated mesh; look up from tessface -> mpoly
+ * - OR Failsafe; tessface == mpoly. Could probably assert(false) in this case? */
+ int *origindex;
+ origindex = RE_vlakren_get_origindex(obr, vlr, 1);
+ if (index_mf_to_mpoly && index_mp_to_orig)
+ *origindex = DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a);
+ else if (index_mf_to_mpoly)
+ *origindex = index_mf_to_mpoly[a];
+ else
+ *origindex = a;
+ }
+ }
+ }
+ }
+ }
+ }
+
+#ifdef WITH_FREESTYLE
+ /* release the hash table of Freestyle edge marks */
+ if (edge_hash)
+ BLI_edgehash_free(edge_hash, NULL);
+#endif
+
+ /* exception... we do edges for wire mode. potential conflict when faces exist... */
+ end= dm->getNumEdges(dm);
+ mvert= dm->getVertArray(dm);
+ ma= give_render_material(re, ob, 1);
+ if (end && (ma->material_type == MA_TYPE_WIRE)) {
+ MEdge *medge;
+ struct edgesort *edgetable;
+ int totedge= 0;
+ recalc_normals= 1;
+
+ medge= dm->getEdgeArray(dm);
+
+ /* we want edges to have UV and vcol too... */
+ edgetable= make_mesh_edge_lookup(dm, &totedge);
+
+ for (a1=0; a1<end; a1++, medge++) {
+ if (medge->flag&ME_EDGERENDER) {
+ MVert *v0 = &mvert[medge->v1];
+ MVert *v1 = &mvert[medge->v2];
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, vertofs+medge->v1);
+ vlr->v2= RE_findOrAddVert(obr, vertofs+medge->v2);
+ vlr->v3= vlr->v2;
+ vlr->v4= NULL;
+
+ if (edgetable)
+ use_mesh_edge_lookup(obr, dm, medge, vlr, edgetable, totedge);
+
+ xn= -(v0->no[0]+v1->no[0]);
+ yn= -(v0->no[1]+v1->no[1]);
+ zn= -(v0->no[2]+v1->no[2]);
+ /* transpose ! */
+ vlr->n[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn;
+ vlr->n[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn;
+ vlr->n[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn;
+ normalize_v3(vlr->n);
+
+ vlr->mat= ma;
+ vlr->flag= 0;
+ vlr->ec= ME_V1V2;
+ }
+ }
+ if (edgetable)
+ MEM_freeN(edgetable);
+ }
+ }
+ }
+
+ if (!timeoffset) {
+ if (need_stress)
+ calc_edge_stress(re, obr, me);
+
+ if (do_displace) {
+ calc_vertexnormals(re, obr, 1, 0, 0);
+ displace(re, obr);
+ recalc_normals = 0; /* Already computed by displace! */
+ }
+ else if (do_autosmooth) {
+ recalc_normals = (loop_nors == NULL); /* Should never happen, but better be safe than sorry. */
+ autosmooth(re, obr, mat, loop_nors);
+ }
+
+ if (recalc_normals!=0 || need_tangent!=0)
+ calc_vertexnormals(re, obr, recalc_normals, need_tangent, need_nmap_tangent_concrete);
+ }
+
+ MEM_SAFE_FREE(loop_nors);
+
+ dm->release(dm);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Lamps and Shadowbuffers */
+/* ------------------------------------------------------------------------- */
+
+static void initshadowbuf(Render *re, LampRen *lar, float mat[4][4])
+{
+ struct ShadBuf *shb;
+ float viewinv[4][4];
+
+ /* if (la->spsi<16) return; */
+
+ /* memory alloc */
+ shb= (struct ShadBuf *)MEM_callocN(sizeof(struct ShadBuf), "initshadbuf");
+ lar->shb= shb;
+
+ if (shb==NULL) return;
+
+ VECCOPY(shb->co, lar->co); /* int copy */
+
+ /* percentage render: keep track of min and max */
+ shb->size= (lar->bufsize*re->r.size)/100;
+
+ if (shb->size<512) shb->size= 512;
+ else if (shb->size > lar->bufsize) shb->size= lar->bufsize;
+
+ shb->size &= ~15; /* make sure its multiples of 16 */
+
+ shb->samp= lar->samp;
+ shb->soft= lar->soft;
+ shb->shadhalostep= lar->shadhalostep;
+
+ normalize_m4(mat);
+ invert_m4_m4(shb->winmat, mat); /* winmat is temp */
+
+ /* matrix: combination of inverse view and lampmat */
+ /* calculate again: the ortho-render has no correct viewinv */
+ invert_m4_m4(viewinv, re->viewmat);
+ mul_m4_m4m4(shb->viewmat, shb->winmat, viewinv);
+
+ /* projection */
+ shb->d= lar->clipsta;
+ shb->clipend= lar->clipend;
+
+ /* bias is percentage, made 2x larger because of correction for angle of incidence */
+ /* when a ray is closer to parallel of a face, bias value is increased during render */
+ shb->bias= (0.02f*lar->bias)*0x7FFFFFFF;
+
+ /* halfway method (average of first and 2nd z) reduces bias issues */
+ if (ELEM(lar->buftype, LA_SHADBUF_HALFWAY, LA_SHADBUF_DEEP))
+ shb->bias= 0.1f*shb->bias;
+
+ shb->compressthresh= lar->compressthresh;
+}
+
+void area_lamp_vectors(LampRen *lar)
+{
+ float xsize= 0.5f*lar->area_size, ysize= 0.5f*lar->area_sizey, multifac;
+
+ /* make it smaller, so area light can be multisampled */
+ multifac= 1.0f/sqrtf((float)lar->ray_totsamp);
+ xsize *= multifac;
+ ysize *= multifac;
+
+ /* corner vectors */
+ lar->area[0][0]= lar->co[0] - xsize*lar->mat[0][0] - ysize*lar->mat[1][0];
+ lar->area[0][1]= lar->co[1] - xsize*lar->mat[0][1] - ysize*lar->mat[1][1];
+ lar->area[0][2]= lar->co[2] - xsize*lar->mat[0][2] - ysize*lar->mat[1][2];
+
+ /* corner vectors */
+ lar->area[1][0]= lar->co[0] - xsize*lar->mat[0][0] + ysize*lar->mat[1][0];
+ lar->area[1][1]= lar->co[1] - xsize*lar->mat[0][1] + ysize*lar->mat[1][1];
+ lar->area[1][2]= lar->co[2] - xsize*lar->mat[0][2] + ysize*lar->mat[1][2];
+
+ /* corner vectors */
+ lar->area[2][0]= lar->co[0] + xsize*lar->mat[0][0] + ysize*lar->mat[1][0];
+ lar->area[2][1]= lar->co[1] + xsize*lar->mat[0][1] + ysize*lar->mat[1][1];
+ lar->area[2][2]= lar->co[2] + xsize*lar->mat[0][2] + ysize*lar->mat[1][2];
+
+ /* corner vectors */
+ lar->area[3][0]= lar->co[0] + xsize*lar->mat[0][0] - ysize*lar->mat[1][0];
+ lar->area[3][1]= lar->co[1] + xsize*lar->mat[0][1] - ysize*lar->mat[1][1];
+ lar->area[3][2]= lar->co[2] + xsize*lar->mat[0][2] - ysize*lar->mat[1][2];
+ /* only for correction button size, matrix size works on energy */
+ lar->areasize= lar->dist*lar->dist/(4.0f*xsize*ysize);
+}
+
+/* If lar takes more lamp data, the decoupling will be better. */
+static GroupObject *add_render_lamp(Render *re, Object *ob)
+{
+ Lamp *la= ob->data;
+ LampRen *lar;
+ GroupObject *go;
+ float mat[4][4], angle, xn, yn;
+ float vec[3];
+ int c;
+
+ /* previewrender sets this to zero... prevent accidents */
+ if (la==NULL) return NULL;
+
+ /* prevent only shadow from rendering light */
+ if (la->mode & LA_ONLYSHADOW)
+ if ((re->r.mode & R_SHADOW)==0)
+ return NULL;
+
+ re->totlamp++;
+
+ /* groups is used to unify support for lightgroups, this is the global lightgroup */
+ go= MEM_callocN(sizeof(GroupObject), "groupobject");
+ BLI_addtail(&re->lights, go);
+ go->ob= ob;
+ /* lamprens are in own list, for freeing */
+ lar= (LampRen *)MEM_callocN(sizeof(LampRen), "lampren");
+ BLI_addtail(&re->lampren, lar);
+ go->lampren= lar;
+
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat);
+
+ copy_m4_m4(lar->lampmat, ob->obmat);
+ copy_m3_m4(lar->mat, mat);
+ copy_m3_m4(lar->imat, ob->imat);
+
+ lar->bufsize = la->bufsize;
+ lar->samp = la->samp;
+ lar->buffers= la->buffers;
+ if (lar->buffers==0) lar->buffers= 1;
+ lar->buftype= la->buftype;
+ lar->filtertype= la->filtertype;
+ lar->soft = la->soft;
+ lar->shadhalostep = la->shadhalostep;
+ lar->clipsta = la->clipsta;
+ lar->clipend = la->clipend;
+
+ lar->bias = la->bias;
+ lar->compressthresh = la->compressthresh;
+
+ lar->type= la->type;
+ lar->mode= la->mode;
+
+ lar->energy= la->energy;
+ if (la->mode & LA_NEG) lar->energy= -lar->energy;
+
+ lar->vec[0]= -mat[2][0];
+ lar->vec[1]= -mat[2][1];
+ lar->vec[2]= -mat[2][2];
+ normalize_v3(lar->vec);
+ lar->co[0]= mat[3][0];
+ lar->co[1]= mat[3][1];
+ lar->co[2]= mat[3][2];
+ lar->dist= la->dist;
+ lar->haint= la->haint;
+ lar->distkw= lar->dist*lar->dist;
+ lar->r= lar->energy*la->r;
+ lar->g= lar->energy*la->g;
+ lar->b= lar->energy*la->b;
+ lar->shdwr= la->shdwr;
+ lar->shdwg= la->shdwg;
+ lar->shdwb= la->shdwb;
+ lar->k= la->k;
+
+ /* area */
+ lar->ray_samp= la->ray_samp;
+ lar->ray_sampy= la->ray_sampy;
+ lar->ray_sampz= la->ray_sampz;
+
+ lar->area_size= la->area_size;
+ lar->area_sizey= la->area_sizey;
+ lar->area_sizez= la->area_sizez;
+
+ lar->area_shape= la->area_shape;
+
+ /* Annoying, lamp UI does this, but the UI might not have been used? - add here too.
+ * make sure this matches buttons_shading.c's logic */
+ if (ELEM(la->type, LA_AREA, LA_SPOT, LA_SUN, LA_LOCAL) && (la->mode & LA_SHAD_RAY))
+ if (ELEM(la->type, LA_SPOT, LA_SUN, LA_LOCAL))
+ if (la->ray_samp_method == LA_SAMP_CONSTANT) la->ray_samp_method = LA_SAMP_HALTON;
+
+ lar->ray_samp_method= la->ray_samp_method;
+ lar->ray_samp_type= la->ray_samp_type;
+
+ lar->adapt_thresh= la->adapt_thresh;
+ lar->sunsky = NULL;
+
+ if ( ELEM(lar->type, LA_SPOT, LA_LOCAL)) {
+ lar->ray_totsamp= lar->ray_samp*lar->ray_samp;
+ lar->area_shape = LA_AREA_SQUARE;
+ lar->area_sizey= lar->area_size;
+ }
+ else if (lar->type==LA_AREA) {
+ switch (lar->area_shape) {
+ case LA_AREA_SQUARE:
+ lar->ray_totsamp= lar->ray_samp*lar->ray_samp;
+ lar->ray_sampy= lar->ray_samp;
+ lar->area_sizey= lar->area_size;
+ break;
+ case LA_AREA_RECT:
+ lar->ray_totsamp= lar->ray_samp*lar->ray_sampy;
+ break;
+ case LA_AREA_CUBE:
+ lar->ray_totsamp= lar->ray_samp*lar->ray_samp*lar->ray_samp;
+ lar->ray_sampy= lar->ray_samp;
+ lar->ray_sampz= lar->ray_samp;
+ lar->area_sizey= lar->area_size;
+ lar->area_sizez= lar->area_size;
+ break;
+ case LA_AREA_BOX:
+ lar->ray_totsamp= lar->ray_samp*lar->ray_sampy*lar->ray_sampz;
+ break;
+ }
+
+ area_lamp_vectors(lar);
+ init_jitter_plane(lar); /* subsamples */
+ }
+ else if (lar->type==LA_SUN) {
+ lar->ray_totsamp= lar->ray_samp*lar->ray_samp;
+ lar->area_shape = LA_AREA_SQUARE;
+ lar->area_sizey= lar->area_size;
+
+ if ((la->sun_effect_type & LA_SUN_EFFECT_SKY) ||
+ (la->sun_effect_type & LA_SUN_EFFECT_AP))
+ {
+ lar->sunsky = (struct SunSky*)MEM_callocN(sizeof(struct SunSky), "sunskyren");
+ lar->sunsky->effect_type = la->sun_effect_type;
+
+ copy_v3_v3(vec, ob->obmat[2]);
+ normalize_v3(vec);
+
+ InitSunSky(
+ lar->sunsky, la->atm_turbidity, vec, la->horizon_brightness,
+ la->spread, la->sun_brightness, la->sun_size, la->backscattered_light,
+ la->skyblendfac, la->skyblendtype, la->sky_exposure, la->sky_colorspace);
+ InitAtmosphere(
+ lar->sunsky, la->sun_intensity, 1.0, 1.0, la->atm_inscattering_factor, la->atm_extinction_factor,
+ la->atm_distance_factor);
+ }
+ }
+ else lar->ray_totsamp= 0;
+
+ lar->spotsi= la->spotsize;
+ if (lar->mode & LA_HALO) {
+ if (lar->spotsi > DEG2RADF(170.0f)) lar->spotsi = DEG2RADF(170.0f);
+ }
+ lar->spotsi= cosf(lar->spotsi * 0.5f);
+ lar->spotbl= (1.0f-lar->spotsi)*la->spotblend;
+
+ memcpy(lar->mtex, la->mtex, MAX_MTEX*sizeof(void *));
+
+ lar->lay = ob->lay & 0xFFFFFF; /* higher 8 bits are localview layers */
+
+ lar->falloff_type = la->falloff_type;
+ lar->ld1= la->att1;
+ lar->ld2= la->att2;
+ lar->coeff_const= la->coeff_const;
+ lar->coeff_lin= la->coeff_lin;
+ lar->coeff_quad= la->coeff_quad;
+ lar->curfalloff = curvemapping_copy(la->curfalloff);
+
+ if (lar->curfalloff) {
+ /* so threads don't conflict on init */
+ curvemapping_initialize(lar->curfalloff);
+ }
+
+ if (lar->type==LA_SPOT) {
+
+ normalize_v3(lar->imat[0]);
+ normalize_v3(lar->imat[1]);
+ normalize_v3(lar->imat[2]);
+
+ xn = saacos(lar->spotsi);
+ xn = sinf(xn) / cosf(xn);
+ lar->spottexfac= 1.0f/(xn);
+
+ if (lar->mode & LA_ONLYSHADOW) {
+ if ((lar->mode & (LA_SHAD_BUF|LA_SHAD_RAY))==0) lar->mode -= LA_ONLYSHADOW;
+ }
+
+ }
+
+ /* set flag for spothalo en initvars */
+ if ((la->type == LA_SPOT) && (la->mode & LA_HALO) &&
+ (!(la->mode & LA_SHAD_BUF) || la->buftype != LA_SHADBUF_DEEP))
+ {
+ if (la->haint>0.0f) {
+ re->flag |= R_LAMPHALO;
+
+ /* camera position (0, 0, 0) rotate around lamp */
+ lar->sh_invcampos[0]= -lar->co[0];
+ lar->sh_invcampos[1]= -lar->co[1];
+ lar->sh_invcampos[2]= -lar->co[2];
+ mul_m3_v3(lar->imat, lar->sh_invcampos);
+
+ /* z factor, for a normalized volume */
+ angle= saacos(lar->spotsi);
+ xn= lar->spotsi;
+ yn = sinf(angle);
+ lar->sh_zfac= yn/xn;
+ /* pre-scale */
+ lar->sh_invcampos[2]*= lar->sh_zfac;
+
+ /* halfway shadow buffer doesn't work for volumetric effects */
+ if (ELEM(lar->buftype, LA_SHADBUF_HALFWAY, LA_SHADBUF_DEEP))
+ lar->buftype = LA_SHADBUF_REGULAR;
+
+ }
+ }
+ else if (la->type==LA_HEMI) {
+ lar->mode &= ~(LA_SHAD_RAY|LA_SHAD_BUF);
+ }
+
+ for (c=0; c<MAX_MTEX; c++) {
+ if (la->mtex[c] && la->mtex[c]->tex) {
+ if (la->mtex[c]->mapto & LAMAP_COL)
+ lar->mode |= LA_TEXTURE;
+ if (la->mtex[c]->mapto & LAMAP_SHAD)
+ lar->mode |= LA_SHAD_TEX;
+
+ if (G.is_rendering) {
+ if (re->osa) {
+ if (la->mtex[c]->tex->type==TEX_IMAGE) lar->mode |= LA_OSATEX;
+ }
+ }
+ }
+ }
+
+ /* old code checked for internal render (aka not yafray) */
+ {
+ /* to make sure we can check ray shadow easily in the render code */
+ if (lar->mode & LA_SHAD_RAY) {
+ if ( (re->r.mode & R_RAYTRACE)==0)
+ lar->mode &= ~LA_SHAD_RAY;
+ }
+
+
+ if (re->r.mode & R_SHADOW) {
+
+ if (la->type==LA_AREA && (lar->mode & LA_SHAD_RAY) && (lar->ray_samp_method == LA_SAMP_CONSTANT)) {
+ init_jitter_plane(lar);
+ }
+ else if (la->type==LA_SPOT && (lar->mode & LA_SHAD_BUF) ) {
+ /* Per lamp, one shadow buffer is made. */
+ lar->bufflag= la->bufflag;
+ copy_m4_m4(mat, ob->obmat);
+ initshadowbuf(re, lar, mat); /* mat is altered */
+ }
+
+
+ /* this is the way used all over to check for shadow */
+ if (lar->shb || (lar->mode & LA_SHAD_RAY)) {
+ LampShadowSample *ls;
+ LampShadowSubSample *lss;
+ int a, b;
+
+ memset(re->shadowsamplenr, 0, sizeof(re->shadowsamplenr));
+
+ lar->shadsamp= MEM_mallocN(re->r.threads*sizeof(LampShadowSample), "lamp shadow sample");
+ ls= lar->shadsamp;
+
+ /* shadfacs actually mean light, let's put them to 1 to prevent unitialized accidents */
+ for (a=0; a<re->r.threads; a++, ls++) {
+ lss= ls->s;
+ for (b=0; b<re->r.osa; b++, lss++) {
+ lss->samplenr= -1; /* used to detect whether we store or read */
+ lss->shadfac[0]= 1.0f;
+ lss->shadfac[1]= 1.0f;
+ lss->shadfac[2]= 1.0f;
+ lss->shadfac[3]= 1.0f;
+ }
+ }
+ }
+ }
+ }
+
+ return go;
+}
+
+static bool is_object_restricted(Render *re, Object *ob)
+{
+ if (re->r.scemode & R_VIEWPORT_PREVIEW)
+ return (ob->restrictflag & OB_RESTRICT_VIEW) != 0;
+ else
+ return (ob->restrictflag & OB_RESTRICT_RENDER) != 0;
+}
+
+static bool is_object_hidden(Render *re, Object *ob)
+{
+ if (is_object_restricted(re, ob))
+ return true;
+
+ if (re->r.scemode & R_VIEWPORT_PREVIEW) {
+ /* Mesh deform cages and so on mess up the preview. To avoid the problem,
+ * viewport doesn't show mesh object if its draw type is bounding box or wireframe.
+ * Unless it's an active smoke domain!
+ */
+ ModifierData *md = NULL;
+
+ if ((md = modifiers_findByType(ob, eModifierType_Smoke)) &&
+ (modifier_isEnabled(re->scene, md, eModifierMode_Realtime)))
+ {
+ return false;
+ }
+ return ELEM(ob->dt, OB_BOUNDBOX, OB_WIRE);
+ }
+ else {
+ return false;
+ }
+}
+
+/* layflag: allows material group to ignore layerflag */
+static void add_lightgroup(Render *re, Group *group, int exclusive)
+{
+ GroupObject *go, *gol;
+
+ group->id.tag &= ~LIB_TAG_DOIT;
+
+ /* it's a bit too many loops in loops... but will survive */
+ /* note that 'exclusive' will remove it from the global list */
+ for (go= group->gobject.first; go; go= go->next) {
+ go->lampren= NULL;
+
+ if (is_object_hidden(re, go->ob))
+ continue;
+
+ if (go->ob->lay & re->lay) {
+ if (go->ob && go->ob->type==OB_LAMP) {
+ for (gol= re->lights.first; gol; gol= gol->next) {
+ if (gol->ob==go->ob) {
+ go->lampren= gol->lampren;
+ break;
+ }
+ }
+ if (go->lampren==NULL)
+ gol= add_render_lamp(re, go->ob);
+ if (gol && exclusive) {
+ BLI_remlink(&re->lights, gol);
+ MEM_freeN(gol);
+ }
+ }
+ }
+ }
+}
+
+static void set_material_lightgroups(Render *re)
+{
+ Group *group;
+ Material *ma;
+
+ /* not for preview render */
+ if (re->scene->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))
+ return;
+
+ for (group= re->main->group.first; group; group=group->id.next)
+ group->id.tag |= LIB_TAG_DOIT;
+
+ /* it's a bit too many loops in loops... but will survive */
+ /* hola! materials not in use...? */
+ for (ma= re->main->mat.first; ma; ma=ma->id.next) {
+ if (ma->group && (ma->group->id.tag & LIB_TAG_DOIT))
+ add_lightgroup(re, ma->group, ma->mode & MA_GROUP_NOLAY);
+ }
+}
+
+static void set_renderlayer_lightgroups(Render *re, Scene *sce)
+{
+ SceneRenderLayer *srl;
+
+ for (srl= sce->r.layers.first; srl; srl= srl->next) {
+ if (srl->light_override)
+ add_lightgroup(re, srl->light_override, 0);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* World */
+/* ------------------------------------------------------------------------- */
+
+void init_render_world(Render *re)
+{
+ void *wrld_prev[2] = {
+ re->wrld.aotables,
+ re->wrld.aosphere,
+ };
+
+ int a;
+
+ if (re->scene && re->scene->world) {
+ re->wrld = *(re->scene->world);
+
+ copy_v3_v3(re->grvec, re->viewmat[2]);
+ normalize_v3(re->grvec);
+ copy_m3_m4(re->imat, re->viewinv);
+
+ for (a=0; a<MAX_MTEX; a++)
+ if (re->wrld.mtex[a] && re->wrld.mtex[a]->tex) re->wrld.skytype |= WO_SKYTEX;
+
+ /* AO samples should be OSA minimum */
+ if (re->osa)
+ while (re->wrld.aosamp*re->wrld.aosamp < re->osa)
+ re->wrld.aosamp++;
+ if (!(re->r.mode & R_RAYTRACE) && (re->wrld.ao_gather_method == WO_AOGATHER_RAYTRACE))
+ re->wrld.mode &= ~(WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT);
+ }
+ else {
+ memset(&re->wrld, 0, sizeof(World));
+ re->wrld.exp= 0.0f;
+ re->wrld.range= 1.0f;
+
+ /* for mist pass */
+ re->wrld.miststa= re->clipsta;
+ re->wrld.mistdist= re->clipend-re->clipsta;
+ re->wrld.misi= 1.0f;
+ }
+
+ re->wrld.linfac= 1.0f + powf((2.0f*re->wrld.exp + 0.5f), -10);
+ re->wrld.logfac= logf((re->wrld.linfac-1.0f)/re->wrld.linfac) / re->wrld.range;
+
+ /* restore runtime vars, needed for viewport rendering [#36005] */
+ re->wrld.aotables = wrld_prev[0];
+ re->wrld.aosphere = wrld_prev[1];
+}
+
+
+
+/* ------------------------------------------------------------------------- */
+/* Object Finalization */
+/* ------------------------------------------------------------------------- */
+
+/* prevent phong interpolation for giving ray shadow errors (terminator problem) */
+static void set_phong_threshold(ObjectRen *obr)
+{
+// VertRen *ver;
+ VlakRen *vlr;
+ float thresh= 0.0, dot;
+ int tot=0, i;
+
+ /* Added check for 'pointy' situations, only dotproducts of 0.9 and larger
+ * are taken into account. This threshold is meant to work on smooth geometry, not
+ * for extreme cases (ton) */
+
+ for (i=0; i<obr->totvlak; i++) {
+ vlr= RE_findOrAddVlak(obr, i);
+ if ((vlr->flag & R_SMOOTH) && (vlr->flag & R_STRAND)==0) {
+ dot= dot_v3v3(vlr->n, vlr->v1->n);
+ dot= ABS(dot);
+ if (dot>0.9f) {
+ thresh+= dot; tot++;
+ }
+ dot= dot_v3v3(vlr->n, vlr->v2->n);
+ dot= ABS(dot);
+ if (dot>0.9f) {
+ thresh+= dot; tot++;
+ }
+
+ dot= dot_v3v3(vlr->n, vlr->v3->n);
+ dot= ABS(dot);
+ if (dot>0.9f) {
+ thresh+= dot; tot++;
+ }
+
+ if (vlr->v4) {
+ dot= dot_v3v3(vlr->n, vlr->v4->n);
+ dot= ABS(dot);
+ if (dot>0.9f) {
+ thresh+= dot; tot++;
+ }
+ }
+ }
+ }
+
+ if (tot) {
+ thresh/= (float)tot;
+ obr->ob->smoothresh= cosf(0.5f*(float)M_PI-saacos(thresh));
+ }
+}
+
+/* per face check if all samples should be taken.
+ * if raytrace or multisample, do always for raytraced material, or when material full_osa set */
+static void set_fullsample_trace_flag(Render *re, ObjectRen *obr)
+{
+ VlakRen *vlr;
+ int a, trace, mode, osa;
+
+ osa= re->osa;
+ trace= re->r.mode & R_RAYTRACE;
+
+ for (a=obr->totvlak-1; a>=0; a--) {
+ vlr= RE_findOrAddVlak(obr, a);
+ mode= vlr->mat->mode;
+
+ if (trace && (mode & MA_TRACEBLE))
+ vlr->flag |= R_TRACEBLE;
+
+ if (osa) {
+ if (mode & MA_FULL_OSA) {
+ vlr->flag |= R_FULL_OSA;
+ }
+ else if (trace) {
+ if (mode & MA_SHLESS) {
+ /* pass */
+ }
+ else if (vlr->mat->material_type == MA_TYPE_VOLUME) {
+ /* pass */
+ }
+ else if ((mode & MA_RAYMIRROR) || ((mode & MA_TRANSP) && (mode & MA_RAYTRANSP))) {
+ /* for blurry reflect/refract, better to take more samples
+ * inside the raytrace than as OSA samples */
+ if ((vlr->mat->gloss_mir == 1.0f) && (vlr->mat->gloss_tra == 1.0f))
+ vlr->flag |= R_FULL_OSA;
+ }
+ }
+ }
+ }
+}
+
+/* split quads for predictable baking
+ * dir 1 == (0, 1, 2) (0, 2, 3), 2 == (1, 3, 0) (1, 2, 3)
+ */
+static void split_quads(ObjectRen *obr, int dir)
+{
+ VlakRen *vlr, *vlr1;
+ int a;
+
+ for (a=obr->totvlak-1; a>=0; a--) {
+ vlr= RE_findOrAddVlak(obr, a);
+
+ /* test if rendering as a quad or triangle, skip wire */
+ if ((vlr->flag & R_STRAND)==0 && (vlr->mat->material_type != MA_TYPE_WIRE)) {
+
+ if (vlr->v4) {
+
+ vlr1= RE_vlakren_copy(obr, vlr);
+ vlr1->flag |= R_FACE_SPLIT;
+
+ if ( dir==2 ) vlr->flag |= R_DIVIDE_24;
+ else vlr->flag &= ~R_DIVIDE_24;
+
+ /* new vertex pointers */
+ if (vlr->flag & R_DIVIDE_24) {
+ vlr1->v1= vlr->v2;
+ vlr1->v2= vlr->v3;
+ vlr1->v3= vlr->v4;
+
+ vlr->v3 = vlr->v4;
+
+ vlr1->flag |= R_DIVIDE_24;
+ }
+ else {
+ vlr1->v1= vlr->v1;
+ vlr1->v2= vlr->v3;
+ vlr1->v3= vlr->v4;
+
+ vlr1->flag &= ~R_DIVIDE_24;
+ }
+ vlr->v4 = vlr1->v4 = NULL;
+
+#ifdef WITH_FREESTYLE
+ /* Freestyle edge marks */
+ if (vlr->flag & R_DIVIDE_24) {
+ vlr1->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V2V3) ? R_EDGE_V1V2 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V3V4) ? R_EDGE_V2V3 : 0);
+ vlr->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V1V2) ? R_EDGE_V1V2 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V4V1) ? R_EDGE_V3V1 : 0);
+ }
+ else {
+ vlr1->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V3V4) ? R_EDGE_V2V3 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V4V1) ? R_EDGE_V3V1 : 0);
+ vlr->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V1V2) ? R_EDGE_V1V2 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V2V3) ? R_EDGE_V2V3 : 0);
+ }
+#endif
+
+ /* new normals */
+ normal_tri_v3(vlr->n, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ normal_tri_v3(vlr1->n, vlr1->v3->co, vlr1->v2->co, vlr1->v1->co);
+ }
+ /* clear the flag when not divided */
+ else vlr->flag &= ~R_DIVIDE_24;
+ }
+ }
+}
+
+static void check_non_flat_quads(ObjectRen *obr)
+{
+ VlakRen *vlr, *vlr1;
+ VertRen *v1, *v2, *v3, *v4;
+ float nor[3], xn, flen;
+ int a;
+
+ for (a=obr->totvlak-1; a>=0; a--) {
+ vlr= RE_findOrAddVlak(obr, a);
+
+ /* test if rendering as a quad or triangle, skip wire */
+ if (vlr->v4 && (vlr->flag & R_STRAND)==0 && (vlr->mat->material_type != MA_TYPE_WIRE)) {
+
+ /* check if quad is actually triangle */
+ v1= vlr->v1;
+ v2= vlr->v2;
+ v3= vlr->v3;
+ v4= vlr->v4;
+ sub_v3_v3v3(nor, v1->co, v2->co);
+ if ( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
+ vlr->v1= v2;
+ vlr->v2= v3;
+ vlr->v3= v4;
+ vlr->v4= NULL;
+ vlr->flag |= (R_DIVIDE_24 | R_FACE_SPLIT);
+ }
+ else {
+ sub_v3_v3v3(nor, v2->co, v3->co);
+ if ( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
+ vlr->v2= v3;
+ vlr->v3= v4;
+ vlr->v4= NULL;
+ vlr->flag |= R_FACE_SPLIT;
+ }
+ else {
+ sub_v3_v3v3(nor, v3->co, v4->co);
+ if ( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
+ vlr->v4= NULL;
+ }
+ else {
+ sub_v3_v3v3(nor, v4->co, v1->co);
+ if ( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
+ vlr->v4= NULL;
+ }
+ }
+ }
+ }
+
+ if (vlr->v4) {
+
+ /* Face is divided along edge with the least gradient */
+ /* Flagged with R_DIVIDE_24 if divide is from vert 2 to 4 */
+ /* 4---3 4---3 */
+ /* |\ 1| or |1 /| */
+ /* |0\ | |/ 0| */
+ /* 1---2 1---2 0 = orig face, 1 = new face */
+
+ /* render normals are inverted in render! we calculate normal of single tria here */
+ flen= normal_tri_v3(nor, vlr->v4->co, vlr->v3->co, vlr->v1->co);
+ if (flen==0.0f) normal_tri_v3(nor, vlr->v4->co, vlr->v2->co, vlr->v1->co);
+
+ xn = dot_v3v3(nor, vlr->n);
+
+ if (ABS(xn) < 0.999995f ) { /* checked on noisy fractal grid */
+
+ float d1, d2;
+
+ vlr1= RE_vlakren_copy(obr, vlr);
+ vlr1->flag |= R_FACE_SPLIT;
+
+ /* split direction based on vnorms */
+ normal_tri_v3(nor, vlr->v1->co, vlr->v2->co, vlr->v3->co);
+ d1 = dot_v3v3(nor, vlr->v1->n);
+
+ normal_tri_v3(nor, vlr->v2->co, vlr->v3->co, vlr->v4->co);
+ d2 = dot_v3v3(nor, vlr->v2->n);
+
+ if (fabsf(d1) < fabsf(d2) ) vlr->flag |= R_DIVIDE_24;
+ else vlr->flag &= ~R_DIVIDE_24;
+
+ /* new vertex pointers */
+ if (vlr->flag & R_DIVIDE_24) {
+ vlr1->v1= vlr->v2;
+ vlr1->v2= vlr->v3;
+ vlr1->v3= vlr->v4;
+
+ vlr->v3 = vlr->v4;
+
+ vlr1->flag |= R_DIVIDE_24;
+ }
+ else {
+ vlr1->v1= vlr->v1;
+ vlr1->v2= vlr->v3;
+ vlr1->v3= vlr->v4;
+
+ vlr1->flag &= ~R_DIVIDE_24;
+ }
+ vlr->v4 = vlr1->v4 = NULL;
+
+ /* new normals */
+ normal_tri_v3(vlr->n, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ normal_tri_v3(vlr1->n, vlr1->v3->co, vlr1->v2->co, vlr1->v1->co);
+
+#ifdef WITH_FREESTYLE
+ /* Freestyle edge marks */
+ if (vlr->flag & R_DIVIDE_24) {
+ vlr1->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V2V3) ? R_EDGE_V1V2 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V3V4) ? R_EDGE_V2V3 : 0);
+ vlr->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V1V2) ? R_EDGE_V1V2 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V4V1) ? R_EDGE_V3V1 : 0);
+ }
+ else {
+ vlr1->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V3V4) ? R_EDGE_V2V3 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V4V1) ? R_EDGE_V3V1 : 0);
+ vlr->freestyle_edge_mark=
+ ((vlr->freestyle_edge_mark & R_EDGE_V1V2) ? R_EDGE_V1V2 : 0) |
+ ((vlr->freestyle_edge_mark & R_EDGE_V2V3) ? R_EDGE_V2V3 : 0);
+ }
+#endif
+ }
+ /* clear the flag when not divided */
+ else vlr->flag &= ~R_DIVIDE_24;
+ }
+ }
+ }
+}
+
+static void finalize_render_object(Render *re, ObjectRen *obr, int timeoffset)
+{
+ Object *ob= obr->ob;
+ VertRen *ver= NULL;
+ StrandRen *strand= NULL;
+ StrandBound *sbound= NULL;
+ float min[3], max[3], smin[3], smax[3];
+ int a, b;
+
+ if (obr->totvert || obr->totvlak || obr->tothalo || obr->totstrand) {
+ /* the exception below is because displace code now is in init_render_mesh call,
+ * I will look at means to have autosmooth enabled for all object types
+ * and have it as general postprocess, like displace */
+ if (ob->type!=OB_MESH && test_for_displace(re, ob))
+ displace(re, obr);
+
+ if (!timeoffset) {
+ /* phong normal interpolation can cause error in tracing
+ * (terminator problem) */
+ ob->smoothresh= 0.0;
+ if ((re->r.mode & R_RAYTRACE) && (re->r.mode & R_SHADOW))
+ set_phong_threshold(obr);
+
+ if (re->flag & R_BAKING && re->r.bake_quad_split != 0) {
+ /* Baking lets us define a quad split order */
+ split_quads(obr, re->r.bake_quad_split);
+ }
+ else if (BKE_object_is_animated(re->scene, ob))
+ split_quads(obr, 1);
+ else {
+ if ((re->r.mode & R_SIMPLIFY && re->r.simplify_flag & R_SIMPLE_NO_TRIANGULATE) == 0)
+ check_non_flat_quads(obr);
+ }
+
+ set_fullsample_trace_flag(re, obr);
+
+ /* compute bounding boxes for clipping */
+ INIT_MINMAX(min, max);
+ for (a=0; a<obr->totvert; a++) {
+ if ((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
+ else ver++;
+
+ minmax_v3v3_v3(min, max, ver->co);
+ }
+
+ if (obr->strandbuf) {
+ float width;
+
+ /* compute average bounding box of strandpoint itself (width) */
+ if (obr->strandbuf->flag & R_STRAND_B_UNITS)
+ obr->strandbuf->maxwidth = max_ff(obr->strandbuf->ma->strand_sta, obr->strandbuf->ma->strand_end);
+ else
+ obr->strandbuf->maxwidth= 0.0f;
+
+ width= obr->strandbuf->maxwidth;
+ sbound= obr->strandbuf->bound;
+ for (b=0; b<obr->strandbuf->totbound; b++, sbound++) {
+
+ INIT_MINMAX(smin, smax);
+
+ for (a=sbound->start; a<sbound->end; a++) {
+ strand= RE_findOrAddStrand(obr, a);
+ strand_minmax(strand, smin, smax, width);
+ }
+
+ copy_v3_v3(sbound->boundbox[0], smin);
+ copy_v3_v3(sbound->boundbox[1], smax);
+
+ minmax_v3v3_v3(min, max, smin);
+ minmax_v3v3_v3(min, max, smax);
+ }
+ }
+
+ copy_v3_v3(obr->boundbox[0], min);
+ copy_v3_v3(obr->boundbox[1], max);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* Database */
+/* ------------------------------------------------------------------------- */
+
+static int render_object_type(short type)
+{
+ return OB_TYPE_SUPPORT_MATERIAL(type);
+}
+
+static void find_dupli_instances(Render *re, ObjectRen *obr, DupliObject *dob)
+{
+ ObjectInstanceRen *obi;
+ float imat[4][4], obmat[4][4], obimat[4][4], nmat[3][3];
+ int first = 1;
+
+ mul_m4_m4m4(obmat, re->viewmat, obr->obmat);
+ invert_m4_m4(imat, obmat);
+
+ /* for objects instanced by dupliverts/faces/particles, we go over the
+ * list of instances to find ones that instance obr, and setup their
+ * matrices and obr pointer */
+ for (obi=re->instancetable.last; obi; obi=obi->prev) {
+ if (!obi->obr && obi->ob == obr->ob && obi->psysindex == obr->psysindex) {
+ obi->obr= obr;
+
+ /* compute difference between object matrix and
+ * object matrix with dupli transform, in viewspace */
+ copy_m4_m4(obimat, obi->mat);
+ mul_m4_m4m4(obi->mat, obimat, imat);
+
+ copy_m3_m4(nmat, obi->mat);
+ invert_m3_m3(obi->nmat, nmat);
+ transpose_m3(obi->nmat);
+
+ if (dob) {
+ copy_v3_v3(obi->dupliorco, dob->orco);
+ obi->dupliuv[0]= dob->uv[0];
+ obi->dupliuv[1]= dob->uv[1];
+ }
+
+ if (!first) {
+ re->totvert += obr->totvert;
+ re->totvlak += obr->totvlak;
+ re->tothalo += obr->tothalo;
+ re->totstrand += obr->totstrand;
+ }
+ else
+ first= 0;
+ }
+ }
+}
+
+static void assign_dupligroup_dupli(Render *re, ObjectInstanceRen *obi, ObjectRen *obr, DupliObject *dob)
+{
+ float imat[4][4], obmat[4][4], obimat[4][4], nmat[3][3];
+
+ mul_m4_m4m4(obmat, re->viewmat, obr->obmat);
+ invert_m4_m4(imat, obmat);
+
+ obi->obr= obr;
+
+ /* compute difference between object matrix and
+ * object matrix with dupli transform, in viewspace */
+ copy_m4_m4(obimat, obi->mat);
+ mul_m4_m4m4(obi->mat, obimat, imat);
+
+ copy_m3_m4(nmat, obi->mat);
+ invert_m3_m3(obi->nmat, nmat);
+ transpose_m3(obi->nmat);
+
+ if (dob) {
+ copy_v3_v3(obi->dupliorco, dob->orco);
+ obi->dupliuv[0]= dob->uv[0];
+ obi->dupliuv[1]= dob->uv[1];
+ }
+
+ re->totvert += obr->totvert;
+ re->totvlak += obr->totvlak;
+ re->tothalo += obr->tothalo;
+ re->totstrand += obr->totstrand;
+}
+
+static ObjectRen *find_dupligroup_dupli(Render *re, Object *ob, int psysindex)
+{
+ ObjectRen *obr;
+
+ /* if the object is itself instanced, we don't want to create an instance
+ * for it */
+ if (ob->transflag & OB_RENDER_DUPLI)
+ return NULL;
+
+ /* try to find an object that was already created so we can reuse it
+ * and save memory */
+ for (obr=re->objecttable.first; obr; obr=obr->next)
+ if (obr->ob == ob && obr->psysindex == psysindex && (obr->flag & R_INSTANCEABLE))
+ return obr;
+
+ return NULL;
+}
+
+static void set_dupli_tex_mat(Render *re, ObjectInstanceRen *obi, DupliObject *dob, float omat[4][4])
+{
+ /* For duplis we need to have a matrix that transform the coordinate back
+ * to it's original position, without the dupli transforms. We also check
+ * the matrix is actually needed, to save memory on lots of dupliverts for
+ * example */
+ static Object *lastob= NULL;
+ static int needtexmat= 0;
+
+ /* init */
+ if (!re) {
+ lastob= NULL;
+ needtexmat= 0;
+ return;
+ }
+
+ /* check if we actually need it */
+ if (lastob != dob->ob) {
+ Material ***material;
+ short a, *totmaterial;
+
+ lastob= dob->ob;
+ needtexmat= 0;
+
+ totmaterial= give_totcolp(dob->ob);
+ material= give_matarar(dob->ob);
+
+ if (totmaterial && material)
+ for (a= 0; a<*totmaterial; a++)
+ if ((*material)[a] && (*material)[a]->texco & TEXCO_OBJECT)
+ needtexmat= 1;
+ }
+
+ if (needtexmat) {
+ float imat[4][4];
+
+ obi->duplitexmat= BLI_memarena_alloc(re->memArena, sizeof(float)*4*4);
+ invert_m4_m4(imat, dob->mat);
+ mul_m4_series(obi->duplitexmat, re->viewmat, omat, imat, re->viewinv);
+ }
+
+ copy_v3_v3(obi->dupliorco, dob->orco);
+ copy_v2_v2(obi->dupliuv, dob->uv);
+}
+
+static void init_render_object_data(Render *re, ObjectRen *obr, int timeoffset)
+{
+ Object *ob= obr->ob;
+ ParticleSystem *psys;
+ int i;
+
+ if (obr->psysindex) {
+ if ((!obr->prev || obr->prev->ob != ob || (obr->prev->flag & R_INSTANCEABLE)==0) && ob->type==OB_MESH) {
+ /* the emitter mesh wasn't rendered so the modifier stack wasn't
+ * evaluated with render settings */
+ DerivedMesh *dm;
+ const CustomDataMask mask = CD_MASK_RENDER_INTERNAL;
+
+ if (re->r.scemode & R_VIEWPORT_PREVIEW)
+ dm = mesh_create_derived_view(re->scene, ob, mask);
+ else
+ dm = mesh_create_derived_render(re->scene, ob, mask);
+ dm->release(dm);
+ }
+
+ for (psys=ob->particlesystem.first, i=0; i<obr->psysindex-1; i++)
+ psys= psys->next;
+
+ render_new_particle_system(re, obr, psys, timeoffset);
+ }
+ else {
+ if (ELEM(ob->type, OB_FONT, OB_CURVE))
+ init_render_curve(re, obr, timeoffset);
+ else if (ob->type==OB_SURF)
+ init_render_surf(re, obr, timeoffset);
+ else if (ob->type==OB_MESH)
+ init_render_mesh(re, obr, timeoffset);
+ else if (ob->type==OB_MBALL)
+ init_render_mball(re, obr);
+ }
+
+ finalize_render_object(re, obr, timeoffset);
+
+ re->totvert += obr->totvert;
+ re->totvlak += obr->totvlak;
+ re->tothalo += obr->tothalo;
+ re->totstrand += obr->totstrand;
+}
+
+static void add_render_object(Render *re, Object *ob, Object *par, DupliObject *dob, float omat[4][4], int timeoffset)
+{
+ ObjectRen *obr;
+ ObjectInstanceRen *obi;
+ ParticleSystem *psys;
+ int show_emitter, allow_render= 1, index, psysindex, i;
+
+ index= (dob)? dob->persistent_id[0]: 0;
+
+ /* It seems that we may generate psys->renderdata recursively in some nasty intricated cases of
+ * several levels of bupliobject (see T51524).
+ * For now, basic rule is, do not restore psys if it was already in 'render state'.
+ * Another, more robust solution could be to add some reference counting to that renderdata... */
+ bool psys_has_renderdata = false;
+
+ /* the emitter has to be processed first (render levels of modifiers) */
+ /* so here we only check if the emitter should be rendered */
+ if (ob->particlesystem.first) {
+ show_emitter= 0;
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ show_emitter += psys->part->draw & PART_DRAW_EMITTER;
+ if (!(re->r.scemode & R_VIEWPORT_PREVIEW)) {
+ psys_has_renderdata |= (psys->renderdata != NULL);
+ psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset);
+ }
+ }
+
+ /* if no psys has "show emitter" selected don't render emitter */
+ if (show_emitter == 0)
+ allow_render= 0;
+ }
+
+ /* one render object for the data itself */
+ if (allow_render) {
+ obr= RE_addRenderObject(re, ob, par, index, 0, ob->lay);
+ if ((dob && !dob->animated) || (ob->transflag & OB_RENDER_DUPLI)) {
+ obr->flag |= R_INSTANCEABLE;
+ copy_m4_m4(obr->obmat, ob->obmat);
+ }
+ init_render_object_data(re, obr, timeoffset);
+
+ /* only add instance for objects that have not been used for dupli */
+ if (!(ob->transflag & OB_RENDER_DUPLI)) {
+ obi = RE_addRenderInstance(re, obr, ob, par, index, 0, NULL, ob->lay, dob);
+ if (dob) set_dupli_tex_mat(re, obi, dob, omat);
+ }
+ else
+ find_dupli_instances(re, obr, dob);
+
+ for (i=1; i<=ob->totcol; i++) {
+ Material* ma = give_render_material(re, ob, i);
+ if (ma && ma->material_type == MA_TYPE_VOLUME)
+ add_volume(re, obr, ma);
+ }
+ }
+
+ /* and one render object per particle system */
+ if (ob->particlesystem.first) {
+ psysindex= 1;
+ for (psys=ob->particlesystem.first; psys; psys=psys->next, psysindex++) {
+ if (!psys_check_enabled(ob, psys, G.is_rendering))
+ continue;
+
+ obr= RE_addRenderObject(re, ob, par, index, psysindex, ob->lay);
+ if ((dob && !dob->animated) || (ob->transflag & OB_RENDER_DUPLI)) {
+ obr->flag |= R_INSTANCEABLE;
+ copy_m4_m4(obr->obmat, ob->obmat);
+ }
+ if (dob)
+ psys->flag |= PSYS_USE_IMAT;
+ init_render_object_data(re, obr, timeoffset);
+ if (!(re->r.scemode & R_VIEWPORT_PREVIEW) && !psys_has_renderdata) {
+ psys_render_restore(ob, psys);
+ }
+ psys->flag &= ~PSYS_USE_IMAT;
+
+ /* only add instance for objects that have not been used for dupli */
+ if (!(ob->transflag & OB_RENDER_DUPLI)) {
+ obi = RE_addRenderInstance(re, obr, ob, par, index, psysindex, NULL, ob->lay, dob);
+ if (dob) set_dupli_tex_mat(re, obi, dob, omat);
+ }
+ else
+ find_dupli_instances(re, obr, dob);
+ }
+ }
+}
+
+/* par = pointer to duplicator parent, needed for object lookup table */
+/* index = when duplicater copies same object (particle), the counter */
+static void init_render_object(Render *re, Object *ob, Object *par, DupliObject *dob, float omat[4][4], int timeoffset)
+{
+ static double lasttime= 0.0;
+ double time;
+ float mat[4][4];
+
+ if (ob->type==OB_LAMP)
+ add_render_lamp(re, ob);
+ else if (render_object_type(ob->type))
+ add_render_object(re, ob, par, dob, omat, timeoffset);
+ else {
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat);
+ }
+
+ time= PIL_check_seconds_timer();
+ if (time - lasttime > 1.0) {
+ lasttime= time;
+ /* clumsy copying still */
+ re->i.totvert= re->totvert;
+ re->i.totface= re->totvlak;
+ re->i.totstrand= re->totstrand;
+ re->i.tothalo= re->tothalo;
+ re->i.totlamp= re->totlamp;
+ re->stats_draw(re->sdh, &re->i);
+ }
+
+ ob->flag |= OB_DONE;
+}
+
+void RE_Database_Free(Render *re)
+{
+ LampRen *lar;
+
+ /* will crash if we try to free empty database */
+ if (!re->i.convertdone)
+ return;
+
+ /* statistics for debugging render memory usage */
+ if ((G.debug & G_DEBUG) && (G.is_rendering)) {
+ if ((re->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) {
+ BKE_image_print_memlist();
+ MEM_printmemlist_stats();
+ }
+ }
+
+ /* FREE */
+
+ for (lar= re->lampren.first; lar; lar= lar->next) {
+ freeshadowbuf(lar);
+ if (lar->jitter) MEM_freeN(lar->jitter);
+ if (lar->shadsamp) MEM_freeN(lar->shadsamp);
+ if (lar->sunsky) MEM_freeN(lar->sunsky);
+ curvemapping_free(lar->curfalloff);
+ }
+
+ free_volume_precache(re);
+
+ BLI_freelistN(&re->lampren);
+ BLI_freelistN(&re->lights);
+
+ free_renderdata_tables(re);
+
+ /* free orco */
+ free_mesh_orco_hash(re);
+
+ if (re->main) {
+ end_render_materials(re->main);
+ end_render_textures(re);
+ free_pointdensities(re);
+ }
+
+ free_camera_inside_volumes(re);
+
+ if (re->wrld.aosphere) {
+ MEM_freeN(re->wrld.aosphere);
+ re->wrld.aosphere= NULL;
+ if (re->scene && re->scene->world)
+ re->scene->world->aosphere= NULL;
+ }
+ if (re->wrld.aotables) {
+ MEM_freeN(re->wrld.aotables);
+ re->wrld.aotables= NULL;
+ if (re->scene && re->scene->world)
+ re->scene->world->aotables= NULL;
+ }
+ if (re->r.mode & R_RAYTRACE)
+ free_render_qmcsampler(re);
+
+ if (re->r.mode & R_RAYTRACE) freeraytree(re);
+
+ free_sss(re);
+ free_occ(re);
+ free_strand_surface(re);
+
+ re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
+ re->i.convertdone = false;
+
+ re->bakebuf= NULL;
+
+ if (re->scene)
+ if (re->scene->r.scemode & R_FREE_IMAGE)
+ if ((re->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0)
+ BKE_image_free_all_textures();
+
+ if (re->memArena) {
+ BLI_memarena_free(re->memArena);
+ re->memArena = NULL;
+ }
+}
+
+static int allow_render_object(Render *re, Object *ob, int nolamps, int onlyselected, Object *actob)
+{
+ if (is_object_hidden(re, ob))
+ return 0;
+
+ /* Only handle dupli-hiding here if there is no particle systems. Else, let those handle show/noshow. */
+ if (!ob->particlesystem.first) {
+ if ((ob->transflag & OB_DUPLI) && !(ob->transflag & OB_DUPLIFRAMES)) {
+ return 0;
+ }
+ }
+
+ /* don't add non-basic meta objects, ends up having renderobjects with no geometry */
+ if (ob->type == OB_MBALL && ob!=BKE_mball_basis_find(re->eval_ctx, re->scene, ob))
+ return 0;
+
+ if (nolamps && (ob->type==OB_LAMP))
+ return 0;
+
+ if (onlyselected && (ob!=actob && !(ob->flag & SELECT)))
+ return 0;
+
+ return 1;
+}
+
+static int allow_render_dupli_instance(Render *UNUSED(re), DupliObject *dob, Object *obd)
+{
+ ParticleSystem *psys;
+ Material *ma;
+ short a, *totmaterial;
+
+ /* don't allow objects with halos. we need to have
+ * all halo's to sort them globally in advance */
+ totmaterial= give_totcolp(obd);
+
+ if (totmaterial) {
+ for (a= 0; a<*totmaterial; a++) {
+ ma= give_current_material(obd, a + 1);
+ if (ma && (ma->material_type == MA_TYPE_HALO))
+ return 0;
+ }
+ }
+
+ for (psys=obd->particlesystem.first; psys; psys=psys->next)
+ if (!ELEM(psys->part->ren_as, PART_DRAW_BB, PART_DRAW_LINE, PART_DRAW_PATH, PART_DRAW_OB, PART_DRAW_GR))
+ return 0;
+
+ /* don't allow lamp, animated duplis, or radio render */
+ return (render_object_type(obd->type) &&
+ (!(dob->type == OB_DUPLIGROUP) || !dob->animated));
+}
+
+static void dupli_render_particle_set(Render *re, Object *ob, int timeoffset, int level, int enable)
+{
+ /* ugly function, but we need to set particle systems to their render
+ * settings before calling object_duplilist, to get render level duplis */
+ Group *group;
+ GroupObject *go;
+ ParticleSystem *psys;
+ DerivedMesh *dm;
+
+ if (re->r.scemode & R_VIEWPORT_PREVIEW)
+ return;
+
+ if (level >= MAX_DUPLI_RECUR)
+ return;
+
+ if (ob->transflag & OB_DUPLIPARTS) {
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ if (ELEM(psys->part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
+ if (enable)
+ psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset);
+ else
+ psys_render_restore(ob, psys);
+ }
+ }
+
+ if (enable) {
+ /* this is to make sure we get render level duplis in groups:
+ * the derivedmesh must be created before init_render_mesh,
+ * since object_duplilist does dupliparticles before that */
+ dm = mesh_create_derived_render(re->scene, ob, CD_MASK_RENDER_INTERNAL);
+ dm->release(dm);
+
+ for (psys=ob->particlesystem.first; psys; psys=psys->next)
+ psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated;
+ }
+ }
+
+ if (ob->dup_group==NULL) return;
+ group= ob->dup_group;
+
+ for (go= group->gobject.first; go; go= go->next)
+ dupli_render_particle_set(re, go->ob, timeoffset, level+1, enable);
+}
+
+static int get_vector_renderlayers(Scene *sce)
+{
+ SceneRenderLayer *srl;
+ unsigned int lay= 0;
+
+ for (srl= sce->r.layers.first; srl; srl= srl->next)
+ if (srl->passflag & SCE_PASS_VECTOR)
+ lay |= srl->lay;
+
+ return lay;
+}
+
+static void add_group_render_dupli_obs(Render *re, Group *group, int nolamps, int onlyselected, Object *actob, int timeoffset, int level)
+{
+ GroupObject *go;
+ Object *ob;
+
+ /* simple preventing of too deep nested groups */
+ if (level>MAX_DUPLI_RECUR) return;
+
+ /* recursively go into dupligroups to find objects with OB_RENDER_DUPLI
+ * that were not created yet */
+ for (go= group->gobject.first; go; go= go->next) {
+ ob= go->ob;
+
+ if (ob->flag & OB_DONE) {
+ if (ob->transflag & OB_RENDER_DUPLI) {
+ if (allow_render_object(re, ob, nolamps, onlyselected, actob)) {
+ init_render_object(re, ob, NULL, NULL, NULL, timeoffset);
+ ob->transflag &= ~OB_RENDER_DUPLI;
+
+ if (ob->dup_group)
+ add_group_render_dupli_obs(re, ob->dup_group, nolamps, onlyselected, actob, timeoffset, level+1);
+ }
+ }
+ }
+ }
+}
+
+static void database_init_objects(Render *re, unsigned int renderlay, int nolamps, int onlyselected, Object *actob, int timeoffset)
+{
+ Base *base;
+ Object *ob;
+ Group *group;
+ ObjectInstanceRen *obi;
+ Scene *sce_iter;
+ int lay, vectorlay;
+
+ /* for duplis we need the Object texture mapping to work as if
+ * untransformed, set_dupli_tex_mat sets the matrix to allow that
+ * NULL is just for init */
+ set_dupli_tex_mat(NULL, NULL, NULL, NULL);
+
+ /* loop over all objects rather then using SETLOOPER because we may
+ * reference an mtex-mapped object which isn't rendered or is an
+ * empty in a dupli group. We could scan all render material/lamp/world
+ * mtex's for mapto objects but its easier just to set the
+ * 'imat' / 'imat_ren' on all and unlikely to be a performance hit
+ * See bug: [#28744] - campbell */
+ for (ob= re->main->object.first; ob; ob= ob->id.next) {
+ float mat[4][4];
+
+ /* imat objects has to be done here, since displace can have texture using Object map-input */
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat_ren, mat);
+ copy_m4_m4(ob->imat, ob->imat_ren);
+ /* each object should only be rendered once */
+ ob->flag &= ~OB_DONE;
+ ob->transflag &= ~OB_RENDER_DUPLI;
+ }
+
+ for (SETLOOPER(re->scene, sce_iter, base)) {
+ ob= base->object;
+
+ /* in the prev/next pass for making speed vectors, avoid creating
+ * objects that are not on a renderlayer with a vector pass, can
+ * save a lot of time in complex scenes */
+ vectorlay= get_vector_renderlayers(re->scene);
+ lay= (timeoffset)? renderlay & vectorlay: renderlay;
+
+ /* if the object has been restricted from rendering in the outliner, ignore it */
+ if (is_object_restricted(re, ob)) continue;
+
+ /* OB_DONE means the object itself got duplicated, so was already converted */
+ if (ob->flag & OB_DONE) {
+ /* OB_RENDER_DUPLI means instances for it were already created, now
+ * it still needs to create the ObjectRen containing the data */
+ if (ob->transflag & OB_RENDER_DUPLI) {
+ if (allow_render_object(re, ob, nolamps, onlyselected, actob)) {
+ init_render_object(re, ob, NULL, NULL, NULL, timeoffset);
+ ob->transflag &= ~OB_RENDER_DUPLI;
+ }
+ }
+ }
+ else if ((base->lay & lay) || (ob->type==OB_LAMP && (base->lay & re->lay)) ) {
+ if ((ob->transflag & OB_DUPLI) && (ob->type!=OB_MBALL)) {
+ DupliObject *dob;
+ ListBase *duplilist;
+ DupliApplyData *duplilist_apply_data = NULL;
+ int i;
+
+ /* create list of duplis generated by this object, particle
+ * system need to have render settings set for dupli particles */
+ dupli_render_particle_set(re, ob, timeoffset, 0, 1);
+ duplilist = object_duplilist(re->eval_ctx, re->scene, ob);
+ duplilist_apply_data = duplilist_apply(ob, NULL, duplilist);
+ /* postpone 'dupli_render_particle_set', since RE_addRenderInstance reads
+ * index values from 'dob->persistent_id[0]', referencing 'psys->child' which
+ * may be smaller once the particle system is restored, see: T45563. */
+
+ for (dob= duplilist->first, i = 0; dob; dob= dob->next, ++i) {
+ DupliExtraData *dob_extra = &duplilist_apply_data->extra[i];
+ Object *obd= dob->ob;
+
+ copy_m4_m4(obd->obmat, dob->mat);
+
+ /* group duplis need to set ob matrices correct, for deform. so no_draw is part handled */
+ if (!(obd->transflag & OB_RENDER_DUPLI) && dob->no_draw)
+ continue;
+
+ if (is_object_hidden(re, obd))
+ continue;
+
+ if (obd->type==OB_MBALL)
+ continue;
+
+ if (!allow_render_object(re, obd, nolamps, onlyselected, actob))
+ continue;
+
+ if (allow_render_dupli_instance(re, dob, obd)) {
+ ParticleSystem *psys;
+ ObjectRen *obr = NULL;
+ int psysindex;
+ float mat[4][4];
+
+ obi=NULL;
+
+ /* instances instead of the actual object are added in two cases, either
+ * this is a duplivert/face/particle, or it is a non-animated object in
+ * a dupligroup that has already been created before */
+ if (dob->type != OB_DUPLIGROUP || (obr=find_dupligroup_dupli(re, obd, 0))) {
+ mul_m4_m4m4(mat, re->viewmat, dob->mat);
+ /* ob = particle system, use that layer */
+ obi = RE_addRenderInstance(re, NULL, obd, ob, dob->persistent_id[0], 0, mat, ob->lay, dob);
+
+ /* fill in instance variables for texturing */
+ set_dupli_tex_mat(re, obi, dob, dob_extra->obmat);
+ if (dob->type != OB_DUPLIGROUP) {
+ copy_v3_v3(obi->dupliorco, dob->orco);
+ obi->dupliuv[0]= dob->uv[0];
+ obi->dupliuv[1]= dob->uv[1];
+ }
+ else {
+ /* for the second case, setup instance to point to the already
+ * created object, and possibly setup instances if this object
+ * itself was duplicated. for the first case find_dupli_instances
+ * will be called later. */
+ assign_dupligroup_dupli(re, obi, obr, dob);
+ if (obd->transflag & OB_RENDER_DUPLI)
+ find_dupli_instances(re, obr, dob);
+ }
+ }
+
+ /* same logic for particles, each particle system has it's own object, so
+ * need to go over them separately */
+ psysindex= 1;
+ for (psys=obd->particlesystem.first; psys; psys=psys->next) {
+ if (dob->type != OB_DUPLIGROUP || (obr=find_dupligroup_dupli(re, obd, psysindex))) {
+ if (obi == NULL)
+ mul_m4_m4m4(mat, re->viewmat, dob->mat);
+ obi = RE_addRenderInstance(re, NULL, obd, ob, dob->persistent_id[0], psysindex++, mat, obd->lay, dob);
+
+ set_dupli_tex_mat(re, obi, dob, dob_extra->obmat);
+ if (dob->type != OB_DUPLIGROUP) {
+ copy_v3_v3(obi->dupliorco, dob->orco);
+ obi->dupliuv[0]= dob->uv[0];
+ obi->dupliuv[1]= dob->uv[1];
+ }
+ else {
+ assign_dupligroup_dupli(re, obi, obr, dob);
+ if (obd->transflag & OB_RENDER_DUPLI)
+ find_dupli_instances(re, obr, dob);
+ }
+ }
+ }
+
+ if (obi==NULL)
+ /* can't instance, just create the object */
+ init_render_object(re, obd, ob, dob, dob_extra->obmat, timeoffset);
+
+ if (dob->type != OB_DUPLIGROUP) {
+ obd->flag |= OB_DONE;
+ obd->transflag |= OB_RENDER_DUPLI;
+ }
+ }
+ else
+ init_render_object(re, obd, ob, dob, dob_extra->obmat, timeoffset);
+
+ if (re->test_break(re->tbh)) break;
+ }
+
+ /* restore particle system */
+ dupli_render_particle_set(re, ob, timeoffset, 0, false);
+
+ if (duplilist_apply_data) {
+ duplilist_restore(duplilist, duplilist_apply_data);
+ duplilist_free_apply_data(duplilist_apply_data);
+ }
+ free_object_duplilist(duplilist);
+
+ if (allow_render_object(re, ob, nolamps, onlyselected, actob))
+ init_render_object(re, ob, NULL, NULL, NULL, timeoffset);
+ }
+ else if (allow_render_object(re, ob, nolamps, onlyselected, actob))
+ init_render_object(re, ob, NULL, NULL, NULL, timeoffset);
+ }
+
+ if (re->test_break(re->tbh)) break;
+ }
+
+ /* objects in groups with OB_RENDER_DUPLI set still need to be created,
+ * since they may not be part of the scene */
+ for (group= re->main->group.first; group; group=group->id.next)
+ add_group_render_dupli_obs(re, group, nolamps, onlyselected, actob, timeoffset, 0);
+
+ if (!re->test_break(re->tbh))
+ RE_makeRenderInstances(re);
+}
+
+/* used to be 'rotate scene' */
+void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int lay, int use_camera_view)
+{
+ Scene *sce;
+ Object *camera;
+ float mat[4][4];
+ float amb[3];
+
+ re->main= bmain;
+ re->scene= scene;
+ re->lay= lay;
+
+ if (re->r.scemode & R_VIEWPORT_PREVIEW)
+ re->scene_color_manage = BKE_scene_check_color_management_enabled(scene);
+
+ /* scene needs to be set to get camera */
+ camera= RE_GetCamera(re);
+
+ /* per second, per object, stats print this */
+ re->i.infostr= "Preparing Scene data";
+ re->i.cfra= scene->r.cfra;
+ BLI_strncpy(re->i.scene_name, scene->id.name + 2, sizeof(re->i.scene_name));
+
+ /* XXX add test if dbase was filled already? */
+
+ re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "render db arena");
+ re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
+ re->lights.first= re->lights.last= NULL;
+ re->lampren.first= re->lampren.last= NULL;
+
+ re->i.partsdone = false; /* signal now in use for previewrender */
+
+ /* in localview, lamps are using normal layers, objects only local bits */
+ if (re->lay & 0xFF000000)
+ lay &= 0xFF000000;
+
+ /* applies changes fully */
+ if ((re->r.scemode & (R_NO_FRAME_UPDATE|R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) {
+ BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, lay);
+ render_update_anim_renderdata(re, &re->scene->r);
+ }
+
+ /* if no camera, viewmat should have been set! */
+ if (use_camera_view && camera) {
+ /* called before but need to call again in case of lens animation from the
+ * above call to BKE_scene_update_for_newframe, fixes bug. [#22702].
+ * following calls don't depend on 'RE_SetCamera' */
+ RE_SetCamera(re, camera);
+ RE_GetCameraModelMatrix(re, camera, mat);
+ invert_m4(mat);
+ RE_SetView(re, mat);
+
+ /* force correct matrix for scaled cameras */
+ DAG_id_tag_update_ex(re->main, &camera->id, OB_RECALC_OB);
+ }
+
+ /* store for incremental render, viewmat rotates dbase */
+ copy_m4_m4(re->viewmat_orig, re->viewmat);
+
+ init_render_world(re); /* do first, because of ambient. also requires re->osa set correct */
+ if (re->r.mode & R_RAYTRACE) {
+ init_render_qmcsampler(re);
+
+ if (re->wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT))
+ if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT)
+ init_ao_sphere(re, &re->wrld);
+ }
+
+ /* still bad... doing all */
+ init_render_textures(re);
+ copy_v3_v3(amb, &re->wrld.ambr);
+ init_render_materials(re->main, re->r.mode, amb, (re->r.scemode & R_BUTS_PREVIEW) == 0);
+ set_node_shader_lamp_loop(shade_material_loop);
+
+ /* MAKE RENDER DATA */
+ database_init_objects(re, lay, 0, 0, NULL, 0);
+
+ if (!re->test_break(re->tbh)) {
+ set_material_lightgroups(re);
+ for (sce= re->scene; sce; sce= sce->set)
+ set_renderlayer_lightgroups(re, sce);
+
+ /* for now some clumsy copying still */
+ re->i.totvert= re->totvert;
+ re->i.totface= re->totvlak;
+ re->i.totstrand= re->totstrand;
+ re->i.tothalo= re->tothalo;
+ re->i.totlamp= re->totlamp;
+ re->stats_draw(re->sdh, &re->i);
+ }
+}
+
+void RE_Database_Preprocess(Render *re)
+{
+ if (!re->test_break(re->tbh)) {
+ int tothalo;
+
+ tothalo= re->tothalo;
+ sort_halos(re, tothalo);
+
+ init_camera_inside_volumes(re);
+
+ re->i.infostr = IFACE_("Creating Shadowbuffers");
+ re->stats_draw(re->sdh, &re->i);
+
+ /* SHADOW BUFFER */
+ threaded_makeshadowbufs(re);
+
+ /* old code checked for internal render (aka not yafray) */
+ {
+ /* raytree */
+ if (!re->test_break(re->tbh)) {
+ if (re->r.mode & R_RAYTRACE) {
+ makeraytree(re);
+ }
+ }
+ /* ENVIRONMENT MAPS */
+ if (!re->test_break(re->tbh))
+ make_envmaps(re);
+
+ /* point density texture */
+ if (!re->test_break(re->tbh))
+ make_pointdensities(re);
+ /* voxel data texture */
+ if (!re->test_break(re->tbh))
+ make_voxeldata(re);
+ }
+
+ if (!re->test_break(re->tbh))
+ project_renderdata(re, projectverto, (re->r.mode & R_PANORAMA) != 0, 0, 1);
+
+ /* Occlusion */
+ if ((re->wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT)) && !re->test_break(re->tbh))
+ if (re->wrld.ao_gather_method == WO_AOGATHER_APPROX)
+ if (re->r.mode & R_SHADOW)
+ make_occ_tree(re);
+
+ /* SSS */
+ if ((re->r.mode & R_SSS) && !re->test_break(re->tbh))
+ make_sss_tree(re);
+
+ if (!re->test_break(re->tbh))
+ if (re->r.mode & R_RAYTRACE)
+ volume_precache(re);
+ }
+
+ re->i.convertdone = true;
+
+ if (re->test_break(re->tbh))
+ RE_Database_Free(re);
+
+ re->i.infostr = NULL;
+ re->stats_draw(re->sdh, &re->i);
+}
+
+/* exported call to recalculate hoco for vertices, when winmat changed */
+void RE_DataBase_ApplyWindow(Render *re)
+{
+ project_renderdata(re, projectverto, 0, 0, 0);
+}
+
+/* exported call to rotate render data again, when viewmat changed */
+void RE_DataBase_IncrementalView(Render *re, float viewmat[4][4], int restore)
+{
+ float oldviewinv[4][4], tmat[4][4];
+
+ invert_m4_m4(oldviewinv, re->viewmat_orig);
+
+ /* we have to correct for the already rotated vertexcoords */
+ mul_m4_m4m4(tmat, viewmat, oldviewinv);
+
+ copy_m4_m4(re->viewmat, viewmat);
+ invert_m4_m4(re->viewinv, re->viewmat);
+
+ init_camera_inside_volumes(re);
+
+ env_rotate_scene(re, tmat, !restore);
+
+ /* SSS points distribution depends on view */
+ if ((re->r.mode & R_SSS) && !re->test_break(re->tbh))
+ make_sss_tree(re);
+}
+
+
+void RE_DataBase_GetView(Render *re, float mat[4][4])
+{
+ copy_m4_m4(mat, re->viewmat);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Speed Vectors */
+/* ------------------------------------------------------------------------- */
+
+static void database_fromscene_vectors(Render *re, Scene *scene, unsigned int lay, int timeoffset)
+{
+ Object *camera= RE_GetCamera(re);
+ float mat[4][4];
+
+ re->scene= scene;
+ re->lay= lay;
+
+ /* XXX add test if dbase was filled already? */
+
+ re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "vector render db arena");
+ re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
+ re->i.totface=re->i.totvert=re->i.totstrand=re->i.totlamp=re->i.tothalo= 0;
+ re->lights.first= re->lights.last= NULL;
+
+ /* in localview, lamps are using normal layers, objects only local bits */
+ if (re->lay & 0xFF000000)
+ lay &= 0xFF000000;
+
+ /* applies changes fully */
+ scene->r.cfra += timeoffset;
+ BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, lay);
+
+ /* if no camera, viewmat should have been set! */
+ if (camera) {
+ RE_GetCameraModelMatrix(re, camera, mat);
+ normalize_m4(mat);
+ invert_m4(mat);
+ RE_SetView(re, mat);
+ }
+
+ /* MAKE RENDER DATA */
+ database_init_objects(re, lay, 0, 0, NULL, timeoffset);
+
+ if (!re->test_break(re->tbh))
+ project_renderdata(re, projectverto, (re->r.mode & R_PANORAMA) != 0, 0, 1);
+
+ /* do this in end, particles for example need cfra */
+ scene->r.cfra -= timeoffset;
+}
+
+/* choose to use static, to prevent giving too many args to this call */
+static void speedvector_project(Render *re, float zco[2], const float co[3], const float ho[4])
+{
+ static float pixelphix=0.0f, pixelphiy=0.0f, zmulx=0.0f, zmuly=0.0f;
+ static int pano= 0;
+ float div;
+
+ /* initialize */
+ if (re) {
+ pano= re->r.mode & R_PANORAMA;
+
+ /* precalculate amount of radians 1 pixel rotates */
+ if (pano) {
+ /* size of 1 pixel mapped to viewplane coords */
+ float psize;
+
+ psize = BLI_rctf_size_x(&re->viewplane) / (float)re->winx;
+ /* x angle of a pixel */
+ pixelphix = atan(psize / re->clipsta);
+
+ psize = BLI_rctf_size_y(&re->viewplane) / (float)re->winy;
+ /* y angle of a pixel */
+ pixelphiy = atan(psize / re->clipsta);
+ }
+ zmulx= re->winx/2;
+ zmuly= re->winy/2;
+
+ return;
+ }
+
+ /* now map hocos to screenspace, uses very primitive clip still */
+ if (ho[3]<0.1f) div= 10.0f;
+ else div= 1.0f/ho[3];
+
+ /* use cylinder projection */
+ if (pano) {
+ float vec[3], ang;
+ /* angle between (0, 0, -1) and (co) */
+ copy_v3_v3(vec, co);
+
+ ang= saacos(-vec[2]/sqrtf(vec[0]*vec[0] + vec[2]*vec[2]));
+ if (vec[0]<0.0f) ang= -ang;
+ zco[0]= ang/pixelphix + zmulx;
+
+ ang= 0.5f*(float)M_PI - saacos(vec[1] / len_v3(vec));
+ zco[1]= ang/pixelphiy + zmuly;
+
+ }
+ else {
+ zco[0]= zmulx*(1.0f+ho[0]*div);
+ zco[1]= zmuly*(1.0f+ho[1]*div);
+ }
+}
+
+static void calculate_speedvector(const float vectors[2], int step, float winsq, float winroot, const float co[3], const float ho[4], float speed[4])
+{
+ float zco[2], len;
+
+ speedvector_project(NULL, zco, co, ho);
+
+ zco[0]= vectors[0] - zco[0];
+ zco[1]= vectors[1] - zco[1];
+
+ /* enable nice masks for hardly moving stuff or float inaccuracy */
+ if (zco[0]<0.1f && zco[0]>-0.1f && zco[1]<0.1f && zco[1]>-0.1f ) {
+ zco[0]= 0.0f;
+ zco[1]= 0.0f;
+ }
+
+ /* maximize speed for image width, otherwise it never looks good */
+ len= zco[0]*zco[0] + zco[1]*zco[1];
+ if (len > winsq) {
+ len= winroot/sqrtf(len);
+ zco[0]*= len;
+ zco[1]*= len;
+ }
+
+ /* note; in main vecblur loop speedvec is negated again */
+ if (step) {
+ speed[2]= -zco[0];
+ speed[3]= -zco[1];
+ }
+ else {
+ speed[0]= zco[0];
+ speed[1]= zco[1];
+ }
+}
+
+static float *calculate_strandsurface_speedvectors(Render *re, ObjectInstanceRen *obi, StrandSurface *mesh)
+{
+ if (mesh->co && mesh->prevco && mesh->nextco) {
+ float winsq= (float)re->winx*(float)re->winy; /* int's can wrap on large images */
+ float winroot= sqrtf(winsq);
+ float (*winspeed)[4];
+ float ho[4], prevho[4], nextho[4], winmat[4][4], vec[2];
+ int a;
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(winmat, re->winmat, obi->mat);
+ else
+ copy_m4_m4(winmat, re->winmat);
+
+ winspeed= MEM_callocN(sizeof(float)*4*mesh->totvert, "StrandSurfWin");
+
+ for (a=0; a<mesh->totvert; a++) {
+ projectvert(mesh->co[a], winmat, ho);
+
+ projectvert(mesh->prevco[a], winmat, prevho);
+ speedvector_project(NULL, vec, mesh->prevco[a], prevho);
+ calculate_speedvector(vec, 0, winsq, winroot, mesh->co[a], ho, winspeed[a]);
+
+ projectvert(mesh->nextco[a], winmat, nextho);
+ speedvector_project(NULL, vec, mesh->nextco[a], nextho);
+ calculate_speedvector(vec, 1, winsq, winroot, mesh->co[a], ho, winspeed[a]);
+ }
+
+ return (float *)winspeed;
+ }
+
+ return NULL;
+}
+
+static void calculate_speedvectors(Render *re, ObjectInstanceRen *obi, float *vectors, int step)
+{
+ ObjectRen *obr= obi->obr;
+ VertRen *ver= NULL;
+ StrandRen *strand= NULL;
+ StrandBuffer *strandbuf;
+ StrandSurface *mesh= NULL;
+ float *speed, (*winspeed)[4]=NULL, ho[4], winmat[4][4];
+ float *co1, *co2, *co3, *co4, w[4];
+ float winsq = (float)re->winx * (float)re->winy, winroot = sqrtf(winsq); /* int's can wrap on large images */
+ int a, *face, *index;
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(winmat, re->winmat, obi->mat);
+ else
+ copy_m4_m4(winmat, re->winmat);
+
+ if (obr->vertnodes) {
+ for (a=0; a<obr->totvert; a++, vectors+=2) {
+ if ((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
+ else ver++;
+
+ speed= RE_vertren_get_winspeed(obi, ver, 1);
+ projectvert(ver->co, winmat, ho);
+ calculate_speedvector(vectors, step, winsq, winroot, ver->co, ho, speed);
+ }
+ }
+
+ if (obr->strandnodes) {
+ strandbuf= obr->strandbuf;
+ mesh= (strandbuf)? strandbuf->surface: NULL;
+
+ /* compute speed vectors at surface vertices */
+ if (mesh)
+ winspeed= (float(*)[4])calculate_strandsurface_speedvectors(re, obi, mesh);
+
+ if (winspeed) {
+ for (a=0; a<obr->totstrand; a++, vectors+=2) {
+ if ((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
+ else strand++;
+
+ index= RE_strandren_get_face(obr, strand, 0);
+ if (index && *index < mesh->totface) {
+ speed= RE_strandren_get_winspeed(obi, strand, 1);
+
+ /* interpolate speed vectors from strand surface */
+ face= mesh->face[*index];
+
+ co1 = mesh->co[face[0]];
+ co2 = mesh->co[face[1]];
+ co3 = mesh->co[face[2]];
+
+ if (face[3]) {
+ co4 = mesh->co[face[3]];
+ interp_weights_quad_v3(w, co1, co2, co3, co4, strand->vert->co);
+ }
+ else {
+ interp_weights_tri_v3(w, co1, co2, co3, strand->vert->co);
+ }
+
+ zero_v4(speed);
+ madd_v4_v4fl(speed, winspeed[face[0]], w[0]);
+ madd_v4_v4fl(speed, winspeed[face[1]], w[1]);
+ madd_v4_v4fl(speed, winspeed[face[2]], w[2]);
+ if (face[3])
+ madd_v4_v4fl(speed, winspeed[face[3]], w[3]);
+ }
+ }
+
+ MEM_freeN(winspeed);
+ }
+ }
+}
+
+static int load_fluidsimspeedvectors(Render *re, ObjectInstanceRen *obi, float *vectors, int step)
+{
+ ObjectRen *obr= obi->obr;
+ Object *fsob= obr->ob;
+ VertRen *ver= NULL;
+ float *speed, div, zco[2], avgvel[4] = {0.0, 0.0, 0.0, 0.0};
+ float zmulx= re->winx/2, zmuly= re->winy/2, len;
+ float winsq = (float)re->winx * (float)re->winy, winroot= sqrtf(winsq); /* int's can wrap on large images */
+ int a, j;
+ float hoco[4], ho[4], fsvec[4], camco[4];
+ float mat[4][4], winmat[4][4];
+ float imat[4][4];
+ FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(fsob, eModifierType_Fluidsim);
+ FluidsimSettings *fss;
+ FluidVertexVelocity *velarray = NULL;
+
+ /* only one step needed */
+ if (step) return 1;
+
+ if (fluidmd)
+ fss = fluidmd->fss;
+ else
+ return 0;
+
+ copy_m4_m4(mat, re->viewmat);
+ invert_m4_m4(imat, mat);
+
+ /* set first vertex OK */
+ if (!fss->meshVelocities) return 0;
+
+ if ( obr->totvert != fss->totvert) {
+ //fprintf(stderr, "load_fluidsimspeedvectors - modified fluidsim mesh, not using speed vectors (%d,%d)...\n", obr->totvert, fsob->fluidsimSettings->meshSurface->totvert); // DEBUG
+ return 0;
+ }
+
+ velarray = fss->meshVelocities;
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(winmat, re->winmat, obi->mat);
+ else
+ copy_m4_m4(winmat, re->winmat);
+
+ /* (bad) HACK calculate average velocity */
+ /* better solution would be fixing getVelocityAt() in intern/elbeem/intern/solver_util.cpp
+ * so that also small drops/little water volumes return a velocity != 0.
+ * But I had no luck in fixing that function - DG */
+ for (a=0; a<obr->totvert; a++) {
+ for (j=0;j<3;j++) avgvel[j] += velarray[a].vel[j];
+
+ }
+ for (j=0;j<3;j++) avgvel[j] /= (float)(obr->totvert);
+
+
+ for (a=0; a<obr->totvert; a++, vectors+=2) {
+ if ((a & 255)==0)
+ ver= obr->vertnodes[a>>8].vert;
+ else
+ ver++;
+
+ /* get fluid velocity */
+ fsvec[3] = 0.0f;
+ //fsvec[0] = fsvec[1] = fsvec[2] = fsvec[3] = 0.0; fsvec[2] = 2.0f; // NT fixed test
+ for (j=0;j<3;j++) fsvec[j] = velarray[a].vel[j];
+
+ /* (bad) HACK insert average velocity if none is there (see previous comment) */
+ if ((fsvec[0] == 0.0f) && (fsvec[1] == 0.0f) && (fsvec[2] == 0.0f)) {
+ fsvec[0] = avgvel[0];
+ fsvec[1] = avgvel[1];
+ fsvec[2] = avgvel[2];
+ }
+
+ /* transform (=rotate) to cam space */
+ camco[0] = dot_v3v3(imat[0], fsvec);
+ camco[1] = dot_v3v3(imat[1], fsvec);
+ camco[2] = dot_v3v3(imat[2], fsvec);
+
+ /* get homogeneous coordinates */
+ projectvert(camco, winmat, hoco);
+ projectvert(ver->co, winmat, ho);
+
+ /* now map hocos to screenspace, uses very primitive clip still */
+ /* use ho[3] of original vertex, xy component of vel. direction */
+ if (ho[3]<0.1f) div= 10.0f;
+ else div= 1.0f/ho[3];
+ zco[0]= zmulx*hoco[0]*div;
+ zco[1]= zmuly*hoco[1]*div;
+
+ /* maximize speed as usual */
+ len= zco[0]*zco[0] + zco[1]*zco[1];
+ if (len > winsq) {
+ len= winroot/sqrtf(len);
+ zco[0]*= len; zco[1]*= len;
+ }
+
+ speed= RE_vertren_get_winspeed(obi, ver, 1);
+ /* set both to the same value */
+ speed[0]= speed[2]= zco[0];
+ speed[1]= speed[3]= zco[1];
+ //if (a < 20) fprintf(stderr,"speed %d %f,%f | camco %f,%f,%f | hoco %f,%f,%f,%f\n", a, speed[0], speed[1], camco[0],camco[1], camco[2], hoco[0],hoco[1], hoco[2],hoco[3]); // NT DEBUG
+ }
+
+ return 1;
+}
+
+/* makes copy per object of all vectors */
+/* result should be that we can free entire database */
+static void copy_dbase_object_vectors(Render *re, ListBase *lb)
+{
+ ObjectInstanceRen *obi, *obilb;
+ ObjectRen *obr;
+ VertRen *ver= NULL;
+ float *vec, ho[4], winmat[4][4];
+ int a, totvector;
+
+ for (obi= re->instancetable.first; obi; obi= obi->next) {
+ obr= obi->obr;
+
+ obilb= MEM_mallocN(sizeof(ObjectInstanceRen), "ObInstanceVector");
+ memcpy(obilb, obi, sizeof(ObjectInstanceRen));
+ BLI_addtail(lb, obilb);
+
+ obilb->totvector= totvector= obr->totvert;
+
+ if (totvector > 0) {
+ vec= obilb->vectors= MEM_mallocN(2*sizeof(float)*totvector, "vector array");
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(winmat, re->winmat, obi->mat);
+ else
+ copy_m4_m4(winmat, re->winmat);
+
+ for (a=0; a<obr->totvert; a++, vec+=2) {
+ if ((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
+ else ver++;
+
+ projectvert(ver->co, winmat, ho);
+ speedvector_project(NULL, vec, ver->co, ho);
+ }
+ }
+ }
+}
+
+static void free_dbase_object_vectors(ListBase *lb)
+{
+ ObjectInstanceRen *obi;
+
+ for (obi= lb->first; obi; obi= obi->next)
+ if (obi->vectors)
+ MEM_freeN(obi->vectors);
+ BLI_freelistN(lb);
+}
+
+void RE_Database_FromScene_Vectors(Render *re, Main *bmain, Scene *sce, unsigned int lay)
+{
+ ObjectInstanceRen *obi, *oldobi;
+ StrandSurface *mesh;
+ ListBase *table;
+ ListBase oldtable= {NULL, NULL}, newtable= {NULL, NULL};
+ ListBase strandsurface;
+ int step;
+
+ re->i.infostr = IFACE_("Calculating previous frame vectors");
+ re->r.mode |= R_SPEED;
+
+ speedvector_project(re, NULL, NULL, NULL); /* initializes projection code */
+
+ /* creates entire dbase */
+ database_fromscene_vectors(re, sce, lay, -1);
+
+ /* copy away vertex info */
+ copy_dbase_object_vectors(re, &oldtable);
+
+ /* free dbase and make the future one */
+ strandsurface= re->strandsurface;
+ memset(&re->strandsurface, 0, sizeof(ListBase));
+ re->i.convertdone = true;
+ RE_Database_Free(re);
+ re->strandsurface= strandsurface;
+
+ if (!re->test_break(re->tbh)) {
+ /* creates entire dbase */
+ re->i.infostr = IFACE_("Calculating next frame vectors");
+
+ database_fromscene_vectors(re, sce, lay, +1);
+ }
+ /* copy away vertex info */
+ copy_dbase_object_vectors(re, &newtable);
+
+ /* free dbase and make the real one */
+ strandsurface= re->strandsurface;
+ memset(&re->strandsurface, 0, sizeof(ListBase));
+ re->i.convertdone = true;
+ RE_Database_Free(re);
+ re->strandsurface= strandsurface;
+
+ if (!re->test_break(re->tbh)) {
+ RE_Database_FromScene(re, bmain, sce, lay, 1);
+ RE_Database_Preprocess(re);
+ }
+
+ if (!re->test_break(re->tbh)) {
+ int vectorlay= get_vector_renderlayers(re->scene);
+
+ for (step= 0; step<2; step++) {
+
+ if (step)
+ table= &newtable;
+ else
+ table= &oldtable;
+
+ oldobi= table->first;
+ for (obi= re->instancetable.first; obi && oldobi; obi= obi->next) {
+ int ok= 1;
+ FluidsimModifierData *fluidmd;
+
+ if (!(obi->lay & vectorlay))
+ continue;
+
+ obi->totvector= obi->obr->totvert;
+
+ /* find matching object in old table */
+ if (oldobi->ob!=obi->ob || oldobi->par!=obi->par || oldobi->index!=obi->index || oldobi->psysindex!=obi->psysindex) {
+ ok= 0;
+ for (oldobi= table->first; oldobi; oldobi= oldobi->next)
+ if (oldobi->ob==obi->ob && oldobi->par==obi->par && oldobi->index==obi->index && oldobi->psysindex==obi->psysindex)
+ break;
+ if (oldobi==NULL)
+ oldobi= table->first;
+ else
+ ok= 1;
+ }
+ if (ok==0) {
+ printf("speed table: missing object %s\n", obi->ob->id.name + 2);
+ continue;
+ }
+
+ /* NT check for fluidsim special treatment */
+ fluidmd = (FluidsimModifierData *)modifiers_findByType(obi->ob, eModifierType_Fluidsim);
+ if (fluidmd && fluidmd->fss && (fluidmd->fss->type & OB_FLUIDSIM_DOMAIN)) {
+ /* use preloaded per vertex simulation data, only does calculation for step=1 */
+ /* NOTE/FIXME - velocities and meshes loaded unnecessarily often during the database_fromscene_vectors calls... */
+ load_fluidsimspeedvectors(re, obi, oldobi->vectors, step);
+ }
+ else {
+ /* check if both have same amounts of vertices */
+ if (obi->totvector==oldobi->totvector)
+ calculate_speedvectors(re, obi, oldobi->vectors, step);
+ else
+ printf("Warning: object %s has different amount of vertices or strands on other frame\n", obi->ob->id.name + 2);
+ } /* not fluidsim */
+
+ oldobi= oldobi->next;
+ }
+ }
+ }
+
+ free_dbase_object_vectors(&oldtable);
+ free_dbase_object_vectors(&newtable);
+
+ for (mesh=re->strandsurface.first; mesh; mesh=mesh->next) {
+ if (mesh->prevco) {
+ MEM_freeN(mesh->prevco);
+ mesh->prevco= NULL;
+ }
+ if (mesh->nextco) {
+ MEM_freeN(mesh->nextco);
+ mesh->nextco= NULL;
+ }
+ }
+
+ re->i.infostr = NULL;
+ re->stats_draw(re->sdh, &re->i);
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* Baking */
+/* ------------------------------------------------------------------------- */
+
+/* setup for shaded view or bake, so only lamps and materials are initialized */
+/* type:
+ * RE_BAKE_LIGHT: for shaded view, only add lamps
+ * RE_BAKE_ALL: for baking, all lamps and objects
+ * RE_BAKE_NORMALS:for baking, no lamps and only selected objects
+ * RE_BAKE_AO: for baking, no lamps, but all objects
+ * RE_BAKE_TEXTURE:for baking, no lamps, only selected objects
+ * RE_BAKE_VERTEX_COLORS:for baking, no lamps, only selected objects
+ * RE_BAKE_DISPLACEMENT:for baking, no lamps, only selected objects
+ * RE_BAKE_DERIVATIVE:for baking, no lamps, only selected objects
+ * RE_BAKE_SHADOW: for baking, only shadows, but all objects
+ */
+void RE_Database_Baking(Render *re, Main *bmain, Scene *scene, unsigned int lay, const int type, Object *actob)
+{
+ Object *camera;
+ float mat[4][4];
+ float amb[3];
+ const short onlyselected= !ELEM(type, RE_BAKE_LIGHT, RE_BAKE_ALL, RE_BAKE_SHADOW, RE_BAKE_AO, RE_BAKE_VERTEX_COLORS);
+ const short nolamps= ELEM(type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE, RE_BAKE_VERTEX_COLORS);
+
+ re->main= bmain;
+ re->scene= scene;
+ re->lay= lay;
+
+ /* renderdata setup and exceptions */
+ render_copy_renderdata(&re->r, &scene->r);
+
+ RE_init_threadcount(re);
+
+ re->flag |= R_BAKING;
+ re->excludeob= actob;
+ if (actob)
+ re->flag |= R_BAKE_TRACE;
+
+ if (type==RE_BAKE_NORMALS && re->r.bake_normal_space==R_BAKE_SPACE_TANGENT)
+ re->flag |= R_NEED_TANGENT;
+
+ if (type==RE_BAKE_VERTEX_COLORS)
+ re->flag |= R_NEED_VCOL;
+
+ if (!actob && ELEM(type, RE_BAKE_LIGHT, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE, RE_BAKE_VERTEX_COLORS)) {
+ re->r.mode &= ~R_SHADOW;
+ re->r.mode &= ~R_RAYTRACE;
+ }
+
+ if (!actob && (type==RE_BAKE_SHADOW)) {
+ re->r.mode |= R_SHADOW;
+ }
+
+ /* setup render stuff */
+ re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bake db arena");
+
+ re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
+ re->lights.first= re->lights.last= NULL;
+ re->lampren.first= re->lampren.last= NULL;
+
+ /* in localview, lamps are using normal layers, objects only local bits */
+ if (re->lay & 0xFF000000)
+ lay &= 0xFF000000;
+
+ camera= RE_GetCamera(re);
+
+ /* if no camera, set unit */
+ if (camera) {
+ normalize_m4_m4(mat, camera->obmat);
+ invert_m4(mat);
+ RE_SetView(re, mat);
+ }
+ else {
+ unit_m4(mat);
+ RE_SetView(re, mat);
+ }
+ copy_m3_m4(re->imat, re->viewinv);
+
+ /* TODO: deep shadow maps + baking + strands */
+ /* strands use the window matrix and view size, there is to correct
+ * window matrix but at least avoids malloc and crash loop [#27807] */
+ unit_m4(re->winmat);
+ re->winx= re->winy= 256;
+ /* done setting dummy values */
+
+ init_render_world(re); /* do first, because of ambient. also requires re->osa set correct */
+ if (re->r.mode & R_RAYTRACE) {
+ init_render_qmcsampler(re);
+
+ if (re->wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT))
+ if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT)
+ init_ao_sphere(re, &re->wrld);
+ }
+
+ /* still bad... doing all */
+ init_render_textures(re);
+
+ copy_v3_v3(amb, &re->wrld.ambr);
+ init_render_materials(re->main, re->r.mode, amb, true);
+
+ set_node_shader_lamp_loop(shade_material_loop);
+
+ /* MAKE RENDER DATA */
+ database_init_objects(re, lay, nolamps, onlyselected, actob, 0);
+
+ set_material_lightgroups(re);
+
+ /* SHADOW BUFFER */
+ if (type!=RE_BAKE_LIGHT)
+ if (re->r.mode & R_SHADOW)
+ threaded_makeshadowbufs(re);
+
+ /* raytree */
+ if (!re->test_break(re->tbh))
+ if (re->r.mode & R_RAYTRACE)
+ makeraytree(re);
+
+ /* point density texture */
+ if (!re->test_break(re->tbh))
+ make_pointdensities(re);
+
+ /* voxel data texture */
+ if (!re->test_break(re->tbh))
+ make_voxeldata(re);
+
+ /* occlusion */
+ if ((re->wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT)) && !re->test_break(re->tbh))
+ if (re->wrld.ao_gather_method == WO_AOGATHER_APPROX)
+ if (re->r.mode & R_SHADOW)
+ make_occ_tree(re);
+
+ re->i.convertdone = true;
+}
diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c
new file mode 100644
index 00000000000..85a6af92a28
--- /dev/null
+++ b/source/blender/render/intern/source/envmap.c
@@ -0,0 +1,822 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributors: 2004/2005/2006 Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/envmap.c
+ * \ingroup render
+ */
+
+#include <math.h>
+#include <string.h>
+
+/* external modules: */
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h" /* for rectcpy */
+
+#include "DNA_group_types.h"
+#include "DNA_image_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_texture_types.h"
+
+#include "BKE_main.h"
+#include "BKE_image.h" /* BKE_imbuf_write */
+#include "BKE_texture.h"
+#include "BKE_scene.h"
+
+/* this module */
+#include "render_types.h"
+#include "envmap.h"
+#include "renderdatabase.h"
+#include "renderpipeline.h"
+#include "texture.h"
+#include "zbuf.h"
+#include "render_result.h"
+
+/* ------------------------------------------------------------------------- */
+
+static void envmap_split_ima(EnvMap *env, ImBuf *ibuf)
+{
+ int dx, part;
+
+ /* after lock we test cube[1], if set the other thread has done it fine */
+ BLI_thread_lock(LOCK_IMAGE);
+ if (env->cube[1] == NULL) {
+
+ BKE_texture_envmap_free_data(env);
+
+ dx = ibuf->y;
+ dx /= 2;
+ if (3 * dx == ibuf->x) {
+ env->type = ENV_CUBE;
+ env->ok = ENV_OSA;
+ }
+ else if (ibuf->x == ibuf->y) {
+ env->type = ENV_PLANE;
+ env->ok = ENV_OSA;
+ }
+ else {
+ printf("Incorrect envmap size\n");
+ env->ok = 0;
+ env->ima->ok = 0;
+ }
+
+ if (env->ok) {
+ if (env->type == ENV_CUBE) {
+ for (part = 0; part < 6; part++) {
+ env->cube[part] = IMB_allocImBuf(dx, dx, 24, IB_rect | IB_rectfloat);
+ }
+ IMB_float_from_rect(ibuf);
+
+ IMB_rectcpy(env->cube[0], ibuf,
+ 0, 0, 0, 0, dx, dx);
+ IMB_rectcpy(env->cube[1], ibuf,
+ 0, 0, dx, 0, dx, dx);
+ IMB_rectcpy(env->cube[2], ibuf,
+ 0, 0, 2 * dx, 0, dx, dx);
+ IMB_rectcpy(env->cube[3], ibuf,
+ 0, 0, 0, dx, dx, dx);
+ IMB_rectcpy(env->cube[4], ibuf,
+ 0, 0, dx, dx, dx, dx);
+ IMB_rectcpy(env->cube[5], ibuf,
+ 0, 0, 2 * dx, dx, dx, dx);
+
+ }
+ else { /* ENV_PLANE */
+ env->cube[1] = IMB_dupImBuf(ibuf);
+ IMB_float_from_rect(env->cube[1]);
+ }
+ }
+ }
+ BLI_thread_unlock(LOCK_IMAGE);
+}
+
+/* ------------------------------------------------------------------------- */
+/* ****************** RENDER ********************** */
+
+/* copy current render */
+static Render *envmap_render_copy(Render *re, EnvMap *env)
+{
+ Render *envre;
+ float viewscale;
+ int cuberes;
+
+ envre = RE_NewRender("Envmap");
+
+ env->lastsize = re->r.size;
+ cuberes = (env->cuberes * re->r.size) / 100;
+ cuberes &= 0xFFFC;
+
+ /* this flag has R_ZTRA in it for example */
+ envre->flag = re->flag;
+
+ /* set up renderdata */
+ render_copy_renderdata(&envre->r, &re->r);
+ envre->r.mode &= ~(R_BORDER | R_PANORAMA | R_ORTHO | R_MBLUR);
+ BLI_freelistN(&envre->r.layers);
+ BLI_freelistN(&envre->r.views);
+ envre->r.filtertype = 0;
+ envre->r.tilex = envre->r.xsch / 2;
+ envre->r.tiley = envre->r.ysch / 2;
+ envre->r.size = 100;
+ envre->r.yasp = envre->r.xasp = 1;
+
+ RE_InitState(envre, NULL, &envre->r, NULL, cuberes, cuberes, NULL);
+ envre->main = re->main;
+ envre->scene = re->scene; /* unsure about this... */
+ envre->scene_color_manage = re->scene_color_manage;
+ envre->lay = re->lay;
+
+ /* view stuff in env render */
+ viewscale = (env->type == ENV_PLANE) ? env->viewscale : 1.0f;
+ RE_SetEnvmapCamera(envre, env->object, viewscale, env->clipsta, env->clipend);
+ copy_m4_m4(envre->viewmat_orig, re->viewmat_orig);
+
+ /* callbacks */
+ envre->display_update = re->display_update;
+ envre->duh = re->duh;
+ envre->test_break = re->test_break;
+ envre->tbh = re->tbh;
+ envre->current_scene_update = re->current_scene_update;
+ envre->suh = re->suh;
+
+ /* and for the evil stuff; copy the database... */
+ envre->totvlak = re->totvlak;
+ envre->totvert = re->totvert;
+ envre->tothalo = re->tothalo;
+ envre->totstrand = re->totstrand;
+ envre->totlamp = re->totlamp;
+ envre->sortedhalos = re->sortedhalos;
+ envre->lights = re->lights;
+ envre->objecttable = re->objecttable;
+ envre->customdata_names = re->customdata_names;
+ envre->raytree = re->raytree;
+ envre->totinstance = re->totinstance;
+ envre->instancetable = re->instancetable;
+ envre->objectinstance = re->objectinstance;
+ envre->qmcsamplers = re->qmcsamplers;
+
+ return envre;
+}
+
+static void envmap_free_render_copy(Render *envre)
+{
+
+ envre->totvlak = 0;
+ envre->totvert = 0;
+ envre->tothalo = 0;
+ envre->totstrand = 0;
+ envre->totlamp = 0;
+ envre->totinstance = 0;
+ envre->sortedhalos = NULL;
+ BLI_listbase_clear(&envre->lights);
+ BLI_listbase_clear(&envre->objecttable);
+ BLI_listbase_clear(&envre->customdata_names);
+ envre->raytree = NULL;
+ BLI_listbase_clear(&envre->instancetable);
+ envre->objectinstance = NULL;
+ envre->qmcsamplers = NULL;
+
+ RE_FreeRender(envre);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void envmap_transmatrix(float mat[4][4], int part)
+{
+ float tmat[4][4], eul[3], rotmat[4][4];
+
+ eul[0] = eul[1] = eul[2] = 0.0;
+
+ if (part == 0) { /* neg z */
+ /* pass */
+ }
+ else if (part == 1) { /* pos z */
+ eul[0] = M_PI;
+ }
+ else if (part == 2) { /* pos y */
+ eul[0] = M_PI / 2.0;
+ }
+ else if (part == 3) { /* neg x */
+ eul[0] = M_PI / 2.0;
+ eul[2] = M_PI / 2.0;
+ }
+ else if (part == 4) { /* neg y */
+ eul[0] = M_PI / 2.0;
+ eul[2] = M_PI;
+ }
+ else { /* pos x */
+ eul[0] = M_PI / 2.0;
+ eul[2] = -M_PI / 2.0;
+ }
+
+ copy_m4_m4(tmat, mat);
+ eul_to_mat4(rotmat, eul);
+ mul_m4_m4m4(mat, tmat, rotmat);
+}
+/* ------------------------------------------------------------------------- */
+
+static void env_set_imats(Render *re)
+{
+ Base *base;
+ float mat[4][4];
+
+ base = re->scene->base.first;
+ while (base) {
+ mul_m4_m4m4(mat, re->viewmat, base->object->obmat);
+ invert_m4_m4(base->object->imat, mat);
+
+ base = base->next;
+ }
+
+}
+
+/* ------------------------------------------------------------------------- */
+
+void env_rotate_scene(Render *re, float mat[4][4], int do_rotate)
+{
+ ObjectRen *obr;
+ ObjectInstanceRen *obi;
+ LampRen *lar = NULL;
+ HaloRen *har = NULL;
+ float imat[3][3], mat_inverse[4][4], smat[4][4], tmat[4][4], cmat[3][3], tmpmat[4][4];
+ int a;
+
+ if (do_rotate == 0) {
+ invert_m4_m4(tmat, mat);
+ copy_m3_m4(imat, tmat);
+
+ copy_m4_m4(mat_inverse, mat);
+ }
+ else {
+ copy_m4_m4(tmat, mat);
+ copy_m3_m4(imat, mat);
+
+ invert_m4_m4(mat_inverse, tmat);
+ }
+
+ for (obi = re->instancetable.first; obi; obi = obi->next) {
+ /* append or set matrix depending on dupli */
+ if (obi->flag & R_DUPLI_TRANSFORMED) {
+ copy_m4_m4(tmpmat, obi->mat);
+ mul_m4_m4m4(obi->mat, tmat, tmpmat);
+ }
+ else if (do_rotate == 1)
+ copy_m4_m4(obi->mat, tmat);
+ else
+ unit_m4(obi->mat);
+
+ copy_m3_m4(cmat, obi->mat);
+ invert_m3_m3(obi->nmat, cmat);
+ transpose_m3(obi->nmat);
+
+ /* indicate the renderer has to use transform matrices */
+ if (do_rotate == 0)
+ obi->flag &= ~R_ENV_TRANSFORMED;
+ else {
+ obi->flag |= R_ENV_TRANSFORMED;
+ copy_m4_m4(obi->imat, mat_inverse);
+ }
+ }
+
+
+ for (obr = re->objecttable.first; obr; obr = obr->next) {
+ for (a = 0; a < obr->tothalo; a++) {
+ if ((a & 255) == 0) har = obr->bloha[a >> 8];
+ else har++;
+
+ mul_m4_v3(tmat, har->co);
+ }
+
+ /* imat_ren is needed for correct texture coordinates */
+ mul_m4_m4m4(obr->ob->imat_ren, re->viewmat, obr->ob->obmat);
+ invert_m4(obr->ob->imat_ren);
+ }
+
+ for (lar = re->lampren.first; lar; lar = lar->next) {
+ float lamp_imat[4][4];
+
+ /* copy from add_render_lamp */
+ if (do_rotate == 1)
+ mul_m4_m4m4(tmpmat, re->viewmat, lar->lampmat);
+ else
+ mul_m4_m4m4(tmpmat, re->viewmat_orig, lar->lampmat);
+
+ invert_m4_m4(lamp_imat, tmpmat);
+ copy_m3_m4(lar->mat, tmpmat);
+ copy_m3_m4(lar->imat, lamp_imat);
+
+ lar->vec[0]= -tmpmat[2][0];
+ lar->vec[1]= -tmpmat[2][1];
+ lar->vec[2]= -tmpmat[2][2];
+ normalize_v3(lar->vec);
+ lar->co[0]= tmpmat[3][0];
+ lar->co[1]= tmpmat[3][1];
+ lar->co[2]= tmpmat[3][2];
+
+ if (lar->type == LA_AREA) {
+ area_lamp_vectors(lar);
+ }
+ else if (lar->type == LA_SPOT) {
+ normalize_v3(lar->imat[0]);
+ normalize_v3(lar->imat[1]);
+ normalize_v3(lar->imat[2]);
+
+ lar->sh_invcampos[0] = -lar->co[0];
+ lar->sh_invcampos[1] = -lar->co[1];
+ lar->sh_invcampos[2] = -lar->co[2];
+ mul_m3_v3(lar->imat, lar->sh_invcampos);
+ lar->sh_invcampos[2] *= lar->sh_zfac;
+
+ if (lar->shb) {
+ if (do_rotate == 1) {
+ mul_m4_m4m4(smat, lar->shb->viewmat, mat_inverse);
+ mul_m4_m4m4(lar->shb->persmat, lar->shb->winmat, smat);
+ }
+ else mul_m4_m4m4(lar->shb->persmat, lar->shb->winmat, lar->shb->viewmat);
+ }
+ }
+ }
+
+ if (do_rotate) {
+ init_render_world(re);
+ env_set_imats(re);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void env_layerflags(Render *re, unsigned int notlay)
+{
+ ObjectRen *obr;
+ VlakRen *vlr = NULL;
+ int a;
+
+ /* invert notlay, so if face is in multiple layers it will still be visible,
+ * unless all 'notlay' bits match the face bits.
+ * face: 0110
+ * not: 0100
+ * ~not: 1011
+ * now (face & ~not) is true
+ */
+
+ notlay = ~notlay;
+
+ for (obr = re->objecttable.first; obr; obr = obr->next) {
+ if ((obr->lay & notlay) == 0) {
+ for (a = 0; a < obr->totvlak; a++) {
+ if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
+ else vlr++;
+
+ vlr->flag |= R_HIDDEN;
+ }
+ }
+ }
+}
+
+static void env_hideobject(Render *re, Object *ob)
+{
+ ObjectRen *obr;
+ VlakRen *vlr = NULL;
+ int a;
+
+ for (obr = re->objecttable.first; obr; obr = obr->next) {
+ for (a = 0; a < obr->totvlak; a++) {
+ if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
+ else vlr++;
+
+ if (obr->ob == ob)
+ vlr->flag |= R_HIDDEN;
+ }
+ }
+}
+
+static void env_showobjects(Render *re)
+{
+ ObjectRen *obr;
+ VlakRen *vlr = NULL;
+ int a;
+
+ for (obr = re->objecttable.first; obr; obr = obr->next) {
+ for (a = 0; a < obr->totvlak; a++) {
+ if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
+ else vlr++;
+
+ vlr->flag &= ~R_HIDDEN;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void render_envmap(Render *re, EnvMap *env)
+{
+ /* only the cubemap and planar map is implemented */
+ Render *envre;
+ ImBuf *ibuf;
+ float orthmat[4][4];
+ float oldviewinv[4][4], mat[4][4], tmat[4][4];
+ short part;
+
+ /* need a recalc: ortho-render has no correct viewinv */
+ invert_m4_m4(oldviewinv, re->viewmat);
+
+ envre = envmap_render_copy(re, env);
+
+ /* precalc orthmat for object */
+ copy_m4_m4(orthmat, env->object->obmat);
+ normalize_m4(orthmat);
+
+ /* need imat later for texture imat */
+ mul_m4_m4m4(mat, re->viewmat, orthmat);
+ invert_m4_m4(tmat, mat);
+ copy_m3_m4(env->obimat, tmat);
+
+ for (part = 0; part < 6; part++) {
+ if (env->type == ENV_PLANE && part != 1)
+ continue;
+
+ re->display_clear(re->dch, envre->result);
+
+ copy_m4_m4(tmat, orthmat);
+ envmap_transmatrix(tmat, part);
+ invert_m4_m4(mat, tmat);
+ /* mat now is the camera 'viewmat' */
+
+ copy_m4_m4(envre->viewmat, mat);
+ copy_m4_m4(envre->viewinv, tmat);
+
+ /* we have to correct for the already rotated vertexcoords */
+ mul_m4_m4m4(tmat, envre->viewmat, oldviewinv);
+ invert_m4_m4(env->imat, tmat);
+
+ env_rotate_scene(envre, tmat, 1);
+ project_renderdata(envre, projectverto, 0, 0, 1);
+ env_layerflags(envre, env->notlay);
+ env_hideobject(envre, env->object);
+
+ if (re->test_break(re->tbh) == 0) {
+ RE_TileProcessor(envre);
+ }
+
+ /* rotate back */
+ env_showobjects(envre);
+ env_rotate_scene(envre, tmat, 0);
+
+ if (re->test_break(re->tbh) == 0) {
+ int y;
+ float *alpha;
+ float *rect;
+
+ if (envre->result->do_exr_tile) {
+ BLI_rw_mutex_lock(&envre->resultmutex, THREAD_LOCK_WRITE);
+ render_result_exr_file_end(envre);
+ BLI_rw_mutex_unlock(&envre->resultmutex);
+ }
+
+ RenderLayer *rl = envre->result->layers.first;
+
+ /* envmap is rendered independently of multiview */
+ rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, "");
+ ibuf = IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect | IB_rectfloat);
+ memcpy(ibuf->rect_float, rect, ibuf->channels * ibuf->x * ibuf->y * sizeof(float));
+
+ /* envmap renders without alpha */
+ alpha = ibuf->rect_float + 3;
+ for (y = ibuf->x * ibuf->y - 1; y >= 0; y--, alpha += 4)
+ *alpha = 1.0;
+
+ env->cube[part] = ibuf;
+ }
+
+ if (re->test_break(re->tbh)) break;
+
+ }
+
+ if (re->test_break(re->tbh)) BKE_texture_envmap_free_data(env);
+ else {
+ if (envre->r.mode & R_OSA) env->ok = ENV_OSA;
+ else env->ok = ENV_NORMAL;
+ env->lastframe = re->scene->r.cfra;
+ }
+
+ /* restore */
+ envmap_free_render_copy(envre);
+ env_set_imats(re);
+
+}
+
+/* ------------------------------------------------------------------------- */
+
+void make_envmaps(Render *re)
+{
+ Tex *tex;
+ bool do_init = false;
+ int depth = 0, trace;
+
+ if (!(re->r.mode & R_ENVMAP)) return;
+
+ /* we don't raytrace, disabling the flag will cause ray_transp render solid */
+ trace = (re->r.mode & R_RAYTRACE);
+ re->r.mode &= ~R_RAYTRACE;
+
+ re->i.infostr = IFACE_("Creating Environment maps");
+ re->stats_draw(re->sdh, &re->i);
+
+ /* 5 = hardcoded max recursion level */
+ while (depth < 5) {
+ tex = re->main->tex.first;
+ while (tex) {
+ if (tex->id.us && tex->type == TEX_ENVMAP) {
+ if (tex->env && tex->env->object) {
+ EnvMap *env = tex->env;
+
+ if (env->object->lay & re->lay) {
+ if (env->stype == ENV_LOAD) {
+ float orthmat[4][4], mat[4][4], tmat[4][4];
+
+ /* precalc orthmat for object */
+ copy_m4_m4(orthmat, env->object->obmat);
+ normalize_m4(orthmat);
+
+ /* need imat later for texture imat */
+ mul_m4_m4m4(mat, re->viewmat, orthmat);
+ invert_m4_m4(tmat, mat);
+ copy_m3_m4(env->obimat, tmat);
+ }
+ else {
+
+ /* decide if to render an envmap (again) */
+ if (env->depth >= depth) {
+
+ /* set 'recalc' to make sure it does an entire loop of recalcs */
+
+ if (env->ok) {
+ /* free when OSA, and old one isn't OSA */
+ if ((re->r.mode & R_OSA) && env->ok == ENV_NORMAL)
+ BKE_texture_envmap_free_data(env);
+ /* free when size larger */
+ else if (env->lastsize < re->r.size)
+ BKE_texture_envmap_free_data(env);
+ /* free when env is in recalcmode */
+ else if (env->recalc)
+ BKE_texture_envmap_free_data(env);
+ }
+
+ if (env->ok == 0 && depth == 0) env->recalc = 1;
+
+ if (env->ok == 0) {
+ do_init = true;
+ render_envmap(re, env);
+
+ if (depth == env->depth) env->recalc = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ tex = tex->id.next;
+ }
+ depth++;
+ }
+
+ if (do_init) {
+ re->display_init(re->dih, re->result);
+ re->display_clear(re->dch, re->result);
+ // re->flag |= R_REDRAW_PRV;
+ }
+ /* restore */
+ re->r.mode |= trace;
+
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int envcube_isect(EnvMap *env, const float vec[3], float answ[2])
+{
+ float lambda;
+ int face;
+
+ if (env->type == ENV_PLANE) {
+ face = 1;
+
+ lambda = 1.0f / vec[2];
+ answ[0] = env->viewscale * lambda * vec[0];
+ answ[1] = -env->viewscale * lambda * vec[1];
+ }
+ else {
+ /* which face */
+ if (vec[2] <= -fabsf(vec[0]) && vec[2] <= -fabsf(vec[1]) ) {
+ face = 0;
+ lambda = -1.0f / vec[2];
+ answ[0] = lambda * vec[0];
+ answ[1] = lambda * vec[1];
+ }
+ else if (vec[2] >= fabsf(vec[0]) && vec[2] >= fabsf(vec[1])) {
+ face = 1;
+ lambda = 1.0f / vec[2];
+ answ[0] = lambda * vec[0];
+ answ[1] = -lambda * vec[1];
+ }
+ else if (vec[1] >= fabsf(vec[0])) {
+ face = 2;
+ lambda = 1.0f / vec[1];
+ answ[0] = lambda * vec[0];
+ answ[1] = lambda * vec[2];
+ }
+ else if (vec[0] <= -fabsf(vec[1])) {
+ face = 3;
+ lambda = -1.0f / vec[0];
+ answ[0] = lambda * vec[1];
+ answ[1] = lambda * vec[2];
+ }
+ else if (vec[1] <= -fabsf(vec[0])) {
+ face = 4;
+ lambda = -1.0f / vec[1];
+ answ[0] = -lambda * vec[0];
+ answ[1] = lambda * vec[2];
+ }
+ else {
+ face = 5;
+ lambda = 1.0f / vec[0];
+ answ[0] = -lambda * vec[1];
+ answ[1] = lambda * vec[2];
+ }
+ }
+
+ answ[0] = 0.5f + 0.5f * answ[0];
+ answ[1] = 0.5f + 0.5f * answ[1];
+ return face;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void set_dxtdyt(float r_dxt[3], float r_dyt[3], const float dxt[3], const float dyt[3], int face)
+{
+ if (face == 2 || face == 4) {
+ r_dxt[0] = dxt[0];
+ r_dyt[0] = dyt[0];
+ r_dxt[1] = dxt[2];
+ r_dyt[1] = dyt[2];
+ }
+ else if (face == 3 || face == 5) {
+ r_dxt[0] = dxt[1];
+ r_dxt[1] = dxt[2];
+ r_dyt[0] = dyt[1];
+ r_dyt[1] = dyt[2];
+ }
+ else {
+ r_dxt[0] = dxt[0];
+ r_dyt[0] = dyt[0];
+ r_dxt[1] = dxt[1];
+ r_dyt[1] = dyt[1];
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, struct ImagePool *pool, const bool skip_load_image)
+{
+ extern Render R; /* only in this call */
+ /* texvec should be the already reflected normal */
+ EnvMap *env;
+ ImBuf *ibuf;
+ float fac, vec[3], sco[3], dxts[3], dyts[3];
+ int face, face1;
+
+ env = tex->env;
+ if (env == NULL || (env->stype != ENV_LOAD && env->object == NULL)) {
+ texres->tin = 0.0;
+ return 0;
+ }
+
+ if (env->stype == ENV_LOAD) {
+ env->ima = tex->ima;
+ if (env->ima && env->ima->ok) {
+ if (env->cube[1] == NULL) {
+ ImBuf *ibuf_ima = BKE_image_pool_acquire_ibuf(env->ima, NULL, pool);
+ if (ibuf_ima)
+ envmap_split_ima(env, ibuf_ima);
+ else
+ env->ok = 0;
+
+ if (env->type == ENV_PLANE)
+ tex->extend = TEX_EXTEND;
+
+ BKE_image_pool_release_ibuf(env->ima, ibuf_ima, pool);
+ }
+ }
+ }
+
+ if (env->ok == 0) {
+ texres->tin = 0.0;
+ return 0;
+ }
+
+ /* rotate to envmap space, if object is set */
+ copy_v3_v3(vec, texvec);
+ if (env->object) {
+ mul_m3_v3(env->obimat, vec);
+ if (osatex) {
+ mul_m3_v3(env->obimat, dxt);
+ mul_m3_v3(env->obimat, dyt);
+ }
+ }
+ else {
+ if (!BKE_scene_use_world_space_shading(R.scene)) {
+ // texvec is in view space
+ mul_mat3_m4_v3(R.viewinv, vec);
+ if (osatex) {
+ mul_mat3_m4_v3(R.viewinv, dxt);
+ mul_mat3_m4_v3(R.viewinv, dyt);
+ }
+ }
+ }
+
+ face = envcube_isect(env, vec, sco);
+ ibuf = env->cube[face];
+
+ if (osatex) {
+ set_dxtdyt(dxts, dyts, dxt, dyt, face);
+ imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, texres, pool, skip_load_image);
+
+ /* edges? */
+
+ if (texres->ta < 1.0f) {
+ TexResult texr1, texr2;
+
+ texr1.nor = texr2.nor = NULL;
+ texr1.talpha = texr2.talpha = texres->talpha; /* boxclip expects this initialized */
+
+ add_v3_v3(vec, dxt);
+ face1 = envcube_isect(env, vec, sco);
+ sub_v3_v3(vec, dxt);
+
+ if (face != face1) {
+ ibuf = env->cube[face1];
+ set_dxtdyt(dxts, dyts, dxt, dyt, face1);
+ imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr1, pool, skip_load_image);
+ }
+ else texr1.tr = texr1.tg = texr1.tb = texr1.ta = 0.0;
+
+ /* here was the nasty bug! results were not zero-ed. FPE! */
+
+ add_v3_v3(vec, dyt);
+ face1 = envcube_isect(env, vec, sco);
+ sub_v3_v3(vec, dyt);
+
+ if (face != face1) {
+ ibuf = env->cube[face1];
+ set_dxtdyt(dxts, dyts, dxt, dyt, face1);
+ imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr2, pool, skip_load_image);
+ }
+ else texr2.tr = texr2.tg = texr2.tb = texr2.ta = 0.0;
+
+ fac = (texres->ta + texr1.ta + texr2.ta);
+ if (fac != 0.0f) {
+ fac = 1.0f / fac;
+
+ texres->tr = fac * (texres->ta * texres->tr + texr1.ta * texr1.tr + texr2.ta * texr2.tr);
+ texres->tg = fac * (texres->ta * texres->tg + texr1.ta * texr1.tg + texr2.ta * texr2.tg);
+ texres->tb = fac * (texres->ta * texres->tb + texr1.ta * texr1.tb + texr2.ta * texr2.tb);
+ }
+ texres->ta = 1.0;
+ }
+ }
+ else {
+ imagewrap(tex, NULL, ibuf, sco, texres, pool, skip_load_image);
+ }
+
+ return 1;
+}
diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index b541c993bc7..10ed91b53c4 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -112,11 +112,11 @@ void RE_engines_register(RenderEngineType *render_type)
RenderEngineType *RE_engines_find(const char *idname)
{
RenderEngineType *type;
-
+
type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname));
if (!type)
type = BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname));
-
+
return type;
}
@@ -320,7 +320,7 @@ int RE_engine_test_break(RenderEngine *engine)
if (re)
return re->test_break(re->tbh);
-
+
return 0;
}
@@ -776,7 +776,7 @@ int RE_engine_render(Render *re, int do_all)
if (BKE_reports_contain(re->reports, RPT_ERROR))
G.is_break = true;
-
+
#ifdef WITH_FREESTYLE
if (re->r.mode & R_EDGE_FRS)
RE_RenderFreestyleExternal(re);
diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c
index b9d55916f51..1e9ad79e599 100644
--- a/source/blender/render/intern/source/imagetexture.c
+++ b/source/blender/render/intern/source/imagetexture.c
@@ -32,7 +32,7 @@
#include <fcntl.h>
#include <math.h>
#include <float.h>
-#ifndef WIN32
+#ifndef WIN32
#include <unistd.h>
#else
#include <io.h>
@@ -67,7 +67,7 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max
static void ibuf_get_color(float col[4], struct ImBuf *ibuf, int x, int y)
{
int ofs = y * ibuf->x + x;
-
+
if (ibuf->rect_float) {
if (ibuf->channels==4) {
const float *fp= ibuf->rect_float + 4*ofs;
@@ -105,15 +105,15 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
int xi, yi; /* original values */
texres->tin= texres->ta= texres->tr= texres->tg= texres->tb= 0.0f;
-
+
/* we need to set retval OK, otherwise texture code generates normals itself... */
retval= texres->nor ? 3 : 1;
-
+
/* quick tests */
if (ibuf==NULL && ima==NULL)
return retval;
if (ima) {
-
+
/* hack for icon render */
if (skip_load_image && !BKE_image_has_loaded_ibuf(ima))
return retval;
@@ -127,7 +127,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
BKE_image_pool_release_ibuf(ima, ibuf, pool);
return retval;
}
-
+
/* setup mapping */
if (tex->imaflag & TEX_IMAROT) {
fy= texvec[0];
@@ -137,10 +137,10 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
fx= texvec[0];
fy= texvec[1];
}
-
+
if (tex->extend == TEX_CHECKER) {
int xs, ys;
-
+
xs= (int)floor(fx);
ys= (int)floor(fy);
fx-= xs;
@@ -205,7 +205,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
if (y<0) y+= ibuf->y;
}
}
-
+
/* keep this before interpolation [#29761] */
if (ima) {
if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) {
@@ -232,7 +232,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
else { /* no filtering */
ibuf_get_color(&texres->tr, ibuf, x, y);
}
-
+
if (texres->nor) {
if (tex->imaflag & TEX_NORMALMAP) {
/* qdn: normal from color
@@ -283,7 +283,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
else {
texres->ta = texres->tin = 1.0;
}
-
+
if (tex->flag & TEX_NEGALPHA) {
texres->ta = 1.0f - texres->ta;
}
@@ -301,7 +301,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul
BKE_image_pool_release_ibuf(ima, ibuf, pool);
BRICONTRGB;
-
+
return retval;
}
@@ -327,9 +327,9 @@ static void clipx_rctf_swap(rctf *stack, short *count, float x1, float x2)
newrct->xmin = rf->xmin+(x2-x1);
newrct->ymin = rf->ymin;
newrct->ymax = rf->ymax;
-
+
if (newrct->xmin ==newrct->xmax) (*count)--;
-
+
rf->xmin = x1;
}
}
@@ -489,7 +489,7 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres)
else {
div= texres->tr= texres->tg= texres->tb= texres->ta= 0.0;
for (y=starty; y<=endy; y++) {
-
+
muly= 1.0;
if (starty==endy) {
@@ -499,10 +499,10 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres)
if (y==starty) muly= 1.0f-(rf->ymin - y);
if (y==endy) muly= (rf->ymax - y);
}
-
+
if (startx==endx) {
mulx= muly;
-
+
ibuf_get_color(col, ibuf, startx, y);
texres->ta+= mulx*col[3];
@@ -518,7 +518,7 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres)
if (x==endx) mulx*= (rf->xmax - x);
ibuf_get_color(col, ibuf, x, y);
-
+
if (mulx==1.0f) {
texres->ta+= col[3];
texres->tr+= col[0];
@@ -573,7 +573,7 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max
rf->ymax = maxy*(ibuf->y);
texr.talpha= texres->talpha; /* is read by boxsample_clip */
-
+
if (imapextend) {
CLAMP(rf->xmin, 0.0f, ibuf->x-1);
CLAMP(rf->xmax, 0.0f, ibuf->x-1);
@@ -608,7 +608,7 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max
tot= texres->tr= texres->tb= texres->tg= texres->ta= 0.0;
while (count--) {
boxsampleclip(ibuf, rf, &texr);
-
+
opp= square_rctf(rf);
tot+= opp;
@@ -629,7 +629,7 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max
boxsampleclip(ibuf, rf, texres);
if (texres->talpha==0) texres->ta= 1.0;
-
+
if (alphaclip!=1.0f) {
/* premul it all */
texres->tr*= alphaclip;
@@ -637,7 +637,7 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max
texres->tb*= alphaclip;
texres->ta*= alphaclip;
}
-}
+}
/*-----------------------------------------------------------------------------------------------------------------
* from here, some functions only used for the new filtering */
@@ -874,7 +874,7 @@ static void image_mipmap_test(Tex *tex, ImBuf *ibuf)
{
if (tex->imaflag & TEX_MIPMAP) {
if ((ibuf->flags & IB_fields) == 0) {
-
+
if (ibuf->mipmap[0] && (ibuf->userflags & IB_MIPMAP_INVALID)) {
BLI_thread_lock(LOCK_IMAGE);
if (ibuf->userflags & IB_MIPMAP_INVALID) {
@@ -885,7 +885,7 @@ static void image_mipmap_test(Tex *tex, ImBuf *ibuf)
}
if (ibuf->mipmap[0] == NULL) {
BLI_thread_lock(LOCK_IMAGE);
- if (ibuf->mipmap[0] == NULL)
+ if (ibuf->mipmap[0] == NULL)
IMB_makemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP);
BLI_thread_unlock(LOCK_IMAGE);
}
@@ -895,7 +895,7 @@ static void image_mipmap_test(Tex *tex, ImBuf *ibuf)
}
}
}
-
+
}
static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], float dxt[2], float dyt[2], TexResult *texres, struct ImagePool *pool, const bool skip_load_image)
@@ -946,7 +946,7 @@ static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float tex
/* mipmap test */
image_mipmap_test(tex, ibuf);
-
+
if (ima) {
if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) {
if ((tex->imaflag & TEX_CALCALPHA) == 0) {
@@ -1281,7 +1281,7 @@ static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float tex
else
texres->tin = texres->ta;
if (tex->flag & TEX_NEGALPHA) texres->ta = 1.f - texres->ta;
-
+
if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { /* normal from color */
/* The invert of the red channel is to make
* the normal map compliant with the outside world.
@@ -1312,7 +1312,7 @@ static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float tex
BKE_image_pool_release_ibuf(ima, ibuf, pool);
BRICONTRGB;
-
+
return retval;
}
@@ -1334,10 +1334,10 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres, pool, skip_load_image);
texres->tin= texres->ta= texres->tr= texres->tg= texres->tb= 0.0f;
-
+
/* we need to set retval OK, otherwise texture code generates normals itself... */
retval = texres->nor ? 3 : 1;
-
+
/* quick tests */
if (ibuf==NULL && ima==NULL)
return retval;
@@ -1346,7 +1346,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
/* hack for icon render */
if (skip_load_image && !BKE_image_has_loaded_ibuf(ima))
return retval;
-
+
ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool);
ima->flag|= IMA_USED_FOR_RENDER;
@@ -1356,7 +1356,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
BKE_image_pool_release_ibuf(ima, ibuf, pool);
return retval;
}
-
+
/* mipmap test */
image_mipmap_test(tex, ibuf);
@@ -1367,9 +1367,9 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
}
}
}
-
+
texr.talpha= texres->talpha;
-
+
if (tex->imaflag & TEX_IMAROT) {
fy= texvec[0];
fx= texvec[1];
@@ -1378,7 +1378,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
fx= texvec[0];
fy= texvec[1];
}
-
+
/* pixel coordinates */
minx = min_fff(dxt[0], dyt[0], dxt[0] + dyt[0]);
@@ -1389,7 +1389,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
/* tex_sharper has been removed */
minx= (maxx-minx)/2.0f;
miny= (maxy-miny)/2.0f;
-
+
if (tex->imaflag & TEX_FILTER_MIN) {
/* make sure the filtersize is minimal in pixels (normal, ref map can have miniature pixel dx/dy) */
float addval= (0.5f * tex->filtersize) / (float) MIN2(ibuf->x, ibuf->y);
@@ -1402,7 +1402,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
else if (tex->filtersize!=1.0f) {
minx*= tex->filtersize;
miny*= tex->filtersize;
-
+
dxt[0]*= tex->filtersize;
dxt[1]*= tex->filtersize;
dyt[0]*= tex->filtersize;
@@ -1410,13 +1410,13 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
}
if (tex->imaflag & TEX_IMAROT) SWAP(float, minx, miny);
-
+
if (minx>0.25f) minx= 0.25f;
else if (minx<0.00001f) minx= 0.00001f; /* side faces of unit-cube */
if (miny>0.25f) miny= 0.25f;
else if (miny<0.00001f) miny= 0.00001f;
-
+
/* repeat and clip */
imaprepeat= (tex->extend==TEX_REPEAT);
imapextend= (tex->extend==TEX_EXTEND);
@@ -1430,10 +1430,10 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
if (tex->extend == TEX_CHECKER) {
int xs, ys, xs1, ys1, xs2, ys2, boundary;
-
+
xs= (int)floor(fx);
ys= (int)floor(fy);
-
+
/* both checkers available, no boundary exceptions, checkerdist will eat aliasing */
if ( (tex->flag & TEX_CHECKER_ODD) && (tex->flag & TEX_CHECKER_EVEN) ) {
fx-= xs;
@@ -1447,7 +1447,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
return retval;
}
else {
-
+
xs1= (int)floor(fx-minx);
ys1= (int)floor(fy-miny);
xs2= (int)floor(fx+minx);
@@ -1479,14 +1479,14 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
if (tex->flag & TEX_CHECKER_ODD) {
if ((xs1+ys) & 1) fx-= xs2;
else fx-= xs1;
-
+
if ((ys1+xs) & 1) fy-= ys2;
else fy-= ys1;
}
if (tex->flag & TEX_CHECKER_EVEN) {
if ((xs1+ys) & 1) fx-= xs1;
else fx-= xs2;
-
+
if ((ys1+xs) & 1) fy-= ys1;
else fy-= ys2;
}
@@ -1525,7 +1525,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
if (fx>1.0f) fx -= (int)(fx);
else if (fx<0.0f) fx+= 1-(int)(fx);
}
-
+
if (imapextend) {
if (fy>1.0f) fy = 1.0f;
else if (fy<0.0f) fy= 0.0f;
@@ -1540,18 +1540,18 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
if (tex->imaflag & TEX_MIPMAP) {
ImBuf *previbuf, *curibuf;
float bumpscale;
-
+
dx = minx;
dy = miny;
maxd = max_ff(dx, dy);
if (maxd > 0.5f) maxd = 0.5f;
pixsize = 1.0f / (float) MIN2(ibuf->x, ibuf->y);
-
+
bumpscale= pixsize/maxd;
if (bumpscale>1.0f) bumpscale= 1.0f;
else bumpscale*=bumpscale;
-
+
curmap= 0;
previbuf= curibuf= ibuf;
while (curmap < IMB_MIPMAP_LEVELS && ibuf->mipmap[curmap]) {
@@ -1567,12 +1567,12 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
if (minx < 0.5f / ibuf->x) minx = 0.5f / ibuf->x;
if (miny < 0.5f / ibuf->y) miny = 0.5f / ibuf->y;
}
-
+
if (texres->nor && (tex->imaflag & TEX_NORMALMAP)==0) {
/* a bit extra filter */
//minx*= 1.35f;
//miny*= 1.35f;
-
+
boxsample(curibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend);
val1= texres->tr+texres->tg+texres->tb;
boxsample(curibuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend);
@@ -1583,11 +1583,11 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
/* don't switch x or y! */
texres->nor[0]= (val1-val2);
texres->nor[1]= (val1-val3);
-
+
if (previbuf!=curibuf) { /* interpolate */
-
+
boxsample(previbuf, fx-minx, fy-miny, fx+minx, fy+miny, &texr, imaprepeat, imapextend);
-
+
/* calc rgb */
dx= 2.0f*(pixsize-maxd)/pixsize;
if (dx>=1.0f) {
@@ -1601,16 +1601,16 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
texres->tr= dy*texres->tr+ dx*texr.tr;
texres->ta= dy*texres->ta+ dx*texr.ta;
}
-
+
val1= dy*val1+ dx*(texr.tr + texr.tg + texr.tb);
boxsample(previbuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend);
val2= dy*val2+ dx*(texr.tr + texr.tg + texr.tb);
boxsample(previbuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend);
val3= dy*val3+ dx*(texr.tr + texr.tg + texr.tb);
-
+
texres->nor[0]= (val1-val2); /* vals have been interpolated above! */
texres->nor[1]= (val1-val3);
-
+
if (dx<1.0f) {
dy= 1.0f-dx;
texres->tb= dy*texres->tb+ dx*texr.tb;
@@ -1632,9 +1632,9 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
if (previbuf!=curibuf) { /* interpolate */
boxsample(previbuf, minx, miny, maxx, maxy, &texr, imaprepeat, imapextend);
-
+
fx= 2.0f*(pixsize-maxd)/pixsize;
-
+
if (fx>=1.0f) {
texres->ta= texr.ta; texres->tb= texr.tb;
texres->tg= texr.tg; texres->tr= texr.tr;
@@ -1672,7 +1672,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
else
boxsample(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend);
}
-
+
if (tex->imaflag & TEX_CALCALPHA) {
texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb);
}
@@ -1681,7 +1681,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
}
if (tex->flag & TEX_NEGALPHA) texres->ta= 1.0f-texres->ta;
-
+
if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) {
/* qdn: normal from color
* The invert of the red channel is to make
@@ -1705,7 +1705,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const
BKE_image_pool_release_ibuf(ima, ibuf, pool);
BRICONTRGB;
-
+
return retval;
}
@@ -1713,16 +1713,16 @@ void image_sample(Image *ima, float fx, float fy, float dx, float dy, float resu
{
TexResult texres;
ImBuf *ibuf = BKE_image_pool_acquire_ibuf(ima, NULL, pool);
-
+
if (UNLIKELY(ibuf == NULL)) {
zero_v4(result);
return;
}
-
+
texres.talpha = true; /* boxsample expects to be initialized */
boxsample(ibuf, fx, fy, fx + dx, fy + dy, &texres, 0, 1);
copy_v4_v4(result, &texres.tr);
-
+
ima->flag|= IMA_USED_FOR_RENDER;
BKE_image_pool_release_ibuf(ima, ibuf, pool);
@@ -1737,11 +1737,11 @@ void ibuf_sample(ImBuf *ibuf, float fx, float fy, float dx, float dy, float resu
AFD.dyt[0] = dy; AFD.dyt[1] = dy;
//copy_v2_v2(AFD.dxt, dx);
//copy_v2_v2(AFD.dyt, dy);
-
+
AFD.intpol = 1;
AFD.extflag = TXC_EXTD;
ewa_eval(&texres, ibuf, fx, fy, &AFD);
-
+
copy_v4_v4(result, &texres.tr);
}
diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c
index 4274d641674..9611a8a7452 100644
--- a/source/blender/render/intern/source/initrender.c
+++ b/source/blender/render/intern/source/initrender.c
@@ -66,9 +66,9 @@ static float filt_quadratic(float x)
static float filt_cubic(float x)
{
float x2 = x * x;
-
+
if (x < 0.0f) x = -x;
-
+
if (x < 1.0f) return 0.5f * x * x2 - x2 + 2.0f / 3.0f;
if (x < 2.0f) return (2.0f - x) * (2.0f - x) * (2.0f - x) / 6.0f;
return 0.0f;
@@ -78,7 +78,7 @@ static float filt_cubic(float x)
static float filt_catrom(float x)
{
float x2 = x * x;
-
+
if (x < 0.0f) x = -x;
if (x < 1.0f) return 1.5f * x2 * x - 2.5f * x2 + 1.0f;
if (x < 2.0f) return -0.5f * x2 * x + 2.5f * x2 - 4.0f * x + 2.0f;
@@ -108,34 +108,34 @@ static float filt_mitchell(float x) /* Mitchell & Netravali's two-param cubic */
float RE_filter_value(int type, float x)
{
float gaussfac = 1.6f;
-
+
x = ABS(x);
-
+
switch (type) {
case R_FILTER_BOX:
if (x > 1.0f) return 0.0f;
return 1.0f;
-
+
case R_FILTER_TENT:
if (x > 1.0f) return 0.0f;
return 1.0f - x;
-
+
case R_FILTER_GAUSS:
{
const float two_gaussfac2 = 2.0f * gaussfac * gaussfac;
x *= 3.0f * gaussfac;
return 1.0f / sqrtf((float)M_PI * two_gaussfac2) * expf(-x*x / two_gaussfac2);
}
-
+
case R_FILTER_MITCH:
return filt_mitchell(x * gaussfac);
-
+
case R_FILTER_QUAD:
return filt_quadratic(x * gaussfac);
-
+
case R_FILTER_CUBIC:
return filt_cubic(x * gaussfac);
-
+
case R_FILTER_CATROM:
return filt_catrom(x * gaussfac);
}
@@ -221,20 +221,20 @@ void RE_parts_init(Render *re)
{
int nr, xd, yd, partx, party, xparts, yparts;
int xminb, xmaxb, yminb, ymaxb;
-
+
RE_parts_free(re);
-
+
/* this is render info for caller, is not reset when parts are freed! */
re->i.totpart = 0;
re->i.curpart = 0;
re->i.partsdone = 0;
-
+
/* just for readable code.. */
xminb = re->disprect.xmin;
yminb = re->disprect.ymin;
xmaxb = re->disprect.xmax;
ymaxb = re->disprect.ymax;
-
+
RE_parts_clamp(re);
partx = re->partx;
@@ -242,17 +242,17 @@ void RE_parts_init(Render *re)
/* part count */
xparts = (re->rectx + partx - 1) / partx;
yparts = (re->recty + party - 1) / party;
-
+
for (nr = 0; nr < xparts * yparts; nr++) {
rcti disprect;
int rectx, recty;
-
+
xd = (nr % xparts);
yd = (nr - xd) / xparts;
-
+
disprect.xmin = xminb + xd * partx;
disprect.ymin = yminb + yd * party;
-
+
/* ensure we cover the entire picture, so last parts go to end */
if (xd < xparts - 1) {
disprect.xmax = disprect.xmin + partx;
@@ -260,21 +260,21 @@ void RE_parts_init(Render *re)
disprect.xmax = xmaxb;
}
else disprect.xmax = xmaxb;
-
+
if (yd < yparts - 1) {
disprect.ymax = disprect.ymin + party;
if (disprect.ymax > ymaxb)
disprect.ymax = ymaxb;
}
else disprect.ymax = ymaxb;
-
+
rectx = BLI_rcti_size_x(&disprect);
recty = BLI_rcti_size_y(&disprect);
-
+
/* so, now can we add this part? */
if (rectx > 0 && recty > 0) {
RenderPart *pa = MEM_callocN(sizeof(RenderPart), "new part");
-
+
pa->disprect = disprect;
pa->rectx = rectx;
pa->recty = recty;
diff --git a/source/blender/render/intern/source/occlusion.c b/source/blender/render/intern/source/occlusion.c
new file mode 100644
index 00000000000..8aa90a390b3
--- /dev/null
+++ b/source/blender/render/intern/source/occlusion.c
@@ -0,0 +1,1533 @@
+/*
+ * ***** 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) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Brecht Van Lommel.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/occlusion.c
+ * \ingroup render
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_material_types.h"
+
+#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_node.h"
+#include "BKE_scene.h"
+
+
+#include "RE_shader_ext.h"
+
+/* local includes */
+#include "occlusion.h"
+#include "render_types.h"
+#include "rendercore.h"
+#include "renderdatabase.h"
+#include "shading.h"
+
+/* ------------------------- Declarations --------------------------- */
+
+#define INVPI ((float)M_1_PI)
+#define TOTCHILD 8
+#define CACHE_STEP 3
+
+typedef struct OcclusionCacheSample {
+ float co[3], n[3], ao[3], env[3], indirect[3], intensity, dist2;
+ int x, y, filled;
+} OcclusionCacheSample;
+
+typedef struct OcclusionCache {
+ OcclusionCacheSample *sample;
+ int x, y, w, h, step;
+} OcclusionCache;
+
+typedef struct OccFace {
+ int obi;
+ int facenr;
+} OccFace;
+
+typedef struct OccNode {
+ float co[3], area;
+ float sh[9], dco;
+ float occlusion, rad[3];
+ int childflag;
+ union {
+ //OccFace face;
+ int face;
+ struct OccNode *node;
+ } child[TOTCHILD];
+} OccNode;
+
+typedef struct OcclusionTree {
+ MemArena *arena;
+
+ float (*co)[3]; /* temporary during build */
+
+ OccFace *face; /* instance and face indices */
+ float *occlusion; /* occlusion for faces */
+ float (*rad)[3]; /* radiance for faces */
+
+ OccNode *root;
+
+ OccNode **stack[BLENDER_MAX_THREADS];
+ int maxdepth;
+
+ int totface;
+
+ float error;
+ float distfac;
+
+ int dothreadedbuild;
+ int totbuildthread;
+ int doindirect;
+
+ OcclusionCache *cache;
+
+ int num_threads;
+} OcclusionTree;
+
+typedef struct OcclusionThread {
+ Render *re;
+ StrandSurface *mesh;
+ float (*faceao)[3];
+ float (*faceenv)[3];
+ float (*faceindirect)[3];
+ int begin, end;
+ int thread;
+} OcclusionThread;
+
+typedef struct OcclusionBuildThread {
+ OcclusionTree *tree;
+ int begin, end, depth;
+ OccNode *node;
+} OcclusionBuildThread;
+
+/* ------------------------- Shading --------------------------- */
+
+extern Render R; /* meh */
+
+static void occ_shade(ShadeSample *ssamp, ObjectInstanceRen *obi, VlakRen *vlr, float *rad)
+{
+ ShadeInput *shi = ssamp->shi;
+ ShadeResult *shr = ssamp->shr;
+ float l, u, v, *v1, *v2, *v3;
+
+ /* init */
+ if (vlr->v4) {
+ shi->u = u = 0.5f;
+ shi->v = v = 0.5f;
+ }
+ else {
+ shi->u = u = 1.0f / 3.0f;
+ shi->v = v = 1.0f / 3.0f;
+ }
+
+ /* setup render coordinates */
+ v1 = vlr->v1->co;
+ v2 = vlr->v2->co;
+ v3 = vlr->v3->co;
+
+ /* renderco */
+ l = 1.0f - u - v;
+
+ shi->co[0] = l * v3[0] + u * v1[0] + v * v2[0];
+ shi->co[1] = l * v3[1] + u * v1[1] + v * v2[1];
+ shi->co[2] = l * v3[2] + u * v1[2] + v * v2[2];
+
+ shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2);
+
+ /* set up view vector */
+ copy_v3_v3(shi->view, shi->co);
+ normalize_v3(shi->view);
+
+ /* cache for shadow */
+ shi->samplenr++;
+
+ shi->xs = 0; /* TODO */
+ shi->ys = 0;
+
+ shade_input_set_normals(shi);
+
+ /* no normal flip */
+ if (shi->flippednor)
+ shade_input_flip_normals(shi);
+
+ madd_v3_v3fl(shi->co, shi->facenor, -0.0001f); /* ugly.. */
+
+ /* not a pretty solution, but fixes common cases */
+ if (shi->obr->ob && shi->obr->ob->transflag & OB_NEG_SCALE) {
+ negate_v3(shi->vn);
+ negate_v3(shi->vno);
+ negate_v3(shi->nmapnorm);
+ }
+
+ /* init material vars */
+ shade_input_init_material(shi);
+
+ /* render */
+ shade_input_set_shade_texco(shi);
+
+ if (shi->mat->nodetree && shi->mat->use_nodes) {
+ ntreeShaderExecTree(shi->mat->nodetree, shi, shr);
+ shi->mat = vlr->mat; /* shi->mat is being set in nodetree */
+ }
+ else {
+ shade_material_loop(shi, shr);
+ }
+
+ copy_v3_v3(rad, shr->combined);
+}
+
+static void occ_build_shade(Render *re, OcclusionTree *tree)
+{
+ ShadeSample ssamp;
+ ObjectInstanceRen *obi;
+ VlakRen *vlr;
+ int a;
+
+ R = *re;
+
+ /* setup shade sample with correct passes */
+ memset(&ssamp, 0, sizeof(ShadeSample));
+ ssamp.shi[0].lay = re->lay;
+ ssamp.shi[0].passflag = SCE_PASS_DIFFUSE | SCE_PASS_RGBA;
+ ssamp.shi[0].combinedflag = ~(SCE_PASS_SPEC);
+ ssamp.tot = 1;
+
+ for (a = 0; a < tree->totface; a++) {
+ obi = &R.objectinstance[tree->face[a].obi];
+ vlr = RE_findOrAddVlak(obi->obr, tree->face[a].facenr);
+
+ occ_shade(&ssamp, obi, vlr, tree->rad[a]);
+
+ if (re->test_break(re->tbh))
+ break;
+ }
+}
+
+/* ------------------------- Spherical Harmonics --------------------------- */
+
+/* Use 2nd order SH => 9 coefficients, stored in this order:
+ * 0 = (0,0),
+ * 1 = (1,-1), 2 = (1,0), 3 = (1,1),
+ * 4 = (2,-2), 5 = (2,-1), 6 = (2,0), 7 = (2,1), 8 = (2,2) */
+
+static void sh_copy(float *shresult, float *sh)
+{
+ memcpy(shresult, sh, sizeof(float) * 9);
+}
+
+static void sh_mul(float *sh, float f)
+{
+ int i;
+
+ for (i = 0; i < 9; i++)
+ sh[i] *= f;
+}
+
+static void sh_add(float *shresult, float *sh1, float *sh2)
+{
+ int i;
+
+ for (i = 0; i < 9; i++)
+ shresult[i] = sh1[i] + sh2[i];
+}
+
+static void sh_from_disc(float *n, float area, float *shresult)
+{
+ /* See formula (3) in:
+ * "An Efficient Representation for Irradiance Environment Maps" */
+ float sh[9], x, y, z;
+
+ x = n[0];
+ y = n[1];
+ z = n[2];
+
+ sh[0] = 0.282095f;
+
+ sh[1] = 0.488603f * y;
+ sh[2] = 0.488603f * z;
+ sh[3] = 0.488603f * x;
+
+ sh[4] = 1.092548f * x * y;
+ sh[5] = 1.092548f * y * z;
+ sh[6] = 0.315392f * (3.0f * z * z - 1.0f);
+ sh[7] = 1.092548f * x * z;
+ sh[8] = 0.546274f * (x * x - y * y);
+
+ sh_mul(sh, area);
+ sh_copy(shresult, sh);
+}
+
+static float sh_eval(float *sh, float *v)
+{
+ /* See formula (13) in:
+ * "An Efficient Representation for Irradiance Environment Maps" */
+ static const float c1 = 0.429043f, c2 = 0.511664f, c3 = 0.743125f;
+ static const float c4 = 0.886227f, c5 = 0.247708f;
+ float x, y, z, sum;
+
+ x = v[0];
+ y = v[1];
+ z = v[2];
+
+ sum = c1 * sh[8] * (x * x - y * y);
+ sum += c3 * sh[6] * z * z;
+ sum += c4 * sh[0];
+ sum += -c5 * sh[6];
+ sum += 2.0f * c1 * (sh[4] * x * y + sh[7] * x * z + sh[5] * y * z);
+ sum += 2.0f * c2 * (sh[3] * x + sh[1] * y + sh[2] * z);
+
+ return sum;
+}
+
+/* ------------------------------ Building --------------------------------- */
+
+static void occ_face(const OccFace *face, float co[3], float normal[3], float *area)
+{
+ ObjectInstanceRen *obi;
+ VlakRen *vlr;
+ float v1[3], v2[3], v3[3], v4[3];
+
+ obi = &R.objectinstance[face->obi];
+ vlr = RE_findOrAddVlak(obi->obr, face->facenr);
+
+ if (co) {
+ if (vlr->v4)
+ mid_v3_v3v3(co, vlr->v1->co, vlr->v3->co);
+ else
+ mid_v3_v3v3v3(co, vlr->v1->co, vlr->v2->co, vlr->v3->co);
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_v3(obi->mat, co);
+ }
+
+ if (normal) {
+ normal[0] = -vlr->n[0];
+ normal[1] = -vlr->n[1];
+ normal[2] = -vlr->n[2];
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m3_v3(obi->nmat, normal);
+ }
+
+ if (area) {
+ copy_v3_v3(v1, vlr->v1->co);
+ copy_v3_v3(v2, vlr->v2->co);
+ copy_v3_v3(v3, vlr->v3->co);
+ if (vlr->v4) copy_v3_v3(v4, vlr->v4->co);
+
+ if (obi->flag & R_TRANSFORMED) {
+ mul_m4_v3(obi->mat, v1);
+ mul_m4_v3(obi->mat, v2);
+ mul_m4_v3(obi->mat, v3);
+ if (vlr->v4) mul_m4_v3(obi->mat, v4);
+ }
+
+ /* todo: correct area for instances */
+ if (vlr->v4)
+ *area = area_quad_v3(v1, v2, v3, v4);
+ else
+ *area = area_tri_v3(v1, v2, v3);
+ }
+}
+
+static void occ_sum_occlusion(OcclusionTree *tree, OccNode *node)
+{
+ OccNode *child;
+ float occ, area, totarea, rad[3];
+ int a, b, indirect = tree->doindirect;
+
+ occ = 0.0f;
+ totarea = 0.0f;
+ if (indirect) zero_v3(rad);
+
+ for (b = 0; b < TOTCHILD; b++) {
+ if (node->childflag & (1 << b)) {
+ a = node->child[b].face;
+ occ_face(&tree->face[a], NULL, NULL, &area);
+ occ += area * tree->occlusion[a];
+ if (indirect) madd_v3_v3fl(rad, tree->rad[a], area);
+ totarea += area;
+ }
+ else if (node->child[b].node) {
+ child = node->child[b].node;
+ occ_sum_occlusion(tree, child);
+
+ occ += child->area * child->occlusion;
+ if (indirect) madd_v3_v3fl(rad, child->rad, child->area);
+ totarea += child->area;
+ }
+ }
+
+ if (totarea != 0.0f) {
+ occ /= totarea;
+ if (indirect) mul_v3_fl(rad, 1.0f / totarea);
+ }
+
+ node->occlusion = occ;
+ if (indirect) copy_v3_v3(node->rad, rad);
+}
+
+static int occ_find_bbox_axis(OcclusionTree *tree, int begin, int end, float *min, float *max)
+{
+ float len, maxlen = -1.0f;
+ int a, axis = 0;
+
+ INIT_MINMAX(min, max);
+
+ for (a = begin; a < end; a++) {
+ minmax_v3v3_v3(min, max, tree->co[a]);
+ }
+
+ for (a = 0; a < 3; a++) {
+ len = max[a] - min[a];
+
+ if (len > maxlen) {
+ maxlen = len;
+ axis = a;
+ }
+ }
+
+ return axis;
+}
+
+static void occ_node_from_face(OccFace *face, OccNode *node)
+{
+ float n[3];
+
+ occ_face(face, node->co, n, &node->area);
+ node->dco = 0.0f;
+ sh_from_disc(n, node->area, node->sh);
+}
+
+static void occ_build_dco(OcclusionTree *tree, OccNode *node, const float co[3], float *dco)
+{
+ int b;
+ for (b = 0; b < TOTCHILD; b++) {
+ float dist, d[3], nco[3];
+
+ if (node->childflag & (1 << b)) {
+ occ_face(tree->face + node->child[b].face, nco, NULL, NULL);
+ }
+ else if (node->child[b].node) {
+ OccNode *child = node->child[b].node;
+ occ_build_dco(tree, child, co, dco);
+ copy_v3_v3(nco, child->co);
+ }
+ else {
+ continue;
+ }
+
+ sub_v3_v3v3(d, nco, co);
+ dist = dot_v3v3(d, d);
+ if (dist > *dco)
+ *dco = dist;
+ }
+}
+
+static void occ_build_split(OcclusionTree *tree, int begin, int end, int *split)
+{
+ float min[3], max[3], mid;
+ int axis, a, enda;
+
+ /* split in middle of boundbox. this seems faster than median split
+ * on complex scenes, possibly since it avoids two distant faces to
+ * be in the same node better? */
+ axis = occ_find_bbox_axis(tree, begin, end, min, max);
+ mid = 0.5f * (min[axis] + max[axis]);
+
+ a = begin;
+ enda = end;
+ while (a < enda) {
+ if (tree->co[a][axis] > mid) {
+ enda--;
+ SWAP(OccFace, tree->face[a], tree->face[enda]);
+ swap_v3_v3(tree->co[a], tree->co[enda]);
+ }
+ else
+ a++;
+ }
+
+ *split = enda;
+}
+
+static void occ_build_8_split(OcclusionTree *tree, int begin, int end, int *offset, int *count)
+{
+ /* split faces into eight groups */
+ int b, splitx, splity[2], splitz[4];
+
+ occ_build_split(tree, begin, end, &splitx);
+
+ /* force split if none found, to deal with degenerate geometry */
+ if (splitx == begin || splitx == end)
+ splitx = (begin + end) / 2;
+
+ occ_build_split(tree, begin, splitx, &splity[0]);
+ occ_build_split(tree, splitx, end, &splity[1]);
+
+ occ_build_split(tree, begin, splity[0], &splitz[0]);
+ occ_build_split(tree, splity[0], splitx, &splitz[1]);
+ occ_build_split(tree, splitx, splity[1], &splitz[2]);
+ occ_build_split(tree, splity[1], end, &splitz[3]);
+
+ offset[0] = begin;
+ offset[1] = splitz[0];
+ offset[2] = splity[0];
+ offset[3] = splitz[1];
+ offset[4] = splitx;
+ offset[5] = splitz[2];
+ offset[6] = splity[1];
+ offset[7] = splitz[3];
+
+ for (b = 0; b < 7; b++)
+ count[b] = offset[b + 1] - offset[b];
+ count[7] = end - offset[7];
+}
+
+static void occ_build_recursive(OcclusionTree *tree, OccNode *node, int begin, int end, int depth);
+
+static void *exec_occ_build(void *data)
+{
+ OcclusionBuildThread *othread = (OcclusionBuildThread *)data;
+
+ occ_build_recursive(othread->tree, othread->node, othread->begin, othread->end, othread->depth);
+
+ return NULL;
+}
+
+static void occ_build_recursive(OcclusionTree *tree, OccNode *node, int begin, int end, int depth)
+{
+ ListBase threads;
+ OcclusionBuildThread othreads[BLENDER_MAX_THREADS];
+ OccNode *child, tmpnode;
+ /* OccFace *face; */
+ int a, b, totthread = 0, offset[TOTCHILD], count[TOTCHILD];
+
+ /* add a new node */
+ node->occlusion = 1.0f;
+
+ /* leaf node with only children */
+ if (end - begin <= TOTCHILD) {
+ for (a = begin, b = 0; a < end; a++, b++) {
+ /* face= &tree->face[a]; */
+ node->child[b].face = a;
+ node->childflag |= (1 << b);
+ }
+ }
+ else {
+ /* order faces */
+ occ_build_8_split(tree, begin, end, offset, count);
+
+ if (depth == 1 && tree->dothreadedbuild)
+ BLI_threadpool_init(&threads, exec_occ_build, tree->totbuildthread);
+
+ for (b = 0; b < TOTCHILD; b++) {
+ if (count[b] == 0) {
+ node->child[b].node = NULL;
+ }
+ else if (count[b] == 1) {
+ /* face= &tree->face[offset[b]]; */
+ node->child[b].face = offset[b];
+ node->childflag |= (1 << b);
+ }
+ else {
+ if (tree->dothreadedbuild)
+ BLI_thread_lock(LOCK_CUSTOM1);
+
+ child = BLI_memarena_alloc(tree->arena, sizeof(OccNode));
+ node->child[b].node = child;
+
+ /* keep track of maximum depth for stack */
+ if (depth >= tree->maxdepth)
+ tree->maxdepth = depth + 1;
+
+ if (tree->dothreadedbuild)
+ BLI_thread_unlock(LOCK_CUSTOM1);
+
+ if (depth == 1 && tree->dothreadedbuild) {
+ othreads[totthread].tree = tree;
+ othreads[totthread].node = child;
+ othreads[totthread].begin = offset[b];
+ othreads[totthread].end = offset[b] + count[b];
+ othreads[totthread].depth = depth + 1;
+ BLI_threadpool_insert(&threads, &othreads[totthread]);
+ totthread++;
+ }
+ else
+ occ_build_recursive(tree, child, offset[b], offset[b] + count[b], depth + 1);
+ }
+ }
+
+ if (depth == 1 && tree->dothreadedbuild)
+ BLI_threadpool_end(&threads);
+ }
+
+ /* combine area, position and sh */
+ for (b = 0; b < TOTCHILD; b++) {
+ if (node->childflag & (1 << b)) {
+ child = &tmpnode;
+ occ_node_from_face(tree->face + node->child[b].face, &tmpnode);
+ }
+ else {
+ child = node->child[b].node;
+ }
+
+ if (child) {
+ node->area += child->area;
+ sh_add(node->sh, node->sh, child->sh);
+ madd_v3_v3fl(node->co, child->co, child->area);
+ }
+ }
+
+ if (node->area != 0.0f)
+ mul_v3_fl(node->co, 1.0f / node->area);
+
+ /* compute maximum distance from center */
+ node->dco = 0.0f;
+ if (node->area > 0.0f)
+ occ_build_dco(tree, node, node->co, &node->dco);
+}
+
+static void occ_build_sh_normalize(OccNode *node)
+{
+ /* normalize spherical harmonics to not include area, so
+ * we can clamp the dot product and then multiply by area */
+ int b;
+
+ if (node->area != 0.0f)
+ sh_mul(node->sh, 1.0f / node->area);
+
+ for (b = 0; b < TOTCHILD; b++) {
+ if (node->childflag & (1 << b)) {
+ /* pass */
+ }
+ else if (node->child[b].node) {
+ occ_build_sh_normalize(node->child[b].node);
+ }
+ }
+}
+
+static OcclusionTree *occ_tree_build(Render *re)
+{
+ const int num_threads = re->r.threads;
+ OcclusionTree *tree;
+ ObjectInstanceRen *obi;
+ ObjectRen *obr;
+ Material *ma;
+ VlakRen *vlr = NULL;
+ int a, b, c, totface;
+
+ /* count */
+ totface = 0;
+ for (obi = re->instancetable.first; obi; obi = obi->next) {
+ obr = obi->obr;
+ for (a = 0; a < obr->totvlak; a++) {
+ if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
+ else vlr++;
+
+ ma = vlr->mat;
+
+ if ((ma->shade_flag & MA_APPROX_OCCLUSION) && (ma->material_type == MA_TYPE_SURFACE))
+ totface++;
+ }
+ }
+
+ if (totface == 0)
+ return NULL;
+
+ tree = MEM_callocN(sizeof(OcclusionTree), "OcclusionTree");
+ tree->totface = totface;
+
+ /* parameters */
+ tree->error = get_render_aosss_error(&re->r, re->wrld.ao_approx_error);
+ tree->distfac = (re->wrld.aomode & WO_AODIST) ? re->wrld.aodistfac : 0.0f;
+ tree->doindirect = (re->wrld.ao_indirect_energy > 0.0f && re->wrld.ao_indirect_bounces > 0);
+
+ /* allocation */
+ tree->arena = BLI_memarena_new(0x8000 * sizeof(OccNode), "occ tree arena");
+ BLI_memarena_use_calloc(tree->arena);
+
+ if (re->wrld.aomode & WO_AOCACHE)
+ tree->cache = MEM_callocN(sizeof(OcclusionCache) * num_threads, "OcclusionCache");
+
+ tree->face = MEM_callocN(sizeof(OccFace) * totface, "OcclusionFace");
+ tree->co = MEM_callocN(sizeof(float) * 3 * totface, "OcclusionCo");
+ tree->occlusion = MEM_callocN(sizeof(float) * totface, "OcclusionOcclusion");
+
+ if (tree->doindirect)
+ tree->rad = MEM_callocN(sizeof(float) * 3 * totface, "OcclusionRad");
+
+ /* make array of face pointers */
+ for (b = 0, c = 0, obi = re->instancetable.first; obi; obi = obi->next, c++) {
+ obr = obi->obr;
+ for (a = 0; a < obr->totvlak; a++) {
+ if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
+ else vlr++;
+
+ ma = vlr->mat;
+
+ if ((ma->shade_flag & MA_APPROX_OCCLUSION) && (ma->material_type == MA_TYPE_SURFACE)) {
+ tree->face[b].obi = c;
+ tree->face[b].facenr = a;
+ tree->occlusion[b] = 1.0f;
+ occ_face(&tree->face[b], tree->co[b], NULL, NULL);
+ b++;
+ }
+ }
+ }
+
+ /* threads */
+ tree->totbuildthread = (re->r.threads > 1 && totface > 10000) ? 8 : 1;
+ tree->dothreadedbuild = (tree->totbuildthread > 1);
+
+ /* recurse */
+ tree->root = BLI_memarena_alloc(tree->arena, sizeof(OccNode));
+ tree->maxdepth = 1;
+ occ_build_recursive(tree, tree->root, 0, totface, 1);
+
+ if (tree->doindirect) {
+ if (!(re->test_break(re->tbh)))
+ occ_build_shade(re, tree);
+
+ if (!(re->test_break(re->tbh)))
+ occ_sum_occlusion(tree, tree->root);
+ }
+
+ MEM_freeN(tree->co);
+ tree->co = NULL;
+
+ if (!(re->test_break(re->tbh)))
+ occ_build_sh_normalize(tree->root);
+
+ for (a = 0; a < num_threads; a++)
+ tree->stack[a] = MEM_callocN(sizeof(OccNode) * TOTCHILD * (tree->maxdepth + 1), "OccStack");
+
+ tree->num_threads = num_threads;
+
+ return tree;
+}
+
+static void occ_free_tree(OcclusionTree *tree)
+{
+ int a;
+
+ if (tree) {
+ if (tree->arena) BLI_memarena_free(tree->arena);
+ for (a = 0; a < tree->num_threads; a++)
+ if (tree->stack[a])
+ MEM_freeN(tree->stack[a]);
+ if (tree->occlusion) MEM_freeN(tree->occlusion);
+ if (tree->cache) MEM_freeN(tree->cache);
+ if (tree->face) MEM_freeN(tree->face);
+ if (tree->rad) MEM_freeN(tree->rad);
+ MEM_freeN(tree);
+ }
+}
+
+/* ------------------------- Traversal --------------------------- */
+
+static float occ_solid_angle(OccNode *node, const float v[3], float d2, float invd2, const float receivenormal[3])
+{
+ float dotreceive, dotemit;
+ float ev[3];
+
+ ev[0] = -v[0] * invd2;
+ ev[1] = -v[1] * invd2;
+ ev[2] = -v[2] * invd2;
+ dotemit = sh_eval(node->sh, ev);
+ dotreceive = dot_v3v3(receivenormal, v) * invd2;
+
+ CLAMP(dotemit, 0.0f, 1.0f);
+ CLAMP(dotreceive, 0.0f, 1.0f);
+
+ return ((node->area * dotemit * dotreceive) / (d2 + node->area * INVPI)) * INVPI;
+}
+
+static float occ_form_factor(OccFace *face, float *p, float *n)
+{
+ ObjectInstanceRen *obi;
+ VlakRen *vlr;
+ float v1[3], v2[3], v3[3], v4[3], q0[3], q1[3], q2[3], q3[3], contrib = 0.0f;
+
+ obi = &R.objectinstance[face->obi];
+ vlr = RE_findOrAddVlak(obi->obr, face->facenr);
+
+ copy_v3_v3(v1, vlr->v1->co);
+ copy_v3_v3(v2, vlr->v2->co);
+ copy_v3_v3(v3, vlr->v3->co);
+
+ if (obi->flag & R_TRANSFORMED) {
+ mul_m4_v3(obi->mat, v1);
+ mul_m4_v3(obi->mat, v2);
+ mul_m4_v3(obi->mat, v3);
+ }
+
+ if (form_factor_visible_quad(p, n, v1, v2, v3, q0, q1, q2, q3))
+ contrib += form_factor_quad(p, n, q0, q1, q2, q3);
+
+ if (vlr->v4) {
+ copy_v3_v3(v4, vlr->v4->co);
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_v3(obi->mat, v4);
+
+ if (form_factor_visible_quad(p, n, v1, v3, v4, q0, q1, q2, q3))
+ contrib += form_factor_quad(p, n, q0, q1, q2, q3);
+ }
+
+ return contrib;
+}
+
+static void occ_lookup(OcclusionTree *tree, int thread, OccFace *exclude,
+ const float pp[3], const float pn[3], float *occ, float rad[3], float bentn[3])
+{
+ OccNode *node, **stack;
+ OccFace *face;
+ float resultocc, resultrad[3], v[3], p[3], n[3], co[3], invd2;
+ float distfac, fac, error, d2, weight, emitarea;
+ int b, f, totstack;
+
+ /* init variables */
+ copy_v3_v3(p, pp);
+ copy_v3_v3(n, pn);
+ madd_v3_v3fl(p, n, 1e-4f);
+
+ if (bentn)
+ copy_v3_v3(bentn, n);
+
+ error = tree->error;
+ distfac = tree->distfac;
+
+ resultocc = 0.0f;
+ zero_v3(resultrad);
+
+ /* init stack */
+ stack = tree->stack[thread];
+ stack[0] = tree->root;
+ totstack = 1;
+
+ while (totstack) {
+ /* pop point off the stack */
+ node = stack[--totstack];
+
+ sub_v3_v3v3(v, node->co, p);
+ d2 = dot_v3v3(v, v) + 1e-16f;
+ emitarea = MAX2(node->area, node->dco);
+
+ if (d2 * error > emitarea) {
+ if (distfac != 0.0f) {
+ fac = 1.0f / (1.0f + distfac * d2);
+ if (fac < 0.01f)
+ continue;
+ }
+ else
+ fac = 1.0f;
+
+ /* accumulate occlusion from spherical harmonics */
+ invd2 = 1.0f / sqrtf(d2);
+ weight = occ_solid_angle(node, v, d2, invd2, n);
+
+ if (rad)
+ madd_v3_v3fl(resultrad, node->rad, weight * fac);
+
+ weight *= node->occlusion;
+
+ if (bentn) {
+ bentn[0] -= weight * invd2 * v[0];
+ bentn[1] -= weight * invd2 * v[1];
+ bentn[2] -= weight * invd2 * v[2];
+ }
+
+ resultocc += weight * fac;
+ }
+ else {
+ /* traverse into children */
+ for (b = 0; b < TOTCHILD; b++) {
+ if (node->childflag & (1 << b)) {
+ f = node->child[b].face;
+ face = &tree->face[f];
+
+ /* accumulate occlusion with face form factor */
+ if (!exclude || !(face->obi == exclude->obi && face->facenr == exclude->facenr)) {
+ if (bentn || distfac != 0.0f) {
+ occ_face(face, co, NULL, NULL);
+ sub_v3_v3v3(v, co, p);
+ d2 = dot_v3v3(v, v) + 1e-16f;
+
+ fac = (distfac == 0.0f) ? 1.0f : 1.0f / (1.0f + distfac * d2);
+ if (fac < 0.01f)
+ continue;
+ }
+ else
+ fac = 1.0f;
+
+ weight = occ_form_factor(face, p, n);
+
+ if (rad)
+ madd_v3_v3fl(resultrad, tree->rad[f], weight * fac);
+
+ weight *= tree->occlusion[f];
+
+ if (bentn) {
+ invd2 = 1.0f / sqrtf(d2);
+ bentn[0] -= weight * invd2 * v[0];
+ bentn[1] -= weight * invd2 * v[1];
+ bentn[2] -= weight * invd2 * v[2];
+ }
+
+ resultocc += weight * fac;
+ }
+ }
+ else if (node->child[b].node) {
+ /* push child on the stack */
+ stack[totstack++] = node->child[b].node;
+ }
+ }
+ }
+ }
+
+ if (occ) *occ = resultocc;
+ if (rad) copy_v3_v3(rad, resultrad);
+#if 0
+ if (rad && exclude) {
+ int a;
+ for (a = 0; a < tree->totface; a++)
+ if ((tree->face[a].obi == exclude->obi && tree->face[a].facenr == exclude->facenr))
+ copy_v3_v3(rad, tree->rad[a]);
+ }
+#endif
+ if (bentn) normalize_v3(bentn);
+}
+
+static void occ_compute_bounces(Render *re, OcclusionTree *tree, int totbounce)
+{
+ float (*rad)[3], (*sum)[3], (*tmp)[3], co[3], n[3], occ;
+ int bounce, i;
+
+ rad = MEM_callocN(sizeof(float) * 3 * tree->totface, "OcclusionBounceRad");
+ sum = MEM_dupallocN(tree->rad);
+
+ for (bounce = 1; bounce < totbounce; bounce++) {
+ for (i = 0; i < tree->totface; i++) {
+ occ_face(&tree->face[i], co, n, NULL);
+ madd_v3_v3fl(co, n, 1e-8f);
+
+ occ_lookup(tree, 0, &tree->face[i], co, n, &occ, rad[i], NULL);
+ rad[i][0] = MAX2(rad[i][0], 0.0f);
+ rad[i][1] = MAX2(rad[i][1], 0.0f);
+ rad[i][2] = MAX2(rad[i][2], 0.0f);
+ add_v3_v3(sum[i], rad[i]);
+
+ if (re->test_break(re->tbh))
+ break;
+ }
+
+ if (re->test_break(re->tbh))
+ break;
+
+ tmp = tree->rad;
+ tree->rad = rad;
+ rad = tmp;
+
+ occ_sum_occlusion(tree, tree->root);
+ }
+
+ MEM_freeN(rad);
+ MEM_freeN(tree->rad);
+ tree->rad = sum;
+
+ if (!re->test_break(re->tbh))
+ occ_sum_occlusion(tree, tree->root);
+}
+
+static void occ_compute_passes(Render *re, OcclusionTree *tree, int totpass)
+{
+ float *occ, co[3], n[3];
+ int pass, i;
+
+ occ = MEM_callocN(sizeof(float) * tree->totface, "OcclusionPassOcc");
+
+ for (pass = 0; pass < totpass; pass++) {
+ for (i = 0; i < tree->totface; i++) {
+ occ_face(&tree->face[i], co, n, NULL);
+ negate_v3(n);
+ madd_v3_v3fl(co, n, 1e-8f);
+
+ occ_lookup(tree, 0, &tree->face[i], co, n, &occ[i], NULL, NULL);
+ if (re->test_break(re->tbh))
+ break;
+ }
+
+ if (re->test_break(re->tbh))
+ break;
+
+ for (i = 0; i < tree->totface; i++) {
+ tree->occlusion[i] -= occ[i]; //MAX2(1.0f-occ[i], 0.0f);
+ if (tree->occlusion[i] < 0.0f)
+ tree->occlusion[i] = 0.0f;
+ }
+
+ occ_sum_occlusion(tree, tree->root);
+ }
+
+ MEM_freeN(occ);
+}
+
+static void sample_occ_tree(Render *re, OcclusionTree *tree, OccFace *exclude,
+ const float co[3], const float n[3], int thread, int onlyshadow,
+ float *ao, float *env, float *indirect)
+{
+ float nn[3], bn[3], fac, occ, occlusion, correction, rad[3];
+ int envcolor;
+
+ envcolor = re->wrld.aocolor;
+ if (onlyshadow)
+ envcolor = WO_AOPLAIN;
+
+ negate_v3_v3(nn, n);
+
+ occ_lookup(tree, thread, exclude, co, nn, &occ, (tree->doindirect) ? rad : NULL, (env && envcolor) ? bn : NULL);
+
+ correction = re->wrld.ao_approx_correction;
+
+ occlusion = (1.0f - correction) * (1.0f - occ);
+ CLAMP(occlusion, 0.0f, 1.0f);
+ if (correction != 0.0f)
+ occlusion += correction * expf(-occ);
+
+ if (env) {
+ /* sky shading using bent normal */
+ if (ELEM(envcolor, WO_AOSKYCOL, WO_AOSKYTEX)) {
+ fac = 0.5f * (1.0f + dot_v3v3(bn, re->grvec));
+ env[0] = (1.0f - fac) * re->wrld.horr + fac * re->wrld.zenr;
+ env[1] = (1.0f - fac) * re->wrld.horg + fac * re->wrld.zeng;
+ env[2] = (1.0f - fac) * re->wrld.horb + fac * re->wrld.zenb;
+
+ mul_v3_fl(env, occlusion);
+ }
+ else {
+ env[0] = occlusion;
+ env[1] = occlusion;
+ env[2] = occlusion;
+ }
+#if 0
+ else { /* WO_AOSKYTEX */
+ float dxyview[3];
+ bn[0] = -bn[0];
+ bn[1] = -bn[1];
+ bn[2] = -bn[2];
+ dxyview[0] = 1.0f;
+ dxyview[1] = 1.0f;
+ dxyview[2] = 0.0f;
+ shadeSkyView(ao, co, bn, dxyview);
+ }
+#endif
+ }
+
+ if (ao) {
+ ao[0] = occlusion;
+ ao[1] = occlusion;
+ ao[2] = occlusion;
+ }
+
+ if (tree->doindirect) copy_v3_v3(indirect, rad);
+ else zero_v3(indirect);
+}
+
+/* ---------------------------- Caching ------------------------------- */
+
+static OcclusionCacheSample *find_occ_sample(OcclusionCache *cache, int x, int y)
+{
+ x -= cache->x;
+ y -= cache->y;
+
+ x /= cache->step;
+ y /= cache->step;
+ x *= cache->step;
+ y *= cache->step;
+
+ if (x < 0 || x >= cache->w || y < 0 || y >= cache->h)
+ return NULL;
+ else
+ return &cache->sample[y * cache->w + x];
+}
+
+static int sample_occ_cache(OcclusionTree *tree, float *co, float *n, int x, int y, int thread, float *ao, float *env, float *indirect)
+{
+ OcclusionCache *cache;
+ OcclusionCacheSample *samples[4], *sample;
+ float wn[4], wz[4], wb[4], tx, ty, w, totw, mino, maxo;
+ float d[3], dist2;
+ int i, x1, y1, x2, y2;
+
+ if (!tree->cache)
+ return 0;
+
+ /* first try to find a sample in the same pixel */
+ cache = &tree->cache[thread];
+
+ if (cache->sample && cache->step) {
+ sample = &cache->sample[(y - cache->y) * cache->w + (x - cache->x)];
+ if (sample->filled) {
+ sub_v3_v3v3(d, sample->co, co);
+ dist2 = dot_v3v3(d, d);
+ if (dist2 < 0.5f * sample->dist2 && dot_v3v3(sample->n, n) > 0.98f) {
+ copy_v3_v3(ao, sample->ao);
+ copy_v3_v3(env, sample->env);
+ copy_v3_v3(indirect, sample->indirect);
+ return 1;
+ }
+ }
+ }
+ else
+ return 0;
+
+ /* try to interpolate between 4 neighboring pixels */
+ samples[0] = find_occ_sample(cache, x, y);
+ samples[1] = find_occ_sample(cache, x + cache->step, y);
+ samples[2] = find_occ_sample(cache, x, y + cache->step);
+ samples[3] = find_occ_sample(cache, x + cache->step, y + cache->step);
+
+ for (i = 0; i < 4; i++)
+ if (!samples[i] || !samples[i]->filled)
+ return 0;
+
+ /* require intensities not being too different */
+ mino = min_ffff(samples[0]->intensity, samples[1]->intensity, samples[2]->intensity, samples[3]->intensity);
+ maxo = max_ffff(samples[0]->intensity, samples[1]->intensity, samples[2]->intensity, samples[3]->intensity);
+
+ if (maxo - mino > 0.05f)
+ return 0;
+
+ /* compute weighted interpolation between samples */
+ zero_v3(ao);
+ zero_v3(env);
+ zero_v3(indirect);
+ totw = 0.0f;
+
+ x1 = samples[0]->x;
+ y1 = samples[0]->y;
+ x2 = samples[3]->x;
+ y2 = samples[3]->y;
+
+ tx = (float)(x2 - x) / (float)(x2 - x1);
+ ty = (float)(y2 - y) / (float)(y2 - y1);
+
+ wb[3] = (1.0f - tx) * (1.0f - ty);
+ wb[2] = (tx) * (1.0f - ty);
+ wb[1] = (1.0f - tx) * (ty);
+ wb[0] = tx * ty;
+
+ for (i = 0; i < 4; i++) {
+ sub_v3_v3v3(d, samples[i]->co, co);
+ //dist2 = dot_v3v3(d, d);
+
+ wz[i] = 1.0f; //(samples[i]->dist2/(1e-4f + dist2));
+ wn[i] = pow(dot_v3v3(samples[i]->n, n), 32.0f);
+
+ w = wb[i] * wn[i] * wz[i];
+
+ totw += w;
+ madd_v3_v3fl(ao, samples[i]->ao, w);
+ madd_v3_v3fl(env, samples[i]->env, w);
+ madd_v3_v3fl(indirect, samples[i]->indirect, w);
+ }
+
+ if (totw >= 0.9f) {
+ totw = 1.0f / totw;
+ mul_v3_fl(ao, totw);
+ mul_v3_fl(env, totw);
+ mul_v3_fl(indirect, totw);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void sample_occ_surface(ShadeInput *shi)
+{
+ StrandRen *strand = shi->strand;
+ StrandSurface *mesh = strand->buffer->surface;
+ const int *face, *index = RE_strandren_get_face(shi->obr, strand, 0);
+ float w[4], *co1, *co2, *co3, *co4;
+
+ if (mesh && mesh->face && mesh->co && mesh->ao && index) {
+ face = mesh->face[*index];
+
+ co1 = mesh->co[face[0]];
+ co2 = mesh->co[face[1]];
+ co3 = mesh->co[face[2]];
+
+ if (face[3]) {
+ co4 = mesh->co[face[3]];
+ interp_weights_quad_v3(w, co1, co2, co3, co4, strand->vert->co);
+ }
+ else {
+ interp_weights_tri_v3(w, co1, co2, co3, strand->vert->co);
+ }
+
+ zero_v3(shi->ao);
+ zero_v3(shi->env);
+ zero_v3(shi->indirect);
+
+ madd_v3_v3fl(shi->ao, mesh->ao[face[0]], w[0]);
+ madd_v3_v3fl(shi->env, mesh->env[face[0]], w[0]);
+ madd_v3_v3fl(shi->indirect, mesh->indirect[face[0]], w[0]);
+ madd_v3_v3fl(shi->ao, mesh->ao[face[1]], w[1]);
+ madd_v3_v3fl(shi->env, mesh->env[face[1]], w[1]);
+ madd_v3_v3fl(shi->indirect, mesh->indirect[face[1]], w[1]);
+ madd_v3_v3fl(shi->ao, mesh->ao[face[2]], w[2]);
+ madd_v3_v3fl(shi->env, mesh->env[face[2]], w[2]);
+ madd_v3_v3fl(shi->indirect, mesh->indirect[face[2]], w[2]);
+ if (face[3]) {
+ madd_v3_v3fl(shi->ao, mesh->ao[face[3]], w[3]);
+ madd_v3_v3fl(shi->env, mesh->env[face[3]], w[3]);
+ madd_v3_v3fl(shi->indirect, mesh->indirect[face[3]], w[3]);
+ }
+ }
+ else {
+ shi->ao[0] = 1.0f;
+ shi->ao[1] = 1.0f;
+ shi->ao[2] = 1.0f;
+ zero_v3(shi->env);
+ zero_v3(shi->indirect);
+ }
+}
+
+/* ------------------------- External Functions --------------------------- */
+
+static void *exec_strandsurface_sample(void *data)
+{
+ OcclusionThread *othread = (OcclusionThread *)data;
+ Render *re = othread->re;
+ StrandSurface *mesh = othread->mesh;
+ float ao[3], env[3], indirect[3], co[3], n[3], *co1, *co2, *co3, *co4;
+ int a, *face;
+
+ for (a = othread->begin; a < othread->end; a++) {
+ face = mesh->face[a];
+ co1 = mesh->co[face[0]];
+ co2 = mesh->co[face[1]];
+ co3 = mesh->co[face[2]];
+
+ if (face[3]) {
+ co4 = mesh->co[face[3]];
+
+ mid_v3_v3v3(co, co1, co3);
+ normal_quad_v3(n, co1, co2, co3, co4);
+ }
+ else {
+ mid_v3_v3v3v3(co, co1, co2, co3);
+ normal_tri_v3(n, co1, co2, co3);
+ }
+ negate_v3(n);
+
+ sample_occ_tree(re, re->occlusiontree, NULL, co, n, othread->thread, 0, ao, env, indirect);
+ copy_v3_v3(othread->faceao[a], ao);
+ copy_v3_v3(othread->faceenv[a], env);
+ copy_v3_v3(othread->faceindirect[a], indirect);
+ }
+
+ return NULL;
+}
+
+void make_occ_tree(Render *re)
+{
+ OcclusionThread othreads[BLENDER_MAX_THREADS];
+ OcclusionTree *tree;
+ StrandSurface *mesh;
+ ListBase threads;
+ float ao[3], env[3], indirect[3], (*faceao)[3], (*faceenv)[3], (*faceindirect)[3];
+ int a, totface, totthread, *face, *count;
+
+ /* ugly, needed for occ_face */
+ R = *re;
+
+ re->i.infostr = IFACE_("Occlusion preprocessing");
+ re->stats_draw(re->sdh, &re->i);
+
+ re->occlusiontree = tree = occ_tree_build(re);
+
+ if (tree && !re->test_break(re->tbh)) {
+ if (re->wrld.ao_approx_passes > 0)
+ occ_compute_passes(re, tree, re->wrld.ao_approx_passes);
+ if (tree->doindirect && (re->wrld.mode & WO_INDIRECT_LIGHT))
+ occ_compute_bounces(re, tree, re->wrld.ao_indirect_bounces);
+
+ for (mesh = re->strandsurface.first; mesh; mesh = mesh->next) {
+ if (!mesh->face || !mesh->co || !mesh->ao)
+ continue;
+
+ count = MEM_callocN(sizeof(int) * mesh->totvert, "OcclusionCount");
+ faceao = MEM_callocN(sizeof(float) * 3 * mesh->totface, "StrandSurfFaceAO");
+ faceenv = MEM_callocN(sizeof(float) * 3 * mesh->totface, "StrandSurfFaceEnv");
+ faceindirect = MEM_callocN(sizeof(float) * 3 * mesh->totface, "StrandSurfFaceIndirect");
+
+ totthread = (mesh->totface > 10000) ? re->r.threads : 1;
+ totface = mesh->totface / totthread;
+ for (a = 0; a < totthread; a++) {
+ othreads[a].re = re;
+ othreads[a].faceao = faceao;
+ othreads[a].faceenv = faceenv;
+ othreads[a].faceindirect = faceindirect;
+ othreads[a].thread = a;
+ othreads[a].mesh = mesh;
+ othreads[a].begin = a * totface;
+ othreads[a].end = (a == totthread - 1) ? mesh->totface : (a + 1) * totface;
+ }
+
+ if (totthread == 1) {
+ exec_strandsurface_sample(&othreads[0]);
+ }
+ else {
+ BLI_threadpool_init(&threads, exec_strandsurface_sample, totthread);
+
+ for (a = 0; a < totthread; a++)
+ BLI_threadpool_insert(&threads, &othreads[a]);
+
+ BLI_threadpool_end(&threads);
+ }
+
+ for (a = 0; a < mesh->totface; a++) {
+ face = mesh->face[a];
+
+ copy_v3_v3(ao, faceao[a]);
+ copy_v3_v3(env, faceenv[a]);
+ copy_v3_v3(indirect, faceindirect[a]);
+
+ add_v3_v3(mesh->ao[face[0]], ao);
+ add_v3_v3(mesh->env[face[0]], env);
+ add_v3_v3(mesh->indirect[face[0]], indirect);
+ count[face[0]]++;
+ add_v3_v3(mesh->ao[face[1]], ao);
+ add_v3_v3(mesh->env[face[1]], env);
+ add_v3_v3(mesh->indirect[face[1]], indirect);
+ count[face[1]]++;
+ add_v3_v3(mesh->ao[face[2]], ao);
+ add_v3_v3(mesh->env[face[2]], env);
+ add_v3_v3(mesh->indirect[face[2]], indirect);
+ count[face[2]]++;
+
+ if (face[3]) {
+ add_v3_v3(mesh->ao[face[3]], ao);
+ add_v3_v3(mesh->env[face[3]], env);
+ add_v3_v3(mesh->indirect[face[3]], indirect);
+ count[face[3]]++;
+ }
+ }
+
+ for (a = 0; a < mesh->totvert; a++) {
+ if (count[a]) {
+ mul_v3_fl(mesh->ao[a], 1.0f / count[a]);
+ mul_v3_fl(mesh->env[a], 1.0f / count[a]);
+ mul_v3_fl(mesh->indirect[a], 1.0f / count[a]);
+ }
+ }
+
+ MEM_freeN(count);
+ MEM_freeN(faceao);
+ MEM_freeN(faceenv);
+ MEM_freeN(faceindirect);
+ }
+ }
+}
+
+void free_occ(Render *re)
+{
+ if (re->occlusiontree) {
+ occ_free_tree(re->occlusiontree);
+ re->occlusiontree = NULL;
+ }
+}
+
+void sample_occ(Render *re, ShadeInput *shi)
+{
+ OcclusionTree *tree = re->occlusiontree;
+ OcclusionCache *cache;
+ OcclusionCacheSample *sample;
+ OccFace exclude;
+ int onlyshadow;
+
+ if (tree) {
+ if (shi->strand) {
+ sample_occ_surface(shi);
+ }
+ /* try to get result from the cache if possible */
+ else if (shi->depth != 0 || !sample_occ_cache(tree, shi->co, shi->vno, shi->xs, shi->ys, shi->thread, shi->ao, shi->env, shi->indirect)) {
+ /* no luck, let's sample the occlusion */
+ exclude.obi = shi->obi - re->objectinstance;
+ exclude.facenr = shi->vlr->index;
+ onlyshadow = (shi->mat->mode & MA_ONLYSHADOW);
+ sample_occ_tree(re, tree, &exclude, shi->co, shi->vno, shi->thread, onlyshadow, shi->ao, shi->env, shi->indirect);
+
+ /* fill result into sample, each time */
+ if (tree->cache) {
+ cache = &tree->cache[shi->thread];
+
+ if (cache->sample && cache->step) {
+ sample = &cache->sample[(shi->ys - cache->y) * cache->w + (shi->xs - cache->x)];
+ copy_v3_v3(sample->co, shi->co);
+ copy_v3_v3(sample->n, shi->vno);
+ copy_v3_v3(sample->ao, shi->ao);
+ copy_v3_v3(sample->env, shi->env);
+ copy_v3_v3(sample->indirect, shi->indirect);
+ sample->intensity = max_fff(sample->ao[0], sample->ao[1], sample->ao[2]);
+ sample->intensity = max_ff(sample->intensity, max_fff(sample->env[0], sample->env[1], sample->env[2]));
+ sample->intensity = max_ff(sample->intensity, max_fff(sample->indirect[0], sample->indirect[1], sample->indirect[2]));
+ sample->dist2 = dot_v3v3(shi->dxco, shi->dxco) + dot_v3v3(shi->dyco, shi->dyco);
+ sample->filled = 1;
+ }
+ }
+ }
+ }
+ else {
+ shi->ao[0] = 1.0f;
+ shi->ao[1] = 1.0f;
+ shi->ao[2] = 1.0f;
+
+ shi->env[0] = 0.0f;
+ shi->env[1] = 0.0f;
+ shi->env[2] = 0.0f;
+
+ shi->indirect[0] = 0.0f;
+ shi->indirect[1] = 0.0f;
+ shi->indirect[2] = 0.0f;
+ }
+}
+
+void cache_occ_samples(Render *re, RenderPart *pa, ShadeSample *ssamp)
+{
+ OcclusionTree *tree = re->occlusiontree;
+ PixStr ps;
+ OcclusionCache *cache;
+ OcclusionCacheSample *sample;
+ OccFace exclude;
+ ShadeInput *shi;
+ intptr_t *rd = NULL;
+ int *ro = NULL, *rp = NULL, *rz = NULL, onlyshadow;
+ int x, y, step = CACHE_STEP;
+
+ if (!tree->cache)
+ return;
+
+ cache = &tree->cache[pa->thread];
+ cache->w = pa->rectx;
+ cache->h = pa->recty;
+ cache->x = pa->disprect.xmin;
+ cache->y = pa->disprect.ymin;
+ cache->step = step;
+ cache->sample = MEM_callocN(sizeof(OcclusionCacheSample) * cache->w * cache->h, "OcclusionCacheSample");
+ sample = cache->sample;
+
+ if (re->osa) {
+ rd = pa->rectdaps;
+ }
+ else {
+ /* fake pixel struct for non-osa */
+ ps.next = NULL;
+ ps.mask = 0xFFFF;
+
+ ro = pa->recto;
+ rp = pa->rectp;
+ rz = pa->rectz;
+ }
+
+ /* compute a sample at every step pixels */
+ for (y = pa->disprect.ymin; y < pa->disprect.ymax; y++) {
+ for (x = pa->disprect.xmin; x < pa->disprect.xmax; x++, sample++, rd++, ro++, rp++, rz++) {
+ if (!(((x - pa->disprect.xmin + step) % step) == 0 || x == pa->disprect.xmax - 1))
+ continue;
+ if (!(((y - pa->disprect.ymin + step) % step) == 0 || y == pa->disprect.ymax - 1))
+ continue;
+
+ if (re->osa) {
+ if (!*rd) continue;
+
+ shade_samples_fill_with_ps(ssamp, (PixStr *)(*rd), x, y);
+ }
+ else {
+ if (!*rp) continue;
+
+ ps.obi = *ro;
+ ps.facenr = *rp;
+ ps.z = *rz;
+ shade_samples_fill_with_ps(ssamp, &ps, x, y);
+ }
+
+ shi = ssamp->shi;
+ if (shi->vlr) {
+ onlyshadow = (shi->mat->mode & MA_ONLYSHADOW);
+ exclude.obi = shi->obi - re->objectinstance;
+ exclude.facenr = shi->vlr->index;
+ sample_occ_tree(re, tree, &exclude, shi->co, shi->vno, shi->thread, onlyshadow, shi->ao, shi->env, shi->indirect);
+
+ copy_v3_v3(sample->co, shi->co);
+ copy_v3_v3(sample->n, shi->vno);
+ copy_v3_v3(sample->ao, shi->ao);
+ copy_v3_v3(sample->env, shi->env);
+ copy_v3_v3(sample->indirect, shi->indirect);
+ sample->intensity = max_fff(sample->ao[0], sample->ao[1], sample->ao[2]);
+ sample->intensity = max_ff(sample->intensity, max_fff(sample->env[0], sample->env[1], sample->env[2]));
+ sample->intensity = max_ff(sample->intensity, max_fff(sample->indirect[0], sample->indirect[1], sample->indirect[2]));
+ sample->dist2 = dot_v3v3(shi->dxco, shi->dxco) + dot_v3v3(shi->dyco, shi->dyco);
+ sample->x = shi->xs;
+ sample->y = shi->ys;
+ sample->filled = 1;
+ }
+
+ if (re->test_break(re->tbh))
+ break;
+ }
+ }
+}
+
+void free_occ_samples(Render *re, RenderPart *pa)
+{
+ OcclusionTree *tree = re->occlusiontree;
+ OcclusionCache *cache;
+
+ if (tree->cache) {
+ cache = &tree->cache[pa->thread];
+
+ if (cache->sample)
+ MEM_freeN(cache->sample);
+
+ cache->w = 0;
+ cache->h = 0;
+ cache->step = 0;
+ }
+}
+
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index e71cc6d063e..c9f13004836 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -137,7 +137,7 @@
/* here we store all renders */
static struct {
ListBase renderlist;
-} RenderGlobal = {{NULL, NULL}};
+} RenderGlobal = {{NULL, NULL}};
/* ********* alloc and free ******** */
@@ -424,10 +424,10 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id)
if (re->result) {
RenderLayer *rl;
RenderView *rv;
-
+
rr->rectx = re->result->rectx;
rr->recty = re->result->recty;
-
+
/* actview view */
rv = RE_RenderViewGetById(re->result, view_id);
rr->have_combined = (rv->rectf != NULL);
@@ -494,7 +494,7 @@ Render *RE_NewRender(const char *name)
/* only one render per name exists */
re = RE_GetRender(name);
if (re == NULL) {
-
+
/* new render data struct */
re = MEM_callocN(sizeof(Render), "new render");
BLI_addtail(&RenderGlobal.renderlist, re);
@@ -502,7 +502,7 @@ Render *RE_NewRender(const char *name)
BLI_rw_mutex_init(&re->resultmutex);
BLI_rw_mutex_init(&re->partsmutex);
}
-
+
RE_InitRenderCB(re);
return re;
@@ -574,10 +574,10 @@ void RE_FreeRender(Render *re)
/* main dbase can already be invalid now, some database-free code checks it */
re->main = NULL;
re->scene = NULL;
-
+
render_result_free(re->result);
render_result_free(re->pushedresult);
-
+
BLI_remlink(&RenderGlobal.renderlist, re);
MEM_freeN(re);
}
@@ -715,7 +715,7 @@ void RE_InitState(Render *re, Render *source, RenderData *rd,
bool had_freestyle = (re->r.mode & R_EDGE_FRS) != 0;
re->ok = true; /* maybe flag */
-
+
re->i.starttime = PIL_check_seconds_timer();
/* copy render data and render layers for thread safety */
@@ -753,7 +753,7 @@ void RE_InitState(Render *re, Render *source, RenderData *rd,
}
re->r.scemode = check_mode_full_sample(&re->r);
-
+
if (single_layer) {
int index = BLI_findindex(render_layers, single_layer);
if (index != -1) {
@@ -761,7 +761,7 @@ void RE_InitState(Render *re, Render *source, RenderData *rd,
re->r.scemode |= R_SINGLE_LAYER;
}
}
-
+
/* if preview render, we try to keep old result */
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
@@ -794,7 +794,7 @@ void RE_InitState(Render *re, Render *source, RenderData *rd,
}
}
else {
-
+
/* make empty render result, so display callbacks can initialize */
render_result_free(re->result);
re->result = MEM_callocN(sizeof(RenderResult), "new render result");
@@ -811,7 +811,7 @@ void RE_InitState(Render *re, Render *source, RenderData *rd,
RE_parts_clamp(re);
BLI_rw_mutex_unlock(&re->resultmutex);
-
+
RE_init_threadcount(re);
RE_point_density_fix_linking();
@@ -925,7 +925,7 @@ void render_update_anim_renderdata(Render *re, RenderData *rd, ListBase *render_
void RE_SetWindow(Render *re, const rctf *viewplane, float clipsta, float clipend)
{
/* re->ok flag? */
-
+
re->viewplane = *viewplane;
re->clipsta = clipsta;
re->clipend = clipend;
@@ -934,13 +934,13 @@ void RE_SetWindow(Render *re, const rctf *viewplane, float clipsta, float clipen
perspective_m4(re->winmat,
re->viewplane.xmin, re->viewplane.xmax,
re->viewplane.ymin, re->viewplane.ymax, re->clipsta, re->clipend);
-
+
}
void RE_SetOrtho(Render *re, const rctf *viewplane, float clipsta, float clipend)
{
/* re->ok flag? */
-
+
re->viewplane = *viewplane;
re->clipsta = clipsta;
re->clipend = clipend;
@@ -961,7 +961,7 @@ void RE_SetView(Render *re, float mat[4][4])
void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect)
{
*r_viewplane = re->viewplane;
-
+
/* make disprect zero when no border render, is needed to detect changes in 3d view render */
if (re->r.mode & R_BORDER) {
*r_disprect = re->disprect;
@@ -1028,7 +1028,7 @@ void RE_test_break_cb(Render *re, void *handle, int (*f)(void *handle))
#if 0
void RE_AddObject(Render *UNUSED(re), Object *UNUSED(ob))
{
-
+
}
#endif
@@ -1121,9 +1121,9 @@ static void do_render(Render *re)
/* now use renderdata and camera to set viewplane */
RE_SetCamera(re, camera);
-
+
do_render_3d(re);
-
+
/* when border render, check if we have to insert it in black */
render_result_uncrop(re);
}
@@ -1136,7 +1136,7 @@ static void render_scene(Render *re, Scene *sce, int cfra)
{
Render *resc = RE_NewSceneRender(sce);
int winx = re->winx, winy = re->winy;
-
+
sce->r.cfra = cfra;
BKE_scene_camera_switch_update(sce);
@@ -1146,7 +1146,7 @@ static void render_scene(Render *re, Scene *sce, int cfra)
winx = (sce->r.size * sce->r.xsch) / 100;
winy = (sce->r.size * sce->r.ysch) / 100;
}
-
+
/* initial setup */
RE_InitState(resc, re, &sce->r, &sce->view_layers, NULL, winx, winy, &re->disprect);
@@ -1157,7 +1157,7 @@ static void render_scene(Render *re, Scene *sce, int cfra)
resc->main = re->main;
resc->scene = sce;
resc->lay = sce->lay;
-
+
/* ensure scene has depsgraph, base flags etc OK */
BKE_scene_set_background(re->main, sce);
@@ -1170,7 +1170,7 @@ static void render_scene(Render *re, Scene *sce, int cfra)
resc->sdh = re->sdh;
resc->current_scene_update = re->current_scene_update;
resc->suh = re->suh;
-
+
do_render(resc);
}
@@ -1179,11 +1179,11 @@ static int composite_needs_render(Scene *sce, int this_scene)
{
bNodeTree *ntree = sce->nodetree;
bNode *node;
-
+
if (ntree == NULL) return 1;
if (sce->use_nodes == false) return 1;
if ((sce->r.scemode & R_DOCOMP) == 0) return 1;
-
+
for (node = ntree->nodes.first; node; node = node->next) {
if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0)
if (this_scene == 0 || node->id == NULL || node->id == &sce->id)
@@ -1334,14 +1334,14 @@ static void tag_scenes_for_render(Render *re)
{
bNode *node;
Scene *sce;
-
+
for (sce = re->main->scene.first; sce; sce = sce->id.next) {
sce->id.tag &= ~LIB_TAG_DOIT;
#ifdef DEPSGRAPH_WORKAROUND_HACK
tag_dependend_objects_for_render(re->main, sce);
#endif
}
-
+
#ifdef WITH_FREESTYLE
if (re->freestyle_bmain) {
for (sce = re->freestyle_bmain->scene.first; sce; sce = sce->id.next) {
@@ -1359,9 +1359,9 @@ static void tag_scenes_for_render(Render *re)
tag_dependend_objects_for_render(re->main, re->scene);
#endif
}
-
+
if (re->scene->nodetree == NULL) return;
-
+
/* check for render-layers nodes using other scenes, we tag them LIB_TAG_DOIT */
for (node = re->scene->nodetree->nodes.first; node; node = node->next) {
node->flag &= ~NODE_TEST;
@@ -1397,7 +1397,7 @@ static void tag_scenes_for_render(Render *re)
}
}
}
-
+
}
static void ntree_render_scenes(Render *re)
@@ -1406,15 +1406,15 @@ static void ntree_render_scenes(Render *re)
int cfra = re->scene->r.cfra;
Scene *restore_scene = re->scene;
bool scene_changed = false;
-
+
if (re->scene->nodetree == NULL) return;
-
+
tag_scenes_for_render(re);
#ifdef DEPSGRAPH_WORKAROUND_GROUP_HACK
tag_collections_for_render(re);
#endif
-
+
/* now foreach render-result node tagged we do a full render */
/* results are stored in a way compisitor will find it */
for (node = re->scene->nodetree->nodes.first; node; node = node->next) {
@@ -1426,7 +1426,7 @@ static void ntree_render_scenes(Render *re)
scene_changed |= scene != restore_scene;
render_scene(re, scene, cfra);
node->flag &= ~NODE_TEST;
-
+
nodeUpdate(restore_scene->nodetree, node);
}
}
@@ -1531,10 +1531,10 @@ static void do_render_composite(Render *re)
{
bNodeTree *ntree = re->scene->nodetree;
int update_newframe = 0;
-
+
/* INIT seeding, compositor can use random texture */
BLI_srandom(re->r.cfra);
-
+
if (composite_needs_render(re->scene, 1)) {
/* save memory... free all cached images */
ntreeFreeCache(ntree);
@@ -1550,7 +1550,7 @@ static void do_render_composite(Render *re)
/* ensure new result gets added, like for regular renders */
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
-
+
render_result_free(re->result);
if ((re->r.mode & R_CROP) == 0) {
render_result_disprect_to_full_resolution(re);
@@ -1558,30 +1558,30 @@ static void do_render_composite(Render *re)
re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
BLI_rw_mutex_unlock(&re->resultmutex);
-
+
/* scene render process already updates animsys */
update_newframe = 1;
}
-
+
/* swap render result */
if (re->r.scemode & R_SINGLE_LAYER) {
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
render_result_single_layer_end(re);
BLI_rw_mutex_unlock(&re->resultmutex);
}
-
+
if (!re->test_break(re->tbh)) {
-
+
if (ntree) {
ntreeCompositTagRender(re->scene);
ntreeCompositTagAnimated(ntree);
}
-
+
if (ntree && re->scene->use_nodes && re->r.scemode & R_DOCOMP) {
/* checks if there are render-result nodes that need scene */
if ((re->r.scemode & R_SINGLE_LAYER) == 0)
ntree_render_scenes(re);
-
+
if (!re->test_break(re->tbh)) {
ntree->stats_draw = render_composit_stats;
ntree->test_break = re->test_break;
@@ -1589,16 +1589,16 @@ static void do_render_composite(Render *re)
ntree->sdh = re;
ntree->tbh = re->tbh;
ntree->prh = re->prh;
-
+
if (update_newframe) {
/* If we have consistent depsgraph now would be a time to update them. */
}
-
+
RenderView *rv;
for (rv = re->result->views.first; rv; rv = rv->next) {
ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name);
}
-
+
ntree->stats_draw = NULL;
ntree->test_break = NULL;
ntree->progress = NULL;
@@ -1651,15 +1651,15 @@ int RE_seq_render_active(Scene *scene, RenderData *rd)
Sequence *seq;
ed = scene->ed;
-
+
if (!(rd->scemode & R_DOSEQ) || !ed || !ed->seqbase.first)
return 0;
-
+
for (seq = ed->seqbase.first; seq; seq = seq->next) {
if (seq->type != SEQ_TYPE_SOUND_RAM)
return 1;
}
-
+
return 0;
}
@@ -1810,18 +1810,18 @@ static void do_render_all_options(Render *re)
do_render_seq(re);
render_seq = true;
}
-
+
re->stats_draw(re->sdh, &re->i);
re->display_update(re->duh, re->result, NULL);
}
else {
do_render_composite(re);
}
-
+
re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime;
-
+
re->stats_draw(re->sdh, &re->i);
-
+
/* save render result stamp if needed */
if (re->result != NULL) {
camera = RE_GetCamera(re);
@@ -1975,7 +1975,7 @@ static int check_composite_output(Scene *scene)
bool RE_is_rendering_allowed(Scene *scene, ViewLayer *single_layer, Object *camera_override, ReportList *reports)
{
int scemode = check_mode_full_sample(&scene->r);
-
+
if (scene->r.mode & R_BORDER) {
if (scene->r.border.xmax <= scene->r.border.xmin ||
scene->r.border.ymax <= scene->r.border.ymin)
@@ -1984,30 +1984,30 @@ bool RE_is_rendering_allowed(Scene *scene, ViewLayer *single_layer, Object *came
return 0;
}
}
-
+
if (scemode & (R_EXR_TILE_FILE | R_FULL_SAMPLE)) {
char str[FILE_MAX];
-
+
render_result_exr_file_path(scene, "", 0, str);
-
+
if (!BLI_file_is_writable(str)) {
BKE_report(reports, RPT_ERROR, "Cannot save render buffers, check the temp default path");
return 0;
}
}
-
+
if (scemode & R_DOCOMP) {
if (scene->use_nodes) {
if (!scene->nodetree) {
BKE_report(reports, RPT_ERROR, "No node tree in scene");
return 0;
}
-
+
if (!check_composite_output(scene)) {
BKE_report(reports, RPT_ERROR, "No render output node in scene");
return 0;
}
-
+
if (scemode & R_FULL_SAMPLE) {
if (composite_needs_render(scene, 0) == 0) {
BKE_report(reports, RPT_ERROR, "Full sample AA not supported without 3D rendering");
@@ -2016,12 +2016,12 @@ bool RE_is_rendering_allowed(Scene *scene, ViewLayer *single_layer, Object *came
}
}
}
-
+
/* check valid camera, without camera render is OK (compo, seq) */
if (!check_valid_camera(scene, camera_override, reports)) {
return 0;
}
-
+
/* get panorama & ortho, only after camera is set */
BKE_camera_object_mode(&scene->r, camera_override ? camera_override : scene->camera);
@@ -2098,19 +2098,19 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain,
{
int winx, winy;
rcti disprect;
-
+
/* r.xsch and r.ysch has the actual view window size
* r.border is the clipping rect */
-
+
/* calculate actual render result and display size */
winx = (rd->size * rd->xsch) / 100;
winy = (rd->size * rd->ysch) / 100;
-
+
/* we always render smaller part, inserting it in larger image is compositor bizz, it uses disprect for it */
if (scene->r.mode & R_BORDER) {
disprect.xmin = rd->border.xmin * winx;
disprect.xmax = rd->border.xmax * winx;
-
+
disprect.ymin = rd->border.ymin * winy;
disprect.ymax = rd->border.ymax * winy;
}
@@ -2119,7 +2119,7 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain,
disprect.xmax = winx;
disprect.ymax = winy;
}
-
+
re->main = bmain;
re->scene = scene;
re->camera_override = camera_override;
@@ -2134,7 +2134,7 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain,
re->disprect = disprect;
return 1;
}
-
+
/* check all scenes involved */
tag_scenes_for_render(re);
@@ -2153,17 +2153,17 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain,
ViewLayer *view_layer = BKE_view_layer_context_active_PLACEHOLDER(scene);
update_physics_cache(re, scene, view_layer, anim_init);
}
-
+
if (single_layer || scene->r.scemode & R_SINGLE_LAYER) {
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
render_result_single_layer_begin(re);
BLI_rw_mutex_unlock(&re->resultmutex);
}
-
+
RE_InitState(re, NULL, &scene->r, &scene->view_layers, single_layer, winx, winy, &disprect);
if (!re->ok) /* if an error was printed, abort */
return 0;
-
+
/* initstate makes new result, have to send changed tags around */
ntreeCompositTagRender(re->scene);
@@ -2171,7 +2171,7 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain,
re->display_init(re->dih, re->result);
re->display_clear(re->dch, re->result);
-
+
return 1;
}
@@ -2188,9 +2188,9 @@ void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, ViewLayer *single_la
/* ugly global still... is to prevent preview events and signal subsurfs etc to make full resol */
G.is_rendering = true;
-
+
scene->r.cfra = frame;
-
+
if (render_initialize_from_main(re, &scene->r, bmain, scene, single_layer,
camera_override, lay_override, 0, 0))
{
@@ -2473,12 +2473,12 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie
/* write images as individual images or stereo */
ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, true, name);
}
-
+
RE_ReleaseResultImageViews(re, &rres);
render_time = re->i.lastframetime;
re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime;
-
+
BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime);
printf(" Time: %s", name);
@@ -2489,7 +2489,7 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie
BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime - render_time);
printf(" (Saving: %s)\n", name);
-
+
fputc('\n', stdout);
fflush(stdout); /* needed for renderd !! (not anymore... (ton)) */
@@ -2713,10 +2713,10 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
/* run callbacs before rendering, before the scene is updated */
BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_PRE);
-
+
do_render_all_options(re);
totrendered++;
-
+
if (re->test_break(re->tbh) == 0) {
if (!G.is_break)
if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL))
@@ -2724,7 +2724,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
}
else
G.is_break = true;
-
+
if (G.is_break == true) {
/* remove touched file */
if (is_movie == false) {
@@ -2753,7 +2753,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
}
}
}
-
+
break;
}
@@ -2763,12 +2763,12 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
}
}
}
-
+
/* end movie */
if (is_movie) {
re_movie_free_all(re, mh, totvideos);
}
-
+
if (totskipped && totrendered == 0)
BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite");
@@ -2812,16 +2812,16 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode)
int winx, winy;
bool success;
rcti disprect;
-
+
/* calculate actual render result and display size */
winx = (scene->r.size * scene->r.xsch) / 100;
winy = (scene->r.size * scene->r.ysch) / 100;
-
+
/* only in movie case we render smaller part */
if (scene->r.mode & R_BORDER) {
disprect.xmin = scene->r.border.xmin * winx;
disprect.xmax = scene->r.border.xmax * winx;
-
+
disprect.ymin = scene->r.border.ymin * winy;
disprect.ymax = scene->r.border.ymax * winy;
}
@@ -2830,17 +2830,17 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode)
disprect.xmax = winx;
disprect.ymax = winy;
}
-
+
if (scenode)
scene = scenode;
-
+
/* get render: it can be called from UI with draw callbacks */
re = RE_GetSceneRender(scene);
if (re == NULL)
re = RE_NewSceneRender(scene);
RE_InitState(re, NULL, &scene->r, &scene->view_layers, NULL, winx, winy, &disprect);
re->scene = scene;
-
+
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
success = render_result_exr_file_cache_read(re);
BLI_rw_mutex_unlock(&re->resultmutex);
@@ -2850,7 +2850,7 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode)
return success;
}
-void RE_init_threadcount(Render *re)
+void RE_init_threadcount(Render *re)
{
re->r.threads = BKE_render_num_threads(&re->r);
}
@@ -3014,7 +3014,7 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha
rl->rectx = rr->rectx;
rl->recty = rr->recty;
}
-
+
/* clear previous pass if exist or the new image will be over previous one*/
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname);
if (rp) {
diff --git a/source/blender/render/intern/source/pixelblending.c b/source/blender/render/intern/source/pixelblending.c
new file mode 100644
index 00000000000..c7cfe765f5b
--- /dev/null
+++ b/source/blender/render/intern/source/pixelblending.c
@@ -0,0 +1,400 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Full recode, 2004-2006 Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/pixelblending.c
+ * \ingroup render
+ *
+ * Functions to blend pixels with or without alpha, in various formats
+ * nzc - June 2000
+ */
+
+
+#include <math.h>
+#include <string.h>
+
+/* global includes */
+
+/* own includes */
+#include "render_types.h"
+#include "pixelblending.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+
+/* ------------------------------------------------------------------------- */
+/* Debug/behavior defines */
+/* if defined: alpha blending with floats clips color, as with shorts */
+/* #define RE_FLOAT_COLOR_CLIPPING */
+/* if defined: alpha values are clipped */
+/* For now, we just keep alpha clipping. We run into thresholding and */
+/* blending difficulties otherwise. Be careful here. */
+#define RE_ALPHA_CLIPPING
+
+
+
+/* Threshold for a 'full' pixel: pixels with alpha above this level are */
+/* considered opaque This is the decimal value for 0xFFF0 / 0xFFFF */
+#define RE_FULL_COLOR_FLOAT 0.9998f
+/* Threshold for an 'empty' pixel: pixels with alpha above this level are */
+/* considered completely transparent. This is the decimal value */
+/* for 0x000F / 0xFFFF */
+#define RE_EMPTY_COLOR_FLOAT 0.0002f
+
+
+/* ------------------------------------------------------------------------- */
+
+void addAlphaOverFloat(float dest[4], const float source[4])
+{
+ /* d = s + (1-alpha_s)d*/
+ float mul;
+
+ mul = 1.0f - source[3];
+
+ dest[0] = (mul * dest[0]) + source[0];
+ dest[1] = (mul * dest[1]) + source[1];
+ dest[2] = (mul * dest[2]) + source[2];
+ dest[3] = (mul * dest[3]) + source[3];
+
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+void addAlphaUnderFloat(float dest[4], const float source[4])
+{
+ float mul;
+
+ mul = 1.0f - dest[3];
+
+ dest[0] += (mul * source[0]);
+ dest[1] += (mul * source[1]);
+ dest[2] += (mul * source[2]);
+ dest[3] += (mul * source[3]);
+}
+
+
+/* ------------------------------------------------------------------------- */
+void addalphaAddfacFloat(float dest[4], const float source[4], char addfac)
+{
+ float m; /* weiging factor of destination */
+ float c; /* intermediate color */
+
+ /* Addfac is a number between 0 and 1: rescale */
+ /* final target is to diminish the influence of dest when addfac rises */
+ m = 1.0f - (source[3] * ((255 - addfac) / 255.0f));
+
+ /* blend colors*/
+ c = (m * dest[0]) + source[0];
+#ifdef RE_FLOAT_COLOR_CLIPPING
+ if (c >= RE_FULL_COLOR_FLOAT) dest[0] = RE_FULL_COLOR_FLOAT;
+ else
+#endif
+ dest[0] = c;
+
+ c = (m * dest[1]) + source[1];
+#ifdef RE_FLOAT_COLOR_CLIPPING
+ if (c >= RE_FULL_COLOR_FLOAT) dest[1] = RE_FULL_COLOR_FLOAT;
+ else
+#endif
+ dest[1] = c;
+
+ c = (m * dest[2]) + source[2];
+#ifdef RE_FLOAT_COLOR_CLIPPING
+ if (c >= RE_FULL_COLOR_FLOAT) dest[2] = RE_FULL_COLOR_FLOAT;
+ else
+#endif
+ dest[2] = c;
+
+ c = (m * dest[3]) + source[3];
+#ifdef RE_ALPHA_CLIPPING
+ if (c >= RE_FULL_COLOR_FLOAT) dest[3] = RE_FULL_COLOR_FLOAT;
+ else
+#endif
+ dest[3] = c;
+
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+/* filtered adding to scanlines */
+void add_filt_fmask(unsigned int mask, const float col[4], float *rowbuf, int row_w)
+{
+ /* calc the value of mask */
+ float **fmask1 = R.samples->fmask1, **fmask2 = R.samples->fmask2;
+ float *rb1, *rb2, *rb3;
+ float val, r, g, b, al;
+ unsigned int a, maskand, maskshift;
+ int j;
+
+ r = col[0];
+ g = col[1];
+ b = col[2];
+ al = col[3];
+
+ rb2 = rowbuf - 4;
+ rb3 = rb2 - 4 * row_w;
+ rb1 = rb2 + 4 * row_w;
+
+ maskand = (mask & 255);
+ maskshift = (mask >> 8);
+
+ for (j = 2; j >= 0; j--) {
+
+ a = j;
+
+ val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+ if (val != 0.0f) {
+ rb1[0] += val * r;
+ rb1[1] += val * g;
+ rb1[2] += val * b;
+ rb1[3] += val * al;
+ }
+ a += 3;
+
+ val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+ if (val != 0.0f) {
+ rb2[0] += val * r;
+ rb2[1] += val * g;
+ rb2[2] += val * b;
+ rb2[3] += val * al;
+ }
+ a += 3;
+
+ val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+ if (val != 0.0f) {
+ rb3[0] += val * r;
+ rb3[1] += val * g;
+ rb3[2] += val * b;
+ rb3[3] += val * al;
+ }
+
+ rb1 += 4;
+ rb2 += 4;
+ rb3 += 4;
+ }
+}
+
+
+void mask_array(unsigned int mask, float filt[3][3])
+{
+ float **fmask1 = R.samples->fmask1, **fmask2 = R.samples->fmask2;
+ unsigned int maskand = (mask & 255);
+ unsigned int maskshift = (mask >> 8);
+ int a, j;
+
+ for (j = 2; j >= 0; j--) {
+
+ a = j;
+
+ filt[2][2 - j] = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+
+ a += 3;
+
+ filt[1][2 - j] = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+
+ a += 3;
+
+ filt[0][2 - j] = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+ }
+}
+
+
+/**
+ * Index ordering, scanline based:
+ *
+ * <pre>
+ * --- --- ---
+ * | 2,0 | 2,1 | 2,2 |
+ * --- --- ---
+ * | 1,0 | 1,1 | 1,2 |
+ * --- --- ---
+ * | 0,0 | 0,1 | 0,2 |
+ * --- --- ---
+ * </pre>
+ */
+
+void add_filt_fmask_coord(float filt[3][3], const float col[4], float *rowbuf, int row_stride, int x, int y, rcti *mask)
+{
+ float *fpoin[3][3];
+ float val, r, g, b, al, lfilt[3][3];
+
+ r = col[0];
+ g = col[1];
+ b = col[2];
+ al = col[3];
+
+ memcpy(lfilt, filt, sizeof(lfilt));
+
+ fpoin[0][1] = rowbuf - 4 * row_stride;
+ fpoin[1][1] = rowbuf;
+ fpoin[2][1] = rowbuf + 4 * row_stride;
+
+ fpoin[0][0] = fpoin[0][1] - 4;
+ fpoin[1][0] = fpoin[1][1] - 4;
+ fpoin[2][0] = fpoin[2][1] - 4;
+
+ fpoin[0][2] = fpoin[0][1] + 4;
+ fpoin[1][2] = fpoin[1][1] + 4;
+ fpoin[2][2] = fpoin[2][1] + 4;
+
+ /* limit filtering to withing a mask for border rendering, so pixels don't
+ * leak outside of the border */
+ if (y <= mask->ymin) {
+ fpoin[0][0] = fpoin[1][0];
+ fpoin[0][1] = fpoin[1][1];
+ fpoin[0][2] = fpoin[1][2];
+ /* filter needs the opposite value yes! */
+ lfilt[0][0] = filt[2][0];
+ lfilt[0][1] = filt[2][1];
+ lfilt[0][2] = filt[2][2];
+ }
+ else if (y >= mask->ymax - 1) {
+ fpoin[2][0] = fpoin[1][0];
+ fpoin[2][1] = fpoin[1][1];
+ fpoin[2][2] = fpoin[1][2];
+
+ lfilt[2][0] = filt[0][0];
+ lfilt[2][1] = filt[0][1];
+ lfilt[2][2] = filt[0][2];
+ }
+
+ if (x <= mask->xmin) {
+ fpoin[2][0] = fpoin[2][1];
+ fpoin[1][0] = fpoin[1][1];
+ fpoin[0][0] = fpoin[0][1];
+
+ lfilt[2][0] = filt[2][2];
+ lfilt[1][0] = filt[1][2];
+ lfilt[0][0] = filt[0][2];
+ }
+ else if (x >= mask->xmax - 1) {
+ fpoin[2][2] = fpoin[2][1];
+ fpoin[1][2] = fpoin[1][1];
+ fpoin[0][2] = fpoin[0][1];
+
+ lfilt[2][2] = filt[2][0];
+ lfilt[1][2] = filt[1][0];
+ lfilt[0][2] = filt[0][0];
+ }
+
+
+ /* loop unroll */
+#define MASKFILT(i, j) \
+ val = lfilt[i][j]; \
+ if (val != 0.0f) { \
+ float *fp = fpoin[i][j]; \
+ fp[0] += val * r; \
+ fp[1] += val * g; \
+ fp[2] += val * b; \
+ fp[3] += val * al; \
+ } (void)0
+
+ MASKFILT(0, 0);
+ MASKFILT(0, 1);
+ MASKFILT(0, 2);
+ MASKFILT(1, 0);
+ MASKFILT(1, 1);
+ MASKFILT(1, 2);
+ MASKFILT(2, 0);
+ MASKFILT(2, 1);
+ MASKFILT(2, 2);
+
+#undef MASKFILT
+}
+
+void add_filt_fmask_pixsize(unsigned int mask, float *in, float *rowbuf, int row_w, int pixsize)
+{
+ /* calc the value of mask */
+ float **fmask1 = R.samples->fmask1, **fmask2 = R.samples->fmask2;
+ float *rb1, *rb2, *rb3;
+ float val;
+ unsigned int a, maskand, maskshift;
+ int i, j;
+
+ rb2 = rowbuf - pixsize;
+ rb3 = rb2 - pixsize * row_w;
+ rb1 = rb2 + pixsize * row_w;
+
+ maskand = (mask & 255);
+ maskshift = (mask >> 8);
+
+ for (j = 2; j >= 0; j--) {
+
+ a = j;
+
+ val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+ if (val != 0.0f) {
+ for (i = 0; i < pixsize; i++)
+ rb1[i] += val * in[i];
+ }
+ a += 3;
+
+ val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+ if (val != 0.0f) {
+ for (i = 0; i < pixsize; i++)
+ rb2[i] += val * in[i];
+ }
+ a += 3;
+
+ val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
+ if (val != 0.0f) {
+ for (i = 0; i < pixsize; i++)
+ rb3[i] += val * in[i];
+ }
+
+ rb1 += pixsize;
+ rb2 += pixsize;
+ rb3 += pixsize;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+void addalphaAddFloat(float dest[4], const float source[4])
+{
+
+ /* Makes me wonder whether this is required... */
+ if (dest[3] < RE_EMPTY_COLOR_FLOAT) {
+ dest[0] = source[0];
+ dest[1] = source[1];
+ dest[2] = source[2];
+ dest[3] = source[3];
+ return;
+ }
+
+ /* no clipping! */
+ dest[0] = dest[0] + source[0];
+ dest[1] = dest[1] + source[1];
+ dest[2] = dest[2] + source[2];
+ dest[3] = dest[3] + source[3];
+
+}
+
+
+/* ---------------------------------------------------------------------------- */
diff --git a/source/blender/render/intern/source/pixelshading.c b/source/blender/render/intern/source/pixelshading.c
new file mode 100644
index 00000000000..7f202629ce4
--- /dev/null
+++ b/source/blender/render/intern/source/pixelshading.c
@@ -0,0 +1,650 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): 2004-2006, Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/pixelshading.c
+ * \ingroup render
+ */
+
+
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+/* External modules: */
+
+#include "DNA_group_types.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_image_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_lamp_types.h"
+
+#include "BKE_material.h"
+
+
+/* own module */
+#include "render_types.h"
+#include "renderdatabase.h"
+#include "texture.h"
+#include "rendercore.h"
+#include "shadbuf.h"
+#include "pixelshading.h"
+#include "sunsky.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+
+extern const float hashvectf[];
+
+static void render_lighting_halo(HaloRen *har, float col_r[3])
+{
+ GroupObject *go;
+ LampRen *lar;
+ float i, inp, inpr, rco[3], dco[3], lv[3], lampdist, ld, t, *vn;
+ float ir, ig, ib, shadfac, soft, lacol[3];
+
+ ir= ig= ib= 0.0;
+
+ copy_v3_v3(rco, har->co);
+ dco[0]=dco[1]=dco[2]= 1.0f/har->rad;
+
+ vn= har->no;
+
+ for (go=R.lights.first; go; go= go->next) {
+ lar= go->lampren;
+
+ /* test for lamplayer */
+ if (lar->mode & LA_LAYER) if ((lar->lay & har->lay)==0) continue;
+
+ /* lampdist cacluation */
+ if (lar->type==LA_SUN || lar->type==LA_HEMI) {
+ copy_v3_v3(lv, lar->vec);
+ lampdist= 1.0;
+ }
+ else {
+ lv[0]= rco[0]-lar->co[0];
+ lv[1]= rco[1]-lar->co[1];
+ lv[2]= rco[2]-lar->co[2];
+ ld = len_v3(lv);
+ lv[0]/= ld;
+ lv[1]/= ld;
+ lv[2]/= ld;
+
+ /* ld is re-used further on (texco's) */
+
+ if (lar->mode & LA_QUAD) {
+ t= 1.0;
+ if (lar->ld1>0.0f)
+ t= lar->dist/(lar->dist+lar->ld1*ld);
+ if (lar->ld2>0.0f)
+ t*= lar->distkw/(lar->distkw+lar->ld2*ld*ld);
+
+ lampdist= t;
+ }
+ else {
+ lampdist= (lar->dist/(lar->dist+ld));
+ }
+
+ if (lar->mode & LA_SPHERE) {
+ t= lar->dist - ld;
+ if (t<0.0f) continue;
+
+ t/= lar->dist;
+ lampdist*= (t);
+ }
+
+ }
+
+ lacol[0]= lar->r;
+ lacol[1]= lar->g;
+ lacol[2]= lar->b;
+
+ if (lar->mode & LA_TEXTURE) {
+ ShadeInput shi;
+
+ /* Warning, This is not that nice, and possibly a bit slow,
+ * however some variables were not initialized properly in, unless using shade_input_initialize(...),
+ * we need to do a memset */
+ memset(&shi, 0, sizeof(ShadeInput));
+ /* end warning! - Campbell */
+
+ copy_v3_v3(shi.co, rco);
+ shi.osatex= 0;
+ do_lamp_tex(lar, lv, &shi, lacol, LA_TEXTURE);
+ }
+
+ if (lar->type==LA_SPOT) {
+
+ if (lar->mode & LA_SQUARE) {
+ if (lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2]>0.0f) {
+ float x, lvrot[3];
+
+ /* rotate view to lampspace */
+ copy_v3_v3(lvrot, lv);
+ mul_m3_v3(lar->imat, lvrot);
+
+ x = max_ff(fabsf(lvrot[0]/lvrot[2]), fabsf(lvrot[1]/lvrot[2]));
+ /* 1.0/(sqrt(1+x*x)) is equivalent to cos(atan(x)) */
+
+ inpr = 1.0f / (sqrtf(1.0f + x * x));
+ }
+ else inpr= 0.0;
+ }
+ else {
+ inpr= lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2];
+ }
+
+ t= lar->spotsi;
+ if (inpr<t) continue;
+ else {
+ t= inpr-t;
+ soft= 1.0;
+ if (t<lar->spotbl && lar->spotbl!=0.0f) {
+ /* soft area */
+ i= t/lar->spotbl;
+ t= i*i;
+ soft= (3.0f*t-2.0f*t*i);
+ inpr*= soft;
+ }
+ if (lar->mode & LA_ONLYSHADOW) {
+ /* if (ma->mode & MA_SHADOW) { */
+ /* dot product positive: front side face! */
+ inp= vn[0]*lv[0] + vn[1]*lv[1] + vn[2]*lv[2];
+ if (inp>0.0f) {
+ /* testshadowbuf==0.0 : 100% shadow */
+ shadfac = testshadowbuf(&R, lar->shb, rco, dco, dco, inp, 0.0f);
+ if ( shadfac>0.0f ) {
+ shadfac*= inp*soft*lar->energy;
+ ir -= shadfac;
+ ig -= shadfac;
+ ib -= shadfac;
+
+ continue;
+ }
+ }
+ /* } */
+ }
+ lampdist*=inpr;
+ }
+ if (lar->mode & LA_ONLYSHADOW) continue;
+
+ }
+
+ /* dot product and reflectivity*/
+
+ inp = 1.0f - fabsf(dot_v3v3(vn, lv));
+
+ /* inp= cos(0.5*M_PI-acos(inp)); */
+
+ i= inp;
+
+ if (lar->type==LA_HEMI) {
+ i= 0.5f*i+0.5f;
+ }
+ if (i>0.0f) {
+ i*= lampdist;
+ }
+
+ /* shadow */
+ if (i> -0.41f) { /* heuristic valua! */
+ if (lar->shb) {
+ shadfac = testshadowbuf(&R, lar->shb, rco, dco, dco, inp, 0.0f);
+ if (shadfac==0.0f) continue;
+ i*= shadfac;
+ }
+ }
+
+ if (i>0.0f) {
+ ir+= i*lacol[0];
+ ig+= i*lacol[1];
+ ib+= i*lacol[2];
+ }
+ }
+
+ if (ir<0.0f) ir= 0.0f;
+ if (ig<0.0f) ig= 0.0f;
+ if (ib<0.0f) ib= 0.0f;
+
+ col_r[0]*= ir;
+ col_r[1]*= ig;
+ col_r[2]*= ib;
+
+}
+
+
+/**
+ * Converts a halo z-buffer value to distance from the camera's near plane
+ * \param z The z-buffer value to convert
+ * \return a distance from the camera's near plane in blender units
+ */
+static float haloZtoDist(int z)
+{
+ float zco = 0;
+
+ if (z >= 0x7FFFFF)
+ return 10e10;
+ else {
+ zco = (float)z/(float)0x7FFFFF;
+ if (R.r.mode & R_ORTHO)
+ return (R.winmat[3][2] - zco*R.winmat[3][3])/(R.winmat[2][2]);
+ else
+ return (R.winmat[3][2])/(R.winmat[2][2] - R.winmat[2][3]*zco);
+ }
+}
+
+/**
+ * \param col (float[4]) Store the rgb color here (with alpha)
+ * The alpha is used to blend the color to the background
+ * color_new = (1-alpha)*color_background + color
+ * \param zz The current zbuffer value at the place of this pixel
+ * \param dist Distance of the pixel from the center of the halo squared. Given in pixels
+ * \param xn The x coordinate of the pixel relaticve to the center of the halo. given in pixels
+ * \param yn The y coordinate of the pixel relaticve to the center of the halo. given in pixels
+ */
+int shadeHaloFloat(HaloRen *har, float col[4], int zz,
+ float dist, float xn, float yn, short flarec)
+{
+ /* fill in col */
+ float t, zn, radist, ringf=0.0f, linef=0.0f, alpha, si, co;
+ int a;
+
+ if (R.wrld.mode & WO_MIST) {
+ if (har->type & HA_ONLYSKY) {
+ alpha= har->alfa;
+ }
+ else {
+ /* a bit patchy... */
+ alpha= mistfactor(-har->co[2], har->co)*har->alfa;
+ }
+ }
+ else alpha= har->alfa;
+
+ if (alpha==0.0f)
+ return 0;
+
+ /* soften the halo if it intersects geometry */
+ if (har->mat && har->mat->mode & MA_HALO_SOFT) {
+ float segment_length, halo_depth, distance_from_z /* , visible_depth */ /* UNUSED */, soften;
+
+ /* calculate halo depth */
+ segment_length= har->hasize*sasqrt(1.0f - dist/(har->rad*har->rad));
+ halo_depth= 2.0f*segment_length;
+
+ if (halo_depth < FLT_EPSILON)
+ return 0;
+
+ /* calculate how much of this depth is visible */
+ distance_from_z = haloZtoDist(zz) - haloZtoDist(har->zs);
+ /* visible_depth = halo_depth; */ /* UNUSED */
+ if (distance_from_z < segment_length) {
+ soften= (segment_length + distance_from_z)/halo_depth;
+
+ /* apply softening to alpha */
+ if (soften < 1.0f)
+ alpha *= soften;
+ if (alpha <= 0.0f)
+ return 0;
+ }
+ }
+ else {
+ /* not a soft halo. use the old softening code */
+ /* halo being intersected? */
+ if (har->zs> zz-har->zd) {
+ t= ((float)(zz-har->zs))/(float)har->zd;
+ alpha*= sqrtf(sqrtf(t));
+ }
+ }
+
+ radist = sqrtf(dist);
+
+ /* watch it: not used nicely: flarec is set at zero in pixstruct */
+ if (flarec) har->pixels+= (int)(har->rad-radist);
+
+ if (har->ringc) {
+ const float *rc;
+ float fac;
+ int ofs;
+
+ /* per ring an antialised circle */
+ ofs= har->seed;
+
+ for (a= har->ringc; a>0; a--, ofs+=2) {
+
+ rc= hashvectf + (ofs % 768);
+
+ fac = fabsf(rc[1] * (har->rad * fabsf(rc[0]) - radist));
+
+ if (fac< 1.0f) {
+ ringf+= (1.0f-fac);
+ }
+ }
+ }
+
+ if (har->type & HA_VECT) {
+ dist= fabsf(har->cos * (yn) - har->sin * (xn)) / har->rad;
+ if (dist>1.0f) dist= 1.0f;
+ if (har->tex) {
+ zn= har->sin*xn - har->cos*yn;
+ yn= har->cos*xn + har->sin*yn;
+ xn= zn;
+ }
+ }
+ else dist= dist/har->radsq;
+
+ if (har->type & HA_FLARECIRC) {
+ dist = 0.5f + fabsf(dist - 0.5f);
+ }
+
+ if (har->hard>=30) {
+ dist = sqrtf(dist);
+ if (har->hard>=40) {
+ dist = sinf(dist*(float)M_PI_2);
+ if (har->hard>=50) {
+ dist = sqrtf(dist);
+ }
+ }
+ }
+ else if (har->hard<20) dist*=dist;
+
+ if (dist < 1.0f)
+ dist= (1.0f-dist);
+ else
+ dist= 0.0f;
+
+ if (har->linec) {
+ const float *rc;
+ float fac;
+ int ofs;
+
+ /* per starpoint an antialiased line */
+ ofs= har->seed;
+
+ for (a= har->linec; a>0; a--, ofs+=3) {
+
+ rc= hashvectf + (ofs % 768);
+
+ fac = fabsf((xn) * rc[0] + (yn) * rc[1]);
+
+ if (fac< 1.0f )
+ linef+= (1.0f-fac);
+ }
+
+ linef*= dist;
+ }
+
+ if (har->starpoints) {
+ float ster, angle;
+ /* rotation */
+ angle = atan2f(yn, xn);
+ angle *= (1.0f+0.25f*har->starpoints);
+
+ co= cosf(angle);
+ si= sinf(angle);
+
+ angle= (co*xn+si*yn)*(co*yn-si*xn);
+
+ ster = fabsf(angle);
+ if (ster>1.0f) {
+ ster= (har->rad)/(ster);
+
+ if (ster<1.0f) dist*= sqrtf(ster);
+ }
+ }
+
+ /* disputable optimize... (ton) */
+ if (dist<=0.00001f)
+ return 0;
+
+ dist*= alpha;
+ ringf*= dist;
+ linef*= alpha;
+
+ /* The color is either the rgb spec-ed by the user, or extracted from */
+ /* the texture */
+ if (har->tex) {
+ col[0]= har->r;
+ col[1]= har->g;
+ col[2]= har->b;
+ col[3]= dist;
+
+ do_halo_tex(har, xn, yn, col);
+
+ col[0]*= col[3];
+ col[1]*= col[3];
+ col[2]*= col[3];
+
+ }
+ else {
+ col[0]= dist*har->r;
+ col[1]= dist*har->g;
+ col[2]= dist*har->b;
+ if (har->type & HA_XALPHA) col[3]= dist*dist;
+ else col[3]= dist;
+ }
+
+ if (har->mat) {
+ if (har->mat->mode & MA_HALO_SHADE) {
+ /* we test for lights because of preview... */
+ if (R.lights.first) render_lighting_halo(har, col);
+ }
+
+ /* Next, we do the line and ring factor modifications. */
+ if (linef!=0.0f) {
+ Material *ma= har->mat;
+
+ col[0]+= linef * ma->specr;
+ col[1]+= linef * ma->specg;
+ col[2]+= linef * ma->specb;
+
+ if (har->type & HA_XALPHA) col[3]+= linef*linef;
+ else col[3]+= linef;
+ }
+ if (ringf!=0.0f) {
+ Material *ma= har->mat;
+
+ col[0]+= ringf * ma->mirr;
+ col[1]+= ringf * ma->mirg;
+ col[2]+= ringf * ma->mirb;
+
+ if (har->type & HA_XALPHA) col[3]+= ringf*ringf;
+ else col[3]+= ringf;
+ }
+ }
+
+ /* alpha requires clip, gives black dots */
+ if (col[3] > 1.0f)
+ col[3]= 1.0f;
+
+ return 1;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Only view vector is important here. Result goes to col_r[3] */
+void shadeSkyView(float col_r[3], const float rco[3], const float view[3], const float dxyview[2], short thread)
+{
+ float zen[3], hor[3], blend, blendm;
+ int skyflag;
+
+ /* flag indicating if we render the top hemisphere */
+ skyflag = WO_ZENUP;
+
+ /* Some view vector stuff. */
+ if (R.wrld.skytype & WO_SKYREAL) {
+
+ blend = dot_v3v3(view, R.grvec);
+
+ if (blend<0.0f) skyflag= 0;
+
+ blend = fabsf(blend);
+ }
+ else if (R.wrld.skytype & WO_SKYPAPER) {
+ blend= 0.5f + 0.5f * view[1];
+ }
+ else {
+ /* the fraction of how far we are above the bottom of the screen */
+ blend = fabsf(0.5f + view[1]);
+ }
+
+ copy_v3_v3(hor, &R.wrld.horr);
+ copy_v3_v3(zen, &R.wrld.zenr);
+
+ /* Careful: SKYTEX and SKYBLEND are NOT mutually exclusive! If */
+ /* SKYBLEND is active, the texture and color blend are added. */
+ if (R.wrld.skytype & WO_SKYTEX) {
+ float lo[3];
+ copy_v3_v3(lo, view);
+ if (R.wrld.skytype & WO_SKYREAL) {
+
+ mul_m3_v3(R.imat, lo);
+
+ SWAP(float, lo[1], lo[2]);
+
+ }
+ do_sky_tex(rco, view, lo, dxyview, hor, zen, &blend, skyflag, thread);
+ }
+
+ if (blend>1.0f) blend= 1.0f;
+ blendm= 1.0f-blend;
+
+ /* No clipping, no conversion! */
+ if (R.wrld.skytype & WO_SKYBLEND) {
+ col_r[0] = (blendm*hor[0] + blend*zen[0]);
+ col_r[1] = (blendm*hor[1] + blend*zen[1]);
+ col_r[2] = (blendm*hor[2] + blend*zen[2]);
+ }
+ else {
+ /* Done when a texture was grabbed. */
+ col_r[0]= hor[0];
+ col_r[1]= hor[1];
+ col_r[2]= hor[2];
+ }
+}
+
+/* shade sky according to sun lamps, all parameters are like shadeSkyView except sunsky*/
+void shadeSunView(float col_r[3], const float view[3])
+{
+ GroupObject *go;
+ LampRen *lar;
+ float sview[3];
+ bool do_init = true;
+
+ for (go=R.lights.first; go; go= go->next) {
+ lar= go->lampren;
+ if (lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_SKY)) {
+ float sun_collector[3];
+ float colorxyz[3];
+
+ if (do_init) {
+
+ normalize_v3_v3(sview, view);
+ mul_m3_v3(R.imat, sview);
+ if (sview[2] < 0.0f)
+ sview[2] = 0.0f;
+ normalize_v3(sview);
+ do_init = false;
+ }
+
+ GetSkyXYZRadiancef(lar->sunsky, sview, colorxyz);
+ xyz_to_rgb(colorxyz[0], colorxyz[1], colorxyz[2], &sun_collector[0], &sun_collector[1], &sun_collector[2],
+ lar->sunsky->sky_colorspace);
+
+ ramp_blend(lar->sunsky->skyblendtype, col_r, lar->sunsky->skyblendfac, sun_collector);
+ }
+ }
+}
+
+
+/*
+ * Stuff the sky color into the collector.
+ */
+void shadeSkyPixel(float collector[4], float fx, float fy, short thread)
+{
+ float view[3], dxyview[2];
+
+ /*
+ * The rules for sky:
+ * 1. Draw an image, if a background image was provided. Stop
+ * 2. get texture and color blend, and combine these.
+ */
+
+ float fac;
+
+ if ((R.wrld.skytype & (WO_SKYBLEND+WO_SKYTEX))==0) {
+ /* 1. solid color */
+ copy_v3_v3(collector, &R.wrld.horr);
+
+ collector[3] = 0.0f;
+ }
+ else {
+ /* 2. */
+
+ /* This one true because of the context of this routine */
+ if (R.wrld.skytype & WO_SKYPAPER) {
+ view[0]= -1.0f + 2.0f*(fx/(float)R.winx);
+ view[1]= -1.0f + 2.0f*(fy/(float)R.winy);
+ view[2]= 0.0;
+
+ dxyview[0]= 1.0f/(float)R.winx;
+ dxyview[1]= 1.0f/(float)R.winy;
+ }
+ else {
+ calc_view_vector(view, fx, fy);
+ fac= normalize_v3(view);
+
+ if (R.wrld.skytype & WO_SKYTEX) {
+ dxyview[0]= -R.viewdx/fac;
+ dxyview[1]= -R.viewdy/fac;
+ }
+ }
+
+ /* get sky color in the collector */
+ shadeSkyView(collector, NULL, view, dxyview, thread);
+ collector[3] = 0.0f;
+ }
+
+ calc_view_vector(view, fx, fy);
+ shadeSunView(collector, view);
+}
+
+/* aerial perspective */
+void shadeAtmPixel(struct SunSky *sunsky, float collector[3], float fx, float fy, float distance)
+{
+ float view[3];
+
+ calc_view_vector(view, fx, fy);
+ normalize_v3(view);
+ /*mul_m3_v3(R.imat, view);*/
+ AtmospherePixleShader(sunsky, view, distance, collector);
+}
+
+/* eof */
diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c
index 53359c305dc..c025a1fdef7 100644
--- a/source/blender/render/intern/source/pointdensity.c
+++ b/source/blender/render/intern/source/pointdensity.c
@@ -102,7 +102,7 @@ static void point_data_pointers(PointDensity *pd,
const int totpoint = pd->totpoints;
float *data = pd->point_data;
int offset = 0;
-
+
if (data_used & POINT_DATA_VEL) {
if (r_data_velocity)
*r_data_velocity = data + offset;
@@ -112,7 +112,7 @@ static void point_data_pointers(PointDensity *pd,
if (r_data_velocity)
*r_data_velocity = NULL;
}
-
+
if (data_used & POINT_DATA_LIFE) {
if (r_data_life)
*r_data_life = data + offset;
@@ -122,7 +122,7 @@ static void point_data_pointers(PointDensity *pd,
if (r_data_life)
*r_data_life = NULL;
}
-
+
if (data_used & POINT_DATA_COLOR) {
if (r_data_color)
*r_data_color = data + offset;
@@ -283,19 +283,19 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, Object *UNUSED(ob)
const MLoopCol *mcol;
char layername[MAX_CUSTOMDATA_LAYER_NAME];
int i;
-
+
BLI_assert(data_color);
-
+
if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPCOL))
return;
CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPCOL, pd->vertex_attribute_name, layername);
mcol = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPCOL, layername);
if (!mcol)
return;
-
+
/* Stores the number of MLoops using the same vertex, so we can normalize colors. */
int *mcorners = MEM_callocN(sizeof(int) * pd->totpoints, "point density corner count");
-
+
for (i = 0; i < totloop; i++) {
int v = mloop[i].v;
@@ -310,7 +310,7 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, Object *UNUSED(ob)
++mcorners[v];
}
-
+
/* Normalize colors by averaging over mcorners.
* All the corners share the same vertex, ie. occupy the same point in space.
*/
@@ -318,7 +318,7 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, Object *UNUSED(ob)
if (mcorners[i] > 0)
mul_v3_fl(&data_color[i*3], 1.0f / mcorners[i]);
}
-
+
MEM_freeN(mcorners);
}
@@ -328,9 +328,9 @@ static void pointdensity_cache_vertex_weight(PointDensity *pd, Object *ob, Mesh
const MDeformVert *mdef, *dv;
int mdef_index;
int i;
-
+
BLI_assert(data_color);
-
+
mdef = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT);
if (!mdef)
return;
@@ -339,11 +339,11 @@ static void pointdensity_cache_vertex_weight(PointDensity *pd, Object *ob, Mesh
mdef_index = ob->actdef - 1;
if (mdef_index < 0)
return;
-
+
for (i = 0, dv = mdef; i < totvert; ++i, ++dv, data_color += 3) {
MDeformWeight *dw;
int j;
-
+
for (j = 0, dw = dv->dw; j < dv->totweight; ++j, ++dw) {
if (dw->def_nr == mdef_index) {
copy_v3_fl(data_color, dw->weight);
@@ -357,9 +357,9 @@ static void pointdensity_cache_vertex_normal(PointDensity *pd, Object *UNUSED(ob
{
MVert *mvert = mesh->mvert, *mv;
int i;
-
+
BLI_assert(data_color);
-
+
for (i = 0, mv = mvert; i < pd->totpoints; i++, mv++, data_color += 3) {
normal_short_to_float_v3(data_color, mv->no);
}
@@ -413,7 +413,7 @@ static void pointdensity_cache_object(PointDensity *pd,
BLI_bvhtree_insert(pd->point_tree, i, co, 1);
}
-
+
switch (pd->ob_color_source) {
case TEX_PD_COLOR_VERTCOL:
pointdensity_cache_vertex_color(pd, ob, mesh, data_color);
@@ -506,7 +506,7 @@ static float density_falloff(PointDensityRangeData *pdr, int index, float square
{
const float dist = (pdr->squared_radius - squared_dist) / pdr->squared_radius * 0.5f;
float density = 0.0f;
-
+
switch (pdr->falloff_type) {
case TEX_PD_FALLOFF_STD:
density = dist;
@@ -536,12 +536,12 @@ static float density_falloff(PointDensityRangeData *pdr, int index, float square
density = dist;
break;
}
-
+
if (pdr->density_curve && dist != 0.0f) {
curvemapping_initialize(pdr->density_curve);
density = curvemapping_evaluateF(pdr->density_curve, 0, density / dist) * dist;
}
-
+
return density;
}
@@ -666,7 +666,7 @@ static void pointdensity_color(PointDensity *pd, TexResult *texres, float age, c
if (pd->source == TEX_PD_PSYS) {
float rgba[4];
-
+
switch (pd->color_source) {
case TEX_PD_COLOR_PARTAGE:
if (pd->coba) {
@@ -681,7 +681,7 @@ static void pointdensity_color(PointDensity *pd, TexResult *texres, float age, c
case TEX_PD_COLOR_PARTSPEED:
{
float speed = len_v3(vec) * pd->speed_scale;
-
+
if (pd->coba) {
if (BKE_colorband_evaluate(pd->coba, speed, rgba)) {
texres->talpha = true;
@@ -704,7 +704,7 @@ static void pointdensity_color(PointDensity *pd, TexResult *texres, float age, c
}
else {
float rgba[4];
-
+
switch (pd->ob_color_source) {
case TEX_PD_COLOR_VERTCOL:
texres->talpha = true;
diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c
new file mode 100644
index 00000000000..df1cb868230
--- /dev/null
+++ b/source/blender/render/intern/source/rayshade.c
@@ -0,0 +1,2503 @@
+/*
+ * ***** 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) 1990-1998 NeoGeo BV.
+ * All rights reserved.
+ *
+ * Contributors: 2004/2005 Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/rayshade.c
+ * \ingroup render
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <float.h>
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_material_types.h"
+#include "DNA_lamp_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_system.h"
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_node.h"
+
+#include "render_result.h"
+#include "render_types.h"
+#include "rendercore.h"
+#include "renderdatabase.h"
+#include "pixelshading.h"
+#include "shading.h"
+#include "volumetric.h"
+
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "raycounter.h"
+
+#define RAY_TRA 1
+#define RAY_INSIDE 2
+
+#define DEPTH_SHADOW_TRA 10
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+static int test_break(void *data)
+{
+ Render *re = (Render *)data;
+ return re->test_break(re->tbh);
+}
+
+static void RE_rayobject_config_control(RayObject *r, Render *re)
+{
+ if (RE_rayobject_isRayAPI(r)) {
+ r = RE_rayobject_align(r);
+ r->control.data = re;
+ r->control.test_break = test_break;
+ }
+}
+
+RayObject *RE_rayobject_create(int type, int size, int octree_resolution)
+{
+ RayObject * res = NULL;
+
+ if (type == R_RAYSTRUCTURE_AUTO) {
+ /* TODO */
+ //if (detect_simd())
+#ifdef __SSE__
+ type = BLI_cpu_support_sse2()? R_RAYSTRUCTURE_SIMD_SVBVH: R_RAYSTRUCTURE_VBVH;
+#else
+ type = R_RAYSTRUCTURE_VBVH;
+#endif
+ }
+
+#ifndef __SSE__
+ if (type == R_RAYSTRUCTURE_SIMD_SVBVH || type == R_RAYSTRUCTURE_SIMD_QBVH) {
+ puts("Warning: Using VBVH (SSE was disabled at compile time)");
+ type = R_RAYSTRUCTURE_VBVH;
+ }
+#endif
+
+
+ if (type == R_RAYSTRUCTURE_OCTREE) //TODO dynamic ocres
+ res = RE_rayobject_octree_create(octree_resolution, size);
+ else if (type == R_RAYSTRUCTURE_VBVH)
+ res = RE_rayobject_vbvh_create(size);
+ else if (type == R_RAYSTRUCTURE_SIMD_SVBVH)
+ res = RE_rayobject_svbvh_create(size);
+ else if (type == R_RAYSTRUCTURE_SIMD_QBVH)
+ res = RE_rayobject_qbvh_create(size);
+ else
+ res = RE_rayobject_vbvh_create(size); //Fallback
+
+ return res;
+}
+
+static RayObject* rayobject_create(Render *re, int type, int size)
+{
+ RayObject * res = NULL;
+
+ res = RE_rayobject_create(type, size, re->r.ocres);
+
+ if (res)
+ RE_rayobject_config_control(res, re);
+
+ return res;
+}
+
+#ifdef RE_RAYCOUNTER
+RayCounter re_rc_counter[BLENDER_MAX_THREADS];
+#endif
+
+
+void freeraytree(Render *re)
+{
+ ObjectInstanceRen *obi;
+
+ if (re->raytree) {
+ RE_rayobject_free(re->raytree);
+ re->raytree = NULL;
+ }
+ if (re->rayfaces) {
+ MEM_freeN(re->rayfaces);
+ re->rayfaces = NULL;
+ }
+ if (re->rayprimitives) {
+ MEM_freeN(re->rayprimitives);
+ re->rayprimitives = NULL;
+ }
+
+ for (obi=re->instancetable.first; obi; obi=obi->next) {
+ ObjectRen *obr = obi->obr;
+ if (obr->raytree) {
+ RE_rayobject_free(obr->raytree);
+ obr->raytree = NULL;
+ }
+ if (obr->rayfaces) {
+ MEM_freeN(obr->rayfaces);
+ obr->rayfaces = NULL;
+ }
+ if (obi->raytree) {
+ RE_rayobject_free(obi->raytree);
+ obi->raytree = NULL;
+ }
+ }
+
+#ifdef RE_RAYCOUNTER
+ {
+ const int num_threads = re->r.threads;
+ RayCounter sum;
+ memset(&sum, 0, sizeof(sum));
+ int i;
+ for (i=0; i<num_threads; i++)
+ RE_RC_MERGE(&sum, re_rc_counter+i);
+ RE_RC_INFO(&sum);
+ }
+#endif
+}
+
+static bool is_raytraceable_vlr(Render *re, VlakRen *vlr)
+{
+ /* note: volumetric must be tracable, wire must not */
+ if ((re->flag & R_BAKE_TRACE) || (vlr->flag & R_TRACEBLE) || (vlr->mat->material_type == MA_TYPE_VOLUME))
+ if (vlr->mat->material_type != MA_TYPE_WIRE)
+ return 1;
+ return 0;
+}
+
+static bool is_raytraceable(Render *re, ObjectInstanceRen *obi)
+{
+ int v;
+ ObjectRen *obr = obi->obr;
+
+ if (re->excludeob && obr->ob == re->excludeob)
+ return 0;
+
+ for (v=0;v<obr->totvlak;v++) {
+ VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255);
+
+ if (is_raytraceable_vlr(re, vlr))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+RayObject* makeraytree_object(Render *re, ObjectInstanceRen *obi)
+{
+ /*TODO
+ * out-of-memory safeproof
+ * break render
+ * update render stats */
+ ObjectRen *obr = obi->obr;
+
+ if (obr->raytree == NULL) {
+ RayObject *raytree;
+ RayFace *face = NULL;
+ VlakPrimitive *vlakprimitive = NULL;
+ int v;
+
+ //Count faces
+ int faces = 0;
+ for (v=0;v<obr->totvlak;v++) {
+ VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255);
+ if (is_raytraceable_vlr(re, vlr))
+ faces++;
+ }
+
+ if (faces == 0)
+ return NULL;
+
+ //Create Ray cast accelaration structure
+ raytree = rayobject_create( re, re->r.raytrace_structure, faces );
+ if ( (re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS) )
+ vlakprimitive = obr->rayprimitives = (VlakPrimitive *)MEM_callocN(faces * sizeof(VlakPrimitive), "ObjectRen primitives");
+ else
+ face = obr->rayfaces = (RayFace *)MEM_callocN(faces * sizeof(RayFace), "ObjectRen faces");
+
+ obr->rayobi = obi;
+
+ for (v=0;v<obr->totvlak;v++) {
+ VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255);
+ if (is_raytraceable_vlr(re, vlr)) {
+ if ((re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS)) {
+ RE_rayobject_add(raytree, RE_vlakprimitive_from_vlak(vlakprimitive, obi, vlr));
+ vlakprimitive++;
+ }
+ else {
+ RE_rayface_from_vlak(face, obi, vlr);
+ RE_rayobject_add(raytree, RE_rayobject_unalignRayFace(face));
+ face++;
+ }
+ }
+ }
+ RE_rayobject_done(raytree);
+
+ /* in case of cancel during build, raytree is not usable */
+ if (test_break(re))
+ RE_rayobject_free(raytree);
+ else
+ obr->raytree= raytree;
+ }
+
+ if (obr->raytree) {
+ if ((obi->flag & R_TRANSFORMED) && obi->raytree == NULL) {
+ obi->transform_primitives = 0;
+ obi->raytree = RE_rayobject_instance_create( obr->raytree, obi->mat, obi, obi->obr->rayobi );
+ }
+ }
+
+ if (obi->raytree) return obi->raytree;
+ return obi->obr->raytree;
+}
+
+static bool has_special_rayobject(Render *re, ObjectInstanceRen *obi)
+{
+ if ( (obi->flag & R_TRANSFORMED) && (re->r.raytrace_options & R_RAYTRACE_USE_INSTANCES) ) {
+ ObjectRen *obr = obi->obr;
+ int v, faces = 0;
+
+ for (v=0;v<obr->totvlak;v++) {
+ VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255);
+ if (is_raytraceable_vlr(re, vlr)) {
+ faces++;
+ if (faces > 4)
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+/*
+ * create a single raytrace structure with all faces
+ */
+static void makeraytree_single(Render *re)
+{
+ ObjectInstanceRen *obi;
+ RayObject *raytree;
+ RayFace *face = NULL;
+ VlakPrimitive *vlakprimitive = NULL;
+ int faces = 0, special = 0;
+
+ for (obi = re->instancetable.first; obi; obi = obi->next) {
+ if (is_raytraceable(re, obi)) {
+ ObjectRen *obr = obi->obr;
+
+ if (has_special_rayobject(re, obi)) {
+ special++;
+ }
+ else {
+ int v;
+ for (v = 0;v < obr->totvlak; v++) {
+ VlakRen *vlr = obr->vlaknodes[v >> 8].vlak + (v&255);
+ if (is_raytraceable_vlr(re, vlr)) {
+ faces++;
+ }
+ }
+ }
+ }
+ }
+
+ if (faces + special == 0) {
+ re->raytree = RE_rayobject_empty_create();
+ return;
+ }
+
+ //Create raytree
+ raytree = re->raytree = rayobject_create( re, re->r.raytrace_structure, faces+special );
+
+ if ( (re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS) ) {
+ vlakprimitive = re->rayprimitives = (VlakPrimitive *)MEM_callocN(faces * sizeof(VlakPrimitive), "Raytrace vlak-primitives");
+ }
+ else {
+ face = re->rayfaces = (RayFace *)MEM_callocN(faces * sizeof(RayFace), "Render ray faces");
+ }
+
+ for (obi=re->instancetable.first; obi; obi=obi->next)
+ if (is_raytraceable(re, obi)) {
+ if (test_break(re))
+ break;
+
+ if (has_special_rayobject(re, obi)) {
+ RayObject *obj = makeraytree_object(re, obi);
+
+ if (test_break(re))
+ break;
+
+ if (obj)
+ RE_rayobject_add(re->raytree, obj);
+ }
+ else {
+ int v;
+ ObjectRen *obr = obi->obr;
+
+ if (obi->flag & R_TRANSFORMED) {
+ obi->transform_primitives = 1;
+ }
+
+ for (v=0;v<obr->totvlak;v++) {
+ VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255);
+ if (is_raytraceable_vlr(re, vlr)) {
+ if ((re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS)) {
+ RayObject *obj = RE_vlakprimitive_from_vlak( vlakprimitive, obi, vlr );
+ RE_rayobject_add(raytree, obj);
+ vlakprimitive++;
+ }
+ else {
+ RE_rayface_from_vlak(face, obi, vlr);
+ if ((obi->flag & R_TRANSFORMED)) {
+ mul_m4_v3(obi->mat, face->v1);
+ mul_m4_v3(obi->mat, face->v2);
+ mul_m4_v3(obi->mat, face->v3);
+ if (RE_rayface_isQuad(face))
+ mul_m4_v3(obi->mat, face->v4);
+ }
+
+ RE_rayobject_add(raytree, RE_rayobject_unalignRayFace(face));
+ face++;
+ }
+ }
+ }
+ }
+ }
+
+ if (!test_break(re)) {
+ re->i.infostr = IFACE_("Raytree.. building");
+ re->stats_draw(re->sdh, &re->i);
+
+ RE_rayobject_done(raytree);
+ }
+}
+
+void makeraytree(Render *re)
+{
+ float min[3], max[3], sub[3];
+ int i;
+
+ re->i.infostr = IFACE_("Raytree.. preparing");
+ re->stats_draw(re->sdh, &re->i);
+
+ /* disable options not yet supported by octree,
+ * they might actually never be supported (unless people really need it) */
+ if (re->r.raytrace_structure == R_RAYSTRUCTURE_OCTREE)
+ re->r.raytrace_options &= ~( R_RAYTRACE_USE_INSTANCES | R_RAYTRACE_USE_LOCAL_COORDS);
+
+ makeraytree_single(re);
+
+ if (test_break(re)) {
+ freeraytree(re);
+
+ re->i.infostr = IFACE_("Raytree building canceled");
+ re->stats_draw(re->sdh, &re->i);
+ }
+ else {
+ /* Calculate raytree max_size
+ * This is ONLY needed to kept a bogus behavior of SUN and HEMI lights */
+ INIT_MINMAX(min, max);
+ RE_rayobject_merge_bb(re->raytree, min, max);
+ if (min[0] > max[0]) { /* empty raytree */
+ zero_v3(min);
+ zero_v3(max);
+ }
+ for (i=0; i<3; i++) {
+ /* TODO: explain why add top both min and max??? */
+ min[i] += 0.01f;
+ max[i] += 0.01f;
+ sub[i] = max[i]-min[i];
+ }
+
+ re->maxdist = len_v3(sub);
+
+ re->i.infostr = IFACE_("Raytree finished");
+ re->stats_draw(re->sdh, &re->i);
+ }
+
+#ifdef RE_RAYCOUNTER
+ memset(re_rc_counter, 0, sizeof(re_rc_counter));
+#endif
+}
+
+/* if (shi->osatex) */
+static void shade_ray_set_derivative(ShadeInput *shi)
+{
+ float detsh, t00, t10, t01, t11;
+ int axis1, axis2;
+
+ /* find most stable axis to project */
+ axis_dominant_v3(&axis1, &axis2, shi->facenor);
+
+ /* compute u,v and derivatives */
+ if (shi->obi->flag & R_TRANSFORMED) {
+ float v1[3], v2[3], v3[3];
+
+ mul_v3_m3v3(v1, shi->obi->nmat, shi->v1->co);
+ mul_v3_m3v3(v2, shi->obi->nmat, shi->v2->co);
+ mul_v3_m3v3(v3, shi->obi->nmat, shi->v3->co);
+
+ /* same as below */
+ t00= v3[axis1]-v1[axis1]; t01= v3[axis2]-v1[axis2];
+ t10= v3[axis1]-v2[axis1]; t11= v3[axis2]-v2[axis2];
+ }
+ else {
+ const float *v1= shi->v1->co;
+ const float *v2= shi->v2->co;
+ const float *v3= shi->v3->co;
+
+ /* same as above */
+ t00= v3[axis1]-v1[axis1]; t01= v3[axis2]-v1[axis2];
+ t10= v3[axis1]-v2[axis1]; t11= v3[axis2]-v2[axis2];
+ }
+
+ detsh= 1.0f/(t00*t11-t10*t01);
+ t00*= detsh; t01*=detsh;
+ t10*=detsh; t11*=detsh;
+
+ shi->dx_u= shi->dxco[axis1]*t11- shi->dxco[axis2]*t10;
+ shi->dx_v= shi->dxco[axis2]*t00- shi->dxco[axis1]*t01;
+ shi->dy_u= shi->dyco[axis1]*t11- shi->dyco[axis2]*t10;
+ shi->dy_v= shi->dyco[axis2]*t00- shi->dyco[axis1]*t01;
+
+}
+
+/* main ray shader */
+void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr)
+{
+ ObjectInstanceRen *obi = (ObjectInstanceRen *)is->hit.ob;
+ VlakRen *vlr = (VlakRen *)is->hit.face;
+
+ /* set up view vector */
+ copy_v3_v3(shi->view, is->dir);
+
+ /* render co */
+ shi->co[0]= is->start[0]+is->dist*(shi->view[0]);
+ shi->co[1]= is->start[1]+is->dist*(shi->view[1]);
+ shi->co[2]= is->start[2]+is->dist*(shi->view[2]);
+
+ normalize_v3(shi->view);
+
+ shi->obi= obi;
+ shi->obr= obi->obr;
+ shi->vlr= vlr;
+ shi->mat= vlr->mat;
+ shade_input_init_material(shi);
+
+ if (is->isect==2)
+ shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3);
+ else
+ shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2);
+
+ shi->u= is->u;
+ shi->v= is->v;
+ shi->dx_u= shi->dx_v= shi->dy_u= shi->dy_v= 0.0f;
+
+ if (shi->osatex)
+ shade_ray_set_derivative(shi);
+ shade_input_set_normals(shi);
+
+ shade_input_set_shade_texco(shi);
+ if (shi->mat->material_type == MA_TYPE_VOLUME) {
+ if (ELEM(is->mode, RE_RAY_SHADOW, RE_RAY_SHADOW_TRA)) {
+ shade_volume_shadow(shi, shr, is);
+ }
+ else {
+ shade_volume_outside(shi, shr);
+ }
+ }
+ else if (is->mode==RE_RAY_SHADOW_TRA) {
+ /* temp hack to prevent recursion */
+ if (shi->nodes==0 && shi->mat->nodetree && shi->mat->use_nodes) {
+ ntreeShaderExecTree(shi->mat->nodetree, shi, shr);
+ shi->mat= vlr->mat; /* shi->mat is being set in nodetree */
+ }
+ else
+ shade_color(shi, shr);
+ }
+ else {
+ if (shi->mat->nodetree && shi->mat->use_nodes) {
+ ntreeShaderExecTree(shi->mat->nodetree, shi, shr);
+ shi->mat= vlr->mat; /* shi->mat is being set in nodetree */
+ }
+ else {
+ shade_material_loop(shi, shr);
+ }
+
+ /* raytrace likes to separate the spec color */
+ sub_v3_v3v3(shr->diff, shr->combined, shr->spec);
+ copy_v3_v3(shr->diffshad, shr->diff);
+ }
+
+}
+
+static int refraction(float refract[3], const float n[3], const float view[3], float index)
+{
+ float dot, fac;
+
+ copy_v3_v3(refract, view);
+
+ dot = dot_v3v3(view, n);
+
+ if (dot>0.0f) {
+ index = 1.0f/index;
+ fac= 1.0f - (1.0f - dot*dot)*index*index;
+ if (fac <= 0.0f) return 0;
+ fac= -dot*index + sqrtf(fac);
+ }
+ else {
+ fac= 1.0f - (1.0f - dot*dot)*index*index;
+ if (fac <= 0.0f) return 0;
+ fac= -dot*index - sqrtf(fac);
+ }
+
+ refract[0]= index*view[0] + fac*n[0];
+ refract[1]= index*view[1] + fac*n[1];
+ refract[2]= index*view[2] + fac*n[2];
+
+ return 1;
+}
+
+static void reflection_simple(float ref[3], float n[3], const float view[3])
+{
+ const float f1= -2.0f * dot_v3v3(n, view);
+ madd_v3_v3v3fl(ref, view, n, f1);
+}
+
+/* orn = original face normal */
+static void reflection(float ref[3], float n[3], const float view[3], const float orn[3])
+{
+ float f1;
+
+ reflection_simple(ref, n, view);
+
+ /* test phong normals, then we should prevent vector going to the back */
+ f1= dot_v3v3(ref, orn);
+ if (f1>0.0f) {
+ f1+= 0.01f;
+ ref[0]-= f1*orn[0];
+ ref[1]-= f1*orn[1];
+ ref[2]-= f1*orn[2];
+ }
+}
+
+#if 0
+static void color_combine(float *result, float fac1, float fac2, float col1[3], float col2[3])
+{
+ float col1t[3], col2t[3];
+
+ col1t[0]= sqrt(col1[0]);
+ col1t[1]= sqrt(col1[1]);
+ col1t[2]= sqrt(col1[2]);
+ col2t[0]= sqrt(col2[0]);
+ col2t[1]= sqrt(col2[1]);
+ col2t[2]= sqrt(col2[2]);
+
+ result[0]= (fac1*col1t[0] + fac2*col2t[0]);
+ result[0]*= result[0];
+ result[1]= (fac1*col1t[1] + fac2*col2t[1]);
+ result[1]*= result[1];
+ result[2]= (fac1*col1t[2] + fac2*col2t[2]);
+ result[2]*= result[2];
+}
+#endif
+
+static float shade_by_transmission(Isect *is, ShadeInput *shi, ShadeResult *shr)
+{
+ float d;
+ if (0 == (shi->mat->mode & MA_TRANSP))
+ return -1;
+
+ if (shi->mat->tx_limit <= 0.0f) {
+ d= 1.0f;
+ }
+ else {
+ float p;
+
+ /* shi.co[] calculated by shade_ray() */
+ const float dx= shi->co[0] - is->start[0];
+ const float dy= shi->co[1] - is->start[1];
+ const float dz= shi->co[2] - is->start[2];
+ d = sqrtf(dx * dx + dy * dy + dz * dz);
+ if (d > shi->mat->tx_limit)
+ d= shi->mat->tx_limit;
+
+ p = shi->mat->tx_falloff;
+ if (p < 0.0f) p= 0.0f;
+ else if (p > 10.0f) p= 10.0f;
+
+ shr->alpha *= powf(d, p);
+ if (shr->alpha > 1.0f)
+ shr->alpha= 1.0f;
+ }
+
+ return d;
+}
+
+static void ray_fadeout_endcolor(float col[3], ShadeInput *origshi, ShadeInput *shi, ShadeResult *shr, Isect *isec, const float vec[3])
+{
+ /* un-intersected rays get either rendered material color or sky color */
+ if (origshi->mat->fadeto_mir == MA_RAYMIR_FADETOMAT) {
+ copy_v3_v3(col, shr->combined);
+ }
+ else if (origshi->mat->fadeto_mir == MA_RAYMIR_FADETOSKY) {
+ copy_v3_v3(shi->view, vec);
+ normalize_v3(shi->view);
+
+ shadeSkyView(col, isec->start, shi->view, NULL, shi->thread);
+ shadeSunView(col, shi->view);
+ }
+}
+
+static void ray_fadeout(Isect *is, ShadeInput *shi, float col[3], const float blendcol[3], float dist_mir)
+{
+ /* if fading out, linear blend against fade color */
+ float blendfac;
+
+ blendfac = 1.0f - len_v3v3(shi->co, is->start)/dist_mir;
+
+ col[0] = col[0]*blendfac + (1.0f - blendfac)*blendcol[0];
+ col[1] = col[1]*blendfac + (1.0f - blendfac)*blendcol[1];
+ col[2] = col[2]*blendfac + (1.0f - blendfac)*blendcol[2];
+}
+
+/* the main recursive tracer itself
+ * note: 'col' must be initialized */
+static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, const float start[3], const float dir[3], float col[4], ObjectInstanceRen *obi, VlakRen *vlr, int traflag)
+{
+ ShadeInput shi = {NULL};
+ Isect isec;
+ float dist_mir = origshi->mat->dist_mir;
+
+ /* with high depth the number of rays can explode due to the path splitting
+ * in two each time, giving 2^depth rays. we need to be able to cancel such
+ * a render to avoid hanging, a better solution would be random picking
+ * between directions and russian roulette termination */
+ if (R.test_break(R.tbh)) {
+ zero_v4(col);
+ return;
+ }
+
+ copy_v3_v3(isec.start, start);
+ copy_v3_v3(isec.dir, dir);
+ isec.dist = dist_mir > 0 ? dist_mir : RE_RAYTRACE_MAXDIST;
+ isec.mode= RE_RAY_MIRROR;
+ isec.check = RE_CHECK_VLR_RENDER;
+ isec.skip = RE_SKIP_VLR_NEIGHBOUR;
+ isec.hint = NULL;
+
+ isec.orig.ob = obi;
+ isec.orig.face = vlr;
+ RE_RC_INIT(isec, shi);
+
+ /* database is in original view, obi->imat transforms current position back to original */
+ RE_instance_rotate_ray(origshi->obi, &isec);
+
+ if (RE_rayobject_raycast(R.raytree, &isec)) {
+ ShadeResult shr= {{0}};
+ float d= 1.0f;
+
+ RE_instance_rotate_ray_restore(origshi->obi, &isec);
+
+ /* for as long we don't have proper dx/dy transform for rays we copy over original */
+ copy_v3_v3(shi.dxco, origshi->dxco);
+ copy_v3_v3(shi.dyco, origshi->dyco);
+
+ shi.mask= origshi->mask;
+ shi.osatex= origshi->osatex;
+ shi.depth= origshi->depth + 1; /* only used to indicate tracing */
+ shi.thread= origshi->thread;
+ //shi.sample= 0; // memset above, so don't need this
+ shi.xs= origshi->xs;
+ shi.ys= origshi->ys;
+ shi.do_manage= origshi->do_manage;
+ shi.lay= origshi->lay;
+ shi.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */
+ shi.combinedflag= 0xFFFFFF; /* ray trace does all options */
+ //shi.do_preview = false; // memset above, so don't need this
+ shi.light_override= origshi->light_override;
+ shi.mat_override= origshi->mat_override;
+
+ shade_ray(&isec, &shi, &shr);
+ /* ray has traveled inside the material, so shade by transmission */
+ if (traflag & RAY_INSIDE)
+ d= shade_by_transmission(&isec, &shi, &shr);
+
+ if (depth>0) {
+ float fr, fg, fb, f1;
+
+ if ((shi.mat->mode_l & MA_TRANSP) && shr.alpha < 1.0f && (shi.mat->mode_l & (MA_ZTRANSP | MA_RAYTRANSP))) {
+ float nf, f, refract[3], tracol[4];
+
+ tracol[0]= shi.r;
+ tracol[1]= shi.g;
+ tracol[2]= shi.b;
+ tracol[3]= col[3]; /* we pass on and accumulate alpha */
+
+ if ((shi.mat->mode & MA_TRANSP) && (shi.mat->mode & MA_RAYTRANSP)) {
+ /* don't overwrite traflag, it's value is used in mirror reflection */
+ int new_traflag = traflag;
+
+ if (new_traflag & RAY_INSIDE) {
+ /* inside the material, so use inverse normal */
+ float norm[3];
+ norm[0]= - shi.vn[0];
+ norm[1]= - shi.vn[1];
+ norm[2]= - shi.vn[2];
+
+ if (refraction(refract, norm, shi.view, shi.ang)) {
+ /* ray comes out from the material into air */
+ new_traflag &= ~RAY_INSIDE;
+ }
+ else {
+ /* total internal reflection (ray stays inside the material) */
+ reflection(refract, norm, shi.view, shi.vn);
+ }
+ }
+ else {
+ if (refraction(refract, shi.vn, shi.view, shi.ang)) {
+ /* ray goes in to the material from air */
+ new_traflag |= RAY_INSIDE;
+ }
+ else {
+ /* total external reflection (ray doesn't enter the material) */
+ reflection(refract, shi.vn, shi.view, shi.vn);
+ }
+ }
+ traceray(origshi, origshr, depth-1, shi.co, refract, tracol, shi.obi, shi.vlr, new_traflag);
+ }
+ else
+ traceray(origshi, origshr, depth-1, shi.co, shi.view, tracol, shi.obi, shi.vlr, 0);
+
+ f= shr.alpha; f1= 1.0f-f;
+ nf= (shi.mat->mode & MA_RAYTRANSP) ? d * shi.mat->filter : 0.0f;
+ fr= 1.0f+ nf*(shi.r-1.0f);
+ fg= 1.0f+ nf*(shi.g-1.0f);
+ fb= 1.0f+ nf*(shi.b-1.0f);
+ shr.diff[0]= f*shr.diff[0] + f1*fr*tracol[0];
+ shr.diff[1]= f*shr.diff[1] + f1*fg*tracol[1];
+ shr.diff[2]= f*shr.diff[2] + f1*fb*tracol[2];
+
+ shr.spec[0] *=f;
+ shr.spec[1] *=f;
+ shr.spec[2] *=f;
+
+ col[3]= f1*tracol[3] + f;
+ }
+ else {
+ col[3]= 1.0f;
+ }
+
+ float f;
+ if (shi.mat->mode_l & MA_RAYMIRROR) {
+ f= shi.ray_mirror;
+ if (f!=0.0f) f*= fresnel_fac(shi.view, shi.vn, shi.mat->fresnel_mir_i, shi.mat->fresnel_mir);
+ }
+ else f= 0.0f;
+
+ if (f!=0.0f) {
+ float mircol[4];
+ float ref[3];
+
+ reflection_simple(ref, shi.vn, shi.view);
+ traceray(origshi, origshr, depth-1, shi.co, ref, mircol, shi.obi, shi.vlr, traflag);
+
+ f1= 1.0f-f;
+
+ /* combine */
+ //color_combine(col, f*fr*(1.0f-shr.spec[0]), f1, col, shr.diff);
+ //col[0]+= shr.spec[0];
+ //col[1]+= shr.spec[1];
+ //col[2]+= shr.spec[2];
+
+ fr= shi.mirr;
+ fg= shi.mirg;
+ fb= shi.mirb;
+
+ col[0]= f*fr*(1.0f-shr.spec[0])*mircol[0] + f1*shr.diff[0] + shr.spec[0];
+ col[1]= f*fg*(1.0f-shr.spec[1])*mircol[1] + f1*shr.diff[1] + shr.spec[1];
+ col[2]= f*fb*(1.0f-shr.spec[2])*mircol[2] + f1*shr.diff[2] + shr.spec[2];
+ }
+ else {
+ col[0]= shr.diff[0] + shr.spec[0];
+ col[1]= shr.diff[1] + shr.spec[1];
+ col[2]= shr.diff[2] + shr.spec[2];
+ }
+
+ if (dist_mir > 0.0f) {
+ float blendcol[3];
+
+ /* max ray distance set, but found an intersection, so fade this color
+ * out towards the sky/material color for a smooth transition */
+ ray_fadeout_endcolor(blendcol, origshi, &shi, origshr, &isec, dir);
+ ray_fadeout(&isec, &shi, col, blendcol, dist_mir);
+ }
+ }
+ else {
+ col[0]= shr.diff[0] + shr.spec[0];
+ col[1]= shr.diff[1] + shr.spec[1];
+ col[2]= shr.diff[2] + shr.spec[2];
+ }
+
+ }
+ else {
+ ray_fadeout_endcolor(col, origshi, &shi, origshr, &isec, dir);
+ }
+ RE_RC_MERGE(&origshi->raycounter, &shi.raycounter);
+}
+
+/* **************** jitter blocks ********** */
+
+/* calc distributed planar energy */
+
+static void DP_energy(float *table, float vec[2], int tot, float xsize, float ysize)
+{
+ int x, y, a;
+ float *fp, force[3], result[3];
+ float dx, dy, dist, min;
+
+ min= MIN2(xsize, ysize);
+ min*= min;
+ result[0]= result[1]= 0.0f;
+
+ for (y= -1; y<2; y++) {
+ dy= ysize*y;
+ for (x= -1; x<2; x++) {
+ dx= xsize*x;
+ fp= table;
+ for (a=0; a<tot; a++, fp+= 2) {
+ force[0]= vec[0] - fp[0]-dx;
+ force[1]= vec[1] - fp[1]-dy;
+ dist= force[0]*force[0] + force[1]*force[1];
+ if (dist < min && dist>0.0f) {
+ result[0]+= force[0]/dist;
+ result[1]+= force[1]/dist;
+ }
+ }
+ }
+ }
+ vec[0] += 0.1f*min*result[0]/(float)tot;
+ vec[1] += 0.1f*min*result[1]/(float)tot;
+ /* cyclic clamping */
+ vec[0]= vec[0] - xsize*floorf(vec[0]/xsize + 0.5f);
+ vec[1]= vec[1] - ysize*floorf(vec[1]/ysize + 0.5f);
+}
+
+/* random offset of 1 in 2 */
+static void jitter_plane_offset(float *jitter1, float *jitter2, int tot, float sizex, float sizey, float ofsx, float ofsy)
+{
+ float dsizex= sizex*ofsx;
+ float dsizey= sizey*ofsy;
+ float hsizex= 0.5f*sizex, hsizey= 0.5f*sizey;
+ int x;
+
+ for (x=tot; x>0; x--, jitter1+=2, jitter2+=2) {
+ jitter2[0]= jitter1[0] + dsizex;
+ jitter2[1]= jitter1[1] + dsizey;
+ if (jitter2[0] > hsizex) jitter2[0]-= sizex;
+ if (jitter2[1] > hsizey) jitter2[1]-= sizey;
+ }
+}
+
+/* called from convertBlenderScene.c */
+/* we do this in advance to get consistent random, not alter the render seed, and be threadsafe */
+void init_jitter_plane(LampRen *lar)
+{
+ float *fp;
+ int x, tot= lar->ray_totsamp;
+
+ /* test if already initialized */
+ if (lar->jitter) return;
+
+ /* at least 4, or max threads+1 tables */
+ if (BLENDER_MAX_THREADS < 4) x= 4;
+ else x= BLENDER_MAX_THREADS+1;
+ fp= lar->jitter= MEM_callocN(x*tot*2*sizeof(float), "lamp jitter tab");
+
+ /* if 1 sample, we leave table to be zero's */
+ if (tot>1) {
+ /* set per-lamp fixed seed */
+ RNG *rng = BLI_rng_new_srandom(tot);
+ int iter=12;
+
+ /* fill table with random locations, area_size large */
+ for (x=0; x<tot; x++, fp+=2) {
+ fp[0]= (BLI_rng_get_float(rng)-0.5f)*lar->area_size;
+ fp[1]= (BLI_rng_get_float(rng)-0.5f)*lar->area_sizey;
+ }
+
+ while (iter--) {
+ fp= lar->jitter;
+ for (x=tot; x>0; x--, fp+=2) {
+ DP_energy(lar->jitter, fp, tot, lar->area_size, lar->area_sizey);
+ }
+ }
+
+ BLI_rng_free(rng);
+ }
+ /* create the dithered tables (could just check lamp type!) */
+ jitter_plane_offset(lar->jitter, lar->jitter+2*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.0f);
+ jitter_plane_offset(lar->jitter, lar->jitter+4*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.5f);
+ jitter_plane_offset(lar->jitter, lar->jitter+6*tot, tot, lar->area_size, lar->area_sizey, 0.0f, 0.5f);
+}
+
+/* table around origin, -0.5*size to 0.5*size */
+static float *give_jitter_plane(LampRen *lar, int thread, int xs, int ys)
+{
+ int tot;
+
+ tot= lar->ray_totsamp;
+
+ if (lar->ray_samp_type & LA_SAMP_JITTER) {
+ /* made it threadsafe */
+
+ if (lar->xold[thread]!=xs || lar->yold[thread]!=ys) {
+ jitter_plane_offset(lar->jitter, lar->jitter+2*(thread+1)*tot, tot, lar->area_size, lar->area_sizey, BLI_thread_frand(thread), BLI_thread_frand(thread));
+ lar->xold[thread]= xs;
+ lar->yold[thread]= ys;
+ }
+ return lar->jitter+2*(thread+1)*tot;
+ }
+ if (lar->ray_samp_type & LA_SAMP_DITHER) {
+ return lar->jitter + 2*tot*((xs & 1)+2*(ys & 1));
+ }
+
+ return lar->jitter;
+}
+
+
+/* **************** QMC sampling *************** */
+
+static void halton_sample(double *ht_invprimes, double *ht_nums, double *v)
+{
+ /* incremental halton sequence generator, from:
+ * "Instant Radiosity", Keller A. */
+ unsigned int i;
+
+ for (i = 0; i < 2; i++) {
+ double r = fabs((1.0 - ht_nums[i]) - 1e-10);
+
+ if (ht_invprimes[i] >= r) {
+ double lasth;
+ double h = ht_invprimes[i];
+
+ do {
+ lasth = h;
+ h *= ht_invprimes[i];
+ } while (h >= r);
+
+ ht_nums[i] += ((lasth + h) - 1.0);
+ }
+ else
+ ht_nums[i] += ht_invprimes[i];
+
+ v[i] = (float)ht_nums[i];
+ }
+}
+
+/* Generate Hammersley points in [0,1)^2
+ * From Lucille renderer */
+static void hammersley_create(double *out, int n)
+{
+ double p, t;
+ int k, kk;
+
+ for (k = 0; k < n; k++) {
+ t = 0;
+ for (p = 0.5, kk = k; kk; p *= 0.5, kk >>= 1) {
+ if (kk & 1) { /* kk mod 2 = 1 */
+ t += p;
+ }
+ }
+
+ out[2 * k + 0] = (double)k / (double)n;
+ out[2 * k + 1] = t;
+ }
+}
+
+static struct QMCSampler *QMC_initSampler(int type, int tot)
+{
+ QMCSampler *qsa = MEM_callocN(sizeof(QMCSampler), "qmc sampler");
+ qsa->samp2d = MEM_callocN(2*sizeof(double)*tot, "qmc sample table");
+
+ qsa->tot = tot;
+ qsa->type = type;
+
+ if (qsa->type==SAMP_TYPE_HAMMERSLEY)
+ hammersley_create(qsa->samp2d, qsa->tot);
+
+ return qsa;
+}
+
+static void QMC_initPixel(QMCSampler *qsa, int thread)
+{
+ if (qsa->type==SAMP_TYPE_HAMMERSLEY) {
+ /* hammersley sequence is fixed, already created in QMCSampler init.
+ * per pixel, gets a random offset. We create separate offsets per thread, for write-safety */
+ qsa->offs[thread][0] = 0.5f * BLI_thread_frand(thread);
+ qsa->offs[thread][1] = 0.5f * BLI_thread_frand(thread);
+ }
+ else { /* SAMP_TYPE_HALTON */
+
+ /* generate a new randomized halton sequence per pixel
+ * to alleviate qmc artifacts and make it reproducible
+ * between threads/frames */
+ double ht_invprimes[2], ht_nums[2];
+ double r[2];
+ int i;
+
+ ht_nums[0] = BLI_thread_frand(thread);
+ ht_nums[1] = BLI_thread_frand(thread);
+ ht_invprimes[0] = 0.5;
+ ht_invprimes[1] = 1.0/3.0;
+
+ for (i=0; i< qsa->tot; i++) {
+ halton_sample(ht_invprimes, ht_nums, r);
+ qsa->samp2d[2*i+0] = r[0];
+ qsa->samp2d[2*i+1] = r[1];
+ }
+ }
+}
+
+static void QMC_freeSampler(QMCSampler *qsa)
+{
+ MEM_freeN(qsa->samp2d);
+ MEM_freeN(qsa);
+}
+
+static void QMC_getSample(double *s, QMCSampler *qsa, int thread, int num)
+{
+ if (qsa->type == SAMP_TYPE_HAMMERSLEY) {
+ s[0] = fmod(qsa->samp2d[2*num+0] + qsa->offs[thread][0], 1.0f);
+ s[1] = fmod(qsa->samp2d[2*num+1] + qsa->offs[thread][1], 1.0f);
+ }
+ else { /* SAMP_TYPE_HALTON */
+ s[0] = qsa->samp2d[2*num+0];
+ s[1] = qsa->samp2d[2*num+1];
+ }
+}
+
+/* phong weighted disc using 'blur' for exponent, centred on 0,0 */
+static void QMC_samplePhong(float vec[3], QMCSampler *qsa, int thread, int num, float blur)
+{
+ double s[2];
+ float phi, pz, sqr;
+
+ QMC_getSample(s, qsa, thread, num);
+
+ phi = s[0]*2*M_PI;
+ pz = pow(s[1], blur);
+ sqr = sqrtf(1.0f - pz * pz);
+
+ vec[0] = (float)(cosf(phi)*sqr);
+ vec[1] = (float)(sinf(phi)*sqr);
+ vec[2] = 0.0f;
+}
+
+/* rect of edge lengths sizex, sizey, centred on 0.0,0.0 i.e. ranging from -sizex/2 to +sizey/2 */
+static void QMC_sampleRect(float vec[3], QMCSampler *qsa, int thread, int num, float sizex, float sizey)
+{
+ double s[2];
+
+ QMC_getSample(s, qsa, thread, num);
+
+ vec[0] = (float)(s[0] - 0.5) * sizex;
+ vec[1] = (float)(s[1] - 0.5) * sizey;
+ vec[2] = 0.0f;
+}
+
+/* disc of radius 'radius', centred on 0,0 */
+static void QMC_sampleDisc(float vec[3], QMCSampler *qsa, int thread, int num, float radius)
+{
+ double s[2];
+ float phi, sqr;
+
+ QMC_getSample(s, qsa, thread, num);
+
+ phi = s[0]*2*M_PI;
+ sqr = sqrt(s[1]);
+
+ vec[0] = cosf(phi)*sqr* radius/2.0f;
+ vec[1] = sinf(phi)*sqr* radius/2.0f;
+ vec[2] = 0.0f;
+}
+
+/* uniform hemisphere sampling */
+static void QMC_sampleHemi(float vec[3], QMCSampler *qsa, int thread, int num)
+{
+ double s[2];
+ float phi, sqr;
+
+ QMC_getSample(s, qsa, thread, num);
+
+ phi = s[0]*2.0*M_PI;
+ sqr = sqrt(s[1]);
+
+ vec[0] = cosf(phi)*sqr;
+ vec[1] = sinf(phi)*sqr;
+ vec[2] = (float)(1.0 - s[1]*s[1]);
+}
+
+#if 0 /* currently not used */
+/* cosine weighted hemisphere sampling */
+static void QMC_sampleHemiCosine(float vec[3], QMCSampler *qsa, int thread, int num)
+{
+ double s[2];
+ float phi, sqr;
+
+ QMC_getSample(s, qsa, thread, num);
+
+ phi = s[0]*2.f*M_PI;
+ sqr = s[1]*sqrt(2-s[1]*s[1]);
+
+ vec[0] = cos(phi)*sqr;
+ vec[1] = sin(phi)*sqr;
+ vec[2] = 1.f - s[1]*s[1];
+
+}
+#endif
+
+/* called from convertBlenderScene.c */
+void init_render_qmcsampler(Render *re)
+{
+ const int num_threads = re->r.threads;
+ re->qmcsamplers= MEM_callocN(sizeof(ListBase)*num_threads, "QMCListBase");
+ re->num_qmc_samplers = num_threads;
+}
+
+static QMCSampler *get_thread_qmcsampler(Render *re, int thread, int type, int tot)
+{
+ QMCSampler *qsa;
+
+ /* create qmc samplers as needed, since recursion makes it hard to
+ * predict how many are needed */
+
+ for (qsa=re->qmcsamplers[thread].first; qsa; qsa=qsa->next) {
+ if (qsa->type == type && qsa->tot == tot && !qsa->used) {
+ qsa->used = true;
+ return qsa;
+ }
+ }
+
+ qsa= QMC_initSampler(type, tot);
+ qsa->used = true;
+ BLI_addtail(&re->qmcsamplers[thread], qsa);
+
+ return qsa;
+}
+
+static void release_thread_qmcsampler(Render *UNUSED(re), int UNUSED(thread), QMCSampler *qsa)
+{
+ qsa->used= 0;
+}
+
+void free_render_qmcsampler(Render *re)
+{
+ if (re->qmcsamplers) {
+ QMCSampler *qsa, *next;
+ int a;
+ for (a = 0; a < re->num_qmc_samplers; a++) {
+ for (qsa=re->qmcsamplers[a].first; qsa; qsa=next) {
+ next= qsa->next;
+ QMC_freeSampler(qsa);
+ }
+
+ re->qmcsamplers[a].first= re->qmcsamplers[a].last= NULL;
+ }
+
+ MEM_freeN(re->qmcsamplers);
+ re->qmcsamplers= NULL;
+ }
+}
+
+static int adaptive_sample_variance(int samples, const float col[3], const float colsq[3], float thresh)
+{
+ float var[3], mean[3];
+
+ /* scale threshold just to give a bit more precision in input rather than dealing with
+ * tiny tiny numbers in the UI */
+ thresh /= 2;
+
+ mean[0] = col[0] / (float)samples;
+ mean[1] = col[1] / (float)samples;
+ mean[2] = col[2] / (float)samples;
+
+ var[0] = (colsq[0] / (float)samples) - (mean[0]*mean[0]);
+ var[1] = (colsq[1] / (float)samples) - (mean[1]*mean[1]);
+ var[2] = (colsq[2] / (float)samples) - (mean[2]*mean[2]);
+
+ if ((var[0] * 0.4f < thresh) && (var[1] * 0.3f < thresh) && (var[2] * 0.6f < thresh))
+ return 1;
+ else
+ return 0;
+}
+
+static int adaptive_sample_contrast_val(int samples, float prev, float val, float thresh)
+{
+ /* if the last sample's contribution to the total value was below a small threshold
+ * (i.e. the samples taken are very similar), then taking more samples that are probably
+ * going to be the same is wasting effort */
+ if (fabsf(prev / (float)(samples - 1) - val / (float)samples ) < thresh) {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static float get_avg_speed(ShadeInput *shi)
+{
+ float pre_x, pre_y, post_x, post_y, speedavg;
+
+ pre_x = (shi->winspeed[0] == PASS_VECTOR_MAX)?0.0f:shi->winspeed[0];
+ pre_y = (shi->winspeed[1] == PASS_VECTOR_MAX)?0.0f:shi->winspeed[1];
+ post_x = (shi->winspeed[2] == PASS_VECTOR_MAX)?0.0f:shi->winspeed[2];
+ post_y = (shi->winspeed[3] == PASS_VECTOR_MAX)?0.0f:shi->winspeed[3];
+
+ speedavg = (sqrtf(pre_x * pre_x + pre_y * pre_y) + sqrtf(post_x * post_x + post_y * post_y)) / 2.0f;
+
+ return speedavg;
+}
+
+/* ***************** main calls ************** */
+
+
+static void trace_refract(float col[4], ShadeInput *shi, ShadeResult *shr)
+{
+ QMCSampler *qsa=NULL;
+ int samp_type;
+ int traflag=0;
+
+ float samp3d[3], orthx[3], orthy[3];
+ float v_refract[3], v_refract_new[3];
+ float sampcol[4], colsq[4];
+
+ float blur = pow3f(1.0f - shi->mat->gloss_tra);
+ short max_samples = shi->mat->samp_gloss_tra;
+ float adapt_thresh = shi->mat->adapt_thresh_tra;
+
+ int samples=0;
+
+ colsq[0] = colsq[1] = colsq[2] = 0.0;
+ col[0] = col[1] = col[2] = 0.0;
+ col[3]= shr->alpha;
+
+ if (blur > 0.0f) {
+ if (adapt_thresh != 0.0f) samp_type = SAMP_TYPE_HALTON;
+ else samp_type = SAMP_TYPE_HAMMERSLEY;
+
+ /* all samples are generated per pixel */
+ qsa = get_thread_qmcsampler(&R, shi->thread, samp_type, max_samples);
+ QMC_initPixel(qsa, shi->thread);
+ }
+ else
+ max_samples = 1;
+
+
+ while (samples < max_samples) {
+ if (refraction(v_refract, shi->vn, shi->view, shi->ang)) {
+ traflag |= RAY_INSIDE;
+ }
+ else {
+ /* total external reflection can happen for materials with IOR < 1.0 */
+ if ((shi->vlr->flag & R_SMOOTH))
+ reflection(v_refract, shi->vn, shi->view, shi->facenor);
+ else
+ reflection_simple(v_refract, shi->vn, shi->view);
+
+ /* can't blur total external reflection */
+ max_samples = 1;
+ }
+
+ if (max_samples > 1) {
+ /* get a quasi-random vector from a phong-weighted disc */
+ QMC_samplePhong(samp3d, qsa, shi->thread, samples, blur);
+
+ ortho_basis_v3v3_v3(orthx, orthy, v_refract);
+ mul_v3_fl(orthx, samp3d[0]);
+ mul_v3_fl(orthy, samp3d[1]);
+
+ /* and perturb the refraction vector in it */
+ add_v3_v3v3(v_refract_new, v_refract, orthx);
+ add_v3_v3(v_refract_new, orthy);
+
+ normalize_v3(v_refract_new);
+ }
+ else {
+ /* no blurriness, use the original normal */
+ copy_v3_v3(v_refract_new, v_refract);
+ }
+
+ sampcol[0]= sampcol[1]= sampcol[2]= sampcol[3]= 0.0f;
+
+ traceray(shi, shr, shi->mat->ray_depth_tra, shi->co, v_refract_new, sampcol, shi->obi, shi->vlr, traflag);
+
+ col[0] += sampcol[0];
+ col[1] += sampcol[1];
+ col[2] += sampcol[2];
+ col[3] += sampcol[3];
+
+ /* for variance calc */
+ colsq[0] += sampcol[0]*sampcol[0];
+ colsq[1] += sampcol[1]*sampcol[1];
+ colsq[2] += sampcol[2]*sampcol[2];
+
+ samples++;
+
+ /* adaptive sampling */
+ if (adapt_thresh < 1.0f && samples > max_samples/2) {
+ if (adaptive_sample_variance(samples, col, colsq, adapt_thresh))
+ break;
+
+ /* if the pixel so far is very dark, we can get away with less samples */
+ if ( (col[0] + col[1] + col[2])/3.0f/(float)samples < 0.01f )
+ max_samples--;
+ }
+ }
+
+ col[0] /= (float)samples;
+ col[1] /= (float)samples;
+ col[2] /= (float)samples;
+ col[3] /= (float)samples;
+
+ if (qsa)
+ release_thread_qmcsampler(&R, shi->thread, qsa);
+}
+
+static void trace_reflect(float col[3], ShadeInput *shi, ShadeResult *shr, float fresnelfac)
+{
+ QMCSampler *qsa=NULL;
+ int samp_type;
+
+ float samp3d[3], orthx[3], orthy[3];
+ float v_nor_new[3], v_reflect[3];
+ float sampcol[4], colsq[4];
+
+ float blur = pow3f(1.0f - shi->mat->gloss_mir);
+ short max_samples = shi->mat->samp_gloss_mir;
+ float adapt_thresh = shi->mat->adapt_thresh_mir;
+ float aniso = 1.0f - shi->mat->aniso_gloss_mir;
+
+ int samples=0;
+
+ col[0] = col[1] = col[2] = 0.0;
+ colsq[0] = colsq[1] = colsq[2] = 0.0;
+
+ if (blur > 0.0f) {
+ if (adapt_thresh != 0.0f) samp_type = SAMP_TYPE_HALTON;
+ else samp_type = SAMP_TYPE_HAMMERSLEY;
+
+ /* all samples are generated per pixel */
+ qsa = get_thread_qmcsampler(&R, shi->thread, samp_type, max_samples);
+ QMC_initPixel(qsa, shi->thread);
+ }
+ else
+ max_samples = 1;
+
+ while (samples < max_samples) {
+
+ if (max_samples > 1) {
+ /* get a quasi-random vector from a phong-weighted disc */
+ QMC_samplePhong(samp3d, qsa, shi->thread, samples, blur);
+
+ /* find the normal's perpendicular plane, blurring along tangents
+ * if tangent shading enabled */
+ if (shi->mat->mode & (MA_TANGENT_V)) {
+ cross_v3_v3v3(orthx, shi->vn, shi->tang); // bitangent
+ copy_v3_v3(orthy, shi->tang);
+ mul_v3_fl(orthx, samp3d[0]);
+ mul_v3_fl(orthy, samp3d[1]*aniso);
+ }
+ else {
+ ortho_basis_v3v3_v3(orthx, orthy, shi->vn);
+ mul_v3_fl(orthx, samp3d[0]);
+ mul_v3_fl(orthy, samp3d[1]);
+ }
+
+ /* and perturb the normal in it */
+ add_v3_v3v3(v_nor_new, shi->vn, orthx);
+ add_v3_v3(v_nor_new, orthy);
+ normalize_v3(v_nor_new);
+ }
+ else {
+ /* no blurriness, use the original normal */
+ copy_v3_v3(v_nor_new, shi->vn);
+ }
+
+ if ((shi->vlr->flag & R_SMOOTH))
+ reflection(v_reflect, v_nor_new, shi->view, shi->facenor);
+ else
+ reflection_simple(v_reflect, v_nor_new, shi->view);
+
+ sampcol[0]= sampcol[1]= sampcol[2]= sampcol[3]= 0.0f;
+
+ traceray(shi, shr, shi->mat->ray_depth, shi->co, v_reflect, sampcol, shi->obi, shi->vlr, 0);
+
+
+ col[0] += sampcol[0];
+ col[1] += sampcol[1];
+ col[2] += sampcol[2];
+
+ /* for variance calc */
+ colsq[0] += sampcol[0]*sampcol[0];
+ colsq[1] += sampcol[1]*sampcol[1];
+ colsq[2] += sampcol[2]*sampcol[2];
+
+ samples++;
+
+ /* adaptive sampling */
+ if (adapt_thresh > 0.0f && samples > max_samples/3) {
+ if (adaptive_sample_variance(samples, col, colsq, adapt_thresh))
+ break;
+
+ /* if the pixel so far is very dark, we can get away with less samples */
+ if ( (col[0] + col[1] + col[2])/3.0f/(float)samples < 0.01f )
+ max_samples--;
+
+ /* reduce samples when reflection is dim due to low ray mirror blend value or fresnel factor
+ * and when reflection is blurry */
+ if (fresnelfac < 0.1f * (blur+1)) {
+ max_samples--;
+
+ /* even more for very dim */
+ if (fresnelfac < 0.05f * (blur+1))
+ max_samples--;
+ }
+ }
+ }
+
+ col[0] /= (float)samples;
+ col[1] /= (float)samples;
+ col[2] /= (float)samples;
+
+ if (qsa)
+ release_thread_qmcsampler(&R, shi->thread, qsa);
+}
+
+/* extern call from render loop */
+void ray_trace(ShadeInput *shi, ShadeResult *shr)
+{
+ float f1, fr, fg, fb;
+ float mircol[4], tracol[4];
+ float diff[3];
+ int do_tra, do_mir;
+
+ do_tra = ((shi->mode & MA_TRANSP) && (shi->mode & MA_RAYTRANSP) && shr->alpha != 1.0f && (shi->depth <= shi->mat->ray_depth_tra));
+ do_mir = ((shi->mat->mode & MA_RAYMIRROR) && shi->ray_mirror != 0.0f && (shi->depth <= shi->mat->ray_depth));
+
+ /* raytrace mirror and refract like to separate the spec color */
+ if (shi->combinedflag & SCE_PASS_SPEC)
+ sub_v3_v3v3(diff, shr->combined, shr->spec);
+ else
+ copy_v3_v3(diff, shr->combined);
+
+ if (do_tra) {
+ float olddiff[3], f;
+
+ trace_refract(tracol, shi, shr);
+
+ f= shr->alpha; f1= 1.0f-f;
+ fr= 1.0f+ shi->mat->filter*(shi->r-1.0f);
+ fg= 1.0f+ shi->mat->filter*(shi->g-1.0f);
+ fb= 1.0f+ shi->mat->filter*(shi->b-1.0f);
+
+ /* for refract pass */
+ copy_v3_v3(olddiff, diff);
+
+ diff[0]= f*diff[0] + f1*fr*tracol[0];
+ diff[1]= f*diff[1] + f1*fg*tracol[1];
+ diff[2]= f*diff[2] + f1*fb*tracol[2];
+
+ if (shi->passflag & SCE_PASS_REFRACT)
+ sub_v3_v3v3(shr->refr, diff, olddiff);
+
+ if (!(shi->combinedflag & SCE_PASS_REFRACT))
+ sub_v3_v3v3(diff, diff, shr->refr);
+
+ shr->alpha = min_ff(1.0f, tracol[3]);
+ }
+
+ if (do_mir) {
+ const float i= shi->ray_mirror*fresnel_fac(shi->view, shi->vn, shi->mat->fresnel_mir_i, shi->mat->fresnel_mir);
+ if (i!=0.0f) {
+
+ trace_reflect(mircol, shi, shr, i);
+
+ fr= i*shi->mirr;
+ fg= i*shi->mirg;
+ fb= i*shi->mirb;
+
+ if (shi->passflag & SCE_PASS_REFLECT) {
+ /* mirror pass is not blocked out with spec */
+ shr->refl[0]= fr*mircol[0] - fr*diff[0];
+ shr->refl[1]= fg*mircol[1] - fg*diff[1];
+ shr->refl[2]= fb*mircol[2] - fb*diff[2];
+ }
+
+ if (shi->combinedflag & SCE_PASS_REFLECT) {
+ /* values in shr->spec can be greater than 1.0.
+ * In this case the mircol uses a zero blending factor, so ignoring it is ok.
+ * Fixes bug #18837 - when the spec is higher then 1.0,
+ * diff can become a negative color - Campbell */
+
+ f1= 1.0f-i;
+
+ diff[0] *= f1;
+ diff[1] *= f1;
+ diff[2] *= f1;
+
+ if (shr->spec[0]<1.0f) diff[0] += mircol[0] * (fr*(1.0f-shr->spec[0]));
+ if (shr->spec[1]<1.0f) diff[1] += mircol[1] * (fg*(1.0f-shr->spec[1]));
+ if (shr->spec[2]<1.0f) diff[2] += mircol[2] * (fb*(1.0f-shr->spec[2]));
+ }
+ }
+ }
+ /* put back together */
+ if (shi->combinedflag & SCE_PASS_SPEC)
+ add_v3_v3v3(shr->combined, diff, shr->spec);
+ else
+ copy_v3_v3(shr->combined, diff);
+}
+
+/* color 'shadfac' passes through 'col' with alpha and filter */
+/* filter is only applied on alpha defined transparent part */
+static void addAlphaLight(float shadfac[4], const float col[3], float alpha, float filter)
+{
+ float fr, fg, fb;
+
+ fr= 1.0f+ filter*(col[0]-1.0f);
+ fg= 1.0f+ filter*(col[1]-1.0f);
+ fb= 1.0f+ filter*(col[2]-1.0f);
+
+ shadfac[0]= alpha*col[0] + fr*(1.0f-alpha)*shadfac[0];
+ shadfac[1]= alpha*col[1] + fg*(1.0f-alpha)*shadfac[1];
+ shadfac[2]= alpha*col[2] + fb*(1.0f-alpha)*shadfac[2];
+
+ shadfac[3]= (1.0f-alpha)*shadfac[3];
+}
+
+static void ray_trace_shadow_tra(Isect *is, ShadeInput *origshi, int depth, int traflag, float col[4])
+{
+ /* ray to lamp, find first face that intersects, check alpha properties,
+ * if it has col[3]>0.0f continue. so exit when alpha is full */
+ const float initial_dist = is->dist;
+
+ if (RE_rayobject_raycast(R.raytree, is)) {
+ /* Warning regarding initializing to zero's, This is not that nice,
+ * and possibly a bit slow for every ray, however some variables were
+ * not initialized properly in, unless using
+ * shade_input_initialize(...), we need to zero them. */
+ ShadeInput shi= {NULL};
+ /* end warning! - Campbell */
+
+ ShadeResult shr;
+
+ /* we got a face */
+
+ shi.depth= origshi->depth + 1; /* only used to indicate tracing */
+ shi.mask= origshi->mask;
+ shi.thread= origshi->thread;
+ shi.passflag= SCE_PASS_COMBINED;
+ shi.combinedflag= 0xFFFFFF; /* ray trace does all options */
+
+ shi.xs= origshi->xs;
+ shi.ys= origshi->ys;
+ shi.do_manage= origshi->do_manage;
+ shi.lay= origshi->lay;
+ shi.nodes= origshi->nodes;
+
+ RE_instance_rotate_ray_restore(origshi->obi, is);
+
+ shade_ray(is, &shi, &shr);
+ if (shi.mat->material_type == MA_TYPE_SURFACE) {
+ const float d = (shi.mat->mode & MA_RAYTRANSP) ?
+ ((traflag & RAY_TRA) ? shade_by_transmission(is, &shi, &shr) : 1.0f) :
+ 0.0f;
+ /* mix colors based on shadfac (rgb + amount of light factor) */
+ addAlphaLight(col, shr.diff, shr.alpha, d*shi.mat->filter);
+ }
+ else if (shi.mat->material_type == MA_TYPE_VOLUME) {
+ const float a = col[3];
+
+ col[0] = a*col[0] + shr.alpha*shr.combined[0];
+ col[1] = a*col[1] + shr.alpha*shr.combined[1];
+ col[2] = a*col[2] + shr.alpha*shr.combined[2];
+
+ col[3] = (1.0f - shr.alpha)*a;
+ }
+
+ if (depth>0 && col[3]>0.0f) {
+
+ /* adapt isect struct */
+ copy_v3_v3(is->start, shi.co);
+ is->dist = initial_dist-is->dist;
+ is->orig.ob = shi.obi;
+ is->orig.face = shi.vlr;
+
+ ray_trace_shadow_tra(is, origshi, depth-1, traflag | RAY_TRA, col);
+ }
+
+ RE_RC_MERGE(&origshi->raycounter, &shi.raycounter);
+ }
+}
+
+
+/* aolight: function to create random unit sphere vectors for total random sampling */
+
+/* calc distributed spherical energy */
+static void DS_energy(float *sphere, int tot, float vec[3])
+{
+ float *fp, fac, force[3], res[3];
+ int a;
+
+ res[0]= res[1]= res[2]= 0.0f;
+
+ for (a=0, fp=sphere; a<tot; a++, fp+=3) {
+ sub_v3_v3v3(force, vec, fp);
+ fac = dot_v3v3(force, force);
+ if (fac!=0.0f) {
+ fac= 1.0f/fac;
+ res[0]+= fac*force[0];
+ res[1]+= fac*force[1];
+ res[2]+= fac*force[2];
+ }
+ }
+
+ mul_v3_fl(res, 0.5);
+ add_v3_v3(vec, res);
+ normalize_v3(vec);
+
+}
+
+/* called from convertBlenderScene.c */
+/* creates an equally distributed spherical sample pattern */
+/* and allocates threadsafe memory */
+void init_ao_sphere(Render *re, World *wrld)
+{
+ /* fixed random */
+ const int num_threads = re->r.threads;
+ RNG *rng;
+ float *fp;
+ int a, tot, iter= 16;
+
+ /* we make twice the amount of samples, because only a hemisphere is used */
+ tot= 2*wrld->aosamp*wrld->aosamp;
+
+ wrld->aosphere= MEM_mallocN(3*tot*sizeof(float), "AO sphere");
+ rng = BLI_rng_new_srandom(tot);
+
+ /* init */
+ fp= wrld->aosphere;
+ for (a=0; a<tot; a++, fp+= 3) {
+ BLI_rng_get_float_unit_v3(rng, fp);
+ }
+
+ while (iter--) {
+ for (a=0, fp= wrld->aosphere; a<tot; a++, fp+= 3) {
+ DS_energy(wrld->aosphere, tot, fp);
+ }
+ }
+
+ /* tables */
+ wrld->aotables= MEM_mallocN(num_threads*3*tot*sizeof(float), "AO tables");
+
+ BLI_rng_free(rng);
+}
+
+/* give per thread a table, we have to compare xs ys because of way OSA works... */
+static float *threadsafe_table_sphere(int test, int thread, int xs, int ys, int tot)
+{
+ static int xso[BLENDER_MAX_THREADS], yso[BLENDER_MAX_THREADS];
+ static int firsttime= 1;
+
+ if (firsttime) {
+ memset(xso, 255, sizeof(xso));
+ memset(yso, 255, sizeof(yso));
+ firsttime= 0;
+ }
+
+ if (xs==xso[thread] && ys==yso[thread]) return R.wrld.aotables+ thread*tot*3;
+ if (test) return NULL;
+ xso[thread]= xs; yso[thread]= ys;
+ return R.wrld.aotables+ thread*tot*3;
+}
+
+static float *sphere_sampler(int type, int resol, int thread, int xs, int ys, int reset)
+{
+ int tot;
+ float *vec;
+
+ tot= 2*resol*resol;
+
+ if (type & WO_AORNDSMP) {
+ /* total random sampling. NOT THREADSAFE! (should be removed, is not useful) */
+ RNG *rng = BLI_rng_new(BLI_thread_rand(thread));
+ float *sphere;
+ int a;
+
+ /* always returns table */
+ sphere= threadsafe_table_sphere(0, thread, xs, ys, tot);
+
+ vec= sphere;
+ for (a=0; a<tot; a++, vec+=3) {
+ BLI_rng_get_float_unit_v3(rng, vec);
+ }
+
+ BLI_rng_free(rng);
+
+ return sphere;
+ }
+ else {
+ float *sphere;
+ float *vec1;
+
+ /* returns table if xs and ys were equal to last call, and not resetting */
+ sphere= (reset)? NULL: threadsafe_table_sphere(1, thread, xs, ys, tot);
+ if (sphere==NULL) {
+ float cosfi, sinfi, cost, sint;
+ float ang;
+ int a;
+
+ sphere= threadsafe_table_sphere(0, thread, xs, ys, tot);
+
+ /* random rotation */
+ ang = BLI_thread_frand(thread);
+ sinfi = sinf(ang); cosfi = cosf(ang);
+ ang = BLI_thread_frand(thread);
+ sint = sinf(ang); cost = cosf(ang);
+
+ vec= R.wrld.aosphere;
+ vec1= sphere;
+ for (a=0; a<tot; a++, vec+=3, vec1+=3) {
+ vec1[0]= cost*cosfi*vec[0] - sinfi*vec[1] + sint*cosfi*vec[2];
+ vec1[1]= cost*sinfi*vec[0] + cosfi*vec[1] + sint*sinfi*vec[2];
+ vec1[2]= -sint*vec[0] + cost*vec[2];
+ }
+ }
+ return sphere;
+ }
+}
+
+static void ray_ao_qmc(ShadeInput *shi, float ao[3], float env[3])
+{
+ Isect isec;
+ RayHint point_hint;
+ QMCSampler *qsa=NULL;
+ float samp3d[3];
+ float up[3], side[3], dir[3], nrm[3];
+
+ float maxdist = R.wrld.aodist;
+ float fac=0.0f, prev=0.0f;
+ float adapt_thresh = R.wrld.ao_adapt_thresh;
+ float adapt_speed_fac = R.wrld.ao_adapt_speed_fac;
+
+ int samples=0;
+ int max_samples = R.wrld.aosamp*R.wrld.aosamp;
+
+ float dxyview[3], skyadded=0;
+ int envcolor;
+
+ RE_RC_INIT(isec, *shi);
+ isec.orig.ob = shi->obi;
+ isec.orig.face = shi->vlr;
+ isec.check = RE_CHECK_VLR_NON_SOLID_MATERIAL;
+ isec.skip = RE_SKIP_VLR_NEIGHBOUR;
+ isec.hint = NULL;
+
+ isec.hit.ob = NULL;
+ isec.hit.face = NULL;
+
+ isec.last_hit = NULL;
+
+ isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW;
+ isec.lay= -1;
+
+ copy_v3_v3(isec.start, shi->co);
+
+ RE_instance_rotate_ray_start(shi->obi, &isec);
+
+ RE_rayobject_hint_bb(R.raytree, &point_hint, isec.start, isec.start);
+ isec.hint = &point_hint;
+
+ zero_v3(ao);
+ zero_v3(env);
+
+ /* prevent sky colors to be added for only shadow (shadow becomes alpha) */
+ envcolor= R.wrld.aocolor;
+ if (shi->mat->mode & MA_ONLYSHADOW)
+ envcolor= WO_AOPLAIN;
+
+ if (envcolor == WO_AOSKYTEX) {
+ dxyview[0]= 1.0f/(float)R.wrld.aosamp;
+ dxyview[1]= 1.0f/(float)R.wrld.aosamp;
+ dxyview[2]= 0.0f;
+ }
+
+ if (shi->vlr->flag & R_SMOOTH) {
+ copy_v3_v3(nrm, shi->vn);
+ }
+ else {
+ copy_v3_v3(nrm, shi->facenor);
+ }
+
+ ortho_basis_v3v3_v3(up, side, nrm);
+
+ /* sampling init */
+ if (R.wrld.ao_samp_method==WO_AOSAMP_HALTON) {
+ float speedfac;
+
+ speedfac = get_avg_speed(shi) * adapt_speed_fac;
+ CLAMP(speedfac, 1.0f, 1000.0f);
+ max_samples /= speedfac;
+ if (max_samples < 5) max_samples = 5;
+
+ qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HALTON, max_samples);
+ }
+ else if (R.wrld.ao_samp_method==WO_AOSAMP_HAMMERSLEY)
+ qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HAMMERSLEY, max_samples);
+
+ QMC_initPixel(qsa, shi->thread);
+
+ while (samples < max_samples) {
+
+ /* sampling, returns quasi-random vector in unit hemisphere */
+ QMC_sampleHemi(samp3d, qsa, shi->thread, samples);
+
+ dir[0] = (samp3d[0]*up[0] + samp3d[1]*side[0] + samp3d[2]*nrm[0]);
+ dir[1] = (samp3d[0]*up[1] + samp3d[1]*side[1] + samp3d[2]*nrm[1]);
+ dir[2] = (samp3d[0]*up[2] + samp3d[1]*side[2] + samp3d[2]*nrm[2]);
+
+ normalize_v3(dir);
+
+ isec.dir[0] = -dir[0];
+ isec.dir[1] = -dir[1];
+ isec.dir[2] = -dir[2];
+ isec.dist = maxdist;
+
+ RE_instance_rotate_ray_dir(shi->obi, &isec);
+
+ prev = fac;
+
+ if (RE_rayobject_raycast(R.raytree, &isec)) {
+ if (R.wrld.aomode & WO_AODIST) fac+= expf(-isec.dist*R.wrld.aodistfac);
+ else fac+= 1.0f;
+ }
+ else if (envcolor!=WO_AOPLAIN) {
+ float skycol[4];
+ float view[3];
+
+ view[0]= -dir[0];
+ view[1]= -dir[1];
+ view[2]= -dir[2];
+ normalize_v3(view);
+
+ if (envcolor==WO_AOSKYCOL) {
+ const float skyfac= 0.5f * (1.0f + dot_v3v3(view, R.grvec));
+ env[0]+= (1.0f-skyfac)*R.wrld.horr + skyfac*R.wrld.zenr;
+ env[1]+= (1.0f-skyfac)*R.wrld.horg + skyfac*R.wrld.zeng;
+ env[2]+= (1.0f-skyfac)*R.wrld.horb + skyfac*R.wrld.zenb;
+ }
+ else { /* WO_AOSKYTEX */
+ shadeSkyView(skycol, isec.start, view, dxyview, shi->thread);
+ shadeSunView(skycol, shi->view);
+ env[0]+= skycol[0];
+ env[1]+= skycol[1];
+ env[2]+= skycol[2];
+ }
+ skyadded++;
+ }
+
+ samples++;
+
+ if (qsa && qsa->type == SAMP_TYPE_HALTON) {
+ /* adaptive sampling - consider samples below threshold as in shadow (or vice versa) and exit early */
+ if (adapt_thresh > 0.0f && (samples > max_samples/2) ) {
+
+ if (adaptive_sample_contrast_val(samples, prev, fac, adapt_thresh)) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* average color times distances/hits formula */
+ ao[0]= ao[1]= ao[2]= 1.0f - fac/(float)samples;
+
+ if (envcolor!=WO_AOPLAIN && skyadded)
+ mul_v3_fl(env, (1.0f - fac/(float)samples)/((float)skyadded));
+ else
+ copy_v3_v3(env, ao);
+
+ if (qsa)
+ release_thread_qmcsampler(&R, shi->thread, qsa);
+}
+
+/* extern call from shade_lamp_loop, ambient occlusion calculus */
+static void ray_ao_spheresamp(ShadeInput *shi, float ao[3], float env[3])
+{
+ Isect isec;
+ RayHint point_hint;
+ float *vec, *nrm, bias, sh=0.0f;
+ float maxdist = R.wrld.aodist;
+ float dxyview[3];
+ int j= -1, tot, actual=0, skyadded=0, envcolor, resol= R.wrld.aosamp;
+
+ RE_RC_INIT(isec, *shi);
+ isec.orig.ob = shi->obi;
+ isec.orig.face = shi->vlr;
+ isec.check = RE_CHECK_VLR_RENDER;
+ isec.skip = RE_SKIP_VLR_NEIGHBOUR;
+ isec.hint = NULL;
+
+ isec.hit.ob = NULL;
+ isec.hit.face = NULL;
+
+ isec.last_hit = NULL;
+
+ isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW;
+ isec.lay= -1;
+
+ copy_v3_v3(isec.start, shi->co);
+ RE_instance_rotate_ray_start(shi->obi, &isec);
+
+ RE_rayobject_hint_bb(R.raytree, &point_hint, isec.start, isec.start);
+ isec.hint = &point_hint;
+
+ zero_v3(ao);
+ zero_v3(env);
+
+ /* bias prevents smoothed faces to appear flat */
+ if (shi->vlr->flag & R_SMOOTH) {
+ bias= R.wrld.aobias;
+ nrm= shi->vn;
+ }
+ else {
+ bias= 0.0f;
+ nrm= shi->facenor;
+ }
+
+ /* prevent sky colors to be added for only shadow (shadow becomes alpha) */
+ envcolor= R.wrld.aocolor;
+ if (shi->mat->mode & MA_ONLYSHADOW)
+ envcolor= WO_AOPLAIN;
+
+ if (resol>32) resol= 32;
+
+ /* get sphere samples. for faces we get the same samples for sample x/y values,
+ * for strand render we always require a new sampler because x/y are not set */
+ vec= sphere_sampler(R.wrld.aomode, resol, shi->thread, shi->xs, shi->ys, shi->strand != NULL);
+
+ /* warning: since we use full sphere now, and dotproduct is below, we do twice as much */
+ tot= 2*resol*resol;
+
+ if (envcolor == WO_AOSKYTEX) {
+ dxyview[0]= 1.0f/(float)resol;
+ dxyview[1]= 1.0f/(float)resol;
+ dxyview[2]= 0.0f;
+ }
+
+ while (tot--) {
+
+ if (dot_v3v3(vec, nrm) > bias) {
+ /* only ao samples for mask */
+ if (R.r.mode & R_OSA) {
+ j++;
+ if (j==R.osa) j= 0;
+ if (!(shi->mask & (1<<j))) {
+ vec+=3;
+ continue;
+ }
+ }
+
+ actual++;
+
+ /* always set start/vec/dist */
+ isec.dir[0] = -vec[0];
+ isec.dir[1] = -vec[1];
+ isec.dir[2] = -vec[2];
+ isec.dist = maxdist;
+
+ RE_instance_rotate_ray_dir(shi->obi, &isec);
+
+ /* do the trace */
+ if (RE_rayobject_raycast(R.raytree, &isec)) {
+ if (R.wrld.aomode & WO_AODIST) sh+= expf(-isec.dist*R.wrld.aodistfac);
+ else sh+= 1.0f;
+ }
+ else if (envcolor!=WO_AOPLAIN) {
+ float skycol[4];
+ float view[3];
+
+ view[0]= -vec[0];
+ view[1]= -vec[1];
+ view[2]= -vec[2];
+ normalize_v3(view);
+
+ if (envcolor==WO_AOSKYCOL) {
+ const float fac = 0.5f * (1.0f + dot_v3v3(view, R.grvec));
+ env[0]+= (1.0f-fac)*R.wrld.horr + fac*R.wrld.zenr;
+ env[1]+= (1.0f-fac)*R.wrld.horg + fac*R.wrld.zeng;
+ env[2]+= (1.0f-fac)*R.wrld.horb + fac*R.wrld.zenb;
+ }
+ else { /* WO_AOSKYTEX */
+ shadeSkyView(skycol, isec.start, view, dxyview, shi->thread);
+ shadeSunView(skycol, shi->view);
+ env[0]+= skycol[0];
+ env[1]+= skycol[1];
+ env[2]+= skycol[2];
+ }
+ skyadded++;
+ }
+ }
+ /* samples */
+ vec+= 3;
+ }
+
+ if (actual==0) sh= 1.0f;
+ else sh = 1.0f - sh/((float)actual);
+
+ /* average color times distances/hits formula */
+ ao[0]= ao[1]= ao[2]= sh;
+
+ if (envcolor!=WO_AOPLAIN && skyadded)
+ mul_v3_fl(env, sh/((float)skyadded));
+ else
+ copy_v3_v3(env, ao);
+}
+
+void ray_ao(ShadeInput *shi, float ao[3], float env[3])
+{
+ /* Unfortunately, the unusual way that the sphere sampler calculates roughly twice as many
+ * samples as are actually traced, and skips them based on bias and OSA settings makes it very difficult
+ * to reuse code between these two functions. This is the easiest way I can think of to do it
+ * --broken */
+ if (ELEM(R.wrld.ao_samp_method, WO_AOSAMP_HAMMERSLEY, WO_AOSAMP_HALTON))
+ ray_ao_qmc(shi, ao, env);
+ else if (R.wrld.ao_samp_method == WO_AOSAMP_CONSTANT)
+ ray_ao_spheresamp(shi, ao, env);
+}
+
+static void ray_shadow_jittered_coords(ShadeInput *shi, int max, float jitco[RE_MAX_OSA][3], int *totjitco)
+{
+ /* magic numbers for reordering sample positions to give better
+ * results with adaptive sample, when it usually only takes 4 samples */
+ int order8[8] = {0, 1, 5, 6, 2, 3, 4, 7};
+ int order11[11] = {1, 3, 8, 10, 0, 2, 4, 5, 6, 7, 9};
+ int order16[16] = {1, 3, 9, 12, 0, 6, 7, 8, 13, 2, 4, 5, 10, 11, 14, 15};
+ int count = count_mask(shi->mask);
+
+ /* for better antialising shadow samples are distributed over the subpixel
+ * sample coordinates, this only works for raytracing depth 0 though */
+ if (!shi->strand && shi->depth == 0 && count > 1 && count <= max) {
+ float xs, ys, zs, view[3];
+ int samp, ordsamp, tot= 0;
+
+ for (samp=0; samp<R.osa; samp++) {
+ if (R.osa == 8) ordsamp = order8[samp];
+ else if (R.osa == 11) ordsamp = order11[samp];
+ else if (R.osa == 16) ordsamp = order16[samp];
+ else ordsamp = samp;
+
+ if (shi->mask & (1<<ordsamp)) {
+ /* zbuffer has this inverse corrected, ensures xs,ys are inside pixel */
+ xs= (float)shi->scanco[0] + R.jit[ordsamp][0] + 0.5f;
+ ys= (float)shi->scanco[1] + R.jit[ordsamp][1] + 0.5f;
+ zs= shi->scanco[2];
+
+ shade_input_calc_viewco(shi, xs, ys, zs, view, NULL, jitco[tot], NULL, NULL);
+ tot++;
+ }
+ }
+
+ *totjitco= tot;
+ }
+ else {
+ copy_v3_v3(jitco[0], shi->co);
+ *totjitco= 1;
+ }
+}
+
+static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, const float lampco[3], float shadfac[4], Isect *isec)
+{
+ QMCSampler *qsa=NULL;
+ int samples=0;
+ float samp3d[3];
+
+ float fac=0.0f, vec[3], end[3];
+ float colsq[4];
+ float adapt_thresh = lar->adapt_thresh;
+ int min_adapt_samples=4, max_samples = lar->ray_totsamp;
+ float start[3];
+ bool do_soft = true, full_osa = false;
+ int i;
+
+ float min[3], max[3];
+ RayHint bb_hint;
+
+ float jitco[RE_MAX_OSA][3];
+ int totjitco;
+
+ colsq[0] = colsq[1] = colsq[2] = 0.0;
+ if (isec->mode==RE_RAY_SHADOW_TRA) {
+ shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f;
+ }
+ else
+ shadfac[3]= 1.0f;
+
+ if (lar->ray_totsamp < 2) do_soft = false;
+ if ((R.r.mode & R_OSA) && (R.osa > 0) && (shi->vlr->flag & R_FULL_OSA)) full_osa = true;
+
+ if (full_osa) {
+ if (do_soft) max_samples = max_samples/R.osa + 1;
+ else max_samples = 1;
+ }
+ else {
+ if (do_soft) max_samples = lar->ray_totsamp;
+ else if (shi->depth == 0) max_samples = (R.osa > 4)?R.osa:5;
+ else max_samples = 1;
+ }
+
+ ray_shadow_jittered_coords(shi, max_samples, jitco, &totjitco);
+
+ /* sampling init */
+ if (lar->ray_samp_method==LA_SAMP_HALTON)
+ qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HALTON, max_samples);
+ else if (lar->ray_samp_method==LA_SAMP_HAMMERSLEY)
+ qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HAMMERSLEY, max_samples);
+
+ QMC_initPixel(qsa, shi->thread);
+
+ INIT_MINMAX(min, max);
+ for (i = 0; i < totjitco; i++) {
+ minmax_v3v3_v3(min, max, jitco[i]);
+ }
+ if (shi->obi->flag & R_ENV_TRANSFORMED) {
+ mul_m4_v3(shi->obi->imat, min);
+ mul_m4_v3(shi->obi->imat, max);
+ }
+ RE_rayobject_hint_bb(R.raytree, &bb_hint, min, max);
+
+ isec->hint = &bb_hint;
+ isec->check = RE_CHECK_VLR_RENDER;
+ isec->skip = RE_SKIP_VLR_NEIGHBOUR;
+ copy_v3_v3(vec, lampco);
+
+ while (samples < max_samples) {
+
+ isec->orig.ob = shi->obi;
+ isec->orig.face = shi->vlr;
+
+ /* manually jitter the start shading co-ord per sample
+ * based on the pre-generated OSA texture sampling offsets,
+ * for anti-aliasing sharp shadow edges. */
+ copy_v3_v3(start, jitco[samples % totjitco]);
+
+ if (do_soft) {
+ /* sphere shadow source */
+ if (lar->type == LA_LOCAL) {
+ float ru[3], rv[3], v[3], s[3];
+
+ /* calc tangent plane vectors */
+ sub_v3_v3v3(v, start, lampco);
+ normalize_v3(v);
+ ortho_basis_v3v3_v3(ru, rv, v);
+
+ /* sampling, returns quasi-random vector in area_size disc */
+ QMC_sampleDisc(samp3d, qsa, shi->thread, samples, lar->area_size);
+
+ /* distribute disc samples across the tangent plane */
+ s[0] = samp3d[0]*ru[0] + samp3d[1]*rv[0];
+ s[1] = samp3d[0]*ru[1] + samp3d[1]*rv[1];
+ s[2] = samp3d[0]*ru[2] + samp3d[1]*rv[2];
+
+ copy_v3_v3(samp3d, s);
+ }
+ else {
+ /* sampling, returns quasi-random vector in [sizex,sizey]^2 plane */
+ QMC_sampleRect(samp3d, qsa, shi->thread, samples, lar->area_size, lar->area_sizey);
+
+ /* align samples to lamp vector */
+ mul_m3_v3(lar->mat, samp3d);
+ }
+ end[0] = vec[0]+samp3d[0];
+ end[1] = vec[1]+samp3d[1];
+ end[2] = vec[2]+samp3d[2];
+ }
+ else {
+ copy_v3_v3(end, vec);
+ }
+
+ if (shi->strand) {
+ /* bias away somewhat to avoid self intersection */
+ float jitbias= 0.5f*(len_v3(shi->dxco) + len_v3(shi->dyco));
+ float v[3];
+
+ sub_v3_v3v3(v, start, end);
+ normalize_v3(v);
+
+ start[0] -= jitbias*v[0];
+ start[1] -= jitbias*v[1];
+ start[2] -= jitbias*v[2];
+ }
+
+ copy_v3_v3(isec->start, start);
+ sub_v3_v3v3(isec->dir, end, start);
+ isec->dist = normalize_v3(isec->dir);
+
+ RE_instance_rotate_ray(shi->obi, isec);
+
+ /* trace the ray */
+ if (isec->mode==RE_RAY_SHADOW_TRA) {
+ float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+
+ ray_trace_shadow_tra(isec, shi, DEPTH_SHADOW_TRA, 0, col);
+ shadfac[0] += col[0];
+ shadfac[1] += col[1];
+ shadfac[2] += col[2];
+ shadfac[3] += col[3];
+
+ /* for variance calc */
+ colsq[0] += col[0]*col[0];
+ colsq[1] += col[1]*col[1];
+ colsq[2] += col[2]*col[2];
+ }
+ else {
+ if ( RE_rayobject_raycast(R.raytree, isec) ) fac+= 1.0f;
+ }
+
+ samples++;
+
+ if (lar->ray_samp_method == LA_SAMP_HALTON) {
+
+ /* adaptive sampling - consider samples below threshold as in shadow (or vice versa) and exit early */
+ if ((max_samples > min_adapt_samples) && (adapt_thresh > 0.0f) && (samples > max_samples / 3)) {
+ if (isec->mode==RE_RAY_SHADOW_TRA) {
+ if ((shadfac[3] / samples > (1.0f-adapt_thresh)) || (shadfac[3] / samples < adapt_thresh))
+ break;
+ else if (adaptive_sample_variance(samples, shadfac, colsq, adapt_thresh))
+ break;
+ }
+ else {
+ if ((fac / samples > (1.0f-adapt_thresh)) || (fac / samples < adapt_thresh))
+ break;
+ }
+ }
+ }
+ }
+
+ if (isec->mode==RE_RAY_SHADOW_TRA) {
+ shadfac[0] /= samples;
+ shadfac[1] /= samples;
+ shadfac[2] /= samples;
+ shadfac[3] /= samples;
+ }
+ else
+ shadfac[3]= 1.0f-fac/samples;
+
+ if (qsa)
+ release_thread_qmcsampler(&R, shi->thread, qsa);
+}
+
+static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, const float lampco[3], float shadfac[4], Isect *isec)
+{
+ /* area soft shadow */
+ const float *jitlamp;
+ float fac=0.0f, div=0.0f, vec[3];
+ int a, j= -1, mask;
+ RayHint point_hint;
+
+ if (isec->mode==RE_RAY_SHADOW_TRA) {
+ shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f;
+ }
+ else shadfac[3]= 1.0f;
+
+ fac= 0.0f;
+ jitlamp= give_jitter_plane(lar, shi->thread, shi->xs, shi->ys);
+
+ a= lar->ray_totsamp;
+
+ /* this correction to make sure we always take at least 1 sample */
+ mask= shi->mask;
+ if (a==4) mask |= (mask>>4)|(mask>>8);
+ else if (a==9) mask |= (mask>>9);
+
+ copy_v3_v3(isec->start, shi->co);
+ RE_instance_rotate_ray_start(shi->obi, isec);
+
+ isec->orig.ob = shi->obi;
+ isec->orig.face = shi->vlr;
+ RE_rayobject_hint_bb(R.raytree, &point_hint, isec->start, isec->start);
+ isec->hint = &point_hint;
+
+ while (a--) {
+
+ if (R.r.mode & R_OSA) {
+ j++;
+ if (j>=R.osa) j= 0;
+ if (!(mask & (1<<j))) {
+ jitlamp+= 2;
+ continue;
+ }
+ }
+
+ vec[0]= jitlamp[0];
+ vec[1]= jitlamp[1];
+ vec[2]= 0.0f;
+ mul_m3_v3(lar->mat, vec);
+
+ /* set start and vec */
+ isec->dir[0] = vec[0]+lampco[0]-shi->co[0];
+ isec->dir[1] = vec[1]+lampco[1]-shi->co[1];
+ isec->dir[2] = vec[2]+lampco[2]-shi->co[2];
+
+ RE_instance_rotate_ray_dir(shi->obi, isec);
+
+ isec->dist = 1.0f;
+ isec->check = RE_CHECK_VLR_RENDER;
+ isec->skip = RE_SKIP_VLR_NEIGHBOUR;
+
+ if (isec->mode==RE_RAY_SHADOW_TRA) {
+ /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */
+ float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+
+ ray_trace_shadow_tra(isec, shi, DEPTH_SHADOW_TRA, 0, col);
+ shadfac[0] += col[0];
+ shadfac[1] += col[1];
+ shadfac[2] += col[2];
+ shadfac[3] += col[3];
+ }
+ else if ( RE_rayobject_raycast(R.raytree, isec) ) fac+= 1.0f;
+
+ div+= 1.0f;
+ jitlamp+= 2;
+ }
+
+ if (isec->mode==RE_RAY_SHADOW_TRA) {
+ shadfac[0] /= div;
+ shadfac[1] /= div;
+ shadfac[2] /= div;
+ shadfac[3] /= div;
+ }
+ else {
+ /* sqrt makes nice umbra effect */
+ if (lar->ray_samp_type & LA_SAMP_UMBRA)
+ shadfac[3] = sqrtf(1.0f - fac / div);
+ else
+ shadfac[3] = 1.0f - fac / div;
+ }
+}
+/* extern call from shade_lamp_loop */
+void ray_shadow(ShadeInput *shi, LampRen *lar, float shadfac[4])
+{
+ Isect isec;
+ float lampco[3];
+
+ /* setup isec */
+ RE_RC_INIT(isec, *shi);
+ if (shi->mat->mode & MA_SHADOW_TRA) isec.mode= RE_RAY_SHADOW_TRA;
+ else isec.mode= RE_RAY_SHADOW;
+ isec.hint = NULL;
+
+ if (lar->mode & (LA_LAYER|LA_LAYER_SHADOW))
+ isec.lay= lar->lay;
+ else
+ isec.lay= -1;
+
+ /* only when not mir tracing, first hit optimm */
+ if (shi->depth==0) {
+ isec.last_hit = lar->last_hit[shi->thread];
+ }
+ else {
+ isec.last_hit = NULL;
+ }
+
+ if (lar->type==LA_SUN || lar->type==LA_HEMI) {
+ /* jitter and QMC sampling add a displace vector to the lamp position
+ * that's incorrect because a SUN lamp does not has an exact position
+ * and the displace should be done at the ray vector instead of the
+ * lamp position.
+ * This is easily verified by noticing that shadows of SUN lights change
+ * with the scene BB.
+ *
+ * This was detected during SoC 2009 - Raytrace Optimization, but to keep
+ * consistency with older render code it wasn't removed.
+ *
+ * If the render code goes through some recode/serious bug-fix then this
+ * is something to consider!
+ */
+ lampco[0]= shi->co[0] - R.maxdist*lar->vec[0];
+ lampco[1]= shi->co[1] - R.maxdist*lar->vec[1];
+ lampco[2]= shi->co[2] - R.maxdist*lar->vec[2];
+ }
+ else {
+ copy_v3_v3(lampco, lar->co);
+ }
+
+ if (ELEM(lar->ray_samp_method, LA_SAMP_HALTON, LA_SAMP_HAMMERSLEY)) {
+
+ ray_shadow_qmc(shi, lar, lampco, shadfac, &isec);
+
+ }
+ else {
+ if (lar->ray_totsamp<2) {
+
+ isec.orig.ob = shi->obi;
+ isec.orig.face = shi->vlr;
+
+ shadfac[3]= 1.0f; /* 1.0=full light */
+
+ /* set up isec.dir */
+ copy_v3_v3(isec.start, shi->co);
+ sub_v3_v3v3(isec.dir, lampco, isec.start);
+ isec.dist = normalize_v3(isec.dir);
+
+ RE_instance_rotate_ray(shi->obi, &isec);
+
+ if (isec.mode==RE_RAY_SHADOW_TRA) {
+ /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */
+ float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+
+ ray_trace_shadow_tra(&isec, shi, DEPTH_SHADOW_TRA, 0, col);
+ copy_v4_v4(shadfac, col);
+ }
+ else if (RE_rayobject_raycast(R.raytree, &isec))
+ shadfac[3]= 0.0f;
+ }
+ else {
+ ray_shadow_jitter(shi, lar, lampco, shadfac, &isec);
+ }
+ }
+
+ /* for first hit optim, set last interesected shadow face */
+ if (shi->depth==0) {
+ lar->last_hit[shi->thread] = isec.last_hit;
+ }
+
+}
+
diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c
index 5fd897219c4..e0cacdf4b8f 100644
--- a/source/blender/render/intern/source/render_result.c
+++ b/source/blender/render/intern/source/render_result.c
@@ -95,7 +95,7 @@ void render_result_free(RenderResult *res)
if (rl->acolrect) MEM_freeN(rl->acolrect);
if (rl->scolrect) MEM_freeN(rl->scolrect);
if (rl->display_buffer) MEM_freeN(rl->display_buffer);
-
+
while (rl->passes.first) {
RenderPass *rpass = rl->passes.first;
if (rpass->rect) MEM_freeN(rpass->rect);
@@ -128,13 +128,13 @@ void render_result_free(RenderResult *res)
void render_result_free_list(ListBase *lb, RenderResult *rr)
{
RenderResult *rrnext;
-
+
for (; rr; rr = rrnext) {
rrnext = rr->next;
-
+
if (lb && lb->first)
BLI_remlink(lb, rr);
-
+
render_result_free(rr);
}
}
@@ -206,7 +206,7 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int
const int view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name));
RenderPass *rpass = MEM_callocN(sizeof(RenderPass), name);
size_t rectsize = ((size_t)rr->rectx) * rr->recty * channels;
-
+
rpass->channels = channels;
rpass->rectx = rl->rectx;
rpass->recty = rl->recty;
@@ -216,7 +216,7 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int
BLI_strncpy(rpass->chan_id, chan_id, sizeof(rpass->chan_id));
BLI_strncpy(rpass->view, viewname, sizeof(rpass->view));
set_pass_full_name(rpass->fullname, rpass->name, -1, rpass->view, rpass->chan_id);
-
+
if (rl->exrhandle) {
int a;
for (a = 0; a < channels; a++) {
@@ -227,13 +227,13 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int
else {
float *rect;
int x;
-
+
rpass->rect = MEM_mapallocN(sizeof(float) * rectsize, name);
if (rpass->rect == NULL) {
MEM_freeN(rpass);
return NULL;
}
-
+
if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) {
/* initialize to max speed */
rect = rpass->rect;
@@ -267,13 +267,13 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
RenderLayer *rl;
RenderView *rv;
int rectx, recty;
-
+
rectx = BLI_rcti_size_x(partrct);
recty = BLI_rcti_size_y(partrct);
-
+
if (rectx <= 0 || recty <= 0)
return NULL;
-
+
rr = MEM_callocN(sizeof(RenderResult), "new render result");
rr->rectx = rectx;
rr->recty = recty;
@@ -286,7 +286,7 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
rr->tilerect.xmax = partrct->xmax - re->disprect.xmin;
rr->tilerect.ymin = partrct->ymin - re->disprect.ymin;
rr->tilerect.ymax = partrct->ymax - re->disprect.ymin;
-
+
if (savebuffers) {
rr->do_exr_tile = true;
}
@@ -304,14 +304,14 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
rl = MEM_callocN(sizeof(RenderLayer), "new render layer");
BLI_addtail(&rr->layers, rl);
-
+
BLI_strncpy(rl->name, view_layer->name, sizeof(rl->name));
rl->layflag = view_layer->layflag;
rl->passflag = view_layer->passflag; /* for debugging: view_layer->passflag | SCE_PASS_RAYHITS; */
rl->pass_xor = view_layer->pass_xor;
rl->rectx = rectx;
rl->recty = recty;
-
+
if (rr->do_exr_tile) {
rl->display_buffer = MEM_mapallocN((size_t)rectx * recty * sizeof(unsigned int),
"Combined display space rgba");
@@ -412,7 +412,7 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) {
rl = MEM_callocN(sizeof(RenderLayer), "new render layer");
BLI_addtail(&rr->layers, rl);
-
+
rl->rectx = rectx;
rl->recty = recty;
@@ -439,15 +439,15 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
/* note, this has to be in sync with scene.c */
rl->layflag = 0x7FFF; /* solid ztra halo strand */
rl->passflag = SCE_PASS_COMBINED;
-
+
re->active_view_layer = 0;
}
-
+
/* border render; calculate offset for use in compositor. compo is centralized coords */
/* XXX obsolete? I now use it for drawing border render offset (ton) */
rr->xof = re->disprect.xmin + BLI_rcti_cent_x(&re->disprect) - (re->winx / 2);
rr->yof = re->disprect.ymin + BLI_rcti_cent_y(&re->disprect) - (re->winy / 2);
-
+
return rr;
}
@@ -554,7 +554,7 @@ static void *ml_addlayer_cb(void *base, const char *str)
{
RenderResult *rr = base;
RenderLayer *rl;
-
+
rl = MEM_callocN(sizeof(RenderLayer), "new render layer");
BLI_addtail(&rr->layers, rl);
@@ -676,7 +676,7 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace
rr->rectx = rectx;
rr->recty = recty;
-
+
IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb);
for (rl = rr->layers.first; rl; rl = rl->next) {
@@ -695,7 +695,7 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace
}
}
}
-
+
return rr;
}
@@ -740,16 +740,16 @@ static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target,
{
int y, tilex, tiley;
size_t ofs, copylen;
-
+
copylen = tilex = rrpart->rectx;
tiley = rrpart->recty;
-
+
if (rrpart->crop) { /* filters add pixel extra */
tile += pixsize * (rrpart->crop + ((size_t)rrpart->crop) * tilex);
-
+
copylen = tilex - 2 * rrpart->crop;
tiley -= 2 * rrpart->crop;
-
+
ofs = (((size_t)rrpart->tilerect.ymin) + rrpart->crop) * rr->rectx + (rrpart->tilerect.xmin + rrpart->crop);
target += pixsize * ofs;
}
@@ -776,7 +776,7 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart)
{
RenderLayer *rl, *rlp;
RenderPass *rpass, *rpassp;
-
+
for (rl = rr->layers.first; rl; rl = rl->next) {
rlp = RE_GetRenderLayer(rrpart, rl->name);
if (rlp) {
@@ -956,7 +956,7 @@ void render_result_single_layer_begin(Render *re)
/* officially pushed result should be NULL... error can happen with do_seq */
RE_FreeRenderResult(re->pushedresult);
-
+
re->pushedresult = re->result;
re->result = NULL;
}
@@ -980,10 +980,10 @@ void render_result_single_layer_end(Render *re)
if (re->pushedresult->rectx == re->result->rectx && re->pushedresult->recty == re->result->recty) {
/* find which layer in re->pushedresult should be replaced */
rl = re->result->layers.first;
-
+
/* render result should be empty after this */
BLI_remlink(&re->result->layers, rl);
-
+
/* reconstruct render result layers */
for (nr = 0, view_layer = re->view_layers.first; view_layer; view_layer = view_layer->next, nr++) {
if (nr == re->active_view_layer) {
@@ -1010,9 +1010,9 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons
RenderLayer *rlp, *rl;
RenderPass *rpassp;
int offs, partx, party;
-
+
BLI_thread_lock(LOCK_IMAGE);
-
+
for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) {
rl = RE_GetRenderLayer(rr, rlp->name);
@@ -1042,7 +1042,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons
xstride, xstride * rrpart->rectx, rpassp->rect + a + xstride * offs);
}
}
-
+
}
party = rrpart->tilerect.ymin + rrpart->crop;
@@ -1068,7 +1068,7 @@ void render_result_save_empty_result_tiles(Render *re)
RenderPart *pa;
RenderResult *rr;
RenderLayer *rl;
-
+
for (rr = re->result; rr; rr = rr->next) {
for (rl = rr->layers.first; rl; rl = rl->next) {
for (pa = re->parts.first; pa; pa = pa->next) {
@@ -1112,7 +1112,7 @@ void render_result_exr_file_end(Render *re)
rr->do_exr_tile = false;
}
-
+
render_result_free_list(&re->fullresult, re->result);
re->result = NULL;
@@ -1131,7 +1131,7 @@ void render_result_exr_file_path(Scene *scene, const char *layname, int sample,
{
char name[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100];
const char *fi = BLI_path_basename(BKE_main_blendfile_path_from_global());
-
+
if (sample == 0) {
BLI_snprintf(name, sizeof(name), "%s_%s_%s.exr", fi, scene->id.name + 2, layname);
}
@@ -1194,7 +1194,7 @@ int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, c
for (rl = rr->layers.first; rl; rl = rl->next) {
if (rl_single && rl_single != rl)
continue;
-
+
/* passes are allocated in sync */
for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
const int xstride = rpass->channels;
@@ -1292,7 +1292,7 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd, const int vi
/* float factor for random dither, imbuf takes care of it */
ibuf->dither = rd->dither_intensity;
-
+
/* prepare to gamma correct to sRGB color space
* note that sequence editor can generate 8bpc render buffers
*/
@@ -1333,7 +1333,7 @@ void RE_render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), I
if (!rv->rectf)
rv->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf");
-
+
memcpy(rv->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty);
/* TSK! Since sequence render doesn't free the *rr render result, the old rect32
diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c
index 79d13ecab5b..99da5b3ca01 100644
--- a/source/blender/render/intern/source/render_texture.c
+++ b/source/blender/render/intern/source/render_texture.c
@@ -95,7 +95,7 @@ static void tex_normal_derivate(Tex *tex, TexResult *texres)
float col[4];
if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) {
float fac0, fac1, fac2, fac3;
-
+
fac0= (col[0]+col[1]+col[2]);
BKE_colorband_evaluate(tex->coba, texres->nor[0], col);
fac1= (col[0]+col[1]+col[2]);
@@ -103,11 +103,11 @@ static void tex_normal_derivate(Tex *tex, TexResult *texres)
fac2= (col[0]+col[1]+col[2]);
BKE_colorband_evaluate(tex->coba, texres->nor[2], col);
fac3= (col[0]+col[1]+col[2]);
-
+
texres->nor[0]= (fac0 - fac1) / 3.0f;
texres->nor[1]= (fac0 - fac2) / 3.0f;
texres->nor[2]= (fac0 - fac3) / 3.0f;
-
+
return;
}
}
@@ -173,7 +173,7 @@ static int blend(Tex *tex, const float texvec[3], TexResult *texres)
static int clouds(Tex *tex, const float texvec[3], TexResult *texres)
{
int rv = TEX_INT;
-
+
texres->tin = BLI_gTurbulence(tex->noisesize, texvec[0], texvec[1], texvec[2], tex->noisedepth, (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
if (texres->nor!=NULL) {
@@ -181,7 +181,7 @@ static int clouds(Tex *tex, const float texvec[3], TexResult *texres)
texres->nor[0] = BLI_gTurbulence(tex->noisesize, texvec[0] + tex->nabla, texvec[1], texvec[2], tex->noisedepth, (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
texres->nor[1] = BLI_gTurbulence(tex->noisesize, texvec[0], texvec[1] + tex->nabla, texvec[2], tex->noisedepth, (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
texres->nor[2] = BLI_gTurbulence(tex->noisesize, texvec[0], texvec[1], texvec[2] + tex->nabla, tex->noisedepth, (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
-
+
tex_normal_derivate(tex, texres);
rv |= TEX_NOR;
}
@@ -215,7 +215,7 @@ static float tex_sin(float a)
static float tex_saw(float a)
{
const float b = 2*M_PI;
-
+
int n = (int)(a / b);
a -= n*b;
if (a < 0) a += b;
@@ -227,9 +227,9 @@ static float tex_tri(float a)
{
const float b = 2*M_PI;
const float rmax = 1.0;
-
+
a = rmax - 2.0f*fabsf(floorf((a*(1.0f/b))+0.5f) - (a*(1.0f/b)));
-
+
return a;
}
@@ -244,9 +244,9 @@ static float wood_int(Tex *tex, float x, float y, float z)
waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */
waveform[1] = tex_saw;
waveform[2] = tex_tri;
-
+
if ((wf>TEX_TRI) || (wf<TEX_SIN)) wf=0; /* check to be sure noisebasis2 is initialized ahead of time */
-
+
if (wt==TEX_BAND) {
wi = waveform[wf]((x + y + z)*10.0f);
}
@@ -261,7 +261,7 @@ static float wood_int(Tex *tex, float x, float y, float z)
wi = tex->turbul*BLI_gNoise(tex->noisesize, x, y, z, (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
wi = waveform[wf](sqrtf(x*x + y*y + z*z)*20.0f + wi);
}
-
+
return wi;
}
@@ -275,7 +275,7 @@ static int wood(Tex *tex, const float texvec[3], TexResult *texres)
texres->nor[0] = wood_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]);
texres->nor[1] = wood_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]);
texres->nor[2] = wood_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla);
-
+
tex_normal_derivate(tex, texres);
rv |= TEX_NOR;
}
@@ -291,16 +291,16 @@ static float marble_int(Tex *tex, float x, float y, float z)
float n, mi;
short wf = tex->noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */
short mt = tex->stype; /* marble type: TEX_SOFT=0, TEX_SHARP=1,TEX_SHAPER=2 */
-
+
float (*waveform[3])(float); /* create array of pointers to waveform functions */
waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */
waveform[1] = tex_saw;
waveform[2] = tex_tri;
-
+
if ((wf>TEX_TRI) || (wf<TEX_SIN)) wf=0; /* check to be sure noisebasis2 isn't initialized ahead of time */
-
+
n = 5.0f * (x + y + z);
-
+
mi = n + tex->turbul * BLI_gTurbulence(tex->noisesize, x, y, z, tex->noisedepth, (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
if (mt>=TEX_SOFT) { /* TEX_SOFT always true */
@@ -327,9 +327,9 @@ static int marble(Tex *tex, const float texvec[3], TexResult *texres)
texres->nor[0] = marble_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]);
texres->nor[1] = marble_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]);
texres->nor[2] = marble_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla);
-
+
tex_normal_derivate(tex, texres);
-
+
rv |= TEX_NOR;
}
@@ -397,8 +397,8 @@ static int magic(Tex *tex, const float texvec[3], TexResult *texres)
if (turb!=0.0f) {
turb*= 2.0f;
- x/= turb;
- y/= turb;
+ x/= turb;
+ y/= turb;
z/= turb;
}
texres->tr = 0.5f - x;
@@ -406,10 +406,10 @@ static int magic(Tex *tex, const float texvec[3], TexResult *texres)
texres->tb = 0.5f - z;
texres->tin= (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb);
-
+
BRICONTRGB;
texres->ta = 1.0f;
-
+
return TEX_RGB;
}
@@ -420,9 +420,9 @@ static int stucci(Tex *tex, const float texvec[3], TexResult *texres)
{
float nor[3], b2, ofs;
int retval= TEX_INT;
-
+
b2= BLI_gNoise(tex->noisesize, texvec[0], texvec[1], texvec[2], (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
-
+
ofs= tex->turbul/200.0f;
if (tex->stype) ofs*=(b2*b2);
@@ -431,27 +431,27 @@ static int stucci(Tex *tex, const float texvec[3], TexResult *texres)
nor[2] = BLI_gNoise(tex->noisesize, texvec[0], texvec[1], texvec[2]+ofs, (tex->noisetype!=TEX_NOISESOFT), tex->noisebasis);
texres->tin= nor[2];
-
+
if (texres->nor) {
-
+
copy_v3_v3(texres->nor, nor);
tex_normal_derivate(tex, texres);
-
+
if (tex->stype==TEX_WALLOUT) {
texres->nor[0]= -texres->nor[0];
texres->nor[1]= -texres->nor[1];
texres->nor[2]= -texres->nor[2];
}
-
+
retval |= TEX_NOR;
}
-
+
if (tex->stype==TEX_WALLOUT)
texres->tin= 1.0f-texres->tin;
-
+
if (texres->tin<0.0f)
texres->tin= 0.0f;
-
+
return retval;
}
@@ -477,7 +477,7 @@ static float mg_mFractalOrfBmTex(Tex *tex, const float texvec[3], TexResult *tex
texres->nor[0] = tex->ns_outscale*mgravefunc(texvec[0] + offs, texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->noisebasis);
texres->nor[1] = tex->ns_outscale*mgravefunc(texvec[0], texvec[1] + offs, texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->noisebasis);
texres->nor[2] = tex->ns_outscale*mgravefunc(texvec[0], texvec[1], texvec[2] + offs, tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->noisebasis);
-
+
tex_normal_derivate(tex, texres);
rv |= TEX_NOR;
}
@@ -507,7 +507,7 @@ static float mg_ridgedOrHybridMFTex(Tex *tex, const float texvec[3], TexResult *
texres->nor[0] = tex->ns_outscale*mgravefunc(texvec[0] + offs, texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->mg_gain, tex->noisebasis);
texres->nor[1] = tex->ns_outscale*mgravefunc(texvec[0], texvec[1] + offs, texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->mg_gain, tex->noisebasis);
texres->nor[2] = tex->ns_outscale*mgravefunc(texvec[0], texvec[1], texvec[2] + offs, tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->mg_gain, tex->noisebasis);
-
+
tex_normal_derivate(tex, texres);
rv |= TEX_NOR;
}
@@ -532,7 +532,7 @@ static float mg_HTerrainTex(Tex *tex, const float texvec[3], TexResult *texres)
texres->nor[0] = tex->ns_outscale*mg_HeteroTerrain(texvec[0] + offs, texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->noisebasis);
texres->nor[1] = tex->ns_outscale*mg_HeteroTerrain(texvec[0], texvec[1] + offs, texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->noisebasis);
texres->nor[2] = tex->ns_outscale*mg_HeteroTerrain(texvec[0], texvec[1], texvec[2] + offs, tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->noisebasis);
-
+
tex_normal_derivate(tex, texres);
rv |= TEX_NOR;
}
@@ -630,7 +630,7 @@ static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres)
texres->nor[1] = sc * fabsf(tex->vn_w1*da[0] + tex->vn_w2*da[1] + tex->vn_w3*da[2] + tex->vn_w4*da[3]);
voronoi(texvec[0], texvec[1], texvec[2] + offs, da, pa, tex->vn_mexp, tex->vn_distm);
texres->nor[2] = sc * fabsf(tex->vn_w1*da[0] + tex->vn_w2*da[1] + tex->vn_w3*da[2] + tex->vn_w4*da[3]);
-
+
tex_normal_derivate(tex, texres);
rv |= TEX_NOR;
}
@@ -640,7 +640,7 @@ static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres)
texres->ta = 1.0;
return (rv | TEX_RGB);
}
-
+
BRICONT;
return rv;
@@ -653,20 +653,20 @@ static int texnoise(Tex *tex, TexResult *texres, int thread)
{
float div=3.0;
int val, ran, loop, shift = 29;
-
+
ran= BLI_rng_thread_rand(random_tex_array, thread);
-
+
loop= tex->noisedepth;
/* start from top bits since they have more variance */
val= ((ran >> shift) & 3);
-
+
while (loop--) {
- shift -= 2;
+ shift -= 2;
val *= ((ran >> shift) & 3);
div *= 3.0f;
}
-
+
texres->tin= ((float)val)/div;
BRICONT;
@@ -679,7 +679,7 @@ static int cubemap_glob(const float n[3], float x, float y, float z, float *adr1
{
float x1, y1, z1, nor[3];
int ret;
-
+
if (n==NULL) {
nor[0]= x; nor[1]= y; nor[2]= z; /* use local render coord */
}
@@ -690,7 +690,7 @@ static int cubemap_glob(const float n[3], float x, float y, float z, float *adr1
x1 = fabsf(nor[0]);
y1 = fabsf(nor[1]);
z1 = fabsf(nor[2]);
-
+
if (z1>=x1 && z1>=y1) {
*adr1 = (x + 1.0f) / 2.0f;
*adr2 = (y + 1.0f) / 2.0f;
@@ -719,13 +719,13 @@ static void do_2d_mapping(
Tex *tex;
float fx, fy, fac1, area[8];
int ok, proj, areaflag= 0, wrap;
-
+
/* mtex variables localized, only cubemap doesn't cooperate yet... */
wrap= mtex->mapping;
tex= mtex->tex;
if (!(dxt && dyt)) {
-
+
if (wrap==MTEX_FLAT) {
fx = (texvec[0] + 1.0f) / 2.0f;
fy = (texvec[1] + 1.0f) / 2.0f;
@@ -735,15 +735,15 @@ static void do_2d_mapping(
else {
cubemap_glob(n, texvec[0], texvec[1], texvec[2], &fx, &fy);
}
-
+
/* repeat */
if (tex->extend==TEX_REPEAT) {
if (tex->xrepeat>1) {
float origf= fx *= tex->xrepeat;
-
+
if (fx>1.0f) fx -= (int)(fx);
else if (fx<0.0f) fx+= 1-(int)(fx);
-
+
if (tex->flag & TEX_REPEAT_XMIR) {
int orig= (int)floor(origf);
if (orig & 1)
@@ -752,10 +752,10 @@ static void do_2d_mapping(
}
if (tex->yrepeat>1) {
float origf= fy *= tex->yrepeat;
-
+
if (fy>1.0f) fy -= (int)(fy);
else if (fy<0.0f) fy+= 1-(int)(fy);
-
+
if (tex->flag & TEX_REPEAT_YMIR) {
int orig= (int)floor(origf);
if (orig & 1)
@@ -777,7 +777,7 @@ static void do_2d_mapping(
texvec[1]= fy;
}
else {
-
+
if (wrap==MTEX_FLAT) {
fx= (texvec[0] + 1.0f) / 2.0f;
fy= (texvec[1] + 1.0f) / 2.0f;
@@ -854,55 +854,55 @@ static void do_2d_mapping(
dyt[2] *= 0.5f;
}
-
+
/* if area, then reacalculate dxt[] and dyt[] */
if (areaflag) {
- fx= area[0];
+ fx= area[0];
fy= area[1];
dxt[0]= area[2]-fx;
dxt[1]= area[3]-fy;
dyt[0]= area[4]-fx;
dyt[1]= area[5]-fy;
}
-
+
/* repeat */
if (tex->extend==TEX_REPEAT) {
float max= 1.0f;
if (tex->xrepeat>1) {
float origf= fx *= tex->xrepeat;
-
+
/* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */
if (tex->texfilter == TXF_BOX) {
if (fx>1.0f) fx -= (int)(fx);
else if (fx<0.0f) fx+= 1-(int)(fx);
-
+
if (tex->flag & TEX_REPEAT_XMIR) {
int orig= (int)floor(origf);
if (orig & 1)
fx= 1.0f-fx;
}
}
-
+
max= tex->xrepeat;
-
+
dxt[0]*= tex->xrepeat;
dyt[0]*= tex->xrepeat;
}
if (tex->yrepeat>1) {
float origf= fy *= tex->yrepeat;
-
+
/* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */
if (tex->texfilter == TXF_BOX) {
if (fy>1.0f) fy -= (int)(fy);
else if (fy<0.0f) fy+= 1-(int)(fy);
-
+
if (tex->flag & TEX_REPEAT_YMIR) {
int orig= (int)floor(origf);
if (orig & 1)
fy= 1.0f-fy;
}
}
-
+
if (max<tex->yrepeat)
max= tex->yrepeat;
@@ -913,7 +913,7 @@ static void do_2d_mapping(
dxt[2]*= max;
dyt[2]*= max;
}
-
+
}
/* crop */
if (tex->cropxmin!=0.0f || tex->cropxmax!=1.0f) {
@@ -928,7 +928,7 @@ static void do_2d_mapping(
dxt[1]*= fac1;
dyt[1]*= fac1;
}
-
+
texvec[0]= fx;
texvec[1]= fy;
@@ -953,7 +953,7 @@ static int multitex(Tex *tex,
int retval = 0; /* return value, int:0, col:1, nor:2, everything:3 */
texres->talpha = false; /* is set when image texture returns alpha (considered premul) */
-
+
if (use_nodes && tex->use_nodes && tex->nodetree) {
const float cfra = 1.0f; /* This was only set for Blender Internal render before. */
retval = ntreeTexExecTree(tex->nodetree, texres, texvec, dxt, dyt, osatex, thread,
@@ -1072,7 +1072,7 @@ static int multitex_nodes_intern(Tex *tex,
if (mtex)
which_output= mtex->which_output;
-
+
if (tex->type==TEX_IMAGE) {
int rgbnor;
@@ -1093,7 +1093,7 @@ static int multitex_nodes_intern(Tex *tex,
if (mtex->mapto & (MAP_COL)) {
ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool);
-
+
/* don't linearize float buffers, assumed to be linear */
if (ibuf != NULL &&
ibuf->rect_float == NULL &&
@@ -1110,12 +1110,12 @@ static int multitex_nodes_intern(Tex *tex,
/* we don't have mtex, do default flat 2d projection */
MTex localmtex;
float texvec_l[3], dxt_l[3], dyt_l[3];
-
+
localmtex.mapping= MTEX_FLAT;
localmtex.tex= tex;
localmtex.object= NULL;
localmtex.texco= TEXCO_ORCO;
-
+
copy_v3_v3(texvec_l, texvec);
if (dxt && dyt) {
copy_v3_v3(dxt_l, dxt);
@@ -1125,7 +1125,7 @@ static int multitex_nodes_intern(Tex *tex,
zero_v3(dxt_l);
zero_v3(dyt_l);
}
-
+
do_2d_mapping(&localmtex, texvec_l, NULL, dxt_l, dyt_l);
rgbnor = multitex(tex,
texvec_l,
@@ -1244,7 +1244,7 @@ int multitex_ext_safe(Tex *tex, float texvec[3], TexResult *texres, struct Image
void texture_rgb_blend(float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype)
{
float facm;
-
+
switch (blendtype) {
case MTEX_BLEND:
fact*= facg;
@@ -1254,7 +1254,7 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa
in[1]= (fact*tex[1] + facm*out[1]);
in[2]= (fact*tex[2] + facm*out[2]);
break;
-
+
case MTEX_MUL:
fact*= facg;
facm= 1.0f-fact;
@@ -1274,7 +1274,7 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa
case MTEX_OVERLAY:
fact*= facg;
facm= 1.0f-fact;
-
+
if (out[0] < 0.5f)
in[0] = out[0] * (facm + 2.0f*fact*tex[0]);
else
@@ -1288,7 +1288,7 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa
else
in[2] = 1.0f - (facm + 2.0f*fact*(1.0f - tex[2])) * (1.0f - out[2]);
break;
-
+
case MTEX_SUB:
fact= -fact;
ATTR_FALLTHROUGH;
@@ -1302,7 +1302,7 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa
case MTEX_DIV:
fact*= facg;
facm= 1.0f-fact;
-
+
if (tex[0]!=0.0f)
in[0]= facm*out[0] + fact*out[0]/tex[0];
if (tex[1]!=0.0f)
@@ -1323,7 +1323,7 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa
case MTEX_DARK:
fact*= facg;
facm= 1.0f-fact;
-
+
in[0] = min_ff(out[0], tex[0])*fact + out[0]*facm;
in[1] = min_ff(out[1], tex[1])*fact + out[1]*facm;
in[2] = min_ff(out[2], tex[2])*fact + out[2]*facm;
@@ -1336,7 +1336,7 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa
in[1] = max_ff(fact * tex[1], out[1]);
in[2] = max_ff(fact * tex[2], out[2]);
break;
-
+
case MTEX_BLEND_HUE:
fact*= facg;
copy_v3_v3(in, out);
@@ -1357,16 +1357,16 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa
copy_v3_v3(in, out);
ramp_blend(MA_RAMP_COLOR, in, fact, tex);
break;
- case MTEX_SOFT_LIGHT:
- fact*= facg;
+ case MTEX_SOFT_LIGHT:
+ fact*= facg;
copy_v3_v3(in, out);
ramp_blend(MA_RAMP_SOFT, in, fact, tex);
- break;
- case MTEX_LIN_LIGHT:
- fact*= facg;
+ break;
+ case MTEX_LIN_LIGHT:
+ fact*= facg;
copy_v3_v3(in, out);
ramp_blend(MA_RAMP_LINEAR, in, fact, tex);
- break;
+ break;
}
}
@@ -1376,7 +1376,7 @@ float texture_value_blend(float tex, float out, float fact, float facg, int blen
int flip= (facg < 0.0f);
facg= fabsf(facg);
-
+
fact*= facg;
facm= 1.0f-fact;
if (flip) SWAP(float, fact, facm);
@@ -1429,19 +1429,19 @@ float texture_value_blend(float tex, float out, float fact, float facg, int blen
if (col > out) in= col; else in= out;
break;
- case MTEX_SOFT_LIGHT:
+ case MTEX_SOFT_LIGHT:
scf=1.0f - (1.0f - tex) * (1.0f - out);
in= facm*out + fact * ((1.0f - out) * tex * out) + (out * scf);
- break;
+ break;
- case MTEX_LIN_LIGHT:
+ case MTEX_LIN_LIGHT:
if (tex > 0.5f)
in = out + fact*(2.0f*(tex - 0.5f));
- else
+ else
in = out + fact*(2.0f*tex - 1.0f);
break;
}
-
+
return in;
}
@@ -1459,26 +1459,26 @@ int externtex(const MTex *mtex,
TexResult texr;
float dxt[3], dyt[3], texvec[3];
int rgb;
-
+
tex= mtex->tex;
if (tex==NULL) return 0;
texr.nor= NULL;
-
+
/* placement */
if (mtex->projx) texvec[0]= mtex->size[0]*(vec[mtex->projx-1]+mtex->ofs[0]);
else texvec[0]= mtex->size[0]*(mtex->ofs[0]);
-
+
if (mtex->projy) texvec[1]= mtex->size[1]*(vec[mtex->projy-1]+mtex->ofs[1]);
else texvec[1]= mtex->size[1]*(mtex->ofs[1]);
-
+
if (mtex->projz) texvec[2]= mtex->size[2]*(vec[mtex->projz-1]+mtex->ofs[2]);
else texvec[2]= mtex->size[2]*(mtex->ofs[2]);
-
+
/* texture */
if (tex->type==TEX_IMAGE) {
do_2d_mapping(mtex, texvec, NULL, dxt, dyt);
}
-
+
rgb = multitex(tex,
texvec,
dxt, dyt,
@@ -1489,7 +1489,7 @@ int externtex(const MTex *mtex,
skip_load_image,
texnode_preview,
true);
-
+
if (rgb) {
texr.tin = IMB_colormanagement_get_luminance(&texr.tr);
}
@@ -1498,7 +1498,7 @@ int externtex(const MTex *mtex,
texr.tg= mtex->g;
texr.tb= mtex->b;
}
-
+
*tin= texr.tin;
*tr= texr.tr;
*tg= texr.tg;
diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c
new file mode 100644
index 00000000000..99d2436d4bc
--- /dev/null
+++ b/source/blender/render/intern/source/rendercore.c
@@ -0,0 +1,2030 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributors: Hos, Robert Wenzlaff.
+ * Contributors: 2004/2005/2006 Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/rendercore.c
+ * \ingroup render
+ */
+
+
+/* system includes */
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <assert.h>
+
+/* External modules: */
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_rand.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_image_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+#include "DNA_group_types.h"
+
+/* local include */
+#include "renderpipeline.h"
+#include "render_result.h"
+#include "render_types.h"
+#include "renderdatabase.h"
+#include "occlusion.h"
+#include "pixelblending.h"
+#include "pixelshading.h"
+#include "shadbuf.h"
+#include "shading.h"
+#include "sss.h"
+#include "zbuf.h"
+
+/* own include */
+#include "rendercore.h"
+
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* x and y are current pixels in rect to be rendered */
+/* do not normalize! */
+void calc_view_vector(float view[3], float x, float y)
+{
+
+ view[2]= -ABS(R.clipsta);
+
+ if (R.r.mode & R_ORTHO) {
+ view[0]= view[1]= 0.0f;
+ }
+ else {
+
+ if (R.r.mode & R_PANORAMA) {
+ x-= R.panodxp;
+ }
+
+ /* move x and y to real viewplane coords */
+ x = (x / (float)R.winx);
+ view[0] = R.viewplane.xmin + x * BLI_rctf_size_x(&R.viewplane);
+
+ y = (y / (float)R.winy);
+ view[1] = R.viewplane.ymin + y * BLI_rctf_size_y(&R.viewplane);
+
+// if (R.flag & R_SEC_FIELD) {
+// if (R.r.mode & R_ODDFIELD) view[1]= (y+R.ystart)*R.ycor;
+// else view[1]= (y+R.ystart+1.0)*R.ycor;
+// }
+// else view[1]= (y+R.ystart+R.bluroffsy+0.5)*R.ycor;
+
+ if (R.r.mode & R_PANORAMA) {
+ float u= view[0] + R.panodxv; float v= view[2];
+ view[0]= R.panoco*u + R.panosi*v;
+ view[2]= -R.panosi*u + R.panoco*v;
+ }
+ }
+}
+
+void calc_renderco_ortho(float co[3], float x, float y, int z)
+{
+ /* x and y 3d coordinate can be derived from pixel coord and winmat */
+ float fx= 2.0f/(R.winx*R.winmat[0][0]);
+ float fy= 2.0f/(R.winy*R.winmat[1][1]);
+ float zco;
+
+ co[0]= (x - 0.5f*R.winx)*fx - R.winmat[3][0]/R.winmat[0][0];
+ co[1]= (y - 0.5f*R.winy)*fy - R.winmat[3][1]/R.winmat[1][1];
+
+ zco= ((float)z)/2147483647.0f;
+ co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] );
+}
+
+void calc_renderco_zbuf(float co[3], const float view[3], int z)
+{
+ float fac, zco;
+
+ /* inverse of zbuf calc: zbuf = MAXZ*hoco_z/hoco_w */
+ zco= ((float)z)/2147483647.0f;
+ co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] );
+
+ fac= co[2]/view[2];
+ co[0]= fac*view[0];
+ co[1]= fac*view[1];
+}
+
+/* also used in zbuf.c and shadbuf.c */
+int count_mask(unsigned short mask)
+{
+ if (R.samples)
+ return (R.samples->cmask[mask & 255]+R.samples->cmask[mask>>8]);
+ return 0;
+}
+
+static int calchalo_z(HaloRen *har, int zz)
+{
+
+ if (har->type & HA_ONLYSKY) {
+ if (zz < 0x7FFFFFF0) zz= - 0x7FFFFF; /* edge render messes zvalues */
+ }
+ else {
+ zz= (zz>>8);
+ }
+ return zz;
+}
+
+
+
+static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, int od, float dist, float xn, float yn, PixStr *ps)
+{
+ float col[4], accol[4], fac;
+ int amount, amountm, zz, flarec, sample, fullsample, mask=0;
+
+ fullsample= (totsample > 1);
+ amount= 0;
+ accol[0] = accol[1] = accol[2] = accol[3]= 0.0f;
+ col[0] = col[1] = col[2] = col[3]= 0.0f;
+ flarec= har->flarec;
+
+ while (ps) {
+ amountm= count_mask(ps->mask);
+ amount+= amountm;
+
+ zz= calchalo_z(har, ps->z);
+ if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) {
+ if (shadeHaloFloat(har, col, zz, dist, xn, yn, flarec)) {
+ flarec= 0;
+
+ if (fullsample) {
+ for (sample=0; sample<totsample; sample++) {
+ if (ps->mask & (1 << sample)) {
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ addalphaAddfacFloat(pass + od*4, col, har->add);
+ }
+ }
+ }
+ else {
+ fac= ((float)amountm)/(float)R.osa;
+ accol[0]+= fac*col[0];
+ accol[1]+= fac*col[1];
+ accol[2]+= fac*col[2];
+ accol[3]+= fac*col[3];
+ }
+ }
+ }
+
+ mask |= ps->mask;
+ ps= ps->next;
+ }
+
+ /* now do the sky sub-pixels */
+ amount= R.osa-amount;
+ if (amount) {
+ if (shadeHaloFloat(har, col, 0x7FFFFF, dist, xn, yn, flarec)) {
+ if (!fullsample) {
+ fac= ((float)amount)/(float)R.osa;
+ accol[0]+= fac*col[0];
+ accol[1]+= fac*col[1];
+ accol[2]+= fac*col[2];
+ accol[3]+= fac*col[3];
+ }
+ }
+ }
+
+ if (fullsample) {
+ for (sample=0; sample<totsample; sample++) {
+ if (!(mask & (1 << sample))) {
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ addalphaAddfacFloat(pass + od*4, col, har->add);
+ }
+ }
+ }
+ else {
+ col[0]= accol[0];
+ col[1]= accol[1];
+ col[2]= accol[2];
+ col[3]= accol[3];
+
+ for (sample=0; sample<totsample; sample++) {
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ addalphaAddfacFloat(pass + od*4, col, har->add);
+ }
+ }
+}
+
+static void halo_tile(RenderPart *pa, RenderLayer *rl)
+{
+ RenderLayer *rlpp[RE_MAX_OSA];
+ HaloRen *har;
+ rcti disprect= pa->disprect, testrect= pa->disprect;
+ float dist, xsq, ysq, xn, yn;
+ float col[4];
+ intptr_t *rd= NULL;
+ int a, *rz, zz, y, sample, totsample, od;
+ short minx, maxx, miny, maxy, x;
+ unsigned int lay= rl->lay;
+
+ /* we don't render halos in the cropped area, gives errors in flare counter */
+ if (pa->crop) {
+ testrect.xmin+= pa->crop;
+ testrect.xmax-= pa->crop;
+ testrect.ymin+= pa->crop;
+ testrect.ymax-= pa->crop;
+ }
+
+ totsample= get_sample_layers(pa, rl, rlpp);
+
+ for (a=0; a<R.tothalo; a++) {
+ har= R.sortedhalos[a];
+
+ /* layer test, clip halo with y */
+ if ((har->lay & lay) == 0) {
+ /* pass */
+ }
+ else if (testrect.ymin > har->maxy) {
+ /* pass */
+ }
+ else if (testrect.ymax < har->miny) {
+ /* pass */
+ }
+ else {
+
+ minx= floor(har->xs-har->rad);
+ maxx= ceil(har->xs+har->rad);
+
+ if (testrect.xmin > maxx) {
+ /* pass */
+ }
+ else if (testrect.xmax < minx) {
+ /* pass */
+ }
+ else {
+
+ minx = max_ii(minx, testrect.xmin);
+ maxx = min_ii(maxx, testrect.xmax);
+
+ miny = max_ii(har->miny, testrect.ymin);
+ maxy = min_ii(har->maxy, testrect.ymax);
+
+ for (y=miny; y<maxy; y++) {
+ int rectofs= (y-disprect.ymin)*pa->rectx + (minx - disprect.xmin);
+ rz= pa->rectz + rectofs;
+ od= rectofs;
+
+ if (pa->rectdaps)
+ rd= pa->rectdaps + rectofs;
+
+ yn= (y-har->ys)*R.ycor;
+ ysq= yn*yn;
+
+ for (x=minx; x<maxx; x++, rz++, od++) {
+ xn= x- har->xs;
+ xsq= xn*xn;
+ dist= xsq+ysq;
+ if (dist<har->radsq) {
+ if (rd && *rd) {
+ halo_pixelstruct(har, rlpp, totsample, od, dist, xn, yn, (PixStr *)*rd);
+ }
+ else {
+ zz= calchalo_z(har, *rz);
+ if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) {
+ if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) {
+ for (sample=0; sample<totsample; sample++) {
+ float * rect= RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ addalphaAddfacFloat(rect + od*4, col, har->add);
+ }
+ }
+ }
+ }
+ }
+ if (rd) rd++;
+ }
+ }
+ }
+ }
+ if (R.test_break(R.tbh) ) break;
+ }
+}
+
+static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
+{
+ RenderLayer *rlpp[RE_MAX_OSA];
+ ShadeInput shi;
+ float *pass;
+ float fac, col[4];
+ intptr_t *rd= pa->rectdaps;
+ const int *rz= pa->rectz;
+ int x, y, sample, totsample, fullsample, od;
+
+ totsample= get_sample_layers(pa, rl, rlpp);
+ fullsample= (totsample > 1);
+
+ shade_input_initialize(&shi, pa, rl, 0); /* this zero's ShadeInput for us */
+
+ for (od=0, y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
+ for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, rz++, od++) {
+
+ calc_view_vector(shi.view, x, y);
+
+ if (rd && *rd) {
+ PixStr *ps= (PixStr *)*rd;
+ int count, totsamp= 0, mask= 0;
+
+ while (ps) {
+ if (R.r.mode & R_ORTHO)
+ calc_renderco_ortho(shi.co, (float)x, (float)y, ps->z);
+ else
+ calc_renderco_zbuf(shi.co, shi.view, ps->z);
+
+ totsamp+= count= count_mask(ps->mask);
+ mask |= ps->mask;
+
+ col[0]= col[1]= col[2]= col[3]= 0.0f;
+ renderspothalo(&shi, col, 1.0f);
+
+ if (fullsample) {
+ for (sample=0; sample<totsample; sample++) {
+ if (ps->mask & (1 << sample)) {
+ pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ pass += od * 4;
+ pass[0]+= col[0];
+ pass[1]+= col[1];
+ pass[2]+= col[2];
+ pass[3]+= col[3];
+ if (pass[3]>1.0f) pass[3]= 1.0f;
+ }
+ }
+ }
+ else {
+ fac= ((float)count)/(float)R.osa;
+ pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
+ pass += od * 4;
+ pass[0]+= fac*col[0];
+ pass[1]+= fac*col[1];
+ pass[2]+= fac*col[2];
+ pass[3]+= fac*col[3];
+ if (pass[3]>1.0f) pass[3]= 1.0f;
+ }
+
+ ps= ps->next;
+ }
+
+ if (totsamp<R.osa) {
+ shi.co[2]= 0.0f;
+
+ col[0]= col[1]= col[2]= col[3]= 0.0f;
+ renderspothalo(&shi, col, 1.0f);
+
+ if (fullsample) {
+ for (sample=0; sample<totsample; sample++) {
+ if (!(mask & (1 << sample))) {
+
+ pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ pass += od * 4;
+ pass[0]+= col[0];
+ pass[1]+= col[1];
+ pass[2]+= col[2];
+ pass[3]+= col[3];
+ if (pass[3]>1.0f) pass[3]= 1.0f;
+ }
+ }
+ }
+ else {
+ fac= ((float)R.osa-totsamp)/(float)R.osa;
+ pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
+ pass += od * 4;
+ pass[0]+= fac*col[0];
+ pass[1]+= fac*col[1];
+ pass[2]+= fac*col[2];
+ pass[3]+= fac*col[3];
+ if (pass[3]>1.0f) pass[3]= 1.0f;
+ }
+ }
+ }
+ else {
+ if (R.r.mode & R_ORTHO)
+ calc_renderco_ortho(shi.co, (float)x, (float)y, *rz);
+ else
+ calc_renderco_zbuf(shi.co, shi.view, *rz);
+
+ col[0]= col[1]= col[2]= col[3]= 0.0f;
+ renderspothalo(&shi, col, 1.0f);
+
+ for (sample=0; sample<totsample; sample++) {
+ pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ pass += od * 4;
+ pass[0]+= col[0];
+ pass[1]+= col[1];
+ pass[2]+= col[2];
+ pass[3]+= col[3];
+ if (pass[3]>1.0f) pass[3]= 1.0f;
+ }
+ }
+
+ if (rd) rd++;
+ }
+ if (y&1)
+ if (R.test_break(R.tbh)) break;
+ }
+}
+
+
+/* ********************* MAINLOOPS ******************** */
+
+/* osa version */
+static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, ShadeInput *shi, ShadeResult *shr)
+{
+ RenderPass *rpass;
+
+ for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
+ float *fp, *col= NULL;
+ int pixsize= 3;
+
+ if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
+ add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx);
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_Z)) {
+ fp = rpass->rect + offset;
+ *fp = shr->z;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_RGBA)) {
+ col = shr->col;
+ pixsize = 4;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_EMIT)) {
+ col = shr->emit;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) {
+ col = shr->diff;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_SPEC)) {
+ col = shr->spec;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_SHADOW)) {
+ col = shr->shad;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_AO)) {
+ col = shr->ao;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) {
+ col = shr->env;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_INDIRECT)) {
+ col = shr->indirect;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_REFLECT)) {
+ col = shr->refl;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_REFRACT)) {
+ col = shr->refr;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_NORMAL)) {
+ col = shr->nor;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_UV)) {
+ /* box filter only, gauss will screwup UV too much */
+ if (shi->totuv) {
+ float mult = (float)count_mask(curmask)/(float)R.osa;
+ fp = rpass->rect + 3*offset;
+ fp[0]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[0]);
+ fp[1]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[1]);
+ fp[2]+= mult;
+ }
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_INDEXOB)) {
+ /* no filter */
+ if (shi->vlr) {
+ fp = rpass->rect + offset;
+ if (*fp==0.0f)
+ *fp = (float)shi->obr->ob->index;
+ }
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_INDEXMA)) {
+ /* no filter */
+ if (shi->vlr) {
+ fp = rpass->rect + offset;
+ if (*fp==0.0f)
+ *fp = (float)shi->mat->index;
+ }
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_MIST)) {
+ /* */
+ col = &shr->mist;
+ pixsize = 1;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) {
+ /* add minimum speed in pixel, no filter */
+ fp = rpass->rect + 4*offset;
+ if ( (ABS(shr->winspeed[0]) + ABS(shr->winspeed[1]))< (ABS(fp[0]) + ABS(fp[1])) ) {
+ fp[0] = shr->winspeed[0];
+ fp[1] = shr->winspeed[1];
+ }
+ if ( (ABS(shr->winspeed[2]) + ABS(shr->winspeed[3]))< (ABS(fp[2]) + ABS(fp[3])) ) {
+ fp[2] = shr->winspeed[2];
+ fp[3] = shr->winspeed[3];
+ }
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_RAYHITS)) {
+ /* */
+ col = shr->rayhits;
+ pixsize= 4;
+ }
+
+ if (col) {
+ fp= rpass->rect + pixsize*offset;
+ add_filt_fmask_pixsize(curmask, col, fp, rectx, pixsize);
+ }
+ }
+}
+
+/* non-osa version */
+static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult *shr)
+{
+ RenderPass *rpass;
+ float *fp;
+
+ for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
+ float *col= NULL, uvcol[3];
+ int a, pixsize= 3;
+
+ if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
+ /* copy combined to use for preview */
+ copy_v4_v4(rpass->rect + 4*offset, shr->combined);
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_Z)) {
+ fp = rpass->rect + offset;
+ *fp = shr->z;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_RGBA)) {
+ col = shr->col;
+ pixsize = 4;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_EMIT)) {
+ col = shr->emit;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) {
+ col = shr->diff;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_SPEC)) {
+ col = shr->spec;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_SHADOW)) {
+ col = shr->shad;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_AO)) {
+ col = shr->ao;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) {
+ col = shr->env;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_INDIRECT)) {
+ col = shr->indirect;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_REFLECT)) {
+ col = shr->refl;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_REFRACT)) {
+ col = shr->refr;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_NORMAL)) {
+ col = shr->nor;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_UV)) {
+ if (shi->totuv) {
+ uvcol[0] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[0];
+ uvcol[1] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[1];
+ uvcol[2] = 1.0f;
+ col = uvcol;
+ }
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) {
+ col = shr->winspeed;
+ pixsize = 4;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_INDEXOB)) {
+ if (shi->vlr) {
+ fp = rpass->rect + offset;
+ *fp = (float)shi->obr->ob->index;
+ }
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_INDEXMA)) {
+ if (shi->vlr) {
+ fp = rpass->rect + offset;
+ *fp = (float)shi->mat->index;
+ }
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_MIST)) {
+ fp = rpass->rect + offset;
+ *fp = shr->mist;
+ }
+ else if (STREQ(rpass->name, RE_PASSNAME_RAYHITS)) {
+ col = shr->rayhits;
+ pixsize = 4;
+ }
+
+ if (col) {
+ fp = rpass->rect + pixsize*offset;
+ for (a=0; a<pixsize; a++)
+ fp[a] = col[a];
+ }
+ }
+}
+
+int get_sample_layers(RenderPart *pa, RenderLayer *rl, RenderLayer **rlpp)
+{
+
+ if (pa->fullresult.first) {
+ int sample, nr= BLI_findindex(&pa->result->layers, rl);
+
+ for (sample=0; sample<R.osa; sample++) {
+ RenderResult *rr= BLI_findlink(&pa->fullresult, sample);
+
+ rlpp[sample]= BLI_findlink(&rr->layers, nr);
+ }
+ return R.osa;
+ }
+ else {
+ rlpp[0]= rl;
+ return 1;
+ }
+}
+
+
+/* only do sky, is default in the solid layer (shade_tile) btw */
+static void sky_tile(RenderPart *pa, RenderLayer *rl)
+{
+ RenderLayer *rlpp[RE_MAX_OSA];
+ int x, y, od=0, totsample;
+
+ if (R.r.alphamode!=R_ADDSKY)
+ return;
+
+ totsample= get_sample_layers(pa, rl, rlpp);
+
+ for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
+ for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od+=4) {
+ float col[4];
+ int sample;
+ bool done = false;
+
+ for (sample= 0; sample<totsample; sample++) {
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+ pass += od;
+
+ if (pass[3]<1.0f) {
+
+ if (done==0) {
+ shadeSkyPixel(col, x, y, pa->thread);
+ done = true;
+ }
+
+ if (pass[3]==0.0f) {
+ copy_v4_v4(pass, col);
+ pass[3] = 1.0f;
+ }
+ else {
+ addAlphaUnderFloat(pass, col);
+ pass[3] = 1.0f;
+ }
+ }
+ }
+ }
+
+ if (y&1)
+ if (R.test_break(R.tbh)) break;
+ }
+}
+
+static void atm_tile(RenderPart *pa, RenderLayer *rl)
+{
+ RenderPass *zpass;
+ GroupObject *go;
+ LampRen *lar;
+ RenderLayer *rlpp[RE_MAX_OSA];
+ int totsample;
+ int x, y, od= 0;
+
+ totsample= get_sample_layers(pa, rl, rlpp);
+
+ /* check that z pass is enabled */
+ if (pa->rectz==NULL) return;
+ for (zpass= rl->passes.first; zpass; zpass= zpass->next)
+ if (STREQ(zpass->name, RE_PASSNAME_Z))
+ break;
+
+ if (zpass==NULL) return;
+
+ /* check for at least one sun lamp that its atmosphere flag is enabled */
+ for (go=R.lights.first; go; go= go->next) {
+ lar= go->lampren;
+ if (lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_AP))
+ break;
+ }
+ /* do nothign and return if there is no sun lamp */
+ if (go==NULL)
+ return;
+
+ /* for each x,y and each sample, and each sun lamp*/
+ for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
+ for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od++) {
+ int sample;
+
+ for (sample=0; sample<totsample; sample++) {
+ const float *zrect = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_Z, R.viewname) + od;
+ float *rgbrect = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname) + 4*od;
+ float rgb[3] = {0};
+ bool done = false;
+
+ for (go=R.lights.first; go; go= go->next) {
+
+
+ lar= go->lampren;
+ if (lar->type==LA_SUN && lar->sunsky) {
+
+ /* if it's sky continue and don't apply atmosphere effect on it */
+ if (*zrect >= 9.9e10f || rgbrect[3]==0.0f) {
+ continue;
+ }
+
+ if ((lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) {
+ float tmp_rgb[3];
+
+ /* skip if worldspace lamp vector is below horizon */
+ if (go->ob->obmat[2][2] < 0.f) {
+ continue;
+ }
+
+ copy_v3_v3(tmp_rgb, rgbrect);
+ if (rgbrect[3]!=1.0f) { /* de-premul */
+ mul_v3_fl(tmp_rgb, 1.0f/rgbrect[3]);
+ }
+ shadeAtmPixel(lar->sunsky, tmp_rgb, x, y, *zrect);
+ if (rgbrect[3]!=1.0f) { /* premul */
+ mul_v3_fl(tmp_rgb, rgbrect[3]);
+ }
+
+ if (done==0) {
+ copy_v3_v3(rgb, tmp_rgb);
+ done = true;
+ }
+ else {
+ rgb[0] = 0.5f*rgb[0] + 0.5f*tmp_rgb[0];
+ rgb[1] = 0.5f*rgb[1] + 0.5f*tmp_rgb[1];
+ rgb[2] = 0.5f*rgb[2] + 0.5f*tmp_rgb[2];
+ }
+ }
+ }
+ }
+
+ /* if at least for one sun lamp aerial perspective was applied*/
+ if (done) {
+ copy_v3_v3(rgbrect, rgb);
+ }
+ }
+ }
+ }
+}
+
+static void shadeDA_tile(RenderPart *pa, RenderLayer *rl)
+{
+ RenderResult *rr= pa->result;
+ ShadeSample ssamp;
+ intptr_t *rd, *rectdaps= pa->rectdaps;
+ int samp;
+ int x, y, seed, crop=0, offs=0, od;
+
+ if (R.test_break(R.tbh)) return;
+
+ /* irregular shadowb buffer creation */
+ if (R.r.mode & R_SHADOW)
+ ISB_create(pa, NULL);
+
+ /* we set per pixel a fixed seed, for random AO and shadow samples */
+ seed= pa->rectx*pa->disprect.ymin;
+
+ /* general shader info, passes */
+ shade_sample_initialize(&ssamp, pa, rl);
+
+ /* occlusion caching */
+ if (R.occlusiontree)
+ cache_occ_samples(&R, pa, &ssamp);
+
+ /* filtered render, for now we assume only 1 filter size */
+ if (pa->crop) {
+ crop= 1;
+ rectdaps+= pa->rectx + 1;
+ offs= pa->rectx + 1;
+ }
+
+ /* scanline updates have to be 2 lines behind */
+ rr->renrect.ymin = 0;
+ rr->renrect.ymax = -2*crop;
+ rr->renlay= rl;
+
+ for (y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) {
+ rd= rectdaps;
+ od= offs;
+
+ for (x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, rd++, od++) {
+ BLI_thread_srandom(pa->thread, seed++);
+
+ if (*rd) {
+ if (shade_samples(&ssamp, (PixStr *)(*rd), x, y)) {
+
+ /* multisample buffers or filtered mask filling? */
+ if (pa->fullresult.first) {
+ int a;
+ for (samp=0; samp<ssamp.tot; samp++) {
+ int smask= ssamp.shi[samp].mask;
+ for (a=0; a<R.osa; a++) {
+ int mask= 1<<a;
+ if (smask & mask)
+ add_passes(ssamp.rlpp[a], od, &ssamp.shi[samp], &ssamp.shr[samp]);
+ }
+ }
+ }
+ else {
+ for (samp=0; samp<ssamp.tot; samp++)
+ add_filt_passes(rl, ssamp.shi[samp].mask, pa->rectx, od, &ssamp.shi[samp], &ssamp.shr[samp]);
+ }
+ }
+ }
+ }
+
+ rectdaps+= pa->rectx;
+ offs+= pa->rectx;
+
+ if (y&1) if (R.test_break(R.tbh)) break;
+ }
+
+ /* disable scanline updating */
+ rr->renlay= NULL;
+
+ if (R.r.mode & R_SHADOW)
+ ISB_free(pa);
+
+ if (R.occlusiontree)
+ free_occ_samples(&R, pa);
+}
+
+/* ************* pixel struct ******** */
+
+
+static PixStrMain *addpsmain(ListBase *lb)
+{
+ PixStrMain *psm;
+
+ psm= (PixStrMain *)MEM_mallocN(sizeof(PixStrMain), "pixstrMain");
+ BLI_addtail(lb, psm);
+
+ psm->ps= (PixStr *)MEM_mallocN(4096*sizeof(PixStr), "pixstr");
+ psm->counter= 0;
+
+ return psm;
+}
+
+static void freeps(ListBase *lb)
+{
+ PixStrMain *psm, *psmnext;
+
+ for (psm= lb->first; psm; psm= psmnext) {
+ psmnext= psm->next;
+ if (psm->ps)
+ MEM_freeN(psm->ps);
+ MEM_freeN(psm);
+ }
+ BLI_listbase_clear(lb);
+}
+
+static void addps(ListBase *lb, intptr_t *rd, int obi, int facenr, int z, int maskz, unsigned short mask)
+{
+ PixStrMain *psm;
+ PixStr *ps, *last= NULL;
+
+ if (*rd) {
+ ps= (PixStr *)(*rd);
+
+ while (ps) {
+ if ( ps->obi == obi && ps->facenr == facenr ) {
+ ps->mask |= mask;
+ return;
+ }
+ last= ps;
+ ps= ps->next;
+ }
+ }
+
+ /* make new PS (pixel struct) */
+ psm= lb->last;
+
+ if (psm->counter==4095)
+ psm= addpsmain(lb);
+
+ ps= psm->ps + psm->counter++;
+
+ if (last) last->next= ps;
+ else *rd= (intptr_t)ps;
+
+ ps->next= NULL;
+ ps->obi= obi;
+ ps->facenr= facenr;
+ ps->z= z;
+ ps->maskz= maskz;
+ ps->mask = mask;
+ ps->shadfac= 0;
+}
+
+static void edge_enhance_add(RenderPart *pa, float *rectf, float *arect)
+{
+ float addcol[4];
+ int pix;
+
+ if (arect==NULL)
+ return;
+
+ for (pix= pa->rectx*pa->recty; pix>0; pix--, arect++, rectf+=4) {
+ if (*arect != 0.0f) {
+ addcol[0]= *arect * R.r.edgeR;
+ addcol[1]= *arect * R.r.edgeG;
+ addcol[2]= *arect * R.r.edgeB;
+ addcol[3]= *arect;
+ addAlphaOverFloat(rectf, addcol);
+ }
+ }
+}
+
+/* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */
+static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl)
+{
+ RenderLayer *rlpp[RE_MAX_OSA];
+ int y, sample, totsample;
+
+ totsample= get_sample_layers(pa, rl, rlpp);
+
+ /* not for full sample, there we clamp after compositing */
+ if (totsample > 1)
+ return;
+
+ for (sample= 0; sample<totsample; sample++) {
+ float *rectf = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
+
+ for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) {
+ rectf[0] = MAX2(rectf[0], 0.0f);
+ rectf[1] = MAX2(rectf[1], 0.0f);
+ rectf[2] = MAX2(rectf[2], 0.0f);
+ CLAMP(rectf[3], 0.0f, 1.0f);
+ }
+ }
+}
+
+/* adds only alpha values */
+static void edge_enhance_tile(RenderPart *pa, float *rectf, int *rectz)
+{
+ /* use zbuffer to define edges, add it to the image */
+ int y, x, col, *rz, *rz1, *rz2, *rz3;
+ int zval1, zval2, zval3;
+ float *rf;
+
+ /* shift values in zbuffer 4 to the right (anti overflows), for filter we need multiplying with 12 max */
+ rz= rectz;
+ if (rz==NULL) return;
+
+ for (y=0; y<pa->recty; y++)
+ for (x=0; x<pa->rectx; x++, rz++) (*rz)>>= 4;
+
+ rz1= rectz;
+ rz2= rz1+pa->rectx;
+ rz3= rz2+pa->rectx;
+
+ rf= rectf+pa->rectx+1;
+
+ for (y=0; y<pa->recty-2; y++) {
+ for (x=0; x<pa->rectx-2; x++, rz1++, rz2++, rz3++, rf++) {
+
+ /* prevent overflow with sky z values */
+ zval1= rz1[0] + 2*rz1[1] + rz1[2];
+ zval2= 2*rz2[0] + 2*rz2[2];
+ zval3= rz3[0] + 2*rz3[1] + rz3[2];
+
+ col= ( 4*rz2[1] - (zval1 + zval2 + zval3)/3 );
+ if (col<0) col= -col;
+
+ col >>= 5;
+ if (col > (1<<16)) col= (1<<16);
+ else col= (R.r.edgeint*col)>>8;
+
+ if (col>0) {
+ float fcol;
+
+ if (col>255) fcol= 1.0f;
+ else fcol= (float)col/255.0f;
+
+ if (R.osa)
+ *rf+= fcol/(float)R.osa;
+ else
+ *rf= fcol;
+ }
+ }
+ rz1+= 2;
+ rz2+= 2;
+ rz3+= 2;
+ rf+= 2;
+ }
+
+ /* shift back zbuf values, we might need it still */
+ rz= rectz;
+ for (y=0; y<pa->recty; y++)
+ for (x=0; x<pa->rectx; x++, rz++) (*rz)<<= 4;
+
+}
+
+static void reset_sky_speed(RenderPart *pa, RenderLayer *rl)
+{
+ /* for all pixels with max speed, set to zero */
+ RenderLayer *rlpp[RE_MAX_OSA];
+ float *fp;
+ int a, sample, totsample;
+
+ totsample= get_sample_layers(pa, rl, rlpp);
+
+ for (sample= 0; sample<totsample; sample++) {
+ fp= RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_VECTOR, R.viewname);
+ if (fp==NULL) break;
+
+ for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--)
+ if (fp[a] == PASS_VECTOR_MAX) fp[a]= 0.0f;
+ }
+}
+
+static unsigned short *make_solid_mask(RenderPart *pa)
+{
+ intptr_t *rd= pa->rectdaps;
+ unsigned short *solidmask, *sp;
+ int x;
+
+ if (rd==NULL) return NULL;
+
+ sp=solidmask= MEM_mallocN(sizeof(short)*pa->rectx*pa->recty, "solidmask");
+
+ for (x=pa->rectx*pa->recty; x>0; x--, rd++, sp++) {
+ if (*rd) {
+ PixStr *ps= (PixStr *)*rd;
+
+ *sp= ps->mask;
+ for (ps= ps->next; ps; ps= ps->next)
+ *sp |= ps->mask;
+ }
+ else
+ *sp= 0;
+ }
+
+ return solidmask;
+}
+
+static void addAlphaOverFloatMask(float *dest, float *source, unsigned short dmask, unsigned short smask)
+{
+ unsigned short shared= dmask & smask;
+ float mul= 1.0f - source[3];
+
+ if (shared) { /* overlapping masks */
+
+ /* masks differ, we make a mixture of 'add' and 'over' */
+ if (shared!=dmask) {
+ float shared_bits= (float)count_mask(shared); /* alpha over */
+ float tot_bits= (float)count_mask(smask|dmask); /* alpha add */
+
+ float add= (tot_bits - shared_bits)/tot_bits; /* add level */
+ mul= add + (1.0f-add)*mul;
+ }
+ }
+ else if (dmask && smask) {
+ /* works for premul only, of course */
+ dest[0]+= source[0];
+ dest[1]+= source[1];
+ dest[2]+= source[2];
+ dest[3]+= source[3];
+
+ return;
+ }
+
+ dest[0]= (mul*dest[0]) + source[0];
+ dest[1]= (mul*dest[1]) + source[1];
+ dest[2]= (mul*dest[2]) + source[2];
+ dest[3]= (mul*dest[3]) + source[3];
+}
+
+typedef struct ZbufSolidData {
+ RenderLayer *rl;
+ ListBase *psmlist;
+ float *edgerect;
+} ZbufSolidData;
+
+static void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *data)
+{
+ ZbufSolidData *sdata = (ZbufSolidData *)data;
+ ListBase *lb= sdata->psmlist;
+ intptr_t *rd= pa->rectdaps;
+ const int *ro= zspan->recto;
+ const int *rp= zspan->rectp;
+ const int *rz= zspan->rectz;
+ const int *rm= zspan->rectmask;
+ int x, y;
+ int mask= 1<<sample;
+
+ for (y=0; y<pa->recty; y++) {
+ for (x=0; x<pa->rectx; x++, rd++, rp++, ro++, rz++, rm++) {
+ if (*rp) {
+ addps(lb, rd, *ro, *rp, *rz, (zspan->rectmask)? *rm: 0, mask);
+ }
+ }
+ }
+
+ if (sdata->rl->layflag & SCE_LAY_EDGE)
+ if (R.r.mode & R_EDGE)
+ edge_enhance_tile(pa, sdata->edgerect, zspan->rectz);
+}
+
+/* main call for shading Delta Accum, for OSA */
+/* supposed to be fully threadable! */
+void zbufshadeDA_tile(RenderPart *pa)
+{
+ RenderResult *rr= pa->result;
+ RenderLayer *rl;
+ ListBase psmlist= {NULL, NULL};
+ float *edgerect= NULL;
+
+ /* allocate the necessary buffers */
+ /* zbuffer inits these rects */
+ pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
+ pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
+ pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
+ for (rl= rr->layers.first; rl; rl= rl->next) {
+ float *rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
+
+ if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
+ pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
+
+ /* initialize pixelstructs and edge buffer */
+ addpsmain(&psmlist);
+ pa->rectdaps= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "zbufDArectd");
+
+ if (rl->layflag & SCE_LAY_EDGE)
+ if (R.r.mode & R_EDGE)
+ edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge");
+
+ /* always fill visibility */
+ for (pa->sample=0; pa->sample<R.osa; pa->sample+=4) {
+ ZbufSolidData sdata;
+
+ sdata.rl= rl;
+ sdata.psmlist= &psmlist;
+ sdata.edgerect= edgerect;
+ zbuffer_solid(pa, rl, make_pixelstructs, &sdata);
+ if (R.test_break(R.tbh)) break;
+ }
+
+ /* shades solid */
+ if (rl->layflag & SCE_LAY_SOLID)
+ shadeDA_tile(pa, rl);
+
+ /* lamphalo after solid, before ztra, looks nicest because ztra does own halo */
+ if (R.flag & R_LAMPHALO)
+ if (rl->layflag & SCE_LAY_HALO)
+ lamphalo_tile(pa, rl);
+
+ /* halo before ztra, because ztra fills in zbuffer now */
+ if (R.flag & R_HALO)
+ if (rl->layflag & SCE_LAY_HALO)
+ halo_tile(pa, rl);
+
+ /* transp layer */
+ if (R.flag & R_ZTRA || R.totstrand) {
+ if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) {
+ if (pa->fullresult.first) {
+ zbuffer_transp_shade(pa, rl, rect, &psmlist);
+ }
+ else {
+ unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */
+
+ /* allocate, but not free here, for asynchronous display of this rect in main thread */
+ rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
+
+ /* swap for live updates, and it is used in zbuf.c!!! */
+ SWAP(float *, rl->acolrect, rect);
+ ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist);
+ SWAP(float *, rl->acolrect, rect);
+
+ /* zbuffer transp only returns ztramask if there's solid rendered */
+ if (ztramask)
+ solidmask= make_solid_mask(pa);
+
+ if (ztramask && solidmask) {
+ unsigned short *sps= solidmask, *spz= ztramask;
+ unsigned short fullmask= (1<<R.osa)-1;
+ float *fcol= rect;
+ float *acol= rl->acolrect;
+ int x;
+
+ for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) {
+ if (*sps == fullmask)
+ addAlphaOverFloat(fcol, acol);
+ else
+ addAlphaOverFloatMask(fcol, acol, *sps, *spz);
+ }
+ }
+ else {
+ float *fcol= rect;
+ float *acol= rl->acolrect;
+ int x;
+ for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
+ addAlphaOverFloat(fcol, acol);
+ }
+ }
+ if (solidmask) MEM_freeN(solidmask);
+ if (ztramask) MEM_freeN(ztramask);
+ }
+ }
+ }
+
+ /* sun/sky */
+ if (rl->layflag & SCE_LAY_SKY)
+ atm_tile(pa, rl);
+
+ /* sky before edge */
+ if (rl->layflag & SCE_LAY_SKY)
+ sky_tile(pa, rl);
+
+ /* extra layers */
+ if (rl->layflag & SCE_LAY_EDGE)
+ if (R.r.mode & R_EDGE)
+ edge_enhance_add(pa, rect, edgerect);
+
+ if (rl->passflag & SCE_PASS_VECTOR)
+ reset_sky_speed(pa, rl);
+
+ /* clamp alpha to 0..1 range, can go outside due to filter */
+ clamp_alpha_rgb_range(pa, rl);
+
+ /* free stuff within loop! */
+ MEM_freeN(pa->rectdaps); pa->rectdaps= NULL;
+ freeps(&psmlist);
+
+ if (edgerect) MEM_freeN(edgerect);
+ edgerect= NULL;
+
+ if (pa->rectmask) {
+ MEM_freeN(pa->rectmask);
+ pa->rectmask= NULL;
+ }
+ }
+
+ /* free all */
+ MEM_freeN(pa->recto); pa->recto= NULL;
+ MEM_freeN(pa->rectp); pa->rectp= NULL;
+ MEM_freeN(pa->rectz); pa->rectz= NULL;
+
+ /* display active layer */
+ rr->renrect.ymin=rr->renrect.ymax = 0;
+ rr->renlay= render_get_active_layer(&R, rr);
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/* non OSA case, full tile render */
+/* supposed to be fully threadable! */
+void zbufshade_tile(RenderPart *pa)
+{
+ ShadeSample ssamp;
+ RenderResult *rr= pa->result;
+ RenderLayer *rl;
+ PixStr ps;
+ float *edgerect= NULL;
+
+ /* fake pixel struct, to comply to osa render */
+ ps.next= NULL;
+ ps.mask= 0xFFFF;
+
+ /* zbuffer code clears/inits rects */
+ pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
+ pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
+ pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
+
+ for (rl= rr->layers.first; rl; rl= rl->next) {
+ float *rect= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
+ if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
+ pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
+
+ /* general shader info, passes */
+ shade_sample_initialize(&ssamp, pa, rl);
+
+ zbuffer_solid(pa, rl, NULL, NULL);
+
+ if (!R.test_break(R.tbh)) { /* NOTE: this if () is not consistent */
+
+ /* edges only for solid part, ztransp doesn't support it yet anti-aliased */
+ if (rl->layflag & SCE_LAY_EDGE) {
+ if (R.r.mode & R_EDGE) {
+ edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge");
+ edge_enhance_tile(pa, edgerect, pa->rectz);
+ }
+ }
+
+ /* initialize scanline updates for main thread */
+ rr->renrect.ymin = 0;
+ rr->renlay= rl;
+
+ if (rl->layflag & SCE_LAY_SOLID) {
+ const float *fcol = rect;
+ const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz;
+ int x, y, offs=0, seed;
+
+ /* we set per pixel a fixed seed, for random AO and shadow samples */
+ seed= pa->rectx*pa->disprect.ymin;
+
+ /* irregular shadowb buffer creation */
+ if (R.r.mode & R_SHADOW)
+ ISB_create(pa, NULL);
+
+ if (R.occlusiontree)
+ cache_occ_samples(&R, pa, &ssamp);
+
+ for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) {
+ for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, ro++, rz++, rp++, fcol+=4, offs++) {
+ /* per pixel fixed seed */
+ BLI_thread_srandom(pa->thread, seed++);
+
+ if (*rp) {
+ ps.obi= *ro;
+ ps.facenr= *rp;
+ ps.z= *rz;
+ if (shade_samples(&ssamp, &ps, x, y)) {
+ /* combined and passes */
+ add_passes(rl, offs, ssamp.shi, ssamp.shr);
+ }
+ }
+ }
+ if (y&1)
+ if (R.test_break(R.tbh)) break;
+ }
+
+ if (R.occlusiontree)
+ free_occ_samples(&R, pa);
+
+ if (R.r.mode & R_SHADOW)
+ ISB_free(pa);
+ }
+
+ /* disable scanline updating */
+ rr->renlay= NULL;
+ }
+
+ /* lamphalo after solid, before ztra, looks nicest because ztra does own halo */
+ if (R.flag & R_LAMPHALO)
+ if (rl->layflag & SCE_LAY_HALO)
+ lamphalo_tile(pa, rl);
+
+ /* halo before ztra, because ztra fills in zbuffer now */
+ if (R.flag & R_HALO)
+ if (rl->layflag & SCE_LAY_HALO)
+ halo_tile(pa, rl);
+
+ if (R.flag & R_ZTRA || R.totstrand) {
+ if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) {
+ float *fcol, *acol;
+ int x;
+
+ /* allocate, but not free here, for asynchronous display of this rect in main thread */
+ rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
+
+ /* swap for live updates */
+ SWAP(float *, rl->acolrect, rect);
+ zbuffer_transp_shade(pa, rl, rect, NULL);
+ SWAP(float *, rl->acolrect, rect);
+
+ fcol= rect; acol= rl->acolrect;
+ for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
+ addAlphaOverFloat(fcol, acol);
+ }
+ }
+ }
+
+ /* sun/sky */
+ if (rl->layflag & SCE_LAY_SKY)
+ atm_tile(pa, rl);
+
+ /* sky before edge */
+ if (rl->layflag & SCE_LAY_SKY)
+ sky_tile(pa, rl);
+
+ if (!R.test_break(R.tbh)) {
+ if (rl->layflag & SCE_LAY_EDGE)
+ if (R.r.mode & R_EDGE)
+ edge_enhance_add(pa, rect, edgerect);
+ }
+
+ if (rl->passflag & SCE_PASS_VECTOR)
+ reset_sky_speed(pa, rl);
+
+ if (edgerect) MEM_freeN(edgerect);
+ edgerect= NULL;
+
+ if (pa->rectmask) {
+ MEM_freeN(pa->rectmask);
+ pa->rectmask= NULL;
+ }
+ }
+
+ /* display active layer */
+ rr->renrect.ymin=rr->renrect.ymax = 0;
+ rr->renlay= render_get_active_layer(&R, rr);
+
+ MEM_freeN(pa->recto); pa->recto= NULL;
+ MEM_freeN(pa->rectp); pa->rectp= NULL;
+ MEM_freeN(pa->rectz); pa->rectz= NULL;
+}
+
+/* SSS preprocess tile render, fully threadable */
+typedef struct ZBufSSSHandle {
+ RenderPart *pa;
+ ListBase psmlist;
+ int totps;
+} ZBufSSSHandle;
+
+static void addps_sss(void *cb_handle, int obi, int facenr, int x, int y, int z)
+{
+ ZBufSSSHandle *handle = cb_handle;
+ RenderPart *pa= handle->pa;
+
+ /* extra border for filter gives double samples on part edges,
+ * don't use those */
+ if (x<pa->crop || x>=pa->rectx-pa->crop)
+ return;
+ if (y<pa->crop || y>=pa->recty-pa->crop)
+ return;
+
+ if (pa->rectall) {
+ intptr_t *rs= pa->rectall + pa->rectx*y + x;
+
+ addps(&handle->psmlist, rs, obi, facenr, z, 0, 0);
+ handle->totps++;
+ }
+ if (pa->rectz) {
+ int *rz= pa->rectz + pa->rectx*y + x;
+ int *rp= pa->rectp + pa->rectx*y + x;
+ int *ro= pa->recto + pa->rectx*y + x;
+
+ if (z < *rz) {
+ if (*rp == 0)
+ handle->totps++;
+ *rz= z;
+ *rp= facenr;
+ *ro= obi;
+ }
+ }
+ if (pa->rectbackz) {
+ int *rz= pa->rectbackz + pa->rectx*y + x;
+ int *rp= pa->rectbackp + pa->rectx*y + x;
+ int *ro= pa->rectbacko + pa->rectx*y + x;
+
+ if (z >= *rz) {
+ if (*rp == 0)
+ handle->totps++;
+ *rz= z;
+ *rp= facenr;
+ *ro= obi;
+ }
+ }
+}
+
+static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRen *obi, VlakRen *vlr, int quad, float x, float y, float z, float *co, float color[3], float *area)
+{
+ ShadeInput *shi= ssamp->shi;
+ ShadeResult shr;
+ float /* texfac,*/ /* UNUSED */ orthoarea, nor[3], alpha, sx, sy;
+
+ /* cache for shadow */
+ shi->samplenr= R.shadowsamplenr[shi->thread]++;
+
+ if (quad)
+ shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3);
+ else
+ shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2);
+
+ /* center pixel */
+ sx = x + 0.5f;
+ sy = y + 0.5f;
+
+ /* we estimate the area here using shi->dxco and shi->dyco. we need to
+ * enabled shi->osatex these are filled. we compute two areas, one with
+ * the normal pointed at the camera and one with the original normal, and
+ * then clamp to avoid a too large contribution from a single pixel */
+ shi->osatex= 1;
+
+ copy_v3_v3(nor, shi->facenor);
+ calc_view_vector(shi->facenor, sx, sy);
+ normalize_v3(shi->facenor);
+ shade_input_set_viewco(shi, x, y, sx, sy, z);
+ orthoarea= len_v3(shi->dxco)*len_v3(shi->dyco);
+
+ copy_v3_v3(shi->facenor, nor);
+ shade_input_set_viewco(shi, x, y, sx, sy, z);
+ *area = min_ff(len_v3(shi->dxco) * len_v3(shi->dyco), 2.0f * orthoarea);
+
+ shade_input_set_uv(shi);
+ shade_input_set_normals(shi);
+
+ /* we don't want flipped normals, they screw up back scattering */
+ if (shi->flippednor)
+ shade_input_flip_normals(shi);
+
+ /* not a pretty solution, but fixes common cases */
+ if (shi->obr->ob && shi->obr->ob->transflag & OB_NEG_SCALE) {
+ negate_v3(shi->vn);
+ negate_v3(shi->vno);
+ negate_v3(shi->nmapnorm);
+ }
+
+ /* if nodetree, use the material that we are currently preprocessing
+ * instead of the node material */
+ if (shi->mat->nodetree && shi->mat->use_nodes)
+ shi->mat= mat;
+
+ /* init material vars */
+ shade_input_init_material(shi);
+
+ /* render */
+ shade_input_set_shade_texco(shi);
+
+ shade_samples_do_AO(ssamp);
+ shade_material_loop(shi, &shr);
+
+ copy_v3_v3(co, shi->co);
+ copy_v3_v3(color, shr.combined);
+
+ /* texture blending */
+ /* texfac= shi->mat->sss_texfac; */ /* UNUSED */
+
+ alpha= shr.combined[3];
+ *area *= alpha;
+}
+
+static void zbufshade_sss_free(RenderPart *pa)
+{
+#if 0
+ MEM_freeN(pa->rectall); pa->rectall= NULL;
+ freeps(&handle.psmlist);
+#else
+ MEM_freeN(pa->rectz); pa->rectz= NULL;
+ MEM_freeN(pa->rectp); pa->rectp= NULL;
+ MEM_freeN(pa->recto); pa->recto= NULL;
+ MEM_freeN(pa->rectbackz); pa->rectbackz= NULL;
+ MEM_freeN(pa->rectbackp); pa->rectbackp= NULL;
+ MEM_freeN(pa->rectbacko); pa->rectbacko= NULL;
+#endif
+}
+
+void zbufshade_sss_tile(RenderPart *pa)
+{
+ Render *re= &R;
+ ShadeSample ssamp;
+ ZBufSSSHandle handle;
+ RenderResult *rr= pa->result;
+ RenderLayer *rl;
+ VlakRen *vlr;
+ Material *mat= re->sss_mat;
+ float (*co)[3], (*color)[3], *area, *fcol;
+ int x, y, seed, quad, totpoint;
+ const bool display = (re->r.scemode & (R_BUTS_PREVIEW | R_VIEWPORT_PREVIEW)) == 0;
+ int *ro, *rz, *rp, *rbo, *rbz, *rbp, lay;
+#if 0
+ PixStr *ps;
+ intptr_t *rs;
+ int z;
+#endif
+
+ /* setup pixelstr list and buffer for zbuffering */
+ handle.pa= pa;
+ handle.totps= 0;
+
+#if 0
+ handle.psmlist.first= handle.psmlist.last= NULL;
+ addpsmain(&handle.psmlist);
+
+ pa->rectall= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "rectall");
+#else
+ pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
+ pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
+ pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
+ pa->rectbacko= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbacko");
+ pa->rectbackp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackp");
+ pa->rectbackz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackz");
+#endif
+
+ /* setup shade sample with correct passes */
+ memset(&ssamp, 0, sizeof(ssamp));
+ shade_sample_initialize(&ssamp, pa, rr->layers.first);
+ ssamp.tot= 1;
+
+ for (rl=rr->layers.first; rl; rl=rl->next) {
+ ssamp.shi[0].lay |= rl->lay;
+ ssamp.shi[0].layflag |= rl->layflag;
+ ssamp.shi[0].passflag |= rl->passflag;
+ ssamp.shi[0].combinedflag |= ~rl->pass_xor;
+ }
+
+ rl= rr->layers.first;
+ ssamp.shi[0].passflag |= SCE_PASS_RGBA|SCE_PASS_COMBINED;
+ ssamp.shi[0].combinedflag &= ~(SCE_PASS_SPEC);
+ ssamp.shi[0].mat_override= NULL;
+ ssamp.shi[0].light_override= NULL;
+ lay= ssamp.shi[0].lay;
+
+ /* create the pixelstrs to be used later */
+ zbuffer_sss(pa, lay, &handle, addps_sss);
+
+ if (handle.totps==0) {
+ zbufshade_sss_free(pa);
+ return;
+ }
+
+ fcol= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
+
+ co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo");
+ color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor");
+ area= MEM_mallocN(sizeof(float)*handle.totps, "SSSArea");
+
+#if 0
+ /* create ISB (does not work currently!) */
+ if (re->r.mode & R_SHADOW)
+ ISB_create(pa, NULL);
+#endif
+
+ if (display) {
+ /* initialize scanline updates for main thread */
+ rr->renrect.ymin = 0;
+ rr->renlay= rl;
+ }
+
+ seed= pa->rectx*pa->disprect.ymin;
+#if 0
+ rs= pa->rectall;
+#else
+ rz= pa->rectz;
+ rp= pa->rectp;
+ ro= pa->recto;
+ rbz= pa->rectbackz;
+ rbp= pa->rectbackp;
+ rbo= pa->rectbacko;
+#endif
+ totpoint= 0;
+
+ for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) {
+ for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, fcol+=4) {
+ /* per pixel fixed seed */
+ BLI_thread_srandom(pa->thread, seed++);
+
+#if 0
+ if (rs) {
+ /* for each sample in this pixel, shade it */
+ for (ps = (PixStr *)(*rs); ps; ps=ps->next) {
+ ObjectInstanceRen *obi= &re->objectinstance[ps->obi];
+ ObjectRen *obr= obi->obr;
+ vlr= RE_findOrAddVlak(obr, (ps->facenr-1) & RE_QUAD_MASK);
+ quad= (ps->facenr & RE_QUAD_OFFS);
+ z= ps->z;
+
+ shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, z,
+ co[totpoint], color[totpoint], &area[totpoint]);
+
+ totpoint++;
+
+ add_v3_v3(fcol, color);
+ fcol[3]= 1.0f;
+ }
+
+ rs++;
+ }
+#else
+ if (rp) {
+ if (*rp != 0) {
+ ObjectInstanceRen *obi= &re->objectinstance[*ro];
+ ObjectRen *obr= obi->obr;
+
+ /* shade front */
+ vlr= RE_findOrAddVlak(obr, (*rp-1) & RE_QUAD_MASK);
+ quad= ((*rp) & RE_QUAD_OFFS);
+
+ shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rz,
+ co[totpoint], color[totpoint], &area[totpoint]);
+
+ add_v3_v3(fcol, color[totpoint]);
+ fcol[3]= 1.0f;
+ totpoint++;
+ }
+
+ rp++; rz++; ro++;
+ }
+
+ if (rbp) {
+ if (*rbp != 0 && !(*rbp == *(rp-1) && *rbo == *(ro-1))) {
+ ObjectInstanceRen *obi= &re->objectinstance[*rbo];
+ ObjectRen *obr= obi->obr;
+
+ /* shade back */
+ vlr= RE_findOrAddVlak(obr, (*rbp-1) & RE_QUAD_MASK);
+ quad= ((*rbp) & RE_QUAD_OFFS);
+
+ shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rbz,
+ co[totpoint], color[totpoint], &area[totpoint]);
+
+ /* to indicate this is a back sample */
+ area[totpoint]= -area[totpoint];
+
+ add_v3_v3(fcol, color[totpoint]);
+ fcol[3]= 1.0f;
+ totpoint++;
+ }
+
+ rbz++; rbp++; rbo++;
+ }
+#endif
+ }
+
+ if (y&1)
+ if (re->test_break(re->tbh)) break;
+ }
+
+ /* note: after adding we do not free these arrays, sss keeps them */
+ if (totpoint > 0) {
+ sss_add_points(re, co, color, area, totpoint);
+ }
+ else {
+ MEM_freeN(co);
+ MEM_freeN(color);
+ MEM_freeN(area);
+ }
+
+#if 0
+ if (re->r.mode & R_SHADOW)
+ ISB_free(pa);
+#endif
+
+ if (display) {
+ /* display active layer */
+ rr->renrect.ymin=rr->renrect.ymax = 0;
+ rr->renlay= render_get_active_layer(&R, rr);
+ }
+
+ zbufshade_sss_free(pa);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void renderhalo_post(RenderResult *rr, float *rectf, HaloRen *har) /* postprocess version */
+{
+ float dist, xsq, ysq, xn, yn, colf[4], *rectft, *rtf;
+ float haloxs, haloys;
+ int minx, maxx, miny, maxy, x, y;
+
+ /* calculate the disprect mapped coordinate for halo. note: rectx is disprect corrected */
+ haloxs= har->xs - R.disprect.xmin;
+ haloys= har->ys - R.disprect.ymin;
+
+ har->miny= miny= haloys - har->rad/R.ycor;
+ har->maxy= maxy= haloys + har->rad/R.ycor;
+
+ if (maxy < 0) {
+ /* pass */
+ }
+ else if (rr->recty < miny) {
+ /* pass */
+ }
+ else {
+ minx = floor(haloxs - har->rad);
+ maxx = ceil(haloxs + har->rad);
+
+ if (maxx < 0) {
+ /* pass */
+ }
+ else if (rr->rectx < minx) {
+ /* pass */
+ }
+ else {
+ if (minx<0) minx= 0;
+ if (maxx>=rr->rectx) maxx= rr->rectx-1;
+ if (miny<0) miny= 0;
+ if (maxy>rr->recty) maxy= rr->recty;
+
+ rectft= rectf+ 4*rr->rectx*miny;
+
+ for (y=miny; y<maxy; y++) {
+
+ rtf= rectft+4*minx;
+
+ yn= (y - haloys)*R.ycor;
+ ysq= yn*yn;
+
+ for (x=minx; x<=maxx; x++) {
+ xn= x - haloxs;
+ xsq= xn*xn;
+ dist= xsq+ysq;
+ if (dist<har->radsq) {
+
+ if (shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec))
+ addalphaAddfacFloat(rtf, colf, har->add);
+ }
+ rtf+=4;
+ }
+
+ rectft+= 4*rr->rectx;
+
+ if (R.test_break(R.tbh)) break;
+ }
+ }
+ }
+}
+/* ------------------------------------------------------------------------ */
+
+static void renderflare(RenderResult *rr, float *rectf, HaloRen *har)
+{
+ extern const float hashvectf[];
+ HaloRen fla;
+ Material *ma;
+ const float *rc;
+ float rad, alfa, visifac, vec[3];
+ int b, type;
+
+ fla= *har;
+ fla.linec= fla.ringc= fla.flarec= 0;
+
+ rad= har->rad;
+ alfa= har->alfa;
+
+ visifac= R.ycor*(har->pixels);
+ /* all radials added / r^3 == 1.0f! */
+ visifac /= (har->rad*har->rad*har->rad);
+ visifac*= visifac;
+
+ ma= har->mat;
+
+ /* first halo: just do */
+
+ har->rad= rad*ma->flaresize*visifac;
+ har->radsq= har->rad*har->rad;
+ har->zs= fla.zs= 0;
+
+ har->alfa= alfa*visifac;
+
+ renderhalo_post(rr, rectf, har);
+
+ /* next halo's: the flares */
+ rc= hashvectf + ma->seed2;
+
+ for (b=1; b<har->flarec; b++) {
+
+ fla.r = fabsf(rc[0]);
+ fla.g = fabsf(rc[1]);
+ fla.b = fabsf(rc[2]);
+ fla.alfa= ma->flareboost*fabsf(alfa*visifac*rc[3]);
+ fla.hard= 20.0f + fabsf(70.0f*rc[7]);
+ fla.tex= 0;
+
+ type= (int)(fabsf(3.9f*rc[6]));
+
+ fla.rad = ma->subsize * sqrtf(fabsf(2.0f * har->rad * rc[4]));
+
+ if (type==3) {
+ fla.rad*= 3.0f;
+ fla.rad+= R.rectx/10;
+ }
+
+ fla.radsq= fla.rad*fla.rad;
+
+ vec[0]= 1.4f*rc[5]*(har->xs-R.winx/2);
+ vec[1]= 1.4f*rc[5]*(har->ys-R.winy/2);
+ vec[2]= 32.0f*sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + 1.0f);
+
+ fla.xs= R.winx/2 + vec[0] + (1.2f+rc[8])*R.rectx*vec[0]/vec[2];
+ fla.ys= R.winy/2 + vec[1] + (1.2f+rc[8])*R.rectx*vec[1]/vec[2];
+
+ if (R.flag & R_SEC_FIELD) {
+ if (R.r.mode & R_ODDFIELD) fla.ys += 0.5f;
+ else fla.ys -= 0.5f;
+ }
+ if (type & 1) fla.type= HA_FLARECIRC;
+ else fla.type= 0;
+ renderhalo_post(rr, rectf, &fla);
+
+ fla.alfa*= 0.5f;
+ if (type & 2) fla.type= HA_FLARECIRC;
+ else fla.type= 0;
+ renderhalo_post(rr, rectf, &fla);
+
+ rc+= 7;
+ }
+}
+
+/* needs recode... integrate this better! */
+void add_halo_flare(Render *re)
+{
+ RenderResult *rr= re->result;
+ RenderLayer *rl;
+ HaloRen *har;
+ int a, mode;
+ float *rect;
+
+ /* for now, we get the first renderlayer in list with halos set */
+ for (rl= rr->layers.first; rl; rl= rl->next) {
+ bool do_draw = false;
+
+ if ((rl->layflag & SCE_LAY_HALO) == 0)
+ continue;
+
+ rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, re->viewname);
+
+ if (rect==NULL)
+ continue;
+
+ mode= R.r.mode;
+ R.r.mode &= ~R_PANORAMA;
+
+ project_renderdata(&R, projectverto, 0, 0, 0);
+
+ for (a=0; a<R.tothalo; a++) {
+ har= R.sortedhalos[a];
+
+ if (har->flarec && (har->lay & rl->lay)) {
+ do_draw = true;
+ renderflare(rr, rect, har);
+ }
+ }
+
+ if (do_draw) {
+ /* weak... the display callback wants an active renderlayer pointer... */
+ rr->renlay= rl;
+ re->display_update(re->duh, rr, NULL);
+ }
+
+ R.r.mode= mode;
+ }
+}
+
+void render_internal_update_passes(RenderEngine *engine, Scene *scene, SceneRenderLayer *srl)
+{
+ int type;
+
+ RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
+
+#define CHECK_PASS(name, channels, chanid) \
+ if (srl->passflag & (SCE_PASS_ ## name)) { \
+ if (channels == 4) type = SOCK_RGBA; \
+ else if (channels == 3) type = SOCK_VECTOR; \
+ else type = SOCK_FLOAT; \
+ RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_ ## name, channels, chanid, type); \
+ }
+
+ CHECK_PASS(Z, 1, "Z");
+ CHECK_PASS(VECTOR, 4, "XYZW");
+ CHECK_PASS(NORMAL, 3, "XYZ");
+ CHECK_PASS(UV, 3, "UVA");
+ CHECK_PASS(RGBA, 4, "RGBA");
+ CHECK_PASS(EMIT, 3, "RGB");
+ CHECK_PASS(DIFFUSE, 3, "RGB");
+ CHECK_PASS(SPEC, 3, "RGB");
+ CHECK_PASS(AO, 3, "RGB");
+ CHECK_PASS(ENVIRONMENT, 3, "RGB");
+ CHECK_PASS(INDIRECT, 3, "RGB");
+ CHECK_PASS(SHADOW, 3, "RGB");
+ CHECK_PASS(REFLECT, 3, "RGB");
+ CHECK_PASS(REFRACT, 3, "RGB");
+ CHECK_PASS(INDEXOB, 1, "X");
+ CHECK_PASS(INDEXMA, 1, "X");
+ CHECK_PASS(MIST, 1, "Z");
+
+#undef CHECK_PASS
+}
diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c
new file mode 100644
index 00000000000..67bfd1bfdc7
--- /dev/null
+++ b/source/blender/render/intern/source/renderdatabase.c
@@ -0,0 +1,1603 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): 2004-2006, Blender Foundation, full recode
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/renderdatabase.c
+ * \ingroup render
+ */
+
+
+/*
+ * Storage, retrieval and query of render specific data.
+ *
+ * All data from a Blender scene is converted by the renderconverter/
+ * into a special format that is used by the render module to make
+ * images out of. These functions interface to the render-specific
+ * database.
+ *
+ * The blo{ha/ve/vl} arrays store pointers to blocks of 256 data
+ * entries each.
+ *
+ * The index of an entry is >>8 (the highest 24 * bits), to find an
+ * offset in a 256-entry block.
+ *
+ * - If the 256-entry block entry has an entry in the
+ * vertnodes/vlaknodes/bloha array of the current block, the i-th entry in
+ * that block is allocated to this entry.
+ *
+ * - If the entry has no block allocated for it yet, memory is
+ * allocated.
+ *
+ * The pointer to the correct entry is returned. Memory is guaranteed
+ * to exist (as long as the malloc does not break). Since guarded
+ * allocation is used, memory _must_ be available. Otherwise, an
+ * exit(0) would occur.
+ *
+ */
+
+#include <limits.h>
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_hash.h"
+
+#include "DNA_material_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_listBase.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_DerivedMesh.h"
+
+#include "RE_render_ext.h" /* externtex */
+
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "render_types.h"
+#include "renderdatabase.h"
+#include "zbuf.h"
+
+/* ------------------------------------------------------------------------- */
+
+/* More dynamic allocation of options for render vertices and faces, so we don't
+ * have to reserve this space inside vertices.
+ * Important; vertices and faces, should have been created already (to get tables
+ * checked) that's a reason why the calls demand VertRen/VlakRen * as arg, not
+ * the index */
+
+/* NOTE! the hardcoded table size 256 is used still in code for going quickly over vertices/faces */
+#define RE_STRESS_ELEMS 1
+#define RE_RAD_ELEMS 4
+#define RE_STRAND_ELEMS 1
+#define RE_TANGENT_ELEMS 3
+#define RE_WINSPEED_ELEMS 4
+#define RE_MTFACE_ELEMS 1
+#define RE_MCOL_ELEMS 4
+#define RE_UV_ELEMS 2
+#define RE_VLAK_ORIGINDEX_ELEMS 1
+#define RE_VERT_ORIGINDEX_ELEMS 1
+#define RE_SURFNOR_ELEMS 3
+#define RE_RADFACE_ELEMS 1
+#define RE_SIMPLIFY_ELEMS 2
+#define RE_FACE_ELEMS 1
+#define RE_NMAP_TANGENT_ELEMS 16
+
+float *RE_vertren_get_stress(ObjectRen *obr, VertRen *ver, int verify)
+{
+ float *stress;
+ int nr= ver->index>>8;
+
+ stress= obr->vertnodes[nr].stress;
+ if (stress==NULL) {
+ if (verify)
+ stress= obr->vertnodes[nr].stress= MEM_mallocN(256*RE_STRESS_ELEMS*sizeof(float), "stress table");
+ else
+ return NULL;
+ }
+ return stress + (ver->index & 255)*RE_STRESS_ELEMS;
+}
+
+/* this one callocs! */
+float *RE_vertren_get_rad(ObjectRen *obr, VertRen *ver, int verify)
+{
+ float *rad;
+ int nr= ver->index>>8;
+
+ rad= obr->vertnodes[nr].rad;
+ if (rad==NULL) {
+ if (verify)
+ rad= obr->vertnodes[nr].rad= MEM_callocN(256*RE_RAD_ELEMS*sizeof(float), "rad table");
+ else
+ return NULL;
+ }
+ return rad + (ver->index & 255)*RE_RAD_ELEMS;
+}
+
+float *RE_vertren_get_strand(ObjectRen *obr, VertRen *ver, int verify)
+{
+ float *strand;
+ int nr= ver->index>>8;
+
+ strand= obr->vertnodes[nr].strand;
+ if (strand==NULL) {
+ if (verify)
+ strand= obr->vertnodes[nr].strand= MEM_mallocN(256*RE_STRAND_ELEMS*sizeof(float), "strand table");
+ else
+ return NULL;
+ }
+ return strand + (ver->index & 255)*RE_STRAND_ELEMS;
+}
+
+/* needs calloc */
+float *RE_vertren_get_tangent(ObjectRen *obr, VertRen *ver, int verify)
+{
+ float *tangent;
+ int nr= ver->index>>8;
+
+ tangent= obr->vertnodes[nr].tangent;
+ if (tangent==NULL) {
+ if (verify)
+ tangent= obr->vertnodes[nr].tangent= MEM_callocN(256*RE_TANGENT_ELEMS*sizeof(float), "tangent table");
+ else
+ return NULL;
+ }
+ return tangent + (ver->index & 255)*RE_TANGENT_ELEMS;
+}
+
+/* needs calloc! not all renderverts have them */
+/* also winspeed is exception, it is stored per instance */
+float *RE_vertren_get_winspeed(ObjectInstanceRen *obi, VertRen *ver, int verify)
+{
+ float *winspeed;
+ int totvector;
+
+ winspeed= obi->vectors;
+ if (winspeed==NULL) {
+ if (verify) {
+ totvector= obi->obr->totvert + obi->obr->totstrand;
+ winspeed= obi->vectors= MEM_callocN(totvector*RE_WINSPEED_ELEMS*sizeof(float), "winspeed table");
+ }
+ else
+ return NULL;
+ }
+ return winspeed + ver->index*RE_WINSPEED_ELEMS;
+}
+
+int *RE_vertren_get_origindex(ObjectRen *obr, VertRen *ver, int verify)
+{
+ int *origindex;
+ int nr= ver->index>>8;
+
+ origindex= obr->vertnodes[nr].origindex;
+ if (origindex==NULL) {
+ if (verify)
+ origindex= obr->vertnodes[nr].origindex= MEM_mallocN(256*RE_VERT_ORIGINDEX_ELEMS*sizeof(int), "origindex table");
+ else
+ return NULL;
+ }
+ return origindex + (ver->index & 255)*RE_VERT_ORIGINDEX_ELEMS;
+}
+
+VertRen *RE_vertren_copy(ObjectRen *obr, VertRen *ver)
+{
+ VertRen *v1= RE_findOrAddVert(obr, obr->totvert++);
+ float *fp1, *fp2;
+ int *int1, *int2;
+ int index= v1->index;
+
+ *v1= *ver;
+ v1->index= index;
+
+ fp1= RE_vertren_get_stress(obr, ver, 0);
+ if (fp1) {
+ fp2= RE_vertren_get_stress(obr, v1, 1);
+ memcpy(fp2, fp1, RE_STRESS_ELEMS*sizeof(float));
+ }
+ fp1= RE_vertren_get_rad(obr, ver, 0);
+ if (fp1) {
+ fp2= RE_vertren_get_rad(obr, v1, 1);
+ memcpy(fp2, fp1, RE_RAD_ELEMS*sizeof(float));
+ }
+ fp1= RE_vertren_get_strand(obr, ver, 0);
+ if (fp1) {
+ fp2= RE_vertren_get_strand(obr, v1, 1);
+ memcpy(fp2, fp1, RE_STRAND_ELEMS*sizeof(float));
+ }
+ fp1= RE_vertren_get_tangent(obr, ver, 0);
+ if (fp1) {
+ fp2= RE_vertren_get_tangent(obr, v1, 1);
+ memcpy(fp2, fp1, RE_TANGENT_ELEMS*sizeof(float));
+ }
+ int1= RE_vertren_get_origindex(obr, ver, 0);
+ if (int1) {
+ int2= RE_vertren_get_origindex(obr, v1, 1);
+ memcpy(int2, int1, RE_VERT_ORIGINDEX_ELEMS*sizeof(int));
+ }
+ return v1;
+}
+
+VertRen *RE_findOrAddVert(ObjectRen *obr, int nr)
+{
+ VertTableNode *temp;
+ VertRen *v;
+ int a;
+
+ if (nr<0) {
+ printf("error in findOrAddVert: %d\n", nr);
+ return NULL;
+ }
+ a= nr>>8;
+
+ if (a>=obr->vertnodeslen-1) { /* Need to allocate more columns..., and keep last element NULL for free loop */
+ temp= obr->vertnodes;
+
+ obr->vertnodes= MEM_mallocN(sizeof(VertTableNode)*(obr->vertnodeslen+TABLEINITSIZE), "vertnodes");
+ if (temp) memcpy(obr->vertnodes, temp, obr->vertnodeslen*sizeof(VertTableNode));
+ memset(obr->vertnodes+obr->vertnodeslen, 0, TABLEINITSIZE*sizeof(VertTableNode));
+
+ obr->vertnodeslen+=TABLEINITSIZE;
+ if (temp) MEM_freeN(temp);
+ }
+
+ v= obr->vertnodes[a].vert;
+ if (v==NULL) {
+ int i;
+
+ v= (VertRen *)MEM_callocN(256*sizeof(VertRen), "findOrAddVert");
+ obr->vertnodes[a].vert= v;
+
+ for (i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++) {
+ v[a].index= i;
+ }
+ }
+ v+= (nr & 255);
+ return v;
+}
+
+/* ------------------------------------------------------------------------ */
+
+MTFace *RE_vlakren_get_tface(ObjectRen *obr, VlakRen *vlr, int n, char **name, int verify)
+{
+ VlakTableNode *node;
+ int nr= vlr->index>>8, vlakindex= (vlr->index&255);
+ int index= (n<<8) + vlakindex;
+
+ node= &obr->vlaknodes[nr];
+
+ if (verify) {
+ if (n>=node->totmtface) {
+ MTFace *mtface= node->mtface;
+ int size= (n+1)*256;
+
+ node->mtface= MEM_callocN(size*sizeof(MTFace), "Vlak mtface");
+
+ if (mtface) {
+ size= node->totmtface*256;
+ memcpy(node->mtface, mtface, size*sizeof(MTFace));
+ MEM_freeN(mtface);
+ }
+
+ node->totmtface= n+1;
+ }
+ }
+ else {
+ if (n>=node->totmtface)
+ return NULL;
+
+ if (name) *name= obr->mtface[n];
+ }
+
+ return node->mtface + index;
+}
+
+MCol *RE_vlakren_get_mcol(ObjectRen *obr, VlakRen *vlr, int n, char **name, int verify)
+{
+ VlakTableNode *node;
+ int nr= vlr->index>>8, vlakindex= (vlr->index&255);
+ int index= (n<<8) + vlakindex;
+
+ node= &obr->vlaknodes[nr];
+
+ if (verify) {
+ if (n>=node->totmcol) {
+ MCol *mcol= node->mcol;
+ int size= (n+1)*256;
+
+ node->mcol= MEM_callocN(size*sizeof(MCol)*RE_MCOL_ELEMS, "Vlak mcol");
+
+ if (mcol) {
+ size= node->totmcol*256;
+ memcpy(node->mcol, mcol, size*sizeof(MCol)*RE_MCOL_ELEMS);
+ MEM_freeN(mcol);
+ }
+
+ node->totmcol= n+1;
+ }
+ }
+ else {
+ if (n>=node->totmcol)
+ return NULL;
+
+ if (name) *name= obr->mcol[n];
+ }
+
+ return node->mcol + index*RE_MCOL_ELEMS;
+}
+
+int *RE_vlakren_get_origindex(ObjectRen *obr, VlakRen *vlak, int verify)
+{
+ int *origindex;
+ int nr= vlak->index>>8;
+
+ origindex= obr->vlaknodes[nr].origindex;
+ if (origindex==NULL) {
+ if (verify)
+ origindex= obr->vlaknodes[nr].origindex= MEM_callocN(256*RE_VLAK_ORIGINDEX_ELEMS*sizeof(int), "origindex table");
+ else
+ return NULL;
+ }
+ return origindex + (vlak->index & 255)*RE_VLAK_ORIGINDEX_ELEMS;
+}
+
+float *RE_vlakren_get_surfnor(ObjectRen *obr, VlakRen *vlak, int verify)
+{
+ float *surfnor;
+ int nr= vlak->index>>8;
+
+ surfnor= obr->vlaknodes[nr].surfnor;
+ if (surfnor==NULL) {
+ if (verify)
+ surfnor= obr->vlaknodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor table");
+ else
+ return NULL;
+ }
+ return surfnor + (vlak->index & 255)*RE_SURFNOR_ELEMS;
+}
+
+float *RE_vlakren_get_nmap_tangent(ObjectRen *obr, VlakRen *vlak, int index, bool verify)
+{
+ float **tangents;
+ int nr= vlak->index>>8;
+
+ tangents = obr->vlaknodes[nr].tangent_arrays;
+
+ if (index + 1 > 8) {
+ return NULL;
+ }
+
+ index = index < 0 ? 0: index;
+
+ if (tangents[index] == NULL) {
+ if (verify) {
+ tangents[index] = MEM_callocN(256*RE_NMAP_TANGENT_ELEMS*sizeof(float), "tangent table");
+ }
+ else
+ return NULL;
+ }
+
+ return tangents[index] + (vlak->index & 255)*RE_NMAP_TANGENT_ELEMS;
+}
+
+RadFace **RE_vlakren_get_radface(ObjectRen *obr, VlakRen *vlak, int verify)
+{
+ RadFace **radface;
+ int nr= vlak->index>>8;
+
+ radface= obr->vlaknodes[nr].radface;
+ if (radface==NULL) {
+ if (verify)
+ radface = obr->vlaknodes[nr].radface= MEM_callocN(256 * RE_RADFACE_ELEMS * sizeof(void *), "radface table");
+ else
+ return NULL;
+ }
+ return radface + (vlak->index & 255)*RE_RADFACE_ELEMS;
+}
+
+VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr)
+{
+ VlakRen *vlr1 = RE_findOrAddVlak(obr, obr->totvlak++);
+ MTFace *mtface, *mtface1;
+ MCol *mcol, *mcol1;
+ float *surfnor, *surfnor1;
+ float *tangent, *tangent1;
+ int *origindex, *origindex1;
+ RadFace **radface, **radface1;
+ int i, index = vlr1->index;
+ char *name;
+
+ *vlr1= *vlr;
+ vlr1->index= index;
+
+ for (i=0; (mtface=RE_vlakren_get_tface(obr, vlr, i, &name, 0)) != NULL; i++) {
+ mtface1= RE_vlakren_get_tface(obr, vlr1, i, &name, 1);
+ memcpy(mtface1, mtface, sizeof(MTFace)*RE_MTFACE_ELEMS);
+ }
+
+ for (i=0; (mcol=RE_vlakren_get_mcol(obr, vlr, i, &name, 0)) != NULL; i++) {
+ mcol1= RE_vlakren_get_mcol(obr, vlr1, i, &name, 1);
+ memcpy(mcol1, mcol, sizeof(MCol)*RE_MCOL_ELEMS);
+ }
+
+ origindex= RE_vlakren_get_origindex(obr, vlr, 0);
+ if (origindex) {
+ origindex1= RE_vlakren_get_origindex(obr, vlr1, 1);
+ /* Just an int, but memcpy for consistency. */
+ memcpy(origindex1, origindex, sizeof(int)*RE_VLAK_ORIGINDEX_ELEMS);
+ }
+
+ surfnor= RE_vlakren_get_surfnor(obr, vlr, 0);
+ if (surfnor) {
+ surfnor1= RE_vlakren_get_surfnor(obr, vlr1, 1);
+ copy_v3_v3(surfnor1, surfnor);
+ }
+
+ for (i=0; i < MAX_MTFACE; i++) {
+ tangent = RE_vlakren_get_nmap_tangent(obr, vlr, i, false);
+ if (!tangent)
+ continue;
+ tangent1 = RE_vlakren_get_nmap_tangent(obr, vlr1, i, true);
+ memcpy(tangent1, tangent, sizeof(float)*RE_NMAP_TANGENT_ELEMS);
+ }
+
+ radface= RE_vlakren_get_radface(obr, vlr, 0);
+ if (radface) {
+ radface1= RE_vlakren_get_radface(obr, vlr1, 1);
+ *radface1= *radface;
+ }
+
+ return vlr1;
+}
+
+void RE_vlakren_get_normal(Render *UNUSED(re), ObjectInstanceRen *obi, VlakRen *vlr, float r_nor[3])
+{
+ float (*nmat)[3]= obi->nmat;
+
+ if (obi->flag & R_TRANSFORMED) {
+ mul_v3_m3v3(r_nor, nmat, vlr->n);
+ normalize_v3(r_nor);
+ }
+ else {
+ copy_v3_v3(r_nor, vlr->n);
+ }
+}
+
+void RE_set_customdata_names(ObjectRen *obr, CustomData *data)
+{
+ /* CustomData layer names are stored per object here, because the
+ * DerivedMesh which stores the layers is freed */
+
+ CustomDataLayer *layer;
+ int numtf = 0, numcol = 0, i, mtfn, mcn;
+
+ if (CustomData_has_layer(data, CD_MTFACE)) {
+ numtf= CustomData_number_of_layers(data, CD_MTFACE);
+ obr->mtface= MEM_callocN(sizeof(*obr->mtface)*numtf, "mtfacenames");
+ }
+
+ if (CustomData_has_layer(data, CD_MCOL)) {
+ numcol= CustomData_number_of_layers(data, CD_MCOL);
+ obr->mcol= MEM_callocN(sizeof(*obr->mcol)*numcol, "mcolnames");
+ }
+
+ for (i=0, mtfn=0, mcn=0; i < data->totlayer; i++) {
+ layer= &data->layers[i];
+
+ if (layer->type == CD_MTFACE) {
+ BLI_strncpy(obr->mtface[mtfn++], layer->name, sizeof(layer->name));
+ obr->actmtface= CLAMPIS(layer->active_rnd, 0, numtf);
+ obr->bakemtface= layer->active;
+ }
+ else if (layer->type == CD_MCOL) {
+ BLI_strncpy(obr->mcol[mcn++], layer->name, sizeof(layer->name));
+ obr->actmcol= CLAMPIS(layer->active_rnd, 0, numcol);
+ }
+ }
+}
+
+VlakRen *RE_findOrAddVlak(ObjectRen *obr, int nr)
+{
+ VlakTableNode *temp;
+ VlakRen *v;
+ int a;
+
+ if (nr<0) {
+ printf("error in findOrAddVlak: %d\n", nr);
+ return obr->vlaknodes[0].vlak;
+ }
+ a= nr>>8;
+
+ if (a>=obr->vlaknodeslen-1) { /* Need to allocate more columns..., and keep last element NULL for free loop */
+ temp= obr->vlaknodes;
+
+ obr->vlaknodes= MEM_mallocN(sizeof(VlakTableNode)*(obr->vlaknodeslen+TABLEINITSIZE), "vlaknodes");
+ if (temp) memcpy(obr->vlaknodes, temp, obr->vlaknodeslen*sizeof(VlakTableNode));
+ memset(obr->vlaknodes+obr->vlaknodeslen, 0, TABLEINITSIZE*sizeof(VlakTableNode));
+
+ obr->vlaknodeslen+=TABLEINITSIZE; /*Does this really need to be power of 2?*/
+ if (temp) MEM_freeN(temp);
+ }
+
+ v= obr->vlaknodes[a].vlak;
+
+ if (v==NULL) {
+ int i;
+
+ v= (VlakRen *)MEM_callocN(256*sizeof(VlakRen), "findOrAddVlak");
+ obr->vlaknodes[a].vlak= v;
+
+ for (i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++)
+ v[a].index= i;
+ }
+ v+= (nr & 255);
+ return v;
+}
+
+/* ------------------------------------------------------------------------ */
+
+float *RE_strandren_get_surfnor(ObjectRen *obr, StrandRen *strand, int verify)
+{
+ float *surfnor;
+ int nr= strand->index>>8;
+
+ surfnor= obr->strandnodes[nr].surfnor;
+ if (surfnor==NULL) {
+ if (verify)
+ surfnor= obr->strandnodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor strand table");
+ else
+ return NULL;
+ }
+ return surfnor + (strand->index & 255)*RE_SURFNOR_ELEMS;
+}
+
+float *RE_strandren_get_uv(ObjectRen *obr, StrandRen *strand, int n, char **name, int verify)
+{
+ StrandTableNode *node;
+ int nr= strand->index>>8, strandindex= (strand->index&255);
+ int index= (n<<8) + strandindex;
+
+ node= &obr->strandnodes[nr];
+
+ if (verify) {
+ if (n>=node->totuv) {
+ float *uv= node->uv;
+ int size= (n+1)*256;
+
+ node->uv= MEM_callocN(size*sizeof(float)*RE_UV_ELEMS, "strand uv table");
+
+ if (uv) {
+ size= node->totuv*256;
+ memcpy(node->uv, uv, size*sizeof(float)*RE_UV_ELEMS);
+ MEM_freeN(uv);
+ }
+
+ node->totuv= n+1;
+ }
+ }
+ else {
+ if (n>=node->totuv)
+ return NULL;
+
+ if (name) *name= obr->mtface[n];
+ }
+
+ return node->uv + index*RE_UV_ELEMS;
+}
+
+MCol *RE_strandren_get_mcol(ObjectRen *obr, StrandRen *strand, int n, char **name, int verify)
+{
+ StrandTableNode *node;
+ int nr= strand->index>>8, strandindex= (strand->index&255);
+ int index= (n<<8) + strandindex;
+
+ node= &obr->strandnodes[nr];
+
+ if (verify) {
+ if (n>=node->totmcol) {
+ MCol *mcol= node->mcol;
+ int size= (n+1)*256;
+
+ node->mcol= MEM_callocN(size*sizeof(MCol)*RE_MCOL_ELEMS, "strand mcol table");
+
+ if (mcol) {
+ size= node->totmcol*256;
+ memcpy(node->mcol, mcol, size*sizeof(MCol)*RE_MCOL_ELEMS);
+ MEM_freeN(mcol);
+ }
+
+ node->totmcol= n+1;
+ }
+ }
+ else {
+ if (n>=node->totmcol)
+ return NULL;
+
+ if (name) *name= obr->mcol[n];
+ }
+
+ return node->mcol + index*RE_MCOL_ELEMS;
+}
+
+float *RE_strandren_get_simplify(struct ObjectRen *obr, struct StrandRen *strand, int verify)
+{
+ float *simplify;
+ int nr= strand->index>>8;
+
+ simplify= obr->strandnodes[nr].simplify;
+ if (simplify==NULL) {
+ if (verify)
+ simplify= obr->strandnodes[nr].simplify= MEM_callocN(256*RE_SIMPLIFY_ELEMS*sizeof(float), "simplify strand table");
+ else
+ return NULL;
+ }
+ return simplify + (strand->index & 255)*RE_SIMPLIFY_ELEMS;
+}
+
+int *RE_strandren_get_face(ObjectRen *obr, StrandRen *strand, int verify)
+{
+ int *face;
+ int nr= strand->index>>8;
+
+ face= obr->strandnodes[nr].face;
+ if (face==NULL) {
+ if (verify)
+ face= obr->strandnodes[nr].face= MEM_callocN(256*RE_FACE_ELEMS*sizeof(int), "face strand table");
+ else
+ return NULL;
+ }
+ return face + (strand->index & 255)*RE_FACE_ELEMS;
+}
+
+/* winspeed is exception, it is stored per instance */
+float *RE_strandren_get_winspeed(ObjectInstanceRen *obi, StrandRen *strand, int verify)
+{
+ float *winspeed;
+ int totvector;
+
+ winspeed= obi->vectors;
+ if (winspeed==NULL) {
+ if (verify) {
+ totvector= obi->obr->totvert + obi->obr->totstrand;
+ winspeed= obi->vectors= MEM_callocN(totvector*RE_WINSPEED_ELEMS*sizeof(float), "winspeed strand table");
+ }
+ else
+ return NULL;
+ }
+ return winspeed + (obi->obr->totvert + strand->index)*RE_WINSPEED_ELEMS;
+}
+
+StrandRen *RE_findOrAddStrand(ObjectRen *obr, int nr)
+{
+ StrandTableNode *temp;
+ StrandRen *v;
+ int a;
+
+ if (nr<0) {
+ printf("error in findOrAddStrand: %d\n", nr);
+ return obr->strandnodes[0].strand;
+ }
+ a= nr>>8;
+
+ if (a>=obr->strandnodeslen-1) { /* Need to allocate more columns..., and keep last element NULL for free loop */
+ temp= obr->strandnodes;
+
+ obr->strandnodes= MEM_mallocN(sizeof(StrandTableNode)*(obr->strandnodeslen+TABLEINITSIZE), "strandnodes");
+ if (temp) memcpy(obr->strandnodes, temp, obr->strandnodeslen*sizeof(StrandTableNode));
+ memset(obr->strandnodes+obr->strandnodeslen, 0, TABLEINITSIZE*sizeof(StrandTableNode));
+
+ obr->strandnodeslen+=TABLEINITSIZE; /*Does this really need to be power of 2?*/
+ if (temp) MEM_freeN(temp);
+ }
+
+ v= obr->strandnodes[a].strand;
+
+ if (v==NULL) {
+ int i;
+
+ v= (StrandRen *)MEM_callocN(256*sizeof(StrandRen), "findOrAddStrand");
+ obr->strandnodes[a].strand= v;
+
+ for (i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++)
+ v[a].index= i;
+ }
+ v+= (nr & 255);
+ return v;
+}
+
+StrandBuffer *RE_addStrandBuffer(ObjectRen *obr, int totvert)
+{
+ StrandBuffer *strandbuf;
+
+ strandbuf= MEM_callocN(sizeof(StrandBuffer), "StrandBuffer");
+ strandbuf->vert= MEM_callocN(sizeof(StrandVert)*totvert, "StrandVert");
+ strandbuf->totvert= totvert;
+ strandbuf->obr= obr;
+
+ obr->strandbuf= strandbuf;
+
+ return strandbuf;
+}
+
+/* ------------------------------------------------------------------------ */
+
+ObjectRen *RE_addRenderObject(Render *re, Object *ob, Object *par, int index, int psysindex, int lay)
+{
+ ObjectRen *obr= MEM_callocN(sizeof(ObjectRen), "object render struct");
+
+ BLI_addtail(&re->objecttable, obr);
+ obr->ob= ob;
+ obr->par= par;
+ obr->index= index;
+ obr->psysindex= psysindex;
+ obr->lay= lay;
+
+ return obr;
+}
+
+void free_renderdata_vertnodes(VertTableNode *vertnodes)
+{
+ int a;
+
+ if (vertnodes==NULL) return;
+
+ for (a=0; vertnodes[a].vert; a++) {
+ MEM_freeN(vertnodes[a].vert);
+
+ if (vertnodes[a].rad)
+ MEM_freeN(vertnodes[a].rad);
+ if (vertnodes[a].strand)
+ MEM_freeN(vertnodes[a].strand);
+ if (vertnodes[a].tangent)
+ MEM_freeN(vertnodes[a].tangent);
+ if (vertnodes[a].stress)
+ MEM_freeN(vertnodes[a].stress);
+ if (vertnodes[a].winspeed)
+ MEM_freeN(vertnodes[a].winspeed);
+ if (vertnodes[a].origindex)
+ MEM_freeN(vertnodes[a].origindex);
+ }
+
+ MEM_freeN(vertnodes);
+}
+
+void free_renderdata_vlaknodes(VlakTableNode *vlaknodes)
+{
+ int a;
+
+ if (vlaknodes==NULL) return;
+
+ for (a=0; vlaknodes[a].vlak; a++) {
+ MEM_freeN(vlaknodes[a].vlak);
+
+ if (vlaknodes[a].mtface)
+ MEM_freeN(vlaknodes[a].mtface);
+ if (vlaknodes[a].mcol)
+ MEM_freeN(vlaknodes[a].mcol);
+ if (vlaknodes[a].origindex)
+ MEM_freeN(vlaknodes[a].origindex);
+ if (vlaknodes[a].surfnor)
+ MEM_freeN(vlaknodes[a].surfnor);
+ for (int b = 0; b < MAX_MTFACE; b++) {
+ if (vlaknodes[a].tangent_arrays[b])
+ MEM_freeN(vlaknodes[a].tangent_arrays[b]);
+ }
+ if (vlaknodes[a].radface)
+ MEM_freeN(vlaknodes[a].radface);
+ }
+
+ MEM_freeN(vlaknodes);
+}
+
+static void free_renderdata_strandnodes(StrandTableNode *strandnodes)
+{
+ int a;
+
+ if (strandnodes==NULL) return;
+
+ for (a=0; strandnodes[a].strand; a++) {
+ MEM_freeN(strandnodes[a].strand);
+
+ if (strandnodes[a].uv)
+ MEM_freeN(strandnodes[a].uv);
+ if (strandnodes[a].mcol)
+ MEM_freeN(strandnodes[a].mcol);
+ if (strandnodes[a].winspeed)
+ MEM_freeN(strandnodes[a].winspeed);
+ if (strandnodes[a].surfnor)
+ MEM_freeN(strandnodes[a].surfnor);
+ if (strandnodes[a].simplify)
+ MEM_freeN(strandnodes[a].simplify);
+ if (strandnodes[a].face)
+ MEM_freeN(strandnodes[a].face);
+ }
+
+ MEM_freeN(strandnodes);
+}
+
+void free_renderdata_tables(Render *re)
+{
+ ObjectInstanceRen *obi;
+ ObjectRen *obr;
+ StrandBuffer *strandbuf;
+ int a=0;
+
+ for (obr=re->objecttable.first; obr; obr=obr->next) {
+ if (obr->vertnodes) {
+ free_renderdata_vertnodes(obr->vertnodes);
+ obr->vertnodes= NULL;
+ obr->vertnodeslen= 0;
+ }
+
+ if (obr->vlaknodes) {
+ free_renderdata_vlaknodes(obr->vlaknodes);
+ obr->vlaknodes= NULL;
+ obr->vlaknodeslen= 0;
+ obr->totvlak= 0;
+ }
+
+ if (obr->bloha) {
+ for (a=0; obr->bloha[a]; a++)
+ MEM_freeN(obr->bloha[a]);
+
+ MEM_freeN(obr->bloha);
+ obr->bloha= NULL;
+ obr->blohalen= 0;
+ }
+
+ if (obr->strandnodes) {
+ free_renderdata_strandnodes(obr->strandnodes);
+ obr->strandnodes= NULL;
+ obr->strandnodeslen= 0;
+ }
+
+ strandbuf= obr->strandbuf;
+ if (strandbuf) {
+ if (strandbuf->vert) MEM_freeN(strandbuf->vert);
+ if (strandbuf->bound) MEM_freeN(strandbuf->bound);
+ MEM_freeN(strandbuf);
+ }
+
+ if (obr->mtface)
+ MEM_freeN(obr->mtface);
+
+ if (obr->mcol)
+ MEM_freeN(obr->mcol);
+
+ if (obr->rayfaces) {
+ MEM_freeN(obr->rayfaces);
+ obr->rayfaces = NULL;
+ }
+
+ if (obr->rayprimitives) {
+ MEM_freeN(obr->rayprimitives);
+ obr->rayprimitives = NULL;
+ }
+
+ if (obr->raytree) {
+ RE_rayobject_free(obr->raytree);
+ obr->raytree = NULL;
+ }
+ }
+
+ if (re->objectinstance) {
+ for (obi=re->instancetable.first; obi; obi=obi->next) {
+ if (obi->vectors)
+ MEM_freeN(obi->vectors);
+
+ if (obi->raytree)
+ RE_rayobject_free(obi->raytree);
+ }
+
+ MEM_freeN(re->objectinstance);
+ re->objectinstance= NULL;
+ re->totinstance= 0;
+ re->instancetable.first= re->instancetable.last= NULL;
+ }
+
+ if (re->sortedhalos) {
+ MEM_freeN(re->sortedhalos);
+ re->sortedhalos= NULL;
+ }
+
+ BLI_freelistN(&re->customdata_names);
+ BLI_freelistN(&re->objecttable);
+ BLI_freelistN(&re->instancetable);
+}
+
+/* ------------------------------------------------------------------------ */
+
+HaloRen *RE_findOrAddHalo(ObjectRen *obr, int nr)
+{
+ HaloRen *h, **temp;
+ int a;
+
+ if (nr<0) {
+ printf("error in findOrAddHalo: %d\n", nr);
+ return NULL;
+ }
+ a= nr>>8;
+
+ if (a>=obr->blohalen-1) { /* Need to allocate more columns..., and keep last element NULL for free loop */
+ //printf("Allocating %i more halo groups. %i total.\n",
+ // TABLEINITSIZE, obr->blohalen+TABLEINITSIZE );
+ temp=obr->bloha;
+
+ obr->bloha = (HaloRen **)MEM_callocN(sizeof(void *) * (obr->blohalen + TABLEINITSIZE), "Bloha");
+ if (temp) memcpy(obr->bloha, temp, obr->blohalen*sizeof(void *));
+ memset(&(obr->bloha[obr->blohalen]), 0, TABLEINITSIZE * sizeof(void *));
+ obr->blohalen+=TABLEINITSIZE; /*Does this really need to be power of 2?*/
+ if (temp) MEM_freeN(temp);
+ }
+
+ h= obr->bloha[a];
+ if (h==NULL) {
+ h= (HaloRen *)MEM_callocN(256*sizeof(HaloRen), "findOrAdHalo");
+ obr->bloha[a]= h;
+ }
+ h+= (nr & 255);
+ return h;
+}
+
+/* ------------------------------------------------------------------------- */
+
+HaloRen *RE_inithalo(Render *re, ObjectRen *obr, Material *ma,
+ const float vec[3], const float vec1[3],
+ const float *orco, float hasize, float vectsize, int seed)
+{
+ const bool skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0;
+ const bool texnode_preview = (re->r.scemode & R_TEXNODE_PREVIEW) != 0;
+ HaloRen *har;
+ MTex *mtex;
+ float tin, tr, tg, tb, ta;
+ float xn, yn, zn, texvec[3], hoco[4], hoco1[4];
+
+ if (hasize==0.0f) return NULL;
+
+ projectverto(vec, re->winmat, hoco);
+ if (hoco[3]==0.0f) return NULL;
+ if (vec1) {
+ projectverto(vec1, re->winmat, hoco1);
+ if (hoco1[3]==0.0f) return NULL;
+ }
+
+ har= RE_findOrAddHalo(obr, obr->tothalo++);
+ copy_v3_v3(har->co, vec);
+ har->hasize= hasize;
+
+ /* actual projectvert is done in function project_renderdata() because of parts/border/pano */
+ /* we do it here for sorting of halos */
+ zn= hoco[3];
+ har->xs= 0.5f*re->winx*(hoco[0]/zn);
+ har->ys= 0.5f*re->winy*(hoco[1]/zn);
+ har->zs= 0x7FFFFF*(hoco[2]/zn);
+
+ har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn);
+
+ /* halovect */
+ if (vec1) {
+
+ har->type |= HA_VECT;
+
+ xn= har->xs - 0.5f*re->winx*(hoco1[0]/hoco1[3]);
+ yn= har->ys - 0.5f*re->winy*(hoco1[1]/hoco1[3]);
+ if (yn == 0.0f && xn >= 0.0f) zn = 0.0f;
+ else zn = atan2f(yn, xn);
+
+ har->sin = sinf(zn);
+ har->cos = cosf(zn);
+ zn= len_v3v3(vec1, vec);
+
+ har->hasize= vectsize*zn + (1.0f-vectsize)*hasize;
+
+ sub_v3_v3v3(har->no, vec, vec1);
+ normalize_v3(har->no);
+ }
+
+ if (ma->mode & MA_HALO_XALPHA) har->type |= HA_XALPHA;
+
+ har->alfa= ma->alpha;
+ har->r= ma->r;
+ har->g= ma->g;
+ har->b= ma->b;
+ har->add= (255.0f*ma->add);
+ har->mat= ma;
+ har->hard= ma->har;
+ har->seed= seed % 256;
+
+ if (ma->mode & MA_STAR) har->starpoints= ma->starc;
+ if (ma->mode & MA_HALO_LINES) har->linec= ma->linec;
+ if (ma->mode & MA_HALO_RINGS) har->ringc= ma->ringc;
+ if (ma->mode & MA_HALO_FLARE) har->flarec= ma->flarec;
+
+
+ if (ma->mtex[0]) {
+
+ if (ma->mode & MA_HALOTEX) {
+ har->tex = 1;
+ }
+ else if (har->mat->septex & (1 << 0)) {
+ /* only 1 level textures */
+ }
+ else {
+ mtex= ma->mtex[0];
+ copy_v3_v3(texvec, vec);
+
+ if (mtex->texco & TEXCO_NORM) {
+ ;
+ }
+ else if (mtex->texco & TEXCO_OBJECT) {
+ /* texvec[0]+= imatbase->ivec[0]; */
+ /* texvec[1]+= imatbase->ivec[1]; */
+ /* texvec[2]+= imatbase->ivec[2]; */
+ /* mul_m3_v3(imatbase->imat, texvec); */
+ }
+ else {
+ if (orco) {
+ copy_v3_v3(texvec, orco);
+ }
+ }
+
+ externtex(mtex,
+ texvec,
+ &tin, &tr, &tg, &tb, &ta,
+ 0,
+ re->pool,
+ skip_load_image,
+ texnode_preview);
+
+ yn= tin*mtex->colfac;
+ //zn= tin*mtex->alphafac;
+
+ if (mtex->mapto & MAP_COL) {
+ zn= 1.0f-yn;
+ har->r= (yn*tr+ zn*ma->r);
+ har->g= (yn*tg+ zn*ma->g);
+ har->b= (yn*tb+ zn*ma->b);
+ }
+ if (mtex->texco & TEXCO_UV) {
+ har->alfa= tin;
+ }
+ if (mtex->mapto & MAP_ALPHA)
+ har->alfa= tin;
+ }
+ }
+
+ har->pool = re->pool;
+ har->skip_load_image = skip_load_image;
+ har->texnode_preview = texnode_preview;
+
+ return har;
+}
+
+HaloRen *RE_inithalo_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma,
+ const float vec[3], const float vec1[3],
+ const float *orco, const float *uvco, float hasize, float vectsize, int seed, const float pa_co[3])
+{
+ const bool skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0;
+ const bool texnode_preview = (re->r.scemode & R_TEXNODE_PREVIEW) != 0;
+ HaloRen *har;
+ MTex *mtex;
+ float tin, tr, tg, tb, ta;
+ float xn, yn, zn, texvec[3], hoco[4], hoco1[4], in[3], tex[3], out[3];
+ int i, hasrgb;
+
+ if (hasize==0.0f) return NULL;
+
+ projectverto(vec, re->winmat, hoco);
+ if (hoco[3]==0.0f) return NULL;
+ if (vec1) {
+ projectverto(vec1, re->winmat, hoco1);
+ if (hoco1[3]==0.0f) return NULL;
+ }
+
+ har= RE_findOrAddHalo(obr, obr->tothalo++);
+ copy_v3_v3(har->co, vec);
+ har->hasize= hasize;
+
+ /* actual projectvert is done in function project_renderdata() because of parts/border/pano */
+ /* we do it here for sorting of halos */
+ zn= hoco[3];
+ har->xs= 0.5f*re->winx*(hoco[0]/zn);
+ har->ys= 0.5f*re->winy*(hoco[1]/zn);
+ har->zs= 0x7FFFFF*(hoco[2]/zn);
+
+ har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn);
+
+ /* halovect */
+ if (vec1) {
+
+ har->type |= HA_VECT;
+
+ xn= har->xs - 0.5f*re->winx*(hoco1[0]/hoco1[3]);
+ yn= har->ys - 0.5f*re->winy*(hoco1[1]/hoco1[3]);
+ if (yn == 0.0f && xn >= 0.0f) zn = 0.0f;
+ else zn = atan2f(yn, xn);
+
+ har->sin = sinf(zn);
+ har->cos = cosf(zn);
+ zn= len_v3v3(vec1, vec)*0.5f;
+
+ har->hasize= vectsize*zn + (1.0f-vectsize)*hasize;
+
+ sub_v3_v3v3(har->no, vec, vec1);
+ normalize_v3(har->no);
+ }
+
+ if (ma->mode & MA_HALO_XALPHA) har->type |= HA_XALPHA;
+
+ har->alfa= ma->alpha;
+ har->r= ma->r;
+ har->g= ma->g;
+ har->b= ma->b;
+ har->add= (255.0f*ma->add);
+ har->mat= ma;
+ har->hard= ma->har;
+ har->seed= seed % 256;
+
+ if (ma->mode & MA_STAR) har->starpoints= ma->starc;
+ if (ma->mode & MA_HALO_LINES) har->linec= ma->linec;
+ if (ma->mode & MA_HALO_RINGS) har->ringc= ma->ringc;
+ if (ma->mode & MA_HALO_FLARE) har->flarec= ma->flarec;
+
+ if ((ma->mode & MA_HALOTEX) && ma->mtex[0])
+ har->tex= 1;
+
+ for (i=0; i<MAX_MTEX; i++)
+ if (ma->mtex[i] && (ma->septex & (1<<i))==0) {
+ mtex= ma->mtex[i];
+ copy_v3_v3(texvec, vec);
+
+ if (mtex->texco & TEXCO_NORM) {
+ ;
+ }
+ else if (mtex->texco & TEXCO_OBJECT) {
+ if (mtex->object)
+ mul_m4_v3(mtex->object->imat_ren, texvec);
+ }
+ else if (mtex->texco & TEXCO_GLOB) {
+ copy_v3_v3(texvec, vec);
+ }
+ else if (mtex->texco & TEXCO_UV && uvco) {
+ int uv_index=CustomData_get_named_layer_index(&dm->faceData, CD_MTFACE, mtex->uvname);
+ if (uv_index<0)
+ uv_index=CustomData_get_active_layer_index(&dm->faceData, CD_MTFACE);
+
+ uv_index-=CustomData_get_layer_index(&dm->faceData, CD_MTFACE);
+
+ texvec[0]=2.0f*uvco[2*uv_index]-1.0f;
+ texvec[1]=2.0f*uvco[2*uv_index+1]-1.0f;
+ texvec[2]=0.0f;
+ }
+ else if (mtex->texco & TEXCO_PARTICLE) {
+ /* particle coordinates in range [0, 1] */
+ texvec[0] = 2.f * pa_co[0] - 1.f;
+ texvec[1] = 2.f * pa_co[1] - 1.f;
+ texvec[2] = pa_co[2];
+ }
+ else if (orco) {
+ copy_v3_v3(texvec, orco);
+ }
+
+ hasrgb = externtex(mtex,
+ texvec,
+ &tin, &tr, &tg, &tb, &ta,
+ 0,
+ re->pool,
+ skip_load_image,
+ texnode_preview);
+
+ //yn= tin*mtex->colfac;
+ //zn= tin*mtex->alphafac;
+ if (mtex->mapto & MAP_COL) {
+ tex[0]=tr;
+ tex[1]=tg;
+ tex[2]=tb;
+ out[0]=har->r;
+ out[1]=har->g;
+ out[2]=har->b;
+
+ texture_rgb_blend(in, tex, out, tin, mtex->colfac, mtex->blendtype);
+ // zn= 1.0-yn;
+ //har->r= (yn*tr+ zn*ma->r);
+ //har->g= (yn*tg+ zn*ma->g);
+ //har->b= (yn*tb+ zn*ma->b);
+ har->r= in[0];
+ har->g= in[1];
+ har->b= in[2];
+ }
+
+ /* alpha returned, so let's use it instead of intensity */
+ if (hasrgb)
+ tin = ta;
+
+ if (mtex->mapto & MAP_ALPHA)
+ har->alfa = texture_value_blend(mtex->def_var, har->alfa, tin, mtex->alphafac, mtex->blendtype);
+ if (mtex->mapto & MAP_HAR)
+ har->hard = 1.0f+126.0f*texture_value_blend(mtex->def_var, ((float)har->hard)/127.0f, tin, mtex->hardfac, mtex->blendtype);
+ if (mtex->mapto & MAP_RAYMIRR)
+ har->hasize = 100.0f*texture_value_blend(mtex->def_var, har->hasize/100.0f, tin, mtex->raymirrfac, mtex->blendtype);
+ if (mtex->mapto & MAP_TRANSLU) {
+ float add = texture_value_blend(mtex->def_var, (float)har->add/255.0f, tin, mtex->translfac, mtex->blendtype);
+ CLAMP(add, 0.f, 1.f);
+ har->add = 255.0f*add;
+ }
+ /* now what on earth is this good for?? */
+ //if (mtex->texco & 16) {
+ // har->alfa= tin;
+ //}
+ }
+
+ har->pool = re->pool;
+ har->skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0;
+ har->texnode_preview = (re->r.scemode & R_TEXNODE_PREVIEW) != 0;
+
+ return har;
+}
+
+/* -------------------------- operations on entire database ----------------------- */
+
+/* ugly function for halos in panorama */
+static int panotestclip(Render *re, bool do_pano, float v[4])
+{
+ /* part size (ensure we run RE_parts_clamp first) */
+ BLI_assert(re->partx == min_ii(re->r.tilex, re->rectx));
+ BLI_assert(re->party == min_ii(re->r.tiley, re->recty));
+
+ if (do_pano == false) {
+ return testclip(v);
+ }
+ else {
+ /* to be used for halos en infos */
+ float abs4;
+ short c = 0;
+
+ int xparts = (re->rectx + re->partx - 1) / re->partx;
+
+ abs4= fabsf(v[3]);
+
+ if (v[2]< -abs4) c=16; /* this used to be " if (v[2]<0) ", see clippz() */
+ else if (v[2]> abs4) c+= 32;
+
+ if ( v[1]>abs4) c+=4;
+ else if ( v[1]< -abs4) c+=8;
+
+ abs4*= xparts;
+ if ( v[0]>abs4) c+=2;
+ else if ( v[0]< -abs4) c+=1;
+
+ return c;
+ }
+}
+
+/**
+ * This adds the hcs coordinates to vertices. It iterates over all
+ * vertices, halos and faces. After the conversion, we clip in hcs.
+ *
+ * Elsewhere, all primites are converted to vertices.
+ * Called in
+ * - envmapping (envmap.c)
+ * - shadow buffering (shadbuf.c)
+ */
+
+void project_renderdata(Render *re,
+ void (*projectfunc)(const float *, float mat[4][4], float *),
+ bool do_pano, float xoffs, bool UNUSED(do_buckets))
+{
+ ObjectRen *obr;
+ HaloRen *har = NULL;
+ float zn, vec[3], hoco[4];
+ int a;
+
+ if (do_pano) {
+ float panophi= xoffs;
+
+ re->panosi = sinf(panophi);
+ re->panoco = cosf(panophi);
+ }
+
+ for (obr=re->objecttable.first; obr; obr=obr->next) {
+ /* calculate view coordinates (and zbuffer value) */
+ for (a=0; a<obr->tothalo; a++) {
+ if ((a & 255)==0) har= obr->bloha[a>>8];
+ else har++;
+
+ if (do_pano) {
+ vec[0]= re->panoco*har->co[0] + re->panosi*har->co[2];
+ vec[1]= har->co[1];
+ vec[2]= -re->panosi*har->co[0] + re->panoco*har->co[2];
+ }
+ else {
+ copy_v3_v3(vec, har->co);
+ }
+
+ projectfunc(vec, re->winmat, hoco);
+
+ /* we clip halos less critical, but not for the Z */
+ hoco[0]*= 0.5f;
+ hoco[1]*= 0.5f;
+
+ if ( panotestclip(re, do_pano, hoco) ) {
+ har->miny= har->maxy= -10000; /* that way render clips it */
+ }
+ else if (hoco[3]<0.0f) {
+ har->miny= har->maxy= -10000; /* render clips it */
+ }
+ else { /* do the projection...*/
+ /* bring back hocos */
+ hoco[0]*= 2.0f;
+ hoco[1]*= 2.0f;
+
+ zn= hoco[3];
+ har->xs= 0.5f*re->winx*(1.0f+hoco[0]/zn); /* the 0.5 negates the previous 2...*/
+ har->ys= 0.5f*re->winy*(1.0f+hoco[1]/zn);
+
+ /* this should be the zbuffer coordinate */
+ har->zs= 0x7FFFFF*(hoco[2]/zn);
+ /* taking this from the face clip functions? seems ok... */
+ har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn);
+
+ vec[0]+= har->hasize;
+ projectfunc(vec, re->winmat, hoco);
+ vec[0]-= har->hasize;
+ zn= hoco[3];
+ har->rad= fabsf(har->xs- 0.5f*re->winx*(1.0f+hoco[0]/zn));
+
+ /* this clip is not really OK, to prevent stars to become too large */
+ if (har->type & HA_ONLYSKY) {
+ if (har->rad>3.0f) har->rad= 3.0f;
+ }
+
+ har->radsq= har->rad*har->rad;
+
+ har->miny= har->ys - har->rad/re->ycor;
+ har->maxy= har->ys + har->rad/re->ycor;
+
+ /* the Zd value is still not really correct for pano */
+
+ vec[2] -= har->hasize; /* z negative, otherwise it's clipped */
+ projectfunc(vec, re->winmat, hoco);
+ zn = hoco[3];
+ zn = fabsf((float)har->zs - 0x7FFFFF * (hoco[2] / zn));
+ har->zd = CLAMPIS(zn, 0, INT_MAX);
+
+ }
+
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+void RE_updateRenderInstance(Render *re, ObjectInstanceRen *obi, int flag)
+{
+ /* flag specifies what things have changed. */
+ if (flag & RE_OBJECT_INSTANCES_UPDATE_OBMAT) {
+ copy_m4_m4(obi->obmat, obi->ob->obmat);
+ invert_m4_m4(obi->obinvmat, obi->obmat);
+ }
+ if (flag & RE_OBJECT_INSTANCES_UPDATE_VIEW) {
+ mul_m4_m4m4(obi->localtoviewmat, re->viewmat, obi->obmat);
+ mul_m4_m4m4(obi->localtoviewinvmat, obi->obinvmat, re->viewinv);
+ }
+}
+
+void RE_updateRenderInstances(Render *re, int flag)
+{
+ int i = 0;
+ for (i = 0; i < re->totinstance; i++)
+ RE_updateRenderInstance(re, &re->objectinstance[i], flag);
+}
+
+ObjectInstanceRen *RE_addRenderInstance(
+ Render *re, ObjectRen *obr, Object *ob, Object *par,
+ int index, int psysindex, float mat[4][4], int lay, const DupliObject *dob)
+{
+ ObjectInstanceRen *obi;
+ float mat3[3][3];
+
+ obi= MEM_callocN(sizeof(ObjectInstanceRen), "ObjectInstanceRen");
+ obi->obr= obr;
+ obi->ob= ob;
+ obi->par= par;
+ obi->index= index;
+ obi->psysindex= psysindex;
+ obi->lay= lay;
+
+ /* Fill particle info */
+ if (par && dob) {
+ const ParticleSystem *psys = dob->particle_system;
+ if (psys) {
+ int part_index;
+ if (obi->index < psys->totpart) {
+ part_index = obi->index;
+ }
+ else if (psys->child) {
+ part_index = psys->child[obi->index - psys->totpart].parent;
+ }
+ else {
+ part_index = -1;
+ }
+
+ if (part_index >= 0) {
+ const ParticleData *p = &psys->particles[part_index];
+ obi->part_index = part_index;
+ obi->part_size = p->size;
+ obi->part_age = RE_GetStats(re)->cfra - p->time;
+ obi->part_lifetime = p->lifetime;
+
+ copy_v3_v3(obi->part_co, p->state.co);
+ copy_v3_v3(obi->part_vel, p->state.vel);
+ copy_v3_v3(obi->part_avel, p->state.ave);
+ }
+ }
+ }
+
+ /* Fill object info */
+ if (dob) {
+ obi->random_id = dob->random_id;
+ }
+ else {
+ obi->random_id = BLI_hash_int_2d(BLI_hash_string(obi->ob->id.name + 2), 0);
+ }
+
+ RE_updateRenderInstance(re, obi, RE_OBJECT_INSTANCES_UPDATE_OBMAT | RE_OBJECT_INSTANCES_UPDATE_VIEW);
+
+ if (mat) {
+ copy_m4_m4(obi->mat, mat);
+ copy_m3_m4(mat3, mat);
+ invert_m3_m3(obi->nmat, mat3);
+ transpose_m3(obi->nmat);
+ obi->flag |= R_DUPLI_TRANSFORMED;
+ }
+
+ BLI_addtail(&re->instancetable, obi);
+
+ return obi;
+}
+
+void RE_instance_get_particle_info(struct ObjectInstanceRen *obi, float *index, float *random, float *age, float *lifetime, float co[3], float *size, float vel[3], float angvel[3])
+{
+ *index = obi->part_index;
+ *random = BLI_hash_int_01(obi->part_index);
+ *age = obi->part_age;
+ *lifetime = obi->part_lifetime;
+ copy_v3_v3(co, obi->part_co);
+ *size = obi->part_size;
+ copy_v3_v3(vel, obi->part_vel);
+ copy_v3_v3(angvel, obi->part_avel);
+}
+
+
+void RE_makeRenderInstances(Render *re)
+{
+ ObjectInstanceRen *obi, *oldobi;
+ ListBase newlist;
+ int tot;
+
+ /* convert list of object instances to an array for index based lookup */
+ tot= BLI_listbase_count(&re->instancetable);
+ re->objectinstance= MEM_callocN(sizeof(ObjectInstanceRen)*tot, "ObjectInstance");
+ re->totinstance= tot;
+ newlist.first= newlist.last= NULL;
+
+ obi= re->objectinstance;
+ for (oldobi=re->instancetable.first; oldobi; oldobi=oldobi->next) {
+ *obi= *oldobi;
+
+ if (obi->obr) {
+ obi->prev= obi->next= NULL;
+ BLI_addtail(&newlist, obi);
+ obi++;
+ }
+ else
+ re->totinstance--;
+ }
+
+ BLI_freelistN(&re->instancetable);
+ re->instancetable= newlist;
+}
+
+/* four functions to facilitate envmap rotation for raytrace */
+void RE_instance_rotate_ray_start(ObjectInstanceRen *obi, Isect *is)
+{
+ if (obi && (obi->flag & R_ENV_TRANSFORMED)) {
+ copy_v3_v3(is->origstart, is->start);
+ mul_m4_v3(obi->imat, is->start);
+ }
+}
+
+void RE_instance_rotate_ray_dir(ObjectInstanceRen *obi, Isect *is)
+{
+ if (obi && (obi->flag & R_ENV_TRANSFORMED)) {
+ float end[3];
+
+ copy_v3_v3(is->origdir, is->dir);
+ add_v3_v3v3(end, is->origstart, is->dir);
+
+ mul_m4_v3(obi->imat, end);
+ sub_v3_v3v3(is->dir, end, is->start);
+ }
+}
+
+void RE_instance_rotate_ray(ObjectInstanceRen *obi, Isect *is)
+{
+ RE_instance_rotate_ray_start(obi, is);
+ RE_instance_rotate_ray_dir(obi, is);
+}
+
+void RE_instance_rotate_ray_restore(ObjectInstanceRen *obi, Isect *is)
+{
+ if (obi && (obi->flag & R_ENV_TRANSFORMED)) {
+ copy_v3_v3(is->start, is->origstart);
+ copy_v3_v3(is->dir, is->origdir);
+ }
+}
+
+int clip_render_object(float boundbox[2][3], float bounds[4], float winmat[4][4])
+{
+ float mat[4][4], vec[4];
+ int a, fl, flag = -1;
+
+ copy_m4_m4(mat, winmat);
+
+ for (a=0; a < 8; a++) {
+ vec[0]= (a & 1)? boundbox[0][0]: boundbox[1][0];
+ vec[1]= (a & 2)? boundbox[0][1]: boundbox[1][1];
+ vec[2]= (a & 4)? boundbox[0][2]: boundbox[1][2];
+ vec[3]= 1.0;
+ mul_m4_v4(mat, vec);
+
+ fl = 0;
+ if (bounds) {
+ if (vec[0] < bounds[0] * vec[3]) fl |= 1;
+ else if (vec[0] > bounds[1] * vec[3]) fl |= 2;
+
+ if (vec[1] > bounds[3] * vec[3]) fl |= 4;
+ else if (vec[1] < bounds[2] * vec[3]) fl |= 8;
+ }
+ else {
+ if (vec[0] < -vec[3]) fl |= 1;
+ else if (vec[0] > vec[3]) fl |= 2;
+
+ if (vec[1] > vec[3]) fl |= 4;
+ else if (vec[1] < -vec[3]) fl |= 8;
+ }
+ if (vec[2] < -vec[3]) fl |= 16;
+ else if (vec[2] > vec[3]) fl |= 32;
+
+ flag &= fl;
+ if (flag == 0) {
+ return 0;
+ }
+ }
+
+ return flag;
+}
+
diff --git a/source/blender/render/intern/source/shadbuf.c b/source/blender/render/intern/source/shadbuf.c
new file mode 100644
index 00000000000..04e9177241b
--- /dev/null
+++ b/source/blender/render/intern/source/shadbuf.c
@@ -0,0 +1,2647 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): 2004-2006, Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/shadbuf.c
+ * \ingroup render
+ */
+
+
+#include <math.h>
+#include <string.h>
+
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_group_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_jitter_2d.h"
+#include "BLI_memarena.h"
+#include "BLI_rand.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_global.h"
+#include "BKE_scene.h"
+
+#include "PIL_time.h"
+
+#include "render_types.h"
+#include "renderdatabase.h"
+#include "rendercore.h"
+#include "shadbuf.h"
+#include "shading.h"
+#include "zbuf.h"
+
+/* XXX, could be better implemented... this is for endian issues */
+#ifdef __BIG_ENDIAN__
+//# define RCOMP 3
+# define GCOMP 2
+# define BCOMP 1
+# define ACOMP 0
+#else
+//# define RCOMP 0
+# define GCOMP 1
+# define BCOMP 2
+# define ACOMP 3
+#endif
+
+#define RCT_SIZE_X(rct) ((rct)->xmax - (rct)->xmin)
+#define RCT_SIZE_Y(rct) ((rct)->ymax - (rct)->ymin)
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* ------------------------------------------------------------------------- */
+
+/* initshadowbuf() in convertBlenderScene.c */
+
+/* ------------------------------------------------------------------------- */
+
+static void copy_to_ztile(int *rectz, int size, int x1, int y1, int tile, char *r1)
+{
+ int len4, *rz;
+ int x2, y2;
+
+ x2= x1+tile;
+ y2= y1+tile;
+ if (x2>=size) x2= size-1;
+ if (y2>=size) y2= size-1;
+
+ if (x1>=x2 || y1>=y2) return;
+
+ len4= 4*(x2- x1);
+ rz= rectz + size*y1 + x1;
+ for (; y1<y2; y1++) {
+ memcpy(r1, rz, len4);
+ rz+= size;
+ r1+= len4;
+ }
+}
+
+#if 0
+static int sizeoflampbuf(ShadBuf *shb)
+{
+ int num, count=0;
+ char *cp;
+
+ cp= shb->cbuf;
+ num= (shb->size*shb->size)/256;
+
+ while (num--) count+= *(cp++);
+
+ return 256*count;
+}
+#endif
+
+/* not threadsafe... */
+static float *give_jitter_tab(int samp)
+{
+ /* these are all possible jitter tables, takes up some
+ * 12k, not really bad!
+ * For soft shadows, it saves memory and render time
+ */
+ static int tab[17]={1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256};
+ static float jit[1496][2];
+ static char ctab[17]= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int a, offset=0;
+
+ if (samp<2) samp= 2;
+ else if (samp>16) samp= 16;
+
+ for (a=0; a<samp-1; a++) offset+= tab[a];
+
+ if (ctab[samp]==0) {
+ ctab[samp]= 1;
+ BLI_jitter_init((float (*)[2])jit[offset], samp*samp);
+ }
+
+ return jit[offset];
+
+}
+
+static void make_jitter_weight_tab(Render *re, ShadBuf *shb, short filtertype)
+{
+ float *jit, totw= 0.0f;
+ int samp= get_render_shadow_samples(&re->r, shb->samp);
+ int a, tot=samp*samp;
+
+ shb->weight= MEM_mallocN(sizeof(float)*tot, "weight tab lamp");
+
+ for (jit= shb->jit, a=0; a<tot; a++, jit+=2) {
+ if (filtertype==LA_SHADBUF_TENT)
+ shb->weight[a] = 0.71f - sqrtf(jit[0] * jit[0] + jit[1] * jit[1]);
+ else if (filtertype==LA_SHADBUF_GAUSS)
+ shb->weight[a] = RE_filter_value(R_FILTER_GAUSS, 1.8f * sqrtf(jit[0] * jit[0] + jit[1] * jit[1]));
+ else
+ shb->weight[a]= 1.0f;
+
+ totw+= shb->weight[a];
+ }
+
+ totw= 1.0f/totw;
+ for (a=0; a<tot; a++) {
+ shb->weight[a]*= totw;
+ }
+}
+
+static int verg_deepsample(const void *poin1, const void *poin2)
+{
+ const DeepSample *ds1= (const DeepSample*)poin1;
+ const DeepSample *ds2= (const DeepSample*)poin2;
+
+ if (ds1->z < ds2->z) return -1;
+ else if (ds1->z == ds2->z) return 0;
+ else return 1;
+}
+
+static int compress_deepsamples(DeepSample *dsample, int tot, float epsilon)
+{
+ /* uses doubles to avoid overflows and other numerical issues,
+ * could be improved */
+ DeepSample *ds, *newds;
+ float v;
+ double slope, slopemin, slopemax, min, max, div, newmin, newmax;
+ int a, first, z, newtot= 0;
+
+#if 0
+ if (print) {
+ for (a=0, ds=dsample; a<tot; a++, ds++)
+ printf("%lf, %f ", ds->z/(double)0x7FFFFFFF, ds->v);
+ printf("\n");
+ }
+#endif
+
+ /* read from and write into same array */
+ ds= dsample;
+ newds= dsample;
+ a= 0;
+
+ /* as long as we are not at the end of the array */
+ for (a++, ds++; a<tot; a++, ds++) {
+ slopemin= 0.0f;
+ slopemax= 0.0f;
+ first= 1;
+
+ for (; a<tot; a++, ds++) {
+ //dz= ds->z - newds->z;
+ if (ds->z == newds->z) {
+ /* still in same z position, simply check
+ * visibility difference against epsilon */
+ if (!(fabsf(newds->v - ds->v) <= epsilon)) {
+ break;
+ }
+ }
+ else {
+ /* compute slopes */
+ div= (double)0x7FFFFFFF / ((double)ds->z - (double)newds->z);
+ min= (double)((ds->v - epsilon) - newds->v) * div;
+ max= (double)((ds->v + epsilon) - newds->v) * div;
+
+ /* adapt existing slopes */
+ if (first) {
+ newmin= min;
+ newmax= max;
+ first= 0;
+ }
+ else {
+ newmin= MAX2(slopemin, min);
+ newmax= MIN2(slopemax, max);
+
+ /* verify if there is still space between the slopes */
+ if (newmin > newmax) {
+ ds--;
+ a--;
+ break;
+ }
+ }
+
+ slopemin= newmin;
+ slopemax= newmax;
+ }
+ }
+
+ if (a == tot) {
+ ds--;
+ a--;
+ }
+
+ /* always previous z */
+ z= ds->z;
+
+ if (first || a==tot-1) {
+ /* if slopes were not initialized, use last visibility */
+ v= ds->v;
+ }
+ else {
+ /* compute visibility at center between slopes at z */
+ slope = (slopemin + slopemax) * 0.5;
+ v = (double)newds->v + slope * ((double)(z - newds->z) / (double)0x7FFFFFFF);
+ }
+
+ newds++;
+ newtot++;
+
+ newds->z= z;
+ newds->v= v;
+ }
+
+ if (newtot == 0 || (newds->v != (newds-1)->v))
+ newtot++;
+
+#if 0
+ if (print) {
+ for (a=0, ds=dsample; a<newtot; a++, ds++)
+ printf("%lf, %f ", ds->z/(double)0x7FFFFFFF, ds->v);
+ printf("\n");
+ }
+#endif
+
+ return newtot;
+}
+
+static float deep_alpha(Render *re, int obinr, int facenr, bool use_strand)
+{
+ ObjectInstanceRen *obi= &re->objectinstance[obinr];
+ Material *ma;
+
+ if (use_strand) {
+ StrandRen *strand= RE_findOrAddStrand(obi->obr, facenr-1);
+ ma= strand->buffer->ma;
+ }
+ else {
+ VlakRen *vlr= RE_findOrAddVlak(obi->obr, (facenr-1) & RE_QUAD_MASK);
+ ma= vlr->mat;
+ }
+
+ return ma->shad_alpha;
+}
+
+static void compress_deepshadowbuf(Render *re, ShadBuf *shb, APixstr *apixbuf, APixstrand *apixbufstrand)
+{
+ ShadSampleBuf *shsample;
+ DeepSample *ds[RE_MAX_OSA], *sampleds[RE_MAX_OSA], *dsb, *newbuf;
+ APixstr *ap, *apn;
+ APixstrand *aps, *apns;
+ float visibility;
+
+ const int totbuf= shb->totbuf;
+ const float totbuf_f= (float)shb->totbuf;
+ const float totbuf_f_inv= 1.0f/totbuf_f;
+ const int size= shb->size;
+
+ int a, b, c, tot, minz, found, prevtot, newtot;
+ int sampletot[RE_MAX_OSA], totsample = 0, totsamplec = 0;
+
+ shsample= MEM_callocN(sizeof(ShadSampleBuf), "shad sample buf");
+ BLI_addtail(&shb->buffers, shsample);
+
+ shsample->totbuf = MEM_callocN(sizeof(int) * size * size, "deeptotbuf");
+ shsample->deepbuf = MEM_callocN(sizeof(DeepSample *) * size * size, "deepbuf");
+
+ ap= apixbuf;
+ aps= apixbufstrand;
+ for (a=0; a<size*size; a++, ap++, aps++) {
+ /* count number of samples */
+ for (c=0; c<totbuf; c++)
+ sampletot[c]= 0;
+
+ tot= 0;
+ for (apn=ap; apn; apn=apn->next)
+ for (b=0; b<4; b++)
+ if (apn->p[b])
+ for (c=0; c<totbuf; c++)
+ if (apn->mask[b] & (1<<c))
+ sampletot[c]++;
+
+ if (apixbufstrand) {
+ for (apns=aps; apns; apns=apns->next)
+ for (b=0; b<4; b++)
+ if (apns->p[b])
+ for (c=0; c<totbuf; c++)
+ if (apns->mask[b] & (1<<c))
+ sampletot[c]++;
+ }
+
+ for (c=0; c<totbuf; c++)
+ tot += sampletot[c];
+
+ if (tot == 0) {
+ shsample->deepbuf[a]= NULL;
+ shsample->totbuf[a]= 0;
+ continue;
+ }
+
+ /* fill samples */
+ ds[0]= sampleds[0]= MEM_callocN(sizeof(DeepSample)*tot*2, "deepsample");
+ for (c=1; c<totbuf; c++)
+ ds[c]= sampleds[c]= sampleds[c-1] + sampletot[c-1]*2;
+
+ for (apn=ap; apn; apn=apn->next) {
+ for (b=0; b<4; b++) {
+ if (apn->p[b]) {
+ for (c=0; c<totbuf; c++) {
+ if (apn->mask[b] & (1<<c)) {
+ /* two entries to create step profile */
+ ds[c]->z= apn->z[b];
+ ds[c]->v= 1.0f; /* not used */
+ ds[c]++;
+ ds[c]->z= apn->z[b];
+ ds[c]->v= deep_alpha(re, apn->obi[b], apn->p[b], 0);
+ ds[c]++;
+ }
+ }
+ }
+ }
+ }
+
+ if (apixbufstrand) {
+ for (apns=aps; apns; apns=apns->next) {
+ for (b=0; b<4; b++) {
+ if (apns->p[b]) {
+ for (c=0; c<totbuf; c++) {
+ if (apns->mask[b] & (1<<c)) {
+ /* two entries to create step profile */
+ ds[c]->z= apns->z[b];
+ ds[c]->v= 1.0f; /* not used */
+ ds[c]++;
+ ds[c]->z= apns->z[b];
+ ds[c]->v= deep_alpha(re, apns->obi[b], apns->p[b], 1);
+ ds[c]++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (c=0; c<totbuf; c++) {
+ /* sort by increasing z */
+ qsort(sampleds[c], sampletot[c], sizeof(DeepSample)*2, verg_deepsample);
+
+ /* sum visibility, replacing alpha values */
+ visibility= 1.0f;
+ ds[c]= sampleds[c];
+
+ for (b=0; b<sampletot[c]; b++) {
+ /* two entries creating step profile */
+ ds[c]->v= visibility;
+ ds[c]++;
+
+ visibility *= 1.0f-ds[c]->v;
+ ds[c]->v= visibility;
+ ds[c]++;
+ }
+
+ /* halfway trick, probably won't work well for volumes? */
+ ds[c]= sampleds[c];
+ for (b=0; b<sampletot[c]; b++) {
+ if (b+1 < sampletot[c]) {
+ ds[c]->z= (ds[c]->z>>1) + ((ds[c]+2)->z>>1);
+ ds[c]++;
+ ds[c]->z= (ds[c]->z>>1) + ((ds[c]+2)->z>>1);
+ ds[c]++;
+ }
+ else {
+ ds[c]->z= (ds[c]->z>>1) + (0x7FFFFFFF>>1);
+ ds[c]++;
+ ds[c]->z= (ds[c]->z>>1) + (0x7FFFFFFF>>1);
+ ds[c]++;
+ }
+ }
+
+ /* init for merge loop */
+ ds[c]= sampleds[c];
+ sampletot[c] *= 2;
+ }
+
+ shsample->deepbuf[a]= MEM_callocN(sizeof(DeepSample)*tot*2, "deepsample");
+ shsample->totbuf[a]= 0;
+
+ /* merge buffers */
+ dsb= shsample->deepbuf[a];
+ while (1) {
+ minz= 0;
+ found= 0;
+
+ for (c=0; c<totbuf; c++) {
+ if (sampletot[c] && (!found || ds[c]->z < minz)) {
+ minz= ds[c]->z;
+ found= 1;
+ }
+ }
+
+ if (!found)
+ break;
+
+ dsb->z= minz;
+ dsb->v= 0.0f;
+
+ visibility= 0.0f;
+ for (c=0; c<totbuf; c++) {
+ if (sampletot[c] && ds[c]->z == minz) {
+ ds[c]++;
+ sampletot[c]--;
+ }
+
+ if (sampleds[c] == ds[c])
+ visibility += totbuf_f_inv;
+ else
+ visibility += (ds[c]-1)->v / totbuf_f;
+ }
+
+ dsb->v= visibility;
+ dsb++;
+ shsample->totbuf[a]++;
+ }
+
+ prevtot= shsample->totbuf[a];
+ totsample += prevtot;
+
+ newtot= compress_deepsamples(shsample->deepbuf[a], prevtot, shb->compressthresh);
+ shsample->totbuf[a]= newtot;
+ totsamplec += newtot;
+
+ if (newtot < prevtot) {
+ newbuf= MEM_mallocN(sizeof(DeepSample)*newtot, "cdeepsample");
+ memcpy(newbuf, shsample->deepbuf[a], sizeof(DeepSample)*newtot);
+ MEM_freeN(shsample->deepbuf[a]);
+ shsample->deepbuf[a]= newbuf;
+ }
+
+ MEM_freeN(sampleds[0]);
+ }
+
+ //printf("%d -> %d, ratio %f\n", totsample, totsamplec, (float)totsamplec/(float)totsample);
+}
+
+/* create Z tiles (for compression): this system is 24 bits!!! */
+static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square)
+{
+ ShadSampleBuf *shsample;
+ float dist;
+ uintptr_t *ztile;
+ int *rz, *rz1, verg, verg1, size= shb->size;
+ int a, x, y, minx, miny, byt1, byt2;
+ char *rc, *rcline, *ctile, *zt;
+
+ shsample= MEM_callocN(sizeof(ShadSampleBuf), "shad sample buf");
+ BLI_addtail(&shb->buffers, shsample);
+
+ shsample->zbuf= MEM_mallocN(sizeof(uintptr_t)*(size*size)/256, "initshadbuf2");
+ shsample->cbuf= MEM_callocN((size*size)/256, "initshadbuf3");
+
+ ztile= (uintptr_t *)shsample->zbuf;
+ ctile= shsample->cbuf;
+
+ /* help buffer */
+ rcline= MEM_mallocN(256*4+sizeof(int), "makeshadbuf2");
+
+ for (y=0; y<size; y+=16) {
+ if (y< size/2) miny= y+15-size/2;
+ else miny= y-size/2;
+
+ for (x=0; x<size; x+=16) {
+
+ /* is tile within spotbundle? */
+ a= size/2;
+ if (x< a) minx= x+15-a;
+ else minx= x-a;
+
+ dist = sqrtf((float)(minx * minx + miny * miny));
+
+ if (square==0 && dist>(float)(a+12)) { /* 12, tested with a onlyshadow lamp */
+ a= 256; verg= 0; /* 0x80000000; */ /* 0x7FFFFFFF; */
+ rz1= (&verg)+1;
+ }
+ else {
+ copy_to_ztile(rectz, size, x, y, 16, rcline);
+ rz1= (int *)rcline;
+
+ verg= (*rz1 & 0xFFFFFF00);
+
+ for (a=0;a<256;a++, rz1++) {
+ if ( (*rz1 & 0xFFFFFF00) !=verg) break;
+ }
+ }
+ if (a==256) { /* complete empty tile */
+ *ctile= 0;
+ *ztile= *(rz1-1);
+ }
+ else {
+
+ /* ACOMP etc. are defined to work L/B endian */
+
+ rc= rcline;
+ rz1= (int *)rcline;
+ verg= rc[ACOMP];
+ verg1= rc[BCOMP];
+ rc+= 4;
+ byt1= 1; byt2= 1;
+ for (a=1;a<256;a++, rc+=4) {
+ byt1 &= (verg==rc[ACOMP]);
+ byt2 &= (verg1==rc[BCOMP]);
+
+ if (byt1==0) break;
+ }
+ if (byt1 && byt2) { /* only store byte */
+ *ctile= 1;
+ *ztile= (uintptr_t)MEM_mallocN(256+4, "tile1");
+ rz= (int *)*ztile;
+ *rz= *rz1;
+
+ zt= (char *)(rz+1);
+ rc= rcline;
+ for (a=0; a<256; a++, zt++, rc+=4) *zt= rc[GCOMP];
+ }
+ else if (byt1) { /* only store short */
+ *ctile= 2;
+ *ztile= (uintptr_t)MEM_mallocN(2*256+4, "Tile2");
+ rz= (int *)*ztile;
+ *rz= *rz1;
+
+ zt= (char *)(rz+1);
+ rc= rcline;
+ for (a=0; a<256; a++, zt+=2, rc+=4) {
+ zt[0]= rc[BCOMP];
+ zt[1]= rc[GCOMP];
+ }
+ }
+ else { /* store triple */
+ *ctile= 3;
+ *ztile= (uintptr_t)MEM_mallocN(3*256, "Tile3");
+
+ zt= (char *)*ztile;
+ rc= rcline;
+ for (a=0; a<256; a++, zt+=3, rc+=4) {
+ zt[0]= rc[ACOMP];
+ zt[1]= rc[BCOMP];
+ zt[2]= rc[GCOMP];
+ }
+ }
+ }
+ ztile++;
+ ctile++;
+ }
+ }
+
+ MEM_freeN(rcline);
+}
+
+/* sets start/end clipping. lar->shb should be initialized */
+static void shadowbuf_autoclip(Render *re, LampRen *lar)
+{
+ ObjectInstanceRen *obi;
+ ObjectRen *obr;
+ VlakRen *vlr= NULL;
+ VertRen *ver= NULL;
+ Material *ma= NULL;
+ float minz, maxz, vec[3], viewmat[4][4], obviewmat[4][4];
+ unsigned int lay = -1;
+ int i, a, maxtotvert, ok= 1;
+ char *clipflag;
+
+ minz= 1.0e30f; maxz= -1.0e30f;
+ copy_m4_m4(viewmat, lar->shb->viewmat);
+
+ if (lar->mode & (LA_LAYER|LA_LAYER_SHADOW)) lay= lar->lay;
+
+ maxtotvert= 0;
+ for (obr=re->objecttable.first; obr; obr=obr->next)
+ maxtotvert = max_ii(obr->totvert, maxtotvert);
+
+ clipflag= MEM_callocN(sizeof(char)*maxtotvert, "autoclipflag");
+
+ /* set clip in vertices when face visible */
+ for (i=0, obi=re->instancetable.first; obi; i++, obi=obi->next) {
+ obr= obi->obr;
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(obviewmat, viewmat, obi->mat);
+ else
+ copy_m4_m4(obviewmat, viewmat);
+
+ memset(clipflag, 0, sizeof(char)*obr->totvert);
+
+ /* clear clip, is being set if face is visible (clip is calculated for real later) */
+ for (a=0; a<obr->totvlak; a++) {
+ if ((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak;
+ else vlr++;
+
+ /* note; these conditions are copied from zbuffer_shadow() */
+ if (vlr->mat!= ma) {
+ ma= vlr->mat;
+ ok= 1;
+ if ((ma->mode2 & MA_CASTSHADOW)==0 || (ma->mode & MA_SHADBUF)==0) ok= 0;
+ }
+
+ if (ok && (obi->lay & lay)) {
+ clipflag[vlr->v1->index]= 1;
+ clipflag[vlr->v2->index]= 1;
+ clipflag[vlr->v3->index]= 1;
+ if (vlr->v4) clipflag[vlr->v4->index]= 1;
+ }
+ }
+
+ /* calculate min and max */
+ for (a=0; a< obr->totvert;a++) {
+ if ((a & 255)==0) ver= RE_findOrAddVert(obr, a);
+ else ver++;
+
+ if (clipflag[a]) {
+ copy_v3_v3(vec, ver->co);
+ mul_m4_v3(obviewmat, vec);
+ /* Z on visible side of lamp space */
+ if (vec[2] < 0.0f) {
+ float inpr, z= -vec[2];
+
+ /* since vec is rotated in lampspace, this is how to get the cosine of angle */
+ /* precision is set 20% larger */
+ vec[2]*= 1.2f;
+ normalize_v3(vec);
+ inpr= - vec[2];
+
+ if (inpr>=lar->spotsi) {
+ if (z<minz) minz= z;
+ if (z>maxz) maxz= z;
+ }
+ }
+ }
+ }
+ }
+
+ MEM_freeN(clipflag);
+
+ /* set clipping min and max */
+ if (minz < maxz) {
+ float delta= (maxz - minz); /* threshold to prevent precision issues */
+
+ //printf("minz %f maxz %f delta %f\n", minz, maxz, delta);
+ if (lar->bufflag & LA_SHADBUF_AUTO_START)
+ lar->shb->d= minz - delta*0.02f; /* 0.02 is arbitrary... needs more thinking! */
+ if (lar->bufflag & LA_SHADBUF_AUTO_END)
+ lar->shb->clipend= maxz + delta*0.1f;
+
+ /* bias was calculated as percentage, we scale it to prevent animation issues */
+ delta= (lar->clipend-lar->clipsta)/(lar->shb->clipend-lar->shb->d);
+ //printf("bias delta %f\n", delta);
+ lar->shb->bias= (int) (delta*(float)lar->shb->bias);
+ }
+}
+
+static void makeflatshadowbuf(Render *re, LampRen *lar, float *jitbuf)
+{
+ ShadBuf *shb= lar->shb;
+ int *rectz, samples;
+
+ /* zbuffering */
+ rectz= MEM_mapallocN(sizeof(int)*shb->size*shb->size, "makeshadbuf");
+
+ for (samples=0; samples<shb->totbuf; samples++) {
+ zbuffer_shadow(re, shb->persmat, lar, rectz, shb->size, jitbuf[2*samples], jitbuf[2*samples+1]);
+ /* create Z tiles (for compression): this system is 24 bits!!! */
+ compress_shadowbuf(shb, rectz, lar->mode & LA_SQUARE);
+
+ if (re->test_break(re->tbh))
+ break;
+ }
+
+ MEM_freeN(rectz);
+}
+
+static void makedeepshadowbuf(Render *re, LampRen *lar, float *jitbuf)
+{
+ ShadBuf *shb= lar->shb;
+ APixstr *apixbuf;
+ APixstrand *apixbufstrand= NULL;
+ ListBase apsmbase= {NULL, NULL};
+
+ /* zbuffering */
+ apixbuf= MEM_callocN(sizeof(APixstr)*shb->size*shb->size, "APixbuf");
+ if (re->totstrand)
+ apixbufstrand= MEM_callocN(sizeof(APixstrand)*shb->size*shb->size, "APixbufstrand");
+
+ zbuffer_abuf_shadow(re, lar, shb->persmat, apixbuf, apixbufstrand, &apsmbase, shb->size,
+ shb->totbuf, (float(*)[2])jitbuf);
+
+ /* create Z tiles (for compression): this system is 24 bits!!! */
+ compress_deepshadowbuf(re, shb, apixbuf, apixbufstrand);
+
+ MEM_freeN(apixbuf);
+ if (apixbufstrand)
+ MEM_freeN(apixbufstrand);
+ freepsA(&apsmbase);
+}
+
+void makeshadowbuf(Render *re, LampRen *lar)
+{
+ ShadBuf *shb= lar->shb;
+ float wsize, *jitbuf, twozero[2]= {0.0f, 0.0f}, angle, temp;
+
+ if (lar->bufflag & (LA_SHADBUF_AUTO_START|LA_SHADBUF_AUTO_END))
+ shadowbuf_autoclip(re, lar);
+
+ /* just to enforce identical behavior of all irregular buffers */
+ if (lar->buftype==LA_SHADBUF_IRREGULAR)
+ shb->size= 1024;
+
+ /* matrices and window: in winmat the transformation is being put,
+ * transforming from observer view to lamp view, including lamp window matrix */
+
+ angle= saacos(lar->spotsi);
+ temp = 0.5f * shb->size * cosf(angle) / sinf(angle);
+ shb->pixsize= (shb->d)/temp;
+ wsize= shb->pixsize*(shb->size/2.0f);
+
+ perspective_m4(shb->winmat, -wsize, wsize, -wsize, wsize, shb->d, shb->clipend);
+ mul_m4_m4m4(shb->persmat, shb->winmat, shb->viewmat);
+
+ if (ELEM(lar->buftype, LA_SHADBUF_REGULAR, LA_SHADBUF_HALFWAY, LA_SHADBUF_DEEP)) {
+ shb->totbuf= lar->buffers;
+
+ /* jitter, weights - not threadsafe! */
+ BLI_thread_lock(LOCK_CUSTOM1);
+ shb->jit= give_jitter_tab(get_render_shadow_samples(&re->r, shb->samp));
+ make_jitter_weight_tab(re, shb, lar->filtertype);
+ BLI_thread_unlock(LOCK_CUSTOM1);
+
+ if (shb->totbuf==4) jitbuf= give_jitter_tab(2);
+ else if (shb->totbuf==9) jitbuf= give_jitter_tab(3);
+ else jitbuf= twozero;
+
+ /* zbuffering */
+ if (lar->buftype == LA_SHADBUF_DEEP) {
+ makedeepshadowbuf(re, lar, jitbuf);
+ shb->totbuf= 1;
+ }
+ else
+ makeflatshadowbuf(re, lar, jitbuf);
+
+ /* printf("lampbuf %d\n", sizeoflampbuf(shb)); */
+ }
+}
+
+static void *do_shadow_thread(void *re_v)
+{
+ Render *re = (Render *)re_v;
+ LampRen *lar;
+
+ do {
+ BLI_thread_lock(LOCK_CUSTOM1);
+ for (lar=re->lampren.first; lar; lar=lar->next) {
+ if (lar->shb && !lar->thread_assigned) {
+ lar->thread_assigned= 1;
+ break;
+ }
+ }
+ BLI_thread_unlock(LOCK_CUSTOM1);
+
+ /* if type is irregular, this only sets the perspective matrix and autoclips */
+ if (lar) {
+ makeshadowbuf(re, lar);
+ BLI_thread_lock(LOCK_CUSTOM1);
+ lar->thread_ready= 1;
+ BLI_thread_unlock(LOCK_CUSTOM1);
+ }
+ } while (lar && !re->test_break(re->tbh));
+
+ return NULL;
+}
+
+static volatile int g_break= 0;
+static int thread_break(void *UNUSED(arg))
+{
+ return g_break;
+}
+
+void threaded_makeshadowbufs(Render *re)
+{
+ ListBase threads;
+ LampRen *lar;
+ int a, totthread= 0;
+ int (*test_break)(void *);
+
+ /* count number of threads to use */
+ if (G.is_rendering) {
+ for (lar=re->lampren.first; lar; lar= lar->next)
+ if (lar->shb)
+ totthread++;
+
+ totthread = min_ii(totthread, re->r.threads);
+ }
+ else
+ totthread = 1; /* preview render */
+
+ if (totthread <= 1) {
+ for (lar=re->lampren.first; lar; lar= lar->next) {
+ if (re->test_break(re->tbh)) break;
+ if (lar->shb) {
+ /* if type is irregular, this only sets the perspective matrix and autoclips */
+ makeshadowbuf(re, lar);
+ }
+ }
+ }
+ else {
+ /* swap test break function */
+ test_break= re->test_break;
+ re->test_break= thread_break;
+
+ for (lar=re->lampren.first; lar; lar= lar->next) {
+ lar->thread_assigned= 0;
+ lar->thread_ready= 0;
+ }
+
+ BLI_threadpool_init(&threads, do_shadow_thread, totthread);
+
+ for (a=0; a<totthread; a++)
+ BLI_threadpool_insert(&threads, re);
+
+ /* keep rendering as long as there are shadow buffers not ready */
+ do {
+ if ((g_break=test_break(re->tbh)))
+ break;
+
+ PIL_sleep_ms(50);
+
+ BLI_thread_lock(LOCK_CUSTOM1);
+ for (lar=re->lampren.first; lar; lar= lar->next)
+ if (lar->shb && !lar->thread_ready)
+ break;
+ BLI_thread_unlock(LOCK_CUSTOM1);
+ } while (lar);
+
+ BLI_threadpool_end(&threads);
+
+ /* unset threadsafety */
+ re->test_break= test_break;
+ g_break= 0;
+ }
+}
+
+void freeshadowbuf(LampRen *lar)
+{
+ if (lar->shb) {
+ ShadBuf *shb= lar->shb;
+ ShadSampleBuf *shsample;
+ int b, v;
+
+ for (shsample= shb->buffers.first; shsample; shsample= shsample->next) {
+ if (shsample->deepbuf) {
+ v= shb->size*shb->size;
+ for (b=0; b<v; b++)
+ if (shsample->deepbuf[b])
+ MEM_freeN(shsample->deepbuf[b]);
+
+ MEM_freeN(shsample->deepbuf);
+ MEM_freeN(shsample->totbuf);
+ }
+ else {
+ intptr_t *ztile= shsample->zbuf;
+ const char *ctile= shsample->cbuf;
+
+ v= (shb->size*shb->size)/256;
+ for (b=0; b<v; b++, ztile++, ctile++)
+ if (*ctile) MEM_freeN((void *) *ztile);
+
+ MEM_freeN(shsample->zbuf);
+ MEM_freeN(shsample->cbuf);
+ }
+ }
+ BLI_freelistN(&shb->buffers);
+
+ if (shb->weight) MEM_freeN(shb->weight);
+ MEM_freeN(lar->shb);
+
+ lar->shb= NULL;
+ }
+}
+
+
+static int firstreadshadbuf(ShadBuf *shb, ShadSampleBuf *shsample, int **rz, int xs, int ys, int nr)
+{
+ /* return a 1 if fully compressed shadbuf-tile && z==const */
+ int ofs;
+ const char *ct;
+
+ if (shsample->deepbuf)
+ return 0;
+
+ /* always test borders of shadowbuffer */
+ if (xs<0) xs= 0; else if (xs>=shb->size) xs= shb->size-1;
+ if (ys<0) ys= 0; else if (ys>=shb->size) ys= shb->size-1;
+
+ /* calc z */
+ ofs= (ys>>4)*(shb->size>>4) + (xs>>4);
+ ct= shsample->cbuf+ofs;
+ if (*ct==0) {
+ if (nr==0) {
+ *rz= *( (int **)(shsample->zbuf+ofs) );
+ return 1;
+ }
+ else if (*rz!= *( (int **)(shsample->zbuf+ofs) )) return 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static float readdeepvisibility(DeepSample *dsample, int tot, int z, int bias, float *biast)
+{
+ DeepSample *ds, *prevds;
+ float t;
+ int a;
+
+ /* tricky stuff here; we use ints which can overflow easily with bias values */
+
+ ds= dsample;
+ for (a=0; a<tot && (z-bias > ds->z); a++, ds++) {}
+
+ if (a == tot) {
+ if (biast)
+ *biast= 0.0f;
+ return (ds-1)->v; /* completely behind all samples */
+ }
+
+ /* check if this read needs bias blending */
+ if (biast) {
+ if (z > ds->z)
+ *biast= (float)(z - ds->z)/(float)bias;
+ else
+ *biast= 0.0f;
+ }
+
+ if (a == 0)
+ return 1.0f; /* completely in front of all samples */
+
+ /* converting to float early here because ds->z - prevds->z can overflow */
+ prevds= ds-1;
+ t= ((float)(z-bias) - (float)prevds->z)/((float)ds->z - (float)prevds->z);
+ return t*ds->v + (1.0f-t)*prevds->v;
+}
+
+static float readdeepshadowbuf(ShadBuf *shb, ShadSampleBuf *shsample, int bias, int xs, int ys, int zs)
+{
+ float v, biasv, biast;
+ int ofs, tot;
+
+ if (zs < - 0x7FFFFE00 + bias)
+ return 1.0; /* extreme close to clipstart */
+
+ /* calc z */
+ ofs= ys*shb->size + xs;
+ tot= shsample->totbuf[ofs];
+ if (tot == 0)
+ return 1.0f;
+
+ v= readdeepvisibility(shsample->deepbuf[ofs], tot, zs, bias, &biast);
+
+ if (biast != 0.0f) {
+ /* in soft bias area */
+ biasv = readdeepvisibility(shsample->deepbuf[ofs], tot, zs, 0, NULL);
+
+ biast= biast*biast;
+ return (1.0f-biast)*v + biast*biasv;
+ }
+
+ return v;
+}
+
+/* return 1.0 : fully in light */
+static float readshadowbuf(ShadBuf *shb, ShadSampleBuf *shsample, int bias, int xs, int ys, int zs)
+{
+ float temp;
+ int *rz, ofs;
+ int zsamp=0;
+ char *ct, *cz;
+
+ /* simpleclip */
+ /* if (xs<0 || ys<0) return 1.0; */
+ /* if (xs>=shb->size || ys>=shb->size) return 1.0; */
+
+ /* always test borders of shadowbuffer */
+ if (xs<0) xs= 0; else if (xs>=shb->size) xs= shb->size-1;
+ if (ys<0) ys= 0; else if (ys>=shb->size) ys= shb->size-1;
+
+ if (shsample->deepbuf)
+ return readdeepshadowbuf(shb, shsample, bias, xs, ys, zs);
+
+ /* calc z */
+ ofs= (ys>>4)*(shb->size>>4) + (xs>>4);
+ ct= shsample->cbuf+ofs;
+ rz= *( (int **)(shsample->zbuf+ofs) );
+
+ if (*ct==3) {
+ ct= ((char *)rz)+3*16*(ys & 15)+3*(xs & 15);
+ cz= (char *)&zsamp;
+ cz[ACOMP]= ct[0];
+ cz[BCOMP]= ct[1];
+ cz[GCOMP]= ct[2];
+ }
+ else if (*ct==2) {
+ ct= ((char *)rz);
+ ct+= 4+2*16*(ys & 15)+2*(xs & 15);
+ zsamp= *rz;
+
+ cz= (char *)&zsamp;
+ cz[BCOMP]= ct[0];
+ cz[GCOMP]= ct[1];
+ }
+ else if (*ct==1) {
+ ct= ((char *)rz);
+ ct+= 4+16*(ys & 15)+(xs & 15);
+ zsamp= *rz;
+
+ cz= (char *)&zsamp;
+ cz[GCOMP]= ct[0];
+
+ }
+ else {
+ /* got warning on this for 64 bits.... */
+ /* but it's working code! in this case rz is not a pointer but zvalue (ton) */
+ zsamp= GET_INT_FROM_POINTER(rz);
+ }
+
+ /* tricky stuff here; we use ints which can overflow easily with bias values */
+
+ if (zsamp > zs) return 1.0; /* absolute no shadow */
+ else if (zs < - 0x7FFFFE00 + bias) return 1.0; /* extreme close to clipstart */
+ else if (zsamp < zs-bias) return 0.0; /* absolute in shadow */
+ else { /* soft area */
+
+ temp= ( (float)(zs- zsamp) )/(float)bias;
+ return 1.0f - temp*temp;
+
+ }
+}
+
+static void shadowbuf_project_co(float *x, float *y, float *z, ShadBuf *shb, const float co[3])
+{
+ float hco[4], size= 0.5f*(float)shb->size;
+
+ copy_v3_v3(hco, co);
+ hco[3]= 1.0f;
+
+ mul_m4_v4(shb->persmat, hco);
+
+ *x= size*(1.0f+hco[0]/hco[3]);
+ *y= size*(1.0f+hco[1]/hco[3]);
+ if (z) *z= (hco[2]/hco[3]);
+}
+
+/* the externally called shadow testing (reading) function */
+/* return 1.0: no shadow at all */
+float testshadowbuf(Render *re, ShadBuf *shb, const float co[3], const float dxco[3], const float dyco[3], float inp, float mat_bias)
+{
+ ShadSampleBuf *shsample;
+ float fac, dco[3], dx[3], dy[3], shadfac=0.0f;
+ float xs1, ys1, zs1, *jit, *weight, xres, yres, biasf;
+ int xs, ys, zs, bias, *rz;
+ short a, num;
+
+ /* crash preventer */
+ if (shb->buffers.first==NULL)
+ return 1.0f;
+
+ /* when facing away, assume fully in shadow */
+ if (inp <= 0.0f)
+ return 0.0f;
+
+ /* project coordinate to pixel space */
+ shadowbuf_project_co(&xs1, &ys1, &zs1, shb, co);
+
+ /* clip z coordinate, z is projected so that (-1.0, 1.0) matches
+ * (clipstart, clipend), so we can do this simple test */
+ if (zs1>=1.0f)
+ return 0.0f;
+ else if (zs1<= -1.0f)
+ return 1.0f;
+
+ zs= ((float)0x7FFFFFFF)*zs1;
+
+ /* take num*num samples, increase area with fac */
+ num= get_render_shadow_samples(&re->r, shb->samp);
+ num= num*num;
+ fac= shb->soft;
+
+ /* compute z bias */
+ if (mat_bias!=0.0f) biasf= shb->bias*mat_bias;
+ else biasf= shb->bias;
+ /* with inp==1.0, bias is half the size. correction value was 1.1, giving errors
+ * on cube edges, with one side being almost frontal lighted (ton) */
+ bias= (1.5f-inp*inp)*biasf;
+
+ /* in case of no filtering we can do things simpler */
+ if (num==1) {
+ for (shsample= shb->buffers.first; shsample; shsample= shsample->next)
+ shadfac += readshadowbuf(shb, shsample, bias, (int)xs1, (int)ys1, zs);
+
+ return shadfac/(float)shb->totbuf;
+ }
+
+ /* calculate filter size */
+ add_v3_v3v3(dco, co, dxco);
+ shadowbuf_project_co(&dx[0], &dx[1], NULL, shb, dco);
+ dx[0]= xs1 - dx[0];
+ dx[1]= ys1 - dx[1];
+
+ add_v3_v3v3(dco, co, dyco);
+ shadowbuf_project_co(&dy[0], &dy[1], NULL, shb, dco);
+ dy[0]= xs1 - dy[0];
+ dy[1]= ys1 - dy[1];
+
+ xres = fac * (fabsf(dx[0]) + fabsf(dy[0]));
+ yres = fac * (fabsf(dx[1]) + fabsf(dy[1]));
+ if (xres<1.0f) xres= 1.0f;
+ if (yres<1.0f) yres= 1.0f;
+
+ /* make xs1/xs1 corner of sample area */
+ xs1 -= xres*0.5f;
+ ys1 -= yres*0.5f;
+
+ /* in case we have a constant value in a tile, we can do quicker lookup */
+ if (xres<16.0f && yres<16.0f) {
+ shsample= shb->buffers.first;
+ if (firstreadshadbuf(shb, shsample, &rz, (int)xs1, (int)ys1, 0)) {
+ if (firstreadshadbuf(shb, shsample, &rz, (int)(xs1+xres), (int)ys1, 1)) {
+ if (firstreadshadbuf(shb, shsample, &rz, (int)xs1, (int)(ys1+yres), 1)) {
+ if (firstreadshadbuf(shb, shsample, &rz, (int)(xs1+xres), (int)(ys1+yres), 1)) {
+ return readshadowbuf(shb, shsample, bias, (int)xs1, (int)ys1, zs);
+ }
+ }
+ }
+ }
+ }
+
+ /* full jittered shadow buffer lookup */
+ for (shsample= shb->buffers.first; shsample; shsample= shsample->next) {
+ jit= shb->jit;
+ weight= shb->weight;
+
+ for (a=num; a>0; a--, jit+=2, weight++) {
+ /* instead of jit i tried random: ugly! */
+ /* note: the plus 0.5 gives best sampling results, jit goes from -0.5 to 0.5 */
+ /* xs1 and ys1 are already corrected to be corner of sample area */
+ xs= xs1 + xres*(jit[0] + 0.5f);
+ ys= ys1 + yres*(jit[1] + 0.5f);
+
+ shadfac+= *weight * readshadowbuf(shb, shsample, bias, xs, ys, zs);
+ }
+ }
+
+ /* Renormalizes for the sample number: */
+ return shadfac/(float)shb->totbuf;
+}
+
+/* different function... sampling behind clipend can be LIGHT, bias is negative! */
+/* return: light */
+static float readshadowbuf_halo(ShadBuf *shb, ShadSampleBuf *shsample, int xs, int ys, int zs)
+{
+ float temp;
+ int *rz, ofs;
+ int bias, zbias, zsamp;
+ char *ct, *cz;
+
+ /* negative! The other side is more important */
+ bias= -shb->bias;
+
+ /* simpleclip */
+ if (xs<0 || ys<0) return 0.0;
+ if (xs>=shb->size || ys>=shb->size) return 0.0;
+
+ /* calc z */
+ ofs= (ys>>4)*(shb->size>>4) + (xs>>4);
+ ct= shsample->cbuf+ofs;
+ rz= *( (int **)(shsample->zbuf+ofs) );
+
+ if (*ct==3) {
+ ct= ((char *)rz)+3*16*(ys & 15)+3*(xs & 15);
+ cz= (char *)&zsamp;
+ zsamp= 0;
+ cz[ACOMP]= ct[0];
+ cz[BCOMP]= ct[1];
+ cz[GCOMP]= ct[2];
+ }
+ else if (*ct==2) {
+ ct= ((char *)rz);
+ ct+= 4+2*16*(ys & 15)+2*(xs & 15);
+ zsamp= *rz;
+
+ cz= (char *)&zsamp;
+ cz[BCOMP]= ct[0];
+ cz[GCOMP]= ct[1];
+ }
+ else if (*ct==1) {
+ ct= ((char *)rz);
+ ct+= 4+16*(ys & 15)+(xs & 15);
+ zsamp= *rz;
+
+ cz= (char *)&zsamp;
+ cz[GCOMP]= ct[0];
+
+ }
+ else {
+ /* same as before */
+ /* still working code! (ton) */
+ zsamp= GET_INT_FROM_POINTER(rz);
+ }
+
+ /* NO schadow when sampled at 'eternal' distance */
+
+ if (zsamp >= 0x7FFFFE00) return 1.0;
+
+ if (zsamp > zs) return 1.0; /* absolute no shadww */
+ else {
+ /* bias is negative, so the (zs-bias) can be beyond 0x7fffffff */
+ zbias= 0x7fffffff - zs;
+ if (zbias > -bias) {
+ if ( zsamp < zs-bias) return 0.0; /* absolute in shadow */
+ }
+ else return 0.0; /* absolute shadow */
+ }
+
+ /* soft area */
+
+ temp= ( (float)(zs- zsamp) )/(float)bias;
+ return 1.0f - temp*temp;
+}
+
+
+float shadow_halo(LampRen *lar, const float p1[3], const float p2[3])
+{
+ /* p1 p2 already are rotated in spot-space */
+ ShadBuf *shb= lar->shb;
+ ShadSampleBuf *shsample;
+ float co[4], siz;
+ float lambda, lambda_o, lambda_x, lambda_y, ldx, ldy;
+ float zf, xf1, yf1, zf1, xf2, yf2, zf2;
+ float count, lightcount;
+ int x, y, z, xs1, ys1;
+ int dx = 0, dy = 0;
+
+ siz= 0.5f*(float)shb->size;
+
+ co[0]= p1[0];
+ co[1]= p1[1];
+ co[2]= p1[2]/lar->sh_zfac;
+ co[3]= 1.0;
+ mul_m4_v4(shb->winmat, co); /* rational hom co */
+ xf1= siz*(1.0f+co[0]/co[3]);
+ yf1= siz*(1.0f+co[1]/co[3]);
+ zf1= (co[2]/co[3]);
+
+
+ co[0]= p2[0];
+ co[1]= p2[1];
+ co[2]= p2[2]/lar->sh_zfac;
+ co[3]= 1.0;
+ mul_m4_v4(shb->winmat, co); /* rational hom co */
+ xf2= siz*(1.0f+co[0]/co[3]);
+ yf2= siz*(1.0f+co[1]/co[3]);
+ zf2= (co[2]/co[3]);
+
+ /* the 2dda (a pixel line formula) */
+
+ xs1= (int)xf1;
+ ys1= (int)yf1;
+
+ if (xf1 != xf2) {
+ if (xf2-xf1 > 0.0f) {
+ lambda_x= (xf1-xs1-1.0f)/(xf1-xf2);
+ ldx= -shb->shadhalostep/(xf1-xf2);
+ dx= shb->shadhalostep;
+ }
+ else {
+ lambda_x= (xf1-xs1)/(xf1-xf2);
+ ldx= shb->shadhalostep/(xf1-xf2);
+ dx= -shb->shadhalostep;
+ }
+ }
+ else {
+ lambda_x= 1.0;
+ ldx= 0.0;
+ }
+
+ if (yf1 != yf2) {
+ if (yf2-yf1 > 0.0f) {
+ lambda_y= (yf1-ys1-1.0f)/(yf1-yf2);
+ ldy= -shb->shadhalostep/(yf1-yf2);
+ dy= shb->shadhalostep;
+ }
+ else {
+ lambda_y= (yf1-ys1)/(yf1-yf2);
+ ldy= shb->shadhalostep/(yf1-yf2);
+ dy= -shb->shadhalostep;
+ }
+ }
+ else {
+ lambda_y= 1.0;
+ ldy= 0.0;
+ }
+
+ x= xs1;
+ y= ys1;
+ lambda= count= lightcount= 0.0;
+
+/* printf("start %x %x \n", (int)(0x7FFFFFFF*zf1), (int)(0x7FFFFFFF*zf2)); */
+
+ do {
+ lambda_o= lambda;
+
+ if (lambda_x==lambda_y) {
+ lambda_x+= ldx;
+ x+= dx;
+ lambda_y+= ldy;
+ y+= dy;
+ }
+ else {
+ if (lambda_x<lambda_y) {
+ lambda_x+= ldx;
+ x+= dx;
+ }
+ else {
+ lambda_y+= ldy;
+ y+= dy;
+ }
+ }
+
+ lambda = min_ff(lambda_x, lambda_y);
+
+ /* not making any progress? */
+ if (lambda==lambda_o) break;
+
+ /* clip to end of volume */
+ lambda = min_ff(lambda, 1.0f);
+
+ zf= zf1 + lambda*(zf2-zf1);
+ count+= (float)shb->totbuf;
+
+ if (zf<= -1.0f) lightcount += 1.0f; /* close to the spot */
+ else {
+
+ /* make sure, behind the clipend we extend halolines. */
+ if (zf>=1.0f) z= 0x7FFFF000;
+ else z= (int)(0x7FFFF000*zf);
+
+ for (shsample= shb->buffers.first; shsample; shsample= shsample->next)
+ lightcount+= readshadowbuf_halo(shb, shsample, x, y, z);
+
+ }
+ }
+ while (lambda < 1.0f);
+
+ if (count!=0.0f) return (lightcount/count);
+ return 0.0f;
+
+}
+
+
+/* ********************* Irregular Shadow Buffer (ISB) ************* */
+/* ********** storage of all view samples in a raster of lists ***** */
+
+/* based on several articles describing this method, like:
+ * The Irregular Z-Buffer and its Application to Shadow Mapping
+ * Gregory S. Johnson - William R. Mark - Christopher A. Burns
+ * and
+ * Alias-Free Shadow Maps
+ * Timo Aila and Samuli Laine
+ */
+
+/* bsp structure (actually kd tree) */
+
+#define BSPMAX_SAMPLE 128
+#define BSPMAX_DEPTH 32
+
+/* aligned with struct rctf */
+typedef struct Boxf {
+ float xmin, xmax;
+ float ymin, ymax;
+ float zmin, zmax;
+} Boxf;
+
+typedef struct ISBBranch {
+ struct ISBBranch *left, *right;
+ float divider[2];
+ Boxf box;
+ short totsamp, index, full, unused;
+ ISBSample **samples;
+} ISBBranch;
+
+typedef struct BSPFace {
+ Boxf box;
+ const float *v1, *v2, *v3, *v4;
+ int obi; /* object for face lookup */
+ int facenr; /* index to retrieve VlakRen */
+ int type; /* only for strand now */
+ short shad_alpha, is_full;
+
+ /* strand caching data, optimize for point_behind_strand() */
+ float radline, radline_end, len;
+ float vec1[3], vec2[3], rc[3];
+} BSPFace;
+
+/* boxes are in lamp projection */
+static void init_box(Boxf *box)
+{
+ box->xmin = 1000000.0f;
+ box->xmax = 0;
+ box->ymin = 1000000.0f;
+ box->ymax = 0;
+ box->zmin= 0x7FFFFFFF;
+ box->zmax= - 0x7FFFFFFF;
+}
+
+/* use v1 to calculate boundbox */
+static void bound_boxf(Boxf *box, const float v1[3])
+{
+ if (v1[0] < box->xmin) box->xmin = v1[0];
+ if (v1[0] > box->xmax) box->xmax = v1[0];
+ if (v1[1] < box->ymin) box->ymin = v1[1];
+ if (v1[1] > box->ymax) box->ymax = v1[1];
+ if (v1[2] < box->zmin) box->zmin= v1[2];
+ if (v1[2] > box->zmax) box->zmax= v1[2];
+}
+
+/* use v1 to calculate boundbox */
+static void bound_rectf(rctf *box, const float v1[2])
+{
+ if (v1[0] < box->xmin) box->xmin = v1[0];
+ if (v1[0] > box->xmax) box->xmax = v1[0];
+ if (v1[1] < box->ymin) box->ymin = v1[1];
+ if (v1[1] > box->ymax) box->ymax = v1[1];
+}
+
+
+/* halfway splitting, for initializing a more regular tree */
+static void isb_bsp_split_init(ISBBranch *root, MemArena *mem, int level)
+{
+
+ /* if level > 0 we create new branches and go deeper */
+ if (level > 0) {
+ ISBBranch *left, *right;
+ int i;
+
+ /* splitpoint */
+ root->divider[0]= 0.5f*(root->box.xmin+root->box.xmax);
+ root->divider[1]= 0.5f*(root->box.ymin+root->box.ymax);
+
+ /* find best splitpoint */
+ if (RCT_SIZE_X(&root->box) > RCT_SIZE_Y(&root->box))
+ i = root->index = 0;
+ else
+ i = root->index = 1;
+
+ left= root->left= BLI_memarena_alloc(mem, sizeof(ISBBranch));
+ right= root->right= BLI_memarena_alloc(mem, sizeof(ISBBranch));
+
+ /* box info */
+ left->box= root->box;
+ right->box= root->box;
+ if (i==0) {
+ left->box.xmax = root->divider[0];
+ right->box.xmin = root->divider[0];
+ }
+ else {
+ left->box.ymax = root->divider[1];
+ right->box.ymin = root->divider[1];
+ }
+ isb_bsp_split_init(left, mem, level-1);
+ isb_bsp_split_init(right, mem, level-1);
+ }
+ else {
+ /* we add sample array */
+ root->samples= BLI_memarena_alloc(mem, BSPMAX_SAMPLE*sizeof(void *));
+ }
+}
+
+/* note; if all samples on same location we just spread them over 2 new branches */
+static void isb_bsp_split(ISBBranch *root, MemArena *mem)
+{
+ ISBBranch *left, *right;
+ ISBSample *samples[BSPMAX_SAMPLE];
+ int a, i;
+
+ /* splitpoint */
+ root->divider[0]= root->divider[1]= 0.0f;
+ for (a=BSPMAX_SAMPLE-1; a>=0; a--) {
+ root->divider[0]+= root->samples[a]->zco[0];
+ root->divider[1]+= root->samples[a]->zco[1];
+ }
+ root->divider[0]/= BSPMAX_SAMPLE;
+ root->divider[1]/= BSPMAX_SAMPLE;
+
+ /* find best splitpoint */
+ if (RCT_SIZE_X(&root->box) > RCT_SIZE_Y(&root->box))
+ i = root->index = 0;
+ else
+ i = root->index = 1;
+
+ /* new branches */
+ left= root->left= BLI_memarena_alloc(mem, sizeof(ISBBranch));
+ right= root->right= BLI_memarena_alloc(mem, sizeof(ISBBranch));
+
+ /* new sample array */
+ left->samples = BLI_memarena_alloc(mem, BSPMAX_SAMPLE*sizeof(void *));
+ right->samples = samples; /* tmp */
+
+ /* split samples */
+ for (a=BSPMAX_SAMPLE-1; a>=0; a--) {
+ int comp= 0;
+ /* this prevents adding samples all to 1 branch when divider is equal to samples */
+ if (root->samples[a]->zco[i] == root->divider[i])
+ comp= a & 1;
+ else if (root->samples[a]->zco[i] < root->divider[i])
+ comp= 1;
+
+ if (comp==1) {
+ left->samples[left->totsamp]= root->samples[a];
+ left->totsamp++;
+ }
+ else {
+ right->samples[right->totsamp]= root->samples[a];
+ right->totsamp++;
+ }
+ }
+
+ /* copy samples from tmp */
+ memcpy(root->samples, samples, right->totsamp*(sizeof(void *)));
+ right->samples= root->samples;
+ root->samples= NULL;
+
+ /* box info */
+ left->box= root->box;
+ right->box= root->box;
+ if (i==0) {
+ left->box.xmax = root->divider[0];
+ right->box.xmin = root->divider[0];
+ }
+ else {
+ left->box.ymax = root->divider[1];
+ right->box.ymin = root->divider[1];
+ }
+}
+
+/* inserts sample in main tree, also splits on threshold */
+/* returns 1 if error */
+static int isb_bsp_insert(ISBBranch *root, MemArena *memarena, ISBSample *sample)
+{
+ ISBBranch *bspn= root;
+ const float *zco= sample->zco;
+ int i= 0;
+
+ /* debug counter, also used to check if something was filled in ever */
+ root->totsamp++;
+
+ /* going over branches until last one found */
+ while (bspn->left) {
+ if (zco[bspn->index] <= bspn->divider[bspn->index])
+ bspn= bspn->left;
+ else
+ bspn= bspn->right;
+ i++;
+ }
+ /* bspn now is the last branch */
+
+ if (bspn->totsamp==BSPMAX_SAMPLE) {
+ printf("error in bsp branch\n"); /* only for debug, cannot happen */
+ return 1;
+ }
+
+ /* insert */
+ bspn->samples[bspn->totsamp]= sample;
+ bspn->totsamp++;
+
+ /* split if allowed and needed */
+ if (bspn->totsamp==BSPMAX_SAMPLE) {
+ if (i==BSPMAX_DEPTH) {
+ bspn->totsamp--; /* stop filling in... will give errors */
+ return 1;
+ }
+ isb_bsp_split(bspn, memarena);
+ }
+ return 0;
+}
+
+/* initialize vars in face, for optimal point-in-face test */
+static void bspface_init_strand(BSPFace *face)
+{
+
+ face->radline= 0.5f* len_v2v2(face->v1, face->v2);
+
+ mid_v3_v3v3(face->vec1, face->v1, face->v2);
+ if (face->v4)
+ mid_v3_v3v3(face->vec2, face->v3, face->v4);
+ else
+ copy_v3_v3(face->vec2, face->v3);
+
+ face->rc[0]= face->vec2[0]-face->vec1[0];
+ face->rc[1]= face->vec2[1]-face->vec1[1];
+ face->rc[2]= face->vec2[2]-face->vec1[2];
+
+ face->len= face->rc[0]*face->rc[0]+ face->rc[1]*face->rc[1];
+
+ if (face->len != 0.0f) {
+ face->radline_end = face->radline / sqrtf(face->len);
+ face->len = 1.0f / face->len;
+ }
+}
+
+/* brought back to a simple 2d case */
+static int point_behind_strand(const float p[3], BSPFace *face)
+{
+ /* v1 - v2 is radius, v1 - v3 length */
+ float dist, rc[2], pt[2];
+
+ /* using code from dist_to_line_segment_v2(), distance vec to line-piece */
+
+ if (face->len==0.0f) {
+ rc[0]= p[0]-face->vec1[0];
+ rc[1]= p[1]-face->vec1[1];
+ dist = len_v2(rc);
+
+ if (dist < face->radline)
+ return 1;
+ }
+ else {
+ float lambda= ( face->rc[0]*(p[0]-face->vec1[0]) + face->rc[1]*(p[1]-face->vec1[1]) )*face->len;
+
+ if (lambda > -face->radline_end && lambda < 1.0f+face->radline_end) {
+ /* hesse for dist: */
+ //dist= (float)(fabs( (p[0]-vec2[0])*rc[1] + (p[1]-vec2[1])*rc[0])/len);
+
+ pt[0]= lambda*face->rc[0]+face->vec1[0];
+ pt[1]= lambda*face->rc[1]+face->vec1[1];
+
+ rc[0]= pt[0]-p[0];
+ rc[1]= pt[1]-p[1];
+ dist = len_v2(rc);
+
+ if (dist < face->radline) {
+ float zval= face->vec1[2] + lambda*face->rc[2];
+ if (p[2] > zval)
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/* return 1 if inside. code derived from src/parametrizer.c */
+static int point_behind_tria2d(const float p[3], const float v1[3], const float v2[3], const float v3[3])
+{
+ float a[2], c[2], h[2], div;
+ float u, v;
+
+ a[0] = v2[0] - v1[0];
+ a[1] = v2[1] - v1[1];
+ c[0] = v3[0] - v1[0];
+ c[1] = v3[1] - v1[1];
+
+ div = a[0]*c[1] - a[1]*c[0];
+ if (div==0.0f)
+ return 0;
+
+ h[0] = p[0] - v1[0];
+ h[1] = p[1] - v1[1];
+
+ div = 1.0f/div;
+
+ u = (h[0]*c[1] - h[1]*c[0])*div;
+ if (u >= 0.0f) {
+ v = (a[0]*h[1] - a[1]*h[0])*div;
+ if (v >= 0.0f) {
+ if ( u + v <= 1.0f) {
+ /* inside, now check if point p is behind */
+ float z= (1.0f-u-v)*v1[2] + u*v2[2] + v*v3[2];
+ if (z <= p[2])
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+/* tested these calls, but it gives inaccuracy, 'side' cannot be found reliably using v3 */
+
+/* check if line v1-v2 has all rect points on other side of point v3 */
+static int rect_outside_line(rctf *rect, const float v1[3], const float v2[3], const float v3[3])
+{
+ float a, b, c;
+ int side;
+
+ /* line formula for v1-v2 */
+ a= v2[1]-v1[1];
+ b= v1[0]-v2[0];
+ c= -a*v1[0] - b*v1[1];
+ side= a*v3[0] + b*v3[1] + c < 0.0f;
+
+ /* the four quad points */
+ if ( side==(rect->xmin*a + rect->ymin*b + c >= 0.0f) )
+ if ( side==(rect->xmax*a + rect->ymin*b + c >= 0.0f) )
+ if ( side==(rect->xmax*a + rect->ymax*b + c >= 0.0f) )
+ if ( side==(rect->xmin*a + rect->ymax*b + c >= 0.0f) )
+ return 1;
+ return 0;
+}
+
+/* check if one of the triangle edges separates all rect points on 1 side */
+static int rect_isect_tria(rctf *rect, const float v1[3], const float v2[3], const float v3[3])
+{
+ if (rect_outside_line(rect, v1, v2, v3))
+ return 0;
+ if (rect_outside_line(rect, v2, v3, v1))
+ return 0;
+ if (rect_outside_line(rect, v3, v1, v2))
+ return 0;
+ return 1;
+}
+#endif
+
+/* if face overlaps a branch, it executes func. recursive */
+static void isb_bsp_face_inside(ISBBranch *bspn, BSPFace *face)
+{
+
+ /* are we descending? */
+ if (bspn->left) {
+ /* hrmf, the box struct cannot be addressed with index */
+ if (bspn->index==0) {
+ if (face->box.xmin <= bspn->divider[0])
+ isb_bsp_face_inside(bspn->left, face);
+ if (face->box.xmax > bspn->divider[0])
+ isb_bsp_face_inside(bspn->right, face);
+ }
+ else {
+ if (face->box.ymin <= bspn->divider[1])
+ isb_bsp_face_inside(bspn->left, face);
+ if (face->box.ymax > bspn->divider[1])
+ isb_bsp_face_inside(bspn->right, face);
+ }
+ }
+ else {
+ /* else: end branch reached */
+ int a;
+
+ if (bspn->totsamp==0) return;
+
+ /* check for nodes entirely in shadow, can be skipped */
+ if (bspn->totsamp==bspn->full)
+ return;
+
+ /* if bsp node is entirely in front of face, give up */
+ if (bspn->box.zmax < face->box.zmin)
+ return;
+
+ /* if face boundbox is outside of branch rect, give up */
+ if (0==BLI_rctf_isect((rctf *)&face->box, (rctf *)&bspn->box, NULL))
+ return;
+
+ /* test all points inside branch */
+ for (a=bspn->totsamp-1; a>=0; a--) {
+ ISBSample *samp= bspn->samples[a];
+
+ if ((samp->facenr!=face->facenr || samp->obi!=face->obi) && samp->shadfac) {
+ if (face->box.zmin < samp->zco[2]) {
+ if (BLI_rctf_isect_pt_v((rctf *)&face->box, samp->zco)) {
+ int inshadow= 0;
+
+ if (face->type) {
+ if (point_behind_strand(samp->zco, face))
+ inshadow= 1;
+ }
+ else if ( point_behind_tria2d(samp->zco, face->v1, face->v2, face->v3))
+ inshadow= 1;
+ else if (face->v4 && point_behind_tria2d(samp->zco, face->v1, face->v3, face->v4))
+ inshadow= 1;
+
+ if (inshadow) {
+ *(samp->shadfac) += face->shad_alpha;
+ /* optimize; is_full means shad_alpha==4096 */
+ if (*(samp->shadfac) >= 4096 || face->is_full) {
+ bspn->full++;
+ samp->shadfac= NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/* based on available samples, recalculate the bounding box for bsp nodes, recursive */
+static void isb_bsp_recalc_box(ISBBranch *root)
+{
+ if (root->left) {
+ isb_bsp_recalc_box(root->left);
+ isb_bsp_recalc_box(root->right);
+ }
+ else if (root->totsamp) {
+ int a;
+
+ init_box(&root->box);
+ for (a=root->totsamp-1; a>=0; a--)
+ bound_boxf(&root->box, root->samples[a]->zco);
+ }
+}
+
+/* callback function for zbuf clip */
+static void isb_bsp_test_strand(ZSpan *zspan, int obi, int zvlnr,
+ const float *v1, const float *v2, const float *v3, const float *v4)
+{
+ BSPFace face;
+
+ face.v1= v1;
+ face.v2= v2;
+ face.v3= v3;
+ face.v4= v4;
+ face.obi= obi;
+ face.facenr= zvlnr & ~RE_QUAD_OFFS;
+ face.type= R_STRAND;
+ if (R.osa)
+ face.shad_alpha= (short)ceil(4096.0f*zspan->shad_alpha/(float)R.osa);
+ else
+ face.shad_alpha= (short)ceil(4096.0f*zspan->shad_alpha);
+
+ face.is_full= (zspan->shad_alpha==1.0f);
+
+ /* setup boundbox */
+ init_box(&face.box);
+ bound_boxf(&face.box, v1);
+ bound_boxf(&face.box, v2);
+ bound_boxf(&face.box, v3);
+ if (v4)
+ bound_boxf(&face.box, v4);
+
+ /* optimize values */
+ bspface_init_strand(&face);
+
+ isb_bsp_face_inside((ISBBranch *)zspan->rectz, &face);
+
+}
+
+/* callback function for zbuf clip */
+static void isb_bsp_test_face(ZSpan *zspan, int obi, int zvlnr,
+ const float *v1, const float *v2, const float *v3, const float *v4)
+{
+ BSPFace face;
+
+ face.v1= v1;
+ face.v2= v2;
+ face.v3= v3;
+ face.v4= v4;
+ face.obi= obi;
+ face.facenr= zvlnr & ~RE_QUAD_OFFS;
+ face.type= 0;
+ if (R.osa)
+ face.shad_alpha= (short)ceil(4096.0f*zspan->shad_alpha/(float)R.osa);
+ else
+ face.shad_alpha= (short)ceil(4096.0f*zspan->shad_alpha);
+
+ face.is_full= (zspan->shad_alpha==1.0f);
+
+ /* setup boundbox */
+ init_box(&face.box);
+ bound_boxf(&face.box, v1);
+ bound_boxf(&face.box, v2);
+ bound_boxf(&face.box, v3);
+ if (v4)
+ bound_boxf(&face.box, v4);
+
+ isb_bsp_face_inside((ISBBranch *)zspan->rectz, &face);
+}
+
+static int testclip_minmax(const float ho[4], const float minmax[4])
+{
+ float wco= ho[3];
+ int flag= 0;
+
+ if ( ho[0] > minmax[1]*wco) flag = 1;
+ else if ( ho[0]< minmax[0]*wco) flag = 2;
+
+ if ( ho[1] > minmax[3]*wco) flag |= 4;
+ else if ( ho[1]< minmax[2]*wco) flag |= 8;
+
+ return flag;
+}
+
+/* main loop going over all faces and check in bsp overlaps, fill in shadfac values */
+static void isb_bsp_fillfaces(Render *re, LampRen *lar, ISBBranch *root)
+{
+ ObjectInstanceRen *obi;
+ ObjectRen *obr;
+ ShadBuf *shb= lar->shb;
+ ZSpan zspan, zspanstrand;
+ VlakRen *vlr= NULL;
+ Material *ma= NULL;
+ float minmaxf[4], winmat[4][4];
+ int size= shb->size;
+ int i, a, ok=1, lay= -1;
+
+ /* further optimize, also sets minz maxz */
+ isb_bsp_recalc_box(root);
+
+ /* extra clipping for minmax */
+ minmaxf[0]= (2.0f*root->box.xmin - size-2.0f)/size;
+ minmaxf[1]= (2.0f*root->box.xmax - size+2.0f)/size;
+ minmaxf[2]= (2.0f*root->box.ymin - size-2.0f)/size;
+ minmaxf[3]= (2.0f*root->box.ymax - size+2.0f)/size;
+
+ if (lar->mode & (LA_LAYER|LA_LAYER_SHADOW)) lay= lar->lay;
+
+ /* (ab)use zspan, since we use zbuffer clipping code */
+ zbuf_alloc_span(&zspan, size, size, re->clipcrop);
+
+ zspan.zmulx= ((float)size)/2.0f;
+ zspan.zmuly= ((float)size)/2.0f;
+ zspan.zofsx= -0.5f;
+ zspan.zofsy= -0.5f;
+
+ /* pass on bsp root to zspan */
+ zspan.rectz= (int *)root;
+
+ /* filling methods */
+ zspanstrand= zspan;
+ // zspan.zbuflinefunc= zbufline_onlyZ;
+ zspan.zbuffunc= isb_bsp_test_face;
+ zspanstrand.zbuffunc= isb_bsp_test_strand;
+
+ for (i=0, obi=re->instancetable.first; obi; i++, obi=obi->next) {
+ obr= obi->obr;
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(winmat, shb->persmat, obi->mat);
+ else
+ copy_m4_m4(winmat, shb->persmat);
+
+ for (a=0; a<obr->totvlak; a++) {
+
+ if ((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak;
+ else vlr++;
+
+ /* note, these conditions are copied in shadowbuf_autoclip() */
+ if (vlr->mat!= ma) {
+ ma= vlr->mat;
+ ok= 1;
+ if ((ma->mode2 & MA_CASTSHADOW)==0 || (ma->mode & MA_SHADBUF)==0) ok= 0;
+ if (ma->material_type == MA_TYPE_WIRE) ok= 0;
+ zspanstrand.shad_alpha= zspan.shad_alpha= ma->shad_alpha;
+ }
+
+ if (ok && (obi->lay & lay)) {
+ float hoco[4][4];
+ int c1, c2, c3, c4=0;
+ int d1, d2, d3, d4=0;
+ int partclip;
+
+ /* create hocos per face, it is while render */
+ projectvert(vlr->v1->co, winmat, hoco[0]); d1= testclip_minmax(hoco[0], minmaxf);
+ projectvert(vlr->v2->co, winmat, hoco[1]); d2= testclip_minmax(hoco[1], minmaxf);
+ projectvert(vlr->v3->co, winmat, hoco[2]); d3= testclip_minmax(hoco[2], minmaxf);
+ if (vlr->v4) {
+ projectvert(vlr->v4->co, winmat, hoco[3]); d4= testclip_minmax(hoco[3], minmaxf);
+ }
+
+ /* minmax clipping */
+ if (vlr->v4) partclip= d1 & d2 & d3 & d4;
+ else partclip= d1 & d2 & d3;
+
+ if (partclip==0) {
+
+ /* window clipping */
+ c1= testclip(hoco[0]);
+ c2= testclip(hoco[1]);
+ c3= testclip(hoco[2]);
+ if (vlr->v4)
+ c4= testclip(hoco[3]);
+
+ /* ***** NO WIRE YET */
+ if (ma->material_type == MA_TYPE_WIRE) {
+ if (vlr->v4)
+ zbufclipwire(&zspan, i, a+1, vlr->ec, hoco[0], hoco[1], hoco[2], hoco[3], c1, c2, c3, c4);
+ else
+ zbufclipwire(&zspan, i, a+1, vlr->ec, hoco[0], hoco[1], hoco[2], NULL, c1, c2, c3, 0);
+ }
+ else if (vlr->v4) {
+ if (vlr->flag & R_STRAND)
+ zbufclip4(&zspanstrand, i, a+1, hoco[0], hoco[1], hoco[2], hoco[3], c1, c2, c3, c4);
+ else
+ zbufclip4(&zspan, i, a+1, hoco[0], hoco[1], hoco[2], hoco[3], c1, c2, c3, c4);
+ }
+ else
+ zbufclip(&zspan, i, a+1, hoco[0], hoco[1], hoco[2], c1, c2, c3);
+
+ }
+ }
+ }
+ }
+
+ zbuf_free_span(&zspan);
+}
+
+/* returns 1 when the viewpixel is visible in lampbuffer */
+static int viewpixel_to_lampbuf(ShadBuf *shb, ObjectInstanceRen *obi, VlakRen *vlr, float x, float y, float co_r[3])
+{
+ float hoco[4], v1[3], nor[3];
+ float dface, fac, siz;
+
+ RE_vlakren_get_normal(&R, obi, vlr, nor);
+ copy_v3_v3(v1, vlr->v1->co);
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_v3(obi->mat, v1);
+
+ /* from shadepixel() */
+ dface = dot_v3v3(v1, nor);
+ hoco[3]= 1.0f;
+
+ /* ortho viewplane cannot intersect using view vector originating in (0, 0, 0) */
+ if (R.r.mode & R_ORTHO) {
+ /* x and y 3d coordinate can be derived from pixel coord and winmat */
+ float fx= 2.0f/(R.winx*R.winmat[0][0]);
+ float fy= 2.0f/(R.winy*R.winmat[1][1]);
+
+ hoco[0]= (x - 0.5f*R.winx)*fx - R.winmat[3][0]/R.winmat[0][0];
+ hoco[1]= (y - 0.5f*R.winy)*fy - R.winmat[3][1]/R.winmat[1][1];
+
+ /* using a*x + b*y + c*z = d equation, (a b c) is normal */
+ if (nor[2]!=0.0f)
+ hoco[2]= (dface - nor[0]*hoco[0] - nor[1]*hoco[1])/nor[2];
+ else
+ hoco[2]= 0.0f;
+ }
+ else {
+ float div, view[3];
+
+ calc_view_vector(view, x, y);
+
+ div = dot_v3v3(nor, view);
+ if (div==0.0f)
+ return 0;
+
+ fac= dface/div;
+
+ hoco[0]= fac*view[0];
+ hoco[1]= fac*view[1];
+ hoco[2]= fac*view[2];
+ }
+
+ /* move 3d vector to lampbuf */
+ mul_m4_v4(shb->persmat, hoco); /* rational hom co */
+
+ /* clip We can test for -1.0/1.0 because of the properties of the
+ * coordinate transformations. */
+ fac = fabsf(hoco[3]);
+ if (hoco[0]<-fac || hoco[0]>fac)
+ return 0;
+ if (hoco[1]<-fac || hoco[1]>fac)
+ return 0;
+ if (hoco[2]<-fac || hoco[2]>fac)
+ return 0;
+
+ siz= 0.5f*(float)shb->size;
+ co_r[0]= siz*(1.0f+hoco[0]/hoco[3]) -0.5f;
+ co_r[1]= siz*(1.0f+hoco[1]/hoco[3]) -0.5f;
+ co_r[2]= ((float)0x7FFFFFFF)*(hoco[2]/hoco[3]);
+
+ /* XXXX bias, much less than normal shadbuf, or do we need a constant? */
+ co_r[2] -= 0.05f*shb->bias;
+
+ return 1;
+}
+
+/* storage of shadow results, solid osa and transp case */
+static void isb_add_shadfac(ISBShadfacA **isbsapp, MemArena *mem, int obi, int facenr, short shadfac, short samples)
+{
+ ISBShadfacA *new;
+ float shadfacf;
+
+ /* in osa case, the samples were filled in with factor 1.0/R.osa. if fewer samples we have to correct */
+ if (R.osa)
+ shadfacf= ((float)shadfac*R.osa)/(4096.0f*samples);
+ else
+ shadfacf= ((float)shadfac)/(4096.0f);
+
+ new= BLI_memarena_alloc(mem, sizeof(ISBShadfacA));
+ new->obi= obi;
+ new->facenr= facenr & ~RE_QUAD_OFFS;
+ new->shadfac= shadfacf;
+ if (*isbsapp)
+ new->next= (*isbsapp);
+ else
+ new->next= NULL;
+
+ *isbsapp= new;
+}
+
+/* adding samples, solid case */
+static int isb_add_samples(RenderPart *pa, ISBBranch *root, MemArena *memarena, ISBSample **samplebuf)
+{
+ int xi, yi, *xcos, *ycos;
+ int sample, bsp_err= 0;
+
+ /* bsp split doesn't like to handle regular sequences */
+ xcos= MEM_mallocN(pa->rectx*sizeof(int), "xcos");
+ ycos= MEM_mallocN(pa->recty*sizeof(int), "ycos");
+ for (xi=0; xi<pa->rectx; xi++)
+ xcos[xi]= xi;
+ for (yi=0; yi<pa->recty; yi++)
+ ycos[yi]= yi;
+ BLI_array_randomize(xcos, sizeof(int), pa->rectx, 12345);
+ BLI_array_randomize(ycos, sizeof(int), pa->recty, 54321);
+
+ for (sample=0; sample<(R.osa?R.osa:1); sample++) {
+ ISBSample *samp= samplebuf[sample], *samp1;
+
+ for (yi=0; yi<pa->recty; yi++) {
+ int y= ycos[yi];
+ for (xi=0; xi<pa->rectx; xi++) {
+ int x= xcos[xi];
+ samp1= samp + y*pa->rectx + x;
+ if (samp1->facenr)
+ bsp_err |= isb_bsp_insert(root, memarena, samp1);
+ }
+ if (bsp_err) break;
+ }
+ }
+
+ MEM_freeN(xcos);
+ MEM_freeN(ycos);
+
+ return bsp_err;
+}
+
+/* solid version */
+/* lar->shb, pa->rectz and pa->rectp should exist */
+static void isb_make_buffer(RenderPart *pa, LampRen *lar)
+{
+ ShadBuf *shb= lar->shb;
+ ISBData *isbdata;
+ ISBSample *samp, *samplebuf[16]; /* should be RE_MAX_OSA */
+ ISBBranch root;
+ MemArena *memarena;
+ intptr_t *rd;
+ int *recto, *rectp, x, y, sindex, sample, bsp_err=0;
+
+ /* storage for shadow, per thread */
+ isbdata= shb->isb_result[pa->thread];
+
+ /* to map the shi->xs and ys coordinate */
+ isbdata->minx= pa->disprect.xmin;
+ isbdata->miny= pa->disprect.ymin;
+ isbdata->rectx= pa->rectx;
+ isbdata->recty= pa->recty;
+
+ /* branches are added using memarena (32k branches) */
+ memarena = BLI_memarena_new(0x8000 * sizeof(ISBBranch), "isb arena");
+ BLI_memarena_use_calloc(memarena);
+
+ /* samplebuf is in camera view space (pixels) */
+ for (sample=0; sample<(R.osa?R.osa:1); sample++)
+ samplebuf[sample]= MEM_callocN(sizeof(ISBSample)*pa->rectx*pa->recty, "isb samplebuf");
+
+ /* for end result, ISBSamples point to this in non OSA case, otherwise to pixstruct->shadfac */
+ if (R.osa==0)
+ isbdata->shadfacs= MEM_callocN(pa->rectx*pa->recty*sizeof(short), "isb shadfacs");
+
+ /* setup bsp root */
+ memset(&root, 0, sizeof(ISBBranch));
+ root.box.xmin = (float)shb->size;
+ root.box.ymin = (float)shb->size;
+
+ /* create the sample buffers */
+ for (sindex=0, y=0; y<pa->recty; y++) {
+ for (x=0; x<pa->rectx; x++, sindex++) {
+
+ /* this makes it a long function, but splitting it out would mean 10+ arguments */
+ /* first check OSA case */
+ if (R.osa) {
+ rd= pa->rectdaps + sindex;
+ if (*rd) {
+ float xs= (float)(x + pa->disprect.xmin);
+ float ys= (float)(y + pa->disprect.ymin);
+
+ for (sample=0; sample<R.osa; sample++) {
+ PixStr *ps= (PixStr *)(*rd);
+ int mask= (1<<sample);
+
+ while (ps) {
+ if (ps->mask & mask)
+ break;
+ ps= ps->next;
+ }
+ if (ps && ps->facenr>0) {
+ ObjectInstanceRen *obi= &R.objectinstance[ps->obi];
+ ObjectRen *obr= obi->obr;
+ VlakRen *vlr= RE_findOrAddVlak(obr, (ps->facenr-1) & RE_QUAD_MASK);
+
+ samp= samplebuf[sample] + sindex;
+ /* convert image plane pixel location to lamp buffer space */
+ if (viewpixel_to_lampbuf(shb, obi, vlr, xs + R.jit[sample][0], ys + R.jit[sample][1], samp->zco)) {
+ samp->obi= ps->obi;
+ samp->facenr= ps->facenr & ~RE_QUAD_OFFS;
+ ps->shadfac= 0;
+ samp->shadfac= &ps->shadfac;
+ bound_rectf((rctf *)&root.box, samp->zco);
+ }
+ }
+ }
+ }
+ }
+ else {
+ rectp= pa->rectp + sindex;
+ recto= pa->recto + sindex;
+ if (*rectp>0) {
+ ObjectInstanceRen *obi= &R.objectinstance[*recto];
+ ObjectRen *obr= obi->obr;
+ VlakRen *vlr= RE_findOrAddVlak(obr, (*rectp-1) & RE_QUAD_MASK);
+ float xs= (float)(x + pa->disprect.xmin);
+ float ys= (float)(y + pa->disprect.ymin);
+
+ samp= samplebuf[0] + sindex;
+ /* convert image plane pixel location to lamp buffer space */
+ if (viewpixel_to_lampbuf(shb, obi, vlr, xs, ys, samp->zco)) {
+ samp->obi= *recto;
+ samp->facenr= *rectp & ~RE_QUAD_OFFS;
+ samp->shadfac= isbdata->shadfacs + sindex;
+ bound_rectf((rctf *)&root.box, samp->zco);
+ }
+ }
+ }
+ }
+ }
+
+ /* simple method to see if we have samples */
+ if (root.box.xmin != (float)shb->size) {
+ /* now create a regular split, root.box has the initial bounding box of all pixels */
+ /* split bsp 8 levels deep, in regular grid (16 x 16) */
+ isb_bsp_split_init(&root, memarena, 8);
+
+ /* insert all samples in BSP now */
+ bsp_err= isb_add_samples(pa, &root, memarena, samplebuf);
+
+ if (bsp_err==0) {
+ /* go over all faces and fill in shadow values */
+
+ isb_bsp_fillfaces(&R, lar, &root); /* shb->persmat should have been calculated */
+
+ /* copy shadow samples to persistent buffer, reduce memory overhead */
+ if (R.osa) {
+ ISBShadfacA **isbsa= isbdata->shadfaca= MEM_callocN(pa->rectx*pa->recty*sizeof(void *), "isb shadfacs");
+
+ isbdata->memarena = BLI_memarena_new(0x8000 * sizeof(ISBSampleA), "isb arena");
+ BLI_memarena_use_calloc(isbdata->memarena);
+
+ for (rd= pa->rectdaps, x=pa->rectx*pa->recty; x>0; x--, rd++, isbsa++) {
+
+ if (*rd) {
+ PixStr *ps= (PixStr *)(*rd);
+ while (ps) {
+ if (ps->shadfac)
+ isb_add_shadfac(isbsa, isbdata->memarena, ps->obi, ps->facenr, ps->shadfac, count_mask(ps->mask));
+ ps= ps->next;
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (isbdata->shadfacs) {
+ MEM_freeN(isbdata->shadfacs);
+ isbdata->shadfacs= NULL;
+ }
+ }
+
+ /* free BSP */
+ BLI_memarena_free(memarena);
+
+ /* free samples */
+ for (x=0; x<(R.osa?R.osa:1); x++)
+ MEM_freeN(samplebuf[x]);
+
+ if (bsp_err) printf("error in filling bsp\n");
+}
+
+/* add sample to buffer, isbsa is the root sample in a buffer */
+static ISBSampleA *isb_alloc_sample_transp(ISBSampleA **isbsa, MemArena *mem)
+{
+ ISBSampleA *new;
+
+ new= BLI_memarena_alloc(mem, sizeof(ISBSampleA));
+ if (*isbsa)
+ new->next= (*isbsa);
+ else
+ new->next= NULL;
+
+ *isbsa= new;
+ return new;
+}
+
+/* adding samples in BSP, transparent case */
+static int isb_add_samples_transp(RenderPart *pa, ISBBranch *root, MemArena *memarena, ISBSampleA ***samplebuf)
+{
+ int xi, yi, *xcos, *ycos;
+ int sample, bsp_err= 0;
+
+ /* bsp split doesn't like to handle regular sequences */
+ xcos= MEM_mallocN(pa->rectx*sizeof(int), "xcos");
+ ycos= MEM_mallocN(pa->recty*sizeof(int), "ycos");
+ for (xi=0; xi<pa->rectx; xi++)
+ xcos[xi]= xi;
+ for (yi=0; yi<pa->recty; yi++)
+ ycos[yi]= yi;
+ BLI_array_randomize(xcos, sizeof(int), pa->rectx, 12345);
+ BLI_array_randomize(ycos, sizeof(int), pa->recty, 54321);
+
+ for (sample=0; sample<(R.osa?R.osa:1); sample++) {
+ ISBSampleA **samp= samplebuf[sample], *samp1;
+
+ for (yi=0; yi<pa->recty; yi++) {
+ int y= ycos[yi];
+ for (xi=0; xi<pa->rectx; xi++) {
+ int x= xcos[xi];
+
+ samp1= *(samp + y*pa->rectx + x);
+ while (samp1) {
+ bsp_err |= isb_bsp_insert(root, memarena, (ISBSample *)samp1);
+ samp1= samp1->next;
+ }
+ }
+ if (bsp_err) break;
+ }
+ }
+
+ MEM_freeN(xcos);
+ MEM_freeN(ycos);
+
+ return bsp_err;
+}
+
+
+/* Ztransp version */
+/* lar->shb, pa->rectz and pa->rectp should exist */
+static void isb_make_buffer_transp(RenderPart *pa, APixstr *apixbuf, LampRen *lar)
+{
+ ShadBuf *shb= lar->shb;
+ ISBData *isbdata;
+ ISBSampleA *samp, **samplebuf[16]; /* MAX_OSA */
+ ISBBranch root;
+ MemArena *memarena;
+ APixstr *ap;
+ int x, y, sindex, sample, bsp_err=0;
+
+ /* storage for shadow, per thread */
+ isbdata= shb->isb_result[pa->thread];
+
+ /* to map the shi->xs and ys coordinate */
+ isbdata->minx= pa->disprect.xmin;
+ isbdata->miny= pa->disprect.ymin;
+ isbdata->rectx= pa->rectx;
+ isbdata->recty= pa->recty;
+
+ /* branches are added using memarena (32k branches) */
+ memarena = BLI_memarena_new(0x8000 * sizeof(ISBBranch), "isb arena");
+ BLI_memarena_use_calloc(memarena);
+
+ /* samplebuf is in camera view space (pixels) */
+ for (sample=0; sample<(R.osa?R.osa:1); sample++)
+ samplebuf[sample]= MEM_callocN(sizeof(void *)*pa->rectx*pa->recty, "isb alpha samplebuf");
+
+ /* setup bsp root */
+ memset(&root, 0, sizeof(ISBBranch));
+ root.box.xmin = (float)shb->size;
+ root.box.ymin = (float)shb->size;
+
+ /* create the sample buffers */
+ for (ap= apixbuf, sindex=0, y=0; y<pa->recty; y++) {
+ for (x=0; x<pa->rectx; x++, sindex++, ap++) {
+
+ if (ap->p[0]) {
+ APixstr *apn;
+ float xs= (float)(x + pa->disprect.xmin);
+ float ys= (float)(y + pa->disprect.ymin);
+
+ for (apn=ap; apn; apn= apn->next) {
+ int a;
+ for (a=0; a<4; a++) {
+ if (apn->p[a]) {
+ ObjectInstanceRen *obi= &R.objectinstance[apn->obi[a]];
+ ObjectRen *obr= obi->obr;
+ VlakRen *vlr= RE_findOrAddVlak(obr, (apn->p[a]-1) & RE_QUAD_MASK);
+ float zco[3];
+
+ /* here we store shadfac, easier to create the end storage buffer. needs zero'ed, multiple shadowbufs use it */
+ apn->shadfac[a]= 0;
+
+ if (R.osa) {
+ for (sample=0; sample<R.osa; sample++) {
+ int mask= (1<<sample);
+
+ if (apn->mask[a] & mask) {
+
+ /* convert image plane pixel location to lamp buffer space */
+ if (viewpixel_to_lampbuf(shb, obi, vlr, xs + R.jit[sample][0], ys + R.jit[sample][1], zco)) {
+ samp= isb_alloc_sample_transp(samplebuf[sample] + sindex, memarena);
+ samp->obi= apn->obi[a];
+ samp->facenr= apn->p[a] & ~RE_QUAD_OFFS;
+ samp->shadfac= &apn->shadfac[a];
+
+ copy_v3_v3(samp->zco, zco);
+ bound_rectf((rctf *)&root.box, samp->zco);
+ }
+ }
+ }
+ }
+ else {
+
+ /* convert image plane pixel location to lamp buffer space */
+ if (viewpixel_to_lampbuf(shb, obi, vlr, xs, ys, zco)) {
+
+ samp= isb_alloc_sample_transp(samplebuf[0] + sindex, memarena);
+ samp->obi= apn->obi[a];
+ samp->facenr= apn->p[a] & ~RE_QUAD_OFFS;
+ samp->shadfac= &apn->shadfac[a];
+
+ copy_v3_v3(samp->zco, zco);
+ bound_rectf((rctf *)&root.box, samp->zco);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* simple method to see if we have samples */
+ if (root.box.xmin != (float)shb->size) {
+ /* now create a regular split, root.box has the initial bounding box of all pixels */
+ /* split bsp 8 levels deep, in regular grid (16 x 16) */
+ isb_bsp_split_init(&root, memarena, 8);
+
+ /* insert all samples in BSP now */
+ bsp_err= isb_add_samples_transp(pa, &root, memarena, samplebuf);
+
+ if (bsp_err==0) {
+ ISBShadfacA **isbsa;
+
+ /* go over all faces and fill in shadow values */
+ isb_bsp_fillfaces(&R, lar, &root); /* shb->persmat should have been calculated */
+
+ /* copy shadow samples to persistent buffer, reduce memory overhead */
+ isbsa= isbdata->shadfaca= MEM_callocN(pa->rectx*pa->recty*sizeof(void *), "isb shadfacs");
+
+ isbdata->memarena = BLI_memarena_new(0x8000 * sizeof(ISBSampleA), "isb arena");
+
+ for (ap= apixbuf, x=pa->rectx*pa->recty; x>0; x--, ap++, isbsa++) {
+
+ if (ap->p[0]) {
+ APixstr *apn;
+ for (apn=ap; apn; apn= apn->next) {
+ int a;
+ for (a=0; a<4; a++) {
+ if (apn->p[a] && apn->shadfac[a]) {
+ if (R.osa)
+ isb_add_shadfac(isbsa, isbdata->memarena, apn->obi[a], apn->p[a], apn->shadfac[a], count_mask(apn->mask[a]));
+ else
+ isb_add_shadfac(isbsa, isbdata->memarena, apn->obi[a], apn->p[a], apn->shadfac[a], 0);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* free BSP */
+ BLI_memarena_free(memarena);
+
+ /* free samples */
+ for (x=0; x<(R.osa?R.osa:1); x++)
+ MEM_freeN(samplebuf[x]);
+
+ if (bsp_err) printf("error in filling bsp\n");
+}
+
+
+
+/* exported */
+
+/* returns amount of light (1.0 = no shadow) */
+/* note, shadepixel() rounds the coordinate, not the real sample info */
+float ISB_getshadow(ShadeInput *shi, ShadBuf *shb)
+{
+ /* if raytracing, we can't accept irregular shadow */
+ if (shi->depth==0) {
+ ISBData *isbdata= shb->isb_result[shi->thread];
+
+ if (isbdata) {
+ if (isbdata->shadfacs || isbdata->shadfaca) {
+ int x= shi->xs - isbdata->minx;
+
+ if (x >= 0 && x < isbdata->rectx) {
+ int y= shi->ys - isbdata->miny;
+
+ if (y >= 0 && y < isbdata->recty) {
+ if (isbdata->shadfacs) {
+ const short *sp= isbdata->shadfacs + y*isbdata->rectx + x;
+ return *sp>=4096?0.0f:1.0f - ((float)*sp)/4096.0f;
+ }
+ else {
+ int sindex= y*isbdata->rectx + x;
+ int obi= shi->obi - R.objectinstance;
+ ISBShadfacA *isbsa= *(isbdata->shadfaca + sindex);
+
+ while (isbsa) {
+ if (isbsa->facenr==shi->facenr+1 && isbsa->obi==obi)
+ return isbsa->shadfac>=1.0f?0.0f:1.0f - isbsa->shadfac;
+ isbsa= isbsa->next;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return 1.0f;
+}
+
+/* part is supposed to be solid zbuffered (apixbuf==NULL) or transparent zbuffered */
+void ISB_create(RenderPart *pa, APixstr *apixbuf)
+{
+ GroupObject *go;
+
+ /* go over all lamps, and make the irregular buffers */
+ for (go=R.lights.first; go; go= go->next) {
+ LampRen *lar= go->lampren;
+
+ if (lar->type==LA_SPOT && lar->shb && lar->buftype==LA_SHADBUF_IRREGULAR) {
+
+ /* create storage for shadow, per thread */
+ lar->shb->isb_result[pa->thread]= MEM_callocN(sizeof(ISBData), "isb data");
+
+ if (apixbuf)
+ isb_make_buffer_transp(pa, apixbuf, lar);
+ else
+ isb_make_buffer(pa, lar);
+ }
+ }
+}
+
+
+/* end of part rendering, free stored shadow data for this thread from all lamps */
+void ISB_free(RenderPart *pa)
+{
+ GroupObject *go;
+
+ /* go over all lamps, and free the irregular buffers */
+ for (go=R.lights.first; go; go= go->next) {
+ LampRen *lar= go->lampren;
+
+ if (lar->type==LA_SPOT && lar->shb && lar->buftype==LA_SHADBUF_IRREGULAR) {
+ ISBData *isbdata= lar->shb->isb_result[pa->thread];
+
+ if (isbdata) {
+ if (isbdata->shadfacs)
+ MEM_freeN(isbdata->shadfacs);
+ if (isbdata->shadfaca)
+ MEM_freeN(isbdata->shadfaca);
+
+ if (isbdata->memarena)
+ BLI_memarena_free(isbdata->memarena);
+
+ MEM_freeN(isbdata);
+ lar->shb->isb_result[pa->thread]= NULL;
+ }
+ }
+ }
+}
diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c
new file mode 100644
index 00000000000..d79749871c3
--- /dev/null
+++ b/source/blender/render/intern/source/shadeinput.c
@@ -0,0 +1,1490 @@
+/*
+ * ***** 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) 2006 Blender Foundation
+ * All rights reserved.
+ *
+ * Contributors: Hos, Robert Wenzlaff.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/shadeinput.c
+ * \ingroup render
+ */
+
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_lamp_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_material_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_scene.h"
+
+#include "BKE_node.h"
+
+/* local include */
+#include "raycounter.h"
+#include "render_types.h"
+#include "renderdatabase.h"
+#include "rendercore.h"
+#include "shading.h"
+#include "strand.h"
+#include "texture.h"
+#include "volumetric.h"
+#include "zbuf.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* Shade Sample order:
+ *
+ * - shade_samples_fill_with_ps()
+ * - for each sample
+ * - shade_input_set_triangle() <- if prev sample-face is same, use shade_input_copy_triangle()
+ * - if vlr
+ * - shade_input_set_viewco() <- not for ray or bake
+ * - shade_input_set_uv() <- not for ray or bake
+ * - shade_input_set_normals()
+ * - shade_samples()
+ * - if AO
+ * - shade_samples_do_AO()
+ * - if shading happens
+ * - for each sample
+ * - shade_input_set_shade_texco()
+ * - shade_samples_do_shade()
+ * - OSA: distribute sample result with filter masking
+ *
+ */
+
+/* initialize material variables in shadeinput,
+ * doing inverse gamma correction where applicable */
+void shade_input_init_material(ShadeInput *shi)
+{
+ /* note, keep this synced with render_types.h */
+ memcpy(&shi->r, &shi->mat->r, 23 * sizeof(float));
+ shi->har = shi->mat->har;
+}
+
+/* also used as callback for nodes */
+/* delivers a fully filled in ShadeResult, for all passes */
+void shade_material_loop(ShadeInput *shi, ShadeResult *shr)
+{
+
+ shade_lamp_loop(shi, shr); /* clears shr */
+
+ if (shi->translucency != 0.0f) {
+ ShadeResult shr_t;
+ float fac = shi->translucency;
+
+ shade_input_init_material(shi);
+ negate_v3_v3(shi->vn, shi->vno);
+ negate_v3(shi->facenor);
+ shi->depth++; /* hack to get real shadow now */
+ shade_lamp_loop(shi, &shr_t);
+ shi->depth--;
+
+ /* a couple of passes */
+ madd_v3_v3fl(shr->combined, shr_t.combined, fac);
+ if (shi->passflag & SCE_PASS_SPEC)
+ madd_v3_v3fl(shr->spec, shr_t.spec, fac);
+ if (shi->passflag & SCE_PASS_DIFFUSE) {
+ madd_v3_v3fl(shr->diff, shr_t.diff, fac);
+ madd_v3_v3fl(shr->diffshad, shr_t.diffshad, fac);
+ }
+ if (shi->passflag & SCE_PASS_SHADOW)
+ madd_v3_v3fl(shr->shad, shr_t.shad, fac);
+
+ negate_v3(shi->vn);
+ negate_v3(shi->facenor);
+ }
+
+ /* depth >= 1 when ray-shading */
+ if (shi->depth == 0 || shi->volume_depth > 0) {
+ if (R.r.mode & R_RAYTRACE) {
+ if (shi->ray_mirror != 0.0f || ((shi->mode & MA_TRANSP) && (shi->mode & MA_RAYTRANSP) && shr->alpha != 1.0f)) {
+ /* ray trace works on combined, but gives pass info */
+ ray_trace(shi, shr);
+ }
+ }
+ /* disable adding of sky for raytransp */
+ if ((shi->mode & MA_TRANSP) && (shi->mode & MA_RAYTRANSP))
+ if ((shi->layflag & SCE_LAY_SKY) && (R.r.alphamode == R_ADDSKY))
+ shr->alpha = 1.0f;
+ }
+
+ if (R.r.mode & R_RAYTRACE) {
+ if (R.render_volumes_inside.first)
+ shade_volume_inside(shi, shr);
+ }
+}
+
+
+/* do a shade, finish up some passes, apply mist */
+void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr)
+{
+ bool compat = false;
+ float alpha;
+
+ /* ------ main shading loop -------- */
+#ifdef RE_RAYCOUNTER
+ memset(&shi->raycounter, 0, sizeof(shi->raycounter));
+#endif
+
+ if (shi->mat->nodetree && shi->mat->use_nodes) {
+ compat = ntreeShaderExecTree(shi->mat->nodetree, shi, shr);
+ }
+
+ /* also run this when node shaders fail, due to incompatible shader nodes */
+ if (compat == false) {
+ /* copy all relevant material vars, note, keep this synced with render_types.h */
+ shade_input_init_material(shi);
+
+ if (shi->mat->material_type == MA_TYPE_VOLUME) {
+ if (R.r.mode & R_RAYTRACE) {
+ shade_volume_outside(shi, shr);
+ }
+ }
+ else { /* MA_TYPE_SURFACE, MA_TYPE_WIRE */
+ shade_material_loop(shi, shr);
+ }
+ }
+
+ /* copy additional passes */
+ if (shi->passflag & (SCE_PASS_VECTOR | SCE_PASS_NORMAL)) {
+ copy_v4_v4(shr->winspeed, shi->winspeed);
+ copy_v3_v3(shr->nor, shi->vn);
+ }
+
+ /* MIST */
+ if ((shi->passflag & SCE_PASS_MIST) || ((R.wrld.mode & WO_MIST) && (shi->mat->mode & MA_NOMIST) == 0)) {
+ if (R.r.mode & R_ORTHO)
+ shr->mist = mistfactor(-shi->co[2], shi->co);
+ else
+ shr->mist = mistfactor(len_v3(shi->co), shi->co);
+ }
+ else shr->mist = 0.0f;
+
+ if ((R.wrld.mode & WO_MIST) && (shi->mat->mode & MA_NOMIST) == 0) {
+ alpha = shr->mist;
+ }
+ else alpha = 1.0f;
+
+ /* add mist and premul color */
+ if (shr->alpha != 1.0f || alpha != 1.0f) {
+ float fac = alpha * (shr->alpha);
+ shr->combined[3] = fac;
+
+ if (shi->mat->material_type != MA_TYPE_VOLUME)
+ mul_v3_fl(shr->combined, fac);
+ }
+ else
+ shr->combined[3] = 1.0f;
+
+ /* add z */
+ shr->z = -shi->co[2];
+
+ /* RAYHITS */
+#if 0
+ if (1 || shi->passflag & SCE_PASS_RAYHITS) {
+ shr->rayhits[0] = (float)shi->raycounter.faces.test;
+ shr->rayhits[1] = (float)shi->raycounter.bb.hit;
+ shr->rayhits[2] = 0.0;
+ shr->rayhits[3] = 1.0;
+ }
+#endif
+
+ RE_RC_MERGE(&re_rc_counter[shi->thread], &shi->raycounter);
+}
+
+/* **************************************************************************** */
+/* ShadeInput */
+/* **************************************************************************** */
+
+
+void vlr_set_uv_indices(VlakRen *vlr, int *i1, int *i2, int *i3)
+{
+ /* to prevent storing new tfaces or vcols, we check a split runtime */
+ /* 4---3 4---3 */
+ /* |\ 1| or |1 /| */
+ /* |0\ | |/ 0| */
+ /* 1---2 1---2 0 = orig face, 1 = new face */
+
+ /* Update vert nums to point to correct verts of original face */
+ if (vlr->flag & R_DIVIDE_24) {
+ if (vlr->flag & R_FACE_SPLIT) {
+ (*i1)++; (*i2)++; (*i3)++;
+ }
+ else {
+ (*i3)++;
+ }
+ }
+ else if (vlr->flag & R_FACE_SPLIT) {
+ (*i2)++; (*i3)++;
+ }
+}
+
+/* copy data from face to ShadeInput, general case */
+/* indices 0 1 2 3 only */
+void shade_input_set_triangle_i(ShadeInput *shi, ObjectInstanceRen *obi, VlakRen *vlr, short i1, short i2, short i3)
+{
+ VertRen **vpp = &vlr->v1;
+
+ shi->vlr = vlr;
+ shi->obi = obi;
+ shi->obr = obi->obr;
+
+ shi->v1 = vpp[i1];
+ shi->v2 = vpp[i2];
+ shi->v3 = vpp[i3];
+
+ shi->i1 = i1;
+ shi->i2 = i2;
+ shi->i3 = i3;
+
+ /* note, shi->mat is set in node shaders */
+ shi->mat = shi->mat_override ? shi->mat_override : vlr->mat;
+
+ shi->osatex = (shi->mat->texco & TEXCO_OSA);
+ shi->mode = shi->mat->mode_l; /* or-ed result for all nodes */
+ shi->mode2 = shi->mat->mode2_l;
+
+ /* facenormal copy, can get flipped */
+ shi->flippednor = 0;
+ RE_vlakren_get_normal(&R, obi, vlr, shi->facenor);
+
+ /* calculate vertexnormals */
+ if (vlr->flag & R_SMOOTH) {
+ copy_v3_v3(shi->n1, shi->v1->n);
+ copy_v3_v3(shi->n2, shi->v2->n);
+ copy_v3_v3(shi->n3, shi->v3->n);
+
+ if (obi->flag & R_TRANSFORMED) {
+ mul_m3_v3(obi->nmat, shi->n1); normalize_v3(shi->n1);
+ mul_m3_v3(obi->nmat, shi->n2); normalize_v3(shi->n2);
+ mul_m3_v3(obi->nmat, shi->n3); normalize_v3(shi->n3);
+ }
+ }
+}
+
+/* copy data from face to ShadeInput, scanline case */
+void shade_input_set_triangle(ShadeInput *shi, int obi, int facenr, int UNUSED(normal_flip))
+{
+ if (facenr > 0) {
+ shi->obi = &R.objectinstance[obi];
+ shi->obr = shi->obi->obr;
+ shi->facenr = (facenr - 1) & RE_QUAD_MASK;
+ if (shi->facenr < shi->obr->totvlak) {
+ VlakRen *vlr = RE_findOrAddVlak(shi->obr, shi->facenr);
+
+ if (facenr & RE_QUAD_OFFS)
+ shade_input_set_triangle_i(shi, shi->obi, vlr, 0, 2, 3);
+ else
+ shade_input_set_triangle_i(shi, shi->obi, vlr, 0, 1, 2);
+ }
+ else
+ shi->vlr = NULL; /* general signal we got sky */
+ }
+ else
+ shi->vlr = NULL; /* general signal we got sky */
+}
+
+/* full osa case: copy static info */
+void shade_input_copy_triangle(ShadeInput *shi, ShadeInput *from)
+{
+ /* not so nice, but works... warning is in RE_shader_ext.h */
+ memcpy(shi, from, sizeof(struct ShadeInputCopy));
+}
+
+/* copy data from strand to shadeinput */
+void shade_input_set_strand(ShadeInput *shi, StrandRen *strand, StrandPoint *spoint)
+{
+ /* note, shi->mat is set in node shaders */
+ shi->mat = shi->mat_override ? shi->mat_override : strand->buffer->ma;
+
+ shi->osatex = (shi->mat->texco & TEXCO_OSA);
+ shi->mode = shi->mat->mode_l; /* or-ed result for all nodes */
+
+ /* shade_input_set_viewco equivalent */
+ copy_v3_v3(shi->co, spoint->co);
+ copy_v3_v3(shi->view, shi->co);
+ normalize_v3(shi->view);
+
+ shi->xs = (int)spoint->x;
+ shi->ys = (int)spoint->y;
+
+ if (shi->osatex || (R.r.mode & R_SHADOW)) {
+ copy_v3_v3(shi->dxco, spoint->dtco);
+ copy_v3_v3(shi->dyco, spoint->dsco);
+ }
+
+ /* dxview, dyview, not supported */
+
+ /* facenormal, simply viewco flipped */
+ copy_v3_v3(shi->facenor, spoint->nor);
+
+ /* shade_input_set_normals equivalent */
+ if (shi->mat->mode & MA_TANGENT_STR) {
+ copy_v3_v3(shi->vn, spoint->tan);
+ }
+ else {
+ float cross[3];
+
+ cross_v3_v3v3(cross, spoint->co, spoint->tan);
+ cross_v3_v3v3(shi->vn, cross, spoint->tan);
+ normalize_v3(shi->vn);
+
+ if (dot_v3v3(shi->vn, shi->view) < 0.0f)
+ negate_v3(shi->vn);
+ }
+
+ copy_v3_v3(shi->vno, shi->vn);
+}
+
+void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert *svert, StrandPoint *spoint)
+{
+ StrandBuffer *strandbuf = strand->buffer;
+ ObjectRen *obr = strandbuf->obr;
+ StrandVert *sv;
+ int mode = shi->mode; /* or-ed result for all nodes */
+ short texco = shi->mat->texco;
+
+ if ((shi->mat->texco & TEXCO_REFL)) {
+ /* shi->dxview, shi->dyview, not supported */
+ }
+
+ if (shi->osatex && (texco & (TEXCO_NORM | TEXCO_REFL))) {
+ /* not supported */
+ }
+
+ if (mode & (MA_TANGENT_V | MA_NORMAP_TANG)) {
+ copy_v3_v3(shi->tang, spoint->tan);
+ copy_v3_v3(shi->nmaptang, spoint->tan);
+ }
+
+ if (mode & MA_STR_SURFDIFF) {
+ const float *surfnor = RE_strandren_get_surfnor(obr, strand, 0);
+
+ if (surfnor)
+ copy_v3_v3(shi->surfnor, surfnor);
+ else
+ copy_v3_v3(shi->surfnor, shi->vn);
+
+ if (shi->mat->strand_surfnor > 0.0f) {
+ shi->surfdist = 0.0f;
+ for (sv = strand->vert; sv != svert; sv++)
+ shi->surfdist += len_v3v3(sv->co, (sv + 1)->co);
+ shi->surfdist += spoint->t * len_v3v3(sv->co, (sv + 1)->co);
+ }
+ }
+
+ if (R.r.mode & R_SPEED) {
+ const float *speed;
+
+ speed = RE_strandren_get_winspeed(shi->obi, strand, 0);
+ if (speed)
+ copy_v4_v4(shi->winspeed, speed);
+ else
+ shi->winspeed[0] = shi->winspeed[1] = shi->winspeed[2] = shi->winspeed[3] = 0.0f;
+ }
+
+ /* shade_input_set_shade_texco equivalent */
+ if (texco & NEED_UV) {
+ if (texco & TEXCO_ORCO) {
+ copy_v3_v3(shi->lo, strand->orco);
+ /* no shi->osatex, orco derivatives are zero */
+ }
+
+ if (texco & TEXCO_GLOB) {
+ mul_v3_m4v3(shi->gl, R.viewinv, shi->co);
+
+ if (shi->osatex) {
+ mul_v3_mat3_m4v3(shi->dxgl, R.viewinv, shi->dxco);
+ mul_v3_mat3_m4v3(shi->dygl, R.viewinv, shi->dyco);
+ }
+ }
+
+ if (texco & TEXCO_STRAND) {
+ shi->strandco = spoint->strandco;
+
+ if (shi->osatex) {
+ shi->dxstrand = spoint->dtstrandco;
+ shi->dystrand = 0.0f;
+ }
+ }
+
+ if ((texco & TEXCO_UV) || (mode & (MA_VERTEXCOL | MA_VERTEXCOLP | MA_FACETEXTURE))) {
+ MCol *mcol;
+ const float *uv;
+ char *name;
+ int i;
+
+ shi->totuv = 0;
+ shi->totcol = 0;
+ shi->actuv = obr->actmtface;
+ shi->actcol = obr->actmcol;
+
+ if (mode & (MA_VERTEXCOL | MA_VERTEXCOLP)) {
+ for (i = 0; (mcol = RE_strandren_get_mcol(obr, strand, i, &name, 0)); i++) {
+ ShadeInputCol *scol = &shi->col[i];
+ const char *cp = (char *)mcol;
+
+ shi->totcol++;
+ scol->name = name;
+
+ scol->col[0] = cp[3] / 255.0f;
+ scol->col[1] = cp[2] / 255.0f;
+ scol->col[2] = cp[1] / 255.0f;
+ scol->col[3] = cp[0] / 255.0f;
+ }
+
+ if (shi->totcol) {
+ shi->vcol[0] = shi->col[shi->actcol].col[0];
+ shi->vcol[1] = shi->col[shi->actcol].col[1];
+ shi->vcol[2] = shi->col[shi->actcol].col[2];
+ shi->vcol[3] = shi->col[shi->actcol].col[3];
+ }
+ else {
+ shi->vcol[0] = 0.0f;
+ shi->vcol[1] = 0.0f;
+ shi->vcol[2] = 0.0f;
+ shi->vcol[3] = 0.0f;
+ }
+ }
+
+ for (i = 0; (uv = RE_strandren_get_uv(obr, strand, i, &name, 0)); i++) {
+ ShadeInputUV *suv = &shi->uv[i];
+
+ shi->totuv++;
+ suv->name = name;
+
+ if (strandbuf->overrideuv == i) {
+ suv->uv[0] = -1.0f;
+ suv->uv[1] = spoint->strandco;
+ suv->uv[2] = 0.0f;
+ }
+ else {
+ suv->uv[0] = -1.0f + 2.0f * uv[0];
+ suv->uv[1] = -1.0f + 2.0f * uv[1];
+ suv->uv[2] = 0.0f; /* texture.c assumes there are 3 coords */
+ }
+
+ if (shi->osatex) {
+ suv->dxuv[0] = 0.0f;
+ suv->dxuv[1] = 0.0f;
+ suv->dyuv[0] = 0.0f;
+ suv->dyuv[1] = 0.0f;
+ }
+
+ if ((mode & MA_FACETEXTURE) && i == obr->actmtface) {
+ if ((mode & (MA_VERTEXCOL | MA_VERTEXCOLP)) == 0) {
+ shi->vcol[0] = 1.0f;
+ shi->vcol[1] = 1.0f;
+ shi->vcol[2] = 1.0f;
+ shi->vcol[3] = 1.0f;
+ }
+ }
+ }
+
+ if (shi->totuv == 0) {
+ ShadeInputUV *suv = &shi->uv[0];
+
+ suv->uv[0] = 0.0f;
+ suv->uv[1] = spoint->strandco;
+ suv->uv[2] = 0.0f; /* texture.c assumes there are 3 coords */
+
+ if (mode & MA_FACETEXTURE) {
+ /* no tface? set at 1.0f */
+ shi->vcol[0] = 1.0f;
+ shi->vcol[1] = 1.0f;
+ shi->vcol[2] = 1.0f;
+ shi->vcol[3] = 1.0f;
+ }
+ }
+
+ }
+
+ if (texco & TEXCO_NORM) {
+ shi->orn[0] = -shi->vn[0];
+ shi->orn[1] = -shi->vn[1];
+ shi->orn[2] = -shi->vn[2];
+ }
+
+ if (texco & TEXCO_STRESS) {
+ /* not supported */
+ }
+
+ if (texco & TEXCO_TANGENT) {
+ if ((mode & MA_TANGENT_V) == 0) {
+ /* just prevent surprises */
+ shi->tang[0] = shi->tang[1] = shi->tang[2] = 0.0f;
+ shi->nmaptang[0] = shi->nmaptang[1] = shi->nmaptang[2] = 0.0f;
+ }
+ }
+ }
+
+ /* this only avalailable for scanline renders */
+ if (shi->depth == 0) {
+ if (texco & TEXCO_WINDOW) {
+ shi->winco[0] = -1.0f + 2.0f * spoint->x / (float)R.winx;
+ shi->winco[1] = -1.0f + 2.0f * spoint->y / (float)R.winy;
+ shi->winco[2] = 0.0f;
+
+ /* not supported */
+ if (shi->osatex) {
+ shi->dxwin[0] = 0.0f;
+ shi->dywin[1] = 0.0f;
+ shi->dxwin[0] = 0.0f;
+ shi->dywin[1] = 0.0f;
+ }
+ }
+ }
+
+ if (shi->do_manage) {
+ if (mode & (MA_VERTEXCOL | MA_VERTEXCOLP | MA_FACETEXTURE)) {
+ srgb_to_linearrgb_v3_v3(shi->vcol, shi->vcol);
+ }
+ }
+
+}
+
+/* from scanline pixel coordinates to 3d coordinates, requires set_triangle */
+void shade_input_calc_viewco(ShadeInput *shi, float x, float y, float z, float view[3], float dxyview[2], float co[3], float dxco[3], float dyco[3])
+{
+ /* returns not normalized, so is in viewplane coords */
+ calc_view_vector(view, x, y);
+
+ if (shi->mat->material_type == MA_TYPE_WIRE) {
+ /* wire cannot use normal for calculating shi->co, so
+ * we reconstruct the coordinate less accurate */
+ if (R.r.mode & R_ORTHO)
+ calc_renderco_ortho(co, x, y, z);
+ else
+ calc_renderco_zbuf(co, view, z);
+ }
+ else {
+ /* for non-wire, intersect with the triangle to get the exact coord */
+ float fac, dface, v1[3];
+
+ copy_v3_v3(v1, shi->v1->co);
+ if (shi->obi->flag & R_TRANSFORMED)
+ mul_m4_v3(shi->obi->mat, v1);
+
+ dface = dot_v3v3(v1, shi->facenor);
+
+ /* ortho viewplane cannot intersect using view vector originating in (0,0,0) */
+ if (R.r.mode & R_ORTHO) {
+ /* x and y 3d coordinate can be derived from pixel coord and winmat */
+ float fx = 2.0f / (R.winx * R.winmat[0][0]);
+ float fy = 2.0f / (R.winy * R.winmat[1][1]);
+
+ co[0] = (x - 0.5f * R.winx) * fx - R.winmat[3][0] / R.winmat[0][0];
+ co[1] = (y - 0.5f * R.winy) * fy - R.winmat[3][1] / R.winmat[1][1];
+
+ /* using a*x + b*y + c*z = d equation, (a b c) is normal */
+ if (shi->facenor[2] != 0.0f)
+ co[2] = (dface - shi->facenor[0] * co[0] - shi->facenor[1] * co[1]) / shi->facenor[2];
+ else
+ co[2] = 0.0f;
+
+ if (dxco && dyco) {
+ dxco[0] = fx;
+ dxco[1] = 0.0f;
+ if (shi->facenor[2] != 0.0f)
+ dxco[2] = -(shi->facenor[0] * fx) / shi->facenor[2];
+ else
+ dxco[2] = 0.0f;
+
+ dyco[0] = 0.0f;
+ dyco[1] = fy;
+ if (shi->facenor[2] != 0.0f)
+ dyco[2] = -(shi->facenor[1] * fy) / shi->facenor[2];
+ else
+ dyco[2] = 0.0f;
+
+ if (dxyview) {
+ fac = (co[2] != 0.0f) ? (1.0f / co[2]) : 0.0f;
+ dxyview[0] = -R.viewdx * fac;
+ dxyview[1] = -R.viewdy * fac;
+ }
+ }
+ }
+ else {
+ float div;
+
+ div = dot_v3v3(shi->facenor, view);
+ if (div != 0.0f) fac = dface / div;
+ else fac = 0.0f;
+
+ co[0] = fac * view[0];
+ co[1] = fac * view[1];
+ co[2] = fac * view[2];
+
+ /* pixel dx/dy for render coord */
+ if (dxco && dyco) {
+ float u = dface / (div - R.viewdx * shi->facenor[0]);
+ float v = dface / (div - R.viewdy * shi->facenor[1]);
+
+ dxco[0] = co[0] - (view[0] - R.viewdx) * u;
+ dxco[1] = co[1] - (view[1]) * u;
+ dxco[2] = co[2] - (view[2]) * u;
+
+ dyco[0] = co[0] - (view[0]) * v;
+ dyco[1] = co[1] - (view[1] - R.viewdy) * v;
+ dyco[2] = co[2] - (view[2]) * v;
+
+ if (dxyview) {
+ if (fac != 0.0f) fac = 1.0f / fac;
+ dxyview[0] = -R.viewdx * fac;
+ dxyview[1] = -R.viewdy * fac;
+ }
+ }
+ }
+ }
+
+ /* set camera coords - for scanline, it's always 0.0,0.0,0.0 (render is in camera space)
+ * however for raytrace it can be different - the position of the last intersection */
+ shi->camera_co[0] = shi->camera_co[1] = shi->camera_co[2] = 0.0f;
+
+ /* cannot normalize earlier, code above needs it at viewplane level */
+ normalize_v3(view);
+}
+
+/* from scanline pixel coordinates to 3d coordinates, requires set_triangle */
+void shade_input_set_viewco(ShadeInput *shi, float x, float y, float xs, float ys, float z)
+{
+ float *dxyview = NULL, *dxco = NULL, *dyco = NULL;
+
+ /* currently in use for dithering (soft shadow), node preview, irregular shad */
+ shi->xs = (int)xs;
+ shi->ys = (int)ys;
+
+ /* original scanline coordinate without jitter */
+ shi->scanco[0] = x;
+ shi->scanco[1] = y;
+ shi->scanco[2] = z;
+
+ /* check if we need derivatives */
+ if (shi->osatex || (R.r.mode & R_SHADOW)) {
+ dxco = shi->dxco;
+ dyco = shi->dyco;
+
+ if ((shi->mat->texco & TEXCO_REFL))
+ dxyview = &shi->dxview;
+ }
+
+ shade_input_calc_viewco(shi, xs, ys, z, shi->view, dxyview, shi->co, dxco, dyco);
+}
+
+void barycentric_differentials_from_position(
+ const float co[3], const float v1[3], const float v2[3], const float v3[3],
+ const float dxco[3], const float dyco[3], const float facenor[3], const bool differentials,
+ float *u, float *v, float *dx_u, float *dx_v, float *dy_u, float *dy_v)
+{
+ /* find most stable axis to project */
+ int axis1, axis2;
+ axis_dominant_v3(&axis1, &axis2, facenor);
+
+ /* compute u,v and derivatives */
+ float t00 = v3[axis1] - v1[axis1];
+ float t01 = v3[axis2] - v1[axis2];
+ float t10 = v3[axis1] - v2[axis1];
+ float t11 = v3[axis2] - v2[axis2];
+
+ float detsh = (t00 * t11 - t10 * t01);
+ detsh = (detsh != 0.0f) ? 1.0f / detsh : 0.0f;
+ t00 *= detsh; t01 *= detsh;
+ t10 *= detsh; t11 *= detsh;
+
+ *u = (v3[axis1] - co[axis1]) * t11 - (v3[axis2] - co[axis2]) * t10;
+ *v = (v3[axis2] - co[axis2]) * t00 - (v3[axis1] - co[axis1]) * t01;
+ if (differentials) {
+ *dx_u = dxco[axis1] * t11 - dxco[axis2] * t10;
+ *dx_v = dxco[axis2] * t00 - dxco[axis1] * t01;
+ *dy_u = dyco[axis1] * t11 - dyco[axis2] * t10;
+ *dy_v = dyco[axis2] * t00 - dyco[axis1] * t01;
+ }
+}
+/* calculate U and V, for scanline (silly render face u and v are in range -1 to 0) */
+void shade_input_set_uv(ShadeInput *shi)
+{
+ VlakRen *vlr = shi->vlr;
+
+ if ((vlr->flag & R_SMOOTH) || (shi->mat->texco & NEED_UV) || (shi->passflag & SCE_PASS_UV)) {
+ float v1[3], v2[3], v3[3];
+
+ copy_v3_v3(v1, shi->v1->co);
+ copy_v3_v3(v2, shi->v2->co);
+ copy_v3_v3(v3, shi->v3->co);
+
+ if (shi->obi->flag & R_TRANSFORMED) {
+ mul_m4_v3(shi->obi->mat, v1);
+ mul_m4_v3(shi->obi->mat, v2);
+ mul_m4_v3(shi->obi->mat, v3);
+ }
+
+ /* exception case for wire render of edge */
+ if (vlr->v2 == vlr->v3) {
+ float lend, lenc;
+
+ lend = len_v3v3(v2, v1);
+ lenc = len_v3v3(shi->co, v1);
+
+ if (lend == 0.0f) {
+ shi->u = shi->v = 0.0f;
+ }
+ else {
+ shi->u = -(1.0f - lenc / lend);
+ shi->v = 0.0f;
+ }
+
+ if (shi->osatex) {
+ shi->dx_u = 0.0f;
+ shi->dx_v = 0.0f;
+ shi->dy_u = 0.0f;
+ shi->dy_v = 0.0f;
+ }
+ }
+ else {
+ barycentric_differentials_from_position(
+ shi->co, v1, v2, v3, shi->dxco, shi->dyco, shi->facenor, shi->osatex,
+ &shi->u, &shi->v, &shi->dx_u, &shi->dx_v, &shi->dy_u, &shi->dy_v);
+
+ shi->u = -shi->u;
+ shi->v = -shi->v;
+
+ /* u and v are in range -1 to 0, we allow a little bit extra but not too much, screws up speedvectors */
+ CLAMP(shi->u, -2.0f, 1.0f);
+ CLAMP(shi->v, -2.0f, 1.0f);
+ }
+ }
+}
+
+void shade_input_set_normals(ShadeInput *shi)
+{
+ float u = shi->u, v = shi->v;
+ float l = 1.0f + u + v;
+
+ shi->flippednor = 0;
+
+ /* test flip normals to viewing direction */
+ if (!(shi->vlr->flag & R_TANGENT)) {
+ if (dot_v3v3(shi->facenor, shi->view) < 0.0f) {
+ negate_v3(shi->facenor);
+ shi->flippednor = 1;
+ }
+ }
+
+ /* calculate vertexnormals */
+ if (shi->vlr->flag & R_SMOOTH) {
+ float *n1 = shi->n1, *n2 = shi->n2, *n3 = shi->n3;
+
+ if (shi->flippednor) {
+ negate_v3(n1);
+ negate_v3(n2);
+ negate_v3(n3);
+ }
+
+ shi->vn[0] = l * n3[0] - u * n1[0] - v * n2[0];
+ shi->vn[1] = l * n3[1] - u * n1[1] - v * n2[1];
+ shi->vn[2] = l * n3[2] - u * n1[2] - v * n2[2];
+
+ /* use unnormalized normal (closer to games) */
+ copy_v3_v3(shi->nmapnorm, shi->vn);
+
+ normalize_v3(shi->vn);
+ }
+ else {
+ copy_v3_v3(shi->vn, shi->facenor);
+ copy_v3_v3(shi->nmapnorm, shi->vn);
+ }
+
+ /* used in nodes */
+ copy_v3_v3(shi->vno, shi->vn);
+
+ /* flip normals to viewing direction */
+ if (!(shi->vlr->flag & R_TANGENT))
+ if (dot_v3v3(shi->facenor, shi->view) < 0.0f)
+ shade_input_flip_normals(shi);
+}
+
+/* XXX shi->flippednor messes up otherwise */
+void shade_input_set_vertex_normals(ShadeInput *shi)
+{
+ float u = shi->u, v = shi->v;
+ float l = 1.0f + u + v;
+
+ /* calculate vertexnormals */
+ if (shi->vlr->flag & R_SMOOTH) {
+ const float *n1 = shi->n1, *n2 = shi->n2, *n3 = shi->n3;
+
+ shi->vn[0] = l * n3[0] - u * n1[0] - v * n2[0];
+ shi->vn[1] = l * n3[1] - u * n1[1] - v * n2[1];
+ shi->vn[2] = l * n3[2] - u * n1[2] - v * n2[2];
+
+ /* use unnormalized normal (closer to games) */
+ copy_v3_v3(shi->nmapnorm, shi->vn);
+
+ normalize_v3(shi->vn);
+ }
+ else {
+ copy_v3_v3(shi->vn, shi->facenor);
+ copy_v3_v3(shi->nmapnorm, shi->vn);
+ }
+
+ /* used in nodes */
+ copy_v3_v3(shi->vno, shi->vn);
+}
+
+
+/* use by raytrace, sss, bake to flip into the right direction */
+void shade_input_flip_normals(ShadeInput *shi)
+{
+ negate_v3(shi->facenor);
+ negate_v3(shi->vn);
+ negate_v3(shi->vno);
+ negate_v3(shi->nmapnorm);
+ shi->flippednor = !shi->flippednor;
+}
+
+void shade_input_set_shade_texco(ShadeInput *shi)
+{
+ ObjectInstanceRen *obi = shi->obi;
+ ObjectRen *obr = shi->obr;
+ VertRen *v1 = shi->v1, *v2 = shi->v2, *v3 = shi->v3;
+ float u = shi->u, v = shi->v;
+ float l = 1.0f + u + v, dl;
+ int mode = shi->mode; /* or-ed result for all nodes */
+ int mode2 = shi->mode2;
+ short texco = shi->mat->texco;
+ const bool need_mikk_tangent = (mode & MA_NORMAP_TANG || R.flag & R_NEED_TANGENT);
+ const bool need_mikk_tangent_concrete = (mode2 & MA_TANGENT_CONCRETE) != 0;
+
+ /* calculate dxno */
+ if (shi->vlr->flag & R_SMOOTH) {
+
+ if (shi->osatex && (texco & (TEXCO_NORM | TEXCO_REFL)) ) {
+ const float *n1 = shi->n1, *n2 = shi->n2, *n3 = shi->n3;
+
+ dl = shi->dx_u + shi->dx_v;
+ shi->dxno[0] = dl * n3[0] - shi->dx_u * n1[0] - shi->dx_v * n2[0];
+ shi->dxno[1] = dl * n3[1] - shi->dx_u * n1[1] - shi->dx_v * n2[1];
+ shi->dxno[2] = dl * n3[2] - shi->dx_u * n1[2] - shi->dx_v * n2[2];
+ dl = shi->dy_u + shi->dy_v;
+ shi->dyno[0] = dl * n3[0] - shi->dy_u * n1[0] - shi->dy_v * n2[0];
+ shi->dyno[1] = dl * n3[1] - shi->dy_u * n1[1] - shi->dy_v * n2[1];
+ shi->dyno[2] = dl * n3[2] - shi->dy_u * n1[2] - shi->dy_v * n2[2];
+
+ }
+ }
+
+ /* calc tangents */
+ if (mode & (MA_TANGENT_V | MA_NORMAP_TANG) || mode2 & MA_TANGENT_CONCRETE || R.flag & R_NEED_TANGENT) {
+ const float *s1, *s2, *s3;
+ float tl, tu, tv;
+
+ if (shi->vlr->flag & R_SMOOTH) {
+ tl = l;
+ tu = u;
+ tv = v;
+ }
+ else {
+ /* qdn: flat faces have tangents too,
+ * could pick either one, using average here */
+ tl = 1.0f / 3.0f;
+ tu = -1.0f / 3.0f;
+ tv = -1.0f / 3.0f;
+ }
+
+ shi->tang[0] = shi->tang[1] = shi->tang[2] = 0.0f;
+ shi->nmaptang[0] = shi->nmaptang[1] = shi->nmaptang[2] = 0.0f;
+
+ if (mode & MA_TANGENT_V) {
+ s1 = RE_vertren_get_tangent(obr, v1, 0);
+ s2 = RE_vertren_get_tangent(obr, v2, 0);
+ s3 = RE_vertren_get_tangent(obr, v3, 0);
+
+ if (s1 && s2 && s3) {
+ shi->tang[0] = (tl * s3[0] - tu * s1[0] - tv * s2[0]);
+ shi->tang[1] = (tl * s3[1] - tu * s1[1] - tv * s2[1]);
+ shi->tang[2] = (tl * s3[2] - tu * s1[2] - tv * s2[2]);
+
+ if (obi->flag & R_TRANSFORMED)
+ mul_m3_v3(obi->nmat, shi->tang);
+
+ normalize_v3(shi->tang);
+ copy_v3_v3(shi->nmaptang, shi->tang);
+ }
+ }
+
+ if (need_mikk_tangent || need_mikk_tangent_concrete) {
+ int j1 = shi->i1, j2 = shi->i2, j3 = shi->i3;
+ float c0[3], c1[3], c2[3];
+ int acttang = obr->actmtface;
+
+ vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3);
+
+ /* cycle through all tangent in vlakren */
+ for (int i = 0; i < MAX_MTFACE; i++) {
+ const float *tangent = RE_vlakren_get_nmap_tangent(obr, shi->vlr, i, false);
+ if (!tangent)
+ continue;
+
+ copy_v3_v3(c0, &tangent[j1 * 4]);
+ copy_v3_v3(c1, &tangent[j2 * 4]);
+ copy_v3_v3(c2, &tangent[j3 * 4]);
+
+ /* keeping tangents normalized at vertex level
+ * corresponds better to how it's done in game engines */
+ if (obi->flag & R_TRANSFORMED) {
+ mul_mat3_m4_v3(obi->mat, c0); normalize_v3(c0);
+ mul_mat3_m4_v3(obi->mat, c1); normalize_v3(c1);
+ mul_mat3_m4_v3(obi->mat, c2); normalize_v3(c2);
+ }
+
+ /* we don't normalize the interpolated TBN tangent
+ * corresponds better to how it's done in game engines */
+ shi->tangents[i][0] = (tl * c2[0] - tu * c0[0] - tv * c1[0]);
+ shi->tangents[i][1] = (tl * c2[1] - tu * c0[1] - tv * c1[1]);
+ shi->tangents[i][2] = (tl * c2[2] - tu * c0[2] - tv * c1[2]);
+
+ /* the sign is the same for all 3 vertices of any
+ * non degenerate triangle. */
+ shi->tangents[i][3] = tangent[j1 * 4 + 3];
+
+ if (acttang == i && need_mikk_tangent) {
+ for (int m = 0; m < 4; m++) {
+ shi->nmaptang[m] = shi->tangents[i][m];
+ }
+ }
+ }
+ }
+ }
+
+ if (mode & MA_STR_SURFDIFF) {
+ const float *surfnor = RE_vlakren_get_surfnor(obr, shi->vlr, 0);
+
+ if (surfnor) {
+ copy_v3_v3(shi->surfnor, surfnor);
+ if (obi->flag & R_TRANSFORMED)
+ mul_m3_v3(obi->nmat, shi->surfnor);
+ }
+ else
+ copy_v3_v3(shi->surfnor, shi->vn);
+
+ shi->surfdist = 0.0f;
+ }
+
+ if (R.r.mode & R_SPEED) {
+ const float *s1, *s2, *s3;
+
+ s1 = RE_vertren_get_winspeed(obi, v1, 0);
+ s2 = RE_vertren_get_winspeed(obi, v2, 0);
+ s3 = RE_vertren_get_winspeed(obi, v3, 0);
+ if (s1 && s2 && s3) {
+ shi->winspeed[0] = (l * s3[0] - u * s1[0] - v * s2[0]);
+ shi->winspeed[1] = (l * s3[1] - u * s1[1] - v * s2[1]);
+ shi->winspeed[2] = (l * s3[2] - u * s1[2] - v * s2[2]);
+ shi->winspeed[3] = (l * s3[3] - u * s1[3] - v * s2[3]);
+ }
+ else {
+ shi->winspeed[0] = shi->winspeed[1] = shi->winspeed[2] = shi->winspeed[3] = 0.0f;
+ }
+ }
+
+ /* pass option forces UV calc */
+ if ((shi->passflag & SCE_PASS_UV) || (R.flag & R_NEED_VCOL))
+ texco |= (NEED_UV | TEXCO_UV);
+
+ /* texture coordinates. shi->dxuv shi->dyuv have been set */
+ if (texco & NEED_UV) {
+
+ if (texco & TEXCO_ORCO) {
+ if (v1->orco) {
+ const float *o1, *o2, *o3;
+
+ o1 = v1->orco;
+ o2 = v2->orco;
+ o3 = v3->orco;
+
+ shi->lo[0] = l * o3[0] - u * o1[0] - v * o2[0];
+ shi->lo[1] = l * o3[1] - u * o1[1] - v * o2[1];
+ shi->lo[2] = l * o3[2] - u * o1[2] - v * o2[2];
+
+ if (shi->osatex) {
+ dl = shi->dx_u + shi->dx_v;
+ shi->dxlo[0] = dl * o3[0] - shi->dx_u * o1[0] - shi->dx_v * o2[0];
+ shi->dxlo[1] = dl * o3[1] - shi->dx_u * o1[1] - shi->dx_v * o2[1];
+ shi->dxlo[2] = dl * o3[2] - shi->dx_u * o1[2] - shi->dx_v * o2[2];
+ dl = shi->dy_u + shi->dy_v;
+ shi->dylo[0] = dl * o3[0] - shi->dy_u * o1[0] - shi->dy_v * o2[0];
+ shi->dylo[1] = dl * o3[1] - shi->dy_u * o1[1] - shi->dy_v * o2[1];
+ shi->dylo[2] = dl * o3[2] - shi->dy_u * o1[2] - shi->dy_v * o2[2];
+ }
+ }
+
+ copy_v3_v3(shi->duplilo, obi->dupliorco);
+ }
+
+ if (texco & TEXCO_GLOB) {
+ copy_v3_v3(shi->gl, shi->co);
+ mul_m4_v3(R.viewinv, shi->gl);
+ if (shi->osatex) {
+ copy_v3_v3(shi->dxgl, shi->dxco);
+ mul_mat3_m4_v3(R.viewinv, shi->dxgl);
+ copy_v3_v3(shi->dygl, shi->dyco);
+ mul_mat3_m4_v3(R.viewinv, shi->dygl);
+ }
+ }
+
+ if (texco & TEXCO_STRAND) {
+ shi->strandco = (l * v3->accum - u * v1->accum - v * v2->accum);
+ if (shi->osatex) {
+ dl = shi->dx_u + shi->dx_v;
+ shi->dxstrand = dl * v3->accum - shi->dx_u * v1->accum - shi->dx_v * v2->accum;
+ dl = shi->dy_u + shi->dy_v;
+ shi->dystrand = dl * v3->accum - shi->dy_u * v1->accum - shi->dy_v * v2->accum;
+ }
+ }
+
+ if ((texco & TEXCO_UV) || (mode & (MA_VERTEXCOL | MA_VERTEXCOLP | MA_FACETEXTURE)) || (R.flag & R_NEED_VCOL)) {
+ VlakRen *vlr = shi->vlr;
+ MTFace *tface;
+ MCol *mcol;
+ char *name;
+ int i, j1 = shi->i1, j2 = shi->i2, j3 = shi->i3;
+
+ /* uv and vcols are not copied on split, so set them according vlr divide flag */
+ vlr_set_uv_indices(vlr, &j1, &j2, &j3);
+
+ shi->totuv = 0;
+ shi->totcol = 0;
+ shi->actuv = obr->actmtface;
+ shi->actcol = obr->actmcol;
+
+ if ((mode & (MA_VERTEXCOL | MA_VERTEXCOLP)) || (R.flag & R_NEED_VCOL)) {
+ for (i = 0; (mcol = RE_vlakren_get_mcol(obr, vlr, i, &name, 0)); i++) {
+ ShadeInputCol *scol = &shi->col[i];
+ const char *cp1, *cp2, *cp3;
+ float a[3];
+
+ shi->totcol++;
+ scol->name = name;
+
+ cp1 = (char *)(mcol + j1);
+ cp2 = (char *)(mcol + j2);
+ cp3 = (char *)(mcol + j3);
+
+ /* alpha values */
+ a[0] = ((float)cp1[0]) / 255.f;
+ a[1] = ((float)cp2[0]) / 255.f;
+ a[2] = ((float)cp3[0]) / 255.f;
+ scol->col[3] = l * a[2] - u * a[0] - v * a[1];
+
+ /* sample premultiplied color value */
+ scol->col[0] = (l * ((float)cp3[3]) * a[2] - u * ((float)cp1[3]) * a[0] - v * ((float)cp2[3]) * a[1]) / 255.f;
+ scol->col[1] = (l * ((float)cp3[2]) * a[2] - u * ((float)cp1[2]) * a[0] - v * ((float)cp2[2]) * a[1]) / 255.f;
+ scol->col[2] = (l * ((float)cp3[1]) * a[2] - u * ((float)cp1[1]) * a[0] - v * ((float)cp2[1]) * a[1]) / 255.f;
+
+ /* if not zero alpha, restore non-multiplied color */
+ if (scol->col[3]) {
+ mul_v3_fl(scol->col, 1.0f / scol->col[3]);
+ }
+ }
+
+ if (shi->totcol) {
+ shi->vcol[0] = shi->col[shi->actcol].col[0];
+ shi->vcol[1] = shi->col[shi->actcol].col[1];
+ shi->vcol[2] = shi->col[shi->actcol].col[2];
+ shi->vcol[3] = shi->col[shi->actcol].col[3];
+ }
+ else {
+ shi->vcol[0] = 0.0f;
+ shi->vcol[1] = 0.0f;
+ shi->vcol[2] = 0.0f;
+ shi->vcol[3] = 1.0f;
+ }
+ }
+
+ for (i = 0; (tface = RE_vlakren_get_tface(obr, vlr, i, &name, 0)); i++) {
+ ShadeInputUV *suv = &shi->uv[i];
+ const float *uv1 = tface->uv[j1];
+ const float *uv2 = tface->uv[j2];
+ const float *uv3 = tface->uv[j3];
+
+ shi->totuv++;
+ suv->name = name;
+
+ if ((shi->mat->mapflag & MA_MAPFLAG_UVPROJECT) && (shi->depth == 0)) {
+ float x = shi->xs;
+ float y = shi->ys;
+
+ float s1[2] = {-1.0f + 2.0f * uv1[0], -1.0f + 2.0f * uv1[1]};
+ float s2[2] = {-1.0f + 2.0f * uv2[0], -1.0f + 2.0f * uv2[1]};
+ float s3[2] = {-1.0f + 2.0f * uv3[0], -1.0f + 2.0f * uv3[1]};
+
+
+ float obwinmat[4][4], winmat[4][4], ho1[4], ho2[4], ho3[4];
+ float Zmulx, Zmuly;
+ float hox, hoy, l_proj, dl_proj, u_proj, v_proj;
+ float s00, s01, s10, s11, detsh;
+
+ /* old globals, localized now */
+ Zmulx = ((float)R.winx) / 2.0f;
+ Zmuly = ((float)R.winy) / 2.0f;
+
+ zbuf_make_winmat(&R, winmat);
+ if (shi->obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(obwinmat, winmat, obi->mat);
+ else
+ copy_m4_m4(obwinmat, winmat);
+
+ zbuf_render_project(obwinmat, v1->co, ho1);
+ zbuf_render_project(obwinmat, v2->co, ho2);
+ zbuf_render_project(obwinmat, v3->co, ho3);
+
+ s00 = ho3[0] / ho3[3] - ho1[0] / ho1[3];
+ s01 = ho3[1] / ho3[3] - ho1[1] / ho1[3];
+ s10 = ho3[0] / ho3[3] - ho2[0] / ho2[3];
+ s11 = ho3[1] / ho3[3] - ho2[1] / ho2[3];
+
+ detsh = s00 * s11 - s10 * s01;
+ detsh = (detsh != 0.0f) ? 1.0f / detsh : 0.0f;
+ s00 *= detsh; s01 *= detsh;
+ s10 *= detsh; s11 *= detsh;
+
+ /* recalc u and v again */
+ hox = x / Zmulx - 1.0f;
+ hoy = y / Zmuly - 1.0f;
+ u_proj = (hox - ho3[0] / ho3[3]) * s11 - (hoy - ho3[1] / ho3[3]) * s10;
+ v_proj = (hoy - ho3[1] / ho3[3]) * s00 - (hox - ho3[0] / ho3[3]) * s01;
+ l_proj = 1.0f + u_proj + v_proj;
+
+ suv->uv[0] = l_proj * s3[0] - u_proj * s1[0] - v_proj * s2[0];
+ suv->uv[1] = l_proj * s3[1] - u_proj * s1[1] - v_proj * s2[1];
+ suv->uv[2] = 0.0f;
+
+ if (shi->osatex) {
+ float dxuv[2], dyuv[2];
+ dxuv[0] = s11 / Zmulx;
+ dxuv[1] = -s01 / Zmulx;
+ dyuv[0] = -s10 / Zmuly;
+ dyuv[1] = s00 / Zmuly;
+
+ dl_proj = dxuv[0] + dxuv[1];
+ suv->dxuv[0] = dl_proj * s3[0] - dxuv[0] * s1[0] - dxuv[1] * s2[0];
+ suv->dxuv[1] = dl_proj * s3[1] - dxuv[0] * s1[1] - dxuv[1] * s2[1];
+ dl_proj = dyuv[0] + dyuv[1];
+ suv->dyuv[0] = dl_proj * s3[0] - dyuv[0] * s1[0] - dyuv[1] * s2[0];
+ suv->dyuv[1] = dl_proj * s3[1] - dyuv[0] * s1[1] - dyuv[1] * s2[1];
+ }
+ }
+ else {
+
+ suv->uv[0] = -1.0f + 2.0f * (l * uv3[0] - u * uv1[0] - v * uv2[0]);
+ suv->uv[1] = -1.0f + 2.0f * (l * uv3[1] - u * uv1[1] - v * uv2[1]);
+ suv->uv[2] = 0.0f; /* texture.c assumes there are 3 coords */
+
+ if (shi->osatex) {
+ float duv[2];
+
+ dl = shi->dx_u + shi->dx_v;
+ duv[0] = shi->dx_u;
+ duv[1] = shi->dx_v;
+
+ suv->dxuv[0] = 2.0f * (dl * uv3[0] - duv[0] * uv1[0] - duv[1] * uv2[0]);
+ suv->dxuv[1] = 2.0f * (dl * uv3[1] - duv[0] * uv1[1] - duv[1] * uv2[1]);
+
+ dl = shi->dy_u + shi->dy_v;
+ duv[0] = shi->dy_u;
+ duv[1] = shi->dy_v;
+
+ suv->dyuv[0] = 2.0f * (dl * uv3[0] - duv[0] * uv1[0] - duv[1] * uv2[0]);
+ suv->dyuv[1] = 2.0f * (dl * uv3[1] - duv[0] * uv1[1] - duv[1] * uv2[1]);
+ }
+
+ if ((mode & MA_FACETEXTURE) && i == obr->actmtface) {
+ if (((mode & (MA_VERTEXCOL | MA_VERTEXCOLP)) == 0) && ((R.flag & R_NEED_VCOL) == 0)) {
+ shi->vcol[0] = 1.0f;
+ shi->vcol[1] = 1.0f;
+ shi->vcol[2] = 1.0f;
+ shi->vcol[3] = 1.0f;
+ }
+ if (tface->tpage) {
+ render_realtime_texture(shi, tface->tpage);
+ }
+ }
+ }
+ }
+
+ shi->dupliuv[0] = -1.0f + 2.0f * obi->dupliuv[0];
+ shi->dupliuv[1] = -1.0f + 2.0f * obi->dupliuv[1];
+ shi->dupliuv[2] = 0.0f;
+
+ if (shi->totuv == 0) {
+ ShadeInputUV *suv = &shi->uv[0];
+
+ suv->uv[0] = 2.0f * (u + .5f);
+ suv->uv[1] = 2.0f * (v + .5f);
+ suv->uv[2] = 0.0f; /* texture.c assumes there are 3 coords */
+
+ if (mode & MA_FACETEXTURE) {
+ /* no tface? set at 1.0f */
+ shi->vcol[0] = 1.0f;
+ shi->vcol[1] = 1.0f;
+ shi->vcol[2] = 1.0f;
+ shi->vcol[3] = 1.0f;
+ }
+ }
+ }
+
+ if (texco & TEXCO_NORM) {
+ shi->orn[0] = -shi->vn[0];
+ shi->orn[1] = -shi->vn[1];
+ shi->orn[2] = -shi->vn[2];
+ }
+
+ if (texco & TEXCO_STRESS) {
+ const float *s1, *s2, *s3;
+
+ s1 = RE_vertren_get_stress(obr, v1, 0);
+ s2 = RE_vertren_get_stress(obr, v2, 0);
+ s3 = RE_vertren_get_stress(obr, v3, 0);
+ if (s1 && s2 && s3) {
+ shi->stress = l * s3[0] - u * s1[0] - v * s2[0];
+ if (shi->stress < 1.0f) shi->stress -= 1.0f;
+ else shi->stress = (shi->stress - 1.0f) / shi->stress;
+ }
+ else shi->stress = 0.0f;
+ }
+
+ if (texco & TEXCO_TANGENT) {
+ if ((mode & MA_TANGENT_V) == 0) {
+ /* just prevent surprises */
+ shi->tang[0] = shi->tang[1] = shi->tang[2] = 0.0f;
+ shi->nmaptang[0] = shi->nmaptang[1] = shi->nmaptang[2] = 0.0f;
+ }
+ }
+ }
+
+ /* this only avalailable for scanline renders */
+ if (shi->depth == 0) {
+ float x = shi->xs;
+ float y = shi->ys;
+
+ if (texco & TEXCO_WINDOW) {
+ shi->winco[0] = -1.0f + 2.0f * x / (float)R.winx;
+ shi->winco[1] = -1.0f + 2.0f * y / (float)R.winy;
+ shi->winco[2] = 0.0f;
+ if (shi->osatex) {
+ shi->dxwin[0] = 2.0f / (float)R.winx;
+ shi->dywin[1] = 2.0f / (float)R.winy;
+ shi->dxwin[1] = shi->dxwin[2] = 0.0f;
+ shi->dywin[0] = shi->dywin[2] = 0.0f;
+ }
+ }
+ }
+ /* else {
+ * Note! For raytracing winco is not set,
+ * important because thus means all shader input's need to have their variables set to zero
+ * else un-initialized values are used
+ */
+ if (shi->do_manage) {
+ if ((mode & (MA_VERTEXCOL | MA_VERTEXCOLP | MA_FACETEXTURE)) || (R.flag & R_NEED_VCOL)) {
+ srgb_to_linearrgb_v3_v3(shi->vcol, shi->vcol);
+ }
+ }
+
+}
+
+/* ****************** ShadeSample ************************************** */
+
+/* initialize per part, not per pixel! */
+void shade_input_initialize(ShadeInput *shi, RenderPart *pa, RenderLayer *rl, int sample)
+{
+
+ memset(shi, 0, sizeof(ShadeInput));
+
+ shi->sample = sample;
+ shi->thread = pa->thread;
+ shi->do_preview = (R.r.scemode & R_MATNODE_PREVIEW) != 0;
+
+ shi->do_manage = BKE_scene_check_color_management_enabled(R.scene);
+ shi->use_world_space_shading = BKE_scene_use_world_space_shading(R.scene);
+
+ shi->lay = rl->lay;
+ shi->layflag = rl->layflag;
+ shi->passflag = rl->passflag;
+ shi->combinedflag = ~rl->pass_xor;
+ shi->mat_override = rl->mat_override;
+ shi->light_override = rl->light_override;
+// shi->rl= rl;
+ /* note shi.depth==0 means first hit, not raytracing */
+
+}
+
+/* initialize per part, not per pixel! */
+void shade_sample_initialize(ShadeSample *ssamp, RenderPart *pa, RenderLayer *rl)
+{
+ int a, tot;
+
+ tot = R.osa == 0 ? 1 : R.osa;
+
+ for (a = 0; a < tot; a++) {
+ shade_input_initialize(&ssamp->shi[a], pa, rl, a);
+ memset(&ssamp->shr[a], 0, sizeof(ShadeResult));
+ }
+
+ get_sample_layers(pa, rl, ssamp->rlpp);
+}
+
+/* Do AO or (future) GI */
+void shade_samples_do_AO(ShadeSample *ssamp)
+{
+ if (!(R.r.mode & R_SHADOW))
+ return;
+ if (!(R.r.mode & R_RAYTRACE) && !(R.wrld.ao_gather_method == WO_AOGATHER_APPROX))
+ return;
+
+ if (R.wrld.mode & (WO_AMB_OCC | WO_ENV_LIGHT | WO_INDIRECT_LIGHT)) {
+ ShadeInput *shi = &ssamp->shi[0];
+ int sample;
+
+ if (((shi->passflag & SCE_PASS_COMBINED) && (shi->combinedflag & (SCE_PASS_AO | SCE_PASS_ENVIRONMENT | SCE_PASS_INDIRECT))) ||
+ (shi->passflag & (SCE_PASS_AO | SCE_PASS_ENVIRONMENT | SCE_PASS_INDIRECT)))
+ {
+ for (sample = 0; sample < ssamp->tot; shi++, sample++)
+ if (!(shi->mode & MA_SHLESS))
+ ambient_occlusion(shi); /* stores in shi->ao[] */
+ }
+ }
+}
+
+
+void shade_samples_fill_with_ps(ShadeSample *ssamp, PixStr *ps, int x, int y)
+{
+ ShadeInput *shi;
+ float xs, ys;
+
+ ssamp->tot = 0;
+
+ for (shi = ssamp->shi; ps; ps = ps->next) {
+ shade_input_set_triangle(shi, ps->obi, ps->facenr, 1);
+
+ if (shi->vlr) { /* NULL happens for env material or for 'all z' */
+ unsigned short curmask = ps->mask;
+
+ /* full osa is only set for OSA renders */
+ if (shi->vlr->flag & R_FULL_OSA) {
+ short shi_cp = 0, samp;
+
+ for (samp = 0; samp < R.osa; samp++) {
+ if (curmask & (1 << samp)) {
+ /* zbuffer has this inverse corrected, ensures xs,ys are inside pixel */
+ xs = (float)x + R.jit[samp][0] + 0.5f;
+ ys = (float)y + R.jit[samp][1] + 0.5f;
+
+ if (shi_cp)
+ shade_input_copy_triangle(shi, shi - 1);
+
+ shi->mask = (1 << samp);
+// shi->rl= ssamp->rlpp[samp];
+ shi->samplenr = R.shadowsamplenr[shi->thread]++; /* this counter is not being reset per pixel */
+ shade_input_set_viewco(shi, x, y, xs, ys, (float)ps->z);
+ shade_input_set_uv(shi);
+ if (shi_cp == 0)
+ shade_input_set_normals(shi);
+ else /* XXX shi->flippednor messes up otherwise */
+ shade_input_set_vertex_normals(shi);
+
+ shi_cp = 1;
+ shi++;
+ }
+ }
+ }
+ else {
+ if (R.osa) {
+ short b = R.samples->centmask[curmask];
+ xs = (float)x + R.samples->centLut[b & 15] + 0.5f;
+ ys = (float)y + R.samples->centLut[b >> 4] + 0.5f;
+ }
+ else if (R.i.curblur) {
+ xs= (float)x + R.mblur_jit[R.i.curblur-1][0] + 0.5f;
+ ys= (float)y + R.mblur_jit[R.i.curblur-1][1] + 0.5f;
+ }
+ else {
+ xs = (float)x + 0.5f;
+ ys = (float)y + 0.5f;
+ }
+
+ shi->mask = curmask;
+ shi->samplenr = R.shadowsamplenr[shi->thread]++;
+ shade_input_set_viewco(shi, x, y, xs, ys, (float)ps->z);
+ shade_input_set_uv(shi);
+ shade_input_set_normals(shi);
+ shi++;
+ }
+
+ /* total sample amount, shi->sample is static set in initialize */
+ if (shi != ssamp->shi)
+ ssamp->tot = (shi - 1)->sample + 1;
+ }
+ }
+}
+
+/* shades samples, returns true if anything happened */
+int shade_samples(ShadeSample *ssamp, PixStr *ps, int x, int y)
+{
+ shade_samples_fill_with_ps(ssamp, ps, x, y);
+
+ if (ssamp->tot) {
+ ShadeInput *shi = ssamp->shi;
+ ShadeResult *shr = ssamp->shr;
+ int samp;
+
+ /* if shadow or AO? */
+ shade_samples_do_AO(ssamp);
+
+ /* if shade (all shadepinputs have same passflag) */
+ if (ssamp->shi[0].passflag & ~(SCE_PASS_Z | SCE_PASS_INDEXOB | SCE_PASS_INDEXMA)) {
+
+ for (samp = 0; samp < ssamp->tot; samp++, shi++, shr++) {
+ shade_input_set_shade_texco(shi);
+ shade_input_do_shade(shi, shr);
+ }
+ }
+ else if (shi->passflag & SCE_PASS_Z) {
+ for (samp = 0; samp < ssamp->tot; samp++, shi++, shr++)
+ shr->z = -shi->co[2];
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c
new file mode 100644
index 00000000000..090c249defb
--- /dev/null
+++ b/source/blender/render/intern/source/shadeoutput.c
@@ -0,0 +1,2182 @@
+/*
+ * ***** 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) 2006 Blender Foundation
+ * All rights reserved.
+ *
+ * Contributors: Hos, Robert Wenzlaff.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/shadeoutput.c
+ * \ingroup render
+ */
+
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_colorband.h"
+#include "BKE_colortools.h"
+#include "BKE_material.h"
+
+#include "DNA_group_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+
+/* local include */
+#include "occlusion.h"
+#include "render_types.h"
+#include "rendercore.h"
+#include "shadbuf.h"
+#include "sss.h"
+#include "texture.h"
+
+#include "shading.h" /* own include */
+
+#include "IMB_colormanagement.h"
+
+/* could enable at some point but for now there are far too many conversions */
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wdouble-promotion"
+#endif
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+ListBase *get_lights(ShadeInput *shi)
+{
+
+ if (R.r.scemode & R_BUTS_PREVIEW)
+ return &R.lights;
+ if (shi->light_override)
+ return &shi->light_override->gobject;
+ if (shi->mat && shi->mat->group)
+ return &shi->mat->group->gobject;
+
+ return &R.lights;
+}
+
+#if 0
+static void fogcolor(const float colf[3], float *rco, float *view)
+{
+ float alpha, stepsize, startdist, dist, hor[4], zen[3], vec[3], dview[3];
+ float div=0.0f, distfac;
+
+ hor[0]= R.wrld.horr; hor[1]= R.wrld.horg; hor[2]= R.wrld.horb;
+ zen[0]= R.wrld.zenr; zen[1]= R.wrld.zeng; zen[2]= R.wrld.zenb;
+
+ copy_v3_v3(vec, rco);
+
+ /* we loop from cur coord to mist start in steps */
+ stepsize= 1.0f;
+
+ div= ABS(view[2]);
+ dview[0]= view[0]/(stepsize*div);
+ dview[1]= view[1]/(stepsize*div);
+ dview[2]= -stepsize;
+
+ startdist= -rco[2] + BLI_frand();
+ for (dist= startdist; dist>R.wrld.miststa; dist-= stepsize) {
+
+ hor[0]= R.wrld.horr; hor[1]= R.wrld.horg; hor[2]= R.wrld.horb;
+ alpha= 1.0f;
+ do_sky_tex(vec, vec, NULL, hor, zen, &alpha);
+
+ distfac= (dist-R.wrld.miststa)/R.wrld.mistdist;
+
+ hor[3]= hor[0]*distfac*distfac;
+
+ /* premul! */
+ alpha= hor[3];
+ hor[0]= hor[0]*alpha;
+ hor[1]= hor[1]*alpha;
+ hor[2]= hor[2]*alpha;
+ addAlphaOverFloat(colf, hor);
+
+ sub_v3_v3(vec, dview);
+ }
+}
+#endif
+
+/* zcor is distance, co the 3d coordinate in eye space, return alpha */
+float mistfactor(float zcor, float const co[3])
+{
+ float fac, hi;
+
+ fac = zcor - R.wrld.miststa; /* zcor is calculated per pixel */
+
+ /* fac= -co[2]-R.wrld.miststa; */
+
+ if (fac > 0.0f) {
+ if (fac < R.wrld.mistdist) {
+
+ fac = (fac / R.wrld.mistdist);
+
+ if (R.wrld.mistype == 0) {
+ fac *= fac;
+ }
+ else if (R.wrld.mistype == 1) {
+ /* pass */
+ }
+ else {
+ fac = sqrtf(fac);
+ }
+ }
+ else {
+ fac = 1.0f;
+ }
+ }
+ else {
+ fac = 0.0f;
+ }
+
+ /* height switched off mist */
+ if (R.wrld.misthi!=0.0f && fac!=0.0f) {
+ /* at height misthi the mist is completely gone */
+
+ hi = R.viewinv[0][2] * co[0] +
+ R.viewinv[1][2] * co[1] +
+ R.viewinv[2][2] * co[2] +
+ R.viewinv[3][2];
+
+ if (hi > R.wrld.misthi) {
+ fac = 0.0f;
+ }
+ else if (hi>0.0f) {
+ hi= (R.wrld.misthi-hi)/R.wrld.misthi;
+ fac*= hi*hi;
+ }
+ }
+
+ return (1.0f-fac)* (1.0f-R.wrld.misi);
+}
+
+static void spothalo(struct LampRen *lar, ShadeInput *shi, float *intens)
+{
+ double a, b, c, disc, nray[3], npos[3];
+ double t0, t1 = 0.0f, t2= 0.0f, t3;
+ float p1[3], p2[3], ladist, maxz = 0.0f, maxy = 0.0f, haint;
+ int cuts;
+ bool do_clip = true, use_yco = false;
+
+ *intens= 0.0f;
+ haint= lar->haint;
+
+ if (R.r.mode & R_ORTHO) {
+ /* camera pos (view vector) cannot be used... */
+ /* camera position (cox,coy,0) rotate around lamp */
+ p1[0]= shi->co[0]-lar->co[0];
+ p1[1]= shi->co[1]-lar->co[1];
+ p1[2]= -lar->co[2];
+ mul_m3_v3(lar->imat, p1);
+ copy_v3db_v3fl(npos, p1); /* npos is double! */
+
+ /* pre-scale */
+ npos[2] *= (double)lar->sh_zfac;
+ }
+ else {
+ copy_v3db_v3fl(npos, lar->sh_invcampos); /* in initlamp calculated */
+ }
+
+ /* rotate view */
+ copy_v3db_v3fl(nray, shi->view);
+ mul_m3_v3_double(lar->imat, nray);
+
+ if (R.wrld.mode & WO_MIST) {
+ /* patchy... */
+ haint *= mistfactor(-lar->co[2], lar->co);
+ if (haint==0.0f) {
+ return;
+ }
+ }
+
+
+ /* rotate maxz */
+ if (shi->co[2]==0.0f) {
+ do_clip = false; /* for when halo at sky */
+ }
+ else {
+ p1[0]= shi->co[0]-lar->co[0];
+ p1[1]= shi->co[1]-lar->co[1];
+ p1[2]= shi->co[2]-lar->co[2];
+
+ maxz= lar->imat[0][2]*p1[0]+lar->imat[1][2]*p1[1]+lar->imat[2][2]*p1[2];
+ maxz*= lar->sh_zfac;
+ maxy= lar->imat[0][1]*p1[0]+lar->imat[1][1]*p1[1]+lar->imat[2][1]*p1[2];
+
+ if (fabs(nray[2]) < FLT_EPSILON) {
+ use_yco = true;
+ }
+ }
+
+ /* scale z to make sure volume is normalized */
+ nray[2] *= (double)lar->sh_zfac;
+ /* nray does not need normalization */
+
+ ladist= lar->sh_zfac*lar->dist;
+
+ /* solve */
+ a = nray[0] * nray[0] + nray[1] * nray[1] - nray[2]*nray[2];
+ b = nray[0] * npos[0] + nray[1] * npos[1] - nray[2]*npos[2];
+ c = npos[0] * npos[0] + npos[1] * npos[1] - npos[2]*npos[2];
+
+ cuts= 0;
+ if (fabs(a) < DBL_EPSILON) {
+ /*
+ * Only one intersection point...
+ */
+ return;
+ }
+ else {
+ disc = b*b - a*c;
+
+ if (disc==0.0) {
+ t1=t2= (-b)/ a;
+ cuts= 2;
+ }
+ else if (disc > 0.0) {
+ disc = sqrt(disc);
+ t1 = (-b + disc) / a;
+ t2 = (-b - disc) / a;
+ cuts= 2;
+ }
+ }
+ if (cuts==2) {
+ int ok1=0, ok2=0;
+
+ /* sort */
+ if (t1>t2) {
+ a= t1; t1= t2; t2= a;
+ }
+
+ /* z of intersection points with diabolo */
+ p1[2]= npos[2] + t1*nray[2];
+ p2[2]= npos[2] + t2*nray[2];
+
+ /* evaluate both points */
+ if (p1[2]<=0.0f) ok1= 1;
+ if (p2[2]<=0.0f && t1!=t2) ok2= 1;
+
+ /* at least 1 point with negative z */
+ if (ok1==0 && ok2==0) return;
+
+ /* intersction point with -ladist, the bottom of the cone */
+ if (use_yco == false) {
+ t3= ((double)(-ladist)-npos[2])/nray[2];
+
+ /* de we have to replace one of the intersection points? */
+ if (ok1) {
+ if (p1[2]<-ladist) t1= t3;
+ }
+ else {
+ t1= t3;
+ }
+ if (ok2) {
+ if (p2[2]<-ladist) t2= t3;
+ }
+ else {
+ t2= t3;
+ }
+ }
+ else if (ok1==0 || ok2==0) return;
+
+ /* at least 1 visible interesction point */
+ if (t1<0.0 && t2<0.0) return;
+
+ if (t1<0.0) t1= 0.0;
+ if (t2<0.0) t2= 0.0;
+
+ if (t1==t2) return;
+
+ /* sort again to be sure */
+ if (t1>t2) {
+ a= t1; t1= t2; t2= a;
+ }
+
+ /* calculate t0: is the maximum visible z (when halo is intersected by face) */
+ if (do_clip) {
+ if (use_yco == false) t0 = ((double)maxz - npos[2]) / nray[2];
+ else t0 = ((double)maxy - npos[1]) / nray[1];
+
+ if (t0 < t1) return;
+ if (t0 < t2) t2= t0;
+ }
+
+ /* calc points */
+ p1[0]= npos[0] + t1*nray[0];
+ p1[1]= npos[1] + t1*nray[1];
+ p1[2]= npos[2] + t1*nray[2];
+ p2[0]= npos[0] + t2*nray[0];
+ p2[1]= npos[1] + t2*nray[1];
+ p2[2]= npos[2] + t2*nray[2];
+
+
+ /* now we have 2 points, make three lengths with it */
+
+ a = len_v3(p1);
+ b = len_v3(p2);
+ c = len_v3v3(p1, p2);
+
+ a/= ladist;
+ a= sqrt(a);
+ b/= ladist;
+ b= sqrt(b);
+ c/= ladist;
+
+ *intens= c*( (1.0-a)+(1.0-b) );
+
+ /* WATCH IT: do not clip a,b en c at 1.0, this gives nasty little overflows
+ * at the edges (especially with narrow halos) */
+ if (*intens<=0.0f) return;
+
+ /* soft area */
+ /* not needed because t0 has been used for p1/p2 as well */
+ /* if (doclip && t0<t2) { */
+ /* *intens *= (t0-t1)/(t2-t1); */
+ /* } */
+
+ *intens *= haint;
+
+ if (lar->shb && lar->shb->shadhalostep) {
+ *intens *= shadow_halo(lar, p1, p2);
+ }
+
+ }
+}
+
+void renderspothalo(ShadeInput *shi, float col[4], float alpha)
+{
+ ListBase *lights;
+ GroupObject *go;
+ LampRen *lar;
+ float i;
+
+ if (alpha==0.0f) return;
+
+ lights= get_lights(shi);
+ for (go=lights->first; go; go= go->next) {
+ lar= go->lampren;
+ if (lar==NULL) continue;
+
+ if (lar->type==LA_SPOT && (lar->mode & LA_HALO) && (lar->buftype != LA_SHADBUF_DEEP) && lar->haint>0) {
+
+ if (lar->mode & LA_LAYER)
+ if (shi->vlr && (lar->lay & shi->obi->lay)==0)
+ continue;
+ if ((lar->lay & shi->lay)==0)
+ continue;
+
+ spothalo(lar, shi, &i);
+ if (i > 0.0f) {
+ const float i_alpha = i * alpha;
+ col[0] += i_alpha * lar->r;
+ col[1] += i_alpha * lar->g;
+ col[2] += i_alpha * lar->b;
+ col[3] += i_alpha; /* all premul */
+ }
+ }
+ }
+ /* clip alpha, is needed for unified 'alpha threshold' (vanillaRenderPipe.c) */
+ if (col[3]>1.0f) col[3]= 1.0f;
+}
+
+
+
+/* ---------------- shaders ----------------------- */
+
+static double Normalize_d(double *n)
+{
+ double d;
+
+ d= n[0]*n[0]+n[1]*n[1]+n[2]*n[2];
+
+ if (d>0.00000000000000001) {
+ d= sqrt(d);
+
+ n[0]/=d;
+ n[1]/=d;
+ n[2]/=d;
+ }
+ else {
+ n[0]=n[1]=n[2]= 0.0;
+ d= 0.0;
+ }
+ return d;
+}
+
+/* mix of 'real' fresnel and allowing control. grad defines blending gradient */
+float fresnel_fac(const float view[3], const float vn[3], float grad, float fac)
+{
+ float t1, t2;
+
+ if (fac==0.0f) return 1.0f;
+
+ t1 = dot_v3v3(view, vn);
+ if (t1>0.0f) t2= 1.0f+t1;
+ else t2= 1.0f-t1;
+
+ t2= grad + (1.0f-grad)*powf(t2, fac);
+
+ if (t2<0.0f) return 0.0f;
+ else if (t2>1.0f) return 1.0f;
+ return t2;
+}
+
+static double saacos_d(double fac)
+{
+ if (fac<= -1.0) return M_PI;
+ else if (fac>=1.0) return 0.0;
+ else return acos(fac);
+}
+
+/* Stoke's form factor. Need doubles here for extreme small area sizes */
+static float area_lamp_energy(float (*area)[3], const float co[3], const float vn[3])
+{
+ double fac;
+ double vec[4][3]; /* vectors of rendered co to vertices lamp */
+ double cross[4][3]; /* cross products of this */
+ double rad[4]; /* angles between vecs */
+
+ VECSUB(vec[0], co, area[0]);
+ VECSUB(vec[1], co, area[1]);
+ VECSUB(vec[2], co, area[2]);
+ VECSUB(vec[3], co, area[3]);
+
+ Normalize_d(vec[0]);
+ Normalize_d(vec[1]);
+ Normalize_d(vec[2]);
+ Normalize_d(vec[3]);
+
+ /* cross product */
+#define CROSS(dest, a, b) \
+ { \
+ dest[0]= a[1] * b[2] - a[2] * b[1]; \
+ dest[1]= a[2] * b[0] - a[0] * b[2]; \
+ dest[2]= a[0] * b[1] - a[1] * b[0]; \
+ } (void)0
+
+ CROSS(cross[0], vec[0], vec[1]);
+ CROSS(cross[1], vec[1], vec[2]);
+ CROSS(cross[2], vec[2], vec[3]);
+ CROSS(cross[3], vec[3], vec[0]);
+
+#undef CROSS
+
+ Normalize_d(cross[0]);
+ Normalize_d(cross[1]);
+ Normalize_d(cross[2]);
+ Normalize_d(cross[3]);
+
+ /* angles */
+ rad[0]= vec[0][0]*vec[1][0]+ vec[0][1]*vec[1][1]+ vec[0][2]*vec[1][2];
+ rad[1]= vec[1][0]*vec[2][0]+ vec[1][1]*vec[2][1]+ vec[1][2]*vec[2][2];
+ rad[2]= vec[2][0]*vec[3][0]+ vec[2][1]*vec[3][1]+ vec[2][2]*vec[3][2];
+ rad[3]= vec[3][0]*vec[0][0]+ vec[3][1]*vec[0][1]+ vec[3][2]*vec[0][2];
+
+ rad[0]= saacos_d(rad[0]);
+ rad[1]= saacos_d(rad[1]);
+ rad[2]= saacos_d(rad[2]);
+ rad[3]= saacos_d(rad[3]);
+
+ /* Stoke formula */
+ fac= rad[0]*(vn[0]*cross[0][0]+ vn[1]*cross[0][1]+ vn[2]*cross[0][2]);
+ fac+= rad[1]*(vn[0]*cross[1][0]+ vn[1]*cross[1][1]+ vn[2]*cross[1][2]);
+ fac+= rad[2]*(vn[0]*cross[2][0]+ vn[1]*cross[2][1]+ vn[2]*cross[2][2]);
+ fac+= rad[3]*(vn[0]*cross[3][0]+ vn[1]*cross[3][1]+ vn[2]*cross[3][2]);
+
+ if (fac<=0.0) return 0.0;
+ return fac;
+}
+
+static float area_lamp_energy_multisample(LampRen *lar, const float co[3], float *vn)
+{
+ /* corner vectors are moved around according lamp jitter */
+ float *jitlamp= lar->jitter, vec[3];
+ float area[4][3], intens= 0.0f;
+ int a= lar->ray_totsamp;
+
+ /* test if co is behind lamp */
+ sub_v3_v3v3(vec, co, lar->co);
+ if (dot_v3v3(vec, lar->vec) < 0.0f)
+ return 0.0f;
+
+ while (a--) {
+ vec[0]= jitlamp[0];
+ vec[1]= jitlamp[1];
+ vec[2]= 0.0f;
+ mul_m3_v3(lar->mat, vec);
+
+ add_v3_v3v3(area[0], lar->area[0], vec);
+ add_v3_v3v3(area[1], lar->area[1], vec);
+ add_v3_v3v3(area[2], lar->area[2], vec);
+ add_v3_v3v3(area[3], lar->area[3], vec);
+
+ intens+= area_lamp_energy(area, co, vn);
+
+ jitlamp+= 2;
+ }
+ intens /= (float)lar->ray_totsamp;
+
+ return pow(intens * lar->areasize, lar->k); /* corrected for buttons size and lar->dist^2 */
+}
+
+static float spec(float inp, int hard)
+{
+ float b1;
+
+ if (inp>=1.0f) return 1.0f;
+ else if (inp<=0.0f) return 0.0f;
+
+ b1= inp*inp;
+ /* avoid FPE */
+ if (b1<0.01f) b1= 0.01f;
+
+ if ((hard & 1)==0) inp= 1.0f;
+ if (hard & 2) inp*= b1;
+ b1*= b1;
+ if (hard & 4) inp*= b1;
+ b1*= b1;
+ if (hard & 8) inp*= b1;
+ b1*= b1;
+ if (hard & 16) inp*= b1;
+ b1*= b1;
+
+ /* avoid FPE */
+ if (b1<0.001f) b1= 0.0f;
+
+ if (hard & 32) inp*= b1;
+ b1*= b1;
+ if (hard & 64) inp*=b1;
+ b1*= b1;
+ if (hard & 128) inp*=b1;
+
+ if (b1<0.001f) b1= 0.0f;
+
+ if (hard & 256) {
+ b1*= b1;
+ inp*=b1;
+ }
+
+ return inp;
+}
+
+static float Phong_Spec(const float n[3], const float l[3], const float v[3], int hard, int tangent )
+{
+ float h[3];
+ float rslt;
+
+ h[0] = l[0] + v[0];
+ h[1] = l[1] + v[1];
+ h[2] = l[2] + v[2];
+ normalize_v3(h);
+
+ rslt = h[0]*n[0] + h[1]*n[1] + h[2]*n[2];
+ if (tangent) rslt= sasqrt(1.0f - rslt*rslt);
+
+ if ( rslt > 0.0f ) rslt= spec(rslt, hard);
+ else rslt = 0.0f;
+
+ return rslt;
+}
+
+
+/* reduced cook torrance spec (for off-specular peak) */
+static float CookTorr_Spec(const float n[3], const float l[3], const float v[3], int hard, int tangent)
+{
+ float i, nh, nv, h[3];
+
+ h[0]= v[0]+l[0];
+ h[1]= v[1]+l[1];
+ h[2]= v[2]+l[2];
+ normalize_v3(h);
+
+ nh= n[0]*h[0]+n[1]*h[1]+n[2]*h[2];
+ if (tangent) nh= sasqrt(1.0f - nh*nh);
+ else if (nh<0.0f) return 0.0f;
+
+ nv= n[0]*v[0]+n[1]*v[1]+n[2]*v[2];
+ if (tangent) nv= sasqrt(1.0f - nv*nv);
+ else if (nv<0.0f) nv= 0.0f;
+
+ i= spec(nh, hard);
+
+ i= i/(0.1f+nv);
+ return i;
+}
+
+/* Blinn spec */
+static float Blinn_Spec(const float n[3], const float l[3], const float v[3], float refrac, float spec_power, int tangent)
+{
+ float i, nh, nv, nl, vh, h[3];
+ float a, b, c, g=0.0f, p, f, ang;
+
+ if (refrac < 1.0f) return 0.0f;
+ if (spec_power == 0.0f) return 0.0f;
+
+ /* conversion from 'hardness' (1-255) to 'spec_power' (50 maps at 0.1) */
+ if (spec_power<100.0f)
+ spec_power = sqrtf(1.0f / spec_power);
+ else spec_power= 10.0f/spec_power;
+
+ h[0]= v[0]+l[0];
+ h[1]= v[1]+l[1];
+ h[2]= v[2]+l[2];
+ normalize_v3(h);
+
+ nh= n[0]*h[0]+n[1]*h[1]+n[2]*h[2]; /* Dot product between surface normal and half-way vector */
+ if (tangent) nh= sasqrt(1.0f - nh*nh);
+ else if (nh<0.0f) return 0.0f;
+
+ nv= n[0]*v[0]+n[1]*v[1]+n[2]*v[2]; /* Dot product between surface normal and view vector */
+ if (tangent) nv= sasqrt(1.0f - nv*nv);
+ if (nv<=0.01f) nv= 0.01f; /* hrms... */
+
+ nl= n[0]*l[0]+n[1]*l[1]+n[2]*l[2]; /* Dot product between surface normal and light vector */
+ if (tangent) nl= sasqrt(1.0f - nl*nl);
+ if (nl<=0.01f) {
+ return 0.0f;
+ }
+
+ vh= v[0]*h[0]+v[1]*h[1]+v[2]*h[2]; /* Dot product between view vector and half-way vector */
+ if (vh<=0.0f) vh= 0.01f;
+
+ a = 1.0f;
+ b = (2.0f*nh*nv)/vh;
+ c = (2.0f*nh*nl)/vh;
+
+ if ( a < b && a < c ) g = a;
+ else if ( b < a && b < c ) g = b;
+ else if ( c < a && c < b ) g = c;
+
+ p = sqrt((double)((refrac * refrac)+(vh * vh) - 1.0f));
+ f = (((p-vh)*(p-vh))/((p+vh)*(p+vh)))*(1+((((vh*(p+vh))-1.0f)*((vh*(p+vh))-1.0f))/(((vh*(p-vh))+1.0f)*((vh*(p-vh))+1.0f))));
+ ang = saacos(nh);
+
+ i= f * g * exp((double)(-(ang*ang) / (2.0f*spec_power*spec_power)));
+ if (i<0.0f) i= 0.0f;
+
+ return i;
+}
+
+/* cartoon render spec */
+static float Toon_Spec(const float n[3], const float l[3], const float v[3], float size, float smooth, int tangent)
+{
+ float h[3];
+ float ang;
+ float rslt;
+
+ h[0] = l[0] + v[0];
+ h[1] = l[1] + v[1];
+ h[2] = l[2] + v[2];
+ normalize_v3(h);
+
+ rslt = h[0]*n[0] + h[1]*n[1] + h[2]*n[2];
+ if (tangent) rslt = sasqrt(1.0f - rslt*rslt);
+
+ ang = saacos( rslt );
+
+ if ( ang < size ) rslt = 1.0f;
+ else if ( ang >= (size + smooth) || smooth == 0.0f ) rslt = 0.0f;
+ else rslt = 1.0f - ((ang - size) / smooth);
+
+ return rslt;
+}
+
+/* Ward isotropic gaussian spec */
+static float WardIso_Spec(const float n[3], const float l[3], const float v[3], float rms, int tangent)
+{
+ float i, nh, nv, nl, h[3], angle, alpha;
+
+
+ /* half-way vector */
+ h[0] = l[0] + v[0];
+ h[1] = l[1] + v[1];
+ h[2] = l[2] + v[2];
+ normalize_v3(h);
+
+ nh = n[0]*h[0]+n[1]*h[1]+n[2]*h[2]; /* Dot product between surface normal and half-way vector */
+ if (tangent) nh = sasqrt(1.0f - nh*nh);
+ if (nh<=0.0f) nh = 0.001f;
+
+ nv = n[0]*v[0]+n[1]*v[1]+n[2]*v[2]; /* Dot product between surface normal and view vector */
+ if (tangent) nv = sasqrt(1.0f - nv*nv);
+ if (nv<=0.0f) nv = 0.001f;
+
+ nl = n[0]*l[0]+n[1]*l[1]+n[2]*l[2]; /* Dot product between surface normal and light vector */
+ if (tangent) nl = sasqrt(1.0f - nl*nl);
+ if (nl<=0.0f) nl = 0.001f;
+
+ angle = tanf(saacos(nh));
+ alpha = MAX2(rms, 0.001f);
+
+ i= nl * (1.0f/(4.0f*(float)M_PI*alpha*alpha)) * (expf( -(angle*angle)/(alpha*alpha))/(sqrtf(nv*nl)));
+
+ return i;
+}
+
+/* cartoon render diffuse */
+static float Toon_Diff(const float n[3], const float l[3], const float UNUSED(v[3]), float size, float smooth)
+{
+ float rslt, ang;
+
+ rslt = n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
+
+ ang = saacos(rslt);
+
+ if ( ang < size ) rslt = 1.0f;
+ else if ( ang >= (size + smooth) || smooth == 0.0f ) rslt = 0.0f;
+ else rslt = 1.0f - ((ang - size) / smooth);
+
+ return rslt;
+}
+
+/* Oren Nayar diffuse */
+
+/* 'nl' is either dot product, or return value of area light */
+/* in latter case, only last multiplication uses 'nl' */
+static float OrenNayar_Diff(float nl, const float n[3], const float l[3], const float v[3], float rough )
+{
+ float i/*, nh*/, nv /*, vh */, realnl, h[3];
+ float a, b, t, A, B;
+ float Lit_A, View_A, Lit_B[3], View_B[3];
+
+ h[0]= v[0]+l[0];
+ h[1]= v[1]+l[1];
+ h[2]= v[2]+l[2];
+ normalize_v3(h);
+
+ /* nh= n[0]*h[0]+n[1]*h[1]+n[2]*h[2]; */ /* Dot product between surface normal and half-way vector */
+ /* if (nh<0.0f) nh = 0.0f; */
+
+ nv= n[0]*v[0]+n[1]*v[1]+n[2]*v[2]; /* Dot product between surface normal and view vector */
+ if (nv<=0.0f) nv= 0.0f;
+
+ realnl= n[0]*l[0]+n[1]*l[1]+n[2]*l[2]; /* Dot product between surface normal and light vector */
+ if (realnl<=0.0f) return 0.0f;
+ if (nl<0.0f) return 0.0f; /* value from area light */
+
+ /* vh= v[0]*h[0]+v[1]*h[1]+v[2]*h[2]; */ /* Dot product between view vector and halfway vector */
+ /* if (vh<=0.0f) vh= 0.0f; */
+
+ Lit_A = saacos(realnl);
+ View_A = saacos( nv );
+
+ Lit_B[0] = l[0] - (realnl * n[0]);
+ Lit_B[1] = l[1] - (realnl * n[1]);
+ Lit_B[2] = l[2] - (realnl * n[2]);
+ normalize_v3(Lit_B);
+
+ View_B[0] = v[0] - (nv * n[0]);
+ View_B[1] = v[1] - (nv * n[1]);
+ View_B[2] = v[2] - (nv * n[2]);
+ normalize_v3(View_B);
+
+ t = Lit_B[0]*View_B[0] + Lit_B[1]*View_B[1] + Lit_B[2]*View_B[2];
+ if ( t < 0 ) t = 0;
+
+ if ( Lit_A > View_A ) {
+ a = Lit_A;
+ b = View_A;
+ }
+ else {
+ a = View_A;
+ b = Lit_A;
+ }
+
+ A = 1.0f - (0.5f * ((rough * rough) / ((rough * rough) + 0.33f)));
+ B = 0.45f * ((rough * rough) / ((rough * rough) + 0.09f));
+
+ b*= 0.95f; /* prevent tangens from shooting to inf, 'nl' can be not a dot product here. */
+ /* overflow only happens with extreme size area light, and higher roughness */
+ i = nl * ( A + ( B * t * sinf(a) * tanf(b) ) );
+
+ return i;
+}
+
+/* Minnaert diffuse */
+static float Minnaert_Diff(float nl, const float n[3], const float v[3], float darkness)
+{
+ float i, nv;
+
+ /* nl = dot product between surface normal and light vector */
+ if (nl <= 0.0f)
+ return 0.0f;
+
+ /* nv = dot product between surface normal and view vector */
+ nv = dot_v3v3(n, v);
+ if (nv < 0.0f)
+ nv = 0.0f;
+
+ if (darkness <= 1.0f)
+ i = nl * pow(max_ff(nv * nl, 0.1f), (darkness - 1.0f) ); /*The Real model*/
+ else
+ i = nl * pow( (1.001f - nv), (darkness - 1.0f) ); /*Nvidia model*/
+
+ return i;
+}
+
+static float Fresnel_Diff(float *vn, float *lv, float *UNUSED(view), float fac_i, float fac)
+{
+ return fresnel_fac(lv, vn, fac_i, fac);
+}
+
+/* --------------------------------------------- */
+/* also called from texture.c */
+void calc_R_ref(ShadeInput *shi)
+{
+ float i;
+
+ /* shi->vn dot shi->view */
+ i= -2*(shi->vn[0]*shi->view[0]+shi->vn[1]*shi->view[1]+shi->vn[2]*shi->view[2]);
+
+ shi->ref[0]= (shi->view[0]+i*shi->vn[0]);
+ shi->ref[1]= (shi->view[1]+i*shi->vn[1]);
+ shi->ref[2]= (shi->view[2]+i*shi->vn[2]);
+ if (shi->osatex) {
+ if (shi->vlr->flag & R_SMOOTH) {
+ i= -2*( (shi->vn[0]+shi->dxno[0])*(shi->view[0]+shi->dxview) +
+ (shi->vn[1]+shi->dxno[1])*shi->view[1]+ (shi->vn[2]+shi->dxno[2])*shi->view[2] );
+
+ shi->dxref[0]= shi->ref[0]- ( shi->view[0]+shi->dxview+i*(shi->vn[0]+shi->dxno[0]));
+ shi->dxref[1]= shi->ref[1]- (shi->view[1]+ i*(shi->vn[1]+shi->dxno[1]));
+ shi->dxref[2]= shi->ref[2]- (shi->view[2]+ i*(shi->vn[2]+shi->dxno[2]));
+
+ i= -2*( (shi->vn[0]+shi->dyno[0])*shi->view[0]+
+ (shi->vn[1]+shi->dyno[1])*(shi->view[1]+shi->dyview)+ (shi->vn[2]+shi->dyno[2])*shi->view[2] );
+
+ shi->dyref[0]= shi->ref[0]- (shi->view[0]+ i*(shi->vn[0]+shi->dyno[0]));
+ shi->dyref[1]= shi->ref[1]- (shi->view[1]+shi->dyview+i*(shi->vn[1]+shi->dyno[1]));
+ shi->dyref[2]= shi->ref[2]- (shi->view[2]+ i*(shi->vn[2]+shi->dyno[2]));
+
+ }
+ else {
+
+ i= -2*( shi->vn[0]*(shi->view[0]+shi->dxview) +
+ shi->vn[1]*shi->view[1]+ shi->vn[2]*shi->view[2] );
+
+ shi->dxref[0]= shi->ref[0]- (shi->view[0]+shi->dxview+i*shi->vn[0]);
+ shi->dxref[1]= shi->ref[1]- (shi->view[1]+ i*shi->vn[1]);
+ shi->dxref[2]= shi->ref[2]- (shi->view[2]+ i*shi->vn[2]);
+
+ i= -2*( shi->vn[0]*shi->view[0]+
+ shi->vn[1]*(shi->view[1]+shi->dyview)+ shi->vn[2]*shi->view[2] );
+
+ shi->dyref[0]= shi->ref[0]- (shi->view[0]+ i*shi->vn[0]);
+ shi->dyref[1]= shi->ref[1]- (shi->view[1]+shi->dyview+i*shi->vn[1]);
+ shi->dyref[2]= shi->ref[2]- (shi->view[2]+ i*shi->vn[2]);
+ }
+ }
+
+}
+
+/* called from rayshade.c */
+void shade_color(ShadeInput *shi, ShadeResult *shr)
+{
+ Material *ma= shi->mat;
+
+ if (ma->mode & (MA_FACETEXTURE)) {
+ shi->r= shi->vcol[0];
+ shi->g= shi->vcol[1];
+ shi->b= shi->vcol[2];
+ if (ma->mode & (MA_FACETEXTURE_ALPHA))
+ shi->alpha= shi->vcol[3];
+ }
+ else if (ma->mode & (MA_VERTEXCOLP)) {
+ float neg_alpha = 1.0f - shi->vcol[3];
+ shi->r= shi->r*neg_alpha + shi->vcol[0]*shi->vcol[3];
+ shi->g= shi->g*neg_alpha + shi->vcol[1]*shi->vcol[3];
+ shi->b= shi->b*neg_alpha + shi->vcol[2]*shi->vcol[3];
+ }
+
+ if (ma->texco)
+ do_material_tex(shi, &R);
+
+ if (ma->fresnel_tra!=0.0f)
+ shi->alpha*= fresnel_fac(shi->view, shi->vn, ma->fresnel_tra_i, ma->fresnel_tra);
+
+ if (!(shi->mode & MA_TRANSP)) shi->alpha= 1.0f;
+
+ shr->diff[0]= shi->r;
+ shr->diff[1]= shi->g;
+ shr->diff[2]= shi->b;
+ shr->alpha= shi->alpha;
+
+ /* modulate by the object color */
+ if ((ma->shade_flag & MA_OBCOLOR) && shi->obr->ob) {
+ float obcol[4];
+
+ copy_v4_v4(obcol, shi->obr->ob->col);
+ CLAMP(obcol[3], 0.0f, 1.0f);
+
+ shr->diff[0] *= obcol[0];
+ shr->diff[1] *= obcol[1];
+ shr->diff[2] *= obcol[2];
+ if (shi->mode & MA_TRANSP) shr->alpha *= obcol[3];
+ }
+
+ copy_v3_v3(shr->diffshad, shr->diff);
+}
+
+/* ramp for at end of shade */
+static void ramp_diffuse_result(float *diff, ShadeInput *shi)
+{
+ Material *ma= shi->mat;
+ float col[4];
+
+ if (ma->ramp_col) {
+ if (ma->rampin_col==MA_RAMP_IN_RESULT) {
+ float fac = IMB_colormanagement_get_luminance(diff);
+ BKE_colorband_evaluate(ma->ramp_col, fac, col);
+
+ /* blending method */
+ fac= col[3]*ma->rampfac_col;
+
+ ramp_blend(ma->rampblend_col, diff, fac, col);
+ }
+ }
+}
+
+/* r,g,b denote energy, ramp is used with different values to make new material color */
+static void add_to_diffuse(float diff[3], const ShadeInput *shi, const float is, const float rgb[3])
+{
+ Material *ma= shi->mat;
+
+ if (ma->ramp_col && (ma->mode & MA_RAMP_COL)) {
+
+ /* MA_RAMP_IN_RESULT is exceptional */
+ if (ma->rampin_col==MA_RAMP_IN_RESULT) {
+ /* normal add */
+ diff[0] += rgb[0] * shi->r;
+ diff[1] += rgb[1] * shi->g;
+ diff[2] += rgb[2] * shi->b;
+ }
+ else {
+ float colt[3], col[4];
+ float fac;
+
+ /* input */
+ switch (ma->rampin_col) {
+ case MA_RAMP_IN_ENERGY:
+ fac = IMB_colormanagement_get_luminance(rgb);
+ break;
+ case MA_RAMP_IN_SHADER:
+ fac = is;
+ break;
+ case MA_RAMP_IN_NOR:
+ fac = dot_v3v3(shi->view, shi->vn);
+ break;
+ default:
+ fac = 0.0f;
+ break;
+ }
+
+ BKE_colorband_evaluate(ma->ramp_col, fac, col);
+
+ /* blending method */
+ fac = col[3] * ma->rampfac_col;
+ copy_v3_v3(colt, &shi->r);
+
+ ramp_blend(ma->rampblend_col, colt, fac, col);
+
+ /* output to */
+ diff[0] += rgb[0] * colt[0];
+ diff[1] += rgb[1] * colt[1];
+ diff[2] += rgb[2] * colt[2];
+ }
+ }
+ else {
+ diff[0] += rgb[0] * shi->r;
+ diff[1] += rgb[1] * shi->g;
+ diff[2] += rgb[2] * shi->b;
+ }
+}
+
+static void ramp_spec_result(float spec_col[3], ShadeInput *shi)
+{
+ Material *ma= shi->mat;
+
+ if (ma->ramp_spec && (ma->rampin_spec==MA_RAMP_IN_RESULT)) {
+ float col[4];
+ float fac = IMB_colormanagement_get_luminance(spec_col);
+
+ BKE_colorband_evaluate(ma->ramp_spec, fac, col);
+
+ /* blending method */
+ fac= col[3]*ma->rampfac_spec;
+
+ ramp_blend(ma->rampblend_spec, spec_col, fac, col);
+
+ }
+}
+
+/* is = dot product shade, t = spec energy */
+static void do_specular_ramp(ShadeInput *shi, float is, float t, float spec[3])
+{
+ Material *ma= shi->mat;
+
+ spec[0]= shi->specr;
+ spec[1]= shi->specg;
+ spec[2]= shi->specb;
+
+ /* MA_RAMP_IN_RESULT is exception */
+ if (ma->ramp_spec && (ma->rampin_spec!=MA_RAMP_IN_RESULT)) {
+ float fac;
+ float col[4];
+
+ /* input */
+ switch (ma->rampin_spec) {
+ case MA_RAMP_IN_ENERGY:
+ fac= t;
+ break;
+ case MA_RAMP_IN_SHADER:
+ fac= is;
+ break;
+ case MA_RAMP_IN_NOR:
+ fac= shi->view[0]*shi->vn[0] + shi->view[1]*shi->vn[1] + shi->view[2]*shi->vn[2];
+ break;
+ default:
+ fac= 0.0f;
+ break;
+ }
+
+ BKE_colorband_evaluate(ma->ramp_spec, fac, col);
+
+ /* blending method */
+ fac= col[3]*ma->rampfac_spec;
+
+ ramp_blend(ma->rampblend_spec, spec, fac, col);
+ }
+}
+
+/* pure AO, check for raytrace and world should have been done */
+/* preprocess, textures were not done, don't use shi->amb for that reason */
+void ambient_occlusion(ShadeInput *shi)
+{
+ if ((R.wrld.ao_gather_method == WO_AOGATHER_APPROX) && shi->mat->amb!=0.0f) {
+ sample_occ(&R, shi);
+ }
+ else if ((R.r.mode & R_RAYTRACE) && shi->mat->amb!=0.0f) {
+ ray_ao(shi, shi->ao, shi->env);
+ }
+ else {
+ shi->ao[0]= shi->ao[1]= shi->ao[2]= 1.0f;
+ zero_v3(shi->env);
+ zero_v3(shi->indirect);
+ }
+}
+
+
+/* wrld mode was checked for */
+static void ambient_occlusion_apply(ShadeInput *shi, ShadeResult *shr)
+{
+ float f= R.wrld.aoenergy;
+ float tmp[3], tmpspec[3];
+
+ if (!((R.r.mode & R_RAYTRACE) || R.wrld.ao_gather_method == WO_AOGATHER_APPROX))
+ return;
+ if (f == 0.0f)
+ return;
+
+ if (R.wrld.aomix==WO_AOADD) {
+ shr->combined[0] += shi->ao[0]*shi->r*shi->refl*f;
+ shr->combined[1] += shi->ao[1]*shi->g*shi->refl*f;
+ shr->combined[2] += shi->ao[2]*shi->b*shi->refl*f;
+ }
+ else if (R.wrld.aomix==WO_AOMUL) {
+ mul_v3_v3v3(tmp, shr->combined, shi->ao);
+ mul_v3_v3v3(tmpspec, shr->spec, shi->ao);
+
+ if (f == 1.0f) {
+ copy_v3_v3(shr->combined, tmp);
+ copy_v3_v3(shr->spec, tmpspec);
+ }
+ else {
+ interp_v3_v3v3(shr->combined, shr->combined, tmp, f);
+ interp_v3_v3v3(shr->spec, shr->spec, tmpspec, f);
+ }
+ }
+}
+
+void environment_lighting_apply(ShadeInput *shi, ShadeResult *shr)
+{
+ float f= R.wrld.ao_env_energy*shi->amb;
+
+ if (!((R.r.mode & R_RAYTRACE) || R.wrld.ao_gather_method == WO_AOGATHER_APPROX))
+ return;
+ if (f == 0.0f)
+ return;
+
+ shr->combined[0] += shi->env[0]*shi->r*shi->refl*f;
+ shr->combined[1] += shi->env[1]*shi->g*shi->refl*f;
+ shr->combined[2] += shi->env[2]*shi->b*shi->refl*f;
+}
+
+static void indirect_lighting_apply(ShadeInput *shi, ShadeResult *shr)
+{
+ float f= R.wrld.ao_indirect_energy;
+
+ if (!((R.r.mode & R_RAYTRACE) || R.wrld.ao_gather_method == WO_AOGATHER_APPROX))
+ return;
+ if (f == 0.0f)
+ return;
+
+ shr->combined[0] += shi->indirect[0]*shi->r*shi->refl*f;
+ shr->combined[1] += shi->indirect[1]*shi->g*shi->refl*f;
+ shr->combined[2] += shi->indirect[2]*shi->b*shi->refl*f;
+}
+
+/* result written in shadfac */
+void lamp_get_shadow(LampRen *lar, ShadeInput *shi, float inp, float shadfac[4], int do_real)
+{
+ LampShadowSubSample *lss= &(lar->shadsamp[shi->thread].s[shi->sample]);
+
+ if (do_real || lss->samplenr!=shi->samplenr) {
+
+ shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 1.0f;
+
+ if (lar->shb) {
+ if (lar->buftype==LA_SHADBUF_IRREGULAR)
+ shadfac[3]= ISB_getshadow(shi, lar->shb);
+ else
+ shadfac[3] = testshadowbuf(&R, lar->shb, shi->co, shi->dxco, shi->dyco, inp, shi->mat->lbias);
+ }
+ else if (lar->mode & LA_SHAD_RAY) {
+ ray_shadow(shi, lar, shadfac);
+ }
+
+ if (shi->depth==0) {
+ copy_v4_v4(lss->shadfac, shadfac);
+ lss->samplenr= shi->samplenr;
+ }
+ }
+ else {
+ copy_v4_v4(shadfac, lss->shadfac);
+ }
+}
+
+/* lampdistance and spot angle, writes in lv and dist */
+float lamp_get_visibility(LampRen *lar, const float co[3], float lv[3], float *dist)
+{
+ if (lar->type==LA_SUN || lar->type==LA_HEMI) {
+ *dist= 1.0f;
+ copy_v3_v3(lv, lar->vec);
+ return 1.0f;
+ }
+ else {
+ float visifac= 1.0f, visifac_r;
+
+ sub_v3_v3v3(lv, co, lar->co);
+ mul_v3_fl(lv, 1.0f / (*dist = len_v3(lv)));
+
+ /* area type has no quad or sphere option */
+ if (lar->type==LA_AREA) {
+ /* area is single sided */
+ //if (dot_v3v3(lv, lar->vec) > 0.0f)
+ // visifac= 1.0f;
+ //else
+ // visifac= 0.0f;
+ }
+ else {
+ switch (lar->falloff_type) {
+ case LA_FALLOFF_CONSTANT:
+ visifac = 1.0f;
+ break;
+ case LA_FALLOFF_INVLINEAR:
+ visifac = lar->dist/(lar->dist + dist[0]);
+ break;
+ case LA_FALLOFF_INVSQUARE:
+ /* NOTE: This seems to be a hack since commit r12045 says this
+ * option is similar to old Quad, but with slight changes.
+ * Correct inv square would be (which would be old Quad):
+ * visifac = lar->distkw / (lar->distkw + dist[0]*dist[0]);
+ */
+ visifac = lar->dist / (lar->dist + dist[0]*dist[0]);
+ break;
+ case LA_FALLOFF_SLIDERS:
+ if (lar->ld1>0.0f)
+ visifac= lar->dist/(lar->dist+lar->ld1*dist[0]);
+ if (lar->ld2>0.0f)
+ visifac*= lar->distkw/(lar->distkw+lar->ld2*dist[0]*dist[0]);
+ break;
+ case LA_FALLOFF_INVCOEFFICIENTS:
+ visifac_r = lar->coeff_const +
+ lar->coeff_lin * dist[0] +
+ lar->coeff_quad * dist[0] * dist[0];
+ if (visifac_r > 0.0)
+ visifac = 1.0 / visifac_r;
+ else
+ visifac = 0.0;
+ break;
+ case LA_FALLOFF_CURVE:
+ /* curvemapping_initialize is called from #add_render_lamp */
+ visifac = curvemapping_evaluateF(lar->curfalloff, 0, dist[0]/lar->dist);
+ break;
+ }
+
+ if (lar->mode & LA_SPHERE) {
+ float t= lar->dist - dist[0];
+ if (t<=0.0f)
+ visifac= 0.0f;
+ else
+ visifac*= t/lar->dist;
+ }
+
+ if (visifac > 0.0f) {
+ if (lar->type==LA_SPOT) {
+ float inpr, t;
+
+ if (lar->mode & LA_SQUARE) {
+ if (dot_v3v3(lv, lar->vec) > 0.0f) {
+ float lvrot[3], x;
+
+ /* rotate view to lampspace */
+ copy_v3_v3(lvrot, lv);
+ mul_m3_v3(lar->imat, lvrot);
+
+ x = max_ff(fabsf(lvrot[0]/lvrot[2]), fabsf(lvrot[1]/lvrot[2]));
+ /* 1.0f/(sqrt(1+x*x)) is equivalent to cos(atan(x)) */
+
+ inpr = 1.0f / (sqrtf(1.0f + x * x));
+ }
+ else inpr= 0.0f;
+ }
+ else {
+ inpr= lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2];
+ }
+
+ t= lar->spotsi;
+ if (inpr<=t)
+ visifac= 0.0f;
+ else {
+ t= inpr-t;
+ if (t<lar->spotbl && lar->spotbl!=0.0f) {
+ /* soft area */
+ float i= t/lar->spotbl;
+ t= i*i;
+ inpr*= (3.0f*t-2.0f*t*i);
+ }
+ visifac*= inpr;
+ }
+ }
+ }
+ }
+ if (visifac <= 0.001f) visifac = 0.0f;
+ return visifac;
+ }
+}
+
+/* function returns raw diff, spec and full shadowed diff in the 'shad' pass */
+static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int passflag)
+{
+ Material *ma= shi->mat;
+ VlakRen *vlr= shi->vlr;
+ float lv[3], lampdist, lacol[3], shadfac[4], lashdw[3];
+ float i, is, i_noshad, inp, *vn, *view, vnor[3], phongcorr=1.0f;
+ float visifac;
+
+ vn= shi->vn;
+ view= shi->view;
+
+
+ if (lar->energy == 0.0f) return;
+ /* only shadow lamps shouldn't affect shadow-less materials at all */
+ if ((lar->mode & LA_ONLYSHADOW) && (!(ma->mode & MA_SHADOW) || !(R.r.mode & R_SHADOW)))
+ return;
+ /* optimization, don't render fully black lamps */
+ if (!(lar->mode & LA_TEXTURE) && (lar->r + lar->g + lar->b == 0.0f))
+ return;
+
+ /* lampdist, spot angle, area side, ... */
+ visifac= lamp_get_visibility(lar, shi->co, lv, &lampdist);
+ if (visifac==0.0f)
+ return;
+
+ if (lar->type==LA_SPOT) {
+ if (lar->mode & LA_OSATEX) {
+ shi->osatex= 1; /* signal for multitex() */
+
+ shi->dxlv[0]= lv[0] - (shi->co[0]-lar->co[0]+shi->dxco[0])/lampdist;
+ shi->dxlv[1]= lv[1] - (shi->co[1]-lar->co[1]+shi->dxco[1])/lampdist;
+ shi->dxlv[2]= lv[2] - (shi->co[2]-lar->co[2]+shi->dxco[2])/lampdist;
+
+ shi->dylv[0]= lv[0] - (shi->co[0]-lar->co[0]+shi->dyco[0])/lampdist;
+ shi->dylv[1]= lv[1] - (shi->co[1]-lar->co[1]+shi->dyco[1])/lampdist;
+ shi->dylv[2]= lv[2] - (shi->co[2]-lar->co[2]+shi->dyco[2])/lampdist;
+ }
+ }
+
+ /* lamp color texture */
+ lacol[0]= lar->r;
+ lacol[1]= lar->g;
+ lacol[2]= lar->b;
+
+ lashdw[0]= lar->shdwr;
+ lashdw[1]= lar->shdwg;
+ lashdw[2]= lar->shdwb;
+
+ if (lar->mode & LA_TEXTURE) do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE);
+ if (lar->mode & LA_SHAD_TEX) do_lamp_tex(lar, lv, shi, lashdw, LA_SHAD_TEX);
+
+ /* tangent case; calculate fake face normal, aligned with lampvector */
+ /* note, vnor==vn is used as tangent trigger for buffer shadow */
+ if (vlr->flag & R_TANGENT) {
+ float cross[3], nstrand[3], blend;
+
+ if (ma->mode & MA_STR_SURFDIFF) {
+ cross_v3_v3v3(cross, shi->surfnor, vn);
+ cross_v3_v3v3(nstrand, vn, cross);
+
+ blend= dot_v3v3(nstrand, shi->surfnor);
+ blend= 1.0f - blend;
+ CLAMP(blend, 0.0f, 1.0f);
+
+ interp_v3_v3v3(vnor, nstrand, shi->surfnor, blend);
+ normalize_v3(vnor);
+ }
+ else {
+ cross_v3_v3v3(cross, lv, vn);
+ cross_v3_v3v3(vnor, cross, vn);
+ normalize_v3(vnor);
+ }
+
+ if (ma->strand_surfnor > 0.0f) {
+ if (ma->strand_surfnor > shi->surfdist) {
+ blend= (ma->strand_surfnor - shi->surfdist)/ma->strand_surfnor;
+ interp_v3_v3v3(vnor, vnor, shi->surfnor, blend);
+ normalize_v3(vnor);
+ }
+ }
+
+ vnor[0]= -vnor[0];vnor[1]= -vnor[1];vnor[2]= -vnor[2];
+ vn= vnor;
+ }
+ else if (ma->mode & MA_TANGENT_V) {
+ float cross[3];
+ cross_v3_v3v3(cross, lv, shi->tang);
+ cross_v3_v3v3(vnor, cross, shi->tang);
+ normalize_v3(vnor);
+ vnor[0]= -vnor[0];vnor[1]= -vnor[1];vnor[2]= -vnor[2];
+ vn= vnor;
+ }
+
+ /* dot product and reflectivity */
+ /* inp = dotproduct, is = shader result, i = lamp energy (with shadow), i_noshad = i without shadow */
+ inp= dot_v3v3(vn, lv);
+
+ /* phong threshold to prevent backfacing faces having artifacts on ray shadow (terminator problem) */
+ /* this complex construction screams for a nicer implementation! (ton) */
+ if (R.r.mode & R_SHADOW) {
+ if (ma->mode & MA_SHADOW) {
+ if (lar->type == LA_HEMI || lar->type == LA_AREA) {
+ /* pass */
+ }
+ else if ((ma->mode & MA_RAYBIAS) && (lar->mode & LA_SHAD_RAY) && (vlr->flag & R_SMOOTH)) {
+ float thresh= shi->obr->ob->smoothresh;
+ if (inp>thresh)
+ phongcorr= (inp-thresh)/(inp*(1.0f-thresh));
+ else
+ phongcorr= 0.0f;
+ }
+ else if (ma->sbias!=0.0f && ((lar->mode & LA_SHAD_RAY) || lar->shb)) {
+ if (inp>ma->sbias)
+ phongcorr= (inp-ma->sbias)/(inp*(1.0f-ma->sbias));
+ else
+ phongcorr= 0.0f;
+ }
+ }
+ }
+
+ /* diffuse shaders */
+ if (lar->mode & LA_NO_DIFF) {
+ is = 0.0f; /* skip shaders */
+ }
+ else if (lar->type==LA_HEMI) {
+ is = 0.5f * inp + 0.5f;
+ }
+ else {
+
+ if (lar->type==LA_AREA)
+ inp= area_lamp_energy_multisample(lar, shi->co, vn);
+
+ /* diffuse shaders (oren nayer gets inp from area light) */
+ if (ma->diff_shader==MA_DIFF_ORENNAYAR) is= OrenNayar_Diff(inp, vn, lv, view, ma->roughness);
+ else if (ma->diff_shader==MA_DIFF_TOON) is= Toon_Diff(vn, lv, view, ma->param[0], ma->param[1]);
+ else if (ma->diff_shader==MA_DIFF_MINNAERT) is= Minnaert_Diff(inp, vn, view, ma->darkness);
+ else if (ma->diff_shader==MA_DIFF_FRESNEL) is= Fresnel_Diff(vn, lv, view, ma->param[0], ma->param[1]);
+ else is= inp; /* Lambert */
+ }
+
+ /* 'is' is diffuse */
+ if ((ma->shade_flag & MA_CUBIC) && is > 0.0f && is < 1.0f) {
+ is= 3.0f * is * is - 2.0f * is * is * is; /* nicer termination of shades */
+ }
+
+ i= is*phongcorr;
+
+ if (i>0.0f) {
+ i*= visifac*shi->refl;
+ }
+ i_noshad= i;
+
+ vn = shi->vn; /* bring back original vector, we use special specular shaders for tangent */
+ if (ma->mode & MA_TANGENT_V)
+ vn= shi->tang;
+
+ /* init transp shadow */
+ shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 1.0f;
+
+ /* shadow and spec, (visifac==0 outside spot) */
+ if (visifac> 0.0f) {
+
+ if ((R.r.mode & R_SHADOW)) {
+ if (ma->mode & MA_SHADOW) {
+ if (lar->shb || (lar->mode & LA_SHAD_RAY)) {
+
+ if (vn==vnor) /* tangent trigger */
+ lamp_get_shadow(lar, shi, dot_v3v3(shi->vn, lv), shadfac, shi->depth);
+ else
+ lamp_get_shadow(lar, shi, inp, shadfac, shi->depth);
+
+ /* warning, here it skips the loop */
+ if ((lar->mode & LA_ONLYSHADOW) && i>0.0f) {
+
+ shadfac[3]= i*lar->energy*(1.0f-shadfac[3]);
+ shr->shad[0] -= shadfac[3]*shi->r*(1.0f-lashdw[0]);
+ shr->shad[1] -= shadfac[3]*shi->g*(1.0f-lashdw[1]);
+ shr->shad[2] -= shadfac[3]*shi->b*(1.0f-lashdw[2]);
+
+ if (!(lar->mode & LA_NO_SPEC)) {
+ shr->spec[0] -= shadfac[3]*shi->specr*(1.0f-lashdw[0]);
+ shr->spec[1] -= shadfac[3]*shi->specg*(1.0f-lashdw[1]);
+ shr->spec[2] -= shadfac[3]*shi->specb*(1.0f-lashdw[2]);
+ }
+
+ return;
+ }
+
+ i*= shadfac[3];
+ shr->shad[3] = shadfac[3]; /* store this for possible check in troublesome cases */
+ }
+ else {
+ shr->shad[3] = 1.0f; /* No shadow at all! */
+ }
+ }
+ }
+
+ /* in case 'no diffuse' we still do most calculus, spec can be in shadow.*/
+ if (!(lar->mode & LA_NO_DIFF)) {
+ if (i>0.0f) {
+ if (ma->mode & MA_SHADOW_TRA) {
+ const float tcol[3] = {
+ i * shadfac[0] * lacol[0],
+ i * shadfac[1] * lacol[1],
+ i * shadfac[2] * lacol[2],
+ };
+ add_to_diffuse(shr->shad, shi, is, tcol);
+ }
+ else {
+ const float tcol[3] = {
+ i * lacol[0],
+ i * lacol[1],
+ i * lacol[2],
+ };
+ add_to_diffuse(shr->shad, shi, is, tcol);
+ }
+ }
+ /* add light for colored shadow */
+ if (i_noshad>i && !(lashdw[0]==0 && lashdw[1]==0 && lashdw[2]==0)) {
+ const float tcol[3] = {
+ lashdw[0] * (i_noshad - i) * lacol[0],
+ lashdw[1] * (i_noshad - i) * lacol[1],
+ lashdw[2] * (i_noshad - i) * lacol[2],
+ };
+ add_to_diffuse(shr->shad, shi, is, tcol);
+ }
+ if (i_noshad>0.0f) {
+ if (passflag & (SCE_PASS_DIFFUSE|SCE_PASS_SHADOW) ||
+ ((passflag & SCE_PASS_COMBINED) && !(shi->combinedflag & SCE_PASS_SHADOW)))
+ {
+ const float tcol[3] = {
+ i_noshad * lacol[0],
+ i_noshad * lacol[1],
+ i_noshad * lacol[2]
+ };
+ add_to_diffuse(shr->diff, shi, is, tcol);
+ }
+ else {
+ copy_v3_v3(shr->diff, shr->shad);
+ }
+ }
+ }
+
+ /* specularity */
+ shadfac[3]*= phongcorr; /* note, shadfac not allowed to be stored nonlocal */
+
+ if (shadfac[3]>0.0f && shi->spec!=0.0f && !(lar->mode & LA_NO_SPEC) && !(lar->mode & LA_ONLYSHADOW)) {
+
+ if (!(passflag & (SCE_PASS_COMBINED | SCE_PASS_SPEC))) {
+ /* pass */
+ }
+ else if (lar->type == LA_HEMI) {
+ float t;
+ /* hemi uses no spec shaders (yet) */
+
+ lv[0]+= view[0];
+ lv[1]+= view[1];
+ lv[2]+= view[2];
+
+ normalize_v3(lv);
+
+ t= vn[0]*lv[0]+vn[1]*lv[1]+vn[2]*lv[2];
+
+ if (lar->type==LA_HEMI) {
+ t= 0.5f*t+0.5f;
+ }
+
+ t= shadfac[3]*shi->spec*spec(t, shi->har);
+
+ shr->spec[0]+= t*(lacol[0] * shi->specr);
+ shr->spec[1]+= t*(lacol[1] * shi->specg);
+ shr->spec[2]+= t*(lacol[2] * shi->specb);
+ }
+ else {
+ /* specular shaders */
+ float specfac, t;
+
+ if (ma->spec_shader==MA_SPEC_PHONG)
+ specfac= Phong_Spec(vn, lv, view, shi->har, (vlr->flag & R_TANGENT) || (ma->mode & MA_TANGENT_V));
+ else if (ma->spec_shader==MA_SPEC_COOKTORR)
+ specfac= CookTorr_Spec(vn, lv, view, shi->har, (vlr->flag & R_TANGENT) || (ma->mode & MA_TANGENT_V));
+ else if (ma->spec_shader==MA_SPEC_BLINN)
+ specfac= Blinn_Spec(vn, lv, view, ma->refrac, (float)shi->har, (vlr->flag & R_TANGENT) || (ma->mode & MA_TANGENT_V));
+ else if (ma->spec_shader==MA_SPEC_WARDISO)
+ specfac= WardIso_Spec( vn, lv, view, ma->rms, (vlr->flag & R_TANGENT) || (ma->mode & MA_TANGENT_V));
+ else
+ specfac= Toon_Spec(vn, lv, view, ma->param[2], ma->param[3], (vlr->flag & R_TANGENT) || (ma->mode & MA_TANGENT_V));
+
+ /* area lamp correction */
+ if (lar->type==LA_AREA) specfac*= inp;
+
+ t= shadfac[3]*shi->spec*visifac*specfac;
+
+ if (ma->mode & MA_RAMP_SPEC) {
+ float spec[3];
+ do_specular_ramp(shi, specfac, t, spec);
+ shr->spec[0]+= t*(lacol[0] * spec[0]);
+ shr->spec[1]+= t*(lacol[1] * spec[1]);
+ shr->spec[2]+= t*(lacol[2] * spec[2]);
+ }
+ else {
+ shr->spec[0]+= t*(lacol[0] * shi->specr);
+ shr->spec[1]+= t*(lacol[1] * shi->specg);
+ shr->spec[2]+= t*(lacol[2] * shi->specb);
+ }
+ }
+ }
+ }
+}
+
+static void shade_lamp_loop_only_shadow(ShadeInput *shi, ShadeResult *shr)
+{
+
+ if (R.r.mode & R_SHADOW) {
+ ListBase *lights;
+ LampRen *lar;
+ GroupObject *go;
+ float inpr, lv[3];
+ float /* *view, */ shadfac[4];
+ float ir, accum, visifac, lampdist;
+ float shaded = 0.0f, lightness = 0.0f;
+
+
+ /* view= shi->view; */ /* UNUSED */
+ accum= ir= 0.0f;
+
+ lights= get_lights(shi);
+ for (go=lights->first; go; go= go->next) {
+ lar= go->lampren;
+ if (lar==NULL) continue;
+
+ if (lar->mode & LA_LAYER) if ((lar->lay & shi->obi->lay)==0) continue;
+ if ((lar->lay & shi->lay)==0) continue;
+
+ if (lar->shb || (lar->mode & LA_SHAD_RAY)) {
+ visifac= lamp_get_visibility(lar, shi->co, lv, &lampdist);
+ ir+= 1.0f;
+
+ if (visifac <= 0.0f) {
+ if (shi->mat->shadowonly_flag == MA_SO_OLD)
+ accum+= 1.0f;
+
+ continue;
+ }
+ inpr= dot_v3v3(shi->vn, lv);
+ if (inpr <= 0.0f) {
+ if (shi->mat->shadowonly_flag == MA_SO_OLD)
+ accum+= 1.0f;
+
+ continue;
+ }
+
+ lamp_get_shadow(lar, shi, inpr, shadfac, shi->depth);
+
+ if (shi->mat->shadowonly_flag == MA_SO_OLD) {
+ /* Old "Shadows Only" */
+ accum+= (1.0f-visifac) + (visifac)*IMB_colormanagement_get_luminance(shadfac)*shadfac[3];
+ }
+ else {
+ shaded += IMB_colormanagement_get_luminance(shadfac)*shadfac[3] * visifac * lar->energy;
+
+ if (shi->mat->shadowonly_flag == MA_SO_SHADOW) {
+ lightness += visifac * lar->energy;
+ }
+ }
+ }
+ }
+
+ /* Apply shadows as alpha */
+ if (ir>0.0f) {
+ if (shi->mat->shadowonly_flag == MA_SO_OLD) {
+ accum = 1.0f - accum/ir;
+ }
+ else {
+ if (shi->mat->shadowonly_flag == MA_SO_SHADOW) {
+ if (lightness > 0.0f) {
+ /* Get shadow value from between 0.0f and non-shadowed lightness */
+ accum = (lightness - shaded) / (lightness);
+ }
+ else {
+ accum = 0.0f;
+ }
+ }
+ else { /* shadowonly_flag == MA_SO_SHADED */
+ /* Use shaded value */
+ accum = 1.0f - shaded;
+ }
+ }
+
+ shr->alpha= (shi->alpha)*(accum);
+ if (shr->alpha<0.0f) shr->alpha=0.0f;
+ }
+ else {
+ /* If "fully shaded", use full alpha even on areas that have no lights */
+ if (shi->mat->shadowonly_flag == MA_SO_SHADED) shr->alpha=shi->alpha;
+ else shr->alpha= 0.f;
+ }
+ }
+
+ /* quite disputable this... also note it doesn't mirror-raytrace */
+ if ((R.wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT)) && shi->amb!=0.0f) {
+ float f;
+
+ if (R.wrld.mode & WO_AMB_OCC) {
+ f= R.wrld.aoenergy*shi->amb;
+
+ if (R.wrld.aomix==WO_AOADD) {
+ if (shi->mat->shadowonly_flag == MA_SO_OLD) {
+ f= f*(1.0f - IMB_colormanagement_get_luminance(shi->ao));
+ shr->alpha= (shr->alpha + f)*f;
+ }
+ else {
+ shr->alpha -= f*IMB_colormanagement_get_luminance(shi->ao);
+ if (shr->alpha<0.0f) shr->alpha=0.0f;
+ }
+ }
+ else /* AO Multiply */
+ shr->alpha= (1.0f - f)*shr->alpha + f*(1.0f - (1.0f - shr->alpha)*IMB_colormanagement_get_luminance(shi->ao));
+ }
+
+ if (R.wrld.mode & WO_ENV_LIGHT) {
+ if (shi->mat->shadowonly_flag == MA_SO_OLD) {
+ f= R.wrld.ao_env_energy*shi->amb*(1.0f - IMB_colormanagement_get_luminance(shi->env));
+ shr->alpha= (shr->alpha + f)*f;
+ }
+ else {
+ f= R.wrld.ao_env_energy*shi->amb;
+ shr->alpha -= f*IMB_colormanagement_get_luminance(shi->env);
+ if (shr->alpha<0.0f) shr->alpha=0.0f;
+ }
+ }
+ }
+}
+
+/* let's map negative light as if it mirrors positive light, otherwise negative values disappear */
+static void wrld_exposure_correct(float diff[3])
+{
+
+ diff[0]= R.wrld.linfac*(1.0f-expf( diff[0]*R.wrld.logfac) );
+ diff[1]= R.wrld.linfac*(1.0f-expf( diff[1]*R.wrld.logfac) );
+ diff[2]= R.wrld.linfac*(1.0f-expf( diff[2]*R.wrld.logfac) );
+}
+
+void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr)
+{
+ /* Passes which might need to know material color.
+ *
+ * It seems to be faster to just calculate material color
+ * even if the pass doesn't really need it than trying to
+ * figure out whether color is really needed or not.
+ */
+ const int color_passes =
+ SCE_PASS_COMBINED | SCE_PASS_RGBA | SCE_PASS_DIFFUSE | SCE_PASS_SPEC |
+ SCE_PASS_REFLECT | SCE_PASS_NORMAL | SCE_PASS_REFRACT | SCE_PASS_EMIT | SCE_PASS_SHADOW;
+
+ Material *ma= shi->mat;
+ int passflag= shi->passflag;
+
+ memset(shr, 0, sizeof(ShadeResult));
+
+ if (!(shi->mode & MA_TRANSP)) shi->alpha = 1.0f;
+
+ /* separate loop */
+ if (ma->mode & MA_ONLYSHADOW) {
+ shade_lamp_loop_only_shadow(shi, shr);
+ return;
+ }
+
+ /* envmap hack, always reset */
+ shi->refcol[0]= shi->refcol[1]= shi->refcol[2]= shi->refcol[3]= 0.0f;
+
+ /* material color itself */
+ if (passflag & color_passes) {
+ if (ma->mode & (MA_FACETEXTURE)) {
+ shi->r= shi->vcol[0];
+ shi->g= shi->vcol[1];
+ shi->b= shi->vcol[2];
+ if (ma->mode & (MA_FACETEXTURE_ALPHA))
+ shi->alpha= shi->vcol[3];
+ }
+#ifdef WITH_FREESTYLE
+ else if (ma->vcol_alpha) {
+ shi->r= shi->vcol[0];
+ shi->g= shi->vcol[1];
+ shi->b= shi->vcol[2];
+ shi->alpha= shi->vcol[3];
+ }
+#endif
+ else if (ma->mode & (MA_VERTEXCOLP)) {
+ float neg_alpha = 1.0f - shi->vcol[3];
+ shi->r= shi->r*neg_alpha + shi->vcol[0]*shi->vcol[3];
+ shi->g= shi->g*neg_alpha + shi->vcol[1]*shi->vcol[3];
+ shi->b= shi->b*neg_alpha + shi->vcol[2]*shi->vcol[3];
+ }
+ if (ma->texco) {
+ do_material_tex(shi, &R);
+ if (!(shi->mode & MA_TRANSP)) shi->alpha = 1.0f;
+ }
+
+ shr->col[0]= shi->r*shi->alpha;
+ shr->col[1]= shi->g*shi->alpha;
+ shr->col[2]= shi->b*shi->alpha;
+ shr->col[3]= shi->alpha;
+
+ if ((ma->sss_flag & MA_DIFF_SSS) && !sss_pass_done(&R, ma)) {
+ if (ma->sss_texfac == 0.0f) {
+ shi->r= shi->g= shi->b= shi->alpha= 1.0f;
+ shr->col[0]= shr->col[1]= shr->col[2]= shr->col[3]= 1.0f;
+ }
+ else {
+ shi->r= pow(max_ff(shi->r, 0.0f), ma->sss_texfac);
+ shi->g= pow(max_ff(shi->g, 0.0f), ma->sss_texfac);
+ shi->b= pow(max_ff(shi->b, 0.0f), ma->sss_texfac);
+ shi->alpha= pow(max_ff(shi->alpha, 0.0f), ma->sss_texfac);
+
+ shr->col[0]= pow(max_ff(shr->col[0], 0.0f), ma->sss_texfac);
+ shr->col[1]= pow(max_ff(shr->col[1], 0.0f), ma->sss_texfac);
+ shr->col[2]= pow(max_ff(shr->col[2], 0.0f), ma->sss_texfac);
+ shr->col[3]= pow(max_ff(shr->col[3], 0.0f), ma->sss_texfac);
+ }
+ }
+ }
+
+ if (ma->mode & MA_SHLESS) {
+ shr->combined[0]= shi->r;
+ shr->combined[1]= shi->g;
+ shr->combined[2]= shi->b;
+ shr->alpha= shi->alpha;
+ goto finally_shadeless;
+ }
+
+ if ( (ma->mode & (MA_VERTEXCOL|MA_VERTEXCOLP))== MA_VERTEXCOL ) { /* vertexcolor light */
+ shr->emit[0]= shi->r*(shi->emit+shi->vcol[0]*shi->vcol[3]);
+ shr->emit[1]= shi->g*(shi->emit+shi->vcol[1]*shi->vcol[3]);
+ shr->emit[2]= shi->b*(shi->emit+shi->vcol[2]*shi->vcol[3]);
+ }
+ else {
+ shr->emit[0]= shi->r*shi->emit;
+ shr->emit[1]= shi->g*shi->emit;
+ shr->emit[2]= shi->b*shi->emit;
+ }
+
+ /* AO pass */
+ if (((passflag & SCE_PASS_COMBINED) && (shi->combinedflag & (SCE_PASS_AO|SCE_PASS_ENVIRONMENT|SCE_PASS_INDIRECT))) ||
+ (passflag & (SCE_PASS_AO|SCE_PASS_ENVIRONMENT|SCE_PASS_INDIRECT))) {
+ if ((R.wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT)) && (R.r.mode & R_SHADOW)) {
+ /* AO was calculated for scanline already */
+ if (shi->depth || shi->volume_depth)
+ ambient_occlusion(shi);
+ copy_v3_v3(shr->ao, shi->ao);
+ copy_v3_v3(shr->env, shi->env); /* XXX multiply */
+ copy_v3_v3(shr->indirect, shi->indirect); /* XXX multiply */
+ }
+ else {
+ shr->ao[0]= shr->ao[1]= shr->ao[2]= 1.0f;
+ zero_v3(shr->env);
+ zero_v3(shr->indirect);
+ }
+ }
+
+ /* lighting pass */
+ if (passflag & (SCE_PASS_COMBINED|SCE_PASS_DIFFUSE|SCE_PASS_SPEC|SCE_PASS_SHADOW)) {
+ GroupObject *go;
+ ListBase *lights;
+ LampRen *lar;
+
+ lights= get_lights(shi);
+ for (go=lights->first; go; go= go->next) {
+ lar= go->lampren;
+ if (lar==NULL) continue;
+
+ /* test for lamp layer */
+ if (lar->mode & LA_LAYER) if ((lar->lay & shi->obi->lay)==0) continue;
+ if ((lar->lay & shi->lay)==0) continue;
+
+ /* accumulates in shr->diff and shr->spec and shr->shad (diffuse with shadow!) */
+ shade_one_light(lar, shi, shr, passflag);
+ }
+
+ /* this check is to prevent only shadow lamps from producing negative
+ * colors.*/
+ if (shr->spec[0] < 0) shr->spec[0] = 0;
+ if (shr->spec[1] < 0) shr->spec[1] = 0;
+ if (shr->spec[2] < 0) shr->spec[2] = 0;
+
+ if (shr->shad[0] < 0) shr->shad[0] = 0;
+ if (shr->shad[1] < 0) shr->shad[1] = 0;
+ if (shr->shad[2] < 0) shr->shad[2] = 0;
+
+ if (ma->sss_flag & MA_DIFF_SSS) {
+ float sss[3], col[3], invalpha, texfac= ma->sss_texfac;
+
+ /* this will return false in the preprocess stage */
+ if (sample_sss(&R, ma, shi->co, sss)) {
+ invalpha= (shr->col[3] > FLT_EPSILON)? 1.0f/shr->col[3]: 1.0f;
+
+ if (texfac==0.0f) {
+ copy_v3_v3(col, shr->col);
+ mul_v3_fl(col, invalpha);
+ }
+ else if (texfac==1.0f) {
+ col[0]= col[1]= col[2]= 1.0f;
+ mul_v3_fl(col, invalpha);
+ }
+ else {
+ copy_v3_v3(col, shr->col);
+ mul_v3_fl(col, invalpha);
+ col[0]= pow(max_ff(col[0], 0.0f), 1.0f-texfac);
+ col[1]= pow(max_ff(col[1], 0.0f), 1.0f-texfac);
+ col[2]= pow(max_ff(col[2], 0.0f), 1.0f-texfac);
+ }
+
+ shr->diff[0]= sss[0]*col[0];
+ shr->diff[1]= sss[1]*col[1];
+ shr->diff[2]= sss[2]*col[2];
+
+ if (shi->combinedflag & SCE_PASS_SHADOW) {
+ shr->shad[0]= shr->diff[0];
+ shr->shad[1]= shr->diff[1];
+ shr->shad[2]= shr->diff[2];
+ }
+ }
+ }
+
+ if (shi->combinedflag & SCE_PASS_SHADOW)
+ copy_v3_v3(shr->diffshad, shr->shad);
+ else
+ copy_v3_v3(shr->diffshad, shr->diff);
+
+ copy_v3_v3(shr->combined, shr->diffshad);
+
+ /* calculate shadow pass, we use a multiplication mask */
+ /* Even if diff = 0,0,0, it does matter what the shadow pass is, since we may want it 'for itself'! */
+ if (passflag & SCE_PASS_SHADOW) {
+ if (shr->diff[0]!=0.0f) shr->shad[0]= shr->shad[0]/shr->diff[0];
+ /* can't determine proper shadow from shad/diff (0/0), so use shadow intensity */
+ else if (shr->shad[0]==0.0f) shr->shad[0]= shr->shad[3];
+
+ if (shr->diff[1]!=0.0f) shr->shad[1]= shr->shad[1]/shr->diff[1];
+ else if (shr->shad[1]==0.0f) shr->shad[1]= shr->shad[3];
+
+ if (shr->diff[2]!=0.0f) shr->shad[2]= shr->shad[2]/shr->diff[2];
+ else if (shr->shad[2]==0.0f) shr->shad[2]= shr->shad[3];
+ }
+
+ /* exposure correction */
+ if ((R.wrld.exp!=0.0f || R.wrld.range!=1.0f) && !R.sss_points) {
+ wrld_exposure_correct(shr->combined); /* has no spec! */
+ wrld_exposure_correct(shr->spec);
+ }
+ }
+
+ /* alpha in end, spec can influence it */
+ if (passflag & (SCE_PASS_COMBINED)) {
+ if ((ma->fresnel_tra!=0.0f) && (shi->mode & MA_TRANSP))
+ shi->alpha*= fresnel_fac(shi->view, shi->vn, ma->fresnel_tra_i, ma->fresnel_tra);
+
+ /* note: shi->mode! */
+ if (shi->mode & MA_TRANSP && (shi->mode & (MA_ZTRANSP|MA_RAYTRANSP))) {
+ if (shi->spectra!=0.0f) {
+ float t = max_fff(shr->spec[0], shr->spec[1], shr->spec[2]);
+ t *= shi->spectra;
+ if (t>1.0f) t= 1.0f;
+ shi->alpha= (1.0f-t)*shi->alpha+t;
+ }
+ }
+ }
+ shr->alpha= shi->alpha;
+
+ /* from now stuff everything in shr->combined: ambient, AO, ramps, exposure */
+ if (!(ma->sss_flag & MA_DIFF_SSS) || !sss_pass_done(&R, ma)) {
+ if (R.r.mode & R_SHADOW) {
+ /* add AO in combined? */
+ if (R.wrld.mode & WO_AMB_OCC)
+ if (shi->combinedflag & SCE_PASS_AO)
+ ambient_occlusion_apply(shi, shr);
+
+ if (R.wrld.mode & WO_ENV_LIGHT)
+ if (shi->combinedflag & SCE_PASS_ENVIRONMENT)
+ environment_lighting_apply(shi, shr);
+
+ if (R.wrld.mode & WO_INDIRECT_LIGHT)
+ if (shi->combinedflag & SCE_PASS_INDIRECT)
+ indirect_lighting_apply(shi, shr);
+ }
+
+ shr->combined[0]+= shi->ambr;
+ shr->combined[1]+= shi->ambg;
+ shr->combined[2]+= shi->ambb;
+
+ if (ma->mode & MA_RAMP_COL) ramp_diffuse_result(shr->combined, shi);
+ }
+
+ if (ma->mode & MA_RAMP_SPEC) ramp_spec_result(shr->spec, shi);
+
+ /* refcol is for envmap only */
+ if (shi->refcol[0]!=0.0f) {
+ float result[3];
+
+ result[0]= shi->mirr*shi->refcol[1] + (1.0f - shi->mirr*shi->refcol[0])*shr->combined[0];
+ result[1]= shi->mirg*shi->refcol[2] + (1.0f - shi->mirg*shi->refcol[0])*shr->combined[1];
+ result[2]= shi->mirb*shi->refcol[3] + (1.0f - shi->mirb*shi->refcol[0])*shr->combined[2];
+
+ if (passflag & SCE_PASS_REFLECT)
+ sub_v3_v3v3(shr->refl, result, shr->combined);
+
+ if (shi->combinedflag & SCE_PASS_REFLECT)
+ copy_v3_v3(shr->combined, result);
+
+ }
+
+ /* and add emit and spec */
+ if (shi->combinedflag & SCE_PASS_EMIT)
+ add_v3_v3(shr->combined, shr->emit);
+ if (shi->combinedflag & SCE_PASS_SPEC)
+ add_v3_v3(shr->combined, shr->spec);
+
+
+ /* Last section of this function applies to shadeless colors too */
+finally_shadeless:
+
+ /* modulate by the object color */
+ if ((ma->shade_flag & MA_OBCOLOR) && shi->obr->ob) {
+ if (!(ma->sss_flag & MA_DIFF_SSS) || !sss_pass_done(&R, ma)) {
+ float obcol[4];
+
+ copy_v4_v4(obcol, shi->obr->ob->col);
+ CLAMP(obcol[3], 0.0f, 1.0f);
+
+ shr->combined[0] *= obcol[0];
+ shr->combined[1] *= obcol[1];
+ shr->combined[2] *= obcol[2];
+ if (shi->mode & MA_TRANSP) shr->alpha *= obcol[3];
+ }
+ }
+
+ shr->combined[3]= shr->alpha;
+}
+
+/* used for "Lamp Data" shader node */
+static float lamp_get_data_internal(ShadeInput *shi, GroupObject *go, float col[4], float lv[3], float *dist, float shadow[4])
+{
+ LampRen *lar = go->lampren;
+ float visifac, inp;
+
+ if (!lar
+ || ((lar->mode & LA_LAYER) && (lar->lay & shi->obi->lay) == 0)
+ || (lar->lay & shi->lay) == 0)
+ return 0.0f;
+
+ if (lar->mode & LA_TEXTURE)
+ do_lamp_tex(lar, lv, shi, col, LA_TEXTURE);
+
+ visifac = lamp_get_visibility(lar, shi->co, lv, dist);
+
+ if (visifac == 0.0f
+ || lar->type == LA_HEMI
+ || (lar->type != LA_SPOT && !(lar->mode & LA_SHAD_RAY))
+ || (R.r.scemode & R_BUTS_PREVIEW))
+ return visifac;
+
+ inp = dot_v3v3(shi->vn, lv);
+
+ if (inp > 0.0f) {
+ float shadfac[4];
+
+ shadow[0] = lar->shdwr;
+ shadow[1] = lar->shdwg;
+ shadow[2] = lar->shdwb;
+
+ if (lar->mode & LA_SHAD_TEX)
+ do_lamp_tex(lar, lv, shi, shadow, LA_SHAD_TEX);
+
+ if (R.r.mode & R_SHADOW) {
+ lamp_get_shadow(lar, shi, inp, shadfac, shi->depth);
+
+ shadow[0] = 1.0f - ((1.0f - shadfac[0] * shadfac[3]) * (1.0f - shadow[0]));
+ shadow[1] = 1.0f - ((1.0f - shadfac[1] * shadfac[3]) * (1.0f - shadow[1]));
+ shadow[2] = 1.0f - ((1.0f - shadfac[2] * shadfac[3]) * (1.0f - shadow[2]));
+ }
+ }
+
+ return visifac;
+}
+
+float RE_lamp_get_data(ShadeInput *shi, Object *lamp_obj, float col[4], float lv[3], float *dist, float shadow[4])
+{
+ col[0] = col[1] = col[2] = 0.0f;
+ col[3] = 1.0f;
+ copy_v3_v3(lv, shi->vn);
+ *dist = 1.0f;
+ shadow[0] = shadow[1] = shadow[2] = shadow[3] = 1.0f;
+
+ if (lamp_obj->type == OB_LAMP) {
+ GroupObject *go;
+ Lamp *lamp = (Lamp *)lamp_obj->data;
+
+ col[0] = lamp->r * lamp->energy;
+ col[1] = lamp->g * lamp->energy;
+ col[2] = lamp->b * lamp->energy;
+
+ if (R.r.scemode & R_BUTS_PREVIEW) {
+ for (go = R.lights.first; go; go = go->next) {
+ /* "Lamp.002" is main key light of material preview */
+ if (STREQ(go->ob->id.name + 2, "Lamp.002"))
+ return lamp_get_data_internal(shi, go, col, lv, dist, shadow);
+ }
+ return 0.0f;
+ }
+
+ if (shi->light_override) {
+ for (go = shi->light_override->gobject.first; go; go = go->next) {
+ if (go->ob == lamp_obj)
+ return lamp_get_data_internal(shi, go, col, lv, dist, shadow);
+ }
+ }
+
+ if (shi->mat && shi->mat->group) {
+ for (go = shi->mat->group->gobject.first; go; go = go->next) {
+ if (go->ob == lamp_obj)
+ return lamp_get_data_internal(shi, go, col, lv, dist, shadow);
+ }
+ }
+
+ for (go = R.lights.first; go; go = go->next) {
+ if (go->ob == lamp_obj)
+ return lamp_get_data_internal(shi, go, col, lv, dist, shadow);
+ }
+ }
+
+ return 0.0f;
+}
+
+const float (*RE_object_instance_get_matrix(struct ObjectInstanceRen *obi, int matrix_id))[4]
+{
+ if (obi) {
+ switch (matrix_id) {
+ case RE_OBJECT_INSTANCE_MATRIX_OB:
+ return (const float(*)[4])obi->obmat;
+ case RE_OBJECT_INSTANCE_MATRIX_OBINV:
+ return (const float(*)[4])obi->obinvmat;
+ case RE_OBJECT_INSTANCE_MATRIX_LOCALTOVIEW:
+ return (const float(*)[4])obi->localtoviewmat;
+ case RE_OBJECT_INSTANCE_MATRIX_LOCALTOVIEWINV:
+ return (const float(*)[4])obi->localtoviewinvmat;
+ }
+ }
+ return NULL;
+}
+
+float RE_object_instance_get_object_pass_index(struct ObjectInstanceRen *obi)
+{
+ return obi->ob->index;
+}
+
+float RE_object_instance_get_random_id(struct ObjectInstanceRen *obi)
+{
+ return obi->random_id;
+}
+
+const float (*RE_render_current_get_matrix(int matrix_id))[4]
+{
+ switch (matrix_id) {
+ case RE_VIEW_MATRIX:
+ return (const float(*)[4])R.viewmat;
+ case RE_VIEWINV_MATRIX:
+ return (const float(*)[4])R.viewinv;
+ }
+ return NULL;
+}
+
+float RE_fresnel_dielectric(float incoming[3], float normal[3], float eta)
+{
+ /* compute fresnel reflectance without explicitly computing
+ * the refracted direction */
+ float c = fabs(dot_v3v3(incoming, normal));
+ float g = eta * eta - 1.0 + c * c;
+ float result;
+
+ if (g > 0.0) {
+ g = sqrtf(g);
+ float A = (g - c) / (g + c);
+ float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
+ result = 0.5 * A * A * (1.0 + B * B);
+ }
+ else {
+ result = 1.0; /* TIR (no refracted component) */
+ }
+
+ return result;
+}
diff --git a/source/blender/render/intern/source/sss.c b/source/blender/render/intern/source/sss.c
new file mode 100644
index 00000000000..5919b8130d7
--- /dev/null
+++ b/source/blender/render/intern/source/sss.c
@@ -0,0 +1,1074 @@
+/*
+ * ***** 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) 2007 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/sss.c
+ * \ingroup render
+ */
+
+/* Possible Improvements:
+ * - add fresnel terms
+ * - adapt Rd table to scale, now with small scale there are a lot of misses?
+ * - possible interesting method: perform sss on all samples in the tree,
+ * and then use those values interpolated somehow later. can also do this
+ * filtering on demand for speed. since we are doing things in screen
+ * space now there is an exact correspondence
+ * - avoid duplicate shading (filtering points in advance, irradiance cache
+ * like lookup?)
+ * - lower resolution samples
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+
+/* external modules: */
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_memarena.h"
+
+#include "BLT_translation.h"
+
+
+#include "DNA_material_types.h"
+
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+
+/* this module */
+#include "render_types.h"
+#include "sss.h"
+
+/* Generic Multiple Scattering API */
+
+/* Relevant papers:
+ * [1] A Practical Model for Subsurface Light Transport
+ * [2] A Rapid Hierarchical Rendering Technique for Translucent Materials
+ * [3] Efficient Rendering of Local Subsurface Scattering
+ * [4] Implementing a skin BSSRDF (or several...)
+ */
+
+/* Defines */
+
+#define RD_TABLE_RANGE 100.0f
+#define RD_TABLE_RANGE_2 10000.0f
+#define RD_TABLE_SIZE 10000
+
+#define MAX_OCTREE_NODE_POINTS 8
+#define MAX_OCTREE_DEPTH 15
+
+/* Struct Definitions */
+
+struct ScatterSettings {
+ float eta; /* index of refraction */
+ float sigma_a; /* absorption coefficient */
+ float sigma_s_; /* reduced scattering coefficient */
+ float sigma_t_; /* reduced extinction coefficient */
+ float sigma; /* effective extinction coefficient */
+ float Fdr; /* diffuse fresnel reflectance */
+ float D; /* diffusion constant */
+ float A;
+ float alpha_; /* reduced albedo */
+ float zr; /* distance of virtual lightsource above surface */
+ float zv; /* distance of virtual lightsource below surface */
+ float ld; /* mean free path */
+ float ro; /* diffuse reflectance */
+ float color;
+ float invsigma_t_;
+ float frontweight;
+ float backweight;
+
+ float *tableRd; /* lookup table to avoid computing Rd */
+ float *tableRd2; /* lookup table to avoid computing Rd for bigger values */
+};
+
+typedef struct ScatterPoint {
+ float co[3];
+ float rad[3];
+ float area;
+ int back;
+} ScatterPoint;
+
+typedef struct ScatterNode {
+ float co[3];
+ float rad[3];
+ float backrad[3];
+ float area, backarea;
+
+ int totpoint;
+ ScatterPoint *points;
+
+ float split[3];
+ struct ScatterNode *child[8];
+} ScatterNode;
+
+struct ScatterTree {
+ MemArena *arena;
+
+ ScatterSettings *ss[3];
+ float error, scale;
+
+ ScatterNode *root;
+ ScatterPoint *points;
+ ScatterPoint **refpoints;
+ ScatterPoint **tmppoints;
+ int totpoint;
+ float min[3], max[3];
+};
+
+typedef struct ScatterResult {
+ float rad[3];
+ float backrad[3];
+ float rdsum[3];
+ float backrdsum[3];
+} ScatterResult;
+
+/* Functions for BSSRDF reparametrization in to more intuitive parameters,
+ * see [2] section 4 for more info. */
+
+static float f_Rd(float alpha_, float A, float ro)
+{
+ float sq;
+
+ sq = sqrtf(3.0f * (1.0f - alpha_));
+ return (alpha_/2.0f)*(1.0f + expf((-4.0f/3.0f)*A*sq))*expf(-sq) - ro;
+}
+
+static float compute_reduced_albedo(ScatterSettings *ss)
+{
+ const float tolerance= 1e-8;
+ const int max_iteration_count= 20;
+ float d, fsub, xn_1= 0.0f, xn= 1.0f, fxn, fxn_1;
+ int i;
+
+ /* use secant method to compute reduced albedo using Rd function inverse
+ * with a given reflectance */
+ fxn= f_Rd(xn, ss->A, ss->ro);
+ fxn_1= f_Rd(xn_1, ss->A, ss->ro);
+
+ for (i= 0; i < max_iteration_count; i++) {
+ fsub= (fxn - fxn_1);
+ if (fabsf(fsub) < tolerance)
+ break;
+ d= ((xn - xn_1)/fsub)*fxn;
+ if (fabsf(d) < tolerance)
+ break;
+
+ xn_1= xn;
+ fxn_1= fxn;
+ xn= xn - d;
+
+ if (xn > 1.0f) xn= 1.0f;
+ if (xn_1 > 1.0f) xn_1= 1.0f;
+
+ fxn= f_Rd(xn, ss->A, ss->ro);
+ }
+
+ /* avoid division by zero later */
+ if (xn <= 0.0f)
+ xn= 0.00001f;
+
+ return xn;
+}
+
+/* Exponential falloff functions */
+
+static float Rd_rsquare(ScatterSettings *ss, float rr)
+{
+ float sr, sv, Rdr, Rdv;
+
+ sr = sqrtf(rr + ss->zr * ss->zr);
+ sv = sqrtf(rr + ss->zv * ss->zv);
+
+ Rdr= ss->zr*(1.0f + ss->sigma*sr)*expf(-ss->sigma*sr)/(sr*sr*sr);
+ Rdv= ss->zv*(1.0f + ss->sigma*sv)*expf(-ss->sigma*sv)/(sv*sv*sv);
+
+ return /*ss->alpha_*/(1.0f/(4.0f*(float)M_PI))*(Rdr + Rdv);
+}
+
+static float Rd(ScatterSettings *ss, float r)
+{
+ return Rd_rsquare(ss, r*r);
+}
+
+/* table lookups for Rd. this avoids expensive exp calls. we use two
+ * separate tables as well for lower and higher numbers to improve
+ * precision, since the number are poorly distributed because we do
+ * a lookup with the squared distance for smaller distances, saving
+ * another sqrt. */
+
+static void approximate_Rd_rgb(ScatterSettings **ss, float rr, float *rd)
+{
+ float indexf, t, idxf;
+ int index;
+
+ if (rr > (RD_TABLE_RANGE_2 * RD_TABLE_RANGE_2)) {
+ /* pass */
+ }
+ else if (rr > RD_TABLE_RANGE) {
+ rr = sqrtf(rr);
+ indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE_2);
+ index= (int)indexf;
+ idxf= (float)index;
+ t= indexf - idxf;
+
+ if (index >= 0 && index < RD_TABLE_SIZE) {
+ rd[0]= (ss[0]->tableRd2[index]*(1-t) + ss[0]->tableRd2[index+1]*t);
+ rd[1]= (ss[1]->tableRd2[index]*(1-t) + ss[1]->tableRd2[index+1]*t);
+ rd[2]= (ss[2]->tableRd2[index]*(1-t) + ss[2]->tableRd2[index+1]*t);
+ return;
+ }
+ }
+ else {
+ indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE);
+ index= (int)indexf;
+ idxf= (float)index;
+ t= indexf - idxf;
+
+ if (index >= 0 && index < RD_TABLE_SIZE) {
+ rd[0]= (ss[0]->tableRd[index]*(1-t) + ss[0]->tableRd[index+1]*t);
+ rd[1]= (ss[1]->tableRd[index]*(1-t) + ss[1]->tableRd[index+1]*t);
+ rd[2]= (ss[2]->tableRd[index]*(1-t) + ss[2]->tableRd[index+1]*t);
+ return;
+ }
+ }
+
+ /* fallback to slow Rd computation */
+ rd[0]= Rd_rsquare(ss[0], rr);
+ rd[1]= Rd_rsquare(ss[1], rr);
+ rd[2]= Rd_rsquare(ss[2], rr);
+}
+
+static void build_Rd_table(ScatterSettings *ss)
+{
+ float r;
+ int i, size = RD_TABLE_SIZE+1;
+
+ ss->tableRd= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
+ ss->tableRd2= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
+
+ for (i= 0; i < size; i++) {
+ r= i*(RD_TABLE_RANGE/RD_TABLE_SIZE);
+#if 0
+ if (r < ss->invsigma_t_*ss->invsigma_t_) {
+ r= ss->invsigma_t_*ss->invsigma_t_;
+ }
+#endif
+ ss->tableRd[i]= Rd(ss, sqrtf(r));
+
+ r= i*(RD_TABLE_RANGE_2/RD_TABLE_SIZE);
+#if 0
+ if (r < ss->invsigma_t_) {
+ r= ss->invsigma_t_;
+ }
+#endif
+ ss->tableRd2[i]= Rd(ss, r);
+ }
+}
+
+ScatterSettings *scatter_settings_new(float refl, float radius, float ior, float reflfac, float frontweight, float backweight)
+{
+ ScatterSettings *ss;
+
+ ss= MEM_callocN(sizeof(ScatterSettings), "ScatterSettings");
+
+ /* see [1] and [3] for these formulas */
+ ss->eta= ior;
+ ss->Fdr= -1.440f/ior*ior + 0.710f/ior + 0.668f + 0.0636f*ior;
+ ss->A= (1.0f + ss->Fdr)/(1.0f - ss->Fdr);
+ ss->ld= radius;
+ ss->ro= min_ff(refl, 0.99f);
+ ss->color= ss->ro*reflfac + (1.0f-reflfac);
+
+ ss->alpha_= compute_reduced_albedo(ss);
+
+ ss->sigma= 1.0f/ss->ld;
+ ss->sigma_t_= ss->sigma/sqrtf(3.0f*(1.0f - ss->alpha_));
+ ss->sigma_s_= ss->alpha_*ss->sigma_t_;
+ ss->sigma_a= ss->sigma_t_ - ss->sigma_s_;
+
+ ss->D= 1.0f/(3.0f*ss->sigma_t_);
+
+ ss->zr= 1.0f/ss->sigma_t_;
+ ss->zv= ss->zr + 4.0f*ss->A*ss->D;
+
+ ss->invsigma_t_= 1.0f/ss->sigma_t_;
+
+ ss->frontweight= frontweight;
+ ss->backweight= backweight;
+
+ /* precompute a table of Rd values for quick lookup */
+ build_Rd_table(ss);
+
+ return ss;
+}
+
+void scatter_settings_free(ScatterSettings *ss)
+{
+ MEM_freeN(ss->tableRd);
+ MEM_freeN(ss->tableRd2);
+ MEM_freeN(ss);
+}
+
+/* Hierarchical method as in [2]. */
+
+/* traversal */
+
+#define SUBNODE_INDEX(co, split) \
+ ((co[0]>=split[0]) + (co[1]>=split[1])*2 + (co[2]>=split[2])*4)
+
+static void add_radiance(ScatterTree *tree, float *frontrad, float *backrad, float area, float backarea, float rr, ScatterResult *result)
+{
+ float rd[3], frontrd[3], backrd[3];
+
+ approximate_Rd_rgb(tree->ss, rr, rd);
+
+ if (frontrad && area) {
+ frontrd[0] = rd[0]*area;
+ frontrd[1] = rd[1]*area;
+ frontrd[2] = rd[2]*area;
+
+ result->rad[0] += frontrad[0]*frontrd[0];
+ result->rad[1] += frontrad[1]*frontrd[1];
+ result->rad[2] += frontrad[2]*frontrd[2];
+
+ result->rdsum[0] += frontrd[0];
+ result->rdsum[1] += frontrd[1];
+ result->rdsum[2] += frontrd[2];
+ }
+ if (backrad && backarea) {
+ backrd[0] = rd[0]*backarea;
+ backrd[1] = rd[1]*backarea;
+ backrd[2] = rd[2]*backarea;
+
+ result->backrad[0] += backrad[0]*backrd[0];
+ result->backrad[1] += backrad[1]*backrd[1];
+ result->backrad[2] += backrad[2]*backrd[2];
+
+ result->backrdsum[0] += backrd[0];
+ result->backrdsum[1] += backrd[1];
+ result->backrdsum[2] += backrd[2];
+ }
+}
+
+static void traverse_octree(ScatterTree *tree, ScatterNode *node, const float co[3], int self, ScatterResult *result)
+{
+ float sub[3], dist;
+ int i, index = 0;
+
+ if (node->totpoint > 0) {
+ /* leaf - add radiance from all samples */
+ for (i=0; i<node->totpoint; i++) {
+ ScatterPoint *p= &node->points[i];
+
+ sub_v3_v3v3(sub, co, p->co);
+ dist= dot_v3v3(sub, sub);
+
+ if (p->back)
+ add_radiance(tree, NULL, p->rad, 0.0f, p->area, dist, result);
+ else
+ add_radiance(tree, p->rad, NULL, p->area, 0.0f, dist, result);
+ }
+ }
+ else {
+ /* branch */
+ if (self)
+ index = SUBNODE_INDEX(co, node->split);
+
+ for (i=0; i<8; i++) {
+ if (node->child[i]) {
+ ScatterNode *subnode= node->child[i];
+
+ if (self && index == i) {
+ /* always traverse node containing the point */
+ traverse_octree(tree, subnode, co, 1, result);
+ }
+ else {
+ /* decide subnode traversal based on maximum solid angle */
+ sub_v3_v3v3(sub, co, subnode->co);
+ dist= dot_v3v3(sub, sub);
+
+ /* actually area/dist > error, but this avoids division */
+ if (subnode->area+subnode->backarea>tree->error*dist) {
+ traverse_octree(tree, subnode, co, 0, result);
+ }
+ else {
+ add_radiance(tree, subnode->rad, subnode->backrad,
+ subnode->area, subnode->backarea, dist, result);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void compute_radiance(ScatterTree *tree, const float co[3], float *rad)
+{
+ ScatterResult result;
+ float rdsum[3], backrad[3], backrdsum[3];
+
+ memset(&result, 0, sizeof(result));
+
+ traverse_octree(tree, tree->root, co, 1, &result);
+
+ /* the original paper doesn't do this, but we normalize over the
+ * sampled area and multiply with the reflectance. this is because
+ * our point samples are incomplete, there are no samples on parts
+ * of the mesh not visible from the camera. this can not only make
+ * it darker, but also lead to ugly color shifts */
+
+ mul_v3_fl(result.rad, tree->ss[0]->frontweight);
+ mul_v3_fl(result.backrad, tree->ss[0]->backweight);
+
+ copy_v3_v3(rad, result.rad);
+ add_v3_v3v3(backrad, result.rad, result.backrad);
+
+ copy_v3_v3(rdsum, result.rdsum);
+ add_v3_v3v3(backrdsum, result.rdsum, result.backrdsum);
+
+ if (rdsum[0] > 1e-16f) rad[0]= tree->ss[0]->color*rad[0]/rdsum[0];
+ if (rdsum[1] > 1e-16f) rad[1]= tree->ss[1]->color*rad[1]/rdsum[1];
+ if (rdsum[2] > 1e-16f) rad[2]= tree->ss[2]->color*rad[2]/rdsum[2];
+
+ if (backrdsum[0] > 1e-16f) backrad[0]= tree->ss[0]->color*backrad[0]/backrdsum[0];
+ if (backrdsum[1] > 1e-16f) backrad[1]= tree->ss[1]->color*backrad[1]/backrdsum[1];
+ if (backrdsum[2] > 1e-16f) backrad[2]= tree->ss[2]->color*backrad[2]/backrdsum[2];
+
+ rad[0]= MAX2(rad[0], backrad[0]);
+ rad[1]= MAX2(rad[1], backrad[1]);
+ rad[2]= MAX2(rad[2], backrad[2]);
+}
+
+/* building */
+
+static void sum_leaf_radiance(ScatterTree *UNUSED(tree), ScatterNode *node)
+{
+ ScatterPoint *p;
+ float rad, totrad= 0.0f, inv;
+ int i;
+
+ node->co[0]= node->co[1]= node->co[2]= 0.0;
+ node->rad[0]= node->rad[1]= node->rad[2]= 0.0;
+ node->backrad[0]= node->backrad[1]= node->backrad[2]= 0.0;
+
+ /* compute total rad, rad weighted average position,
+ * and total area */
+ for (i=0; i<node->totpoint; i++) {
+ p= &node->points[i];
+
+ rad= p->area*fabsf(p->rad[0] + p->rad[1] + p->rad[2]);
+ totrad += rad;
+
+ node->co[0] += rad*p->co[0];
+ node->co[1] += rad*p->co[1];
+ node->co[2] += rad*p->co[2];
+
+ if (p->back) {
+ node->backrad[0] += p->rad[0]*p->area;
+ node->backrad[1] += p->rad[1]*p->area;
+ node->backrad[2] += p->rad[2]*p->area;
+
+ node->backarea += p->area;
+ }
+ else {
+ node->rad[0] += p->rad[0]*p->area;
+ node->rad[1] += p->rad[1]*p->area;
+ node->rad[2] += p->rad[2]*p->area;
+
+ node->area += p->area;
+ }
+ }
+
+ if (node->area > 1e-16f) {
+ inv= 1.0f/node->area;
+ node->rad[0] *= inv;
+ node->rad[1] *= inv;
+ node->rad[2] *= inv;
+ }
+ if (node->backarea > 1e-16f) {
+ inv= 1.0f/node->backarea;
+ node->backrad[0] *= inv;
+ node->backrad[1] *= inv;
+ node->backrad[2] *= inv;
+ }
+
+ if (totrad > 1e-16f) {
+ inv= 1.0f/totrad;
+ node->co[0] *= inv;
+ node->co[1] *= inv;
+ node->co[2] *= inv;
+ }
+ else {
+ /* make sure that if radiance is 0.0f, we still have these points in
+ * the tree at a good position, they count for rdsum too */
+ for (i=0; i<node->totpoint; i++) {
+ p= &node->points[i];
+
+ node->co[0] += p->co[0];
+ node->co[1] += p->co[1];
+ node->co[2] += p->co[2];
+ }
+
+ node->co[0] /= node->totpoint;
+ node->co[1] /= node->totpoint;
+ node->co[2] /= node->totpoint;
+ }
+}
+
+static void sum_branch_radiance(ScatterTree *UNUSED(tree), ScatterNode *node)
+{
+ ScatterNode *subnode;
+ float rad, totrad= 0.0f, inv;
+ int i, totnode;
+
+ node->co[0]= node->co[1]= node->co[2]= 0.0;
+ node->rad[0]= node->rad[1]= node->rad[2]= 0.0;
+ node->backrad[0]= node->backrad[1]= node->backrad[2]= 0.0;
+
+ /* compute total rad, rad weighted average position,
+ * and total area */
+ for (i=0; i<8; i++) {
+ if (node->child[i] == NULL)
+ continue;
+
+ subnode= node->child[i];
+
+ rad= subnode->area*fabsf(subnode->rad[0] + subnode->rad[1] + subnode->rad[2]);
+ rad += subnode->backarea*fabsf(subnode->backrad[0] + subnode->backrad[1] + subnode->backrad[2]);
+ totrad += rad;
+
+ node->co[0] += rad*subnode->co[0];
+ node->co[1] += rad*subnode->co[1];
+ node->co[2] += rad*subnode->co[2];
+
+ node->rad[0] += subnode->rad[0]*subnode->area;
+ node->rad[1] += subnode->rad[1]*subnode->area;
+ node->rad[2] += subnode->rad[2]*subnode->area;
+
+ node->backrad[0] += subnode->backrad[0]*subnode->backarea;
+ node->backrad[1] += subnode->backrad[1]*subnode->backarea;
+ node->backrad[2] += subnode->backrad[2]*subnode->backarea;
+
+ node->area += subnode->area;
+ node->backarea += subnode->backarea;
+ }
+
+ if (node->area > 1e-16f) {
+ inv= 1.0f/node->area;
+ node->rad[0] *= inv;
+ node->rad[1] *= inv;
+ node->rad[2] *= inv;
+ }
+ if (node->backarea > 1e-16f) {
+ inv= 1.0f/node->backarea;
+ node->backrad[0] *= inv;
+ node->backrad[1] *= inv;
+ node->backrad[2] *= inv;
+ }
+
+ if (totrad > 1e-16f) {
+ inv= 1.0f/totrad;
+ node->co[0] *= inv;
+ node->co[1] *= inv;
+ node->co[2] *= inv;
+ }
+ else {
+ /* make sure that if radiance is 0.0f, we still have these points in
+ * the tree at a good position, they count for rdsum too */
+ totnode= 0;
+
+ for (i=0; i<8; i++) {
+ if (node->child[i]) {
+ subnode= node->child[i];
+
+ node->co[0] += subnode->co[0];
+ node->co[1] += subnode->co[1];
+ node->co[2] += subnode->co[2];
+
+ totnode++;
+ }
+ }
+
+ node->co[0] /= totnode;
+ node->co[1] /= totnode;
+ node->co[2] /= totnode;
+ }
+}
+
+static void sum_radiance(ScatterTree *tree, ScatterNode *node)
+{
+ if (node->totpoint > 0) {
+ sum_leaf_radiance(tree, node);
+ }
+ else {
+ int i;
+
+ for (i=0; i<8; i++)
+ if (node->child[i])
+ sum_radiance(tree, node->child[i]);
+
+ sum_branch_radiance(tree, node);
+ }
+}
+
+static void subnode_middle(int i, float *mid, float *subsize, float *submid)
+{
+ int x= i & 1, y= i & 2, z= i & 4;
+
+ submid[0]= mid[0] + ((x)? subsize[0]: -subsize[0]);
+ submid[1]= mid[1] + ((y)? subsize[1]: -subsize[1]);
+ submid[2]= mid[2] + ((z)? subsize[2]: -subsize[2]);
+}
+
+static void create_octree_node(ScatterTree *tree, ScatterNode *node, float *mid, float *size, ScatterPoint **refpoints, int depth)
+{
+ ScatterNode *subnode;
+ ScatterPoint **subrefpoints, **tmppoints= tree->tmppoints;
+ int index, nsize[8], noffset[8], i, subco, used_nodes, usedi;
+ float submid[3], subsize[3];
+
+ /* stopping condition */
+ if (node->totpoint <= MAX_OCTREE_NODE_POINTS || depth == MAX_OCTREE_DEPTH) {
+ for (i=0; i<node->totpoint; i++)
+ node->points[i]= *(refpoints[i]);
+
+ return;
+ }
+
+ subsize[0]= size[0]*0.5f;
+ subsize[1]= size[1]*0.5f;
+ subsize[2]= size[2]*0.5f;
+
+ node->split[0]= mid[0];
+ node->split[1]= mid[1];
+ node->split[2]= mid[2];
+
+ memset(nsize, 0, sizeof(nsize));
+ memset(noffset, 0, sizeof(noffset));
+
+ /* count points in subnodes */
+ for (i=0; i<node->totpoint; i++) {
+ index= SUBNODE_INDEX(refpoints[i]->co, node->split);
+ tmppoints[i]= refpoints[i];
+ nsize[index]++;
+ }
+
+ /* here we check if only one subnode is used. if this is the case, we don't
+ * create a new node, but rather call this function again, with different
+ * size and middle position for the same node. */
+ for (usedi=0, used_nodes=0, i=0; i<8; i++) {
+ if (nsize[i]) {
+ used_nodes++;
+ usedi = i;
+ }
+ if (i != 0)
+ noffset[i]= noffset[i-1]+nsize[i-1];
+ }
+
+ if (used_nodes <= 1) {
+ subnode_middle(usedi, mid, subsize, submid);
+ create_octree_node(tree, node, submid, subsize, refpoints, depth+1);
+ return;
+ }
+
+ /* reorder refpoints by subnode */
+ for (i=0; i<node->totpoint; i++) {
+ index= SUBNODE_INDEX(tmppoints[i]->co, node->split);
+ refpoints[noffset[index]]= tmppoints[i];
+ noffset[index]++;
+ }
+
+ /* create subnodes */
+ for (subco=0, i=0; i<8; subco+=nsize[i], i++) {
+ if (nsize[i] > 0) {
+ subnode= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
+ node->child[i]= subnode;
+ subnode->points= node->points + subco;
+ subnode->totpoint= nsize[i];
+ subrefpoints= refpoints + subco;
+
+ subnode_middle(i, mid, subsize, submid);
+
+ create_octree_node(tree, subnode, submid, subsize, subrefpoints,
+ depth+1);
+ }
+ else
+ node->child[i]= NULL;
+ }
+
+ node->points= NULL;
+ node->totpoint= 0;
+}
+
+/* public functions */
+
+ScatterTree *scatter_tree_new(ScatterSettings *ss[3], float scale, float error,
+ float (*co)[3], float (*color)[3], float *area, int totpoint)
+{
+ ScatterTree *tree;
+ ScatterPoint *points, **refpoints;
+ int i;
+
+ /* allocate tree */
+ tree= MEM_callocN(sizeof(ScatterTree), "ScatterTree");
+ tree->scale= scale;
+ tree->error= error;
+ tree->totpoint= totpoint;
+
+ tree->ss[0]= ss[0];
+ tree->ss[1]= ss[1];
+ tree->ss[2]= ss[2];
+
+ points = MEM_callocN(sizeof(ScatterPoint) * totpoint, "ScatterPoints");
+ refpoints = MEM_callocN(sizeof(ScatterPoint *) * totpoint, "ScatterRefPoints");
+
+ tree->points= points;
+ tree->refpoints= refpoints;
+
+ /* build points */
+ INIT_MINMAX(tree->min, tree->max);
+
+ for (i=0; i<totpoint; i++) {
+ copy_v3_v3(points[i].co, co[i]);
+ copy_v3_v3(points[i].rad, color[i]);
+ points[i].area= fabsf(area[i])/(tree->scale*tree->scale);
+ points[i].back= (area[i] < 0.0f);
+
+ mul_v3_fl(points[i].co, 1.0f / tree->scale);
+ minmax_v3v3_v3(tree->min, tree->max, points[i].co);
+
+ refpoints[i]= points + i;
+ }
+
+ return tree;
+}
+
+void scatter_tree_build(ScatterTree *tree)
+{
+ ScatterPoint *newpoints, **tmppoints;
+ float mid[3], size[3];
+ int totpoint= tree->totpoint;
+
+ newpoints = MEM_callocN(sizeof(ScatterPoint) * totpoint, "ScatterPoints");
+ tmppoints = MEM_callocN(sizeof(ScatterPoint *) * totpoint, "ScatterTmpPoints");
+ tree->tmppoints= tmppoints;
+
+ tree->arena= BLI_memarena_new(0x8000 * sizeof(ScatterNode), "sss tree arena");
+ BLI_memarena_use_calloc(tree->arena);
+
+ /* build tree */
+ tree->root= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
+ tree->root->points= newpoints;
+ tree->root->totpoint= totpoint;
+
+ mid[0]= (tree->min[0]+tree->max[0])*0.5f;
+ mid[1]= (tree->min[1]+tree->max[1])*0.5f;
+ mid[2]= (tree->min[2]+tree->max[2])*0.5f;
+
+ size[0]= (tree->max[0]-tree->min[0])*0.5f;
+ size[1]= (tree->max[1]-tree->min[1])*0.5f;
+ size[2]= (tree->max[2]-tree->min[2])*0.5f;
+
+ create_octree_node(tree, tree->root, mid, size, tree->refpoints, 0);
+
+ MEM_freeN(tree->points);
+ MEM_freeN(tree->refpoints);
+ MEM_freeN(tree->tmppoints);
+ tree->refpoints= NULL;
+ tree->tmppoints= NULL;
+ tree->points= newpoints;
+
+ /* sum radiance at nodes */
+ sum_radiance(tree, tree->root);
+}
+
+void scatter_tree_sample(ScatterTree *tree, const float co[3], float color[3])
+{
+ float sco[3];
+
+ copy_v3_v3(sco, co);
+ mul_v3_fl(sco, 1.0f / tree->scale);
+
+ compute_radiance(tree, sco, color);
+}
+
+void scatter_tree_free(ScatterTree *tree)
+{
+ if (tree->arena) BLI_memarena_free(tree->arena);
+ if (tree->points) MEM_freeN(tree->points);
+ if (tree->refpoints) MEM_freeN(tree->refpoints);
+
+ MEM_freeN(tree);
+}
+
+/* Internal Renderer API */
+
+/* sss tree building */
+
+typedef struct SSSData {
+ ScatterTree *tree;
+ ScatterSettings *ss[3];
+} SSSData;
+
+typedef struct SSSPoints {
+ struct SSSPoints *next, *prev;
+
+ float (*co)[3];
+ float (*color)[3];
+ float *area;
+ int totpoint;
+} SSSPoints;
+
+static void sss_create_tree_mat(Render *re, Material *mat)
+{
+ SSSPoints *p;
+ RenderResult *rr;
+ ListBase points;
+ float (*co)[3] = NULL, (*color)[3] = NULL, *area = NULL;
+ int totpoint = 0, osa, osaflag, frsflag, partsdone;
+
+ if (re->test_break(re->tbh))
+ return;
+
+ points.first= points.last= NULL;
+
+ /* TODO: this is getting a bit ugly, copying all those variables and
+ * setting them back, maybe we need to create our own Render? */
+
+ /* do SSS preprocessing render */
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+ rr= re->result;
+ osa= re->osa;
+ osaflag= re->r.mode & R_OSA;
+ frsflag= re->r.mode & R_EDGE_FRS;
+ partsdone= re->i.partsdone;
+
+ re->osa= 0;
+ re->r.mode &= ~(R_OSA | R_EDGE_FRS);
+ re->sss_points= &points;
+ re->sss_mat= mat;
+ re->i.partsdone = 0;
+
+ if (!(re->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW)))
+ re->result= NULL;
+ BLI_rw_mutex_unlock(&re->resultmutex);
+
+ RE_TileProcessor(re);
+
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+ if (!(re->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))) {
+ RE_FreeRenderResult(re->result);
+ re->result= rr;
+ }
+ BLI_rw_mutex_unlock(&re->resultmutex);
+
+ re->i.partsdone= partsdone;
+ re->sss_mat= NULL;
+ re->sss_points= NULL;
+ re->osa= osa;
+ if (osaflag) re->r.mode |= R_OSA;
+ if (frsflag) re->r.mode |= R_EDGE_FRS;
+
+ /* no points? no tree */
+ if (!points.first)
+ return;
+
+ /* merge points together into a single buffer */
+ if (!re->test_break(re->tbh)) {
+ for (totpoint=0, p=points.first; p; p=p->next)
+ totpoint += p->totpoint;
+
+ co= MEM_mallocN(sizeof(*co)*totpoint, "SSSCo");
+ color= MEM_mallocN(sizeof(*color)*totpoint, "SSSColor");
+ area= MEM_mallocN(sizeof(*area)*totpoint, "SSSArea");
+
+ for (totpoint=0, p=points.first; p; p=p->next) {
+ memcpy(co+totpoint, p->co, sizeof(*co)*p->totpoint);
+ memcpy(color+totpoint, p->color, sizeof(*color)*p->totpoint);
+ memcpy(area+totpoint, p->area, sizeof(*area)*p->totpoint);
+ totpoint += p->totpoint;
+ }
+ }
+
+ /* free points */
+ for (p=points.first; p; p=p->next) {
+ MEM_freeN(p->co);
+ MEM_freeN(p->color);
+ MEM_freeN(p->area);
+ }
+ BLI_freelistN(&points);
+
+ /* build tree */
+ if (!re->test_break(re->tbh)) {
+ SSSData *sss= MEM_callocN(sizeof(*sss), "SSSData");
+ float ior= mat->sss_ior, cfac= mat->sss_colfac;
+ const float *radius = mat->sss_radius;
+ float fw= mat->sss_front, bw= mat->sss_back;
+ float error = mat->sss_error;
+
+ error= get_render_aosss_error(&re->r, error);
+ if ((re->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW)) && error < 0.5f)
+ error= 0.5f;
+
+ sss->ss[0]= scatter_settings_new(mat->sss_col[0], radius[0], ior, cfac, fw, bw);
+ sss->ss[1]= scatter_settings_new(mat->sss_col[1], radius[1], ior, cfac, fw, bw);
+ sss->ss[2]= scatter_settings_new(mat->sss_col[2], radius[2], ior, cfac, fw, bw);
+ sss->tree= scatter_tree_new(sss->ss, mat->sss_scale, error,
+ co, color, area, totpoint);
+
+ MEM_freeN(co);
+ MEM_freeN(color);
+ MEM_freeN(area);
+
+ scatter_tree_build(sss->tree);
+
+ BLI_ghash_insert(re->sss_hash, mat, sss);
+ }
+ else {
+ if (co) MEM_freeN(co);
+ if (color) MEM_freeN(color);
+ if (area) MEM_freeN(area);
+ }
+}
+
+void sss_add_points(Render *re, float (*co)[3], float (*color)[3], float *area, int totpoint)
+{
+ SSSPoints *p;
+
+ if (totpoint > 0) {
+ p= MEM_callocN(sizeof(SSSPoints), "SSSPoints");
+
+ p->co= co;
+ p->color= color;
+ p->area= area;
+ p->totpoint= totpoint;
+
+ BLI_thread_lock(LOCK_CUSTOM1);
+ BLI_addtail(re->sss_points, p);
+ BLI_thread_unlock(LOCK_CUSTOM1);
+ }
+}
+
+static void sss_free_tree(SSSData *sss)
+{
+ scatter_tree_free(sss->tree);
+ scatter_settings_free(sss->ss[0]);
+ scatter_settings_free(sss->ss[1]);
+ scatter_settings_free(sss->ss[2]);
+ MEM_freeN(sss);
+}
+
+/* public functions */
+
+void make_sss_tree(Render *re)
+{
+ Material *mat;
+ bool infostr_set = false;
+ const char *prevstr = NULL;
+
+ free_sss(re);
+
+ re->sss_hash= BLI_ghash_ptr_new("make_sss_tree gh");
+
+ re->stats_draw(re->sdh, &re->i);
+
+ for (mat= re->main->mat.first; mat; mat= mat->id.next) {
+ if (mat->id.us && (mat->flag & MA_IS_USED) && (mat->sss_flag & MA_DIFF_SSS)) {
+ if (!infostr_set) {
+ prevstr = re->i.infostr;
+ re->i.infostr = IFACE_("SSS preprocessing");
+ infostr_set = true;
+ }
+
+ sss_create_tree_mat(re, mat);
+ }
+ }
+
+ /* XXX preview exception */
+ /* localizing preview render data is not fun for node trees :( */
+ if (re->main!=G.main) {
+ for (mat= G.main->mat.first; mat; mat= mat->id.next) {
+ if (mat->id.us && (mat->flag & MA_IS_USED) && (mat->sss_flag & MA_DIFF_SSS)) {
+ if (!infostr_set) {
+ prevstr = re->i.infostr;
+ re->i.infostr = IFACE_("SSS preprocessing");
+ infostr_set = true;
+ }
+
+ sss_create_tree_mat(re, mat);
+ }
+ }
+ }
+
+ if (infostr_set)
+ re->i.infostr = prevstr;
+}
+
+void free_sss(Render *re)
+{
+ if (re->sss_hash) {
+ GHashIterator gh_iter;
+
+ GHASH_ITER (gh_iter, re->sss_hash) {
+ sss_free_tree(BLI_ghashIterator_getValue(&gh_iter));
+ }
+
+ BLI_ghash_free(re->sss_hash, NULL, NULL);
+ re->sss_hash= NULL;
+ }
+}
+
+int sample_sss(Render *re, Material *mat, const float co[3], float color[3])
+{
+ if (re->sss_hash) {
+ SSSData *sss= BLI_ghash_lookup(re->sss_hash, mat);
+
+ if (sss) {
+ scatter_tree_sample(sss->tree, co, color);
+ return 1;
+ }
+ else {
+ color[0]= 0.0f;
+ color[1]= 0.0f;
+ color[2]= 0.0f;
+ }
+ }
+
+ return 0;
+}
+
+int sss_pass_done(struct Render *re, struct Material *mat)
+{
+ return ((re->flag & R_BAKING) || !(re->r.mode & R_SSS) || (re->sss_hash && BLI_ghash_lookup(re->sss_hash, mat)));
+}
+
diff --git a/source/blender/render/intern/source/strand.c b/source/blender/render/intern/source/strand.c
new file mode 100644
index 00000000000..5fde688481a
--- /dev/null
+++ b/source/blender/render/intern/source/strand.c
@@ -0,0 +1,1069 @@
+/*
+ * ***** 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.
+ *
+ * Contributors: Brecht Van Lommel.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/strand.c
+ * \ingroup render
+ */
+
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_key_types.h"
+#include "DNA_material_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_memarena.h"
+#include "BLI_rand.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_key.h"
+
+
+#include "render_types.h"
+#include "rendercore.h"
+#include "renderdatabase.h"
+#include "shading.h"
+#include "strand.h"
+#include "zbuf.h"
+
+/* *************** */
+
+static float strand_eval_width(Material *ma, float strandco)
+{
+ float fac;
+
+ strandco= 0.5f*(strandco + 1.0f);
+
+ if (ma->strand_ease!=0.0f) {
+ if (ma->strand_ease<0.0f)
+ fac= pow(strandco, 1.0f+ma->strand_ease);
+ else
+ fac= pow(strandco, 1.0f/(1.0f-ma->strand_ease));
+ }
+ else fac= strandco;
+
+ return ((1.0f-fac)*ma->strand_sta + (fac)*ma->strand_end);
+}
+
+void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint)
+{
+ Material *ma;
+ StrandBuffer *strandbuf;
+ const float *simplify;
+ float p[4][3], data[4], cross[3], w, dx, dy, t;
+ int type;
+
+ strandbuf= sseg->buffer;
+ ma= sseg->buffer->ma;
+ t= spoint->t;
+ type= (strandbuf->flag & R_STRAND_BSPLINE)? KEY_BSPLINE: KEY_CARDINAL;
+
+ copy_v3_v3(p[0], sseg->v[0]->co);
+ copy_v3_v3(p[1], sseg->v[1]->co);
+ copy_v3_v3(p[2], sseg->v[2]->co);
+ copy_v3_v3(p[3], sseg->v[3]->co);
+
+ if (sseg->obi->flag & R_TRANSFORMED) {
+ mul_m4_v3(sseg->obi->mat, p[0]);
+ mul_m4_v3(sseg->obi->mat, p[1]);
+ mul_m4_v3(sseg->obi->mat, p[2]);
+ mul_m4_v3(sseg->obi->mat, p[3]);
+ }
+
+ if (t == 0.0f) {
+ copy_v3_v3(spoint->co, p[1]);
+ spoint->strandco= sseg->v[1]->strandco;
+
+ spoint->dtstrandco= (sseg->v[2]->strandco - sseg->v[0]->strandco);
+ if (sseg->v[0] != sseg->v[1])
+ spoint->dtstrandco *= 0.5f;
+ }
+ else if (t == 1.0f) {
+ copy_v3_v3(spoint->co, p[2]);
+ spoint->strandco= sseg->v[2]->strandco;
+
+ spoint->dtstrandco= (sseg->v[3]->strandco - sseg->v[1]->strandco);
+ if (sseg->v[3] != sseg->v[2])
+ spoint->dtstrandco *= 0.5f;
+ }
+ else {
+ key_curve_position_weights(t, data, type);
+ spoint->co[0]= data[0]*p[0][0] + data[1]*p[1][0] + data[2]*p[2][0] + data[3]*p[3][0];
+ spoint->co[1]= data[0]*p[0][1] + data[1]*p[1][1] + data[2]*p[2][1] + data[3]*p[3][1];
+ spoint->co[2]= data[0]*p[0][2] + data[1]*p[1][2] + data[2]*p[2][2] + data[3]*p[3][2];
+ spoint->strandco= (1.0f-t)*sseg->v[1]->strandco + t*sseg->v[2]->strandco;
+ }
+
+ key_curve_tangent_weights(t, data, type);
+ spoint->dtco[0]= data[0]*p[0][0] + data[1]*p[1][0] + data[2]*p[2][0] + data[3]*p[3][0];
+ spoint->dtco[1]= data[0]*p[0][1] + data[1]*p[1][1] + data[2]*p[2][1] + data[3]*p[3][1];
+ spoint->dtco[2]= data[0]*p[0][2] + data[1]*p[1][2] + data[2]*p[2][2] + data[3]*p[3][2];
+
+ normalize_v3_v3(spoint->tan, spoint->dtco);
+ normalize_v3_v3(spoint->nor, spoint->co);
+ negate_v3(spoint->nor);
+
+ spoint->width= strand_eval_width(ma, spoint->strandco);
+
+ /* simplification */
+ simplify= RE_strandren_get_simplify(strandbuf->obr, sseg->strand, 0);
+ spoint->alpha= (simplify)? simplify[1]: 1.0f;
+
+ /* outer points */
+ cross_v3_v3v3(cross, spoint->co, spoint->tan);
+
+ w= spoint->co[2]*strandbuf->winmat[2][3] + strandbuf->winmat[3][3];
+ dx= strandbuf->winx*cross[0]*strandbuf->winmat[0][0]/w;
+ dy= strandbuf->winy*cross[1]*strandbuf->winmat[1][1]/w;
+ w = sqrtf(dx * dx + dy * dy);
+
+ if (w > 0.0f) {
+ if (strandbuf->flag & R_STRAND_B_UNITS) {
+ const float crosslen= len_v3(cross);
+ w= 2.0f*crosslen*strandbuf->minwidth/w;
+
+ if (spoint->width < w) {
+ spoint->alpha= spoint->width/w;
+ spoint->width= w;
+ }
+
+ if (simplify)
+ /* squared because we only change width, not length */
+ spoint->width *= simplify[0]*simplify[0];
+
+ mul_v3_fl(cross, spoint->width*0.5f/crosslen);
+ }
+ else
+ mul_v3_fl(cross, spoint->width/w);
+ }
+
+ sub_v3_v3v3(spoint->co1, spoint->co, cross);
+ add_v3_v3v3(spoint->co2, spoint->co, cross);
+
+ copy_v3_v3(spoint->dsco, cross);
+}
+
+/* *************** */
+
+static void interpolate_vec1(float *v1, float *v2, float t, float negt, float *v)
+{
+ v[0]= negt*v1[0] + t*v2[0];
+}
+
+static void interpolate_vec3(float *v1, float *v2, float t, float negt, float *v)
+{
+ v[0]= negt*v1[0] + t*v2[0];
+ v[1]= negt*v1[1] + t*v2[1];
+ v[2]= negt*v1[2] + t*v2[2];
+}
+
+static void interpolate_vec4(float *v1, float *v2, float t, float negt, float *v)
+{
+ v[0]= negt*v1[0] + t*v2[0];
+ v[1]= negt*v1[1] + t*v2[1];
+ v[2]= negt*v1[2] + t*v2[2];
+ v[3]= negt*v1[3] + t*v2[3];
+}
+
+static void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float t, ShadeResult *shr, int addpassflag)
+{
+ float negt= 1.0f - t;
+
+ interpolate_vec4(shr1->combined, shr2->combined, t, negt, shr->combined);
+
+ if (addpassflag & SCE_PASS_VECTOR) {
+ interpolate_vec4(shr1->winspeed, shr2->winspeed, t, negt, shr->winspeed);
+ }
+ /* optim... */
+ if (addpassflag & ~(SCE_PASS_VECTOR)) {
+ if (addpassflag & SCE_PASS_Z)
+ interpolate_vec1(&shr1->z, &shr2->z, t, negt, &shr->z);
+ if (addpassflag & SCE_PASS_RGBA)
+ interpolate_vec4(shr1->col, shr2->col, t, negt, shr->col);
+ if (addpassflag & SCE_PASS_NORMAL) {
+ interpolate_vec3(shr1->nor, shr2->nor, t, negt, shr->nor);
+ normalize_v3(shr->nor);
+ }
+ if (addpassflag & SCE_PASS_EMIT)
+ interpolate_vec3(shr1->emit, shr2->emit, t, negt, shr->emit);
+ if (addpassflag & SCE_PASS_DIFFUSE) {
+ interpolate_vec3(shr1->diff, shr2->diff, t, negt, shr->diff);
+ interpolate_vec3(shr1->diffshad, shr2->diffshad, t, negt, shr->diffshad);
+ }
+ if (addpassflag & SCE_PASS_SPEC)
+ interpolate_vec3(shr1->spec, shr2->spec, t, negt, shr->spec);
+ if (addpassflag & SCE_PASS_SHADOW)
+ interpolate_vec3(shr1->shad, shr2->shad, t, negt, shr->shad);
+ if (addpassflag & SCE_PASS_AO)
+ interpolate_vec3(shr1->ao, shr2->ao, t, negt, shr->ao);
+ if (addpassflag & SCE_PASS_ENVIRONMENT)
+ interpolate_vec3(shr1->env, shr2->env, t, negt, shr->env);
+ if (addpassflag & SCE_PASS_INDIRECT)
+ interpolate_vec3(shr1->indirect, shr2->indirect, t, negt, shr->indirect);
+ if (addpassflag & SCE_PASS_REFLECT)
+ interpolate_vec3(shr1->refl, shr2->refl, t, negt, shr->refl);
+ if (addpassflag & SCE_PASS_REFRACT)
+ interpolate_vec3(shr1->refr, shr2->refr, t, negt, shr->refr);
+ if (addpassflag & SCE_PASS_MIST)
+ interpolate_vec1(&shr1->mist, &shr2->mist, t, negt, &shr->mist);
+ }
+}
+
+static void strand_apply_shaderesult_alpha(ShadeResult *shr, float alpha)
+{
+ if (alpha < 1.0f) {
+ shr->combined[0] *= alpha;
+ shr->combined[1] *= alpha;
+ shr->combined[2] *= alpha;
+ shr->combined[3] *= alpha;
+
+ shr->col[0] *= alpha;
+ shr->col[1] *= alpha;
+ shr->col[2] *= alpha;
+ shr->col[3] *= alpha;
+
+ shr->alpha *= alpha;
+ }
+}
+
+static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandVert *svert, StrandPoint *spoint)
+{
+ ShadeInput *shi= ssamp->shi;
+ ShadeResult *shr= ssamp->shr;
+ VlakRen vlr;
+ int seed;
+
+ memset(&vlr, 0, sizeof(vlr));
+ vlr.flag= R_SMOOTH;
+ if (sseg->buffer->ma->mode & MA_TANGENT_STR)
+ vlr.flag |= R_TANGENT;
+
+ shi->vlr= &vlr;
+ shi->v1= NULL;
+ shi->v2= NULL;
+ shi->v3= NULL;
+ shi->strand= sseg->strand;
+ shi->obi= sseg->obi;
+ shi->obr= sseg->obi->obr;
+
+ /* cache for shadow */
+ shi->samplenr= re->shadowsamplenr[shi->thread]++;
+
+ /* all samples */
+ shi->mask= 0xFFFF;
+
+ /* seed RNG for consistent results across tiles */
+ seed = shi->strand->index + (svert - shi->strand->vert);
+ BLI_thread_srandom(shi->thread, seed);
+
+ shade_input_set_strand(shi, sseg->strand, spoint);
+ shade_input_set_strand_texco(shi, sseg->strand, sseg->v[1], spoint);
+
+ /* init material vars */
+ shade_input_init_material(shi);
+
+ /* shade */
+ shade_samples_do_AO(ssamp);
+ shade_input_do_shade(shi, shr);
+
+ /* apply simplification */
+ strand_apply_shaderesult_alpha(shr, spoint->alpha);
+
+ /* include lamphalos for strand, since halo layer was added already */
+ if (re->flag & R_LAMPHALO)
+ if (shi->layflag & SCE_LAY_HALO)
+ renderspothalo(shi, shr->combined, shr->combined[3]);
+
+ shi->strand= NULL;
+}
+
+/* *************** */
+
+struct StrandShadeCache {
+ GHash *resulthash;
+ GHash *refcounthash;
+ MemArena *memarena;
+};
+
+typedef struct StrandCacheEntry {
+ GHashPair pair;
+ ShadeResult shr;
+} StrandCacheEntry;
+
+StrandShadeCache *strand_shade_cache_create(void)
+{
+ StrandShadeCache *cache;
+
+ cache= MEM_callocN(sizeof(StrandShadeCache), "StrandShadeCache");
+ cache->resulthash= BLI_ghash_pair_new("strand_shade_cache_create1 gh");
+ cache->refcounthash= BLI_ghash_pair_new("strand_shade_cache_create2 gh");
+ cache->memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "strand shade cache arena");
+
+ return cache;
+}
+
+void strand_shade_cache_free(StrandShadeCache *cache)
+{
+ BLI_ghash_free(cache->refcounthash, NULL, NULL);
+ BLI_ghash_free(cache->resulthash, MEM_freeN, NULL);
+ BLI_memarena_free(cache->memarena);
+ MEM_freeN(cache);
+}
+
+static GHashPair strand_shade_hash_pair(ObjectInstanceRen *obi, StrandVert *svert)
+{
+ GHashPair pair = {obi, svert};
+ return pair;
+}
+
+static void strand_shade_get(Render *re, StrandShadeCache *cache, ShadeSample *ssamp, StrandSegment *sseg, StrandVert *svert)
+{
+ StrandCacheEntry *entry;
+ StrandPoint p;
+ int *refcount;
+ GHashPair pair = strand_shade_hash_pair(sseg->obi, svert);
+
+ entry= BLI_ghash_lookup(cache->resulthash, &pair);
+ refcount= BLI_ghash_lookup(cache->refcounthash, &pair);
+
+ if (!entry) {
+ /* not shaded yet, shade and insert into hash */
+ p.t= (sseg->v[1] == svert)? 0.0f: 1.0f;
+ strand_eval_point(sseg, &p);
+ strand_shade_point(re, ssamp, sseg, svert, &p);
+
+ entry= MEM_callocN(sizeof(StrandCacheEntry), "StrandCacheEntry");
+ entry->pair = pair;
+ entry->shr = ssamp->shr[0];
+ BLI_ghash_insert(cache->resulthash, entry, entry);
+ }
+ else
+ /* already shaded, just copy previous result from hash */
+ ssamp->shr[0]= entry->shr;
+
+ /* lower reference count and remove if not needed anymore by any samples */
+ (*refcount)--;
+ if (*refcount == 0) {
+ BLI_ghash_remove(cache->resulthash, &pair, MEM_freeN, NULL);
+ BLI_ghash_remove(cache->refcounthash, &pair, NULL, NULL);
+ }
+}
+
+void strand_shade_segment(Render *re, StrandShadeCache *cache, StrandSegment *sseg, ShadeSample *ssamp, float t, float s, int addpassflag)
+{
+ ShadeResult shr1, shr2;
+
+ /* get shading for two endpoints and interpolate */
+ strand_shade_get(re, cache, ssamp, sseg, sseg->v[1]);
+ shr1= ssamp->shr[0];
+ strand_shade_get(re, cache, ssamp, sseg, sseg->v[2]);
+ shr2= ssamp->shr[0];
+
+ interpolate_shade_result(&shr1, &shr2, t, ssamp->shr, addpassflag);
+
+ /* apply alpha along width */
+ if (sseg->buffer->widthfade != -1.0f) {
+ s = 1.0f - powf(fabsf(s), sseg->buffer->widthfade);
+
+ strand_apply_shaderesult_alpha(ssamp->shr, s);
+ }
+}
+
+void strand_shade_unref(StrandShadeCache *cache, ObjectInstanceRen *obi, StrandVert *svert)
+{
+ GHashPair pair = strand_shade_hash_pair(obi, svert);
+ int *refcount;
+
+ /* lower reference count and remove if not needed anymore by any samples */
+ refcount= BLI_ghash_lookup(cache->refcounthash, &pair);
+
+ (*refcount)--;
+ if (*refcount == 0) {
+ BLI_ghash_remove(cache->resulthash, &pair, MEM_freeN, NULL);
+ BLI_ghash_remove(cache->refcounthash, &pair, NULL, NULL);
+ }
+}
+
+static void strand_shade_refcount(StrandShadeCache *cache, StrandSegment *sseg, StrandVert *svert)
+{
+ GHashPair pair = strand_shade_hash_pair(sseg->obi, svert);
+ GHashPair *key;
+ int *refcount= BLI_ghash_lookup(cache->refcounthash, &pair);
+
+ if (!refcount) {
+ key= BLI_memarena_alloc(cache->memarena, sizeof(GHashPair));
+ *key = pair;
+ refcount= BLI_memarena_alloc(cache->memarena, sizeof(int));
+ *refcount= 1;
+ BLI_ghash_insert(cache->refcounthash, key, refcount);
+ }
+ else
+ (*refcount)++;
+}
+
+/* *************** */
+
+typedef struct StrandPart {
+ Render *re;
+ ZSpan *zspan;
+
+ APixstrand *apixbuf;
+ int *totapixbuf;
+ int *rectz;
+ int *rectmask;
+ intptr_t *rectdaps;
+ int rectx, recty;
+ int sample;
+ int shadow;
+ float (*jit)[2];
+ int samples;
+
+ StrandSegment *segment;
+ float t[3], s[3];
+
+ StrandShadeCache *cache;
+} StrandPart;
+
+typedef struct StrandSortSegment {
+ struct StrandSortSegment *next;
+ int obi, strand, segment;
+ float z;
+} StrandSortSegment;
+
+static int compare_strand_segment(const void *poin1, const void *poin2)
+{
+ const StrandSortSegment *seg1= (const StrandSortSegment*)poin1;
+ const StrandSortSegment *seg2= (const StrandSortSegment*)poin2;
+
+ if (seg1->z < seg2->z)
+ return -1;
+ else if (seg1->z == seg2->z)
+ return 0;
+ else
+ return 1;
+}
+
+static void do_strand_point_project(float winmat[4][4], ZSpan *zspan, float *co, float *hoco, float *zco)
+{
+ projectvert(co, winmat, hoco);
+ hoco_to_zco(zspan, zco, hoco);
+}
+
+static void strand_project_point(float winmat[4][4], float winx, float winy, StrandPoint *spoint)
+{
+ float div;
+
+ projectvert(spoint->co, winmat, spoint->hoco);
+
+ div= 1.0f/spoint->hoco[3];
+ spoint->x= spoint->hoco[0]*div*winx*0.5f;
+ spoint->y= spoint->hoco[1]*div*winy*0.5f;
+}
+
+static APixstrand *addpsmainAstrand(ListBase *lb)
+{
+ APixstrMain *psm;
+
+ psm= MEM_mallocN(sizeof(APixstrMain), "addpsmainA");
+ BLI_addtail(lb, psm);
+ psm->ps = MEM_callocN(4096 * sizeof(APixstrand), "pixstr");
+
+ return psm->ps;
+}
+
+static APixstrand *addpsAstrand(ZSpan *zspan)
+{
+ /* make new PS */
+ if (zspan->apstrandmcounter==0) {
+ zspan->curpstrand= addpsmainAstrand(zspan->apsmbase);
+ zspan->apstrandmcounter= 4095;
+ }
+ else {
+ zspan->curpstrand++;
+ zspan->apstrandmcounter--;
+ }
+ return zspan->curpstrand;
+}
+
+#define MAX_ZROW 2000
+
+static void do_strand_fillac(void *handle, int x, int y, float u, float v, float z)
+{
+ StrandPart *spart= (StrandPart *)handle;
+ StrandShadeCache *cache= spart->cache;
+ StrandSegment *sseg= spart->segment;
+ APixstrand *apn, *apnew;
+ float t, s;
+ int offset, mask, obi, strnr, seg, zverg, bufferz, maskz=0;
+
+ offset = y*spart->rectx + x;
+ obi= sseg->obi - spart->re->objectinstance;
+ strnr= sseg->strand->index + 1;
+ seg= sseg->v[1] - sseg->strand->vert;
+ mask= (1<<spart->sample);
+
+ /* check against solid z-buffer */
+ zverg= (int)z;
+
+ if (spart->rectdaps) {
+ /* find the z of the sample */
+ PixStr *ps;
+ intptr_t *rd= spart->rectdaps + offset;
+
+ bufferz= 0x7FFFFFFF;
+ if (spart->rectmask) maskz= 0x7FFFFFFF;
+
+ if (*rd) {
+ for (ps= (PixStr *)(*rd); ps; ps= ps->next) {
+ if (mask & ps->mask) {
+ bufferz= ps->z;
+ if (spart->rectmask)
+ maskz= ps->maskz;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ bufferz= (spart->rectz)? spart->rectz[offset]: 0x7FFFFFFF;
+ if (spart->rectmask)
+ maskz= spart->rectmask[offset];
+ }
+
+#define CHECK_ADD(n) \
+ if (apn->p[n]==strnr && apn->obi[n]==obi && apn->seg[n]==seg) \
+ { if (!(apn->mask[n] & mask)) { apn->mask[n] |= mask; apn->v[n] += t; apn->u[n] += s; } break; } (void)0
+#define CHECK_ASSIGN(n) \
+ if (apn->p[n]==0) \
+ {apn->obi[n]= obi; apn->p[n]= strnr; apn->z[n]= zverg; apn->mask[n]= mask; apn->v[n]= t; apn->u[n]= s; apn->seg[n]= seg; break; } (void)0
+
+ /* add to pixel list */
+ if (zverg < bufferz && (spart->totapixbuf[offset] < MAX_ZROW)) {
+ if (!spart->rectmask || zverg > maskz) {
+ t = u * spart->t[0] + v * spart->t[1] + (1.0f - u - v) * spart->t[2];
+ s = fabsf(u * spart->s[0] + v * spart->s[1] + (1.0f - u - v) * spart->s[2]);
+
+ apn= spart->apixbuf + offset;
+ while (apn) {
+ CHECK_ADD(0);
+ CHECK_ADD(1);
+ CHECK_ADD(2);
+ CHECK_ADD(3);
+ CHECK_ASSIGN(0);
+ CHECK_ASSIGN(1);
+ CHECK_ASSIGN(2);
+ CHECK_ASSIGN(3);
+
+ apnew= addpsAstrand(spart->zspan);
+ SWAP(APixstrand, *apnew, *apn);
+ apn->next= apnew;
+ CHECK_ASSIGN(0);
+ }
+
+ if (cache) {
+ strand_shade_refcount(cache, sseg, sseg->v[1]);
+ strand_shade_refcount(cache, sseg, sseg->v[2]);
+ }
+ spart->totapixbuf[offset]++;
+ }
+ }
+}
+
+/* width is calculated in hoco space, to ensure strands are visible */
+static int strand_test_clip(float winmat[4][4], ZSpan *UNUSED(zspan), float *bounds, float *co, float *zcomp, float widthx, float widthy)
+{
+ float hoco[4];
+ int clipflag= 0;
+
+ projectvert(co, winmat, hoco);
+
+ /* we compare z without perspective division for segment sorting */
+ *zcomp= hoco[2];
+
+ if (hoco[0]+widthx < bounds[0]*hoco[3]) clipflag |= 1;
+ else if (hoco[0]-widthx > bounds[1]*hoco[3]) clipflag |= 2;
+
+ if (hoco[1]-widthy > bounds[3]*hoco[3]) clipflag |= 4;
+ else if (hoco[1]+widthy < bounds[2]*hoco[3]) clipflag |= 8;
+
+ clipflag |= testclip(hoco);
+
+ return clipflag;
+}
+
+static void do_scanconvert_strand(Render *UNUSED(re), StrandPart *spart, ZSpan *zspan, float t, float dt, float *co1, float *co2, float *co3, float *co4, int sample)
+{
+ float jco1[3], jco2[3], jco3[3], jco4[3], jx, jy;
+
+ copy_v3_v3(jco1, co1);
+ copy_v3_v3(jco2, co2);
+ copy_v3_v3(jco3, co3);
+ copy_v3_v3(jco4, co4);
+
+ if (spart->jit) {
+ jx= -spart->jit[sample][0];
+ jy= -spart->jit[sample][1];
+
+ jco1[0] += jx; jco1[1] += jy;
+ jco2[0] += jx; jco2[1] += jy;
+ jco3[0] += jx; jco3[1] += jy;
+ jco4[0] += jx; jco4[1] += jy;
+
+ /* XXX mblur? */
+ }
+
+ spart->sample= sample;
+
+ spart->t[0]= t-dt;
+ spart->s[0]= -1.0f;
+ spart->t[1]= t-dt;
+ spart->s[1]= 1.0f;
+ spart->t[2]= t;
+ spart->s[2]= 1.0f;
+ zspan_scanconvert_strand(zspan, spart, jco1, jco2, jco3, do_strand_fillac);
+ spart->t[0]= t-dt;
+ spart->s[0]= -1.0f;
+ spart->t[1]= t;
+ spart->s[1]= 1.0f;
+ spart->t[2]= t;
+ spart->s[2]= -1.0f;
+ zspan_scanconvert_strand(zspan, spart, jco1, jco3, jco4, do_strand_fillac);
+}
+
+static void strand_render(Render *re, StrandSegment *sseg, float winmat[4][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandPoint *p1, StrandPoint *p2)
+{
+ if (spart) {
+ float t= p2->t;
+ float dt= p2->t - p1->t;
+ int a;
+
+ for (a=0; a<spart->samples; a++)
+ do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, a);
+ }
+ else {
+ float hoco1[4], hoco2[4];
+ int a, obi, index;
+
+ obi= sseg->obi - re->objectinstance;
+ index= sseg->strand->index;
+
+ projectvert(p1->co, winmat, hoco1);
+ projectvert(p2->co, winmat, hoco2);
+
+
+ for (a=0; a<totzspan; a++) {
+#if 0
+ /* render both strand and single pixel wire to counter aliasing */
+ zbufclip4(re, &zspan[a], obi, index, p1->hoco2, p1->hoco1, p2->hoco1, p2->hoco2, p1->clip2, p1->clip1, p2->clip1, p2->clip2);
+#endif
+ /* only render a line for now, which makes the shadow map more
+ * similar across frames, and so reduces flicker */
+ zbufsinglewire(&zspan[a], obi, index, hoco1, hoco2);
+ }
+ }
+}
+
+static int strand_segment_recursive(Render *re, float winmat[4][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg, StrandPoint *p1, StrandPoint *p2, int depth)
+{
+ StrandPoint p;
+ StrandBuffer *buffer= sseg->buffer;
+ float dot, d1[2], d2[2], len1, len2;
+
+ if (depth == buffer->maxdepth)
+ return 0;
+
+ p.t= (p1->t + p2->t)*0.5f;
+ strand_eval_point(sseg, &p);
+ strand_project_point(buffer->winmat, buffer->winx, buffer->winy, &p);
+
+ d1[0]= (p.x - p1->x);
+ d1[1]= (p.y - p1->y);
+ len1= d1[0]*d1[0] + d1[1]*d1[1];
+
+ d2[0]= (p2->x - p.x);
+ d2[1]= (p2->y - p.y);
+ len2= d2[0]*d2[0] + d2[1]*d2[1];
+
+ if (len1 == 0.0f || len2 == 0.0f)
+ return 0;
+
+ dot= d1[0]*d2[0] + d1[1]*d2[1];
+ if (dot*dot > sseg->sqadaptcos*len1*len2)
+ return 0;
+
+ if (spart) {
+ do_strand_point_project(winmat, zspan, p.co1, p.hoco1, p.zco1);
+ do_strand_point_project(winmat, zspan, p.co2, p.hoco2, p.zco2);
+ }
+ else {
+#if 0
+ projectvert(p.co1, winmat, p.hoco1);
+ projectvert(p.co2, winmat, p.hoco2);
+ p.clip1= testclip(p.hoco1);
+ p.clip2= testclip(p.hoco2);
+#endif
+ }
+
+ if (!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, &p, depth+1))
+ strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, &p);
+ if (!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, &p, p2, depth+1))
+ strand_render(re, sseg, winmat, spart, zspan, totzspan, &p, p2);
+
+ return 1;
+}
+
+void render_strand_segment(Render *re, float winmat[4][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg)
+{
+ StrandBuffer *buffer= sseg->buffer;
+ StrandPoint *p1= &sseg->point1;
+ StrandPoint *p2= &sseg->point2;
+
+ p1->t= 0.0f;
+ p2->t= 1.0f;
+
+ strand_eval_point(sseg, p1);
+ strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p1);
+ strand_eval_point(sseg, p2);
+ strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p2);
+
+ if (spart) {
+ do_strand_point_project(winmat, zspan, p1->co1, p1->hoco1, p1->zco1);
+ do_strand_point_project(winmat, zspan, p1->co2, p1->hoco2, p1->zco2);
+ do_strand_point_project(winmat, zspan, p2->co1, p2->hoco1, p2->zco1);
+ do_strand_point_project(winmat, zspan, p2->co2, p2->hoco2, p2->zco2);
+ }
+ else {
+#if 0
+ projectvert(p1->co1, winmat, p1->hoco1);
+ projectvert(p1->co2, winmat, p1->hoco2);
+ projectvert(p2->co1, winmat, p2->hoco1);
+ projectvert(p2->co2, winmat, p2->hoco2);
+ p1->clip1= testclip(p1->hoco1);
+ p1->clip2= testclip(p1->hoco2);
+ p2->clip1= testclip(p2->hoco1);
+ p2->clip2= testclip(p2->hoco2);
+#endif
+ }
+
+ if (!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, p2, 0))
+ strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, p2);
+}
+
+/* render call to fill in strands */
+int zbuffer_strands_abuf(Render *re, RenderPart *pa, APixstrand *apixbuf, ListBase *apsmbase, unsigned int lay, int UNUSED(negzmask), float winmat[4][4], int winx, int winy, int samples, float (*jit)[2], float clipcrop, int shadow, StrandShadeCache *cache)
+{
+ ObjectRen *obr;
+ ObjectInstanceRen *obi;
+ ZSpan zspan;
+ StrandRen *strand = NULL;
+ StrandVert *svert;
+ StrandBound *sbound;
+ StrandPart spart;
+ StrandSegment sseg;
+ StrandSortSegment *sortsegments = NULL, *sortseg, *firstseg;
+ MemArena *memarena;
+ float z[4], bounds[4], obwinmat[4][4];
+ int a, b, c, i, totsegment, clip[4];
+
+ if (re->test_break(re->tbh))
+ return 0;
+ if (re->totstrand == 0)
+ return 0;
+
+ /* setup StrandPart */
+ memset(&spart, 0, sizeof(spart));
+
+ spart.re= re;
+ spart.rectx= pa->rectx;
+ spart.recty= pa->recty;
+ spart.apixbuf= apixbuf;
+ spart.zspan= &zspan;
+ spart.rectdaps= pa->rectdaps;
+ spart.rectz= pa->rectz;
+ spart.rectmask= pa->rectmask;
+ spart.cache= cache;
+ spart.shadow= shadow;
+ spart.jit= jit;
+ spart.samples= samples;
+
+ zbuf_alloc_span(&zspan, pa->rectx, pa->recty, clipcrop);
+
+ /* needed for transform from hoco to zbuffer co */
+ zspan.zmulx= ((float)winx)/2.0f;
+ zspan.zmuly= ((float)winy)/2.0f;
+
+ zspan.zofsx= -pa->disprect.xmin;
+ zspan.zofsy= -pa->disprect.ymin;
+
+ /* to center the sample position */
+ if (!shadow) {
+ zspan.zofsx -= 0.5f;
+ zspan.zofsy -= 0.5f;
+ }
+
+ zspan.apsmbase= apsmbase;
+
+ /* clipping setup */
+ bounds[0]= (2*pa->disprect.xmin - winx-1)/(float)winx;
+ bounds[1]= (2*pa->disprect.xmax - winx+1)/(float)winx;
+ bounds[2]= (2*pa->disprect.ymin - winy-1)/(float)winy;
+ bounds[3]= (2*pa->disprect.ymax - winy+1)/(float)winy;
+
+ memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "strand sort arena");
+ firstseg= NULL;
+ totsegment= 0;
+
+ /* for all object instances */
+ for (obi=re->instancetable.first, i=0; obi; obi=obi->next, i++) {
+ Material *ma;
+ float widthx, widthy;
+
+ obr= obi->obr;
+
+ if (!obr->strandbuf || !(obr->strandbuf->lay & lay))
+ continue;
+
+ /* compute matrix and try clipping whole object */
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_m4m4(obwinmat, winmat, obi->mat);
+ else
+ copy_m4_m4(obwinmat, winmat);
+
+ /* test if we should skip it */
+ ma = obr->strandbuf->ma;
+
+ if (shadow && (!(ma->mode2 & MA_CASTSHADOW) || !(ma->mode & MA_SHADBUF)))
+ continue;
+ else if (!shadow && (ma->mode & MA_ONLYCAST))
+ continue;
+
+ if (clip_render_object(obi->obr->boundbox, bounds, obwinmat))
+ continue;
+
+ widthx= obr->strandbuf->maxwidth*obwinmat[0][0];
+ widthy= obr->strandbuf->maxwidth*obwinmat[1][1];
+
+ /* for each bounding box containing a number of strands */
+ sbound= obr->strandbuf->bound;
+ for (c=0; c<obr->strandbuf->totbound; c++, sbound++) {
+ if (clip_render_object(sbound->boundbox, bounds, obwinmat))
+ continue;
+
+ /* for each strand in this bounding box */
+ for (a=sbound->start; a<sbound->end; a++) {
+ strand= RE_findOrAddStrand(obr, a);
+ svert= strand->vert;
+
+ /* keep clipping and z depth for 4 control points */
+ clip[1]= strand_test_clip(obwinmat, &zspan, bounds, svert->co, &z[1], widthx, widthy);
+ clip[2]= strand_test_clip(obwinmat, &zspan, bounds, (svert+1)->co, &z[2], widthx, widthy);
+ clip[0]= clip[1]; z[0]= z[1];
+
+ for (b=0; b<strand->totvert-1; b++, svert++) {
+ /* compute 4th point clipping and z depth */
+ if (b < strand->totvert-2) {
+ clip[3]= strand_test_clip(obwinmat, &zspan, bounds, (svert+2)->co, &z[3], widthx, widthy);
+ }
+ else {
+ clip[3]= clip[2]; z[3]= z[2];
+ }
+
+ /* check clipping and add to sortsegments buffer */
+ if (!(clip[0] & clip[1] & clip[2] & clip[3])) {
+ sortseg= BLI_memarena_alloc(memarena, sizeof(StrandSortSegment));
+ sortseg->obi= i;
+ sortseg->strand= strand->index;
+ sortseg->segment= b;
+
+ sortseg->z= 0.5f*(z[1] + z[2]);
+
+ sortseg->next= firstseg;
+ firstseg= sortseg;
+ totsegment++;
+ }
+
+ /* shift clipping and z depth */
+ clip[0]= clip[1]; z[0]= z[1];
+ clip[1]= clip[2]; z[1]= z[2];
+ clip[2]= clip[3]; z[2]= z[3];
+ }
+ }
+ }
+ }
+
+ if (!re->test_break(re->tbh)) {
+ /* convert list to array and sort */
+ sortsegments= MEM_mallocN(sizeof(StrandSortSegment)*totsegment, "StrandSortSegment");
+ for (a=0, sortseg=firstseg; a<totsegment; a++, sortseg=sortseg->next)
+ sortsegments[a]= *sortseg;
+ qsort(sortsegments, totsegment, sizeof(StrandSortSegment), compare_strand_segment);
+ }
+
+ BLI_memarena_free(memarena);
+
+ spart.totapixbuf= MEM_callocN(sizeof(int)*pa->rectx*pa->recty, "totapixbuf");
+
+ if (!re->test_break(re->tbh)) {
+ /* render segments in sorted order */
+ sortseg= sortsegments;
+ for (a=0; a<totsegment; a++, sortseg++) {
+ if (re->test_break(re->tbh))
+ break;
+
+ obi= &re->objectinstance[sortseg->obi];
+ obr= obi->obr;
+
+ sseg.obi= obi;
+ sseg.strand= RE_findOrAddStrand(obr, sortseg->strand);
+ sseg.buffer= sseg.strand->buffer;
+ sseg.sqadaptcos= sseg.buffer->adaptcos;
+ sseg.sqadaptcos *= sseg.sqadaptcos;
+
+ svert= sseg.strand->vert + sortseg->segment;
+ sseg.v[0]= (sortseg->segment > 0)? (svert-1): svert;
+ sseg.v[1]= svert;
+ sseg.v[2]= svert+1;
+ sseg.v[3]= (sortseg->segment < sseg.strand->totvert-2)? svert+2: svert+1;
+ sseg.shaded= 0;
+
+ spart.segment= &sseg;
+
+ render_strand_segment(re, winmat, &spart, &zspan, 1, &sseg);
+ }
+ }
+
+ if (sortsegments)
+ MEM_freeN(sortsegments);
+ MEM_freeN(spart.totapixbuf);
+
+ zbuf_free_span(&zspan);
+
+ return totsegment;
+}
+
+/* *************** */
+
+StrandSurface *cache_strand_surface(Render *re, ObjectRen *obr, DerivedMesh *dm, float mat[4][4], int timeoffset)
+{
+ StrandSurface *mesh;
+ MFace *mface;
+ MVert *mvert;
+ float (*co)[3];
+ int a, totvert, totface;
+
+ totvert= dm->getNumVerts(dm);
+ totface= dm->getNumTessFaces(dm);
+
+ for (mesh = re->strandsurface.first; mesh; mesh = mesh->next) {
+ if ((mesh->obr.ob == obr->ob) &&
+ (mesh->obr.par == obr->par) &&
+ (mesh->obr.index == obr->index) &&
+ (mesh->totvert == totvert) &&
+ (mesh->totface == totface))
+ {
+ break;
+ }
+ }
+
+ if (!mesh) {
+ mesh= MEM_callocN(sizeof(StrandSurface), "StrandSurface");
+ mesh->obr= *obr;
+ mesh->totvert= totvert;
+ mesh->totface= totface;
+ mesh->face= MEM_callocN(sizeof(int)*4*mesh->totface, "StrandSurfFaces");
+ mesh->ao= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfAO");
+ mesh->env= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfEnv");
+ mesh->indirect= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfIndirect");
+ BLI_addtail(&re->strandsurface, mesh);
+ }
+
+ if (timeoffset == -1 && !mesh->prevco)
+ mesh->prevco= co= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfCo");
+ else if (timeoffset == 0 && !mesh->co)
+ mesh->co= co= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfCo");
+ else if (timeoffset == 1 && !mesh->nextco)
+ mesh->nextco= co= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfCo");
+ else
+ return mesh;
+
+ mvert= dm->getVertArray(dm);
+ for (a=0; a<mesh->totvert; a++, mvert++) {
+ copy_v3_v3(co[a], mvert->co);
+ mul_m4_v3(mat, co[a]);
+ }
+
+ mface= dm->getTessFaceArray(dm);
+ for (a=0; a<mesh->totface; a++, mface++) {
+ mesh->face[a][0]= mface->v1;
+ mesh->face[a][1]= mface->v2;
+ mesh->face[a][2]= mface->v3;
+ mesh->face[a][3]= mface->v4;
+ }
+
+ return mesh;
+}
+
+void free_strand_surface(Render *re)
+{
+ StrandSurface *mesh;
+
+ for (mesh=re->strandsurface.first; mesh; mesh=mesh->next) {
+ if (mesh->co) MEM_freeN(mesh->co);
+ if (mesh->prevco) MEM_freeN(mesh->prevco);
+ if (mesh->nextco) MEM_freeN(mesh->nextco);
+ if (mesh->ao) MEM_freeN(mesh->ao);
+ if (mesh->env) MEM_freeN(mesh->env);
+ if (mesh->indirect) MEM_freeN(mesh->indirect);
+ if (mesh->face) MEM_freeN(mesh->face);
+ }
+
+ BLI_freelistN(&re->strandsurface);
+}
+
+void strand_minmax(StrandRen *strand, float min[3], float max[3], const float width)
+{
+ StrandVert *svert;
+ const float width2 = width * 2.0f;
+ float vec[3];
+ int a;
+
+ for (a=0, svert=strand->vert; a<strand->totvert; a++, svert++) {
+ copy_v3_v3(vec, svert->co);
+ minmax_v3v3_v3(min, max, vec);
+
+ if (width!=0.0f) {
+ add_v3_fl(vec, width);
+ minmax_v3v3_v3(min, max, vec);
+ add_v3_fl(vec, -width2);
+ minmax_v3v3_v3(min, max, vec);
+ }
+ }
+}
+
diff --git a/source/blender/render/intern/source/sunsky.c b/source/blender/render/intern/source/sunsky.c
new file mode 100644
index 00000000000..80dd52c220c
--- /dev/null
+++ b/source/blender/render/intern/source/sunsky.c
@@ -0,0 +1,506 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/sunsky.c
+ * \ingroup render
+ *
+ * This feature comes from Preetham paper on "A Practical Analytic Model for Daylight"
+ * and example code from Brian Smits, another author of that paper in
+ * http://www.cs.utah.edu/vissim/papers/sunsky/code/
+ */
+
+#include "sunsky.h"
+#include "BLI_math.h"
+
+/**
+ * These macros are defined for vector operations
+ * */
+
+/**
+ * compute v1 = v2 op v3
+ * v1, v2 and v3 are vectors contains 3 float
+ * */
+#define VEC3OPV(v1, v2, op, v3) \
+ { \
+ v1[0] = (v2[0] op v3[0]); \
+ v1[1] = (v2[1] op v3[1]); \
+ v1[2] = (v2[2] op v3[2]); \
+ } (void)0
+
+/**
+ * compute v1 = v2 op f1
+ * v1, v2 are vectors contains 3 float
+ * and f1 is a float
+ * */
+#define VEC3OPF(v1, v2, op, f1) \
+ { \
+ v1[0] = (v2[0] op(f1)); \
+ v1[1] = (v2[1] op(f1)); \
+ v1[2] = (v2[2] op(f1)); \
+ } (void)0
+
+/**
+ * compute v1 = f1 op v2
+ * v1, v2 are vectors contains 3 float
+ * and f1 is a float
+ * */
+#define FOPVEC3(v1, f1, op, v2) \
+ { \
+ v1[0] = ((f1) op v2[0]); \
+ v1[1] = ((f1) op v2[1]); \
+ v1[2] = ((f1) op v2[2]); \
+ } (void)0
+
+/**
+ * ClipColor:
+ * clip a color to range [0, 1];
+ * */
+void ClipColor(float c[3])
+{
+ if (c[0] > 1.0f) c[0] = 1.0f;
+ if (c[0] < 0.0f) c[0] = 0.0f;
+ if (c[1] > 1.0f) c[1] = 1.0f;
+ if (c[1] < 0.0f) c[1] = 0.0f;
+ if (c[2] > 1.0f) c[2] = 1.0f;
+ if (c[2] < 0.0f) c[2] = 0.0f;
+}
+
+/**
+ * AngleBetween:
+ * compute angle between to direction
+ * all angles are in radians
+ * */
+static float AngleBetween(float thetav, float phiv, float theta, float phi)
+{
+ float cospsi = sinf(thetav) * sinf(theta) * cosf(phi - phiv) + cosf(thetav) * cosf(theta);
+
+ if (cospsi > 1.0f)
+ return 0;
+ if (cospsi < -1.0f)
+ return M_PI;
+
+ return acosf(cospsi);
+}
+
+/**
+ * DirectionToThetaPhi:
+ * this function convert a direction to it's theta and phi value
+ * parameters:
+ * toSun: contains direction information
+ * theta, phi, are return values from this conversion
+ * */
+static void DirectionToThetaPhi(float *toSun, float *theta, float *phi)
+{
+ *theta = acosf(toSun[2]);
+ if (fabsf(*theta) < 1e-5f)
+ *phi = 0;
+ else
+ *phi = atan2f(toSun[1], toSun[0]);
+}
+
+/**
+ * PerezFunction:
+ * compute perez function value based on input parameters
+ */
+static float PerezFunction(struct SunSky *sunsky, const float *lam, float theta, float gamma, float lvz)
+{
+ float den, num;
+
+ den = ((1 + lam[0] * expf(lam[1])) *
+ (1 + lam[2] * expf(lam[3] * sunsky->theta) + lam[4] * cosf(sunsky->theta) * cosf(sunsky->theta)));
+
+ num = ((1 + lam[0] * expf(lam[1] / cosf(theta))) *
+ (1 + lam[2] * expf(lam[3] * gamma) + lam[4] * cosf(gamma) * cosf(gamma)));
+
+ return(lvz * num / den);
+}
+
+/**
+ * InitSunSky:
+ * this function compute some sun,sky parameters according to input parameters and also initiate some other sun, sky parameters
+ * parameters:
+ * sunSky, is a structure that contains information about sun, sky and atmosphere, in this function, most of its values initiated
+ * turb, is atmosphere turbidity
+ * toSun, contains sun direction
+ * horizon_brighness, controls the brightness of the horizon colors
+ * spread, controls colors spreed at horizon
+ * sun_brightness, controls sun's brightness
+ * sun_size, controls sun's size
+ * back_scatter, controls back scatter light
+ * */
+void InitSunSky(struct SunSky *sunsky, float turb, const float toSun[3], float horizon_brightness,
+ float spread, float sun_brightness, float sun_size, float back_scatter,
+ float skyblendfac, short skyblendtype, float sky_exposure, float sky_colorspace)
+{
+ float theta2;
+ float theta3;
+ float T;
+ float T2;
+ float chi;
+
+ sunsky->turbidity = turb;
+
+ sunsky->horizon_brightness = horizon_brightness;
+ sunsky->spread = spread;
+ sunsky->sun_brightness = sun_brightness;
+ sunsky->sun_size = sun_size;
+ sunsky->backscattered_light = back_scatter;
+ sunsky->skyblendfac = skyblendfac;
+ sunsky->skyblendtype = skyblendtype;
+ sunsky->sky_exposure = -sky_exposure;
+ sunsky->sky_colorspace = sky_colorspace;
+
+ sunsky->toSun[0] = toSun[0];
+ sunsky->toSun[1] = toSun[1];
+ sunsky->toSun[2] = toSun[2];
+
+ DirectionToThetaPhi(sunsky->toSun, &sunsky->theta, &sunsky->phi);
+
+ sunsky->sunSolidAngle = 0.25 * M_PI * 1.39 * 1.39 / (150 * 150); /* = 6.7443e-05 */
+
+ theta2 = sunsky->theta * sunsky->theta;
+ theta3 = theta2 * sunsky->theta;
+ T = turb;
+ T2 = turb * turb;
+
+ chi = (4.0f / 9.0f - T / 120.0f) * ((float)M_PI - 2.0f * sunsky->theta);
+ sunsky->zenith_Y = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;
+ sunsky->zenith_Y *= 1000; /* conversion from kcd/m^2 to cd/m^2 */
+
+ if (sunsky->zenith_Y <= 0)
+ sunsky->zenith_Y = 1e-6;
+
+ sunsky->zenith_x =
+ (+0.00165f * theta3 - 0.00374f * theta2 + 0.00208f * sunsky->theta + 0.0f) * T2 +
+ (-0.02902f * theta3 + 0.06377f * theta2 - 0.03202f * sunsky->theta + 0.00394f) * T +
+ (+0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * sunsky->theta + 0.25885f);
+
+ sunsky->zenith_y =
+ (+0.00275f * theta3 - 0.00610f * theta2 + 0.00316f * sunsky->theta + 0.0f) * T2 +
+ (-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * sunsky->theta + 0.00515f) * T +
+ (+0.15346f * theta3 - 0.26756f * theta2 + 0.06669f * sunsky->theta + 0.26688f);
+
+
+ sunsky->perez_Y[0] = 0.17872f * T - 1.46303f;
+ sunsky->perez_Y[1] = -0.35540f * T + 0.42749f;
+ sunsky->perez_Y[2] = -0.02266f * T + 5.32505f;
+ sunsky->perez_Y[3] = 0.12064f * T - 2.57705f;
+ sunsky->perez_Y[4] = -0.06696f * T + 0.37027f;
+
+ sunsky->perez_x[0] = -0.01925f * T - 0.25922f;
+ sunsky->perez_x[1] = -0.06651f * T + 0.00081f;
+ sunsky->perez_x[2] = -0.00041f * T + 0.21247f;
+ sunsky->perez_x[3] = -0.06409f * T - 0.89887f;
+ sunsky->perez_x[4] = -0.00325f * T + 0.04517f;
+
+ sunsky->perez_y[0] = -0.01669f * T - 0.26078f;
+ sunsky->perez_y[1] = -0.09495f * T + 0.00921f;
+ sunsky->perez_y[2] = -0.00792f * T + 0.21023f;
+ sunsky->perez_y[3] = -0.04405f * T - 1.65369f;
+ sunsky->perez_y[4] = -0.01092f * T + 0.05291f;
+
+ /* suggested by glome in patch [#8063] */
+ sunsky->perez_Y[0] *= sunsky->horizon_brightness;
+ sunsky->perez_x[0] *= sunsky->horizon_brightness;
+ sunsky->perez_y[0] *= sunsky->horizon_brightness;
+
+ sunsky->perez_Y[1] *= sunsky->spread;
+ sunsky->perez_x[1] *= sunsky->spread;
+ sunsky->perez_y[1] *= sunsky->spread;
+
+ sunsky->perez_Y[2] *= sunsky->sun_brightness;
+ sunsky->perez_x[2] *= sunsky->sun_brightness;
+ sunsky->perez_y[2] *= sunsky->sun_brightness;
+
+ sunsky->perez_Y[3] *= sunsky->sun_size;
+ sunsky->perez_x[3] *= sunsky->sun_size;
+ sunsky->perez_y[3] *= sunsky->sun_size;
+
+ sunsky->perez_Y[4] *= sunsky->backscattered_light;
+ sunsky->perez_x[4] *= sunsky->backscattered_light;
+ sunsky->perez_y[4] *= sunsky->backscattered_light;
+}
+
+/**
+ * GetSkyXYZRadiance:
+ * this function compute sky radiance according to a view parameters `theta' and `phi'and sunSky values
+ * parameters:
+ * sunSky, sontains sun and sky parameters
+ * theta, is sun's theta
+ * phi, is sun's phi
+ * color_out, is computed color that shows sky radiance in XYZ color format
+ * */
+void GetSkyXYZRadiance(struct SunSky *sunsky, float theta, float phi, float color_out[3])
+{
+ float gamma;
+ float x, y, Y, X, Z;
+ float hfade = 1, nfade = 1;
+
+
+ if (theta > (float)M_PI_2) {
+ hfade = 1.0f - (theta * (float)M_1_PI - 0.5f) * 2.0f;
+ hfade = hfade * hfade * (3.0f - 2.0f * hfade);
+ theta = M_PI_2;
+ }
+
+ if (sunsky->theta > (float)M_PI_2) {
+ if (theta <= (float)M_PI_2) {
+ nfade = 1.0f - (0.5f - theta * (float)M_1_PI) * 2.0f;
+ nfade *= 1.0f - (sunsky->theta * (float)M_1_PI - 0.5f) * 2.0f;
+ nfade = nfade * nfade * (3.0f - 2.0f * nfade);
+ }
+ }
+
+ gamma = AngleBetween(theta, phi, sunsky->theta, sunsky->phi);
+
+ /* Compute xyY values */
+ x = PerezFunction(sunsky, sunsky->perez_x, theta, gamma, sunsky->zenith_x);
+ y = PerezFunction(sunsky, sunsky->perez_y, theta, gamma, sunsky->zenith_y);
+ Y = 6.666666667e-5f * nfade * hfade * PerezFunction(sunsky, sunsky->perez_Y, theta, gamma, sunsky->zenith_Y);
+
+ if (sunsky->sky_exposure != 0.0f)
+ Y = 1.0 - exp(Y * sunsky->sky_exposure);
+
+ X = (x / y) * Y;
+ Z = ((1 - x - y) / y) * Y;
+
+ color_out[0] = X;
+ color_out[1] = Y;
+ color_out[2] = Z;
+}
+
+/**
+ * GetSkyXYZRadiancef:
+ * this function compute sky radiance according to a view direction `varg' and sunSky values
+ * parameters:
+ * sunSky, sontains sun and sky parameters
+ * varg, shows direction
+ * color_out, is computed color that shows sky radiance in XYZ color format
+ * */
+void GetSkyXYZRadiancef(struct SunSky *sunsky, const float varg[3], float color_out[3])
+{
+ float theta, phi;
+ float v[3];
+
+ normalize_v3_v3(v, varg);
+
+ if (v[2] < 0.001f) {
+ v[2] = 0.001f;
+ normalize_v3(v);
+ }
+
+ DirectionToThetaPhi(v, &theta, &phi);
+ GetSkyXYZRadiance(sunsky, theta, phi, color_out);
+}
+
+/**
+ * ComputeAttenuatedSunlight:
+ * this function compute attenuated sun light based on sun's theta and atmosphere turbidity
+ * parameters:
+ * theta, is sun's theta
+ * turbidity: is atmosphere turbidity
+ * fTau: contains computed attenuated sun light
+ * */
+static void ComputeAttenuatedSunlight(float theta, int turbidity, float fTau[3])
+{
+ float fBeta;
+ float fTauR, fTauA;
+ float m;
+ float fAlpha;
+
+ int i;
+ float fLambda[3];
+ fLambda[0] = 0.65f;
+ fLambda[1] = 0.57f;
+ fLambda[2] = 0.475f;
+
+ fAlpha = 1.3f;
+ fBeta = 0.04608365822050f * turbidity - 0.04586025928522f;
+
+ m = 1.0f / (cosf(theta) + 0.15f * powf(93.885f - theta / (float)M_PI * 180.0f, -1.253f));
+
+ for (i = 0; i < 3; i++) {
+ /* Rayleigh Scattering */
+ fTauR = expf(-m * 0.008735f * powf(fLambda[i], (float)(-4.08f)));
+
+ /* Aerosal (water + dust) attenuation */
+ fTauA = exp(-m * fBeta * powf(fLambda[i], -fAlpha));
+
+ fTau[i] = fTauR * fTauA;
+ }
+}
+
+/**
+ * InitAtmosphere:
+ * this function initiate sunSky structure with user input parameters.
+ * parameters:
+ * sunSky, contains information about sun, and in this function some atmosphere parameters will initiated
+ * sun_intens, shows sun intensity value
+ * mief, Mie scattering factor this factor currently call with 1.0
+ * rayf, Rayleigh scattering factor, this factor currently call with 1.0
+ * inscattf, inscatter light factor that range from 0.0 to 1.0, 0.0 means no inscatter light and 1.0 means full inscatter light
+ * extincf, extinction light factor that range from 0.0 to 1.0, 0.0 means no extinction and 1.0 means full extinction
+ * disf, is distance factor, multiplied to pixle's z value to compute each pixle's distance to camera,
+ * */
+void InitAtmosphere(struct SunSky *sunSky, float sun_intens, float mief, float rayf,
+ float inscattf, float extincf, float disf)
+{
+ const float pi = M_PI;
+ const float n = 1.003f; /* refractive index */
+ const float N = 2.545e25;
+ const float pn = 0.035f;
+ const float T = 2.0f;
+ float fTemp, fTemp2, fTemp3, fBeta, fBetaDash;
+ float c = (6.544f * T - 6.51f) * 1e-17f;
+ float K[3] = {0.685f, 0.679f, 0.670f};
+ float vBetaMieTemp[3];
+
+ float fLambda[3], fLambda2[3], fLambda4[3];
+ float vLambda2[3];
+ float vLambda4[3];
+
+ int i;
+
+ sunSky->atm_SunIntensity = sun_intens;
+ sunSky->atm_BetaMieMultiplier = mief;
+ sunSky->atm_BetaRayMultiplier = rayf;
+ sunSky->atm_InscatteringMultiplier = inscattf;
+ sunSky->atm_ExtinctionMultiplier = extincf;
+ sunSky->atm_DistanceMultiplier = disf;
+
+ sunSky->atm_HGg = 0.8;
+
+ fLambda[0] = 1 / 650e-9f;
+ fLambda[1] = 1 / 570e-9f;
+ fLambda[2] = 1 / 475e-9f;
+ for (i = 0; i < 3; i++) {
+ fLambda2[i] = fLambda[i] * fLambda[i];
+ fLambda4[i] = fLambda2[i] * fLambda2[i];
+ }
+
+ vLambda2[0] = fLambda2[0];
+ vLambda2[1] = fLambda2[1];
+ vLambda2[2] = fLambda2[2];
+
+ vLambda4[0] = fLambda4[0];
+ vLambda4[1] = fLambda4[1];
+ vLambda4[2] = fLambda4[2];
+
+ /* Rayleigh scattering constants. */
+ fTemp = pi * pi * (n * n - 1) * (n * n - 1) * (6 + 3 * pn) / (6 - 7 * pn) / N;
+ fBeta = 8 * fTemp * pi / 3;
+
+ VEC3OPF(sunSky->atm_BetaRay, vLambda4, *, fBeta);
+ fBetaDash = fTemp / 2;
+ VEC3OPF(sunSky->atm_BetaDashRay, vLambda4, *, fBetaDash);
+
+
+ /* Mie scattering constants. */
+ fTemp2 = 0.434f * c * (2 * pi) * (2 * pi) * 0.5f;
+ VEC3OPF(sunSky->atm_BetaDashMie, vLambda2, *, fTemp2);
+
+ fTemp3 = 0.434f * c * pi * (2 * pi) * (2 * pi);
+
+ VEC3OPV(vBetaMieTemp, K, *, fLambda);
+ VEC3OPF(sunSky->atm_BetaMie, vBetaMieTemp, *, fTemp3);
+
+}
+
+/**
+ * AtmospherePixleShader:
+ * this function apply atmosphere effect on a pixle color `rgb' at distance `s'
+ * parameters:
+ * sunSky, contains information about sun parameters and user values
+ * view, is camera view vector
+ * s, is distance
+ * rgb, contains rendered color value for a pixle
+ * */
+void AtmospherePixleShader(struct SunSky *sunSky, float view[3], float s, float rgb[3])
+{
+ float costheta;
+ float Phase_1;
+ float Phase_2;
+ float sunColor[3];
+
+ float E[3];
+ float E1[3];
+
+
+ float I[3];
+ float fTemp;
+ float vTemp1[3], vTemp2[3];
+
+ float sunDirection[3];
+
+ s *= sunSky->atm_DistanceMultiplier;
+
+ sunDirection[0] = sunSky->toSun[0];
+ sunDirection[1] = sunSky->toSun[1];
+ sunDirection[2] = sunSky->toSun[2];
+
+ costheta = dot_v3v3(view, sunDirection); /* cos(theta) */
+ Phase_1 = 1 + (costheta * costheta); /* Phase_1 */
+
+ VEC3OPF(sunSky->atm_BetaRay, sunSky->atm_BetaRay, *, sunSky->atm_BetaRayMultiplier);
+ VEC3OPF(sunSky->atm_BetaMie, sunSky->atm_BetaMie, *, sunSky->atm_BetaMieMultiplier);
+ VEC3OPV(sunSky->atm_BetaRM, sunSky->atm_BetaRay, +, sunSky->atm_BetaMie);
+
+ /* e^(-(beta_1 + beta_2) * s) = E1 */
+ VEC3OPF(E1, sunSky->atm_BetaRM, *, -s / (float)M_LN2);
+ E1[0] = exp(E1[0]);
+ E1[1] = exp(E1[1]);
+ E1[2] = exp(E1[2]);
+
+ copy_v3_v3(E, E1);
+
+ /* Phase2(theta) = (1-g^2)/(1+g-2g*cos(theta))^(3/2) */
+ fTemp = 1 + sunSky->atm_HGg - 2 * sunSky->atm_HGg * costheta;
+ fTemp = fTemp * sqrtf(fTemp);
+ Phase_2 = (1 - sunSky->atm_HGg * sunSky->atm_HGg) / fTemp;
+
+ VEC3OPF(vTemp1, sunSky->atm_BetaDashRay, *, Phase_1);
+ VEC3OPF(vTemp2, sunSky->atm_BetaDashMie, *, Phase_2);
+
+ VEC3OPV(vTemp1, vTemp1, +, vTemp2);
+ FOPVEC3(vTemp2, 1.0f, -, E1);
+ VEC3OPV(vTemp1, vTemp1, *, vTemp2);
+
+ FOPVEC3(vTemp2, 1.0f, /, sunSky->atm_BetaRM);
+
+ VEC3OPV(I, vTemp1, *, vTemp2);
+
+ VEC3OPF(I, I, *, sunSky->atm_InscatteringMultiplier);
+ VEC3OPF(E, E, *, sunSky->atm_ExtinctionMultiplier);
+
+ /* scale to color sun */
+ ComputeAttenuatedSunlight(sunSky->theta, sunSky->turbidity, sunColor);
+ VEC3OPV(E, E, *, sunColor);
+
+ VEC3OPF(I, I, *, sunSky->atm_SunIntensity);
+
+ VEC3OPV(rgb, rgb, *, E);
+ VEC3OPV(rgb, rgb, +, I);
+}
+
+#undef VEC3OPV
+#undef VEC3OPF
+#undef FOPVEC3
+
+/* EOF */
diff --git a/source/blender/render/intern/source/volume_precache.c b/source/blender/render/intern/source/volume_precache.c
new file mode 100644
index 00000000000..8e79f309814
--- /dev/null
+++ b/source/blender/render/intern/source/volume_precache.c
@@ -0,0 +1,855 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Matt Ebb, Ra˙l Fern·ndez Hern·ndez (Farsthary).
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/volume_precache.c
+ * \ingroup render
+ */
+
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <float.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_task.h"
+#include "BLI_threads.h"
+#include "BLI_voxel.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "PIL_time.h"
+
+#include "RE_shader_ext.h"
+
+#include "DNA_material_types.h"
+
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "render_types.h"
+#include "rendercore.h"
+#include "renderdatabase.h"
+#include "volumetric.h"
+#include "volume_precache.h"
+
+#include "atomic_ops.h"
+
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* *** utility code to set up an individual raytree for objectinstance, for checking inside/outside *** */
+
+/* Recursive test for intersections, from a point inside the mesh, to outside
+ * Number of intersections (depth) determine if a point is inside or outside the mesh */
+static int intersect_outside_volume(RayObject *tree, Isect *isect, float *offset, int limit, int depth)
+{
+ if (limit == 0) return depth;
+
+ if (RE_rayobject_raycast(tree, isect)) {
+
+ isect->start[0] = isect->start[0] + isect->dist*isect->dir[0];
+ isect->start[1] = isect->start[1] + isect->dist*isect->dir[1];
+ isect->start[2] = isect->start[2] + isect->dist*isect->dir[2];
+
+ isect->dist = FLT_MAX;
+ isect->skip = RE_SKIP_VLR_NEIGHBOUR;
+ isect->orig.face= isect->hit.face;
+ isect->orig.ob= isect->hit.ob;
+
+ return intersect_outside_volume(tree, isect, offset, limit-1, depth+1);
+ }
+ else {
+ return depth;
+ }
+}
+
+/* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */
+static int point_inside_obi(RayObject *tree, ObjectInstanceRen *obi, const float co[3])
+{
+ Isect isect= {{0}};
+ float dir[3] = {0.0f, 0.0f, 1.0f};
+ int final_depth=0, depth=0, limit=20;
+
+ /* set up the isect */
+ copy_v3_v3(isect.start, co);
+ copy_v3_v3(isect.dir, dir);
+ isect.mode= RE_RAY_MIRROR;
+ isect.last_hit= NULL;
+ isect.lay= -1;
+
+ isect.dist = FLT_MAX;
+ isect.orig.face= NULL;
+ isect.orig.ob = NULL;
+
+ RE_instance_rotate_ray(obi, &isect);
+ final_depth = intersect_outside_volume(tree, &isect, dir, limit, depth);
+ RE_instance_rotate_ray_restore(obi, &isect);
+
+ /* even number of intersections: point is outside
+ * odd number: point is inside */
+ if (final_depth % 2 == 0) return 0;
+ else return 1;
+}
+
+/* find the bounding box of an objectinstance in global space */
+void global_bounds_obi(Render *re, ObjectInstanceRen *obi, float bbmin[3], float bbmax[3])
+{
+ ObjectRen *obr = obi->obr;
+ VolumePrecache *vp = obi->volume_precache;
+ VertRen *ver= NULL;
+ float co[3];
+ int a;
+
+ if (vp->bbmin != NULL && vp->bbmax != NULL) {
+ copy_v3_v3(bbmin, vp->bbmin);
+ copy_v3_v3(bbmax, vp->bbmax);
+ return;
+ }
+
+ vp->bbmin = MEM_callocN(sizeof(float)*3, "volume precache min boundbox corner");
+ vp->bbmax = MEM_callocN(sizeof(float)*3, "volume precache max boundbox corner");
+
+ INIT_MINMAX(bbmin, bbmax);
+
+ for (a=0; a<obr->totvert; a++) {
+ if ((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
+ else ver++;
+
+ copy_v3_v3(co, ver->co);
+
+ /* transformed object instance in camera space */
+ if (obi->flag & R_TRANSFORMED)
+ mul_m4_v3(obi->mat, co);
+
+ /* convert to global space */
+ mul_m4_v3(re->viewinv, co);
+
+ minmax_v3v3_v3(vp->bbmin, vp->bbmax, co);
+ }
+
+ copy_v3_v3(bbmin, vp->bbmin);
+ copy_v3_v3(bbmax, vp->bbmax);
+
+}
+
+/* *** light cache filtering *** */
+
+static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz)
+{
+ int x, y, z, x_, y_, z_;
+ int added=0;
+ float tot=0.0f;
+
+ for (z=-1; z <= 1; z++) {
+ z_ = zz+z;
+ if (z_ >= 0 && z_ <= res[2]-1) {
+
+ for (y=-1; y <= 1; y++) {
+ y_ = yy+y;
+ if (y_ >= 0 && y_ <= res[1]-1) {
+
+ for (x=-1; x <= 1; x++) {
+ x_ = xx+x;
+ if (x_ >= 0 && x_ <= res[0]-1) {
+ const int64_t i = BLI_VOXEL_INDEX(x_, y_, z_, res);
+
+ if (cache[i] > 0.0f) {
+ tot += cache[i];
+ added++;
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (added > 0) tot /= added;
+
+ return tot;
+}
+
+/* function to filter the edges of the light cache, where there was no volume originally.
+ * For each voxel which was originally external to the mesh, it finds the average values of
+ * the surrounding internal voxels and sets the original external voxel to that average amount.
+ * Works almost a bit like a 'dilate' filter */
+static void lightcache_filter(VolumePrecache *vp)
+{
+ int x, y, z;
+
+ for (z=0; z < vp->res[2]; z++) {
+ for (y=0; y < vp->res[1]; y++) {
+ for (x=0; x < vp->res[0]; x++) {
+ /* trigger for outside mesh */
+ const int64_t i = BLI_VOXEL_INDEX(x, y, z, vp->res);
+
+ if (vp->data_r[i] < -0.f)
+ vp->data_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
+ if (vp->data_g[i] < -0.f)
+ vp->data_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
+ if (vp->data_b[i] < -0.f)
+ vp->data_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
+ }
+ }
+ }
+}
+
+#if 0
+static void lightcache_filter2(VolumePrecache *vp)
+{
+ int x, y, z;
+ float *new_r, *new_g, *new_b;
+ int field_size = vp->res[0]*vp->res[1]*vp->res[2]*sizeof(float);
+
+ new_r = MEM_mallocN(field_size, "temp buffer for light cache filter r channel");
+ new_g = MEM_mallocN(field_size, "temp buffer for light cache filter g channel");
+ new_b = MEM_mallocN(field_size, "temp buffer for light cache filter b channel");
+
+ memcpy(new_r, vp->data_r, field_size);
+ memcpy(new_g, vp->data_g, field_size);
+ memcpy(new_b, vp->data_b, field_size);
+
+ for (z=0; z < vp->res[2]; z++) {
+ for (y=0; y < vp->res[1]; y++) {
+ for (x=0; x < vp->res[0]; x++) {
+ /* trigger for outside mesh */
+ const int64_t i = BLI_VOXEL_INDEX(x, y, z, vp->res);
+ if (vp->data_r[i] < -0.f)
+ new_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
+ if (vp->data_g[i] < -0.f)
+ new_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
+ if (vp->data_b[i] < -0.f)
+ new_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
+ }
+ }
+ }
+
+ SWAP(float *, vp->data_r, new_r);
+ SWAP(float *, vp->data_g, new_g);
+ SWAP(float *, vp->data_b, new_b);
+
+ if (new_r) { MEM_freeN(new_r); new_r=NULL; }
+ if (new_g) { MEM_freeN(new_g); new_g=NULL; }
+ if (new_b) { MEM_freeN(new_b); new_b=NULL; }
+}
+#endif
+
+/* has a pad of 1 voxel surrounding the core for boundary simulation */
+BLI_INLINE int64_t ms_I(int x, int y, int z, const int *n)
+{
+ /* different ordering to light cache */
+ return ((int64_t)x * (int64_t)(n[1] + 2) * (int64_t)(n[2] + 2) +
+ (int64_t)y * (int64_t)(n[2] + 2) +
+ (int64_t)z);
+}
+
+/* has a pad of 1 voxel surrounding the core for boundary simulation */
+BLI_INLINE int64_t v_I_pad(int x, int y, int z, const int *n)
+{
+ /* same ordering to light cache, with padding */
+ return ((int64_t)z * (int64_t)(n[1] + 2) * (int64_t)(n[0] + 2) +
+ (int64_t)y * (int64_t)(n[0] + 2) +
+ (int64_t)x);
+}
+
+BLI_INLINE int64_t lc_to_ms_I(int x, int y, int z, const int *n)
+{
+ /* converting light cache index to multiple scattering index */
+ return ((int64_t)(x - 1) * ((int64_t)n[1] * (int64_t)n[2]) +
+ (int64_t)(y - 1) * ((int64_t)n[2]) +
+ (int64_t)(z - 1));
+}
+
+/* *** multiple scattering approximation *** */
+
+/* get the total amount of light energy in the light cache. used to normalize after multiple scattering */
+static float total_ss_energy(Render *re, int do_test_break, VolumePrecache *vp)
+{
+ int x, y, z;
+ const int *res = vp->res;
+ float energy=0.f;
+
+ for (z=0; z < res[2]; z++) {
+ for (y=0; y < res[1]; y++) {
+ for (x=0; x < res[0]; x++) {
+ const int64_t i = BLI_VOXEL_INDEX(x, y, z, res);
+
+ if (vp->data_r[i] > 0.f) energy += vp->data_r[i];
+ if (vp->data_g[i] > 0.f) energy += vp->data_g[i];
+ if (vp->data_b[i] > 0.f) energy += vp->data_b[i];
+ }
+ }
+
+ if (do_test_break && re->test_break(re->tbh)) break;
+ }
+
+ return energy;
+}
+
+static float total_ms_energy(Render *re, int do_test_break, float *sr, float *sg, float *sb, const int res[3])
+{
+ int x, y, z;
+ float energy=0.f;
+
+ for (z=1;z<=res[2];z++) {
+ for (y=1;y<=res[1];y++) {
+ for (x=1;x<=res[0];x++) {
+ const int64_t i = ms_I(x, y, z, res);
+
+ if (sr[i] > 0.f) energy += sr[i];
+ if (sg[i] > 0.f) energy += sg[i];
+ if (sb[i] > 0.f) energy += sb[i];
+ }
+ }
+
+ if (do_test_break && re->test_break(re->tbh)) break;
+ }
+
+ return energy;
+}
+
+/**
+ * \param n: the unpadded resolution
+ */
+static void ms_diffuse(Render *re, int do_test_break, const float *x0, float *x, float diff, const int n[3])
+{
+ int i, j, k, l;
+ const float dt = VOL_MS_TIMESTEP;
+ int64_t size = (int64_t)n[0] * (int64_t)n[1] * (int64_t)n[2];
+ const float a = dt * diff * size;
+
+ for (l=0; l<20; l++) {
+ for (k=1; k<=n[2]; k++) {
+ for (j=1; j<=n[1]; j++) {
+ for (i=1; i<=n[0]; i++) {
+ x[v_I_pad(i, j, k, n)] =
+ ((x0[v_I_pad(i, j, k, n)]) + (
+ (x0[v_I_pad(i - 1, j, k, n)] +
+ x0[v_I_pad(i + 1, j, k, n)] +
+ x0[v_I_pad(i, j - 1, k, n)] +
+ x0[v_I_pad(i, j + 1, k, n)] +
+ x0[v_I_pad(i, j, k - 1, n)] +
+ x0[v_I_pad(i, j, k + 1, n)]) * a) / (1 + 6 * a));
+ }
+ }
+
+ if (do_test_break && re->test_break(re->tbh)) break;
+ }
+
+ if (re->test_break(re->tbh)) break;
+ }
+}
+
+static void multiple_scattering_diffusion(Render *re, VolumePrecache *vp, Material *ma)
+{
+ const float diff = ma->vol.ms_diff * 0.001f; /* compensate for scaling for a nicer UI range */
+ const int simframes = (int)(ma->vol.ms_spread * (float)max_iii(vp->res[0], vp->res[1], vp->res[2]));
+ const int shade_type = ma->vol.shade_type;
+ float fac = ma->vol.ms_intensity;
+
+ int x, y, z, m;
+ const int *n = vp->res;
+ const int size = (n[0]+2)*(n[1]+2)*(n[2]+2);
+ const int do_test_break = (size > 100000);
+ double time, lasttime= PIL_check_seconds_timer();
+ float total;
+ float c=1.0f;
+ float origf; /* factor for blending in original light cache */
+ float energy_ss, energy_ms;
+
+ float *sr0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
+ float *sr=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
+ float *sg0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
+ float *sg=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
+ float *sb0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
+ float *sb=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
+
+ total = (float)(n[0]*n[1]*n[2]*simframes);
+
+ energy_ss = total_ss_energy(re, do_test_break, vp);
+
+ /* Scattering as diffusion pass */
+ for (m=0; m<simframes; m++) {
+ /* add sources */
+ for (z=1; z<=n[2]; z++) {
+ for (y=1; y<=n[1]; y++) {
+ for (x=1; x<=n[0]; x++) {
+ const int64_t i = lc_to_ms_I(x, y, z, n); //lc index
+ const int64_t j = ms_I(x, y, z, n); //ms index
+
+ time= PIL_check_seconds_timer();
+ c++;
+ if (vp->data_r[i] > 0.0f)
+ sr[j] += vp->data_r[i];
+ if (vp->data_g[i] > 0.0f)
+ sg[j] += vp->data_g[i];
+ if (vp->data_b[i] > 0.0f)
+ sb[j] += vp->data_b[i];
+
+ /* Displays progress every second */
+ if (time-lasttime>1.0) {
+ char str[64];
+ BLI_snprintf(str, sizeof(str), IFACE_("Simulating multiple scattering: %d%%"),
+ (int)(100.0f * (c / total)));
+ re->i.infostr = str;
+ re->stats_draw(re->sdh, &re->i);
+ re->i.infostr = NULL;
+ lasttime= time;
+ }
+ }
+ }
+
+ if (do_test_break && re->test_break(re->tbh)) break;
+ }
+
+ if (re->test_break(re->tbh)) break;
+
+ SWAP(float *, sr, sr0);
+ SWAP(float *, sg, sg0);
+ SWAP(float *, sb, sb0);
+
+ /* main diffusion simulation */
+ ms_diffuse(re, do_test_break, sr0, sr, diff, n);
+ ms_diffuse(re, do_test_break, sg0, sg, diff, n);
+ ms_diffuse(re, do_test_break, sb0, sb, diff, n);
+
+ if (re->test_break(re->tbh)) break;
+ }
+
+ /* normalization factor to conserve energy */
+ energy_ms = total_ms_energy(re, do_test_break, sr, sg, sb, n);
+ fac *= (energy_ss / energy_ms);
+
+ /* blend multiple scattering back in the light cache */
+ if (shade_type == MA_VOL_SHADE_SHADEDPLUSMULTIPLE) {
+ /* conserve energy - half single, half multiple */
+ origf = 0.5f;
+ fac *= 0.5f;
+ }
+ else {
+ origf = 0.0f;
+ }
+
+ for (z=1;z<=n[2];z++) {
+ for (y=1;y<=n[1];y++) {
+ for (x=1;x<=n[0];x++) {
+ const int64_t i = lc_to_ms_I(x, y, z, n); //lc index
+ const int64_t j = ms_I(x, y, z, n); //ms index
+
+ vp->data_r[i] = origf * vp->data_r[i] + fac * sr[j];
+ vp->data_g[i] = origf * vp->data_g[i] + fac * sg[j];
+ vp->data_b[i] = origf * vp->data_b[i] + fac * sb[j];
+ }
+ }
+
+ if (do_test_break && re->test_break(re->tbh)) break;
+ }
+
+ MEM_freeN(sr0);
+ MEM_freeN(sr);
+ MEM_freeN(sg0);
+ MEM_freeN(sg);
+ MEM_freeN(sb0);
+ MEM_freeN(sb);
+}
+
+
+
+#if 0 /* debug stuff */
+static void *vol_precache_part_test(void *data)
+{
+ VolPrecachePart *pa = data;
+
+ printf("part number: %d\n", pa->num);
+ printf("done: %d\n", pa->done);
+ printf("x min: %d x max: %d\n", pa->minx, pa->maxx);
+ printf("y min: %d y max: %d\n", pa->miny, pa->maxy);
+ printf("z min: %d z max: %d\n", pa->minz, pa->maxz);
+
+ return NULL;
+}
+#endif
+
+/* Iterate over the 3d voxel grid, and fill the voxels with scattering information
+ *
+ * It's stored in memory as 3 big float grids next to each other, one for each RGB channel.
+ * I'm guessing the memory alignment may work out better this way for the purposes
+ * of doing linear interpolation, but I haven't actually tested this theory! :)
+ */
+typedef struct VolPrecacheState {
+ double lasttime;
+ unsigned int doneparts;
+ unsigned int totparts;
+} VolPrecacheState;
+
+static void vol_precache_part(TaskPool * __restrict pool, void *taskdata, int UNUSED(threadid))
+{
+ VolPrecacheState *state = (VolPrecacheState *)BLI_task_pool_userdata(pool);
+ VolPrecachePart *pa = (VolPrecachePart *)taskdata;
+ Render *re = pa->re;
+
+ ObjectInstanceRen *obi = pa->obi;
+ RayObject *tree = pa->tree;
+ ShadeInput *shi = pa->shi;
+ float scatter_col[3] = {0.f, 0.f, 0.f};
+ float co[3], cco[3], view[3];
+ int x, y, z;
+ int res[3];
+ double time;
+
+ if (re->test_break && re->test_break(re->tbh))
+ return;
+
+ //printf("thread id %d\n", threadid);
+
+ res[0]= pa->res[0];
+ res[1]= pa->res[1];
+ res[2]= pa->res[2];
+
+ for (z= pa->minz; z < pa->maxz; z++) {
+ co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f));
+
+ for (y= pa->miny; y < pa->maxy; y++) {
+ co[1] = pa->bbmin[1] + (pa->voxel[1] * (y + 0.5f));
+
+ for (x=pa->minx; x < pa->maxx; x++) {
+ int64_t i;
+ co[0] = pa->bbmin[0] + (pa->voxel[0] * (x + 0.5f));
+
+ if (re->test_break && re->test_break(re->tbh))
+ break;
+
+ /* convert from world->camera space for shading */
+ mul_v3_m4v3(cco, pa->viewmat, co);
+
+ i = BLI_VOXEL_INDEX(x, y, z, res);
+
+ /* don't bother if the point is not inside the volume mesh */
+ if (!point_inside_obi(tree, obi, cco)) {
+ obi->volume_precache->data_r[i] = -1.0f;
+ obi->volume_precache->data_g[i] = -1.0f;
+ obi->volume_precache->data_b[i] = -1.0f;
+ continue;
+ }
+
+ copy_v3_v3(view, cco);
+ normalize_v3(view);
+ vol_get_scattering(shi, scatter_col, cco, view);
+
+ obi->volume_precache->data_r[i] = scatter_col[0];
+ obi->volume_precache->data_g[i] = scatter_col[1];
+ obi->volume_precache->data_b[i] = scatter_col[2];
+
+ }
+ }
+ }
+
+ unsigned int doneparts = atomic_add_and_fetch_u(&state->doneparts, 1);
+
+ time = PIL_check_seconds_timer();
+ if (time - state->lasttime > 1.0) {
+ ThreadMutex *mutex = BLI_task_pool_user_mutex(pool);
+
+ if (BLI_mutex_trylock(mutex)) {
+ char str[64];
+ float ratio = (float)doneparts/(float)state->totparts;
+ BLI_snprintf(str, sizeof(str), IFACE_("Precaching volume: %d%%"), (int)(100.0f * ratio));
+ re->i.infostr = str;
+ re->stats_draw(re->sdh, &re->i);
+ re->i.infostr = NULL;
+ state->lasttime = time;
+
+ BLI_mutex_unlock(mutex);
+ }
+ }
+}
+
+static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Material *ma, ShadeInput *shi)
+{
+ memset(shi, 0, sizeof(ShadeInput));
+ shi->depth= 1;
+ shi->mask= 1;
+ shi->mat = ma;
+ shi->vlr = NULL;
+ memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); /* note, keep this synced with render_types.h */
+ shi->har= shi->mat->har;
+ shi->obi= obi;
+ shi->obr= obi->obr;
+ shi->lay = re->lay;
+}
+
+static void precache_launch_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi)
+{
+ TaskScheduler *task_scheduler;
+ TaskPool *task_pool;
+ VolumePrecache *vp = obi->volume_precache;
+ VolPrecacheState state;
+ int i=0, x, y, z;
+ float voxel[3];
+ int sizex, sizey, sizez;
+ float bbmin[3], bbmax[3];
+ const int *res;
+ int minx, maxx;
+ int miny, maxy;
+ int minz, maxz;
+ int totthread = re->r.threads;
+ int parts[3];
+
+ if (!vp) return;
+
+ /* currently we just subdivide the box, number of threads per side */
+ parts[0] = parts[1] = parts[2] = totthread;
+ res = vp->res;
+
+ /* setup task scheduler */
+ memset(&state, 0, sizeof(state));
+ state.doneparts = 0;
+ state.totparts = parts[0]*parts[1]*parts[2];
+ state.lasttime = PIL_check_seconds_timer();
+
+ task_scheduler = BLI_task_scheduler_create(totthread);
+ task_pool = BLI_task_pool_create(task_scheduler, &state);
+
+ /* using boundbox in worldspace */
+ global_bounds_obi(re, obi, bbmin, bbmax);
+ sub_v3_v3v3(voxel, bbmax, bbmin);
+
+ voxel[0] /= (float)res[0];
+ voxel[1] /= (float)res[1];
+ voxel[2] /= (float)res[2];
+
+ for (x=0; x < parts[0]; x++) {
+ sizex = ceil(res[0] / (float)parts[0]);
+ minx = x * sizex;
+ maxx = minx + sizex;
+ maxx = (maxx>res[0])?res[0]:maxx;
+
+ for (y=0; y < parts[1]; y++) {
+ sizey = ceil(res[1] / (float)parts[1]);
+ miny = y * sizey;
+ maxy = miny + sizey;
+ maxy = (maxy>res[1])?res[1]:maxy;
+
+ for (z=0; z < parts[2]; z++) {
+ VolPrecachePart *pa= MEM_callocN(sizeof(VolPrecachePart), "new precache part");
+
+ sizez = ceil(res[2] / (float)parts[2]);
+ minz = z * sizez;
+ maxz = minz + sizez;
+ maxz = (maxz>res[2])?res[2]:maxz;
+
+ pa->re = re;
+ pa->num = i;
+ pa->tree = tree;
+ pa->shi = shi;
+ pa->obi = obi;
+ copy_m4_m4(pa->viewmat, re->viewmat);
+
+ copy_v3_v3(pa->bbmin, bbmin);
+ copy_v3_v3(pa->voxel, voxel);
+ copy_v3_v3_int(pa->res, res);
+
+ pa->minx = minx; pa->maxx = maxx;
+ pa->miny = miny; pa->maxy = maxy;
+ pa->minz = minz; pa->maxz = maxz;
+
+ BLI_task_pool_push(task_pool, vol_precache_part, pa, true, TASK_PRIORITY_HIGH);
+
+ i++;
+ }
+ }
+ }
+
+ /* work and wait until tasks are done */
+ BLI_task_pool_work_and_wait(task_pool);
+
+ /* free */
+ BLI_task_pool_free(task_pool);
+ BLI_task_scheduler_free(task_scheduler);
+}
+
+/* calculate resolution from bounding box in world space */
+static int precache_resolution(Render *re, VolumePrecache *vp, ObjectInstanceRen *obi, int res)
+{
+ float dim[3], div;
+ float bbmin[3], bbmax[3];
+
+ /* bound box in global space */
+ global_bounds_obi(re, obi, bbmin, bbmax);
+ sub_v3_v3v3(dim, bbmax, bbmin);
+
+ div = max_fff(dim[0], dim[1], dim[2]);
+ dim[0] /= div;
+ dim[1] /= div;
+ dim[2] /= div;
+
+ vp->res[0] = ceil(dim[0] * res);
+ vp->res[1] = ceil(dim[1] * res);
+ vp->res[2] = ceil(dim[2] * res);
+
+ if ((vp->res[0] < 1) || (vp->res[1] < 1) || (vp->res[2] < 1))
+ return 0;
+
+ return 1;
+}
+
+/* Precache a volume into a 3D voxel grid.
+ * The voxel grid is stored in the ObjectInstanceRen,
+ * in camera space, aligned with the ObjectRen's bounding box.
+ * Resolution is defined by the user.
+ */
+static void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma)
+{
+ VolumePrecache *vp;
+ RayObject *tree;
+ ShadeInput shi;
+
+ R = *re;
+
+ /* create a raytree with just the faces of the instanced ObjectRen,
+ * used for checking if the cached point is inside or outside. */
+ tree = makeraytree_object(&R, obi);
+ if (!tree) return;
+
+ vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache");
+ obi->volume_precache = vp;
+
+ if (!precache_resolution(re, vp, obi, ma->vol.precache_resolution)) {
+ MEM_freeN(vp);
+ vp = NULL;
+ return;
+ }
+
+ vp->data_r = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data red channel");
+ vp->data_g = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data green channel");
+ vp->data_b = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data blue channel");
+ if (vp->data_r==NULL || vp->data_g==NULL || vp->data_b==NULL) {
+ MEM_freeN(vp);
+ return;
+ }
+
+ /* Need a shadeinput to calculate scattering */
+ precache_setup_shadeinput(re, obi, ma, &shi);
+
+ precache_launch_parts(re, tree, &shi, obi);
+
+ if (tree) {
+ /* TODO: makeraytree_object creates a tree and saves it on OBI,
+ * if we free this tree we should also clear other pointers to it */
+ //RE_rayobject_free(tree);
+ //tree= NULL;
+ }
+
+ if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) {
+ /* this should be before the filtering */
+ multiple_scattering_diffusion(re, obi->volume_precache, ma);
+ }
+
+ lightcache_filter(obi->volume_precache);
+}
+
+static int using_lightcache(Material *ma)
+{
+ return (((ma->vol.shadeflag & MA_VOL_PRECACHESHADING) && (ma->vol.shade_type == MA_VOL_SHADE_SHADED)) ||
+ (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)));
+}
+
+/* loop through all objects (and their associated materials)
+ * marked for pre-caching in convertblender.c, and pre-cache them */
+void volume_precache(Render *re)
+{
+ ObjectInstanceRen *obi;
+ VolumeOb *vo;
+
+ re->i.infostr = IFACE_("Volume preprocessing");
+ re->stats_draw(re->sdh, &re->i);
+
+ for (vo= re->volumes.first; vo; vo= vo->next) {
+ if (using_lightcache(vo->ma)) {
+ for (obi= re->instancetable.first; obi; obi= obi->next) {
+ if (obi->obr == vo->obr) {
+ vol_precache_objectinstance_threads(re, obi, vo->ma);
+
+ if (re->test_break && re->test_break(re->tbh))
+ break;
+ }
+ }
+
+ if (re->test_break && re->test_break(re->tbh))
+ break;
+ }
+ }
+
+ re->i.infostr = NULL;
+ re->stats_draw(re->sdh, &re->i);
+}
+
+void free_volume_precache(Render *re)
+{
+ ObjectInstanceRen *obi;
+
+ for (obi= re->instancetable.first; obi; obi= obi->next) {
+ if (obi->volume_precache != NULL) {
+ MEM_freeN(obi->volume_precache->data_r);
+ MEM_freeN(obi->volume_precache->data_g);
+ MEM_freeN(obi->volume_precache->data_b);
+ MEM_freeN(obi->volume_precache->bbmin);
+ MEM_freeN(obi->volume_precache->bbmax);
+ MEM_freeN(obi->volume_precache);
+ obi->volume_precache = NULL;
+ }
+ }
+
+ BLI_freelistN(&re->volumes);
+}
+
+int point_inside_volume_objectinstance(Render *re, ObjectInstanceRen *obi, const float co[3])
+{
+ RayObject *tree;
+ int inside=0;
+
+ tree = makeraytree_object(re, obi);
+ if (!tree) return 0;
+
+ inside = point_inside_obi(tree, obi, co);
+
+ //TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
+ //RE_rayobject_free(tree);
+ //tree= NULL;
+
+ return inside;
+}
+
diff --git a/source/blender/render/intern/source/volumetric.c b/source/blender/render/intern/source/volumetric.c
new file mode 100644
index 00000000000..583353ed8cf
--- /dev/null
+++ b/source/blender/render/intern/source/volumetric.c
@@ -0,0 +1,836 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Matt Ebb, Raul Fernandez Hernandez (Farsthary)
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/volumetric.c
+ * \ingroup render
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <float.h>
+
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_voxel.h"
+#include "BLI_utildefines.h"
+
+#include "RE_shader_ext.h"
+
+#include "IMB_colormanagement.h"
+
+#include "DNA_material_types.h"
+#include "DNA_group_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_meta_types.h"
+
+
+#include "render_types.h"
+#include "pixelshading.h"
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "renderdatabase.h"
+#include "shading.h"
+#include "shadbuf.h"
+#include "texture.h"
+#include "volumetric.h"
+#include "volume_precache.h"
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* tracing */
+static float vol_get_shadow(ShadeInput *shi, LampRen *lar, const float co[3])
+{
+ float visibility = 1.f;
+
+ if (lar->shb) {
+ float dxco[3] = {0.f, 0.f, 0.f}, dyco[3] = {0.f, 0.f, 0.f};
+
+ visibility = testshadowbuf(&R, lar->shb, co, dxco, dyco, 1.0, 0.0);
+ }
+ else if (lar->mode & LA_SHAD_RAY) {
+ /* trace shadow manually, no good lamp api atm */
+ Isect is;
+
+ copy_v3_v3(is.start, co);
+ if (lar->type == LA_SUN || lar->type == LA_HEMI) {
+ is.dir[0] = -lar->vec[0];
+ is.dir[1] = -lar->vec[1];
+ is.dir[2] = -lar->vec[2];
+ is.dist = R.maxdist;
+ }
+ else {
+ sub_v3_v3v3(is.dir, lar->co, is.start);
+ is.dist = normalize_v3(is.dir);
+ }
+
+ is.mode = RE_RAY_MIRROR;
+ is.check = RE_CHECK_VLR_NON_SOLID_MATERIAL;
+ is.skip = 0;
+
+ if (lar->mode & (LA_LAYER | LA_LAYER_SHADOW))
+ is.lay = lar->lay;
+ else
+ is.lay = -1;
+
+ is.orig.ob = NULL;
+ is.orig.face = NULL;
+ is.last_hit = lar->last_hit[shi->thread];
+
+ RE_instance_rotate_ray(shi->obi, &is);
+
+ if (RE_rayobject_raycast(R.raytree, &is)) {
+ RE_instance_rotate_ray_restore(shi->obi, &is);
+
+ visibility = 0.f;
+ }
+
+ lar->last_hit[shi->thread] = is.last_hit;
+ }
+ return visibility;
+}
+
+static int vol_get_bounds(ShadeInput *shi, const float co[3], const float vec[3], float hitco[3], Isect *isect, int intersect_type)
+{
+
+ copy_v3_v3(isect->start, co);
+ copy_v3_v3(isect->dir, vec);
+ isect->dist = FLT_MAX;
+ isect->mode = RE_RAY_MIRROR;
+ isect->last_hit = NULL;
+ isect->lay = -1;
+ isect->check = RE_CHECK_VLR_NONE;
+
+ if (intersect_type == VOL_BOUNDS_DEPTH) {
+ isect->skip = RE_SKIP_VLR_NEIGHBOUR;
+ isect->orig.face = (void *)shi->vlr;
+ isect->orig.ob = (void *)shi->obi;
+ }
+ else { // if (intersect_type == VOL_BOUNDS_SS) {
+ isect->skip = 0;
+ isect->orig.face = NULL;
+ isect->orig.ob = NULL;
+ }
+
+ RE_instance_rotate_ray(shi->obi, isect);
+
+ if (RE_rayobject_raycast(R.raytree, isect)) {
+ RE_instance_rotate_ray_restore(shi->obi, isect);
+
+ hitco[0] = isect->start[0] + isect->dist * isect->dir[0];
+ hitco[1] = isect->start[1] + isect->dist * isect->dir[1];
+ hitco[2] = isect->start[2] + isect->dist * isect->dir[2];
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static void shade_intersection(ShadeInput *shi, float col_r[4], Isect *is)
+{
+ ShadeInput shi_new;
+ ShadeResult shr_new;
+
+ memset(&shi_new, 0, sizeof(ShadeInput));
+
+ shi_new.mask = shi->mask;
+ shi_new.osatex = shi->osatex;
+ shi_new.thread = shi->thread;
+ shi_new.depth = shi->depth + 1;
+ shi_new.volume_depth = shi->volume_depth + 1;
+ shi_new.xs = shi->xs;
+ shi_new.ys = shi->ys;
+ shi_new.lay = shi->lay;
+ shi_new.passflag = SCE_PASS_COMBINED; /* result of tracing needs no pass info */
+ shi_new.combinedflag = 0xFFFFFF; /* ray trace does all options */
+ shi_new.light_override = shi->light_override;
+ shi_new.mat_override = shi->mat_override;
+
+ copy_v3_v3(shi_new.camera_co, is->start);
+
+ memset(&shr_new, 0, sizeof(ShadeResult));
+
+ /* hardcoded limit of 100 for now - prevents problems in weird geometry */
+ if (shi->volume_depth < 100) {
+ shade_ray(is, &shi_new, &shr_new);
+ }
+
+ copy_v3_v3(col_r, shr_new.combined);
+ col_r[3] = shr_new.alpha;
+}
+
+static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, const float co[3], float col_r[4])
+{
+ Isect isect;
+
+ copy_v3_v3(isect.start, co);
+ copy_v3_v3(isect.dir, shi->view);
+ isect.dist = FLT_MAX;
+
+ isect.mode = RE_RAY_MIRROR;
+ isect.check = RE_CHECK_VLR_NONE;
+ isect.skip = RE_SKIP_VLR_NEIGHBOUR;
+ isect.orig.ob = (void *) shi->obi;
+ isect.orig.face = (void *)vlr;
+ isect.last_hit = NULL;
+ isect.lay = -1;
+
+ /* check to see if there's anything behind the volume, otherwise shade the sky */
+ RE_instance_rotate_ray(shi->obi, &isect);
+
+ if (RE_rayobject_raycast(R.raytree, &isect)) {
+ RE_instance_rotate_ray_restore(shi->obi, &isect);
+
+ shade_intersection(shi, col_r, &isect);
+ }
+ else {
+ shadeSkyView(col_r, co, shi->view, NULL, shi->thread);
+ shadeSunView(col_r, shi->view);
+ }
+}
+
+
+/* trilinear interpolation */
+static void vol_get_precached_scattering(Render *re, ShadeInput *shi, float scatter_col[3], const float co[3])
+{
+ VolumePrecache *vp = shi->obi->volume_precache;
+ float bbmin[3], bbmax[3], dim[3];
+ float world_co[3], sample_co[3];
+
+ if (!vp) return;
+
+ /* find sample point in global space bounding box 0.0-1.0 */
+ global_bounds_obi(re, shi->obi, bbmin, bbmax);
+ sub_v3_v3v3(dim, bbmax, bbmin);
+ mul_v3_m4v3(world_co, re->viewinv, co);
+
+ /* sample_co in 0.0-1.0 */
+ sample_co[0] = (world_co[0] - bbmin[0]) / dim[0];
+ sample_co[1] = (world_co[1] - bbmin[1]) / dim[1];
+ sample_co[2] = (world_co[2] - bbmin[2]) / dim[2];
+
+ scatter_col[0] = BLI_voxel_sample_triquadratic(vp->data_r, vp->res, sample_co);
+ scatter_col[1] = BLI_voxel_sample_triquadratic(vp->data_g, vp->res, sample_co);
+ scatter_col[2] = BLI_voxel_sample_triquadratic(vp->data_b, vp->res, sample_co);
+}
+
+/* Meta object density, brute force for now
+ * (might be good enough anyway, don't need huge number of metaobs to model volumetric objects */
+static float metadensity(Object *ob, const float co[3])
+{
+ float mat[4][4], imat[4][4], dens = 0.f;
+ MetaBall *mb = (MetaBall *)ob->data;
+ MetaElem *ml;
+
+ /* transform co to meta-element */
+ float tco[3] = {co[0], co[1], co[2]};
+ mul_m4_m4m4(mat, R.viewmat, ob->obmat);
+ invert_m4_m4(imat, mat);
+ mul_m4_v3(imat, tco);
+
+ for (ml = mb->elems.first; ml; ml = ml->next) {
+ float bmat[3][3], dist2;
+
+ /* element rotation transform */
+ float tp[3] = {ml->x - tco[0], ml->y - tco[1], ml->z - tco[2]};
+ quat_to_mat3(bmat, ml->quat);
+ transpose_m3(bmat); /* rot.only, so inverse == transpose */
+ mul_m3_v3(bmat, tp);
+
+ /* MB_BALL default */
+ switch (ml->type) {
+ case MB_ELIPSOID:
+ tp[0] /= ml->expx;
+ tp[1] /= ml->expy;
+ tp[2] /= ml->expz;
+ break;
+ case MB_CUBE:
+ tp[2] = (tp[2] > ml->expz) ? (tp[2] - ml->expz) : ((tp[2] < -ml->expz) ? (tp[2] + ml->expz) : 0.f);
+ /* no break, xy as plane */
+ ATTR_FALLTHROUGH;
+ case MB_PLANE:
+ tp[1] = (tp[1] > ml->expy) ? (tp[1] - ml->expy) : ((tp[1] < -ml->expy) ? (tp[1] + ml->expy) : 0.f);
+ /* no break, x as tube */
+ ATTR_FALLTHROUGH;
+ case MB_TUBE:
+ tp[0] = (tp[0] > ml->expx) ? (tp[0] - ml->expx) : ((tp[0] < -ml->expx) ? (tp[0] + ml->expx) : 0.f);
+ }
+
+ /* ml->rad2 is not set */
+ dist2 = 1.0f - (dot_v3v3(tp, tp) / (ml->rad * ml->rad));
+ if (dist2 > 0.f)
+ dens += (ml->flag & MB_NEGATIVE) ? -ml->s * dist2 * dist2 * dist2 : ml->s * dist2 * dist2 * dist2;
+ }
+
+ dens -= mb->thresh;
+ return (dens < 0.f) ? 0.f : dens;
+}
+
+float vol_get_density(struct ShadeInput *shi, const float co[3])
+{
+ float density = shi->mat->vol.density;
+ float density_scale = shi->mat->vol.density_scale;
+
+ if (shi->mat->mapto_textured & MAP_DENSITY)
+ do_volume_tex(shi, co, MAP_DENSITY, NULL, &density, &R);
+
+ /* if meta-object, modulate by metadensity without increasing it */
+ if (shi->obi->obr->ob->type == OB_MBALL) {
+ const float md = metadensity(shi->obi->obr->ob, co);
+ if (md < 1.f) density *= md;
+ }
+
+ return density * density_scale;
+}
+
+
+/* Color of light that gets scattered out by the volume */
+/* Uses same physically based scattering parameter as in transmission calculations,
+ * along with artificial reflection scale/reflection color tint */
+static void vol_get_reflection_color(ShadeInput *shi, float ref_col[3], const float co[3])
+{
+ float scatter = shi->mat->vol.scattering;
+ float reflection = shi->mat->vol.reflection;
+ copy_v3_v3(ref_col, shi->mat->vol.reflection_col);
+
+ if (shi->mat->mapto_textured & (MAP_SCATTERING + MAP_REFLECTION_COL))
+ do_volume_tex(shi, co, MAP_SCATTERING + MAP_REFLECTION_COL, ref_col, &scatter, &R);
+
+ /* only one single float parameter at a time... :s */
+ if (shi->mat->mapto_textured & (MAP_REFLECTION))
+ do_volume_tex(shi, co, MAP_REFLECTION, NULL, &reflection, &R);
+
+ ref_col[0] = reflection * ref_col[0] * scatter;
+ ref_col[1] = reflection * ref_col[1] * scatter;
+ ref_col[2] = reflection * ref_col[2] * scatter;
+}
+
+/* compute emission component, amount of radiance to add per segment
+ * can be textured with 'emit' */
+static void vol_get_emission(ShadeInput *shi, float emission_col[3], const float co[3])
+{
+ float emission = shi->mat->vol.emission;
+ copy_v3_v3(emission_col, shi->mat->vol.emission_col);
+
+ if (shi->mat->mapto_textured & (MAP_EMISSION + MAP_EMISSION_COL))
+ do_volume_tex(shi, co, MAP_EMISSION + MAP_EMISSION_COL, emission_col, &emission, &R);
+
+ emission_col[0] = emission_col[0] * emission;
+ emission_col[1] = emission_col[1] * emission;
+ emission_col[2] = emission_col[2] * emission;
+}
+
+
+/* A combination of scattering and absorption -> known as sigma T.
+ * This can possibly use a specific scattering color,
+ * and absorption multiplier factor too, but these parameters are left out for simplicity.
+ * It's easy enough to get a good wide range of results with just these two parameters. */
+static void vol_get_sigma_t(ShadeInput *shi, float sigma_t[3], const float co[3])
+{
+ /* technically absorption, but named transmission color
+ * since it describes the effect of the coloring *after* absorption */
+ float transmission_col[3] = {shi->mat->vol.transmission_col[0], shi->mat->vol.transmission_col[1], shi->mat->vol.transmission_col[2]};
+ float scattering = shi->mat->vol.scattering;
+
+ if (shi->mat->mapto_textured & (MAP_SCATTERING + MAP_TRANSMISSION_COL))
+ do_volume_tex(shi, co, MAP_SCATTERING + MAP_TRANSMISSION_COL, transmission_col, &scattering, &R);
+
+ sigma_t[0] = (1.0f - transmission_col[0]) + scattering;
+ sigma_t[1] = (1.0f - transmission_col[1]) + scattering;
+ sigma_t[2] = (1.0f - transmission_col[2]) + scattering;
+}
+
+/* phase function - determines in which directions the light
+ * is scattered in the volume relative to incoming direction
+ * and view direction */
+static float vol_get_phasefunc(ShadeInput *UNUSED(shi), float g, const float w[3], const float wp[3])
+{
+ const float normalize = 0.25f; // = 1.f/4.f = M_PI/(4.f*M_PI)
+
+ /* normalization constant is 1/4 rather than 1/4pi, since
+ * Blender's shading system doesn't normalize for
+ * energy conservation - eg. multiplying by pdf ( 1/pi for a lambert brdf ).
+ * This means that lambert surfaces in Blender are pi times brighter than they 'should be'
+ * and therefore, with correct energy conservation, volumes will darker than other solid objects,
+ * for the same lighting intensity.
+ * To correct this, scale up the phase function values by pi
+ * until Blender's shading system supports this better. --matt
+ */
+
+ if (g == 0.f) { /* isotropic */
+ return normalize * 1.f;
+ }
+ else { /* schlick */
+ const float k = 1.55f * g - 0.55f * g * g * g;
+ const float kcostheta = k * dot_v3v3(w, wp);
+ return normalize * (1.f - k * k) / ((1.f - kcostheta) * (1.f - kcostheta));
+ }
+
+ /* not used, but here for reference: */
+#if 0
+ switch (phasefunc_type) {
+ case MA_VOL_PH_MIEHAZY:
+ return normalize * (0.5f + 4.5f * powf(0.5 * (1.f + costheta), 8.f));
+ case MA_VOL_PH_MIEMURKY:
+ return normalize * (0.5f + 16.5f * powf(0.5 * (1.f + costheta), 32.f));
+ case MA_VOL_PH_RAYLEIGH:
+ return normalize * 3.f / 4.f * (1 + costheta * costheta);
+ case MA_VOL_PH_HG:
+ return normalize * (1.f - g * g) / powf(1.f + g * g - 2.f * g * costheta, 1.5f);
+ case MA_VOL_PH_SCHLICK:
+ {
+ const float k = 1.55f * g - 0.55f * g * g * g;
+ const float kcostheta = k * costheta;
+ return normalize * (1.f - k * k) / ((1.f - kcostheta) * (1.f - kcostheta));
+ }
+ case MA_VOL_PH_ISOTROPIC:
+ default:
+ return normalize * 1.f;
+ }
+#endif
+}
+
+/* Compute transmittance = e^(-attenuation) */
+static void vol_get_transmittance_seg(ShadeInput *shi, float tr[3], float stepsize, const float co[3], float density)
+{
+ /* input density = density at co */
+ float tau[3] = {0.f, 0.f, 0.f};
+ const float stepd = density * stepsize;
+ float sigma_t[3];
+
+ vol_get_sigma_t(shi, sigma_t, co);
+
+ /* homogeneous volume within the sampled distance */
+ tau[0] += stepd * sigma_t[0];
+ tau[1] += stepd * sigma_t[1];
+ tau[2] += stepd * sigma_t[2];
+
+ tr[0] *= expf(-tau[0]);
+ tr[1] *= expf(-tau[1]);
+ tr[2] *= expf(-tau[2]);
+}
+
+/* Compute transmittance = e^(-attenuation) */
+static void vol_get_transmittance(ShadeInput *shi, float tr[3], const float co[3], const float endco[3])
+{
+ float p[3] = {co[0], co[1], co[2]};
+ float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]};
+ float tau[3] = {0.f, 0.f, 0.f};
+
+ float t0 = 0.f;
+ float t1 = normalize_v3(step_vec);
+ float pt0 = t0;
+
+ t0 += shi->mat->vol.stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread));
+ p[0] += t0 * step_vec[0];
+ p[1] += t0 * step_vec[1];
+ p[2] += t0 * step_vec[2];
+ mul_v3_fl(step_vec, shi->mat->vol.stepsize);
+
+ for (; t0 < t1; pt0 = t0, t0 += shi->mat->vol.stepsize) {
+ const float d = vol_get_density(shi, p);
+ const float stepd = (t0 - pt0) * d;
+ float sigma_t[3];
+
+ vol_get_sigma_t(shi, sigma_t, p);
+
+ tau[0] += stepd * sigma_t[0];
+ tau[1] += stepd * sigma_t[1];
+ tau[2] += stepd * sigma_t[2];
+
+ add_v3_v3(p, step_vec);
+ }
+
+ /* return transmittance */
+ tr[0] = expf(-tau[0]);
+ tr[1] = expf(-tau[1]);
+ tr[2] = expf(-tau[2]);
+}
+
+static void vol_shade_one_lamp(struct ShadeInput *shi, const float co[3], const float view[3], LampRen *lar, float lacol[3])
+{
+ float visifac, lv[3], lampdist;
+ float tr[3] = {1.0, 1.0, 1.0};
+ float hitco[3], *atten_co;
+ float p, ref_col[3];
+
+ if (lar->mode & LA_LAYER) if ((lar->lay & shi->obi->lay) == 0) return;
+ if ((lar->lay & shi->lay) == 0) return;
+ if (lar->energy == 0.0f) return;
+
+ if ((visifac = lamp_get_visibility(lar, co, lv, &lampdist)) == 0.f) return;
+
+ copy_v3_v3(lacol, &lar->r);
+
+ if (lar->mode & LA_TEXTURE) {
+ shi->osatex = 0;
+ do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE);
+ }
+
+ mul_v3_fl(lacol, visifac);
+
+ if (ELEM(lar->type, LA_SUN, LA_HEMI))
+ copy_v3_v3(lv, lar->vec);
+ negate_v3(lv);
+
+ if (shi->mat->vol.shade_type == MA_VOL_SHADE_SHADOWED) {
+ mul_v3_fl(lacol, vol_get_shadow(shi, lar, co));
+ }
+ else if (ELEM(shi->mat->vol.shade_type, MA_VOL_SHADE_SHADED, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) {
+ Isect is;
+
+ if (shi->mat->vol.shadeflag & MA_VOL_RECV_EXT_SHADOW) {
+ mul_v3_fl(lacol, vol_get_shadow(shi, lar, co));
+ if (IMB_colormanagement_get_luminance(lacol) < 0.001f) return;
+ }
+
+ /* find minimum of volume bounds, or lamp coord */
+ if (vol_get_bounds(shi, co, lv, hitco, &is, VOL_BOUNDS_SS)) {
+ float dist = len_v3v3(co, hitco);
+ VlakRen *vlr = (VlakRen *)is.hit.face;
+
+ /* simple internal shadowing */
+ if (vlr->mat->material_type == MA_TYPE_SURFACE) {
+ lacol[0] = lacol[1] = lacol[2] = 0.0f;
+ return;
+ }
+
+ if (ELEM(lar->type, LA_SUN, LA_HEMI))
+ /* infinite lights, can never be inside volume */
+ atten_co = hitco;
+ else if (lampdist < dist) {
+ atten_co = lar->co;
+ }
+ else
+ atten_co = hitco;
+
+ vol_get_transmittance(shi, tr, co, atten_co);
+
+ mul_v3_v3v3(lacol, lacol, tr);
+ }
+ else {
+ /* Point is on the outside edge of the volume,
+ * therefore no attenuation, full transmission.
+ * Radiance from lamp remains unchanged */
+ }
+ }
+
+ if (IMB_colormanagement_get_luminance(lacol) < 0.001f) return;
+
+ normalize_v3(lv);
+ p = vol_get_phasefunc(shi, shi->mat->vol.asymmetry, view, lv);
+
+ /* physically based scattering with non-physically based RGB gain */
+ vol_get_reflection_color(shi, ref_col, co);
+
+ lacol[0] *= p * ref_col[0];
+ lacol[1] *= p * ref_col[1];
+ lacol[2] *= p * ref_col[2];
+}
+
+/* single scattering only for now */
+void vol_get_scattering(ShadeInput *shi, float scatter_col[3], const float co[3], const float view[3])
+{
+ ListBase *lights;
+ GroupObject *go;
+ LampRen *lar;
+
+ zero_v3(scatter_col);
+
+ lights = get_lights(shi);
+ for (go = lights->first; go; go = go->next) {
+ float lacol[3] = {0.f, 0.f, 0.f};
+ lar = go->lampren;
+
+ if (lar) {
+ vol_shade_one_lamp(shi, co, view, lar, lacol);
+ add_v3_v3(scatter_col, lacol);
+ }
+ }
+}
+
+
+/*
+ * The main volumetric integrator, using an emission/absorption/scattering model.
+ *
+ * Incoming radiance =
+ *
+ * outgoing radiance from behind surface * beam transmittance/attenuation
+ * + added radiance from all points along the ray due to participating media
+ * --> radiance for each segment =
+ * (radiance added by scattering + radiance added by emission) * beam transmittance/attenuation
+ */
+
+/* For ease of use, I've also introduced a 'reflection' and 'reflection color' parameter, which isn't
+ * physically correct. This works as an RGB tint/gain on out-scattered light, but doesn't affect the light
+ * that is transmitted through the volume. While having wavelength dependent absorption/scattering is more correct,
+ * it also makes it harder to control the overall look of the volume since coloring the outscattered light results
+ * in the inverse color being transmitted through the rest of the volume.
+ */
+static void volumeintegrate(struct ShadeInput *shi, float col[4], const float co[3], const float endco[3])
+{
+ float radiance[3] = {0.f, 0.f, 0.f};
+ float tr[3] = {1.f, 1.f, 1.f};
+ float p[3] = {co[0], co[1], co[2]};
+ float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]};
+ const float stepsize = shi->mat->vol.stepsize;
+
+ float t0 = 0.f;
+ float pt0 = t0;
+ float t1 = normalize_v3(step_vec); /* returns vector length */
+
+ t0 += stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread));
+ p[0] += t0 * step_vec[0];
+ p[1] += t0 * step_vec[1];
+ p[2] += t0 * step_vec[2];
+ mul_v3_fl(step_vec, stepsize);
+
+ for (; t0 < t1; pt0 = t0, t0 += stepsize) {
+ const float density = vol_get_density(shi, p);
+
+ if (density > 0.00001f) {
+ float scatter_col[3] = {0.f, 0.f, 0.f}, emit_col[3];
+ const float stepd = (t0 - pt0) * density;
+
+ /* transmittance component (alpha) */
+ vol_get_transmittance_seg(shi, tr, stepsize, co, density);
+
+ if (t0 > t1 * 0.25f) {
+ /* only use depth cutoff after we've traced a little way into the volume */
+ if (IMB_colormanagement_get_luminance(tr) < shi->mat->vol.depth_cutoff) break;
+ }
+
+ vol_get_emission(shi, emit_col, p);
+
+ if (shi->obi->volume_precache) {
+ float p2[3];
+
+ p2[0] = p[0] + (step_vec[0] * 0.5f);
+ p2[1] = p[1] + (step_vec[1] * 0.5f);
+ p2[2] = p[2] + (step_vec[2] * 0.5f);
+
+ vol_get_precached_scattering(&R, shi, scatter_col, p2);
+ }
+ else
+ vol_get_scattering(shi, scatter_col, p, shi->view);
+
+ radiance[0] += stepd * tr[0] * (emit_col[0] + scatter_col[0]);
+ radiance[1] += stepd * tr[1] * (emit_col[1] + scatter_col[1]);
+ radiance[2] += stepd * tr[2] * (emit_col[2] + scatter_col[2]);
+ }
+ add_v3_v3(p, step_vec);
+ }
+
+ /* multiply original color (from behind volume) with transmittance over entire distance */
+ mul_v3_v3v3(col, tr, col);
+ add_v3_v3(col, radiance);
+
+ /* alpha <-- transmission luminance */
+ col[3] = 1.0f - IMB_colormanagement_get_luminance(tr);
+}
+
+/* the main entry point for volume shading */
+static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int inside_volume)
+{
+ float hitco[3], col[4] = {0.f, 0.f, 0.f, 0.f};
+ const float *startco, *endco;
+ int trace_behind = 1;
+ const int ztransp = ((shi->depth == 0) && (shi->mat->mode & MA_TRANSP) && (shi->mat->mode & MA_ZTRANSP));
+ Isect is;
+
+ /* check for shading an internal face a volume object directly */
+ if (inside_volume == VOL_SHADE_INSIDE)
+ trace_behind = 0;
+ else if (inside_volume == VOL_SHADE_OUTSIDE) {
+ if (shi->flippednor)
+ inside_volume = VOL_SHADE_INSIDE;
+ }
+
+ if (ztransp && inside_volume == VOL_SHADE_INSIDE) {
+ MatInside *mi;
+ int render_this = 0;
+
+ /* don't render the backfaces of ztransp volume materials.
+ *
+ * volume shading renders the internal volume from between the
+ * ' view intersection of the solid volume to the
+ * intersection on the other side, as part of the shading of
+ * the front face.
+ *
+ * Because ztransp renders both front and back faces independently
+ * this will double up, so here we prevent rendering the backface as well,
+ * which would otherwise render the volume in between the camera and the backface
+ * --matt */
+
+ for (mi = R.render_volumes_inside.first; mi; mi = mi->next) {
+ /* weak... */
+ if (mi->ma == shi->mat) render_this = 1;
+ }
+ if (!render_this) return;
+ }
+
+
+ if (inside_volume == VOL_SHADE_INSIDE) {
+ startco = shi->camera_co;
+ endco = shi->co;
+
+ if (trace_behind) {
+ if (!ztransp)
+ /* trace behind the volume object */
+ vol_trace_behind(shi, shi->vlr, endco, col);
+ }
+ else {
+ /* we're tracing through the volume between the camera
+ * and a solid surface, so use that pre-shaded radiance */
+ copy_v4_v4(col, shr->combined);
+ }
+
+ /* shade volume from 'camera' to 1st hit point */
+ volumeintegrate(shi, col, startco, endco);
+ }
+ /* trace to find a backface, the other side bounds of the volume */
+ /* (ray intersect ignores front faces here) */
+ else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) {
+ VlakRen *vlr = (VlakRen *)is.hit.face;
+
+ startco = shi->co;
+ endco = hitco;
+
+ if (!ztransp) {
+ /* if it's another face in the same material */
+ if (vlr->mat == shi->mat) {
+ /* trace behind the 2nd (raytrace) hit point */
+ vol_trace_behind(shi, (VlakRen *)is.hit.face, endco, col);
+ }
+ else {
+ shade_intersection(shi, col, &is);
+ }
+ }
+
+ /* shade volume from 1st hit point to 2nd hit point */
+ volumeintegrate(shi, col, startco, endco);
+ }
+
+ if (ztransp)
+ col[3] = col[3] > 1.f ? 1.f : col[3];
+ else
+ col[3] = 1.f;
+
+ copy_v3_v3(shr->combined, col);
+ shr->alpha = col[3];
+
+ copy_v3_v3(shr->diff, shr->combined);
+ copy_v3_v3(shr->diffshad, shr->diff);
+}
+
+/* Traces a shadow through the object,
+ * pretty much gets the transmission over a ray path */
+void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is)
+{
+ float hitco[3];
+ float tr[3] = {1.0, 1.0, 1.0};
+ Isect is = {{0}};
+ const float *startco, *endco;
+
+ memset(shr, 0, sizeof(ShadeResult));
+
+ /* if 1st hit normal is facing away from the camera,
+ * then we're inside the volume already. */
+ if (shi->flippednor) {
+ startco = last_is->start;
+ endco = shi->co;
+ }
+
+ /* trace to find a backface, the other side bounds of the volume */
+ /* (ray intersect ignores front faces here) */
+ else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) {
+ startco = shi->co;
+ endco = hitco;
+ }
+ else {
+ shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f;
+ shr->alpha = shr->combined[3] = 1.f;
+ return;
+ }
+
+ vol_get_transmittance(shi, tr, startco, endco);
+
+
+ /* if we hit another face in the same volume bounds */
+ /* shift raytrace coordinates to the hit point, to avoid shading volume twice */
+ /* due to idiosyncracy in ray_trace_shadow_tra() */
+ if (is.hit.ob == shi->obi) {
+ copy_v3_v3(shi->co, hitco);
+ last_is->dist += is.dist;
+ shi->vlr = (VlakRen *)is.hit.face;
+ }
+
+
+ copy_v3_v3(shr->combined, tr);
+ shr->combined[3] = 1.0f - IMB_colormanagement_get_luminance(tr);
+ shr->alpha = shr->combined[3];
+}
+
+
+/* delivers a fully filled in ShadeResult, for all passes */
+void shade_volume_outside(ShadeInput *shi, ShadeResult *shr)
+{
+ memset(shr, 0, sizeof(ShadeResult));
+ volume_trace(shi, shr, VOL_SHADE_OUTSIDE);
+}
+
+
+void shade_volume_inside(ShadeInput *shi, ShadeResult *shr)
+{
+ MatInside *m;
+ Material *mat_backup;
+ ObjectInstanceRen *obi_backup;
+ float prev_alpha = shr->alpha;
+
+ /* XXX: extend to multiple volumes perhaps later */
+ mat_backup = shi->mat;
+ obi_backup = shi->obi;
+
+ m = R.render_volumes_inside.first;
+ shi->mat = m->ma;
+ shi->obi = m->obi;
+ shi->obr = m->obi->obr;
+
+ volume_trace(shi, shr, VOL_SHADE_INSIDE);
+
+ shr->alpha = shr->alpha + prev_alpha;
+ CLAMP(shr->alpha, 0.0f, 1.0f);
+
+ shi->mat = mat_backup;
+ shi->obi = obi_backup;
+ shi->obr = obi_backup->obr;
+}
diff --git a/source/blender/render/intern/source/voxeldata.c b/source/blender/render/intern/source/voxeldata.c
index 0d9f7b197e1..2daa4123536 100644
--- a/source/blender/render/intern/source/voxeldata.c
+++ b/source/blender/render/intern/source/voxeldata.c
@@ -88,10 +88,10 @@ static size_t vd_resol_size(VoxelData *vd)
}
static int load_frame_blendervoxel(VoxelData *vd, FILE *fp, int frame)
-{
+{
const size_t size = vd_resol_size(vd);
size_t offset = sizeof(VoxelDataHeader);
-
+
if (is_vd_res_ok(vd) == false)
return 0;
@@ -102,7 +102,7 @@ static int load_frame_blendervoxel(VoxelData *vd, FILE *fp, int frame)
return 0;
if (fread(vd->dataset, sizeof(float), size, fp) != size)
return 0;
-
+
vd->cachedframe = frame;
vd->ok = 1;
return 1;
@@ -138,12 +138,12 @@ static int load_frame_raw8(VoxelData *vd, FILE *fp, int frame)
vd->dataset = NULL;
return 0;
}
-
+
for (i = 0; i < size; i++) {
vd->dataset[i] = (float)data_c[i] / 255.f;
}
MEM_freeN(data_c);
-
+
vd->cachedframe = frame;
vd->ok = 1;
return 1;
@@ -160,7 +160,7 @@ static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
if (!ima) return;
if (iuser.frames == 0) return;
-
+
ima->source = IMA_SRC_SEQUENCE;
iuser.framenr = 1 + iuser.offset;
@@ -173,13 +173,13 @@ static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
}
if (!ibuf) return;
if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
-
+
vd->flag |= TEX_VD_STILL;
vd->resol[0] = ibuf->x;
vd->resol[1] = ibuf->y;
vd->resol[2] = iuser.frames;
vd->dataset = MEM_mapallocN(sizeof(float) * vd_resol_size(vd), "voxel dataset");
-
+
for (z = 0; z < iuser.frames; z++) {
/* get a new ibuf for each frame */
if (z > 0) {
@@ -190,7 +190,7 @@ static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
}
rf = ibuf->rect_float;
-
+
for (y = 0; y < ibuf->y; y++) {
for (x = 0; x < ibuf->x; x++) {
/* currently averaged to monchrome */
@@ -198,7 +198,7 @@ static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
rf += 4;
}
}
-
+
BKE_image_free_anim_ibufs(ima, iuser.framenr);
}
@@ -211,13 +211,13 @@ static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
static int read_voxeldata_header(FILE *fp, struct VoxelData *vd)
{
VoxelDataHeader *h = (VoxelDataHeader *)MEM_mallocN(sizeof(VoxelDataHeader), "voxel data header");
-
+
rewind(fp);
if (fread(h, sizeof(VoxelDataHeader), 1, fp) != 1) {
MEM_freeN(h);
return 0;
}
-
+
vd->resol[0] = h->resolX;
vd->resol[1] = h->resolY;
vd->resol[2] = h->resolZ;
@@ -231,16 +231,16 @@ static void init_frame_smoke(VoxelData *vd, int cfra)
#ifdef WITH_SMOKE
Object *ob;
ModifierData *md;
-
+
vd->dataset = NULL;
if (vd->object == NULL) return;
ob = vd->object;
-
+
/* draw code for smoke */
if ((md = (ModifierData *)modifiers_findByType(ob, eModifierType_Smoke))) {
SmokeModifierData *smd = (SmokeModifierData *)md;
SmokeDomainSettings *sds = smd->domain;
-
+
if (sds && sds->fluid) {
BLI_rw_mutex_lock(sds->fluid_mutex, THREAD_LOCK_READ);
@@ -356,7 +356,7 @@ static void init_frame_smoke(VoxelData *vd, int cfra)
BLI_rw_mutex_unlock(sds->fluid_mutex);
}
}
-
+
vd->ok = 1;
#else // WITH_SMOKE
@@ -371,14 +371,14 @@ static void init_frame_hair(VoxelData *vd, int UNUSED(cfra))
{
Object *ob;
ModifierData *md;
-
+
vd->dataset = NULL;
if (vd->object == NULL) return;
ob = vd->object;
-
+
if ((md = (ModifierData *)modifiers_findByType(ob, eModifierType_ParticleSystem))) {
ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
-
+
if (pmd->psys && pmd->psys->clmd) {
vd->ok |= BPH_cloth_solver_get_texture_data(ob, pmd->psys->clmd, vd);
}
@@ -386,16 +386,16 @@ static void init_frame_hair(VoxelData *vd, int UNUSED(cfra))
}
void cache_voxeldata(Tex *tex, int scene_frame)
-{
+{
VoxelData *vd = tex->vd;
FILE *fp;
int curframe;
char path[sizeof(vd->source_path)];
-
+
/* only re-cache if dataset needs updating */
if ((vd->flag & TEX_VD_STILL) || (vd->cachedframe == scene_frame))
if (vd->ok) return;
-
+
/* clear out old cache, ready for new */
if (vd->dataset) {
MEM_freeN(vd->dataset);
@@ -408,9 +408,9 @@ void cache_voxeldata(Tex *tex, int scene_frame)
curframe = vd->still_frame;
else
curframe = scene_frame;
-
+
BLI_strncpy(path, vd->source_path, sizeof(path));
-
+
/* each type is responsible for setting to true */
vd->ok = false;
@@ -428,7 +428,7 @@ void cache_voxeldata(Tex *tex, int scene_frame)
BLI_path_abs(path, BKE_main_blendfile_path_from_global());
fp = BLI_fopen(path, "rb");
if (!fp) return;
-
+
if (read_voxeldata_header(fp, vd))
load_frame_blendervoxel(vd, fp, curframe - 1);
@@ -438,7 +438,7 @@ void cache_voxeldata(Tex *tex, int scene_frame)
BLI_path_abs(path, BKE_main_blendfile_path_from_global());
fp = BLI_fopen(path, "rb");
if (!fp) return;
-
+
load_frame_raw8(vd, fp, curframe);
fclose(fp);
return;
@@ -448,24 +448,24 @@ void cache_voxeldata(Tex *tex, int scene_frame)
void make_voxeldata(struct Render *re)
{
Tex *tex;
-
+
re->i.infostr = IFACE_("Loading voxel datasets");
re->stats_draw(re->sdh, &re->i);
-
+
/* XXX: should be doing only textures used in this render */
for (tex = re->main->tex.first; tex; tex = tex->id.next) {
if (tex->id.us && tex->type == TEX_VOXELDATA) {
cache_voxeldata(tex, re->r.cfra);
}
}
-
+
re->i.infostr = NULL;
re->stats_draw(re->sdh, &re->i);
-
+
}
int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texres)
-{
+{
VoxelData *vd = tex->vd;
float co[3], offset[3] = {0.5, 0.5, 0.5}, a;
int retval = (vd->data_type == TEX_VD_RGBA_PREMUL) ? TEX_RGB : TEX_INT;
@@ -476,7 +476,7 @@ int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texre
texres->tin = 0.0f;
return 0;
}
-
+
/* scale lookup from 0.0-1.0 (original location) to -1.0, 1.0, consistent with image texture tex coords */
/* in implementation this works backwards, bringing sample locations from -1.0, 1.0
* to the range 0.0, 1.0, before looking up in the voxel structure. */
@@ -531,7 +531,7 @@ int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texre
switch (vd->interp_type) {
case TEX_VD_NEARESTNEIGHBOR:
*result = BLI_voxel_sample_nearest(dataset, vd->resol, co);
- break;
+ break;
case TEX_VD_LINEAR:
*result = BLI_voxel_sample_trilinear(dataset, vd->resol, co);
break;
@@ -548,7 +548,7 @@ int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texre
a = texres->tin;
texres->tin *= vd->int_multiplier;
BRICONT;
-
+
if (vd->data_type == TEX_VD_RGBA_PREMUL) {
/* unmultiply */
if (a>0.001f) {
@@ -566,6 +566,6 @@ int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texre
texres->ta = texres->tin;
BRICONTRGB;
-
+
return retval;
}
diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c
index 3837383c4c7..436ee590f5c 100644
--- a/source/blender/render/intern/source/zbuf.c
+++ b/source/blender/render/intern/source/zbuf.c
@@ -54,10 +54,10 @@
void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty)
{
memset(zspan, 0, sizeof(ZSpan));
-
+
zspan->rectx= rectx;
zspan->recty= recty;
-
+
zspan->span1= MEM_mallocN(recty*sizeof(float), "zspan");
zspan->span2= MEM_mallocN(recty*sizeof(float), "zspan");
}
@@ -85,27 +85,27 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2])
float *span;
float xx1, dx0, xs0;
int y, my0, my2;
-
+
if (v1[1]<v2[1]) {
minv= v1; maxv= v2;
}
else {
minv= v2; maxv= v1;
}
-
+
my0= ceil(minv[1]);
my2= floor(maxv[1]);
-
+
if (my2<0 || my0>= zspan->recty) return;
-
+
/* clip top */
if (my2>=zspan->recty) my2= zspan->recty-1;
/* clip bottom */
if (my0<0) my0= 0;
-
+
if (my0>my2) return;
/* if (my0>my2) should still fill in, that way we get spans that skip nicely */
-
+
xx1= maxv[1]-minv[1];
if (xx1>FLT_EPSILON) {
dx0= (minv[0]-maxv[0])/xx1;
@@ -115,7 +115,7 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2])
dx0 = 0.0f;
xs0 = min_ff(minv[0], maxv[0]);
}
-
+
/* empty span */
if (zspan->maxp1 == NULL) {
span= zspan->span1;
@@ -158,9 +158,9 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2])
}
}
-/*-----------------------------------------------------------*/
+/*-----------------------------------------------------------*/
/* Functions */
-/*-----------------------------------------------------------*/
+/*-----------------------------------------------------------*/
/* scanconvert for strand triangles, calls func for each x, y coordinate and gives UV barycentrics and z */
@@ -170,30 +170,30 @@ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float *
float u, v, uxd, uyd, vxd, vyd, uy0, vy0, xx1;
const float *span1, *span2;
int i, j, x, y, sn1, sn2, rectx = zspan->rectx, my0, my2;
-
+
/* init */
zbuf_init_span(zspan);
-
+
/* set spans */
zbuf_add_to_span(zspan, v1, v2);
zbuf_add_to_span(zspan, v2, v3);
zbuf_add_to_span(zspan, v3, v1);
-
+
/* clipped */
if (zspan->minp2==NULL || zspan->maxp2==NULL) return;
-
+
my0 = max_ii(zspan->miny1, zspan->miny2);
my2 = min_ii(zspan->maxy1, zspan->maxy2);
-
+
// printf("my %d %d\n", my0, my2);
if (my2<my0) return;
-
+
/* ZBUF DX DY, in floats still */
x1= v1[0]- v2[0];
x2= v2[0]- v3[0];
y1= v1[1]- v2[1];
y2= v2[1]- v3[1];
-
+
z1= 1.0f; /* (u1 - u2) */
z2= 0.0f; /* (u2 - u3) */
@@ -213,28 +213,28 @@ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float *
x0= y1*z2-z1*y2;
y0= z1*x2-x1*z2;
-
+
xx1= (x0*v1[0] + y0*v1[1])/z0;
vxd= -(double)x0/(double)z0;
vyd= -(double)y0/(double)z0;
vy0= ((double)my2)*vyd + (double)xx1;
-
+
/* correct span */
span1= zspan->span1+my2;
span2= zspan->span2+my2;
-
+
for (i = 0, y = my2; y >= my0; i++, y--, span1--, span2--) {
-
+
sn1= floor(min_ff(*span1, *span2));
sn2= floor(max_ff(*span1, *span2));
- sn1++;
-
+ sn1++;
+
if (sn2>=rectx) sn2= rectx-1;
if (sn1<0) sn1= 0;
-
+
u = (((double)sn1 * uxd) + uy0) - (i * uyd);
v = (((double)sn1 * vxd) + vy0) - (i * vyd);
-
+
for (j = 0, x = sn1; x <= sn2; j++, x++) {
func(handle, x, y, u + (j * uxd), v + (j * vxd));
}