Code refactor: add ProjectionTransform separate from regular Transform.
[blender-staging.git] / intern / cycles / blender / blender_object_cull.cpp
1 /*
2  * Copyright 2011-2016 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <cstdlib>
18
19 #include "render/camera.h"
20
21 #include "blender/blender_object_cull.h"
22
23 CCL_NAMESPACE_BEGIN
24
25 BlenderObjectCulling::BlenderObjectCulling(Scene *scene, BL::Scene& b_scene)
26         : use_scene_camera_cull_(false),
27           use_camera_cull_(false),
28           camera_cull_margin_(0.0f),
29           use_scene_distance_cull_(false),
30           use_distance_cull_(false),
31           distance_cull_margin_(0.0f)
32 {
33         if(b_scene.render().use_simplify()) {
34                 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
35
36                 use_scene_camera_cull_ = scene->camera->type != CAMERA_PANORAMA &&
37                                          !b_scene.render().use_multiview() &&
38                                          get_boolean(cscene, "use_camera_cull");
39                 use_scene_distance_cull_ = scene->camera->type != CAMERA_PANORAMA &&
40                                            !b_scene.render().use_multiview() &&
41                                            get_boolean(cscene, "use_distance_cull");
42
43                 camera_cull_margin_ = get_float(cscene, "camera_cull_margin");
44                 distance_cull_margin_ = get_float(cscene, "distance_cull_margin");
45
46                 if(distance_cull_margin_ == 0.0f) {
47                         use_scene_distance_cull_ = false;
48                 }
49         }
50 }
51
52 void BlenderObjectCulling::init_object(Scene *scene, BL::Object& b_ob)
53 {
54         if(!use_scene_camera_cull_ && !use_scene_distance_cull_) {
55                 return;
56         }
57
58         PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
59
60         use_camera_cull_ = use_scene_camera_cull_ && get_boolean(cobject, "use_camera_cull");
61         use_distance_cull_ = use_scene_distance_cull_ && get_boolean(cobject, "use_distance_cull");
62
63         if(use_camera_cull_ || use_distance_cull_) {
64                 /* Need to have proper projection matrix. */
65                 scene->camera->update(scene);
66         }
67 }
68
69 bool BlenderObjectCulling::test(Scene *scene, BL::Object& b_ob, Transform& tfm)
70 {
71         if(!use_camera_cull_ && !use_distance_cull_) {
72                 return false;
73         }
74
75         /* Compute world space bounding box corners. */
76         float3 bb[8];
77         BL::Array<float, 24> boundbox = b_ob.bound_box();
78         for(int i = 0; i < 8; ++i) {
79                 float3 p = make_float3(boundbox[3 * i + 0],
80                                        boundbox[3 * i + 1],
81                                        boundbox[3 * i + 2]);
82                 bb[i] = transform_point(&tfm, p);
83         }
84
85         bool camera_culled = use_camera_cull_ && test_camera(scene, bb);
86         bool distance_culled = use_distance_cull_ && test_distance(scene, bb);
87
88         return ((camera_culled && distance_culled) ||
89                 (camera_culled && !use_distance_cull_) ||
90                 (distance_culled && !use_camera_cull_));
91 }
92
93 /* TODO(sergey): Not really optimal, consider approaches based on k-DOP in order
94  * to reduce number of objects which are wrongly considered visible.
95  */
96 bool BlenderObjectCulling::test_camera(Scene *scene, float3 bb[8])
97 {
98         Camera *cam = scene->camera;
99         const ProjectionTransform& worldtondc = cam->worldtondc;
100         float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
101                bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
102         bool all_behind = true;
103         for(int i = 0; i < 8; ++i) {
104                 float3 p = bb[i];
105                 float4 b = make_float4(p.x, p.y, p.z, 1.0f);
106                 float4 c = make_float4(dot(worldtondc.x, b),
107                                        dot(worldtondc.y, b),
108                                        dot(worldtondc.z, b),
109                                        dot(worldtondc.w, b));
110                 p = float4_to_float3(c / c.w);
111                 if(c.z < 0.0f) {
112                         p.x = 1.0f - p.x;
113                         p.y = 1.0f - p.y;
114                 }
115                 if(c.z >= -camera_cull_margin_) {
116                         all_behind = false;
117                 }
118                 bb_min = min(bb_min, p);
119                 bb_max = max(bb_max, p);
120         }
121         if(all_behind) {
122                 return true;
123         }
124         return (bb_min.x >= 1.0f + camera_cull_margin_ ||
125                 bb_min.y >= 1.0f + camera_cull_margin_ ||
126                 bb_max.x <= -camera_cull_margin_ ||
127                 bb_max.y <= -camera_cull_margin_);
128 }
129
130 bool BlenderObjectCulling::test_distance(Scene *scene, float3 bb[8])
131 {
132         float3 camera_position = transform_get_column(&scene->camera->matrix, 3);
133         float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
134                bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
135
136         /* Find min & max points for x & y & z on bounding box */
137         for(int i = 0; i < 8; ++i) {
138                 float3 p = bb[i];
139                 bb_min = min(bb_min, p);
140                 bb_max = max(bb_max, p);
141         }
142
143         float3 closest_point = max(min(bb_max,camera_position),bb_min);
144         return (len_squared(camera_position - closest_point) >
145                 distance_cull_margin_ * distance_cull_margin_);
146 }
147
148 CCL_NAMESPACE_END
149