1#include "Rendering/IGraphicsDevice.h"
2#include "Rendering/ICapabilities.h"
3#include "Foundation/Logging/Logger.h"
5#include "BasicOceanSystem.h"
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"
13#include "Systems/Core/TransformSystem.h"
14#include "Systems/Geometry/AdaptivePlanarGridSystem.h"
16#include "Resources/MaterialManager.h"
17#include "Resources/TextureManager.h"
18#include "Resources/Texture.h"
20#include "Services/Variables.h"
21#include "Services/Time.h"
22#include "Services/TaskManager.h"
24#include "EntityStore.h"
26#include "Utilities/Parsing.h"
30#define _USE_MATH_DEFINES
38#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
46 const std::string animateKey =
"basic-ocean.animate";
47 const std::string eightbitKey =
"basic-ocean.8bit";
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);
63 inline size_t reverseBits16(
size_t i,
size_t log2N)
68 t = ((0xAAAAu & t) >> 1) | ((t << 1) & 0xAAAAu);
69 t = ((0xCCCCu & t) >> 2) | ((t << 2) & 0xCCCCu);
70 t = ((0xF0F0u & t) >> 4) | ((t << 4) & 0xF0F0u);
71 t = ((0xFF00u & t) >> 8) | ((t << 8) & 0xFF00u);
72 return t >> (16 - log2N);
76 const float twoPi = float(2.0 * M_PI);
78 inline complex<float> twiddleFactor(
const int n,
const int N)
80 const float arg = (-twoPi * float(n)) /
float(N);
81 return complex<float>(cos(arg), sin(arg));
86#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
87 void inverseRadix2MajorIndexTransposeAlignedSoALog4SSE1(
float* dstReal,
95 size_t N = 1 << log2N;
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));
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));
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));
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));
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);
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);
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;
151 int twiddleIndex = int(blockIndex << (log2N - l - 1));
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);
159 __m128 aImag = _mm_load_ps(imag0);
160 __m128 bImag = _mm_load_ps(imag1);
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));
165 _mm_store_ps(real0, _mm_add_ps(aReal, cReal)); real0 += 4;
166 _mm_store_ps(imag0, _mm_add_ps(aImag, cImag)); imag0 += 4;
168 _mm_store_ps(real1, _mm_sub_ps(aReal, cReal)); real1 += 4;
169 _mm_store_ps(imag1, _mm_sub_ps(aImag, cImag)); imag1 += 4;
176 void inverseRadix2MajorIndexTranspose(
float* dstReal,
178 const size_t dstStride,
179 const float* srcReal,
180 const float* srcImag,
181 const size_t srcStride,
186 size_t N = (size_t)1 << log2N;
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();
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));
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();
239 const size_t dstStride,
240 const uint8_t* srcReal,
241 const uint8_t* srcImag,
242 const size_t srcStride,
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);
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);
263#if !defined(EMSCRIPTEN) && !defined(__APPLE__)
264 size_t N = 1 << log2N;
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))
274 inverseRadix2MajorIndexTransposeAlignedSoALog4SSE1(scratchf, scratchf + N * N,
276 factor, scale, log2N);
278 inverseRadix2MajorIndexTransposeAlignedSoALog4SSE1(dstRealf, dstImagf,
279 scratchf, scratchf + N * N,
285 inverseRadix2MajorIndexTranspose(scratchf, scratchf + 1, 2,
286 srcRealf, srcImagf, srcStridef,
287 factor, scale, log2N);
289 inverseRadix2MajorIndexTranspose(dstRealf, dstImagf, dstStridef,
290 scratchf, scratchf + 1, 2,
304 inline size_t fastGenericFourierTransform2DScratchsize(
size_t log2N) {
return sizeof(float) * (2 << (log2N << 1)) + 64; }
308 std::vector<uint8_t>& scratch,
314 CpuInstrumentationScope(SCOPE_GEOMETRY,
"OceanSystem::fastInverseFourierTransform2D");
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()),
322 reinterpret_cast<const uint8_t*
>(src.real()),
323 reinterpret_cast<const uint8_t*
>(src.imag()),
325 float(2.0 * 3.14159265358979323846),
465 const float pi = std::acos(-1.f);
467 const float twoPi = float(2.0 * glm::pi<float>());
469 const float g = 9.80665f;
471 float waveSpectrumPhillips(
const float omega,
472 const float alpha = 0.0081f)
474 const float omega2 = omega * omega;
475 const float omega4 = omega2 * omega2;
476 const float omega5 = omega4 * omega;
478 return (alpha * g * g) / omega5;
481 float waveSpectrumPiersonMoskowitz(
const float omega,
483 const float alpha = 0.0081f)
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);
488 return waveSpectrumPhillips(omega, alpha) * exp(
float(-5.0 / 4.0) * omega_p_over_omega4);
491 float dispersionLonguetHiggins(
const float omega,
494 const float windWaveAngle)
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));
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));
525 float D = N_s_omega * pow(max(0.0f, cos(0.5f * windWaveAngle)), 2.f * s_omega);
526 assert(std::isfinite(D));
532 void createDirectionalWaveSpectrum(std::vector<float>& E,
539 float dominantWavePeriod)
541 if (windDirection >= pi) {
542 windDirection -= twoPi;
544 if (windSpeed < 2.f) {
548 float dominantAngularVelocity = float(2.0 * glm::pi<float>()) / dominantWavePeriod;
551 glm::vec2 windDir(glm::cos(windDirection), glm::sin(windDirection));
553 for (
size_t j = 0; j < N; j++) {
554 for (
size_t i = 0; i < N; i++) {
555 if ((i == 0) && (j == 0)) {
560 ivec2 ij(i <= N / 2 ?
int(i) :
int(i) -
int(N),
561 j <= N / 2 ?
int(j) :
int(j) -
int(N));
563 const vec2 K = (twoPi / L) * vec2(ij);
565 float angularVelocity = sqrt(g * k);
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));
572 const float S = waveSpectrumPiersonMoskowitz(angularVelocity,
573 dominantAngularVelocity,
575 const float D = dispersionLonguetHiggins(angularVelocity,
576 dominantAngularVelocity,
580 const float chainFactor = (1.f / (2.f * k)) * sqrt(g / k);
582 sumS += chainFactor * S;
583 E[N * j + i] = chainFactor * S * D;
589 float w = 1.f / sumS;
590 for (
size_t i = 0; i < N * N; i++) {
597 const std::vector<float>& E,
598 const unsigned int seed,
601 std::minstd_rand eng(seed);
602 std::uniform_int_distribution<int> dist(0, RAND_MAX);
604 for (
size_t j = 0; j < N; j++) {
605 for (
size_t i = 0; i < N; i++) {
609 for (
int q = 0; q < 12; q++) {
610 eta0 += float(dist(eng)) / float(RAND_MAX);
611 eta1 += float(dist(eng)) / float(RAND_MAX);
615 H.store(N * j + i, sqrt(E[N * j + i] / 2.f) * std::complex<float>(eta0, eta1));
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));
624 complex<float> eta = sqrt(-2.f *
log(U0)) * complex<float>(cos(U1), sin(U1));
626 H.store(N * j + i, sqrt(E[N * j + i] / 2.f) * eta);
632 void fastSinCos(
float& sin,
float& cos,
const float x)
634 float r = (2.0f / glm::pi<float>()) * x;
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;
643 float c = ((k & 1) == 0) ? vx : vy;
644 float s = ((k & 1) == 0) ? vy : vx;
646 cos = (((k + 1) & 2) == 0) ? c : -c;
647 sin = ((k & 2) == 0) ? s : -s;
650 complex<float> fastPolar(
float rho,
float theta)
654 fastSinCos(sin, cos, theta);
656 return complex<float>(rho * cos, rho * sin);
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));
676 vec2 kn = (1.f / k) * K;
677 float omega = sqrt(g * k);
679 complex<float> A = a.load(N * j + i) * fastPolar(1.f, omega * t);
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);
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));
695 void encodeTextureData(vec4* tangents,
706 for (
size_t i = begin; i < end; i++) {
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());
713 real[ix] = vec4(re, 0.f);
714 imag[ix] = vec4(im, 0.f);
719 void encodeTextureDataTangents(glm::u8vec4* tangents,
721 float& magnitude0Out,
722 float& magnitude1Out,
728 const float magnitude0In,
729 const float magnitude1In,
733 const float scale0 = 0.5f * 255.f / magnitude0In;
734 const float scale1 = 0.5f * 255.f / magnitude1In;
736 float magnitude0 = 0.f;
737 float magnitude1 = 0.f;
738 for (
size_t i = begin; i < end; i++) {
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());
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)),
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))),
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));
757 magnitude0Out = magnitude0;
758 magnitude1Out = magnitude1;
762 void encodeTextureDataQuux(vec4* pos,
770 const float significantWaveHeight,
775 const size_t N = (size_t)1 << m;
776 const auto M = N - 1;
777 const auto s = 1.f / L;
778 const auto l = L / N;
779 const auto h = significantWaveHeight;
809 for (
size_t j = begin; j < end; j++) {
810 size_t jp = (j + 1) & M;
812 for (
size_t i = 0; i < N; i++) {
813 size_t ip = (i + 1) & M;
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();
819 float dx_du = l * (Dx.load((j << m) + ip).imag() - x);
820 float dy_du = l * (Dy.load((j << m) + ip).imag() - y);
822 float dx_dv = l * (Dx.load((jp << m) + i).imag() - x);
823 float dy_dv = l * (Dy.load((jp << m) + i).imag() - y);
825 float zdu = dZdu.load((j << m) + i).imag();
826 float zdv = dZdv.load((j << m) + i).imag();
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));
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);
839 void encodeTextureDataQuux(glm::u8vec4* pos,
841 float& magnitude0Out,
842 float& magnitude1Out,
848 const float magnitude0In,
849 const float magnitude1In,
851 const float significantWaveHeight,
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;
860 const auto s = 1.f / L;
861 const auto l = L / N;
862 const auto h = significantWaveHeight;
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;
869 for (
size_t i = 0; i < N; i++) {
870 size_t ip = (i + 1) & M;
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());
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);
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);
882 float zdu = dZdu.load((j << m) + i).imag();
883 float zdv = dZdv.load((j << m) + i).imag();
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));
889 magnitude0 = std::max(std::max(std::abs(p.x), std::abs(p.y)),
890 std::max(std::abs(p.z), magnitude0));
894 magnitude1 = std::max(std::max(std::abs(n.x), std::abs(n.y)),
895 std::max(std::abs(n.z - 1.f), magnitude1));
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,
900 n.z - 1.f), glm::vec3(0.f), glm::vec3(255.f)), 0);
903 magnitude0Out = magnitude0;
904 magnitude1Out = magnitude1;
917 DisplacementTexH =
context->textureManager->create();
918 NormalTexH =
context->textureManager->create();
919 TangentsTexH =
context->textureManager->create();
921 DisplacementTexH->
setName(
"BasicOcean.Displacement");
922 NormalTexH->
setName(
"BasicOcean.Normals");
923 TangentsTexH->
setName(
"BasicOcean.Tangents");
931 if (animateVar->isEmpty()) {
932 animateVar->setBool(
true);
935 if (eightbitVar->isEmpty()) {
936 eightbitVar->setBool(
true);
942 if (oceanTaskGroup.isValid()) {
954void Cogs::Core::BasicOceanSystem::setupMaterial()
956 auto adaptiveMat = context->materialManager->loadMaterial(
"AdaptiveGridMaterial.material");
957 oceanMaterial = context->materialManager->loadMaterial(
"BasicOceanMaterial.material");
958 oceanMaterial2 = context->materialManager->loadMaterial(
"BasicSkyMaterial.material");
962 context->materialManager->processLoading();
966 auto m = oceanMaterial.resolve();
968 DisplacementKey = m->getTextureKey(
"Displacement");
969 NormalKey = m->getTextureKey(
"Difference");
971 TangentsKey = m->getTextureKey(
"Tangents");
973 m->setTextureProperty(DisplacementKey, DisplacementTexH);
974 m->setTextureProperty(NormalKey, NormalTexH);
975 m->setTextureProperty(TangentsKey, TangentsTexH);
977 ReflectionTextureH = context->textureManager->create();
978 ReflectionTextureH->setName(
"BasicOcean.Reflection");
980 PlanarReflectionKey = m->getTextureKey(
"PlanarReflection");
981 m->setTextureProperty(PlanarReflectionKey, ReflectionTextureH);
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");
999 const auto textureResolution =
static_cast<uint32_t
>(std::max(1,context->
variables->get(
"ocean.reflectionTextureResolution", 1024)));
1000 auto reflectionTex = context->textureManager->get(ReflectionTextureH);
1002 TextureFormat format = TextureFormat::R8G8B8A8_UNORM_SRGB;
1003 if (oceanComp !=
nullptr) {
1004 format = parseTextureFormat(oceanComp->reflectionTexFormat, format);
1007 if ((reflectionTex->description.width != textureResolution)
1008 || (reflectionTex->description.format != format))
1010 reflectionTex->description.width = textureResolution;
1011 reflectionTex->description.height = textureResolution;
1012 reflectionTex->description.format = format;
1014 reflectionTex->setChanged();
1018void Cogs::Core::BasicOceanSystem::setupWaveSpectrum()
1020 size_t N = size_t(1) << fftTileResolutionLog2;
1021 size_t N_times_N = N*N;
1023 constexpr float oneOvertwoPi = 1.f / (2.0f * glm::pi<float>());
1024 const float dominantWaveLength = oneOvertwoPi*(g*dominantWavePeriod*dominantWavePeriod);
1026 fftTileExtent = dominantWaveLength;
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);
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);
1042 fftScratch.resize(fastGenericFourierTransform2DScratchsize(N_times_N));
1044 const float omega_p = (0.855f*g) / windSpeed;
1046 createDirectionalWaveSpectrum(P, N, fftTileExtent, omega_p, windSpeed, 0.f , 1.f, dominantWavePeriod);
1047 createRandomizedWaveSpectrumInstance(frqH0, P, 42, N);
1050void Cogs::Core::BasicOceanSystem::updateTextures(
const float magnitudeIn0,
const float magnitudeIn1)
1052 CpuInstrumentationScope(SCOPE_SYSTEMS,
"OceanSystem::updateTextures");
1054 const uint16_t N = uint16_t(1) << fftTileResolutionLog2;
1056 auto realTex = context->textureManager->get(DisplacementTexH);
1057 auto imagTex = context->textureManager->get(NormalTexH);
1058 auto tangentsTex = context->textureManager->get(TangentsTexH);
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;
1069 size_t incr = (M + split - 1) / split;
1073 std::vector<float> magnitudes_scratch0(split, 0.f);
1074 std::vector<float> magnitudes_scratch1(split, 0.f);
1079 case BasicOceanWaves::Default:
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));
1090 if (1 < split) { context->
taskManager->enqueueChild(gr, f); }
else { f(); }
1099 case BasicOceanWaves::Quux:
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);
1111 if (1 < split) { context->
taskManager->enqueueChild(gr, f); }
else { f(); }
1126 case BasicOceanWaves::Default:
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));
1136 if (1 < split) { context->
taskManager->enqueueChild(gr, f); }
1146 case BasicOceanWaves::Quux:
1147 for (
size_t i = 0; i < split; ++i) {
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);
1156 if (1 < split) { context->
taskManager->enqueueChild(gr, f); }
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());
1175void Cogs::Core::BasicOceanSystem::updateTileMaterialInstances(
const BasicOceanData & oceanData,
1178 const glm::mat4 & viewToWorld,
1179 const glm::vec2 & viewPortSize)
1181 const glm::vec2 camPlaneDir = glm::normalize(glm::vec2(viewToWorld[2]));
1182 float camAzimuth = (0.5f / glm::pi<float>()) * acos(camPlaneDir.x);
1184 if (camPlaneDir.y > 0.f) {
1185 camAzimuth = -camAzimuth;
1188 camAzimuth = camAzimuth + 0.75f;
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));
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));
1207 m->setVec2Property(m->getVec2Key(
"viewportScale"), 2.f*viewPortSize);
1208 m->setVec2Property(m->getVec2Key(
"valueScale"), 2.f * glm::vec2(magnitudes.channel0, magnitudes.channel1));
1211 for (
auto & tile : gridData.tiles) {
1212 if (oceanData.transparent) {
1213 tile.materialInstance->setTransparent();
1216 tile.materialInstance->setOpaque();
1223 if (!pool.size())
return;
1225 updateTextureResolution(&(*pool.begin()));
1227 bool encodingChanged =
false;
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;
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) {
1243 oceanData.defaultReflectionCamera.reset();
1245 if (oceanData.reflectionCamera != oceanComp.reflectionCamera) {
1246 oceanData.reflectionCamera = oceanComp.reflectionCamera;
1247 updateCamera =
true;
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;
1262 visibleInstances = visibleInstances || sceneComp->visible;
1264 if (!oceanData.initialized) {
1265 context->adaptivePlanarGridSystem->registerMaterial(gridComp, oceanMaterial, initMaterialInstanceCallback, &oceanData);
1266 gridComp->layer = RenderLayers::Ocean;
1267 gridComp->setChanged();
1268 oceanData.initialized =
true;
1272 auto reflectionComponent = oceanData.reflectionCamera->getComponent<
ReflectionComponent>();
1273 reflectionComponent->texture = ReflectionTextureH;
1277 auto reflectionTex = context->textureManager->get(ReflectionTextureH);
1279 auto cameraComponent = oceanData.reflectionCamera->getComponent<
CameraComponent>();
1280 cameraComponent->
viewportSize = glm::vec2(reflectionTex->description.width, reflectionTex->description.height);
1283 auto * refCamComp = oceanData.reflectionCamera->getComponent<
CameraComponent>();
1284 if (sceneComp->visible) {
1285 switch (oceanComp.reflection) {
1286 case BasicOceanReflection::Planar:
1289 case BasicOceanReflection::EnvSkyBox:
1290 case BasicOceanReflection::EnvRadiance:
1302 if (oceanComp.
hasChanged() || encodingChanged) {
1303 specsChanged =
true;
1305 const char * waveVariant =
nullptr;
1306 switch (oceanComp.waves)
1308 case BasicOceanWaves::Default: waveVariant =
"Basic";
break;
1309 case BasicOceanWaves::Quux: waveVariant =
"Quux";
break;
1310 default: assert(
false);
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);
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);
1328 const char* lightModelVariant =
nullptr;
1329 lightModelVariant = oceanComp.pbr ?
"PBR" :
"Phong";
1331 const char* encodingVariant = eightBit ?
"Scaled" :
"AsIs";
1333 if ((oceanData.encoding != encodingVariant) ||
1334 (oceanData.waveVariant != waveVariant) ||
1335 (oceanData.reflectionVariant != reflectionVariant) ||
1336 (oceanData.lightModelVariant != lightModelVariant) ||
1337 (oceanData.beliefSystem != beliefSystem))
1339 oceanData.encoding = encodingVariant;
1340 oceanData.waveVariant = waveVariant;
1341 oceanData.beliefSystem = beliefSystem;
1342 oceanData.reflectionVariant = reflectionVariant;
1343 oceanData.lightModelVariant = lightModelVariant;
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);
1356 oceanData.color = oceanComp.
color;
1358 oceanData.seaLevel = oceanComp.
seaLevel;
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;
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();
1376 setupWaveSpectrum();
1378 for (
auto & oceanComp : pool) {
1381 auto c = glm::cos(windDirection);
1382 auto s = glm::sin(windDirection);
1384 context->adaptivePlanarGridSystem->setTexCoordTransform(gridComp, glm::mat2(c, s, -s, c), glm::vec2(fftTileExtent*tilePeriod));
1388 if (!visibleInstances)
return;
1392 bool doAnimate = context->
variables->get(
"basic-ocean.animate",
true);
1393 if (!doAnimate && !specsChanged) {
1395 specsChanged =
true;
1406 context->
engine->setDirty();
1409 const int N = 1 << fftTileResolutionLog2;
1411 const bool workParallel = context->
engine->workParallel();
1413 extraStep = eightBit && ((doAnimate ==
false) && (this->animate || specsChanged));
1416 const float time = doAnimate ?
static_cast<float>(context->
time->getAnimationTime()) : 7.f;
1417 if (animate || specsChanged) {
1418 if (workParallel ||
true) {
1420 const size_t subRange = N / numTasks;
1424 for (
size_t i = 0; i < numTasks; ++i) {
1425 context->
taskManager->enqueueChild(gr, [&, i, time, N, subRange]()
1429 disperseWaves(frqdDzdu, frqdDzdv, frqDx, frqDy, frqDz, frqH0, time, fftTileExtent, N, i * subRange, (i + 1) * subRange);
1433 context->
taskManager->enqueueChild(oceanTaskGroup, [
this, context, gr]()
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); });
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);
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);
1469 if (!pool.size())
return;
1472 for (
auto & oceanComp : pool) {
1473 auto & oceanData = getData(&oceanComp);
1475 auto & gridData = context->adaptivePlanarGridSystem->getData(gridComp);
1478 if (
auto e = gridComp->lodReference.lock(); e) {
1482 lodRefComp = context->cameraSystem->getMainCamera()->getComponent<
TransformComponent>();
1486 glm::vec2 viewportSize;
1487 for (
auto & weak : gridComp->cameras) {
1488 if (
auto entity = weak.lock(); entity) {
1490 auto & camData = context->cameraSystem->getData(comp);
1491 viewportSize = glm::max(viewportSize, camData.viewportSize);
1497 viewportSize = context->cameraSystem->getMainCameraData().viewportSize;
1500 updateTileMaterialInstances(oceanData,
1503 context->transformSystem->getLocalToWorld(lodRefComp),
1507 if (animate || specsChanged) {
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;
1516 magnitudes.channel0 = 0.9f * magnitudes.channel0 + 0.1f * magnitudes_tmp.channel0;
1517 magnitudes.channel1 = 0.9f * magnitudes.channel1 + 0.1f * magnitudes_tmp.channel1;
1521 specsChanged =
false;
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
ComponentType * getComponent() const
Container for components, providing composition of dynamic entities.
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.
class EntityStore * store
Entity store.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
std::unique_ptr< class Variables > variables
Variables service instance.
std::unique_ptr< class Time > time
Time service instance.
std::unique_ptr< class Engine > engine
Engine instance.
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.
Contains information on how the entity behaves in the scene.
static constexpr TaskQueueId GlobalQueue
Global task queue.
Log implementation class.
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.
@ EnableRender
Renderable.
@ None
No primitive culling performed.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
void COGSFOUNDATION_API log(const char *message, const char *source, const Category category, uint32_t errorNumber)
Logs the given message with source and category.
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.
Task id struct used to identify unique Task instances.
bool isValid() const
Check if the task id is valid.
Runtime control variable.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ RenderTarget
The texture can be used as a render target and drawn into.