Cogs.Core
BasicOceanSystem.cpp
1#include "Rendering/IGraphicsDevice.h"
2#include "Rendering/ICapabilities.h"
3#include "Foundation/Logging/Logger.h"
4
5#include "BasicOceanSystem.h"
6
7#include "Components/Appearance/MaterialComponent.h"
8#include "Components/Geometry/AdaptivePlanarGridComponent.h"
9#include "Components/Core/SceneComponent.h"
10#include "Components/Core/CameraComponent.h"
11#include "Components/Behavior/ReflectionComponent.h"
12
13#include "Systems/Core/TransformSystem.h"
14#include "Systems/Geometry/AdaptivePlanarGridSystem.h"
15
16#include "Resources/MaterialManager.h"
17#include "Resources/TextureManager.h"
18#include "Resources/Texture.h"
19
20#include "Services/Variables.h"
21#include "Services/Time.h"
22#include "Services/TaskManager.h"
23
24#include "EntityStore.h"
25
26#include "Utilities/Parsing.h"
27
28#include "Context.h"
29
30#define _USE_MATH_DEFINES
31#include <cmath>
32#include <glm/glm.hpp>
33#include <algorithm>
34#include <limits>
35#include <random>
36
37#include <cassert>
38#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
39#include <xmmintrin.h>
40#endif
41
42namespace {
43 using namespace Cogs::Core;
44 Cogs::Logging::Log logger = Cogs::Logging::getLogger("BasicOceanSystem");
45
46 const std::string animateKey = "basic-ocean.animate";
47 const std::string eightbitKey = "basic-ocean.8bit";
48
49 void initMaterialInstanceCallback(MaterialInstance* instance, Cogs::ComponentModel::Entity* /*container*/, void* data)
50 {
51 auto * oceanData = static_cast<BasicOceanData*>(data);
52 assert(oceanData->waveVariant != nullptr);
53 instance->setVariant("Encoding", oceanData->encoding);
54 instance->setVariant("Waves", oceanData->waveVariant);
55 instance->setVariant("BeliefSystem", oceanData->beliefSystem);
56 instance->setVariant("LightModel", oceanData->lightModelVariant);
57 instance->setVariant("Reflection", oceanData->reflectionVariant);
58 }
59
60 using std::complex;
61
62 // Reverse the lower logN bits of i
63 inline size_t reverseBits16(size_t i, size_t log2N)
64 {
65 assert(log2N <= 16);
66
67 size_t t = i;
68 t = ((0xAAAAu & t) >> 1) | ((t << 1) & 0xAAAAu); // 1010101010101010
69 t = ((0xCCCCu & t) >> 2) | ((t << 2) & 0xCCCCu); // 1100110011001100
70 t = ((0xF0F0u & t) >> 4) | ((t << 4) & 0xF0F0u); // 1111000011110000
71 t = ((0xFF00u & t) >> 8) | ((t << 8) & 0xFF00u); // 1111111100000000
72 return t >> (16 - log2N);
73 }
74
75#if 0
76 const float twoPi = float(2.0 * M_PI);
77 // Calculate complex twiddle-factor e^{-2\pi n/N}
78 inline complex<float> twiddleFactor(const int n, const int N)
79 {
80 const float arg = (-twoPi * float(n)) / float(N);
81 return complex<float>(cos(arg), sin(arg));
82 }
83#endif
84
85
86#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
87 void inverseRadix2MajorIndexTransposeAlignedSoALog4SSE1(float* dstReal,
88 float* dstImag,
89 const float* srcReal,
90 const float* srcImag,
91 const float factor,
92 const float scale,
93 const size_t log2N)
94 {
95 size_t N = 1 << log2N;
96 size_t Nq = N / 4;
97
98 // first pass, also handle transpose and shuffle
99 for (size_t j = 0; j < N / 2; j++) {
100 size_t index0 = 2 * j;
101 size_t index1 = index0 + 1;
102 size_t reversedIndex0 = reverseBits16(index0, log2N);
103 size_t reversedIndex1 = reverseBits16(index1, log2N);
104 __m128 mm_scale = _mm_set1_ps(scale);
105 for (size_t i = 0; i < Nq; i++) {
106 size_t srcIx0 = (4 * i * N + reversedIndex0);
107 __m128 aReal02 = _mm_unpacklo_ps(_mm_load_ss(srcReal + srcIx0 + 0 * N), _mm_load_ss(srcReal + srcIx0 + 2 * N));
108 __m128 aReal13 = _mm_unpacklo_ps(_mm_load_ss(srcReal + srcIx0 + 1 * N), _mm_load_ss(srcReal + srcIx0 + 3 * N));
109 __m128 aReal = _mm_mul_ps(mm_scale, _mm_unpacklo_ps(aReal02, aReal13));
110
111 __m128 aImag02 = _mm_unpacklo_ps(_mm_load_ss(srcImag + srcIx0 + 0 * N), _mm_load_ss(srcImag + srcIx0 + 2 * N));
112 __m128 aImag13 = _mm_unpacklo_ps(_mm_load_ss(srcImag + srcIx0 + 1 * N), _mm_load_ss(srcImag + srcIx0 + 3 * N));
113 __m128 aImag = _mm_mul_ps(mm_scale, _mm_unpacklo_ps(aImag02, aImag13));
114
115 size_t srcIx1 = (4 * i * N + reversedIndex1);
116 __m128 bReal02 = _mm_unpacklo_ps(_mm_load_ss(srcReal + srcIx1 + 0 * N), _mm_load_ss(srcReal + srcIx1 + 2 * N));
117 __m128 bReal13 = _mm_unpacklo_ps(_mm_load_ss(srcReal + srcIx1 + 1 * N), _mm_load_ss(srcReal + srcIx1 + 3 * N));
118 __m128 bReal = _mm_mul_ps(mm_scale, _mm_unpacklo_ps(bReal02, bReal13));
119
120 __m128 bImag02 = _mm_unpacklo_ps(_mm_load_ss(srcImag + srcIx1 + 0 * N), _mm_load_ss(srcImag + srcIx1 + 2 * N));
121 __m128 bImag13 = _mm_unpacklo_ps(_mm_load_ss(srcImag + srcIx1 + 1 * N), _mm_load_ss(srcImag + srcIx1 + 3 * N));
122 __m128 bImag = _mm_mul_ps(mm_scale, _mm_unpacklo_ps(bImag02, bImag13));
123
124 __m128 pReal = _mm_add_ps(aReal, bReal);
125 __m128 pImag = _mm_add_ps(aImag, bImag);
126 size_t dstIx0 = (index0 * N + 4 * i);
127 _mm_store_ps(dstReal + dstIx0, pReal);
128 _mm_store_ps(dstImag + dstIx0, pImag);
129
130 __m128 qReal = _mm_sub_ps(aReal, bReal);
131 __m128 qImag = _mm_sub_ps(aImag, bImag);
132 size_t dstIx1 = (index1 * N + 4 * i);
133 _mm_store_ps(dstReal + dstIx1, qReal);
134 _mm_store_ps(dstImag + dstIx1, qImag);
135 }
136 }
137
138 // remaining passes
139 for (size_t l = 1; l < log2N; l++) {
140 int blockSize = 2 << l;
141 for (size_t j = 0; j < N / 2; j++) {
142 size_t blockNumber = j >> l;
143 size_t blockIndex = j & ((1 << l) - 1);
144 size_t index0 = (blockSize * blockNumber + blockIndex);
145 size_t index1 = (index0 + blockSize / 2);
146 float* real0 = dstReal + index0 * N;
147 float* imag0 = dstImag + index0 * N;
148 float* real1 = dstReal + index1 * N;
149 float* imag1 = dstImag + index1 * N;
150
151 int twiddleIndex = int(blockIndex << (log2N - l - 1)); // of N
152 const float twiddleArg = (factor * twiddleIndex) / N;
153 __m128 wReal = _mm_set1_ps(cos(twiddleArg));
154 __m128 wImag = _mm_set1_ps(sin(twiddleArg));
155 for (size_t i = 0; i < Nq; i++) {
156 __m128 aReal = _mm_load_ps(real0);
157 __m128 bReal = _mm_load_ps(real1);
158
159 __m128 aImag = _mm_load_ps(imag0);
160 __m128 bImag = _mm_load_ps(imag1);
161
162 __m128 cReal = _mm_sub_ps(_mm_mul_ps(wReal, bReal), _mm_mul_ps(wImag, bImag));
163 __m128 cImag = _mm_add_ps(_mm_mul_ps(wReal, bImag), _mm_mul_ps(wImag, bReal));
164
165 _mm_store_ps(real0, _mm_add_ps(aReal, cReal)); real0 += 4;
166 _mm_store_ps(imag0, _mm_add_ps(aImag, cImag)); imag0 += 4;
167
168 _mm_store_ps(real1, _mm_sub_ps(aReal, cReal)); real1 += 4;
169 _mm_store_ps(imag1, _mm_sub_ps(aImag, cImag)); imag1 += 4;
170 }
171 }
172 }
173 }
174#endif
175
176 void inverseRadix2MajorIndexTranspose(float* dstReal,
177 float* dstImag,
178 const size_t dstStride,
179 const float* srcReal,
180 const float* srcImag,
181 const size_t srcStride,
182 const float factor,
183 const float scale,
184 const size_t log2N)
185 {
186 size_t N = (size_t)1 << log2N;
187
188 // first pass, also handle transpose and shuffle
189 for (size_t j = 0; j < N / 2; j++) {
190 size_t index0 = 2 * j;
191 size_t index1 = index0 + 1;
192 size_t reversedIndex0 = reverseBits16(index0, log2N);
193 size_t reversedIndex1 = reverseBits16(index1, log2N);
194 for (size_t i = 0; i < N; i++) {
195 size_t srcIx0 = srcStride * (i * N + reversedIndex0);
196 size_t srcIx1 = srcStride * (i * N + reversedIndex1);
197 size_t dstIx0 = dstStride * (index0 * N + i);
198 size_t dstIx1 = dstStride * (index1 * N + i);
199 complex<float> a = scale * complex<float>(srcReal[srcIx0], srcImag[srcIx0]);
200 complex<float> b = scale * complex<float>(srcReal[srcIx1], srcImag[srcIx1]);
201 complex<float> p = a + b;
202 complex<float> q = a - b;
203 dstReal[dstIx0] = p.real(); dstImag[dstIx0] = p.imag();
204 dstReal[dstIx1] = q.real(); dstImag[dstIx1] = q.imag();
205 }
206 }
207
208 // remaining passes
209 for (size_t l = 1; l < log2N; l++) {
210 int blockSize = 2 << l;
211 for (size_t j = 0; j < N / 2; j++) {
212 size_t blockNumber = j >> l;
213 size_t blockIndex = j & ((1 << l) - 1);
214 size_t index0 = (blockSize * blockNumber + blockIndex);
215 size_t index1 = (index0 + blockSize / 2);
216 int twiddleIndex = int(blockIndex << (log2N - l - 1)); // of N
217 const float twiddleArg = (factor * twiddleIndex) / N;
218 complex<float> w(cos(twiddleArg), sin(twiddleArg));
219 for (size_t i = 0; i < N; i++) {
220 size_t srcDstIx0 = dstStride * (index0 * N + i);
221 size_t srcDstIx1 = dstStride * (index1 * N + i);
222 complex<float> a = complex<float>(dstReal[srcDstIx0], dstImag[srcDstIx0]);
223 complex<float> b = complex<float>(dstReal[srcDstIx1], dstImag[srcDstIx1]);
224 complex<float> c = w * b;
225 complex<float> p = a + c;
226 complex<float> q = a - c;
227 dstReal[srcDstIx0] = p.real(); dstImag[srcDstIx0] = p.imag();
228 dstReal[srcDstIx1] = q.real(); dstImag[srcDstIx1] = q.imag();
229 }
230 }
231 }
232 }
233
235 void fastGenericFourierTransform2D(Cogs::Core::Context* /*context*/,
236 uint8_t* scratch,
237 uint8_t* dstReal,
238 uint8_t* dstImag,
239 const size_t dstStride,
240 const uint8_t* srcReal,
241 const uint8_t* srcImag,
242 const size_t srcStride,
243 const float factor,
244 const float scale,
245 const size_t log2N)
246 {
247 // We assume that we can cast these pointers to float pointers (i.e., multiple of four bytes)
248 assert((reinterpret_cast<size_t>(dstReal) & 0x3) == 0);
249 assert((reinterpret_cast<size_t>(dstImag) & 0x3) == 0);
250 assert((reinterpret_cast<size_t>(srcReal) & 0x3) == 0);
251 assert((reinterpret_cast<size_t>(srcImag) & 0x3) == 0);
252 assert((dstStride & 0x3) == 0);
253 assert((srcStride & 0x3) == 0);
254
255 float* scratchf = reinterpret_cast<float*>(32 * ((reinterpret_cast<size_t>(scratch) + 31) / 32));
256 float* dstRealf = reinterpret_cast<float*>(dstReal);
257 float* dstImagf = reinterpret_cast<float*>(dstImag);
258 const float* srcRealf = reinterpret_cast<const float*>(srcReal);
259 const float* srcImagf = reinterpret_cast<const float*>(srcImag);
260 size_t dstStridef = dstStride / sizeof(float);
261 size_t srcStridef = srcStride / sizeof(float);
262
263#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
264 size_t N = 1 << log2N;
265 if ((N >= 4) &&
266 ((N & 3) == 0) &&
267 (srcStridef == 1) &&
268 (dstStridef == 1) &&
269 ((reinterpret_cast<size_t>(dstReal) & 0xf) == 0) &&
270 ((reinterpret_cast<size_t>(dstImag) & 0xf) == 0) &&
271 ((reinterpret_cast<size_t>(srcReal) & 0xf) == 0) &&
272 ((reinterpret_cast<size_t>(srcImag) & 0xf) == 0))
273 {
274 inverseRadix2MajorIndexTransposeAlignedSoALog4SSE1(scratchf, scratchf + N * N,
275 srcRealf, srcImagf,
276 factor, scale, log2N);
277
278 inverseRadix2MajorIndexTransposeAlignedSoALog4SSE1(dstRealf, dstImagf,
279 scratchf, scratchf + N * N,
280 factor, 1.f, log2N);
281 return;
282 }
283#endif
284
285 inverseRadix2MajorIndexTranspose(scratchf, scratchf + 1, 2,
286 srcRealf, srcImagf, srcStridef,
287 factor, scale, log2N);
288
289 inverseRadix2MajorIndexTranspose(dstRealf, dstImagf, dstStridef,
290 scratchf, scratchf + 1, 2,
291 factor, 1.f, log2N);
292 /*
293 inverseRadix2MajorIndexTranspose(scratchf, scratchf + N*N, 1,
294 srcRealf, srcImagf, srcStridef,
295 factor, log2N);
296
297 inverseRadix2MajorIndexTranspose(dstRealf, dstImagf, dstStridef,
298 scratchf, scratchf + N*N, 1,
299 1.f, log2N);
300 */
301 }
302
304 inline size_t fastGenericFourierTransform2DScratchsize(size_t log2N) { return sizeof(float) * (2 << (log2N << 1)) + 64; }
305
307 inline void fastInverseFourierTransform2D(Cogs::Core::Context* context,
308 std::vector<uint8_t>& scratch,
310 const Cogs::Core::ComplexArray& src,
311 const float scale,
312 const size_t log2N)
313 {
314 CpuInstrumentationScope(SCOPE_GEOMETRY, "OceanSystem::fastInverseFourierTransform2D");
315
316 scratch.resize(fastGenericFourierTransform2DScratchsize(log2N));
317 fastGenericFourierTransform2D(context,
318 reinterpret_cast<uint8_t*>(scratch.data()),
319 reinterpret_cast<uint8_t*>(dst.real()),
320 reinterpret_cast<uint8_t*>(dst.imag()),
321 sizeof(float),
322 reinterpret_cast<const uint8_t*>(src.real()),
323 reinterpret_cast<const uint8_t*>(src.imag()),
324 sizeof(float),
325 float(2.0 * 3.14159265358979323846),
326 scale,
327 log2N);
328 }
329
330 // Linear ocean wave theory
331 // ------------------------
332 //
333 // We assume that wave amplitudes are small, and the surface elevation z of a
334 // wave traveling along x is
335 //
336 // z = a sin( k x - omega t),
337 //
338 // where
339 //
340 // omega = 2 pi f = 2 pi / T is the wave frequency in rad/s,
341 // f is the frequency in Hz,
342 // T is the wave period in seconds,
343 // k = 2 pi / L is the wave number (in rad/meter I guess)
344 // L is the wave length in meters.
345 //
346 // Here, the wave frequency is described in two domains, omega is from the
347 // _time domain_ and k is from the _spatial domain_. These two domains are
348 // related by the dispersion relation,
349 //
350 // omega^2 = g k tanh(k d),
351 //
352 // where
353 //
354 // g is the acceleration of gravity, and
355 // d is the water depth.
356 //
357 // In deep water (d > L/4), we can use the following approximation
358 //
359 // omega^2 = g k,
360 //
361 // and in shallow water (d < L/11), we can use the following approximation
362 //
363 // omega^2 = g k^2 d.
364 //
365 // Phase velocity is the speed at which the wave propagates,
366 //
367 // c = omega / k = L / T.
368 //
369 // The deep and shallow water approximations give
370 //
371 // c = sqrt(g/k) = g/omega in deep-water, and
372 // c = sqrt(g d) in shallow water.
373 //
374 // Significant wave height H is given by
375 //
376 // H = 4 stddev(z),
377 //
378 // i.e., four times the standard deviation of the surface displacement.
379 //
380 //
381 // Von Gerstner waves
382 // ------------------
383 //
384 // Linear waves describe waves with sinusoidal shapes, which is appropriate in
385 // calm weather. However, when wave steepness increases, the crests become
386 // sharper and troughs flatter.
387 //
388 // This can be modeled by trochoids, that is, a fixed point on a circle as the
389 // circle spins,
390 //
391 // x = a theta - b sin( theta )
392 // y = a - b cos( theta ),
393 //
394 // in other words, add spatial displacement in addition to vertical
395 // displacement. To classify trochoids, we have prolate (folding, a/b < 1),
396 // common (sharp tip, a/b=1), and prolate (smoothed tip, a/b > 1). When our
397 // trochoids are prolate, our water surface folds, which is a hint that we
398 // may have reached the limits of the validity of our model.
399 //
400 // Our sea is a composition of waves, and summing over several wave numbers,
401 //
402 // X(u,v,t) = u + \sum_j\sum_i i/|ij| a(ij) sin(dot(ij,uv) - omega(ij)t + phi(ij)),
403 // Y(u,v,t) = v + \sum_j\sum_i j/|ij| a(ij) sin(dot(ij,uv) - omega(ij)t + phi(ij)),
404 // Z(u,v,t) = - \sum_j\sum_i a(ij) cos(dot(ij,uv) - omega(ij)t + phi(ij)).
405 //
406 // To get analytical normal vectors,
407 // dX(u,v,t)/du = 1 + \sum_j\sum_i i^2/|ij| a(ij) cos(dot(ij,uv) - omega(ij)t + phi(ij)),
408 // dX(u,v,t)/dv = dY(u,v,t)/du = \sum_j\sum_i ij/|ij| a(ij) cos(dot(ij,uv) - omega(ij)t + phi(ij)),
409 // dY(u,v,t)/dv = 1 + \sum_j\sum_i j^2/|ij| a(ij) cos(dot(ij,uv) - omega(ij)t + phi(ij)),
410 // dZ(u,v,t)/du = \sum_j\sum_i i a(ij) sin(dot(ij,uv) - omega(ij)t + phi(ij)).
411 // dZ(u,v,t)/dv = \sum_j\sum_i j a(ij) sin(dot(ij,uv) - omega(ij)t + phi(ij)).
412 //
413 // Wave spectrum
414 // --------------
415 //
416 //
417 // Swell periods
418 // -------------
419 //
420 // Notes about wave periods according to magicseaweed surf forecasting site:
421 //
422 // 1-4 seconds: Weak and unsurfable. Sea will look lumpy and bumpy, but
423 // you'll struggle to see individual waves.
424 // 5-6 seconds: You will see the odd weak rideable wave face if you're very
425 // desperate.
426 // 7-9 seconds: Ok for areas that don't get great waves, will in general be
427 // weaker and jumbled up without clear sets.
428 // 10-12 seconds: Swells that often be starting to head away from the storms
429 // that created them, and an travel the open ocean for some
430 // distance. Often creates good quality surf.
431 // 13-15 seconds: Definitely groundswell, normally created some considerable
432 // distance from the beach by powerful storms. They most often
433 // arrive without the storm that created them. These swells
434 // will have more defined sets and look a lot more lined up
435 // than lower period swells.
436 // 16+ seconds: Extremely powerful swells generated by distant storms often
437 // traveling the breadth of the largest oceans.
438
439
440 // z(u,v,t) = \sum_i\sum_j a(i,j)cos(<ij,uv> - omega(k)t + phi(k))
441 // dz(u,v,t)/du = \sum\sum -ia(i,j)sin(..)
442 // dz(u,v,t)/dv = \sum\sum -ja(i,j)sin(..)
443
444 using std::complex;
445 using std::sqrt;
446 using std::min;
447 using std::max;
448 using std::pow;
449 using std::exp;
450 using std::tgamma;
451 using std::sin;
452 using std::cos;
453 using std::acos;
454 using std::log;
455 using std::polar;
456
457 using glm::vec2;
458 using glm::vec3;
459 using glm::vec4;
460 using glm::ivec2;
461 using glm::length;
462 using glm::dot;
463 using glm::cross;
464
465 const float pi = std::acos(-1.f);
466
467 const float twoPi = float(2.0 * glm::pi<float>());
468
469 const float g = 9.80665f; // gravity of earth
470
471 float waveSpectrumPhillips(const float omega,
472 const float alpha = 0.0081f)
473 {
474 const float omega2 = omega * omega;
475 const float omega4 = omega2 * omega2;
476 const float omega5 = omega4 * omega;
477
478 return (alpha * g * g) / omega5;
479 }
480
481 float waveSpectrumPiersonMoskowitz(const float omega,
482 const float omega_p, // frequency of the spectrum peak
483 const float alpha = 0.0081f)
484 {
485 const float omega_p_over_omega = omega_p / omega;
486 const float omega_p_over_omega4 = (omega_p_over_omega * omega_p_over_omega) * (omega_p_over_omega * omega_p_over_omega);
487
488 return waveSpectrumPhillips(omega, alpha) * exp(float(-5.0 / 4.0) * omega_p_over_omega4);
489 }
490
491 float dispersionLonguetHiggins(const float omega,
492 const float omega_p,
493 const float U10,
494 const float windWaveAngle) // in [-pi,pi]
495 {
496
497 // Sharpness of directional spreading (Mitsuyasu et al., 1975)
498 float mu = omega <= omega_p ? 5.f : -2.5f;
499 float s_omega = 11.5f * pow(g / (omega_p * U10), 2.5f) * pow(omega / omega_p, mu);
500 assert(std::isfinite(s_omega));
501
502 // Normalization factor of dispersion relation
503
504 // Part of this is the ratio gamma(x+1)/gamma(x+0.5). Evaluating this
505 // directly is begging for overflow since gamma rapidly becomes'
506 // ridiculously large, e.g. gamma(30)=8.8x10^30. However, since we are
507 // interested in the ratio, the following asymptotic series,
508 //
509 // gamma(J+1/2)/gamma(J) = sqrt(gamma)*(1 - 1/8J + 1/128J^2
510 // 5/1024J^3 - 21/32768J^4 + ... )
511 //
512 // is handy, as if we define J = s_omega + 0.5, we can use it directly
513 // to evaluate the ration.
514 float J = s_omega + 0.5f;
515 float gammaRatio = sqrt(J) * (1.f - 1.f / (8.f * J) + 1.f / (128.f * J * J) + 5.f / (1024.f * J * J * J) - 21.f / (32768.f * J * J * J * J));
516 float N_s_omega = float(1.0 / (2.0 * sqrt(glm::pi<float>()))) * (gammaRatio);
517 assert(std::isfinite(N_s_omega));
518
519 // x!(x / ((1 / 2 (2 x + 1))!) + 1 / (2 (1 / 2 (2 x + 1))!))
520
521 // Wind-wave direction angle halved
522 // float theta_half = 0.5f*(waveDirection);
523
524 // Dispersion relation (Longuet-Higgins, 1962)
525 float D = N_s_omega * pow(max(0.0f, cos(0.5f * windWaveAngle)), 2.f * s_omega);
526 assert(std::isfinite(D));
527 return D;
528 }
529
530
531
532 void createDirectionalWaveSpectrum(std::vector<float>& E,
533 size_t N,
534 float L,
535 float /*omega_pz*/,
536 float windSpeed,
537 float windDirection,
538 float /*significantWaveHeight*/,
539 float dominantWavePeriod)
540 {
541 if (windDirection >= pi) {
542 windDirection -= twoPi;
543 }
544 if (windSpeed < 2.f) {
545 windSpeed = 2.f;
546 }
547
548 float dominantAngularVelocity = float(2.0 * glm::pi<float>()) / dominantWavePeriod;
549 float sumS = 0.f;
550
551 glm::vec2 windDir(glm::cos(windDirection), glm::sin(windDirection));
552
553 for (size_t j = 0; j < N; j++) {
554 for (size_t i = 0; i < N; i++) {
555 if ((i == 0) && (j == 0)) {
556 continue;
557 }
558 // [0,N/2] -> [0,N/2]
559 // [N/2+1,N-1] -> [-N/2+1,-1]
560 ivec2 ij(i <= N / 2 ? int(i) : int(i) - int(N),
561 j <= N / 2 ? int(j) : int(j) - int(N));
562
563 const vec2 K = (twoPi / L) * vec2(ij); // K: Wave vector
564 float k = length(K); // k: Wave number
565 float angularVelocity = sqrt(g * k); // omega: Angular velocity
566 //float waveDirection = std::atan2(K.y, K.x); // theta_w: Wave direction
567
568 float cosWindWaveAngle = std::max(-1.f, std::min(1.f, (1.f / k) * dot(K, windDir)));
569 float windWaveAngle = std::acos(cosWindWaveAngle);
570 assert(std::isfinite(windWaveAngle));
571
572 const float S = waveSpectrumPiersonMoskowitz(angularVelocity,
573 dominantAngularVelocity,
574 0.0081f);
575 const float D = dispersionLonguetHiggins(angularVelocity,
576 dominantAngularVelocity,
577 windSpeed,
578 windWaveAngle);
579
580 const float chainFactor = (1.f / (2.f * k)) * sqrt(g / k);
581
582 sumS += chainFactor * S;
583 E[N * j + i] = chainFactor * S * D;
584 }
585 }
586 E[0] = 0.f;
587
588 // Adjust spectrum to match significant wave height
589 float w = 1.f / sumS;// (significantWaveHeight*significantWaveHeight) / (sumS);
590 for (size_t i = 0; i < N * N; i++) {
591 E[i] = w * E[i];
592 }
593
594 }
595
596 void createRandomizedWaveSpectrumInstance(Cogs::Core::ComplexArray& H,
597 const std::vector<float>& E,
598 const unsigned int seed,
599 const size_t N)
600 {
601 std::minstd_rand eng(seed);
602 std::uniform_int_distribution<int> dist(0, RAND_MAX);
603
604 for (size_t j = 0; j < N; j++) {
605 for (size_t i = 0; i < N; i++) {
606#if 0
607 float eta0 = 0.f;
608 float eta1 = 0.f;
609 for (int q = 0; q < 12; q++) {
610 eta0 += float(dist(eng)) / float(RAND_MAX);
611 eta1 += float(dist(eng)) / float(RAND_MAX);
612 }
613 eta0 -= 12.f;
614 eta1 -= 12.f;
615 H.store(N * j + i, sqrt(E[N * j + i] / 2.f) * std::complex<float>(eta0, eta1));
616#else
617
618 // Check the sanity of this trying to comply with normal distribution
619
620 float U0 = float(dist(eng) + 1) / (float(RAND_MAX) + 2);
621 float U1 = (2.f * float(glm::pi<float>())) * (float(dist(eng)) / (float(RAND_MAX) + 1));
622
623 // Box-Muller transform to create normal distributed numbers, mean 0, var 1.
624 complex<float> eta = sqrt(-2.f * log(U0)) * complex<float>(cos(U1), sin(U1));
625
626 H.store(N * j + i, sqrt(E[N * j + i] / 2.f) * eta);
627#endif
628 }
629 }
630 }
631
632 void fastSinCos(float& sin, float& cos, const float x)
633 {
634 float r = (2.0f / glm::pi<float>()) * x;
635
636 int k = int(r);
637 float x_ = r - k;
638 float y_ = 1.f - x_;
639
640 float vx = x_ * x_ * (x_ * (2.f - 33.f / 20.f) + (33.f / 20.f - 3.f)) + 1.f;
641 float vy = y_ * y_ * (y_ * (2.f - 33.f / 20.f) + (33.f / 20.f - 3.f)) + 1.f;
642
643 float c = ((k & 1) == 0) ? vx : vy;
644 float s = ((k & 1) == 0) ? vy : vx;
645
646 cos = (((k + 1) & 2) == 0) ? c : -c;
647 sin = ((k & 2) == 0) ? s : -s;
648 }
649
650 complex<float> fastPolar(float rho, float theta)
651 {
652 float cos, sin;
653
654 fastSinCos(sin, cos, theta);
655
656 return complex<float>(rho * cos, rho * sin);
657 }
658
659 void disperseWaves(Cogs::Core::ComplexArray& dZdu,
665 const float t,
666 const float L,
667 const size_t N,
668 const size_t start,
669 const size_t end)
670 {
671 for (size_t j = start; j < end; j++) {
672 for (size_t i = 0; i < N; i++) {
673 const vec2 K = (twoPi / L) * vec2(i <= N / 2 ? int(i) : int(i) - int(N),
674 j <= N / 2 ? int(j) : int(j) - int(N)); // K: Wave vector
675 float k = length(K); // k: Wave number
676 vec2 kn = (1.f / k) * K;
677 float omega = sqrt(g * k); // omega: Angular velocity
678
679 complex<float> A = a.load(N * j + i) * fastPolar(1.f, omega * t);
680
681 dZdu.store(N * j + i, K.x * A);
682 dZdv.store(N * j + i, K.y * A);
683 Ax.store(N * j + i, kn.x * A);
684 Ay.store(N * j + i, kn.y * A);
685 Az.store(N * j + i, A);
686 }
687 }
688 dZdu.store(0, complex<float>(0.f));
689 dZdv.store(0, complex<float>(0.f));
690 Ax.store(0, complex<float>(0.f));
691 Ay.store(0, complex<float>(0.f));
692 Az.store(0, complex<float>(0.f));
693 }
694
695 void encodeTextureData(vec4* tangents,
696 vec4* real,
697 vec4* imag,
698 const Cogs::Core::ComplexArray& dZdu,
699 const Cogs::Core::ComplexArray& dZdv,
700 const Cogs::Core::ComplexArray& Dx,
701 const Cogs::Core::ComplexArray& Dy,
702 const Cogs::Core::ComplexArray& Dz,
703 const size_t begin,
704 const size_t end)
705 {
706 for (size_t i = begin; i < end; i++) {
707 const size_t ix = i;
708
709 glm::vec3 re = glm::vec3(Dx.load(ix).real(), Dy.load(ix).real(), Dz.load(ix).real());
710 glm::vec3 im = glm::vec3(Dx.load(ix).imag(), Dy.load(ix).imag(), Dz.load(ix).imag());
711 glm::vec4 ta = glm::vec4(dZdu.load(ix).real(), dZdu.load(ix).imag(), dZdv.load(ix).real(), dZdv.load(ix).imag());
712
713 real[ix] = vec4(re, 0.f);
714 imag[ix] = vec4(im, 0.f);
715 tangents[ix] = ta;
716 }
717 }
718
719 void encodeTextureDataTangents(glm::u8vec4* tangents,
720 glm::u8vec4* real,
721 float& magnitude0Out,
722 float& magnitude1Out,
723 const Cogs::Core::ComplexArray& dZdu,
724 const Cogs::Core::ComplexArray& dZdv,
725 const Cogs::Core::ComplexArray& Dx,
726 const Cogs::Core::ComplexArray& Dy,
727 const Cogs::Core::ComplexArray& Dz,
728 const float magnitude0In,
729 const float magnitude1In,
730 const size_t begin,
731 const size_t end)
732 {
733 const float scale0 = 0.5f * 255.f / magnitude0In;
734 const float scale1 = 0.5f * 255.f / magnitude1In;
735
736 float magnitude0 = 0.f;
737 float magnitude1 = 0.f;
738 for (size_t i = begin; i < end; i++) {
739 const size_t ix = i;
740
741 glm::vec3 re = glm::vec3(Dx.load(ix).real(), Dy.load(ix).real(), Dz.load(ix).real());
742 glm::vec3 im = glm::vec3(Dx.load(ix).imag(), Dy.load(ix).imag(), Dz.load(ix).imag());
743 glm::vec4 ta = glm::vec4(dZdu.load(ix).real(), dZdu.load(ix).imag(), dZdv.load(ix).real(), dZdv.load(ix).imag());
744
745 magnitude0 = std::max(std::max(std::max(std::abs(re.x), std::abs(re.y)),
746 std::max(std::abs(re.z), std::abs(im.x))),
747 std::max(std::max(std::abs(im.y), std::abs(im.z)),
748 magnitude0));
749
750 magnitude1 = std::max(std::max(std::max(std::abs(ta.x), std::abs(ta.y)),
751 std::max(std::abs(ta.z), std::abs(ta.w))),
752 magnitude1);
753
754 real[ix] = vec4(glm::clamp(scale0 * (re + glm::vec3(magnitude0In)), glm::vec3(0.f), glm::vec3(255.f)), 0.f);
755 tangents[ix] = glm::clamp(scale1 * (ta + vec4(magnitude1In)), glm::vec4(0.f), glm::vec4(255.f));
756 }
757 magnitude0Out = magnitude0;
758 magnitude1Out = magnitude1;
759 }
760
761
762 void encodeTextureDataQuux(vec4* pos,
763 vec4* nrm,
764 const Cogs::Core::ComplexArray& dZdu,
765 const Cogs::Core::ComplexArray& dZdv,
766 const Cogs::Core::ComplexArray& Dx,
767 const Cogs::Core::ComplexArray& Dy,
768 const Cogs::Core::ComplexArray& Dz,
769 const float L,
770 const float significantWaveHeight,
771 const size_t begin,
772 const size_t end,
773 const size_t m)
774 {
775 const size_t N = (size_t)1 << m;
776 const auto M = N - 1; // assume N is a power of two.
777 const auto s = 1.f / L;
778 const auto l = L / N;
779 const auto h = significantWaveHeight;
780 //const auto worldSpaceMeter = 1.f;
781
782 // Displacement function is
783 //
784 // +- -+
785 // | u + Ix(s*u, s*v) |
786 // P(u,v) = | v + Iy(s*u, s*v) |
787 // | -h * Rz(s*u, s*v) |
788 // +- -+
789 //
790 // where s = 1.f / fftTileExtent and h is significant wave height,
791 // and the two partial derivatives is thus
792 //
793 // +- -+
794 // | 1 + s * dIx(s*u, s*v)/du |
795 // dP(u,v)/du = | s * dIy(s*u, s*v)/du |
796 // | -s * h * dRz(s*u, s*v)/du |
797 // +- -+
798 //
799 // +- -+
800 // | s * dIx(s*u, s*v)/dv |
801 // dP(u,v)/dv = | 1 + s * dIy(s*u, s*v)/dv |
802 // | -s * h * dRz(s*u, s*v)/dv |
803 // +- -+
804 //
805 // We have dRz/du and dRz/dv explicitly. dIx/du|dv and dIy/du|dv is
806 // approximated using forward differences,
807 //
808
809 for (size_t j = begin; j < end; j++) {
810 size_t jp = (j + 1) & M;
811
812 for (size_t i = 0; i < N; i++) {
813 size_t ip = (i + 1) & M;
814
815 float x = Dx.load((j << m) + i).imag();
816 float y = Dy.load((j << m) + i).imag();
817 float z = -Dz.load((j << m) + i).real();
818
819 float dx_du = l * (Dx.load((j << m) + ip).imag() - x);
820 float dy_du = l * (Dy.load((j << m) + ip).imag() - y);
821
822 float dx_dv = l * (Dx.load((jp << m) + i).imag() - x);
823 float dy_dv = l * (Dy.load((jp << m) + i).imag() - y);
824
825 float zdu = dZdu.load((j << m) + i).imag();
826 float zdv = dZdv.load((j << m) + i).imag();
827
828 glm::vec3 u = glm::vec3(s * dx_du + 1.f, s * dy_du + 0.f, h * zdu);
829 glm::vec3 v = glm::vec3(s * dx_dv + 0.f, s * dy_dv + 1.f, h * zdv);
830 glm::vec3 n = glm::normalize(glm::cross(u, v));
831
832 pos[(j << m) + i] = glm::vec4(x, y, z, 0);
833 nrm[(j << m) + i] = glm::vec4(n.x, n.y, n.z, 0);
834 }
835 }
836 }
837
838
839 void encodeTextureDataQuux(glm::u8vec4* pos,
840 glm::u8vec4* nrm,
841 float& magnitude0Out,
842 float& magnitude1Out,
843 const Cogs::Core::ComplexArray& dZdu,
844 const Cogs::Core::ComplexArray& dZdv,
845 const Cogs::Core::ComplexArray& Dx,
846 const Cogs::Core::ComplexArray& Dy,
847 const Cogs::Core::ComplexArray& Dz,
848 const float magnitude0In,
849 const float magnitude1In,
850 const float L,
851 const float significantWaveHeight,
852 const size_t begin,
853 const size_t end,
854 const size_t m)
855 {
856 float magnitude0 = 0.f;
857 float magnitude1 = 0.f;
858 const size_t N = (size_t)1 << m;
859 const auto M = N - 1; // assume N is a power of two.
860 const auto s = 1.f / L;
861 const auto l = L / N;
862 const auto h = significantWaveHeight;
863
864 const float scale0 = 0.5f * 255.f / magnitude0In;
865 const float scale1 = 0.5f * 255.f / magnitude1In;
866 for (size_t j = begin; j < end; j++) {
867 size_t jp = (j + 1) & M;
868
869 for (size_t i = 0; i < N; i++) {
870 size_t ip = (i + 1) & M;
871
872 glm::vec3 p = glm::vec3(Dx.load((j << m) + i).imag(),
873 Dy.load((j << m) + i).imag(),
874 -Dz.load((j << m) + i).real());
875
876 float dx_du = l * (Dx.load((j << m) + ip).imag() - p.x);
877 float dy_du = l * (Dy.load((j << m) + ip).imag() - p.y);
878
879 float dx_dv = l * (Dx.load((jp << m) + i).imag() - p.x);
880 float dy_dv = l * (Dy.load((jp << m) + i).imag() - p.y);
881
882 float zdu = dZdu.load((j << m) + i).imag();
883 float zdv = dZdv.load((j << m) + i).imag();
884
885 glm::vec3 u = glm::vec3(s * dx_du + 1.f, s * dy_du + 0.f, h * zdu);
886 glm::vec3 v = glm::vec3(s * dx_dv + 0.f, s * dy_dv + 1.f, h * zdv);
887 glm::vec3 n = glm::normalize(glm::cross(u, v));
888
889 magnitude0 = std::max(std::max(std::abs(p.x), std::abs(p.y)),
890 std::max(std::abs(p.z), magnitude0));
891
892 // Note: z-component of normal is usually quite close to 1,
893 // so we encode difference from 1 instead to make range match n.x and n.y.
894 magnitude1 = std::max(std::max(std::abs(n.x), std::abs(n.y)),
895 std::max(std::abs(n.z - 1.f), magnitude1));
896
897 pos[(j << m) + i] = glm::vec4(glm::clamp(scale0 * (p + glm::vec3(magnitude0In)), glm::vec3(0.f), glm::vec3(255.f)), 0);
898 nrm[(j << m) + i] = glm::vec4(glm::clamp(scale1 * glm::vec3(n.x + magnitude1In,
899 n.y + magnitude1In,
900 n.z - 1.f), glm::vec3(0.f), glm::vec3(255.f)), 0);
901 }
902 }
903 magnitude0Out = magnitude0;
904 magnitude1Out = magnitude1;
905
906 }
907}
908
909using glm::normalize;
910using glm::clamp;
911using glm::dvec2;
912
914{
916
917 DisplacementTexH = context->textureManager->create();
918 NormalTexH = context->textureManager->create();
919 TangentsTexH = context->textureManager->create();
920
921 DisplacementTexH->setName("BasicOcean.Displacement");
922 NormalTexH->setName("BasicOcean.Normals");
923 TangentsTexH->setName("BasicOcean.Tangents");
924
925 oceanTaskGroup = context->taskManager->createGroup(TaskManager::GlobalQueue);
926
927 setupMaterial();
928 setupWaveSpectrum();
929
930 Variable* animateVar = context->variables->get(animateKey);
931 if (animateVar->isEmpty()) {
932 animateVar->setBool(true);
933 }
934 Variable* eightbitVar = context->variables->get(eightbitKey);
935 if (eightbitVar->isEmpty()) {
936 eightbitVar->setBool(true);
937 }
938}
939
941{
942 if (oceanTaskGroup.isValid()) {
943 context->taskManager->destroy(oceanTaskGroup);
944 }
945 DisplacementTexH = TextureHandle();
946 NormalTexH = TextureHandle();
947 TangentsTexH = TextureHandle();
948 ReflectionTextureH = TextureHandle();
949
950 oceanMaterial = MaterialHandle();
951 oceanMaterial2 = MaterialHandle();
952}
953
954void Cogs::Core::BasicOceanSystem::setupMaterial()
955{
956 auto adaptiveMat = context->materialManager->loadMaterial("AdaptiveGridMaterial.material");
957 oceanMaterial = context->materialManager->loadMaterial("BasicOceanMaterial.material");
958 oceanMaterial2 = context->materialManager->loadMaterial("BasicSkyMaterial.material");
959
960
961 //NOTE: This should all be moved to after loading has been performed.
962 context->materialManager->processLoading();
963
964 oceanMaterial->options.cullMode = CullMode::None;
965
966 auto m = oceanMaterial.resolve();
967
968 DisplacementKey = m->getTextureKey("Displacement");
969 NormalKey = m->getTextureKey("Difference");
970
971 TangentsKey = m->getTextureKey("Tangents");
972
973 m->setTextureProperty(DisplacementKey, DisplacementTexH);
974 m->setTextureProperty(NormalKey, NormalTexH);
975 m->setTextureProperty(TangentsKey, TangentsTexH);
976
977 ReflectionTextureH = context->textureManager->create();
978 ReflectionTextureH->setName("BasicOcean.Reflection");
979
980 PlanarReflectionKey = m->getTextureKey("PlanarReflection");
981 m->setTextureProperty(PlanarReflectionKey, ReflectionTextureH);
982 m->setTextureAddressMode(PlanarReflectionKey, SamplerState::Clamp);
983
984 cameraYAxisKey = m->getVec4Key("cameraYAxis");
985 waterColorKey = m->getVec4Key("waterColor");
986 waveDirectionKey = m->getVec2Key("waveDirection");
987 camPlaneDirKey = m->getVec2Key("camPlaneDir");
988 significantWaveHeightKey = m->getFloatKey("significantWaveHeight");
989 fftTileScaleKey = m->getFloatKey("fftTileScale");
990 camAzimuthKey = m->getFloatKey("camAzimuth");
991 seaLevelKey = m->getFloatKey("seaLevel");
992 reflectionBrightnessKey = m->getFloatKey("reflectionBrightness");
993 phaseShiftNoiseFrequencyKey = m->getFloatKey("phaseShiftNoiseFrequency");
994 phaseShiftNoisePeriodKey = m->getFloatKey("phaseShiftNoisePeriod");
995}
996
997void Cogs::Core::BasicOceanSystem::updateTextureResolution(BasicOceanComponent* oceanComp)
998{
999 const auto textureResolution = static_cast<uint32_t>(std::max(1,context->variables->get("ocean.reflectionTextureResolution", 1024)));
1000 auto reflectionTex = context->textureManager->get(ReflectionTextureH);
1001
1002 TextureFormat format = TextureFormat::R8G8B8A8_UNORM_SRGB;
1003 if (oceanComp != nullptr) {
1004 format = parseTextureFormat(oceanComp->reflectionTexFormat, format);
1005 }
1006
1007 if ((reflectionTex->description.width != textureResolution)
1008 || (reflectionTex->description.format != format))
1009 {
1010 reflectionTex->description.width = textureResolution;
1011 reflectionTex->description.height = textureResolution;
1012 reflectionTex->description.format = format;
1013 reflectionTex->description.flags = TextureFlags::RenderTarget;
1014 reflectionTex->setChanged();
1015 }
1016}
1017
1018void Cogs::Core::BasicOceanSystem::setupWaveSpectrum()
1019{
1020 size_t N = size_t(1) << fftTileResolutionLog2;
1021 size_t N_times_N = N*N;
1022
1023 constexpr float oneOvertwoPi = 1.f / (2.0f * glm::pi<float>());
1024 const float dominantWaveLength = oneOvertwoPi*(g*dominantWavePeriod*dominantWavePeriod);
1025
1026 fftTileExtent = dominantWaveLength;
1027
1028 P.resize(N_times_N);
1029 frqH0.resize(N_times_N);
1030 frqDx.resize(N_times_N);
1031 frqDy.resize(N_times_N);
1032 frqDz.resize(N_times_N);
1033 frqdDzdu.resize(N_times_N);
1034 frqdDzdv.resize(N_times_N);
1035
1036 spcDx.resize(N_times_N);
1037 spcDy.resize(N_times_N);
1038 spcDz.resize(N_times_N);
1039 spcdDzdu.resize(N_times_N);
1040 spcdDzdv.resize(N_times_N);
1041
1042 fftScratch.resize(fastGenericFourierTransform2DScratchsize(N_times_N));
1043
1044 const float omega_p = (0.855f*g) / windSpeed;
1045
1046 createDirectionalWaveSpectrum(P, N, fftTileExtent, omega_p, windSpeed, 0.f /*windDirection*/, 1.f, dominantWavePeriod);
1047 createRandomizedWaveSpectrumInstance(frqH0, P, 42, N);
1048}
1049
1050void Cogs::Core::BasicOceanSystem::updateTextures(const float magnitudeIn0, const float magnitudeIn1) // Max magnitude of data kind 0 found while encoding
1051{
1052 CpuInstrumentationScope(SCOPE_SYSTEMS, "OceanSystem::updateTextures");
1053
1054 const uint16_t N = uint16_t(1) << fftTileResolutionLog2;
1055
1056 auto realTex = context->textureManager->get(DisplacementTexH);
1057 auto imagTex = context->textureManager->get(NormalTexH);
1058 auto tangentsTex = context->textureManager->get(TangentsTexH);
1059
1060 size_t M = 0;
1061 switch (waves)
1062 {
1063 case BasicOceanWaves::Default: M = static_cast<size_t>(N) * N; break;
1064 case BasicOceanWaves::Quux: M = static_cast<size_t>(N); break;
1065 default: assert(false && "Illegal wave type"); break;
1066 }
1067
1068 size_t split = std::min(size_t(8), 1 + (context->engine->workParallel() ? context->taskManager->getQueueConcurrency(TaskManager::GlobalQueue) : size_t(0)));
1069 size_t incr = (M + split - 1) / split;
1070
1071 TaskId gr = 1 < split ? context->taskManager->createGroup(TaskManager::GlobalQueue) : NoTask;
1072
1073 std::vector<float> magnitudes_scratch0(split, 0.f);
1074 std::vector<float> magnitudes_scratch1(split, 0.f);
1075 if (eightBit) {
1076 MappedTexture<glm::u8vec4> real = realTex->map<glm::u8vec4>(N, N, false);
1077 switch (waves)
1078 {
1079 case BasicOceanWaves::Default:
1080 {
1081 MappedTexture<glm::u8vec4> tangents = tangentsTex->map<glm::u8vec4>(N, N, true);
1082 for (size_t i = 0; i < split; ++i) {
1083 TaskFunction f = [this, i, magnitudeIn0, magnitudeIn1, incr, M, &tangents, &real, &magnitudes_scratch0, &magnitudes_scratch1]() {
1084 CpuInstrumentationScope(SCOPE_SYSTEMS, "OceanSystem::encodeTextureData");
1085 encodeTextureDataTangents(tangents.data(), real.data(), magnitudes_scratch0[i], magnitudes_scratch1[i],
1086 spcdDzdu, spcdDzdv, spcDx, spcDy, spcDz,
1087 magnitudeIn0, magnitudeIn1,
1088 i * incr, std::min(M, (i + 1) * incr));
1089 };
1090 if (1 < split) { context->taskManager->enqueueChild(gr, f); } else { f(); }
1091 }
1092
1093 if (gr.isValid()) {
1094 context->taskManager->destroy(gr);
1095 gr = NoTask;
1096 }
1097 break;
1098 }
1099 case BasicOceanWaves::Quux:
1100 MappedTexture<glm::u8vec4> imag = imagTex->map<glm::u8vec4>(N, N, true);
1101 for (size_t i = 0; i < split; ++i) {
1102 TaskFunction f = [this, i, magnitudeIn0, magnitudeIn1, incr, M, &real, &imag, &magnitudes_scratch0, &magnitudes_scratch1]() {
1103 CpuInstrumentationScope(SCOPE_SYSTEMS, "OceanSystem::encodeTextureData");
1104 encodeTextureDataQuux(real.data(), imag.data(), magnitudes_scratch0[i], magnitudes_scratch1[i],
1105 spcdDzdu, spcdDzdv, spcDx, spcDy, spcDz,
1106 magnitudeIn0, magnitudeIn1,
1107 fftTileExtent, significantWaveHeight,
1108 i * incr, std::min(M, (i + 1) * incr),
1109 fftTileResolutionLog2);
1110 };
1111 if (1 < split) { context->taskManager->enqueueChild(gr, f); } else { f(); }
1112 }
1113
1114 if (gr.isValid()) {
1115 context->taskManager->destroy(gr);
1116 gr = NoTask;
1117 }
1118 break;
1119 }
1120 }
1121 else {
1122 MappedTexture<glm::vec4> real = realTex->map<glm::vec4>(N, N, true);
1123 MappedTexture<glm::vec4> imag = imagTex->map<glm::vec4>(N, N, true);
1124 switch (waves)
1125 {
1126 case BasicOceanWaves::Default:
1127 {
1128 MappedTexture<glm::vec4> tangents = tangentsTex->map<glm::vec4>(N, N, true);
1129 for (size_t i = 0; i < split; ++i) {
1130 TaskFunction f = [this, i, incr, M, &tangents, &real, &imag]() {
1131 CpuInstrumentationScope(SCOPE_SYSTEMS, "OceanSystem::encodeTextureData");
1132 encodeTextureData(tangents.data(), real.data(), imag.data(),
1133 spcdDzdu, spcdDzdv, spcDx, spcDy, spcDz,
1134 i * incr, std::min(M, (i + 1) * incr));
1135 };
1136 if (1 < split) { context->taskManager->enqueueChild(gr, f); }
1137 else { f(); }
1138 }
1139
1140 if (gr.isValid()) {
1141 context->taskManager->destroy(gr);
1142 gr = NoTask;
1143 }
1144 break;
1145 }
1146 case BasicOceanWaves::Quux:
1147 for (size_t i = 0; i < split; ++i) {
1148 TaskFunction f = [this, i, incr, M, &real, &imag]() {
1149 CpuInstrumentationScope(SCOPE_SYSTEMS, "OceanSystem::encodeTextureData");
1150 encodeTextureDataQuux(real.data(), imag.data(),
1151 spcdDzdu, spcdDzdv, spcDx, spcDy, spcDz,
1152 fftTileExtent, significantWaveHeight,
1153 i * incr, std::min(M, (i + 1) * incr),
1154 fftTileResolutionLog2);
1155 };
1156 if (1 < split) { context->taskManager->enqueueChild(gr, f); }
1157 else { f(); }
1158 }
1159
1160 if (gr.isValid()) {
1161 context->taskManager->destroy(gr);
1162 gr = NoTask;
1163 }
1164 break;
1165 }
1166 }
1167 if (gr.isValid()) context->taskManager->destroy(gr);
1168
1169 if (eightBit) {
1170 magnitudes_tmp.channel0 = *std::max_element(magnitudes_scratch0.begin(), magnitudes_scratch0.end());
1171 magnitudes_tmp.channel1 = *std::max_element(magnitudes_scratch1.begin(), magnitudes_scratch1.end());
1172 }
1173}
1174
1175void Cogs::Core::BasicOceanSystem::updateTileMaterialInstances(const BasicOceanData & oceanData,
1176 AdaptivePlanarGridComponent * /*gridComp*/,
1177 AdaptivePlanarGridData & gridData,
1178 const glm::mat4 & viewToWorld,
1179 const glm::vec2 & viewPortSize)
1180{
1181 const glm::vec2 camPlaneDir = glm::normalize(glm::vec2(viewToWorld[2]));
1182 float camAzimuth = (0.5f / glm::pi<float>()) * acos(camPlaneDir.x);
1183
1184 if (camPlaneDir.y > 0.f) {
1185 camAzimuth = -camAzimuth;
1186 }
1187
1188 camAzimuth = camAzimuth + 0.75f;
1189
1190 const vec4 camYAxis = vec4(normalize(vec3(viewToWorld[1])), 0.5f * viewPortSize.y);
1191 const vec2 waveDirection(cos(windDirection), sin(windDirection));
1192 const vec4 rgba(vec3(oceanData.color), clamp(1.f - oceanData.transparency, 0.f, 1.f));
1193
1194 auto m = oceanMaterial.resolve();
1195 m->setVec4Property(cameraYAxisKey, camYAxis);
1196 m->setVec4Property(waterColorKey, rgba);
1197 m->setVec2Property(waveDirectionKey, vec2(waveDirection));
1198 m->setVec2Property(camPlaneDirKey, camPlaneDir);
1199 m->setFloatProperty(significantWaveHeightKey, significantWaveHeight);
1200 m->setFloatProperty(fftTileScaleKey, 1.f / fftTileExtent);
1201 m->setFloatProperty(camAzimuthKey, camAzimuth);
1202 m->setFloatProperty(seaLevelKey, oceanData.seaLevel);
1203 m->setFloatProperty(reflectionBrightnessKey, oceanData.reflectionBrightness);
1204 m->setFloatProperty(phaseShiftNoiseFrequencyKey, float(phaseShiftNoiseFrequency));
1205 m->setFloatProperty(phaseShiftNoisePeriodKey, float(phaseShiftNoiseFrequency*tilePeriod));
1206
1207 m->setVec2Property(m->getVec2Key("viewportScale"), 2.f*viewPortSize);
1208 m->setVec2Property(m->getVec2Key("valueScale"), 2.f * glm::vec2(magnitudes.channel0, magnitudes.channel1));
1209
1210
1211 for (auto & tile : gridData.tiles) {
1212 if (oceanData.transparent) {
1213 tile.materialInstance->setTransparent();
1214 }
1215 else {
1216 tile.materialInstance->setOpaque();
1217 }
1218 }
1219}
1220
1222{
1223 if (!pool.size()) return;
1224
1225 updateTextureResolution(&(*pool.begin()));
1226
1227 bool encodingChanged = false;
1228 {
1229 // Use 8-bit encoding if float is not supported or it is requested
1230 bool eightBit = ((context->device->getCapabilities()->getDeviceCapabilities().FloatTextures == false) ||
1231 (context->variables->get("basic-ocean.8bit", true)));
1232 encodingChanged = this->eightBit != eightBit;
1233 this->eightBit = eightBit;
1234 }
1235
1236 bool visibleInstances = false;
1237 for (auto & oceanComp : pool) {
1238 auto & oceanData = getData(&oceanComp);
1239 bool updateCamera = false;
1240 if (oceanComp.reflectionCamera) {
1241 if (oceanData.defaultReflectionCamera) {
1242 context->store->destroyEntity(oceanData.defaultReflectionCamera->getId());
1243 oceanData.defaultReflectionCamera.reset();
1244 }
1245 if (oceanData.reflectionCamera != oceanComp.reflectionCamera) {
1246 oceanData.reflectionCamera = oceanComp.reflectionCamera;
1247 updateCamera = true;
1248 }
1249 }
1250 else {
1251 if (!oceanData.defaultReflectionCamera) {
1252 auto camera = context->cameraSystem->getMainCamera();
1253 oceanData.defaultReflectionCamera = context->store->createChildEntity("ReflectionCamera", camera->getContainer());
1254 oceanData.reflectionCamera = oceanData.defaultReflectionCamera;
1255 updateCamera = true;
1256 }
1257 }
1258
1259 auto * gridComp = oceanComp.getComponent<AdaptivePlanarGridComponent>();
1260 auto * sceneComp = oceanComp.getComponent<SceneComponent>();
1261
1262 visibleInstances = visibleInstances || sceneComp->visible;
1263
1264 if (!oceanData.initialized) {
1265 context->adaptivePlanarGridSystem->registerMaterial(gridComp, oceanMaterial, initMaterialInstanceCallback, &oceanData);
1266 gridComp->layer = RenderLayers::Ocean;
1267 gridComp->setChanged();
1268 oceanData.initialized = true;
1269 }
1270
1271 if (updateCamera) {
1272 auto reflectionComponent = oceanData.reflectionCamera->getComponent<ReflectionComponent>();
1273 reflectionComponent->texture = ReflectionTextureH;
1274 sceneComp->setChanged(); // Trigger EnableRender code below.
1275 }
1276
1277 auto reflectionTex = context->textureManager->get(ReflectionTextureH);
1278
1279 auto cameraComponent = oceanData.reflectionCamera->getComponent<CameraComponent>();
1280 cameraComponent->viewportSize = glm::vec2(reflectionTex->description.width, reflectionTex->description.height);
1281
1282 if (sceneComp->hasChanged() || oceanComp.hasChanged()) {
1283 auto * refCamComp = oceanData.reflectionCamera->getComponent<CameraComponent>();
1284 if (sceneComp->visible) {
1285 switch (oceanComp.reflection) {
1286 case BasicOceanReflection::Planar:
1287 refCamComp->flags |= CameraFlags::EnableRender;
1288 break;
1289 case BasicOceanReflection::EnvSkyBox:
1290 case BasicOceanReflection::EnvRadiance:
1291 refCamComp->flags &= ~CameraFlags::EnableRender;
1292 break;
1293 default:
1294 assert(false);
1295 }
1296 }
1297 else {
1298 refCamComp->flags &= ~CameraFlags::EnableRender;
1299 }
1300 }
1301
1302 if (oceanComp.hasChanged() || encodingChanged) {
1303 specsChanged = true;
1304
1305 const char * waveVariant = nullptr;
1306 switch (oceanComp.waves)
1307 {
1308 case BasicOceanWaves::Default: waveVariant = "Basic"; break;
1309 case BasicOceanWaves::Quux: waveVariant = "Quux"; break;
1310 default: assert(false);
1311 }
1312
1313 const char* beliefSystem = nullptr;
1314 switch (oceanComp.beliefSystem) {
1315 case BasicOceanBeliefSystem::FlatEarth: beliefSystem = "FlatEarth"; break;
1316 case BasicOceanBeliefSystem::CurvedEarth: beliefSystem = "CurvedEarth"; break;
1317 default: assert(false);
1318 }
1319
1320 const char * reflectionVariant = nullptr;
1321 switch (oceanComp.reflection) {
1322 case BasicOceanReflection::Planar: reflectionVariant = "Planar"; break;
1323 case BasicOceanReflection::EnvSkyBox: reflectionVariant = "EnvSkyBox"; break;
1324 case BasicOceanReflection::EnvRadiance: reflectionVariant = "EnvRadiance"; break;
1325 default: assert(false);
1326 }
1327
1328 const char* lightModelVariant = nullptr;
1329 lightModelVariant = oceanComp.pbr ? "PBR" : "Phong";
1330
1331 const char* encodingVariant = eightBit ? "Scaled" : "AsIs";
1332
1333 if ((oceanData.encoding != encodingVariant) ||
1334 (oceanData.waveVariant != waveVariant) ||
1335 (oceanData.reflectionVariant != reflectionVariant) ||
1336 (oceanData.lightModelVariant != lightModelVariant) ||
1337 (oceanData.beliefSystem != beliefSystem))
1338 {
1339 oceanData.encoding = encodingVariant;
1340 oceanData.waveVariant = waveVariant;
1341 oceanData.beliefSystem = beliefSystem;
1342 oceanData.reflectionVariant = reflectionVariant;
1343 oceanData.lightModelVariant = lightModelVariant;
1344
1345 for (auto & instance : context->adaptivePlanarGridSystem->getData(gridComp).materialPool) {
1346 instance->setVariant("Encoding", encodingVariant);
1347 instance->setVariant("Waves", waveVariant);
1348 instance->setVariant("BeliefSystem", beliefSystem);
1349 instance->setVariant("Reflection", reflectionVariant);
1350 instance->setVariant("LightModel", lightModelVariant);
1351 }
1352 }
1353
1354
1355 oceanData.transparency = oceanComp.transparency;
1356 oceanData.color = oceanComp.color;
1357 oceanData.transparent = oceanComp.transparency > 0.0f;
1358 oceanData.seaLevel = oceanComp.seaLevel;
1359 oceanData.reflectionBrightness = oceanComp.reflectionBrightness;
1360
1361 fftTileResolutionLog2 = std::max(1, oceanComp.fftTileResolutionLog2);
1362 significantWaveHeight = oceanComp.significantWaveHeight;
1363 dominantWavePeriod = oceanComp.dominantWavePeriod;
1364 windSpeed = oceanComp.windSpeed;
1365 windDirection = oceanComp.windDirection;
1366 waves = oceanComp.waves;
1367
1368 auto adjustedDisplacement = std::max(significantWaveHeight, 0.01f);
1369 gridComp->displaceMin = glm::vec3(-0.75f * adjustedDisplacement) + glm::vec3(0.f, 0.f, oceanComp.seaLevel);
1370 gridComp->displaceMax = glm::vec3(0.75f * adjustedDisplacement) + glm::vec3(0.f, 0.f, oceanComp.seaLevel);
1371 gridComp->setChanged();
1372 }
1373 }
1374
1375 if (specsChanged) {
1376 setupWaveSpectrum();
1377
1378 for (auto & oceanComp : pool) {
1379 auto gridComp = oceanComp.getComponent<AdaptivePlanarGridComponent>();
1380
1381 auto c = glm::cos(windDirection);
1382 auto s = glm::sin(windDirection);
1383
1384 context->adaptivePlanarGridSystem->setTexCoordTransform(gridComp, glm::mat2(c, s, -s, c), glm::vec2(fftTileExtent*tilePeriod));
1385 }
1386 }
1387
1388 if (!visibleInstances) return; // No visible instances, no point in updating shared data
1389
1390 // We want to get one extra frame when we switch from animate == true to false
1391 // to set up the waves at a fixed time.
1392 bool doAnimate = context->variables->get("basic-ocean.animate", true);
1393 if (!doAnimate && !specsChanged) {
1394 if (animate) {
1395 specsChanged = true;
1396 animate = false;
1397 }
1398 else {
1399 return;
1400 }
1401 }
1402 else {
1403 animate = true;
1404 }
1405
1406 context->engine->setDirty(); // Trigger new frame as ocean is animated
1407
1408
1409 const int N = 1 << fftTileResolutionLog2;
1410
1411 const bool workParallel = context->engine->workParallel();
1412
1413 extraStep = eightBit && ((doAnimate == false) && (this->animate || specsChanged));
1414
1415 //TODO: Remove force-true.
1416 const float time = doAnimate ? static_cast<float>(context->time->getAnimationTime()) : 7.f;
1417 if (animate || specsChanged) {
1418 if (workParallel || true) {
1419 const size_t numTasks = 1 + context->taskManager->getQueueConcurrency(TaskManager::GlobalQueue);
1420 const size_t subRange = N / numTasks;
1421
1422 auto gr = context->taskManager->createGroup(TaskManager::GlobalQueue);
1423
1424 for (size_t i = 0; i < numTasks; ++i) {
1425 context->taskManager->enqueueChild(gr, [&, i, time, N, subRange]()
1426 {
1427 //CpuInstrumentationScope("Systems", "OceanSystem::disperseWaves");
1428
1429 disperseWaves(frqdDzdu, frqdDzdv, frqDx, frqDy, frqDz, frqH0, time, fftTileExtent, N, i * subRange, (i + 1) * subRange);
1430 });
1431 }
1432
1433 context->taskManager->enqueueChild(oceanTaskGroup, [this, context, gr]()
1434 {
1435 context->taskManager->wait(gr);
1436
1437 context->taskManager->enqueueChild(gr, [&, context]() { fastInverseFourierTransform2D(context, fftScratch1, spcdDzdu, frqdDzdu, 1.f, fftTileResolutionLog2); });
1438 context->taskManager->enqueueChild(gr, [&, context]() { fastInverseFourierTransform2D(context, fftScratch2, spcdDzdv, frqdDzdv, 1.f, fftTileResolutionLog2); });
1439 context->taskManager->enqueueChild(gr, [&, context]() { fastInverseFourierTransform2D(context, fftScratch3, spcDx, frqDx, 1.f, fftTileResolutionLog2); });
1440 context->taskManager->enqueueChild(gr, [&, context]() { fastInverseFourierTransform2D(context, fftScratch4, spcDy, frqDy, 1.f, fftTileResolutionLog2); });
1441 context->taskManager->enqueueChild(gr, [&, context]() { fastInverseFourierTransform2D(context, fftScratch5, spcDz, frqDz, 1.f, fftTileResolutionLog2); });
1442
1443 context->taskManager->destroy(gr);
1444
1445 updateTextures(magnitudes.channel0, magnitudes.channel1);
1446 if (this->extraStep) {
1447 LOG_DEBUG(logger, "Extra texture encoder run to get magnitudes right");
1448 updateTextures(magnitudes_tmp.channel0, magnitudes_tmp.channel1);
1449 }
1450 });
1451
1452 }
1453 else {
1454 disperseWaves(frqdDzdu, frqdDzdv, frqDx, frqDy, frqDz, frqH0, time, fftTileExtent, N, 0, N);
1455 fastInverseFourierTransform2D(context, fftScratch, spcdDzdu, frqdDzdu, 1.f, fftTileResolutionLog2);
1456 fastInverseFourierTransform2D(context, fftScratch, spcdDzdv, frqdDzdv, 1.f, fftTileResolutionLog2);
1457 fastInverseFourierTransform2D(context, fftScratch, spcDx, frqDx, 1.f, fftTileResolutionLog2);
1458 fastInverseFourierTransform2D(context, fftScratch, spcDy, frqDy, 1.f, fftTileResolutionLog2);
1459 fastInverseFourierTransform2D(context, fftScratch, spcDz, frqDz, 1.f, fftTileResolutionLog2);
1460 }
1461 }
1462
1463}
1464
1466{
1468
1469 if (!pool.size()) return;
1470
1471 // Update materials after new material instances has been instantiated.
1472 for (auto & oceanComp : pool) {
1473 auto & oceanData = getData(&oceanComp);
1474 auto gridComp = oceanComp.getComponent<AdaptivePlanarGridComponent>();
1475 auto & gridData = context->adaptivePlanarGridSystem->getData(gridComp);
1476
1477 const TransformComponent* lodRefComp = nullptr;
1478 if (auto e = gridComp->lodReference.lock(); e) {
1479 lodRefComp = e->getComponent<TransformComponent>();
1480 }
1481 if (!lodRefComp) {
1482 lodRefComp = context->cameraSystem->getMainCamera()->getComponent<TransformComponent>();
1483 }
1484
1485 bool any = false;
1486 glm::vec2 viewportSize;
1487 for (auto & weak : gridComp->cameras) {
1488 if (auto entity = weak.lock(); entity) {
1489 if (auto * comp = entity->getComponent<CameraComponent>(); comp) {
1490 auto & camData = context->cameraSystem->getData(comp);
1491 viewportSize = glm::max(viewportSize, camData.viewportSize);
1492 any = true;
1493 }
1494 }
1495 }
1496 if (!any) {
1497 viewportSize = context->cameraSystem->getMainCameraData().viewportSize;
1498 }
1499
1500 updateTileMaterialInstances(oceanData,
1501 gridComp,
1502 gridData,
1503 context->transformSystem->getLocalToWorld(lodRefComp),
1504 viewportSize);
1505 }
1506
1507 if (animate || specsChanged) {
1508 context->taskManager->wait(oceanTaskGroup);
1509 // Update
1510 if (eightBit) {
1511 if (!std::isfinite(magnitudes.channel0) || !std::isfinite(magnitudes.channel1) || specsChanged || this->extraStep) {
1512 magnitudes.channel0 = magnitudes_tmp.channel0;
1513 magnitudes.channel1 = magnitudes_tmp.channel1;
1514 }
1515 else {
1516 magnitudes.channel0 = 0.9f * magnitudes.channel0 + 0.1f * magnitudes_tmp.channel0;
1517 magnitudes.channel1 = 0.9f * magnitudes.channel1 + 0.1f * magnitudes_tmp.channel1;
1518 }
1519 }
1520 }
1521 specsChanged = false;
1522
1523}
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
Definition: Component.h:202
ComponentType * getComponent() const
Definition: Component.h:159
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
float reflectionBrightness
Multiplicative factor reflection.
float seaLevel
Vertical displacement of average sea height.
float transparency
Transparency of water.
glm::vec4 color
Color of water. Alpha taken from transparency component.
void initialize(Context *context) override
Initialize the system.
void cleanup(Context *context) override
Provided for custom cleanup logic in derived systems.
CameraFlags flags
Camera behavior flags.
glm::vec2 viewportSize
Size of the viewport covered by this instance, given in pixels.
Context * context
Pointer to the Context instance the system lives in.
void postUpdate()
Perform post update logic in the system.
virtual void initialize(Context *context)
Initialize the system.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
class EntityStore * store
Entity store.
Definition: Context.h:231
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
std::unique_ptr< class Time > time
Time service instance.
Definition: Context.h:198
std::unique_ptr< class Engine > engine
Engine instance.
Definition: Context.h:222
void destroyEntity(const EntityId id)
Destroy the entity with the given id.
EntityPtr createChildEntity(const StringView &type, ComponentModel::Entity *parent, const StringView &name=StringView())
Create a new Entity, parenting it to the given parent.
Wrapper for mapped texture data, ensuring RAII behavior of stream map/unmap operations.
Definition: Texture.h:34
Contains information on how the entity behaves in the scene.
static constexpr TaskQueueId GlobalQueue
Global task queue.
Definition: TaskManager.h:224
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
std::function< void()> TaskFunction
Type of task function used by the task manager.
Definition: TaskManager.h:38
@ EnableRender
Renderable.
@ None
No primitive culling performed.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
void COGSFOUNDATION_API log(const char *message, const char *source, const Category category, uint32_t errorNumber)
Logs the given message with source and category.
Definition: LogManager.cpp:306
Material instances represent a specialized Material combined with state for all its buffers and prope...
void setName(const StringView &name)
Set the user friendly name of the resource.
Definition: ResourceBase.h:298
Task id struct used to identify unique Task instances.
Definition: TaskManager.h:20
bool isValid() const
Check if the task id is valid.
Definition: TaskManager.h:29
Runtime control variable.
Definition: Variables.h:27
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
Definition: SamplerState.h:17
@ RenderTarget
The texture can be used as a render target and drawn into.
Definition: Flags.h:120