Cogs.Core
TextureGenerator.cpp
1#include "TextureGenerator.h"
2
3#include <unordered_map>
4#include <numbers>
5#include <glm/glm.hpp>
6#include <glm/gtc/constants.hpp>
7
8#include "Foundation/Platform/Threads.h"
9
10#include "Resources/TextureManager.h"
11#include "Utilities/NoiseSampler.h"
12
13#include "Context.h"
14
15namespace
16{
17 using namespace Cogs::Core;
18
19 struct half4
20 {
21 glm::detail::hdata r, g, b, a;
22 };
23
24
25#if 0
26 glm::vec3 inverseSphereGeoTexMapping(float u, float v)
27 {
28 glm::vec3 dir;
29 dir.z = -std::cos(glm::pi<float>() * v);
30 float t = 1.f / std::sqrt(1.f - dir.z*dir.z);
31 dir.y = std::cos(2.f*glm::pi<float>()* u) / t;
32 dir.x = (u < 0.5 ? 1 : -1.f)* std::sqrt(1.f - dir.z*dir.z - dir.y*dir.y);
33
34 return dir;
35 }
36#endif
37
38 glm::mat3 cubeMapPermutationMatrix(size_t f)
39 {
40 glm::mat3 R;
41
42 switch (f)
43 {
44 case 0:
45 //d = glm::vec3(1.f, V, -U); break;
46 R = glm::mat3(0.f, 0.f, -1.f,
47 0.f, 1.f, 0.f,
48 1.f, 0.f, 0.f);
49 break;
50 case 1:
51 //d = glm::vec3(-1.f, V, U); break;
52 R = glm::mat3(0.f, 0.f, 1.f,
53 0.f, 1.f, 0.f,
54 -1.f, 0.f, 0.f);
55 break;
56 case 2:
57 //d = glm::vec3(U, 1.f, -V); break;
58 R = glm::mat3(1.f, 0.f, 0.f,
59 0.f, 0.f, -1.f,
60 0.f, 1.f, 0.f);
61 break;
62 case 3:
63 //d = glm::vec3(U, -1.f, V); break;
64 R = glm::mat3(1.f, 0.f, 0.f,
65 0.f, 0.f, 1.f,
66 0.f, -1.f, 0.f);
67 break;
68 case 4:
69 // d = glm::vec3(U, V, 1.f); break;
70 R = glm::mat3(1.f, 0.f, 0.f,
71 0.f, 1.f, 0.f,
72 0.f, 0.f, 1.f);
73 break;
74
75 default:
76 //d = glm::vec3(-U, V, -1.f); break;
77 R = glm::mat3(-1.f, 0.f, 0.f,
78 0.f, 1.f, 0.f,
79 0.f, 0.f, -1.f);
80 break;
81
82 }
83 return R;
84 }
85
86 glm::vec3 cubeMapDir(float u, float v, size_t f)
87 {
88 glm::mat3 T(2.f, 0.f, 0.f,
89 0.f, -2.f, 0.f,
90 -1.f, 1.f, 1.f);
91 return glm::normalize(cubeMapPermutationMatrix(f)*(T*glm::vec3(u, v, 1.f)));
92 }
93
94 template<typename Kernel, typename Collection, typename Element>
95 struct Sampler
96 {
97 static void run(Collection& rgba, const Kernel& kernel, const size_t w, const size_t h, const size_t f);
98 };
99
100 template<typename Kernel, typename Collection>
101 struct Sampler<Kernel, Collection, glm::u8vec4>
102 {
103 static void run(Collection& rgba, const Kernel& kernel, const size_t w, const size_t h, const size_t f)
104 {
105 for (size_t k = 0; k < f; k++) {
106 for (size_t j = 0; j < h; j++) {
107 for (size_t i = 0; i < w; i++) {
108 const auto v = kernel(i, j, k);
109 rgba[(k*h + j)*w + i].r = static_cast<uint8_t>(glm::clamp(255.f*v.r, 0.f, 255.f));
110 rgba[(k*h + j)*w + i].g = static_cast<uint8_t>(glm::clamp(255.f*v.g, 0.f, 255.f));
111 rgba[(k*h + j)*w + i].b = static_cast<uint8_t>(glm::clamp(255.f*v.b, 0.f, 255.f));
112 rgba[(k*h + j)*w + i].a = static_cast<uint8_t>(glm::clamp(255.f*v.a, 0.f, 255.f));
113 }
114 }
115 }
116 }
117 };
118
119 template<typename Kernel, typename Collection>
120 struct Sampler<Kernel, Collection, half4>
121 {
122 static void run(Collection& rgba, const Kernel& kernel, const size_t w, const size_t h, const size_t f)
123 {
124 for (size_t k = 0; k < f; k++) {
125 for (size_t j = 0; j < h; j++) {
126 for (size_t i = 0; i < w; i++) {
127 const auto v = kernel(i, j, k);
128 rgba[(k*h + j)*w + i].r = glm::detail::toFloat16(v.r);
129 rgba[(k*h + j)*w + i].g = glm::detail::toFloat16(v.g);
130 rgba[(k*h + j)*w + i].b = glm::detail::toFloat16(v.b);
131 rgba[(k*h + j)*w + i].a = glm::detail::toFloat16(v.a);
132 }
133 }
134 }
135 }
136 };
137
138 template<typename Kernel, typename Collection>
139 struct Sampler<Kernel, Collection, glm::vec4>
140 {
141 static void run(Collection& rgba, const Kernel& kernel, const size_t w, const size_t h, const size_t f)
142 {
143 for (size_t k = 0; k < f; k++) {
144 for (size_t j = 0; j < h; j++) {
145 for (size_t i = 0; i < w; i++) {
146 const auto v = kernel(i, j, k);
147 rgba[(k*h + j)*w + i].r = v.r;
148 rgba[(k*h + j)*w + i].g = v.g;
149 rgba[(k*h + j)*w + i].b = v.b;
150 rgba[(k*h + j)*w + i].a = v.a;
151 }
152 }
153 }
154 }
155 };
156
157 // x,y [-1,1]
158 //
159 // Differential area cubemap voxel at x,y is 1/(x^2 + y^2 + 1)^{3/2}.
160 //
161 // We integrate this from [0,0] to [s,t],
162 //
163 // f(x,y) = \int_y=0^t \int_x=0^s 1/(x^2 + y^2 + 1)^{3/2} dx dy
164 // = tan^{-1}(st/sqrt(s^2 + t^2 + 1).
165 //
166 // If A, B, C, and D are corners of the texel, we get
167 //
168 // solidAngle = f(A)-F(B)+f(C)-f(D).
169 //
170 // See http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
171
172 float cubeTexelSolidAngle(float uMin, float uMax, float vMin, float vMax)
173 {
174 float fA = std::atan2(uMin * vMin, std::sqrt(uMin * uMin + vMin * vMin + 1));
175 float fB = std::atan2(uMax * vMin, std::sqrt(uMax * uMax + vMin * vMin + 1));
176 float fC = std::atan2(uMax * vMax, std::sqrt(uMax * uMax + vMax * vMax + 1));
177 float fD = std::atan2(uMin * vMax, std::sqrt(uMin * uMin + vMax * vMax + 1));
178 return fA - fB + fC - fD;
179 }
180
181 void calcIrradiance(Cogs::Memory::TypedBuffer<half4>& dst, const Cogs::Memory::TypedBuffer<glm::vec4>& radiance, const size_t W, const size_t H)
182 {
183 for (size_t k1 = 0; k1 < 6; k1++) {
184 glm::mat3 P1 = cubeMapPermutationMatrix(k1)*glm::mat3(2.f / W, 0.f, 0.f,
185 0.f, -2.f / H, 0.f,
186 -(1.f - 1.f / W), 1.f - 1.f / H, 1.f);
187 for (size_t j1 = 0; j1 < H; j1++) {
188 for (size_t i1 = 0; i1 < W; i1++) {
189 auto d1 = glm::normalize(P1*glm::vec3(i1, j1, 1.f));
190
191 glm::vec3 irradiance;
192 for (size_t k0 = 0; k0 < 6; k0++) {
193 glm::mat3 P0 = cubeMapPermutationMatrix(k0)*glm::mat3(2.f / W, 0.f, 0.f,
194 0.f, -2.f / H, 0.f,
195 -(1.f - 1.f / W), 1.f - 1.f / H, 1.f);
196 for (size_t j0 = 0; j0 < H; j0++) {
197 for (size_t i0 = 0; i0 < W; i0++) {
198 auto d0 = glm::normalize(P0*glm::vec3(i0, j0, 1.f));
199 if (0.f < glm::dot(d0, d1)) {
200
201 float uMin = 2.f*(i0 + 0.f) / W - 1.f;
202 float uMax = 2.f*(i0 + 1.f) / W - 1.f;
203 float vMin = 2.f*(j0 + 0.f) / H - 1.f;
204 float vMax = 2.f*(j0 + 1.f) / H - 1.f;
205 float w = cubeTexelSolidAngle(uMin, uMax, vMin, vMax);
206
207 irradiance += w * glm::vec3(radiance[(k0*H + j0)*W + i0]);
208 }
209 }
210 }
211 }
212
213 //irradiance = /*0.000001f */ irradiance;
214 dst[(k1*H + j1)*W + i1].r = glm::detail::toFloat16(irradiance.r);
215 dst[(k1*H + j1)*W + i1].g = glm::detail::toFloat16(irradiance.g);
216 dst[(k1*H + j1)*W + i1].b = glm::detail::toFloat16(irradiance.b);
217 dst[(k1*H + j1)*W + i1].a = glm::detail::toFloat16(1.f);
218 }
219 }
220 }
221 }
222
223 template<typename T>
224 void sampleSkyLuminance(Cogs::Memory::TypedBuffer<T>& rgba, const ImageDefinition & definition, const size_t w, const size_t h, bool lightBottom, bool withSky, bool withSun)
225 {
226 // Found some numbers where that direct sunlight at zenith gave 1050 W/m^2 irradiance, and 1120 W/m^2
227 // with indirect included, i.e., 70 W/m^2:
228 float indirectDirectRatio = 70.f / 1050.f;
229
230 // Assuming the irradiance is uniform over the hemisphere, and using Lambert's cosine law:
231 // integral_(-pi) ^ (pi)integral_(0) ^ (pi / 2) cos(x) sin(x) dxdy = pi
232 // I.e., irradiance = pi radiance
233 constexpr float radianceIrradianceRatio = 1.f / std::numbers::pi_v<float>;
234
235 // We're in the space that cubemaps tend to be in:
236 //
237 // +X is to the right (cogs +X).
238 // +Y is up (cogs +Z)
239 // +Z is forward (cogs +Y)
240 const auto& s = definition.sunDirection;
241 const glm::vec3 sunDir = glm::normalize(glm::vec3(s.x, s.z, s.y));
242 const glm::vec3 up = normalize(glm::vec3(0, 1, 0));
243
244 auto sunIrradiance = definition.sunIrradiance;
245 auto sunRadiance = radianceIrradianceRatio * indirectDirectRatio*glm::length(sunIrradiance);
246 auto sunCol = (1.f / (std::max(std::max(sunIrradiance.r, sunIrradiance.g), sunIrradiance.b)))*sunIrradiance;
247
248 auto kernel = [&sunDir, sunRadiance, &sunCol, lightBottom, withSky, withSun, &up, w, h](size_t i, size_t j, size_t k) {
249 auto sampleDir = cubeMapDir(float(i + 0.5f) / float(w), float(j + 0.5f) / float(h), k);
250 //float pi = 3.14159265;
251 //float pi2 = 0.5*3.14159265;
252 // float3 sun = normalize(-positions[0].xyz);
253 // float3 up = normalize(float3(viewMatrix._m20_m21_m22));
254
255
256 float zeta_cos = std::max(0.f, glm::dot(sunDir, sampleDir));
257 float zeta = std::acos(zeta_cos);
258 float gamma_sin = std::max(0.f, dot(sampleDir, up));
259 float gamma_s_sin = std::max(0.f, dot(sunDir, up));
260
261 float pi2_zeta_s = glm::half_pi<float>() - std::asin(gamma_s_sin);
262 float pi2_zeta_s_cos = std::cos(pi2_zeta_s);
263
264 float phi_gamma = 1.f - std::exp(-0.32f / gamma_sin); // horizon whiteness
265
266 float f_zeta = 0.91f + 19.f * std::exp(-3.f * zeta) + 0.45f*zeta_cos*zeta_cos; // Sun bloom
267
268 float f_pi2_zeta_s = 0.91f + 19 * std::exp(-3.f * pi2_zeta_s) + 0.45f*pi2_zeta_s_cos*pi2_zeta_s_cos;
269 float phi_pi2 = 0.27385f;
270
271 // Eq 3.1 of CIE spatial Distribution of Daylight, relative of clear sky luminance of zenith.
272 float clear = (phi_gamma*f_zeta) / (phi_pi2* f_pi2_zeta_s);
273
274 glm::vec3 rv;
275 if (withSky) {
276 rv = glm::mix(glm::vec3(0.f, 0.1f, 0.3f), sunCol, 0.1f*std::max(0.f, clear - 0.5f));
277 }
278
279 if (withSun) {
280 rv += 20.f*std::pow(zeta_cos, 100.f)*sunCol;
281 }
282
283 if (!lightBottom && glm::dot(sampleDir, up) < 0.f) {
284 rv = glm::mix(rv, glm::vec3(0, 0, 0.0), glm::clamp(-3 * glm::dot(sampleDir, up), 0.f, 1.f));
285 }
286 return glm::vec4(sunRadiance*rv, 1.f);
287 };
288 rgba.resize(w * h * 6);
289 Sampler<decltype(kernel), decltype(rgba), T>::run(rgba, kernel, w, h, 6);
290 }
291
292 template<typename T>
293 void sampleSubseaLuminance(Cogs::Memory::TypedBuffer<T>& rgba, const ImageDefinition & definition, const size_t w, const size_t h, bool /*lightBottom*/ = true)
294 {
295 const auto& s = definition.sunDirection;
296 const glm::vec3 sunDir = glm::normalize(glm::vec3(s.x, s.z, s.y));
297 auto sunRadiance = glm::length(definition.sunIrradiance);
298
299 auto kernel = [&sunDir, sunRadiance, w, h](size_t i, size_t j, size_t k) {
300 auto sampleDir = cubeMapDir(float(i + 0.5f) / float(w), float(j + 0.5f) / float(h), k);
301
302 const glm::vec3 seaColor(0.1f, 0.7f, 1.f);
303
304 float t = std::pow(1.f - (1.f / glm::pi<float>())*std::acos(glm::dot(sampleDir, sunDir)), 3.f);
305
306 return glm::vec4(1e-3f*seaColor*sunRadiance*t, 1.f);
307 };
308 rgba.resize(w * h * 6);
309 Sampler<decltype(kernel), decltype(rgba), T>::run(rgba, kernel, w, h, 6);
310 }
311
312 template<typename T>
313 void sampleSkyCube(Cogs::Memory::TypedBuffer<T>& rgba, const ImageDefinition & /*definition*/, const size_t w, const size_t h)
314 {
315 auto kernel = [w, h](size_t i, size_t j, size_t k) {
316 auto dir = cubeMapDir(float(i) / float(w), float(j) / float(h), k);
317 float z = 0.5f*(dir.z + 1.f);
318
319 const glm::vec3 waterColor(0.f, 0.2, 0.3);
320 const glm::vec3 skyColorHorizon(0.3, 0.4, 0.5);
321 const glm::vec3 skyColorZenith(0.8, 0.9, 1.0);
322 glm::vec3 c;
323 if (z < 0.45f) {
324 c = glm::mix(waterColor, skyColorHorizon, 2.f*z);
325 }
326 else {
327 c = 1.5f*glm::mix(skyColorHorizon, skyColorZenith, 2.f*(z - 0.5f));
328 }
329 return glm::vec4(c, 1.f);
330 };
331
332 rgba.resize(w * h * 6);
333 Sampler<decltype(kernel), decltype(rgba), T>::run(rgba, kernel, w, h, 6);
334 }
335
336 template<typename Collection>
337 void sampleSky(Collection & rgba, size_t w, size_t h)
338 {
339 glm::vec3 waterColor(0.f, 0.2, 0.3);
340 glm::vec3 skyColorHorizon(0.3, 0.4, 0.5);
341 glm::vec3 skyColorZenith(0.8, 0.9, 1.0);
342
343 for (size_t j = 0; j < h; j++) {
344 for (size_t i = 0; i < w; i++) {
345 float z = float(j) / float(h);
346 glm::vec3 c;
347 if (z < 0.45f) {
348 c = glm::mix(waterColor, skyColorHorizon, 2.f*z);
349 }
350 else {
351 c = 1.5f*glm::mix(skyColorHorizon, skyColorZenith, 2.f*(z - 0.5f));
352 }
353
354 rgba[4 * (w*j + i) + 0] = static_cast<uint8_t>(255.f*glm::clamp(c.r, 0.f, 1.f));
355 rgba[4 * (w*j + i) + 1] = static_cast<uint8_t>(255.f*glm::clamp(c.g, 0.f, 1.f));
356 rgba[4 * (w*j + i) + 2] = static_cast<uint8_t>(255.f*glm::clamp(c.b, 0.f, 1.f));
357 rgba[4 * (w*j + i) + 3] = 255;
358 }
359 }
360 }
361
362 void factory(Texture * texture, const ImageDefinition & definition)
363 {
364 const size_t w = std::max(1u, definition.width);
365 const size_t h = std::max(1u, definition.height);
366 const size_t l = std::max(1u, definition.layers);
367 switch (definition.type) {
368 case ImageType::CheckerBoard:
369 {
370 auto kernel = [](size_t i, size_t j, size_t /*k*/) {
371 bool t = ((i / 16 + j / 16) & 0x1) == 1;
372 return glm::vec4(1.f, 1.f, t ? 0.5f : 1.f, 1.f);
373 };
374 auto rgba = texture->map<glm::u8vec4>((uint16_t)w, (uint16_t)h, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
375 Sampler<decltype(kernel), decltype(rgba), glm::u8vec4>::run(rgba, kernel, w, h, 1);
376 break;
377 }
378
379 case ImageType::ColorfulCheckerBoard:
380 {
381 auto kernel = [A = 1.f / w, B = 1.f / h, C = 1.f / l, l](size_t i, size_t j, size_t k) {
382 bool t = ((i / 16 + j / 16) & 0x1) == 1;
383 return glm::vec4(t ? A * i : 1.f, t ? B * j : 1.f, t ? 0.f : C * (float(l - k)), 1.f);
384 };
386 Sampler<decltype(kernel), decltype(rgba), glm::u8vec4>::run(rgba, kernel, w, h, l);
387 if (2 <= l) {
388 texture->setData(Cogs::ResourceDimensions::Texture2DArray, rgba.data(), rgba.byteSize(), (uint32_t)w, (uint32_t)h, 1, uint32_t(l), 1, 1, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
389 }
390 else {
391 texture->setData(Cogs::ResourceDimensions::Texture2D, rgba.data(), rgba.byteSize(), (uint32_t)w, (uint32_t)h, 1, uint32_t(l), 1, 1, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
392 }
393 break;
394 }
395
396 case ImageType::Dirt:
397 {
398 std::vector<float> noise(w*h);
399 NoiseSampler noiseSampler;
400 noiseSampler.gradNoise2DTurbulence(noise.data(), 16.0f, 256.f, (int)w, (int)h);
401 auto kernel = [&noise, w, h](size_t i, size_t j, size_t /*k*/) {
402 float n0 = 0.5f + 0.5f*noise[w*j + i];
403 float n1 = 0.5f + 0.5f*noise[w*((2 * j + 15) % h) + ((2 * i + 44) % w)];
404 return glm::vec4(0.3f + 0.20f*n0 + 0.15f*n1,
405 0.2f + 0.15f*n0 + 0.20f*n1,
406 0.1f + 0.05f*n0 + 0.15f*n1,
407 1.f);
408 };
409 auto rgba = texture->map<glm::u8vec4>((uint16_t)w, (uint16_t)h, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
410 Sampler<decltype(kernel), decltype(rgba), glm::u8vec4>::run(rgba, kernel, w, h, 1);
411 break;
412 }
413 break;
414
415 case ImageType::Steel:
416 {
417 std::vector<float> noise(w*h);
418 NoiseSampler noiseSampler;
419 noiseSampler.gradNoise2DTurbulence(noise.data(), 16.0f, 64.f, (int)w, (int)h);
420 auto kernel = [&noise, w, h](size_t i, size_t j, size_t /*k*/) {
421 float n0 = 0.5f + 0.5f*noise[w*((j) % h) + (8 * i) % w];
422 float n1 = 0.5f + 0.5f*noise[w*((j + 0) % h) + ((i) % w)];
423 return glm::vec4(0.5f + 0.10f*n0 + 0.07f*n1,
424 0.5f + 0.10f*n0 + 0.06f*n1,
425 0.5f + 0.13f*n0 + 0.05f*n1,
426 1.f);
427 };
428 auto rgba = texture->map<glm::u8vec4>((uint16_t)w, (uint16_t)h, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
429 Sampler<decltype(kernel), decltype(rgba), glm::u8vec4>::run(rgba, kernel, w, h, 1);
430 break;
431 }
432 break;
433
434 case ImageType::ColorCube:
435 {
436 auto kernel = [w, h](size_t i, size_t j, size_t k) {
437 float U = (2.f*i) / w - 1.f;
438 float V = 1.f - (2.f * j) / h;
439
440
441 glm::vec3 d = cubeMapPermutationMatrix(k)*(glm::vec3(U, V, 1.f));
442
443 float m = (((i / 64) + (j / 64)) & 1) ? 1.f : 0.5f;
444 return glm::vec4(m, m, m, 1)*glm::vec4(0.5f*d + glm::vec3(0.5f), 1.f);
445 };
447 Sampler<decltype(kernel), decltype(rgba), glm::u8vec4>::run(rgba, kernel, w, h, 6);
448 texture->setData(Cogs::ResourceDimensions::TextureCube, rgba.data(), rgba.byteSize(), (uint32_t)w, (uint32_t)h, 1, 1, 6, 1, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
449 break;
450 }
451
452 case ImageType::Sky:
453 {
454 auto rgb = texture->map((uint16_t)w, (uint16_t)h, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
455 sampleSky(rgb, w, h);
456 }
457 break;
458
459 case ImageType::SkyCube:
460 {
461 if (!std::isfinite(definition.sunDirection.x) ||
462 !std::isfinite(definition.sunDirection.y) ||
463 !std::isfinite(definition.sunDirection.z))
464 {
465 auto newDefininition = definition;
466 newDefininition.type = ImageType::ColorCube;
467 return factory(texture, newDefininition);
468 }
469
470 if (definition.hdr) {
471 Cogs::Memory::TypedBuffer<half4> rgba(w * h * 6);
472 sampleSkyLuminance(rgba, definition, w, h, false, true, true);
473 texture->setData(Cogs::ResourceDimensions::TextureCube, rgba.data(), rgba.byteSize(), static_cast<uint32_t>(w), static_cast<uint32_t>(h), 1, 1, 6, 1, Cogs::TextureFormat::R16G16B16A16_FLOAT, true);
474 }
475 else {
477 sampleSkyCube(rgba, definition, w, h);
478 texture->setData(Cogs::ResourceDimensions::TextureCube, rgba.data(), rgba.byteSize(), static_cast<uint32_t>(w), static_cast<uint32_t>(h), 1, 1, 6, 1, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
479 }
480 break;
481 }
482
483 case ImageType::SkyRadiance:
484 {
485 if (!std::isfinite(definition.sunDirection.x) ||
486 !std::isfinite(definition.sunDirection.y) ||
487 !std::isfinite(definition.sunDirection.z))
488 {
489 auto newDefininition = definition;
490 newDefininition.type = ImageType::ColorCube;
491 return factory(texture, newDefininition);
492 }
493 size_t W = std::min((size_t)128, w);
494 size_t H = std::min((size_t)128, h);
495
496 Cogs::Memory::TypedBuffer<half4> rgba(W * H * 6);
497 sampleSkyLuminance(rgba, definition, W, H, true, true, true);
498 texture->setData(Cogs::ResourceDimensions::TextureCube, rgba.data(), rgba.byteSize(), static_cast<uint32_t>(W), static_cast<uint32_t>(H), 1, 1, 6, 1, Cogs::TextureFormat::R16G16B16A16_FLOAT, true);
499 break;
500 }
501
502 case ImageType::SkyIrradiance:
503 {
504 if (!std::isfinite(definition.sunDirection.x) ||
505 !std::isfinite(definition.sunDirection.y) ||
506 !std::isfinite(definition.sunDirection.z))
507 {
508 auto newDefininition = definition;
509 newDefininition.type = ImageType::ColorCube;
510 return factory(texture, newDefininition);
511 }
512 size_t W = std::min((size_t)8, w);
513 size_t H = std::min((size_t)8, h);
514
515 Cogs::Memory::TypedBuffer<glm::vec4> radiance(W * H * 6);
516 Cogs::Memory::TypedBuffer<half4> rgba(W * H * 6);
517
518 sampleSkyLuminance(radiance, definition, W, H, true, false, true);
519 calcIrradiance(rgba, radiance, W, H);
520 texture->setData(Cogs::ResourceDimensions::TextureCube, rgba.data(), rgba.byteSize(), static_cast<uint32_t>(W), static_cast<uint32_t>(H), 1, 1, 6, 1, Cogs::TextureFormat::R16G16B16A16_FLOAT, true);
521 break;
522 }
523
524 case ImageType::SkyAmbientIrradiance:
525 {
526 if(!std::isfinite(definition.sunDirection.x) ||
527 !std::isfinite(definition.sunDirection.y) ||
528 !std::isfinite(definition.sunDirection.z))
529 {
530 auto newDefininition = definition;
531 newDefininition.type = ImageType::ColorCube;
532 return factory(texture, newDefininition);
533 }
534 size_t W = std::min((size_t)8, w);
535 size_t H = std::min((size_t)8, h);
536
537 Cogs::Memory::TypedBuffer<glm::vec4> radiance(W * H * 6);
538 Cogs::Memory::TypedBuffer<half4> rgba(W * H * 6);
539
540 sampleSkyLuminance(radiance, definition, W, H, true, true, false);
541 for (size_t i = 0; i < W*H*6; i++) {
542 radiance[i] = glm::vec4(0.5f*glm::vec3(radiance[i]), 1);
543 }
544 calcIrradiance(rgba, radiance, W, H);
545 texture->setData(Cogs::ResourceDimensions::TextureCube, rgba.data(), rgba.byteSize(), static_cast<uint32_t>(W), static_cast<uint32_t>(H), 1, 1, 6, 1, Cogs::TextureFormat::R16G16B16A16_FLOAT, true);
546 break;
547 }
548
549
550 case ImageType::SubseaRadiance:
551 {
552 if (!std::isfinite(definition.sunDirection.x) ||
553 !std::isfinite(definition.sunDirection.y) ||
554 !std::isfinite(definition.sunDirection.z))
555 {
556 auto newDefininition = definition;
557 newDefininition.type = ImageType::ColorCube;
558 return factory(texture, newDefininition);
559 }
560 size_t W = std::min((size_t)32, w);
561 size_t H = std::min((size_t)32, h);
562
563 Cogs::Memory::TypedBuffer<half4> rgba(W * H * 6);
564 sampleSubseaLuminance(rgba, definition, W, H);
565 texture->setData(Cogs::ResourceDimensions::TextureCube, rgba.data(), rgba.byteSize(), static_cast<uint32_t>(W), static_cast<uint32_t>(H), 1, 1, 6, 1, Cogs::TextureFormat::R16G16B16A16_FLOAT, true);
566 break;
567 }
568
569 case ImageType::GradientRainbow:
570 {
571 uint8_t rgba[8 * 4] = {
572 0x9d, 0x00, 0xff, 0xff,
573 0x00, 0x27, 0xff, 0xff,
574 0x00, 0xc4, 0xff, 0xff,
575 0x00, 0xff, 0x9d, 0xff,
576 0x00, 0xff, 0x00, 0xff,
577 0xc4, 0xff, 0x00, 0xff,
578 0xff, 0x9d, 0x00, 0xff,
579 0xff, 0x00, 0x00, 0xff,
580 };
581 texture->setData(Cogs::ResourceDimensions::Texture2D, rgba, sizeof(rgba), 8, 1, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
582 break;
583 }
584 case ImageType::GradientHeat:
585 {
586 uint8_t rgba[8 * 4] = {
587 0x1e, 0x00, 0x00, 0xff,
588 0x55, 0x00, 0x00, 0xff,
589 0xc3, 0x26, 0x0c, 0xff,
590 0xf2, 0x74, 0x29, 0xff,
591 0xfb, 0xc6, 0x21, 0xff,
592 0xf8, 0xf1, 0x4c, 0xff,
593 0xf9, 0xf5, 0xa8, 0xff,
594 0xff, 0xff, 0xff, 0xff,
595 };
596 texture->setData(Cogs::ResourceDimensions::Texture2D, rgba, sizeof(rgba), 8, 1, Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB, true);
597 break;
598 }
599
600 default:
601 break;
602 }
603 }
604
605 ParsedValue * getValue(ParsedValue & p, const Cogs::StringView & key)
606 {
607 for (auto & v : p.values) {
608 if (key == v.key) return &v;
609 }
610
611 return nullptr;
612 }
613
614 int getInt(ParsedValue * p, int defaultValue)
615 {
616 if (!p) return defaultValue;
617
618 switch (p->type)
619 {
620 case ParsedDataType::UInt:
621 case ParsedDataType::Int:
622 return p->intValue;
623 break;
624 case ParsedDataType::Float:
625 return static_cast<int>(p->floatValue);
626 default:
627 break;
628 }
629
630 return defaultValue;
631 }
632
633 uint32_t getUInt(ParsedValue * p, uint32_t defaultValue)
634 {
635 return static_cast<uint32_t>(getInt(p, static_cast<int>(defaultValue)));
636 }
637
638 bool getBool(ParsedValue * p, bool defaultValue)
639 {
640 return p != nullptr && p->type == ParsedDataType::Bool ? p->boolValue : defaultValue;
641 }
642
643 glm::vec3 getFloat3(ParsedValue * p, const glm::vec3& defaultValue)
644 {
645 return p != nullptr && p->type == ParsedDataType::Float3 ? p->float3Value : defaultValue;
646 }
647
648}
649
650namespace Cogs::Core
651{
653 {
654 Mutex lock;
655 std::unordered_map<size_t, TextureHandle> images;
656 };
657}
658
659Cogs::Core::TextureGenerator::TextureGenerator(Context * context) :
660 context(context),
661 cache(std::make_unique<ImageCache>())
662{
663}
664
665Cogs::Core::TextureGenerator::~TextureGenerator()
666{
667}
668
669void Cogs::Core::TextureGenerator::cleanup()
670{
671 LockGuard cacheLock(cache->lock);
672
673 cache->images.clear();
674}
675
676Cogs::Core::TextureHandle Cogs::Core::TextureGenerator::getTexture(ImageType type)
677{
678 return getTexture(type, ParsedValue());
679}
680
681Cogs::Core::TextureHandle Cogs::Core::TextureGenerator::getTexture(ImageType type, ParsedValue parameters)
682{
683 ImageDefinition definition = {
684 .sunDirection = getFloat3(getValue(parameters, "sunDirection"), ImageDefinition().sunDirection),
685 .sunIrradiance = getFloat3(getValue(parameters, "sunIrradiance"), ImageDefinition().sunIrradiance),
686 .type = type,
687 .width = getUInt(getValue(parameters, "width"), ImageDefinition().width),
688 .height = getUInt(getValue(parameters, "height"), ImageDefinition().height),
689 .layers = getUInt(getValue(parameters, "layers"), ImageDefinition().layers),
690 .hdr = getBool(getValue(parameters, "hdr"), ImageDefinition().hdr)
691 };
692
693 size_t hashValue = definition.hash();
694
695 {
696 LockGuard cacheLock(cache->lock);
697
698 auto found = cache->images.find(hashValue);
699
700 if (found != cache->images.end()) {
701 return found->second;
702 }
703 }
704
705 auto texture = context->textureManager->create();
706
707 factory(texture.resolve(), definition);
708
709 LockGuard cacheLock(cache->lock);
710
711 cache->images[hashValue] = texture;
712
713 return texture;
714}
715
716void Cogs::Core::TextureGenerator::getTexture(Texture * texture, const ImageDefinition & definition)
717{
718 factory(texture, definition);
719}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:50
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
STL namespace.
Stores the parsed output of a key/value pair.
Definition: Parsing.h:40
Texture resources contain raster bitmap data to use for texturing.
Definition: Texture.h:91
MappedTexture< uint8_t > map(uint16_t width, uint16_t height, TextureFormat format, bool generateMipMap)
Map the texture data, ensuring the data is sized to hold width * height * bpp of the format bytes.
Definition: Texture.h:129
void setData(ResourceDimensions target, const void *data, size_t size, int width, int height, TextureFormat format, bool generateMipMap)
Set the texture data.
Definition: Texture.cpp:54