Cogs.Core
ProceduralSkyComponent.cpp
1#include <cmath>
2#include <sstream>
3#include "ProceduralSkyComponent.h"
4#include "Types.h"
5#include "Context.h"
6#include "EntityStore.h"
7#include "Resources/MeshManager.h"
8#include "Resources/MaterialManager.h"
9#include "Components/Core/MeshRenderComponent.h"
10#include "Systems/Core/MeshSystem.h"
11
12using namespace Cogs::Geometry;
13using namespace Cogs::Reflection;
14
15static const float EARTH_RADIUS = 10000;// 6370997.0f;
16static const float EARTH_ATMOSPHERE_RADIUS = EARTH_RADIUS * 1.025f;
17
19{
20 float fRayleighScatteringConstant = 0.0025f; // Rayleigh scattering constant
21 float fMieScatteringConstant = 0.0015f; // Mie scattering constant
22 float fSunBrightness = 15.0f; // Sun brightness
23 float fRayleighScaleDepth = 0.25f; // Raylight scale depth
24 float fMiePhaseAsymmetryFactor = -0.98f; // The Mie phase asymmetry factor
25 glm::vec3 vWaveLength = glm::vec3(0.680f, 0.550f, 0.475f); // Wave length for red, green and blue
26};
27
28float toRadian(float _x)
29{
30 return _x * 0.017453292519f;
31}
32
33float lerp(float _x1, float _x2, float _w)
34{
35 return (_x1 + _w * (_x2 - _x1));
36}
37
38float truncate(float a_Value, float a_Min, float a_Max)
39{
40 if (a_Value < a_Min) return a_Min;
41 if (a_Value > a_Max) return a_Max;
42
43 return a_Value;
44}
45
46void createSkyDomeMesh(std::vector<Cogs::Core::PositionNormalVertex>& vertices, std::vector<unsigned int>& indices, int a_nStacks, int a_nSlices, float a_fRadius, float a_fDistribution)
47{
48 // Create vertex buffer
49 for (auto nStackNumber = 0; nStackNumber <= a_nStacks; nStackNumber++)
50 {
51 for (auto nSliceNumber = 0; nSliceNumber < a_nSlices; nSliceNumber++)
52 {
53 auto fTheta = powf((float)nStackNumber / (float)a_nStacks, a_fDistribution) * glm::pi<float>();
54 auto fPhi = (float)nSliceNumber / (float)a_nSlices * glm::pi<float>() * 2.0f;
55 auto fSinTheta = sinf(fTheta);
56 auto fSinPhi = sinf(fPhi);
57 auto fCosTheta = cosf(fTheta);
58 auto fCosPhi = cosf(fPhi);
59 auto pos = glm::vec3(fSinPhi * fSinTheta * a_fRadius, fCosPhi * fSinTheta * a_fRadius, fCosTheta * a_fRadius);
60 auto n = glm::normalize(-pos);
61 vertices.push_back({ pos, n });
62 }
63 }
64
65 // Create index buffer
66 for (auto nStackNumber = 0; nStackNumber < a_nStacks; nStackNumber++)
67 {
68 for (auto nSliceNumber = 0; nSliceNumber <= a_nSlices; nSliceNumber++)
69 {
70 auto nIndexA = (nStackNumber * a_nSlices) + (nSliceNumber % a_nSlices);
71 auto nIndexB = ((nStackNumber + 1) * a_nSlices) + (nSliceNumber % a_nSlices);
72 indices.push_back(nIndexA);
73 indices.push_back(nIndexB);
74 }
75 }
76}
77
78void Cogs::Core::ProceduralSkyComponent::registerType()
79{
80 Field fields[] = {
81 Field(Name("sunDirection"), &ProceduralSkyComponent::sunDirection)
82 };
83
84 Method methods[] = {
85 Method(Name("initialize"), &ProceduralSkyComponent::initialize),
86 Method(Name("update"), &ProceduralSkyComponent::update),
87 };
88
89 DynamicComponent::registerDerivedType<ProceduralSkyComponent>()
90 .setFields(fields)
91 .setMethods(methods);
92}
93
94void Cogs::Core::ProceduralSkyComponent::initialize(Context * ctx)
95{
96 context = ctx;
97
98 auto meshEntity = context->store->createChildEntity("MeshPart", getContainer(), "Skydome");
99
100 auto meshComp = meshEntity->getComponent<MeshComponent>();
101 if (meshComp->meshHandle == MeshHandle::NoHandle)
102 {
103 meshComp->meshHandle = context->meshManager->create();
104 }
105
106 // Material.
107 auto materialHandle = context->materialManager->loadMaterial("ProceduralSkyMaterial.material");
108 context->materialManager->processLoading();
109
110 material = materialHandle.resolve();
111 auto materialInstance = context->materialInstanceManager->createMaterialInstance(MaterialHandle(material));
112
113 auto meshRenderComp = meshEntity->getComponent<MeshRenderComponent>();
114 meshRenderComp->material = materialInstance;
115 meshRenderComp->setChanged();
116
117 krKmMaterialPropertyKey = material->getVec4Key("g_vKrKm");
118 scaleMaterialPropertyKey = material->getVec3Key("g_vScale");
119 invWavelengthMaterialPropertyKey = material->getVec3Key("g_vInvWavelength");
120 miePhaseAsymmetryFactorMaterialPropertyKey = material->getVec2Key("g_fMiePhaseAsymmetryFactor");
121 cameraPos_HeightMaterialPropertyKey = material->getVec4Key("g_vCameraPos_Height");
122 sunDirectionMaterialPropertyKey = material->getVec3Key("g_vSunDirection");
123
124 // Geometry.
125 auto mesh = context->meshManager->get(meshEntity->getComponent<MeshComponent>()->meshHandle);
126 mesh->primitiveType = Cogs::PrimitiveType::TriangleStrip;
127
128 std::vector<PositionNormalVertex> vertices;
129 std::vector<unsigned int> indices;
130 createSkyDomeMesh(vertices, indices, 50, 25, EARTH_ATMOSPHERE_RADIUS, 20.0f);
131
132 mesh->setVertexData(vertices.data(), vertices.size());
133 mesh->setIndexes(std::move(indices));
134}
135
136void Cogs::Core::ProceduralSkyComponent::update()
137{
138 glm::vec4 vKrKm;
139 glm::vec3 vScale;
140 glm::vec3 vInvWavelength;
141 auto fMiePhaseAsymmetryFactor = getAtmosphericScatteringData(vKrKm, vScale, vInvWavelength);
142
143 material->setVec4Property(krKmMaterialPropertyKey, vKrKm);
144 material->setVec3Property(scaleMaterialPropertyKey, vScale);
145 material->setVec3Property(invWavelengthMaterialPropertyKey, vInvWavelength);
146 material->setVec2Property(miePhaseAsymmetryFactorMaterialPropertyKey, glm::vec2(fMiePhaseAsymmetryFactor, fMiePhaseAsymmetryFactor * fMiePhaseAsymmetryFactor));
147 material->setVec4Property(cameraPos_HeightMaterialPropertyKey, glm::vec4(0.0f, EARTH_RADIUS, 0.0f, EARTH_RADIUS)); // Locate camera on then north pole
148 material->setVec3Property(sunDirectionMaterialPropertyKey, sunDirection);
149}
150
151float Cogs::Core::ProceduralSkyComponent::getSunBrightness()
152{
153 static const float s_fAngle95 = toRadian(95.0f);
154 static const float s_fInvAngle20 = 1.0f / toRadian(20.0f);
155 const glm::vec3& vSunDirection = sunDirection;
156 float fAngle = truncate((s_fAngle95 - acosf(vSunDirection.y)) * s_fInvAngle20, 0.0f, 1.0f); // -5..15
157
158 return powf(fAngle, 1.0f / 1.5f);
159}
160
161float Cogs::Core::ProceduralSkyComponent::getAtmosphericScatteringData(glm::vec4& vKrKm, glm::vec3& vScale, glm::vec3& vInvWavelength)
162{
163 AtmosphericScatteringParams cAtmosphericScattering;
164
165 const float fKr = lerp(0.0075f, cAtmosphericScattering.fRayleighScatteringConstant, getSunBrightness());
166 const float fKr4PI = fKr * 4.0f * glm::pi<float>();
167 const float fKm = cAtmosphericScattering.fMieScatteringConstant;
168 const float fKm4PI = fKm * 4.0f * glm::pi<float>();
169 const float fKrESun = fKr * cAtmosphericScattering.fSunBrightness;
170 const float fKmESun = fKm * cAtmosphericScattering.fSunBrightness;
171 vKrKm = glm::vec4(fKrESun, fKmESun, fKr4PI, fKm4PI);
172
173 const float fScale = 1.0f / (EARTH_ATMOSPHERE_RADIUS - EARTH_RADIUS);
174 const float fScaleDepth = cAtmosphericScattering.fRayleighScaleDepth;
175 const float fScaleOverScaleDepth = fScale / cAtmosphericScattering.fRayleighScaleDepth;
176 vScale = glm::vec3(fScale, fScaleDepth, fScaleOverScaleDepth);
177
178 vInvWavelength.x = 1.0f / powf(cAtmosphericScattering.vWaveLength.x, 4.0f);
179 vInvWavelength.y = 1.0f / powf(cAtmosphericScattering.vWaveLength.y, 4.0f);
180 vInvWavelength.z = 1.0f / powf(cAtmosphericScattering.vWaveLength.z, 4.0f);
181
182 return cAtmosphericScattering.fMiePhaseAsymmetryFactor;
183}
Field definition describing a single data member of a data structure.
Definition: Field.h:68
Simple method definition.
Definition: Method.h:72
Contains geometry calculations and generation.
Contains reflection support.
Definition: Component.h:11
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
@ TriangleStrip
Triangle strip.
Definition: Common.h:118
Represents an unique name.
Definition: Name.h:70