Cogs.Core
OceanSystem.cpp
1#include "OceanSystem.h"
2
3#include "Terrain.h"
4
5#include "Context.h"
6#include "ViewContext.h"
7#include "EntityStore.h"
8
9#include "Services/Variables.h"
10
11#include "Components/Core/RenderComponent.h"
12#include "Components/Core/NearLimitComponent.h"
13#include "Components/Behavior/ReflectionComponent.h"
14#include "Components/Appearance/MaterialComponent.h"
15
16#include "Systems/Core/CameraSystem.h"
17
18#include "Resources/ResourceStore.h"
19#include "Resources/MaterialManager.h"
20#include "Resources/TextureManager.h"
21
22#include "TerrainSystem.h"
23
24#include "Foundation/ComponentModel/Entity.h"
25
26using namespace Cogs::Geometry;
27
28Cogs::Core::OceanPicker::OceanPicker(OceanComponent * oceanComponent, OceanData * oceanData)
29 : oceanComponent(oceanComponent), oceanData(oceanData)
30{
31}
32
34 const CameraComponent& camera,
35 const glm::vec2& normPosition,
36 float /*rayLength*/,
37 float /*radius*/,
38 PickingFlags pickingFlags,
39 PicksReturned returnFlag,
40 const RayPicking::RayPickFilter& filter,
41 std::vector<RayPicking::RayPickHit>& hits)
42{
43 const CameraData& cameraData = context->cameraSystem->getData(&camera);
44
45 const RenderComponent* renderComponent = oceanComponent->getComponent<RenderComponent>();
46 if (renderComponent == nullptr || !renderComponent->isPickable() || filter.isUnwantedType(*renderComponent) || !renderComponent->isVisibleInLayer(filter.layerMask)) {
47 return false;
48 }
49
50 float dummy = 0.f;
51 Geometry::BoundingBox terrainBounds = Geometry::makeEmptyBoundingBox<BoundingBox>();
52 oceanData->terrainData->getBounds->getBounds(context, terrainBounds, dummy);
53
54 if (isEmpty(terrainBounds)) {
55 return false;
56 }
57
58 glm::mat4 M = cameraData.inverseViewMatrix*glm::inverse(cameraData.rawProjectionMatrix);
59
60 glm::vec4 aH = M * glm::vec4(normPosition, -1, 1.f);
61 glm::vec4 bH = M * glm::vec4(normPosition, 1, 1.f);
62 glm::vec3 a = (1.f / aH.w)*glm::vec3(aH);
63 glm::vec3 b = (1.f / bH.w)*glm::vec3(bH);
64 glm::vec3 d = b - a;
65
66 glm::vec4 eq = glm::vec4(0, 0, 1, 0); // plane equation of ocean
67
68 float den = glm::dot(d, glm::vec3(eq));
69 if (glm::abs(den) < std::numeric_limits<float>::min()) {
70 return false;
71 }
72 float t = -glm::dot(glm::vec4(a, 1.f), eq) / den;
73 if ((t < 0.f) || (1.f < t)) {
74 return false;
75 }
76
77 glm::vec3 coordinates = glm::mix(a, b, t);
78
79 if ((coordinates.x < terrainBounds.min.x) ||
80 (coordinates.y < terrainBounds.min.y) ||
81 (terrainBounds.max.x < coordinates.x) ||
82 (terrainBounds.max.y < coordinates.y))
83 {
84 return false;
85 }
86
87 glm::vec4 clipPos = cameraData.rawViewProjection * glm::vec4(coordinates, 1.f);
88 if ((-clipPos.w > clipPos.z) || (clipPos.z > clipPos.w)) {
89 return false;
90 }
91
92 glm::vec4 viewPos = cameraData.viewMatrix * glm::vec4(glm::vec3(coordinates), 1.f);
93 float viewDist = -viewPos.z;
94
95 const RayPicking::Ordinal ordinal = filter.getOrdinal(*renderComponent);
96
97 if (returnFlag == PicksReturned::Closest && !hits.empty()) {
98 if (hits[0].isBehind(ordinal, viewDist)) {
99 hits[0] = {*renderComponent, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, coordinates, ordinal, viewDist};
100 return true;
101 }
102 // Else the intersection we found is further, so don't report a hit
103 return false;
104 }
105
106 hits.emplace_back(*renderComponent, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, coordinates, ordinal, viewDist);
107 return true;
108}
109
110
112{
114 Cogs::VertexElement format{ 0, DataFormat::X32Y32_FLOAT, ElementSemantic::Position, 0, {}, {} };
115 oceanFormat = Cogs::VertexFormats::createVertexFormat(format);
116}
117
119{
120 for (auto & ocean : pool) {
121 auto & oceanData = getData(&ocean);
122
123 if (!oceanData.initialized && ocean.terrain) {
124 oceanData.oceanMaterial = context->materialManager->loadMaterial("OceanMaterial.material", MaterialLoadFlags::DelayVariantLoading);
125
126 auto terrainComponent = ocean.terrain->getComponent<TerrainComponent>();
127
128 auto renderComponent = ocean.terrain->getComponent<RenderComponent>();
129 renderComponent->unsetRenderFlag(RenderFlags::CastShadows);
130
131 context->materialManager->processLoading();
132
133 oceanData.streamsLayout.numStreams = 1;
134 oceanData.streamsLayout.vertexFormats[0] = oceanFormat;
135 oceanData.streamsLayout.updateHash();
136 oceanData.material = context->materialInstanceManager->createMaterialInstance(oceanData.oceanMaterial);
137
138 oceanData.terrainData = &terrainSystem->getData(terrainComponent);
139 oceanData.terrainData->oceanData = &oceanData;
140
141 oceanData.reflectionTexture = context->textureManager->create();
142 oceanData.reflectionTexture->setName("Ocean.Reflection");
143
144 auto planarKey = oceanData.oceanMaterial->getTextureKey("PlanarReflection");
145 oceanData.oceanMaterial->setTextureProperty(planarKey, oceanData.reflectionTexture);
146 oceanData.oceanMaterial->setTextureAddressMode(planarKey, SamplerState::Clamp);
147 const auto textureResolution = static_cast<uint32_t>(context->variables->get("ocean.reflectionTextureResolution", 1024));
148 auto reflectionTex = context->textureManager->get(oceanData.reflectionTexture);
149
150 if (reflectionTex->description.width != textureResolution) {
151 reflectionTex->description.width = textureResolution;
152 reflectionTex->description.height = textureResolution;
153 reflectionTex->description.format = TextureFormat::R8G8B8A8_UNORM;
154 reflectionTex->description.flags = TextureFlags::RenderTarget;
155 reflectionTex->setChanged();
156 }
157
158 context->textureManager->processLoading();
159
160 auto camera = context->cameraSystem->getMainCamera();
161 oceanData.reflectionCamera = context->store->createChildEntity("ReflectionCamera", camera->getContainer());
162
163 auto reflectionComponent = oceanData.reflectionCamera->getComponent<ReflectionComponent>();
164 reflectionComponent->texture = oceanData.reflectionTexture;
165
166 auto cameraComponent = oceanData.reflectionCamera->getComponent<CameraComponent>();
167 cameraComponent->viewportSize = glm::vec2(reflectionTex->description.width, reflectionTex->description.height);
168
169 if (!oceanData.rayPick) {
170 oceanData.rayPick = std::make_unique<OceanPicker>(&ocean, &oceanData);
171 context->rayPicking->addPickable(oceanData.rayPick.get());
172 }
173
174 oceanData.bounds = std::make_unique<OceanBounds>(&ocean, &oceanData);
175 context->bounds->addBoundsExtension(oceanData.bounds.get());
176
177 oceanData.initialized = true;
178 }
179
180 if (!oceanData.initialized) continue;
181
182 oceanData.oceanComponent = &ocean;
183
184 if (oceanData.initialized && oceanData.terrainData->initialized) {
185 auto terrainData = oceanData.terrainData;
186
187 terrainData->renderContext.oceanEnabled = true;
188
190 terrainGetEffectParameters(terrainData->context, &p);
191
192 if (p.changed || oceanData.first) {
193 oceanData.material->setVariant("NumTextures", p.numTextures);
194 oceanData.material->setVariant("NumActiveTextures", p.numActiveImagery);
195
196 if (terrainData->customComputeColor.size()) {
197 oceanData.material->setVariant("CustomComputeColor", terrainData->customComputeColor);
198 }
199
200 if (p.precomputeNormals) {
201 oceanData.material->setVariant("PrecomputeNormals", p.precomputeNormals);
202 }
203
204 oceanData.first = false;
205 }
206 }
207 }
208}
209
211{
212 auto & oceanData = getData(component.resolveComponent<OceanComponent>());
213
214 if (oceanData.rayPick) context->rayPicking->removePickable(oceanData.rayPick.get());
215 if (oceanData.bounds) context->bounds->removeBoundsExtension(oceanData.bounds.get());
216
218}
219
220void Cogs::Core::OceanBounds::getBounds(Context* context, Cogs::Geometry::BoundingBox& bounds)
221{
222 if (!oceanData->initialized || !oceanData->terrainData || !oceanData->terrainData->initialized) return;
223
224 float dummy = 0.f;
225 Geometry::BoundingBox terrainBounds = Geometry::makeEmptyBoundingBox<BoundingBox>();
226 oceanData->terrainData->getBounds->getBounds(context, terrainBounds, dummy);
227
228 if (isEmpty(terrainBounds)) {
229 return;
230 }
231
232 auto oceanBounds = terrainBounds;
233 oceanBounds.min.z = -oceanComponent->significantWaveHeight;
234 oceanBounds.max.z = oceanComponent->significantWaveHeight;
235
236 bounds += oceanBounds;
237}
238
239void Cogs::Core::OceanBounds::getBounds(Context* context, Cogs::Geometry::BoundingBox& bounds, float& nearPlaneLimit)
240{
241 if (!oceanData->initialized || !oceanData->terrainData || !oceanData->terrainData->initialized) return;
242
243 getBounds(context, bounds);
244 nearPlaneLimit = oceanComponent->getComponent<NearLimitComponent>()->nearPlaneLimit;
245}
ComponentType * getComponent() const
Definition: Component.h:159
glm::vec2 viewportSize
Size of the viewport covered by this instance, given in pixels.
virtual void initialize(Context *context)
Initialize the system.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
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
std::unique_ptr< class Bounds > bounds
Bounds service instance.
Definition: Context.h:216
class EntityStore * store
Entity store.
Definition: Context.h:231
std::unique_ptr< class RayPicking > rayPicking
RayPicking service instance.
Definition: Context.h:213
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
EntityPtr createChildEntity(const StringView &type, ComponentModel::Entity *parent, const StringView &name=StringView())
Create a new Entity, parenting it to the given parent.
Contains data to describe fog.
void getBounds(Context *context, Cogs::Geometry::BoundingBox &bounds, float &nearPlaneLimit) final
bool pickCamera(Context *context, const CameraComponent &camera, const glm::vec2 &normPosition, float, float, PickingFlags pickingFlags, PicksReturned returnFlag, const RayPicking::RayPickFilter &filter, std::vector< RayPicking::RayPickHit > &hits) override
Do a ray pick from a normalized screen space position in the camera direction and return all hits.
Definition: OceanSystem.cpp:33
void initialize(Context *context) override
Initialize the system.
void destroyComponent(ComponentHandle component) override
Base component for all rendering content.
constexpr bool isVisibleInLayer(RenderLayers layerMask) const
Check if the entity should be visible in the given layer mask.
constexpr bool isPickable() const
Check if the entity is pickable or not.
PicksReturned
  * Options for returning picking hits.
Definition: PickingFlags.h:40
@ Closest
Return just the closest hit.
@ CastShadows
Casts shadows.
PickingFlags
Options for COGS picking.
Definition: PickingFlags.h:12
@ ReturnChildEntity
Return ID if sub-entity picked, not set: return root parent entity.
@ DelayVariantLoading
Do not load any material variants, will be specified later.
Contains geometry calculations and generation.
@ Position
Position semantic.
Handle to a Component instance.
Definition: Component.h:67
ComponentType * resolveComponent() const
Definition: Component.h:90
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
glm::mat4 rawProjectionMatrix
Projection matrix that has not been adjusted by the renderer, and is thus appropriate for direct scre...
Definition: CameraSystem.h:130
bool isUnwantedType(const ComponentModel::Component &comp) const
Helper function used to determine if a given component belongs to an accepted entity type.
Definition: RayPick.cpp:202
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:62
@ 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
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38