diff options
Diffstat (limited to 'source/blender/draw/engines/workbench/workbench_studiolight.c')
-rw-r--r-- | source/blender/draw/engines/workbench/workbench_studiolight.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/source/blender/draw/engines/workbench/workbench_studiolight.c b/source/blender/draw/engines/workbench/workbench_studiolight.c new file mode 100644 index 00000000000..f9e5df91388 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_studiolight.c @@ -0,0 +1,212 @@ +/* + * Copyright 2016, Blender Foundation. + * + * 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): Blender Institute + * + */ + +/** \file workbench_studiolight.c + * \ingroup draw_engine + */ +#include "BKE_studiolight.h" + +#include "DRW_engine.h" +#include "workbench_private.h" + +#include "BKE_object.h" + +#include "BLI_math.h" +#include "BKE_global.h" + +void studiolight_update_world(StudioLight *sl, WORKBENCH_UBO_World *wd) +{ + int i; + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED); + + for (i = 0; i < STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS; i++) { + copy_v3_v3(wd->spherical_harmonics_coefs[i], sl->spherical_harmonics_coefs[i]); + } + for (; i < STUDIOLIGHT_SPHERICAL_HARMONICS_MAX_COMPONENTS; i++) { + copy_v3_fl(wd->spherical_harmonics_coefs[i], 0.0); + } +} + +static void compute_parallel_lines_nor_and_dist(const float v1[2], const float v2[2], const float v3[2], float r_line[2]) +{ + sub_v2_v2v2(r_line, v2, v1); + /* Find orthogonal vector. */ + SWAP(float, r_line[0], r_line[1]); + r_line[0] = -r_line[0]; + /* Edge distances. */ + r_line[2] = dot_v2v2(r_line, v1); + r_line[3] = dot_v2v2(r_line, v3); + /* Make sure r_line[2] is the minimum. */ + if (r_line[2] > r_line[3]) { + SWAP(float, r_line[2], r_line[3]); + } +} + +void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_direction[3]) +{ + wpd->shadow_changed = !compare_v3v3(wpd->cached_shadow_direction, light_direction, 1e-5f); + + if (wpd->shadow_changed) { + float up[3] = {0.0f, 0.0f, 1.0f}; + unit_m4(wpd->shadow_mat); + + /* TODO fix singularity. */ + copy_v3_v3(wpd->shadow_mat[2], light_direction); + cross_v3_v3v3(wpd->shadow_mat[0], wpd->shadow_mat[2], up); + normalize_v3(wpd->shadow_mat[0]); + cross_v3_v3v3(wpd->shadow_mat[1], wpd->shadow_mat[2], wpd->shadow_mat[0]); + + invert_m4_m4(wpd->shadow_inv, wpd->shadow_mat); + + copy_v3_v3(wpd->cached_shadow_direction, light_direction); + } + + float planes[6][4]; + DRW_culling_frustum_planes_get(planes); + /* we only need the far plane. */ + copy_v4_v4(wpd->shadow_far_plane, planes[2]); + + BoundBox frustum_corners; + DRW_culling_frustum_corners_get(&frustum_corners); + + mul_v3_mat3_m4v3(wpd->shadow_near_corners[0], wpd->shadow_inv, frustum_corners.vec[0]); + mul_v3_mat3_m4v3(wpd->shadow_near_corners[1], wpd->shadow_inv, frustum_corners.vec[3]); + mul_v3_mat3_m4v3(wpd->shadow_near_corners[2], wpd->shadow_inv, frustum_corners.vec[7]); + mul_v3_mat3_m4v3(wpd->shadow_near_corners[3], wpd->shadow_inv, frustum_corners.vec[4]); + + INIT_MINMAX(wpd->shadow_near_min, wpd->shadow_near_max); + for (int i = 0; i < 4; ++i) { + minmax_v3v3_v3(wpd->shadow_near_min, wpd->shadow_near_max, wpd->shadow_near_corners[i]); + } + + compute_parallel_lines_nor_and_dist(wpd->shadow_near_corners[0], wpd->shadow_near_corners[1], wpd->shadow_near_corners[2], wpd->shadow_near_sides[0]); + compute_parallel_lines_nor_and_dist(wpd->shadow_near_corners[1], wpd->shadow_near_corners[2], wpd->shadow_near_corners[0], wpd->shadow_near_sides[1]); +} + +static BoundBox *studiolight_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed) +{ + if ((oed->shadow_bbox_dirty) || (wpd->shadow_changed)) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, wpd->shadow_inv, ob->obmat); + + /* Get AABB in shadow space. */ + INIT_MINMAX(oed->shadow_min, oed->shadow_max); + + /* From object space to shadow space */ + BoundBox *bbox = BKE_object_boundbox_get(ob); + for (int i = 0; i < 8; ++i) { + float corner[3]; + mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]); + minmax_v3v3_v3(oed->shadow_min, oed->shadow_max, corner); + } + oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2]; + /* Extend towards infinity. */ + oed->shadow_max[2] += 1e4f; + + /* Get extended AABB in world space. */ + BKE_boundbox_init_from_minmax(&oed->shadow_bbox, oed->shadow_min, oed->shadow_max); + for (int i = 0; i < 8; ++i) { + mul_m4_v3(wpd->shadow_mat, oed->shadow_bbox.vec[i]); + } + oed->shadow_bbox_dirty = false; + } + + return &oed->shadow_bbox; +} + +bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed) +{ + BoundBox *shadow_bbox = studiolight_object_shadow_bbox_get(wpd, ob, oed); + return DRW_culling_box_test(shadow_bbox); +} + +float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed) +{ + BoundBox *shadow_bbox = studiolight_object_shadow_bbox_get(wpd, ob, oed); + + int corners[4] = {0, 3, 4, 7}; + float dist = 1e4f, dist_isect; + for (int i = 0; i < 4; ++i) { + if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]], + wpd->cached_shadow_direction, + wpd->shadow_far_plane, + &dist_isect, true)) + { + if (dist_isect < dist) { + dist = dist_isect; + } + } + else { + /* All rays are parallels. If one fails, the other will too. */ + break; + } + } + return max_ii(dist - oed->shadow_depth, 0); +} + +bool studiolight_camera_in_object_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed) +{ + /* Just to be sure the min, max are updated. */ + studiolight_object_shadow_bbox_get(wpd, ob, oed); + + /* Test if near plane is in front of the shadow. */ + if (oed->shadow_min[2] > wpd->shadow_near_max[2]) { + return false; + } + + /* Separation Axis Theorem test */ + + /* Test bbox sides first (faster) */ + if ((oed->shadow_min[0] > wpd->shadow_near_max[0]) || + (oed->shadow_max[0] < wpd->shadow_near_min[0]) || + (oed->shadow_min[1] > wpd->shadow_near_max[1]) || + (oed->shadow_max[1] < wpd->shadow_near_min[1])) + { + return false; + } + + /* Test projected near rectangle sides */ + float pts[4][2] = { + {oed->shadow_min[0], oed->shadow_min[1]}, + {oed->shadow_min[0], oed->shadow_max[1]}, + {oed->shadow_max[0], oed->shadow_min[1]}, + {oed->shadow_max[0], oed->shadow_max[1]} + }; + + for (int i = 0; i < 2; ++i) { + float min_dst = FLT_MAX, max_dst = -FLT_MAX; + for (int j = 0; j < 4; ++j) { + float dst = dot_v2v2(wpd->shadow_near_sides[i], pts[j]); + /* Do min max */ + if (min_dst > dst) min_dst = dst; + if (max_dst < dst) max_dst = dst; + } + + if ((wpd->shadow_near_sides[i][2] > max_dst) || + (wpd->shadow_near_sides[i][3] < min_dst)) + { + return false; + } + } + + /* No separation axis found. Both shape intersect. */ + return true; +} |