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 assert(renderComponent);
47 if (!renderComponent->isPickable() || filter.isUnwantedType(*renderComponent) || !renderComponent->isVisibleInLayer(filter.layerMask)) {
48 return false;
49 }
50
51 float dummy = 0.f;
52 Geometry::BoundingBox terrainBounds = Geometry::makeEmptyBoundingBox<BoundingBox>();
53 oceanData->terrainData->getBounds->getBounds(context, terrainBounds, dummy);
54
55 if (isEmpty(terrainBounds)) {
56 return false;
57 }
58
59 glm::mat4 M = cameraData.inverseViewMatrix*glm::inverse(cameraData.rawProjectionMatrix);
60
61 glm::vec4 aH = M * glm::vec4(normPosition, -1, 1.f);
62 glm::vec4 bH = M * glm::vec4(normPosition, 1, 1.f);
63 glm::vec3 a = (1.f / aH.w)*glm::vec3(aH);
64 glm::vec3 b = (1.f / bH.w)*glm::vec3(bH);
65 glm::vec3 d = b - a;
66
67 glm::vec4 eq = glm::vec4(0, 0, 1, 0); // plane equation of ocean
68
69 float den = glm::dot(d, glm::vec3(eq));
70 if (glm::abs(den) < std::numeric_limits<float>::min()) {
71 return false;
72 }
73 float t = -glm::dot(glm::vec4(a, 1.f), eq) / den;
74 if ((t < 0.f) || (1.f < t)) {
75 return false;
76 }
77
78 glm::vec3 coordinates = glm::mix(a, b, t);
79
80 if ((coordinates.x < terrainBounds.min.x) ||
81 (coordinates.y < terrainBounds.min.y) ||
82 (terrainBounds.max.x < coordinates.x) ||
83 (terrainBounds.max.y < coordinates.y))
84 {
85 return false;
86 }
87
88 glm::vec4 clipPos = cameraData.rawViewProjection * glm::vec4(coordinates, 1.f);
89 if ((-clipPos.w > clipPos.z) || (clipPos.z > clipPos.w)) {
90 return false;
91 }
92
93 glm::vec4 viewPos = cameraData.viewMatrix * glm::vec4(glm::vec3(coordinates), 1.f);
94 float viewDist = -viewPos.z;
95
96 if (returnFlag == PicksReturned::Closest && !hits.empty()) {
97 if (viewDist < hits[0].distance) {
98 hits[0] = {*oceanComponent, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, coordinates, viewDist};
99 return true;
100 }
101 // Else the intersection we found is further, so don't report a hit
102 return false;
103 }
104
105 hits.emplace_back(*oceanComponent, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, coordinates, viewDist);
106 return true;
107}
108
109
111{
113 Cogs::VertexElement format{ 0, DataFormat::X32Y32_FLOAT, ElementSemantic::Position, 0, {}, {} };
114 oceanFormat = Cogs::VertexFormats::createVertexFormat(format);
115}
116
118{
119 for (auto & ocean : pool) {
120 auto & oceanData = getData(&ocean);
121
122 if (!oceanData.initialized && ocean.terrain) {
123 oceanData.oceanMaterial = context->materialManager->loadMaterial("OceanMaterial.material", MaterialLoadFlags::DelayVariantLoading);
124
125 auto terrainComponent = ocean.terrain->getComponent<TerrainComponent>();
126
127 auto renderComponent = ocean.terrain->getComponent<RenderComponent>();
128 renderComponent->unsetRenderFlag(RenderFlags::CastShadows);
129
130 context->materialManager->processLoading();
131
132 oceanData.streamsLayout.numStreams = 1;
133 oceanData.streamsLayout.vertexFormats[0] = oceanFormat;
134 oceanData.streamsLayout.updateHash();
135 oceanData.material = context->materialInstanceManager->createMaterialInstance(oceanData.oceanMaterial);
136
137 oceanData.terrainData = &terrainSystem->getData(terrainComponent);
138 oceanData.terrainData->oceanData = &oceanData;
139
140 oceanData.reflectionTexture = context->textureManager->create();
141 oceanData.reflectionTexture->setName("Ocean.Reflection");
142
143 auto planarKey = oceanData.oceanMaterial->getTextureKey("PlanarReflection");
144 oceanData.oceanMaterial->setTextureProperty(planarKey, oceanData.reflectionTexture);
145 oceanData.oceanMaterial->setTextureAddressMode(planarKey, SamplerState::Clamp);
146 const auto textureResolution = static_cast<uint32_t>(context->variables->get("ocean.reflectionTextureResolution", 1024));
147 auto reflectionTex = context->textureManager->get(oceanData.reflectionTexture);
148
149 if (reflectionTex->description.width != textureResolution) {
150 reflectionTex->description.width = textureResolution;
151 reflectionTex->description.height = textureResolution;
152 reflectionTex->description.format = TextureFormat::R8G8B8A8_UNORM;
153 reflectionTex->description.flags = TextureFlags::RenderTarget;
154 reflectionTex->setChanged();
155 }
156
157 context->textureManager->processLoading();
158
159 auto camera = context->cameraSystem->getMainCamera();
160 oceanData.reflectionCamera = context->store->createChildEntity("ReflectionCamera", camera->getContainer());
161
162 auto reflectionComponent = oceanData.reflectionCamera->getComponent<ReflectionComponent>();
163 reflectionComponent->texture = oceanData.reflectionTexture;
164
165 auto cameraComponent = oceanData.reflectionCamera->getComponent<CameraComponent>();
166 cameraComponent->viewportSize = glm::vec2(reflectionTex->description.width, reflectionTex->description.height);
167
168 if (!oceanData.rayPick) {
169 oceanData.rayPick = std::make_unique<OceanPicker>(&ocean, &oceanData);
170 context->rayPicking->addPickable(oceanData.rayPick.get());
171 }
172
173 oceanData.bounds = std::make_unique<OceanBounds>(&ocean, &oceanData);
174 context->bounds->addBoundsExtension(oceanData.bounds.get());
175
176 oceanData.initialized = true;
177 }
178
179 if (!oceanData.initialized) continue;
180
181 oceanData.oceanComponent = &ocean;
182
183 if (oceanData.initialized && oceanData.terrainData->initialized) {
184 auto terrainData = oceanData.terrainData;
185
186 terrainData->renderContext.oceanEnabled = true;
187
189 terrainGetEffectParameters(terrainData->context, &p);
190
191 if (p.changed || oceanData.first) {
192 oceanData.material->setVariant("NumTextures", p.numTextures);
193 oceanData.material->setVariant("NumActiveTextures", p.numActiveImagery);
194
195 if (terrainData->customComputeColor.size()) {
196 oceanData.material->setVariant("CustomComputeColor", terrainData->customComputeColor);
197 }
198
199 if (p.precomputeNormals) {
200 oceanData.material->setVariant("PrecomputeNormals", p.precomputeNormals);
201 }
202
203 oceanData.first = false;
204 }
205 }
206 }
207}
208
210{
211 auto & oceanData = getData(component.resolveComponent<OceanComponent>());
212
213 if (oceanData.rayPick) context->rayPicking->removePickable(oceanData.rayPick.get());
214 if (oceanData.bounds) context->bounds->removeBoundsExtension(oceanData.bounds.get());
215
217}
218
219void Cogs::Core::OceanBounds::getBounds(Context* context, Cogs::Geometry::BoundingBox& bounds)
220{
221 if (!oceanData->initialized || !oceanData->terrainData || !oceanData->terrainData->initialized) return;
222
223 float dummy = 0.f;
224 Geometry::BoundingBox terrainBounds = Geometry::makeEmptyBoundingBox<BoundingBox>();
225 oceanData->terrainData->getBounds->getBounds(context, terrainBounds, dummy);
226
227 if (isEmpty(terrainBounds)) {
228 return;
229 }
230
231 auto oceanBounds = terrainBounds;
232 oceanBounds.min.z = -oceanComponent->significantWaveHeight;
233 oceanBounds.max.z = oceanComponent->significantWaveHeight;
234
235 bounds += oceanBounds;
236}
237
238void Cogs::Core::OceanBounds::getBounds(Context* context, Cogs::Geometry::BoundingBox& bounds, float& nearPlaneLimit)
239{
240 if (!oceanData->initialized || !oceanData->terrainData || !oceanData->terrainData->initialized) return;
241
242 getBounds(context, bounds);
243 nearPlaneLimit = oceanComponent->getComponent<NearLimitComponent>()->nearPlaneLimit;
244}
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:129
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:187
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:70
@ 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