Blender V5.0
overlay_light.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include "DNA_light_types.h"
12
13#include "BLI_math_matrix.h"
14
15#include "overlay_base.hh"
16
17namespace blender::draw::overlay {
18
19class Lights : Overlay {
20 using LightInstanceBuf = ShapeInstanceBuf<ExtraInstanceData>;
21 using GroundLineInstanceBuf = ShapeInstanceBuf<float4>;
22
23 private:
24 const SelectionType selection_type_;
25
26 PassSimple ps_ = {"Lights"};
27
28 struct CallBuffers {
29 const SelectionType selection_type_;
30 GroundLineInstanceBuf ground_line_buf = {selection_type_, "ground_line_buf"};
31 LightInstanceBuf icon_inner_buf = {selection_type_, "icon_inner_buf"};
32 LightInstanceBuf icon_outer_buf = {selection_type_, "icon_outer_buf"};
33 LightInstanceBuf icon_sun_rays_buf = {selection_type_, "icon_sun_rays_buf"};
34 LightInstanceBuf point_buf = {selection_type_, "point_buf"};
35 LightInstanceBuf sun_buf = {selection_type_, "sun_buf"};
36 LightInstanceBuf spot_buf = {selection_type_, "spot_buf"};
37 LightInstanceBuf spot_cone_back_buf = {selection_type_, "spot_cone_back_buf"};
38 LightInstanceBuf spot_cone_front_buf = {selection_type_, "spot_cone_front_buf"};
39 LightInstanceBuf area_disk_buf = {selection_type_, "area_disk_buf"};
40 LightInstanceBuf area_square_buf = {selection_type_, "area_square_buf"};
41 } call_buffers_{selection_type_};
42
43 public:
44 Lights(const SelectionType selection_type) : selection_type_(selection_type) {};
45
46 void begin_sync(Resources & /*res*/, const State &state) final
47 {
48 enabled_ = state.is_space_v3d() && state.show_extras();
49 if (!enabled_) {
50 return;
51 }
52
53 call_buffers_.ground_line_buf.clear();
54 call_buffers_.icon_inner_buf.clear();
55 call_buffers_.icon_outer_buf.clear();
56 call_buffers_.icon_sun_rays_buf.clear();
57 call_buffers_.point_buf.clear();
58 call_buffers_.sun_buf.clear();
59 call_buffers_.spot_buf.clear();
60 call_buffers_.spot_cone_back_buf.clear();
61 call_buffers_.spot_cone_front_buf.clear();
62 call_buffers_.area_disk_buf.clear();
63 call_buffers_.area_square_buf.clear();
64 }
65
66 void object_sync(Manager & /*manager*/,
67 const ObjectRef &ob_ref,
68 Resources &res,
69 const State &state) final
70 {
71 if (!enabled_) {
72 return;
73 }
74
75 ExtraInstanceData data(ob_ref.object->object_to_world(),
76 float4(res.object_wire_color(ob_ref, state).xyz(), 1.0f),
77 1.0f);
78 float4 &theme_color = data.color_;
79
80 /* Pack render data into object matrix. */
81 float4x4 &matrix = data.object_to_world;
82 float &area_size_x = matrix[0].w;
83 float &area_size_y = matrix[1].w;
84 float &spot_cosine = matrix[0].w;
85 float &spot_blend = matrix[1].w;
86 float &clip_start = matrix[2].w;
87 float &clip_end = matrix[3].w;
88
89 const Light &la = DRW_object_get_data_for_drawing<Light>(*ob_ref.object);
90 const select::ID select_id = res.select_id(ob_ref);
91
92 /* FIXME / TODO: clip_end has no meaning nowadays.
93 * In EEVEE, Only clip_start is used shadow-mapping.
94 * Clip end is computed automatically based on light power.
95 * For now, always use the custom distance as clip_end. */
96 clip_end = la.att_dist;
97 clip_start = la.clipsta;
98
99 call_buffers_.ground_line_buf.append(float4(matrix.location(), 0.0f), select_id);
100
101 const float4 light_color = {la.r, la.g, la.b, 1.0f};
102 const bool show_light_colors = state.show_light_colors();
103
104 /* Draw the outer ring of the light icon and the sun rays in `light_color`, if required. */
105 call_buffers_.icon_outer_buf.append(data, select_id);
106 call_buffers_.icon_inner_buf.append(show_light_colors ? data.with_color(light_color) : data,
107 select_id);
108
109 switch (la.type) {
110 case LA_LOCAL:
111 area_size_x = area_size_y = la.radius;
112 call_buffers_.point_buf.append(data, select_id);
113 break;
114 case LA_SUN:
115 call_buffers_.sun_buf.append(data, select_id);
116 call_buffers_.icon_sun_rays_buf.append(
117 show_light_colors ? data.with_color(light_color) : data, select_id);
118 break;
119 case LA_SPOT: {
120 /* Previous implementation was using the clip-end distance as cone size.
121 * We cannot do this anymore so we use a fixed size of 10. (see #72871) */
122 rescale_m4(matrix.ptr(), float3(10.0f, 10.0f, 10.0f));
123 /* For cycles and EEVEE the spot attenuation is:
124 * `y = (1/sqrt(1 + x^2) - a)/((1 - a) b)`
125 * x being the tangent of the angle between the light direction and the generatrix of the
126 * cone. We solve the case where spot attenuation y = 1 and y = 0 root for y = 1 is
127 * `sqrt(1/c^2 - 1)`. root for y = 0 is `sqrt(1/a^2 - 1)` and use that to position the
128 * blend circle. */
129 const float a = cosf(la.spotsize * 0.5f);
130 const float b = la.spotblend;
131 const float c = a * b - a - b;
132 const float a2 = a * a;
133 const float c2 = c * c;
134 /* Optimized version or root1 / root0 */
135 spot_blend = sqrtf((a2 - a2 * c2) / (c2 - a2 * c2));
136 spot_cosine = a;
137 /* HACK: We pack the area size in alpha color. This is decoded by the shader. */
138 theme_color[3] = -max_ff(la.radius, FLT_MIN);
139 call_buffers_.spot_buf.append(data, select_id);
140 if ((la.mode & LA_SHOW_CONE) && !res.is_selection()) {
141 const float4 color_inside{0.0f, 0.0f, 0.0f, 0.5f};
142 const float4 color_outside{1.0f, 1.0f, 1.0f, 0.3f};
143 call_buffers_.spot_cone_front_buf.append(data.with_color(color_inside), select_id);
144 call_buffers_.spot_cone_back_buf.append(data.with_color(color_outside), select_id);
145 }
146 break;
147 }
148 case LA_AREA:
149 const bool uniform_scale = !ELEM(la.area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE);
150 LightInstanceBuf &area_buf = ELEM(la.area_shape, LA_AREA_SQUARE, LA_AREA_RECT) ?
151 call_buffers_.area_square_buf :
152 call_buffers_.area_disk_buf;
153 area_size_x = la.area_size;
154 area_size_y = uniform_scale ? la.area_size : la.area_sizey;
155 area_buf.append(data, select_id);
156 break;
157 }
158 }
159
160 void end_sync(Resources &res, const State &state) final
161 {
162 if (!enabled_) {
163 return;
164 }
165
168 ps_.init();
169 ps_.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
170 ps_.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
171 res.select_bind(ps_);
172
173 {
174 PassSimple::Sub &sub_pass = ps_.sub("spot_cone_front");
177 state.clipping_plane_count);
178 sub_pass.shader_set(res.shaders->light_spot_cone.get());
179 call_buffers_.spot_cone_front_buf.end_sync(sub_pass, res.shapes.light_spot_volume.get());
180 }
181 {
182 PassSimple::Sub &sub_pass = ps_.sub("spot_cone_back");
185 state.clipping_plane_count);
186 sub_pass.shader_set(res.shaders->light_spot_cone.get());
187 call_buffers_.spot_cone_back_buf.end_sync(sub_pass, res.shapes.light_spot_volume.get());
188 }
189 {
190 PassSimple::Sub &sub_pass = ps_.sub("light_shapes");
191 sub_pass.state_set(pass_state, state.clipping_plane_count);
192 sub_pass.shader_set(res.shaders->extra_shape.get());
193 call_buffers_.icon_inner_buf.end_sync(sub_pass, res.shapes.light_icon_outer_lines.get());
194 call_buffers_.icon_outer_buf.end_sync(sub_pass, res.shapes.light_icon_inner_lines.get());
195 call_buffers_.icon_sun_rays_buf.end_sync(sub_pass, res.shapes.light_icon_sun_rays.get());
196 call_buffers_.point_buf.end_sync(sub_pass, res.shapes.light_point_lines.get());
197 call_buffers_.sun_buf.end_sync(sub_pass, res.shapes.light_sun_lines.get());
198 call_buffers_.spot_buf.end_sync(sub_pass, res.shapes.light_spot_lines.get());
199 call_buffers_.area_disk_buf.end_sync(sub_pass, res.shapes.light_area_disk_lines.get());
200 call_buffers_.area_square_buf.end_sync(sub_pass, res.shapes.light_area_square_lines.get());
201 }
202 {
203 PassSimple::Sub &sub_pass = ps_.sub("ground_line");
204 sub_pass.state_set(pass_state | DRW_STATE_BLEND_ALPHA, state.clipping_plane_count);
205 sub_pass.shader_set(res.shaders->extra_ground_line.get());
206 call_buffers_.ground_line_buf.end_sync(sub_pass, res.shapes.ground_line.get());
207 }
208 }
209
210 void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
211 {
212 if (!enabled_) {
213 return;
214 }
215
216 GPU_framebuffer_bind(framebuffer);
217 manager.submit(ps_, view);
218 }
219};
220
221} // namespace blender::draw::overlay
MINLINE float max_ff(float a, float b)
void rescale_m4(float mat[4][4], const float scale[3])
#define ELEM(...)
@ LA_AREA
@ LA_LOCAL
@ LA_SPOT
@ LA_SUN
@ LA_SHOW_CONE
@ LA_AREA_ELLIPSE
@ LA_AREA_SQUARE
@ LA_AREA_RECT
T & DRW_object_get_data_for_drawing(const Object &object)
static AppView * view
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
BMesh const char void * data
void shader_set(gpu::Shader *shader)
void state_set(DRWState state, int clip_plane_count=0)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
Lights(const SelectionType selection_type)
void object_sync(Manager &, const ObjectRef &ob_ref, Resources &res, const State &state) final
void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
void end_sync(Resources &res, const State &state) final
void begin_sync(Resources &, const State &state) final
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
DRWState
Definition draw_state.hh:25
@ DRW_STATE_BLEND_ALPHA
Definition draw_state.hh:55
@ DRW_STATE_CULL_FRONT
Definition draw_state.hh:44
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_DEPTH_LESS_EQUAL
Definition draw_state.hh:38
@ DRW_STATE_CULL_BACK
Definition draw_state.hh:43
static ulong state[N]
select::SelectionType SelectionType
detail::Pass< command::DrawCommandBuf > PassSimple
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 3 > float3
#define sqrtf
#define cosf
const c_style_mat & ptr() const
void append(const InstanceDataT &data, select::ID select_id)