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#include <iterator>
16
17using namespace Cogs::Geometry;
18
19namespace {
20 constexpr uint32_t cColours[] = {
21 0xFFC2162E,
22 0xFFA65011,
23 0xFFC8981E,
24 0xFF0F8027,
25 0xFF00B774,
26 0xFF00889C,
27 0xFF0262EF,
28 0xFF565CED,
29 0xFF0303FF,
30 };
31}
32
35
36 mRenderer = new SeaCurrentsRenderer();
38
39 VertexElement elements[] = {
40 { 0, Cogs::Format::R32G32B32A32_FLOAT, Cogs::ElementSemantic::InstanceVector, 0, Cogs::InputType::InstanceData, 1 },
41 { offsetof(InstanceData, colour), Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::InstanceVector, 1, Cogs::InputType::InstanceData, 1 }
42 };
43 mVertexFormatHandle = Cogs::VertexFormats::createVertexFormat(elements, std::size(elements));
44
45
46 mArrowMesh = context->meshManager->create();
47
48 std::vector<glm::vec3> vertices = {
49 { 0.0f, 0.5f, 0.0f },
50 { 0.2f, 0.15f, 0.0f },
51 { 0.1f, 0.15f, 0.0f },
52
53 { 0.1f, 0.15f, 0.0f },
54 { -0.1f, 0.15f, 0.0f },
55 { 0.0f, 0.5f, 0.0f },
56
57 { -0.1f, 0.15f, 0.0f },
58 { -0.2f, 0.15f, 0.0f },
59 { 0.0f, 0.5f, 0.0f },
60
61 { 0.1f, 0.15f, 0.0f },
62 { 0.0f, -0.15f, 0.0f },
63 { -0.1f, 0.15f, 0.0f },
64
65 { 0.1f, 0.15f, 0.0f },
66 { 0.05f, -0.5f, 0.0f },
67 { 0.0f, -0.15f, 0.0f },
68
69 { 0.0f, -0.15f, 0.0f },
70 { 0.05f, -0.5f, 0.0f },
71 { -0.05f, -0.5f, 0.0f },
72
73 { -0.1f, 0.15f, 0.0f },
74 { 0.0f, -0.15f, 0.0f },
75 { -0.05f, -0.5f, 0.0f },
76 };
77
78 Mesh* mesh = mArrowMesh.resolve();
79
80 std::vector<glm::vec4> baricentricCoords = {
81 // values to compute the rate of change in the Pixel Shader
82 // values of 100 (arbitrary high number in a range[0-1]) forces the interpolation to never meet the criteria to color the edge
83 { 1.0f, 100.0f, 0.0f, 1.0f },
84 { 0.0f, 1.0f, 0.0f, 1.0f },
85 { 0.0f, 100.0f, 1.0f, 1.0f },
86
87 { 1.0f, 100.0f, 100.0f, 1.0f },
88 { 100.0f, 1.0f, 100.0f, 1.0f },
89 { 100.0f, 100.0f, 1.0f, 1.0f },
90
91 { 1.0f, 100.0f, 0.0f, 1.0f },
92 { 0.0f, 1.0f, 0.0f, 1.0f },
93 { 0.0f, 100.0f, 1.0f, 1.0f },
94
95 { 2.0f, 100.0f, 100.0f, 1.0f },
96 { 100.0f, 10.0f, 100.0f, 1.0f },
97 { 100.0f, 100.0f, 2.0f, 1.0f },
98
99 { 1.0f, 100.0f, 0.0f, 1.0f },
100 { 100.0f, 1.0f, 0.0f, 1.0f },
101 { 100.0f, 100.0f, 1.0f, 1.0f },
102
103 { 1.0f, 100.0f, 100.0f, 1.0f },
104 { 0.0f, 1.0f, 100.0f, 1.0f },
105 { 0.0f, 100.0f, 1.0f, 1.0f },
106
107 { 1.0f, 0.0f, 100.0f, 1.0f },
108 { 100.0f, 1.0f, 100.0f, 1.0f },
109 { 100.0f, 0.0f, 1.0f, 1.0f },
110 };
111
112 mesh->setName("Sea Currents Arrow");
113 mesh->setPositions(vertices);
114 mesh->setColors(baricentricCoords);
115 mesh->setBounds({ { -0.5f, -0.5f, -0.1f}, {0.5f, 0.5f, 0.1f} });
116
117
118 // Set up mesh stream layout of mesh data combined with instance data.
119 arrowInstancesStreamsLayout = mesh->getStreamsLayout();
120 assert(arrowInstancesStreamsLayout.numStreams + 1 < MeshStreamsLayout::maxStreams);
121 arrowInstancesStreamsLayout.vertexFormats[arrowInstancesStreamsLayout.numStreams++] = mVertexFormatHandle;
122 arrowInstancesStreamsLayout.updateHash();
123
124}
125
127 for (SeaCurrentsComponent& seaCurrentsComp : pool) {
128 SeaCurrentsData& data = getData(&seaCurrentsComp);
129
130 if (seaCurrentsComp.positions.size() == 0) continue;
131
132 constexpr float cSpeedBandMax[] = { 0.5f, 1.0f, 2.0f, 3.0f, 5.0f, 7.0f, 10.0f, 13.0f, 99.0f };
133 constexpr float cSpeedBandMin[] = { 0.0f, 0.5f, 1.0f, 2.0f, 3.0f, 5.0f, 7.0f, 10.0f, 13.0f };
134 constexpr float cSpeedBandWidth[] = { 0.5f, 0.5f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f, 86.0f };
135
136 if (!seaCurrentsComp.isValid()) {
137 continue;
138 }
139
140 if (!mMaterial->isActive()) {
141 Cogs::Core::MaterialHandle material = context->materialManager->loadMaterial("Materials/SeaCurrentsMaterial.material");
142 context->materialManager->processLoading();
143 mMaterial = context->materialInstanceManager->createMaterialInstance(material);
144 }
145
146 if (seaCurrentsComp.hasChanged()) {
147
148 lowestSpeed = context->variables->get("seacurrents.lowestSpeed", 0.01f);
149 highestSpeed = context->variables->get("seacurrents.highestSpeed", 13.0f);
150 minScale = context->variables->get("seacurrents.minScale", 0.2f);
151 maxScale = context->variables->get("seacurrents.maxScale", 1.0f);
152 //non mutating data members
153
154 glm::vec2 minPos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
155 glm::vec2 maxPos(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
156
157 // compute bounds
158 for (const glm::vec2& position : seaCurrentsComp.positions) {
159 minPos = glm::min(minPos, position);
160 maxPos = glm::max(maxPos, position);
161 }
162
163 data.mBounds = {{minPos, -0.01f}, {maxPos, 0.01f}};
164 data.mSystem = this;
165
166 size_t relevantDataPoints = seaCurrentsComp.positions.size();
167
168 if (!seaCurrentsComp.ignorePriorities) {
169 // Find the number of points with a priority higher than the priorityLevel.
170 auto it = std::upper_bound(seaCurrentsComp.priorities.begin(), seaCurrentsComp.priorities.end(), seaCurrentsComp.priorityLevel);
171 // Change the number of relevant data points to match the number of points with a priority < priorityLevel
172 relevantDataPoints = std::distance(seaCurrentsComp.priorities.begin(), it);
173 }
174
175 // Resize data collection to include only relevant data points.
176 data.mData.resize(relevantDataPoints);
177
178 for (size_t idx = 0, pointCount = relevantDataPoints; idx < pointCount; ++idx) {
179 float angle = glm::radians(seaCurrentsComp.angles[idx]);
180 float speed = seaCurrentsComp.speeds[idx];
181
182 uint32_t colouridx = 0;
183 glm::vec2 pos = seaCurrentsComp.positions[idx];
184
185 for (; colouridx < 8; ++colouridx) {
186 if (speed < cSpeedBandMax[colouridx]) {
187 break;
188 }
189 }
190 float scalingFactor = 1.0f;
191
192 if (seaCurrentsComp.scalingMode == ArrowScalingMode::Normal) {
193 scalingFactor = std::min(std::max(lowestSpeed, speed), highestSpeed) / highestSpeed;
194 }
195 else if (seaCurrentsComp.scalingMode == ArrowScalingMode::PerBand) {
196 scalingFactor = (speed - cSpeedBandMin[colouridx]) / cSpeedBandWidth[colouridx];
197 }
198
199 float scale = std::lerp(minScale, maxScale, scalingFactor);
200
201 data.mData[idx].pos = glm::vec4(pos, scale * seaCurrentsComp.scale, angle);
202 data.mData[idx].colour = cColours[colouridx];
203 }
204 }
205 }
206}
207
209 if (mRenderer) {
210 context->renderer->unregisterExtension(mRenderer);
211 delete mRenderer;
212 mRenderer = nullptr;
213 }
214}
215
217{
218 ComponentHandle handle = base_type::createComponent();
219 Cogs::Core::MaterialHandle material = context->materialManager->loadMaterial("Materials/SeaCurrentsMaterial.material");
220
221 context->materialManager->processLoading();
222
223 mMaterial = context->materialInstanceManager->createMaterialInstance(material);
224 // Ensure consistent AssetBounds.
225 if (pool.size() == 1u) {
226 assert(bounds == nullptr);
227 bounds = std::make_unique<SeaCurrentsBounds>(this);
228 context->bounds->addBoundsExtension(bounds.get());
229 }
230
231 return handle;
232}
234{
235 base_type::destroyComponent(component);
236
237 if (pool.size() == 0u && bounds) {
238 context->bounds->removeBoundsExtension(bounds.get());
239 bounds.reset();
240 }
241}
242
243Cogs::Geometry::BoundingBox Cogs::Core::SeaCurrentsSystem::getWorldBounds(const SeaCurrentsComponent& seaCurrentsComponent, bool ignoreVisibility) const
244{
245 if (!ignoreVisibility) {
246 auto sc = seaCurrentsComponent.getComponent<SceneComponent>();
247 if (sc && !sc->visible)
248 return Geometry::makeEmptyBoundingBox<Geometry::BoundingBox>();
249 }
250
251 const SeaCurrentsData& data = getData(&seaCurrentsComponent);
252 return data.mTransformedBounds;
253}
254
255void Cogs::Core::SeaCurrentsBounds::getBounds(Context* /*context*/, Cogs::Geometry::BoundingBox& bounds)
256{
257 for (SeaCurrentsComponent& seaCurrentsComponent : seaCurrentSystem->pool) {
258 const Cogs::Geometry::BoundingBox bbox = seaCurrentSystem->getWorldBounds(seaCurrentsComponent, false);
259 if (!isEmpty(bbox))
260 bounds += bbox;
261 }
262}
263
264bool Cogs::Core::SeaCurrentsBounds::getBounds(Context* /*context*/, const ComponentModel::Entity* entity, Cogs::Geometry::BoundingBox& bounds, bool ignoreVisibility) const
265{
266 auto seaCurrentsComponent = entity->getComponent<SeaCurrentsComponent>();
267 if (!seaCurrentsComponent) return false;
268 // SeaCurrentsBounds always bound to same Context as entity and SeaCurrentSystem
269 const Cogs::Geometry::BoundingBox bbox = seaCurrentSystem->getWorldBounds(*seaCurrentsComponent, ignoreVisibility);
270 if (!isEmpty(bbox)) {
271 bounds = bbox;
272 return true;
273 }
274 else {
275 return false;
276 }
277}
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