Cogs.Core
SeaCurrentsSystem.cpp
1#include "SeaCurrentsSystem.h"
2
3#include "SeaCurrentsRenderer.h"
4
5#include "Components/Core/MeshComponent.h"
6#include "Components/Core/SceneComponent.h"
7#include "Components/Core/TransformComponent.h"
8#include "Components/Core/InstancedMeshRenderComponent.h"
9#include "Resources/MaterialManager.h"
10#include "Resources/MeshManager.h"
11#include "Resources/VertexFormats.h"
12#include "Systems/Core/TransformSystem.h"
13
14#include <stddef.h>
15
16using namespace Cogs::Geometry;
17
18namespace {
19 constexpr uint32_t cColours[] = {
20 0xFFC2162E,
21 0xFFA65011,
22 0xFFC8981E,
23 0xFF0F8027,
24 0xFF00B774,
25 0xFF00889C,
26 0xFF0262EF,
27 0xFF565CED,
28 0xFF0303FF,
29 };
30}
31
34
35 mRenderer = new SeaCurrentsRenderer();
37
38 VertexElement elements[] = {
39 { 0, Cogs::Format::R32G32B32A32_FLOAT, Cogs::ElementSemantic::InstanceVector, 0, Cogs::InputType::InstanceData, 1 },
40 { offsetof(InstanceData, colour), Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::InstanceVector, 1, Cogs::InputType::InstanceData, 1 }
41 };
42 mVertexFormatHandle = Cogs::VertexFormats::createVertexFormat(elements, glm::countof(elements));
43
44
45 mArrowMesh = context->meshManager->create();
46
47 std::vector<glm::vec3> vertices = {
48 { 0.0f, 0.5f, 0.0f },
49 { 0.2f, 0.15f, 0.0f },
50 { 0.1f, 0.15f, 0.0f },
51
52 { 0.1f, 0.15f, 0.0f },
53 { -0.1f, 0.15f, 0.0f },
54 { 0.0f, 0.5f, 0.0f },
55
56 { -0.1f, 0.15f, 0.0f },
57 { -0.2f, 0.15f, 0.0f },
58 { 0.0f, 0.5f, 0.0f },
59
60 { 0.1f, 0.15f, 0.0f },
61 { 0.0f, -0.15f, 0.0f },
62 { -0.1f, 0.15f, 0.0f },
63
64 { 0.1f, 0.15f, 0.0f },
65 { 0.05f, -0.5f, 0.0f },
66 { 0.0f, -0.15f, 0.0f },
67
68 { 0.0f, -0.15f, 0.0f },
69 { 0.05f, -0.5f, 0.0f },
70 { -0.05f, -0.5f, 0.0f },
71
72 { -0.1f, 0.15f, 0.0f },
73 { 0.0f, -0.15f, 0.0f },
74 { -0.05f, -0.5f, 0.0f },
75 };
76
77 Mesh* mesh = mArrowMesh.resolve();
78
79 std::vector<glm::vec4> baricentricCoords = {
80 // values to compute the rate of change in the Pixel Shader
81 // values of 100 (arbitrary high number in a range[0-1]) forces the interpolation to never meet the criteria to color the edge
82 { 1.0f, 100.0f, 0.0f, 1.0f },
83 { 0.0f, 1.0f, 0.0f, 1.0f },
84 { 0.0f, 100.0f, 1.0f, 1.0f },
85
86 { 1.0f, 100.0f, 100.0f, 1.0f },
87 { 100.0f, 1.0f, 100.0f, 1.0f },
88 { 100.0f, 100.0f, 1.0f, 1.0f },
89
90 { 1.0f, 100.0f, 0.0f, 1.0f },
91 { 0.0f, 1.0f, 0.0f, 1.0f },
92 { 0.0f, 100.0f, 1.0f, 1.0f },
93
94 { 2.0f, 100.0f, 100.0f, 1.0f },
95 { 100.0f, 10.0f, 100.0f, 1.0f },
96 { 100.0f, 100.0f, 2.0f, 1.0f },
97
98 { 1.0f, 100.0f, 0.0f, 1.0f },
99 { 100.0f, 1.0f, 0.0f, 1.0f },
100 { 100.0f, 100.0f, 1.0f, 1.0f },
101
102 { 1.0f, 100.0f, 100.0f, 1.0f },
103 { 0.0f, 1.0f, 100.0f, 1.0f },
104 { 0.0f, 100.0f, 1.0f, 1.0f },
105
106 { 1.0f, 0.0f, 100.0f, 1.0f },
107 { 100.0f, 1.0f, 100.0f, 1.0f },
108 { 100.0f, 0.0f, 1.0f, 1.0f },
109 };
110
111 mesh->setName("Sea Currents Arrow");
112 mesh->setPositions(vertices);
113 mesh->setColors(baricentricCoords);
114 mesh->setBounds({ { -0.5f, -0.5f, -0.1f}, {0.5f, 0.5f, 0.1f} });
115
116
117 // Set up mesh stream layout of mesh data combined with instance data.
118 arrowInstancesStreamsLayout = mesh->getStreamsLayout();
119 assert(arrowInstancesStreamsLayout.numStreams + 1 < MeshStreamsLayout::maxStreams);
120 arrowInstancesStreamsLayout.vertexFormats[arrowInstancesStreamsLayout.numStreams++] = mVertexFormatHandle;
121 arrowInstancesStreamsLayout.updateHash();
122
123}
124
126 for (SeaCurrentsComponent& seaCurrentsComp : pool) {
127 SeaCurrentsData& data = getData(&seaCurrentsComp);
128
129 if (seaCurrentsComp.positions.size() == 0) continue;
130
131 constexpr float cSpeedBandMax[] = { 0.5f, 1.0f, 2.0f, 3.0f, 5.0f, 7.0f, 10.0f, 13.0f, 99.0f };
132 constexpr float cSpeedBandMin[] = { 0.0f, 0.5f, 1.0f, 2.0f, 3.0f, 5.0f, 7.0f, 10.0f, 13.0f };
133 constexpr float cSpeedBandWidth[] = { 0.5f, 0.5f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f, 86.0f };
134
135 if (!seaCurrentsComp.isValid()) {
136 continue;
137 }
138
139 if (!mMaterial->isActive()) {
140 Cogs::Core::MaterialHandle material = context->materialManager->loadMaterial("Materials/SeaCurrentsMaterial.material");
141 context->materialManager->processLoading();
142 mMaterial = context->materialInstanceManager->createMaterialInstance(material);
143 }
144
145 if (seaCurrentsComp.hasChanged()) {
146
147 lowestSpeed = context->variables->get("seacurrents.lowestSpeed", 0.01f);
148 highestSpeed = context->variables->get("seacurrents.highestSpeed", 13.0f);
149 minScale = context->variables->get("seacurrents.minScale", 0.2f);
150 maxScale = context->variables->get("seacurrents.maxScale", 1.0f);
151 //non mutating data members
152
153 glm::vec2 minPos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
154 glm::vec2 maxPos(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
155
156 // compute bounds
157 for (const glm::vec2& position : seaCurrentsComp.positions) {
158 minPos = glm::min(minPos, position);
159 maxPos = glm::max(maxPos, position);
160 }
161
162 data.mBounds = {{minPos, -0.01f}, {maxPos, 0.01f}};
163 data.mSystem = this;
164
165 size_t relevantDataPoints = seaCurrentsComp.positions.size();
166
167 if (!seaCurrentsComp.ignorePriorities) {
168 // Find the number of points with a priority higher than the priorityLevel.
169 auto it = std::upper_bound(seaCurrentsComp.priorities.begin(), seaCurrentsComp.priorities.end(), seaCurrentsComp.priorityLevel);
170 // Change the number of relevant data points to match the number of points with a priority < priorityLevel
171 relevantDataPoints = std::distance(seaCurrentsComp.priorities.begin(), it);
172 }
173
174 // Resize data collection to include only relevant data points.
175 data.mData.resize(relevantDataPoints);
176
177 for (size_t idx = 0, pointCount = relevantDataPoints; idx < pointCount; ++idx) {
178 float angle = glm::radians(seaCurrentsComp.angles[idx]);
179 float speed = seaCurrentsComp.speeds[idx];
180
181 uint32_t colouridx = 0;
182 glm::vec2 pos = seaCurrentsComp.positions[idx];
183
184 for (; colouridx < 8; ++colouridx) {
185 if (speed < cSpeedBandMax[colouridx]) {
186 break;
187 }
188 }
189 float scalingFactor = 1.0f;
190
191 if (seaCurrentsComp.scalingMode == ArrowScalingMode::Normal) {
192 scalingFactor = std::min(std::max(lowestSpeed, speed), highestSpeed) / highestSpeed;
193 }
194 else if (seaCurrentsComp.scalingMode == ArrowScalingMode::PerBand) {
195 scalingFactor = (speed - cSpeedBandMin[colouridx]) / cSpeedBandWidth[colouridx];
196 }
197
198 float scale = std::lerp(minScale, maxScale, scalingFactor);
199
200 data.mData[idx].pos = glm::vec4(pos, scale * seaCurrentsComp.scale, angle);
201 data.mData[idx].colour = cColours[colouridx];
202 }
203 }
204 }
205}
206
208 if (mRenderer) {
209 context->renderer->unregisterExtension(mRenderer);
210 delete mRenderer;
211 mRenderer = nullptr;
212 }
213}
214
216{
217 ComponentHandle handle = base_type::createComponent();
218 Cogs::Core::MaterialHandle material = context->materialManager->loadMaterial("Materials/SeaCurrentsMaterial.material");
219
220 context->materialManager->processLoading();
221
222 mMaterial = context->materialInstanceManager->createMaterialInstance(material);
223 // Ensure consistent AssetBounds.
224 if (pool.size() == 1u) {
225 assert(bounds == nullptr);
226 bounds = std::make_unique<SeaCurrentsBounds>(this);
227 context->bounds->addBoundsExtension(bounds.get());
228 }
229
230 return handle;
231}
233{
234 base_type::destroyComponent(component);
235
236 if (pool.size() == 0u && bounds) {
237 context->bounds->removeBoundsExtension(bounds.get());
238 bounds.reset();
239 }
240}
241
242Cogs::Geometry::BoundingBox Cogs::Core::SeaCurrentsSystem::getWorldBounds(const SeaCurrentsComponent& seaCurrentsComponent, bool ignoreVisibility) const
243{
244 if (!ignoreVisibility) {
245 auto sc = seaCurrentsComponent.getComponent<SceneComponent>();
246 if (sc && !sc->visible)
247 return Geometry::makeEmptyBoundingBox<Geometry::BoundingBox>();
248 }
249
250 const SeaCurrentsData& data = getData(&seaCurrentsComponent);
251 return data.mTransformedBounds;
252}
253
254void Cogs::Core::SeaCurrentsBounds::getBounds(Context* /*context*/, Cogs::Geometry::BoundingBox& bounds)
255{
256 for (SeaCurrentsComponent& seaCurrentsComponent : seaCurrentSystem->pool) {
257 const Cogs::Geometry::BoundingBox bbox = seaCurrentSystem->getWorldBounds(seaCurrentsComponent, false);
258 if (!isEmpty(bbox))
259 bounds += bbox;
260 }
261}
262
263bool Cogs::Core::SeaCurrentsBounds::getBounds(Context* /*context*/, const ComponentModel::Entity* entity, Cogs::Geometry::BoundingBox& bounds, bool ignoreVisibility) const
264{
265 auto seaCurrentsComponent = entity->getComponent<SeaCurrentsComponent>();
266 if (!seaCurrentsComponent) return false;
267 // SeaCurrentsBounds always bound to same Context as entity and SeaCurrentSystem
268 const Cogs::Geometry::BoundingBox bbox = seaCurrentSystem->getWorldBounds(*seaCurrentsComponent, ignoreVisibility);
269 if (!isEmpty(bbox)) {
270 bounds = bbox;
271 return true;
272 }
273 else {
274 return false;
275 }
276}
ComponentType * getComponent() const
Definition: Component.h:159
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
Context * context
Pointer to the Context instance the system lives in.
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 IRenderer * renderer
Renderer.
Definition: Context.h:228
virtual void registerExtension(IRendererExtension *extension)=0
Register an extension with the renderer.
virtual void unregisterExtension(IRendererExtension *extension)=0
Unregister an extension with the renderer.
Contains information on how the entity behaves in the scene.
virtual void getBounds(Context *context, Cogs::Geometry::BoundingBox &bounds) override
Expand bounds including bounds of all entities in this system in world coordinates.
virtual void destroyComponent(ComponentHandle component) override
Cogs::Geometry::BoundingBox getWorldBounds(const SeaCurrentsComponent &seaCurrentsComponent, bool ignoreVisibility) const
Used by SeaCurrentsBounds::getBounds.
virtual void initialize(Context *context) override
Initialize the system.
virtual void cleanup(Context *context) override
Provided for custom cleanup logic in derived systems.
virtual ComponentHandle createComponent() override
@ PerBand
Arrows scale with the speed of the current within each band.
@ Normal
Arrows scale with the speed of the current from min speed to max speed.
Contains geometry calculations and generation.
@ InstanceData
Per instance data.
@ InstanceVector
Instance vector semantic.
Handle to a Component instance.
Definition: Component.h:67
VertexFormatHandle vertexFormats[maxStreams]
COGSCORE_DLL_API void updateHash()
static constexpr size_t maxStreams
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
Definition: Mesh.h:265
void setBounds(Geometry::BoundingBox box)
Set custom bounds for the mesh.
Definition: Mesh.h:298
void setPositions(std::span< const glm::vec3 > positions)
Set the position data of the Mesh.
Definition: Mesh.h:315
void setName(const StringView &name)
Set the user friendly name of the resource.
Definition: ResourceBase.h:298
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
Component for displaying surface currents based on the IHO S111 standard.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38