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