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