Cogs.Core
ShadowMapInspector.cpp
1#include "imgui.h"
2#include "Rendering/IGraphicsDevice.h"
3#include "Rendering/ICapabilities.h"
4
5#include "Inspectors.h"
6#include "InspectorGuiHelper.h"
7#include "Context.h"
8#include "Renderer/Renderer.h"
9#include "Renderer/InspectorGui/InspectorGuiRenderer.h"
10#include "Resources/TextureManager.h"
11#include "Renderer/RenderTexture.h"
12#include "Systems/Core/LightSystem.h"
13#include "Utilities/Math.h"
14
15namespace {
16 using namespace Cogs::Core;
17
18 bool orient2D(const float * P, const size_t stride, const unsigned ia, const unsigned ib, const unsigned ic)
19 {
20 auto a = glm::make_vec2(P + stride * ia);
21 auto b = glm::make_vec2(P + stride * ib);
22 auto c = glm::make_vec2(P + stride * ic);
23 auto ab = glm::vec3(b - a, 0);
24 auto ac = glm::vec3(c - a, 0);
25 return 0.f < glm::cross(ab, ac).z;
26 }
27
29 // Impl. of gift wrapping / Jarvis march, not particularly tested yet.
30 // stride is number of elements, not bytes.
31 void basicConvexHull2D(Context* /*context*/, std::vector<unsigned>& loop, const float* P, const size_t stride, const size_t N)
32 {
33 if (N == 0) return;
34 auto n = unsigned(N);
35
36 // Find one point guaranteed to be on the convex hull.
37 unsigned k0 = 0;
38 auto ll = glm::make_vec2(P + k0 * stride);
39 for (unsigned i = 1; i < n; i++) {
40 auto p = glm::make_vec2(P + i * stride);
41 if (p.x < ll.x || (p.x == ll.x && p.y < ll.y)) {
42 ll = p;
43 k0 = i;
44 }
45 }
46
47 loop.push_back(k0);
48 while (loop.size() < N)
49 {
50 unsigned e = 0;
51 for (unsigned j = 1; j < n; j++) {
52 if (e == loop.back() || orient2D(P, stride, j, loop.back(), e)) {
53 e = j;
54 }
55 }
56 if (loop.front() == e) {
57 break;
58 }
59 loop.push_back(e);
60 }
61
62 }
63
64
65 std::vector<ImVec2> buildConvexHull(Context* context, const ImVec2 o, std::vector<glm::vec3>& P, float s, const glm::mat4& M)
66 {
67 std::vector<unsigned> loop;
68 basicConvexHull2D(context, loop, reinterpret_cast<float*>(P.data()), 3, P.size());
69 std::vector<ImVec2> loop_points;
70 for (auto & ix : loop) {
71 auto & p = P[ix];
72 auto q = glm::vec2(s / 2.f)* (glm::vec2(euclidean(M * glm::vec4(p, 1))) + glm::vec2(1));
73 loop_points.push_back(ImVec2(o.x + q.x, o.y + q.y));
74 }
75 return loop_points;
76 }
77
78 std::vector<ImVec2> buildConvexHull(Context* context, const ImVec2 o, std::vector<glm::vec3>& P, float scale, float s, const glm::vec2& c)
79 {
80 std::vector<unsigned> loop;
81 basicConvexHull2D(context, loop, reinterpret_cast<float*>(P.data()), 3, P.size());
82 std::vector<ImVec2> loop_points;
83 for (auto & ix : loop) {
84 auto & r = P[ix];
85 auto q = glm::vec2(o.x, o.y) + scale * (glm::vec2(r) - c) + glm::vec2(s / 2);
86 loop_points.push_back(ImVec2(q.x, q.y));
87 }
88 return loop_points;
89 }
90
91}
92
93
94void Cogs::Core::shadowMapInspector(Context * context, bool * show)
95{
96 if (*show == false) return;
97
98 Cogs::Core::Renderer* renderer = dynamic_cast<Cogs::Core::Renderer*>(context->renderer);
99 guiBegin("Shadow maps", show);
100 {
101 float expFactor = context->variables->get("shadows.cascades.expFactor")->getFloat();
102 if (ImGui::InputFloat("shadows.cascades.expFactor", &expFactor, 0.025f, 0.1f)) {
103 context->variables->set("shadows.cascades.expFactor", glm::clamp(expFactor, 0.f, 1.f));
104 }
105 }
106 {
107 float overlapFactor = context->variables->get("shadows.cascades.overlapFactor")->getFloat();
108 if (ImGui::InputFloat("shadows.cascades.overlapFactor", &overlapFactor, 0.025f, 0.1f)) {
109 context->variables->set("shadows.cascades.overlapFactor", glm::clamp(overlapFactor, 0.f, 1.f));
110 }
111 }
112 {
113 float maxShadowDistance = context->variables->get("renderer.maxShadowDistance")->getFloat();
114 if (ImGui::InputFloat("renderer.maxShadowDistance", &maxShadowDistance, 10.f, 1000.f)) {
115 context->variables->set("renderer.maxShadowDistance", glm::clamp(maxShadowDistance, 0.f, 100000.f));
116 }
117 }
118
119 bool flipTexture = false;
120 switch (context->renderer->getDevice()->getType()) {
123 flipTexture = true;
124 break;
125 default:
126 break;
127 }
128
129 for (auto & lightComp : context->lightSystem->pool) {
130 auto * entity = lightComp.getContainer();
131
132 auto name = entity->getName() + " (" + std::to_string(entity->getId()) + ")";
133
134 bool empty = false;
135 if (lightComp.enabled == false) {
136 name += " (disabled)";
137 empty = true;
138 }
139 else if (lightComp.castShadows == false) {
140 name += " (casts no shadows)";
141 empty = true;
142 }
143
144 ImU32 colors[4] = {
145 IM_COL32(255, 255, 0, 255),
146 IM_COL32(0, 255, 0, 255),
147 IM_COL32(0, 0, 255, 255),
148 IM_COL32(255, 0, 255, 255)
149 };
150 ImU32 colors_light[4] = {
151 IM_COL32(255, 255, 200, 255),
152 IM_COL32(200, 255, 200, 255),
153 IM_COL32(200, 200, 255, 255),
154 IM_COL32(255, 200, 255, 255)
155 };
156
157 if (ImGui::CollapsingHeader(getUniqueHeader(name).c_str(), ImGuiTreeNodeFlags_DefaultOpen | (empty? ImGuiTreeNodeFlags_Leaf:0)) && !empty) {
158 auto & lightData = context->lightSystem->getData(&lightComp);
159 auto * texture = lightData.shadowTexture.resolve();
160
161 if (lightComp.lightType == LightType::Directional) {
162 lightData.frustaPointsCapture = true;
163 const auto s = 256.0f;
164 auto dl = ImGui::GetWindowDrawList();
165
166 auto o = ImGui::GetCursorScreenPos();
167 ImGui::InvisibleButton(getUniqueHeader(name + " canvas").c_str(), ImVec2(s, s));
168 {
169 auto viewportMin = lightData.frustaPoints[0].viewportMin;
170 auto viewportMax = lightData.frustaPoints[0].viewportMax;
171 for (unsigned i = 0; i < lightData.numViewports; i++) {
172 auto & frustumPoints = lightData.frustaPoints[i];
173 viewportMin = glm::min(viewportMin, frustumPoints.viewportMin);
174 viewportMax = glm::max(viewportMax, frustumPoints.viewportMax);
175 }
176 auto size = glm::max(viewportMax.x - viewportMin.x,
177 viewportMax.y - viewportMin.y);
178 auto scale = s / size;
179
180 auto c = 0.5f*(viewportMin + viewportMax);
181
182 for (int i = lightData.numViewports - 1; 0 <= i; --i) {
183 RenderTexture * renderTexture = renderer->getRenderResources().getRenderTexture(context->textureManager->generateHandle((Texture *)texture));
184 if (renderTexture) {
185 auto & frustumPoints = lightData.frustaPoints[i];
186 auto a = glm::vec2(o.x, o.y) + scale * (frustumPoints.viewportMin - c) + glm::vec2(s / 2);
187 auto b = glm::vec2(o.x, o.y) + scale * (frustumPoints.viewportMax - c) + glm::vec2(s / 2);
188
189 dl->AddCallback(setGuiMode, (void*)(uint64_t)(GUI_MODE_TEX_CHANNELS_RED | GUI_MODE_TEX_TYPE_ARRAY | (lightData.arrayOffset + i))); // show only alpha channel for the next texture
190
191 if (flipTexture) {
192 dl->AddImage(ImTextureID(renderTexture->textureHandle.handle), ImVec2(a.x, a.y), ImVec2(b.x, b.y), ImVec2(0, 0), ImVec2(1, 1), colors_light[i & 3]);
193 }
194 else {
195 dl->AddImage(ImTextureID(renderTexture->textureHandle.handle), ImVec2(a.x, a.y), ImVec2(b.x, b.y), ImVec2(0, 1), ImVec2(1, 0), colors_light[i & 3]);
196 }
197 }
198 }
199 dl->AddCallback(setGuiMode, (void*)(GUI_MODE_DEFAULT)); // restore the default texture rendering state
200
201
202 for (unsigned i = 0; i < lightData.numViewports; i++) {
203 auto loop_points = buildConvexHull(context, o, lightData.frustaPoints[i].points, scale, s, c);
204 dl->AddPolyline(loop_points.data(), int(loop_points.size()), colors[i & 3], true, 1.f);
205 }
206 //for (unsigned i = 0; i < lightData.numViewports; i++) {
207 // auto & frustumPoints = lightData.frustaPoints[i];
208 // auto a = glm::vec2(o.x, o.y) + scale * (frustumPoints.viewportMin - c) + glm::vec2(s / 2);
209 // auto b = glm::vec2(o.x, o.y) + scale * (frustumPoints.viewportMax - c) + glm::vec2(s / 2);
210 // dl->AddRect(ImVec2(a.x, a.y), ImVec2(b.x, b.y), colors[i & 3], 0.f, -1, 2.f);
211 //}
212 }
213
214 for (unsigned i = 0; i < lightData.numViewports; i++) {
215 auto & lightCameraData = lightData.lightCameraData[i];
216
217 RenderTexture * renderTexture = renderer->getRenderResources().getRenderTexture(context->textureManager->generateHandle((Texture *)texture));
218 if (!renderTexture) continue;
219
220 ImGui::SameLine();
221
222 auto oo = ImGui::GetCursorScreenPos();
223 ImGui::InvisibleButton(getUniqueHeader(name + " canvas lod" + std::to_string(i)).c_str(), ImVec2(s, s));
224
225 dl->AddCallback(setGuiMode, (void*)(uint64_t)(GUI_MODE_TEX_CHANNELS_RED | GUI_MODE_TEX_TYPE_ARRAY | (lightData.arrayOffset + i))); // show only alpha channel for the next texture
226 if (flipTexture) {
227 dl->AddImage(ImTextureID(renderTexture->textureHandle.handle), oo, ImVec2(oo.x + s, oo.y + s), ImVec2(0, 0), ImVec2(1, 1), colors_light[i & 3]);
228 }
229 else {
230 dl->AddImage(ImTextureID(renderTexture->textureHandle.handle), oo, ImVec2(oo.x + s, oo.y + s), ImVec2(0, 1), ImVec2(1, 0), colors_light[i & 3]);
231 }
232 //ImGui::Image(ImTextureID(renderTexture->textureHandle.handle), ImVec2(s, s), ImVec2(0, 1), ImVec2(1, 0));
233 dl->AddCallback(setGuiMode, (void*)(GUI_MODE_DEFAULT)); // restore the default texture rendering state
234
235 {
236
237 glm::vec4 cullBox_in[4] =
238 {
239 glm::vec4(-1, -1, 0, 1),
240 glm::vec4(1, -1, 0, 1),
241 glm::vec4(1, 1, 0, 1),
242 glm::vec4(-1, 1, 0, 1),
243 };
244
245 ImVec2 cullBox[4];
246 auto M = lightCameraData.rawViewProjection * glm::inverse(lightCameraData.rawViewCullMatrix);
247
248 for (unsigned j = 0; j < 4; j++) {
249 const auto q = glm::vec2(s / 2.f) * (glm::vec2(euclidean(M * cullBox_in[j])) + glm::vec2(1));
250 cullBox[j] = ImVec2(oo.x + q.x, oo.y + q.y);
251 }
252
253 ImGui::PushClipRect(oo, ImVec2(oo.x + s, oo.y + s), true);
254 dl->AddPolyline(cullBox, 4, colors[i & 3], true, 1.f);
255 ImGui::PopClipRect();
256 }
257
258 auto loop_points = buildConvexHull(context, oo, lightData.frustaPoints[i].points, s, lightCameraData.rawProjectionMatrix);
259 dl->AddPolyline(loop_points.data(), int(loop_points.size()), colors[i & 3], true, 3.f);
260
261 for (auto & r : lightData.frustaPoints[i].points) {
262 auto q = glm::vec2(s/2.f)* (glm::vec2(euclidean(lightCameraData.rawProjectionMatrix * glm::vec4(r, 1))) + glm::vec2(1));
263 dl->AddCircleFilled(ImVec2(oo.x + q.x, oo.y + q.y), 4.f, colors[i & 3]);
264 }
265
266 }
267 }
268 }
269
270 }
271
272
273 guiEnd();
274}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
class IRenderer * renderer
Renderer.
Definition: Context.h:228
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
virtual IGraphicsDevice * getDevice()=0
Get the graphics device used by the renderer.
Core renderer system.
Definition: Renderer.h:28
virtual GraphicsDeviceType getType() const
Get the type of the graphics device.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
ImDrawCallback setGuiMode
Callback for Render updates - not really called.
@ OpenGLES30
Graphics device using the OpenGLES 3.0 API.
@ OpenGL20
Graphics device using OpenGL, supporting at least OpenGL 2.0.
Texture resources contain raster bitmap data to use for texturing.
Definition: Texture.h:91
handle_type handle
Internal resource handle.
Definition: Common.h:74