Cogs.Core
VectorFieldSystem.cpp
1#include "VectorFieldSystem.h"
2#include "VectorFieldRenderer.h"
3
4#include "Context.h"
5#include "ViewContext.h"
6#include "Resources/MaterialManager.h"
7#include "Resources/ModelManager.h"
8#include "Systems/ComponentSystem.h"
9#include "Systems/Core/CameraSystem.h"
10#include "Systems/Core/SceneSystem.h"
11#include "Systems/Core/TransformSystem.h"
12
13#include "Math/RayBoxIntersection.h"
14
15#include "glm/glm.hpp"
16
17using namespace Cogs::Geometry;
18
19namespace Cogs::Core::VectorField
20{
21 void VectorFieldBounds::getBounds(Context* context, Cogs::Geometry::BoundingBox& bounds)
22 {
23 getBounds(context, bounds, false);
24 }
25
26 bool VectorFieldBounds::getBounds(Context* context, const ComponentModel::Entity* entity, Cogs::Geometry::BoundingBox& bounds, bool ignoreVisibility) const
27 {
28 auto entityComponent = entity->getComponent<VectorFieldComponent>();
29 if (!entityComponent) return false;
30
32 if (component != entityComponent)
33 return false;
34
35 Cogs::Geometry::BoundingBox myBounds;
36 if (getBounds(context, myBounds, ignoreVisibility)) {
37 bounds = myBounds;
38 return true;
39 }
40 else {
41 return false;
42 }
43 }
44
45
46 bool VectorFieldBounds::getBounds(Context *context, Cogs::Geometry::BoundingBox &bounds, bool ignoreVisibility) const
47 {
49 VectorFieldData &data = system->getData(component);
50
51 if (!ignoreVisibility) {
52 SceneComponent* sceneComp = component->getComponent<SceneComponent>();
53 if (sceneComp->visible == false) return false;
54 }
55
56 auto *transComp = component->getComponent<TransformComponent>();
57 auto &worldMatrix = context->transformSystem->getLocalToWorld(transComp);
58
59 bounds.min = glm::vec3(FLT_MAX);
60 bounds.max = glm::vec3(FLT_MIN);
61 size_t count = 0;
62 if(component->positions)
63 count = component->positions.size();
64 for(size_t i=0; i<count; i++){
65 glm::vec3 p = component->positions[i];
66 glm::vec3 d = component->directions[i];
67 glm::vec3 pos = p;
68 if(component->animation){
69 float ih = fmod(sin(i/(float)count)*43758.5453f, 1.0f);
70 float t = fmod(data.time+ih, 1.0f);
71 if(component->animation && component->animationUseSpeed)
72 t = data.time_offset_speed.size() ? data.time_offset_speed[i] : 0.0f;
73 float anim_scale = component->scale*component->length*component->animationScale;
74 pos += d*anim_scale*(t-0.5f);
75 }
76 {
77 glm::vec4 pos_w = worldMatrix*glm::vec4(pos, 1.0);
78 pos = glm::vec3(pos_w);
79 if(!std::isnan(pos.x)) bounds.min.x = std::min(bounds.min.x, pos.x);
80 if(!std::isnan(pos.y)) bounds.min.y = std::min(bounds.min.y, pos.y);
81 if(!std::isnan(pos.z)) bounds.min.z = std::min(bounds.min.z, pos.z);
82 if(!std::isnan(pos.x)) bounds.max.x = std::max(bounds.max.x, pos.x);
83 if(!std::isnan(pos.y)) bounds.max.y = std::max(bounds.max.y, pos.y);
84 if(!std::isnan(pos.z)) bounds.max.z = std::max(bounds.max.z, pos.z);
85 }
86 {
87 glm::vec4 pos_w = worldMatrix*glm::vec4(pos + d*component->scale, 1.0);
88 pos = glm::vec3(pos_w);
89 if(!std::isnan(pos.x)) bounds.min.x = std::min(bounds.min.x, pos.x);
90 if(!std::isnan(pos.y)) bounds.min.y = std::min(bounds.min.y, pos.y);
91 if(!std::isnan(pos.z)) bounds.min.z = std::min(bounds.min.z, pos.z);
92 if(!std::isnan(pos.x)) bounds.max.x = std::max(bounds.max.x, pos.x);
93 if(!std::isnan(pos.y)) bounds.max.y = std::max(bounds.max.y, pos.y);
94 if(!std::isnan(pos.z)) bounds.max.z = std::max(bounds.max.z, pos.z);
95 }
96 }
97 float r = 2.0f*component->scale;
98 bounds.min -= glm::vec3(r);
99 bounds.max += glm::vec3(r);
100 return !isEmpty(bounds);
101 }
102
104 const CameraComponent& camera,
105 const glm::vec2& normPosition,
106 float /*rayLength*/,
107 float /*radius*/,
108 PickingFlags pickingFlags,
109 PicksReturned returnFlag,
110 const RayPicking::RayPickFilter& filter,
111 std::vector<RayPicking::RayPickHit>& hits)
112 {
113 VectorFieldComponent *component = handle.resolveComponent<VectorFieldComponent>();
114 if (filter.isUnwantedType(*component)) {
115 return false;
116 }
117
118 VectorFieldData &data = system->getData(component);
119
120 SceneComponent *scene = component->getComponent<SceneComponent>();
121 if(scene && !scene->pickable) {
122 return false;
123 }
124
125 const RenderComponent* renderComp = component->getComponent<RenderComponent>();
126 if (renderComp == nullptr || !renderComp->isVisibleInLayer(filter.layerMask)) {
127 return false;
128 }
129
130 const RayPicking::Ordinal ordinal = filter.getOrdinal(*renderComp);
131
132 auto *transComp = component->getComponent<TransformComponent>();
133 auto &worldMatrix = context->transformSystem->getLocalToWorld(transComp);
134
135 auto & cameraData = context->cameraSystem->getData(&camera);
136 auto M = cameraData.inverseViewMatrix*glm::inverse(cameraData.rawProjectionMatrix);
137
138 auto aH = M * glm::vec4(normPosition, 0.0f, 1.f);
139 auto bH = M * glm::vec4(normPosition, 0.5f, 1.f);
140 auto cam_pos = (1.f / aH.w)*glm::vec3(aH);
141 auto forward_point = (1.f / bH.w)*glm::vec3(bH);
142 auto cam_dir = forward_point - cam_pos;
143
144 glm::vec3 cam_dir_n = glm::normalize(cam_dir);
145
146 glm::vec3 intersection;
147 if(!Geometry::intersect(data.bbox, cam_pos, cam_dir, intersection, true)) {
148 return false;
149 }
150
151 size_t count = component->positions.size();
152 for(size_t i=0; i<count; i++){
153 glm::vec3 cp = component->positions[i];
154 glm::vec3 cd = component->directions[i];
155 glm::vec3 pos = cp;
156 if(component->animation){
157 float ih = fmod(sin(i/(float)count)*43758.5453f, 1.0f);
158 float t = fmod(data.time+ih, 1.0f);
159 if(component->animation && component->animationUseSpeed)
160 t = data.time_offset_speed.size() ? data.time_offset_speed[i] : 0.0f;
161 float anim_scale = component->scale*component->length*component->animationScale;
162 pos += cd*anim_scale*(t-0.5f);
163 }
164 glm::vec4 pos_w_a = worldMatrix*glm::vec4(pos, 1.0);
165 glm::vec3 dir = glm::mat3(worldMatrix)*(cd*component->scale*component->length);
166 glm::vec3 pos_a = glm::vec3(pos_w_a)/pos_w_a.w;
167 glm::vec3 dir_norm = glm::normalize(dir);
168
169 float r = 0.05f*component->scale*component->tailScale;
170
171 // Distance between lines
172 float l = glm::length(cd)*component->scale*component->length;
173 if(std::isnan(r) || std::isinf(r)) continue;
174 if(std::isnan(l) || std::isinf(l)) continue;
175 if(r < 0.01 || l < 0.01) continue;
176
177 glm::vec3 p_norm = glm::cross(cam_dir_n, dir_norm);
178 float pn = glm::length(p_norm);
179 if(pn < 0.05f) continue;
180 p_norm = p_norm/pn;
181 float d1 = glm::dot(p_norm, pos_a);
182 float d2 = glm::dot(p_norm, cam_pos);
183 float dist = fabsf(d1-d2);
184 if(dist > r) continue;
185
186 // Off the end of cyliner
187 glm::vec3 n_norm = glm::normalize(glm::cross(cam_dir_n, p_norm));
188 glm::mat3 vs = glm::transpose(glm::mat3(cam_dir_n, p_norm, n_norm));
189 glm::vec3 vs_pos = vs*(pos_a - cam_pos);
190 glm::vec3 vs_dir = vs*dir;
191
192 float t = -vs_pos.z/vs_dir.z;
193 if(t > 1.0 || t < 0.0) continue;
194 intersection = pos_a+t*dir;
195
196 glm::vec4 clipPos = cameraData.rawViewProjection * glm::vec4(intersection, 1.f);
197 if ((-clipPos.w > clipPos.z) && (clipPos.z > clipPos.w)) {
198 return false;
199 }
200
201 glm::vec4 viewPos = cameraData.viewMatrix * glm::vec4(glm::vec3(intersection), 1.f);
202 float viewDist = -viewPos.z;
203
204 if (returnFlag == PicksReturned::Closest && !hits.empty()) {
205 if (hits[0].isBehind(ordinal, viewDist)) {
206 hits[0] = {*renderComp, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, intersection, ordinal, viewDist};
207 return true;
208 }
209 // Else the intersection we found is further, so continue searching
210 continue;
211 }
212
213 // Returning just the first intersected object should be enough for vector fields.
214 hits.emplace_back(*renderComp, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, intersection, ordinal, viewDist);
215 return true;
216 }
217
218 return false;
219 }
221 {
223 vector_field_renderer = new VectorFieldRenderer; // TODO fix mem-leak?
224 context->renderer->registerExtension(vector_field_renderer);
225
226 }
228 {
229 for(VectorFieldComponent &component : pool){
230 VectorFieldData &data = getData(&component);
231 data.getBounds->getBounds(context, data.bbox);
232 }
233 }
235 {
236 auto material = context->materialManager->loadMaterial("Materials/VectorMaterial.material");
237 context->materialManager->processLoading();
238 material_instance_cap = context->materialInstanceManager->createMaterialInstance(material);
239 material_instance_cap->setPermutation("Cap");
240 material_instance_base = context->materialInstanceManager->createMaterialInstance(material);
241 material_instance_base->setPermutation("Base");
242 model_cap = context->modelManager->loadModel("Models/Cap.cogsbin", NoResourceId, ModelLoadFlags::None);
243 model_base = context->modelManager->loadModel("Models/Base.cogsbin", NoResourceId, ModelLoadFlags::None);
244
246 VectorFieldComponent *component = comp.resolveComponent<VectorFieldComponent>();
247 VectorFieldData &data = getData(component);
248 data.system = this;
249 data.handle = comp;
250
251 data.pos_element = Cogs::VertexElement{ 0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::InstanceVector, 0, Cogs::InputType::InstanceData, 1 };
252 data.color_element = Cogs::VertexElement{ offsetof(InstanceData, color), Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::InstanceVector, 1, Cogs::InputType::InstanceData, 1 };
253 data.rot_element = Cogs::VertexElement{ offsetof(InstanceData, rot), Cogs::Format::R32G32B32A32_FLOAT, Cogs::ElementSemantic::InstanceVector, 2, Cogs::InputType::InstanceData, 1 };
254 data.scale_element = Cogs::VertexElement{ offsetof(InstanceData, scale), Cogs::Format::R32G32B32A32_FLOAT, Cogs::ElementSemantic::InstanceVector, 3, Cogs::InputType::InstanceData, 1 };
255
256 std::vector<VertexElement> format{
257 data.pos_element,
258 data.color_element,
259 data.rot_element,
260 data.scale_element
261 };
262 data.pos_format = Cogs::VertexFormats::createVertexFormat(format.data(), format.size());
263
264 data.buffer_count = 0;
265 data.getBounds = std::make_unique<VectorFieldBounds>(this, comp);
266 data.rayPick = std::make_unique<VectorFieldPick>(this, comp);
267 data.time = 0.0f;
268 context->bounds->addBoundsExtension(data.getBounds.get());
269 context->rayPicking->addPickable(data.rayPick.get());
270 return comp;
271 }
273 {
274 VectorFieldData &data = getData(component.resolveComponent<VectorFieldComponent>());
275 if(data.getBounds) context->bounds->removeBoundsExtension(data.getBounds.get());
276 if(data.rayPick) context->rayPicking->removePickable(data.rayPick.get());
278 }
279}// namespace ...
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
virtual ComponentHandle createComponent()
Create a new component instance.
Context * context
Pointer to the Context instance the system lives in.
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.
ComponentPool< ComponentType > pool
Pool of components managed by the system.
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
std::unique_ptr< class Bounds > bounds
Bounds service instance.
Definition: Context.h:216
std::unique_ptr< class RayPicking > rayPicking
RayPicking service instance.
Definition: Context.h:213
virtual void registerExtension(IRendererExtension *extension)=0
Register an extension with the renderer.
Base component for all rendering content.
constexpr bool isVisibleInLayer(RenderLayers layerMask) const
Check if the entity should be visible in the given layer mask.
Contains information on how the entity behaves in the scene.
bool visible
If the entity this component is a member of should be visible.
bool pickable
If the entity this component is a member of should be pickable.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
void getBounds(Context *context, Cogs::Geometry::BoundingBox &bounds) final
Expand bounds including bounds of all entities in this system in world coordinates.
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.
PicksReturned
  * Options for returning picking hits.
Definition: PickingFlags.h:40
@ Closest
Return just the closest hit.
PickingFlags
Options for COGS picking.
Definition: PickingFlags.h:12
@ ReturnChildEntity
Return ID if sub-entity picked, not set: return root parent entity.
Contains geometry calculations and generation.
@ InstanceData
Per instance data.
@ InstanceVector
Instance vector semantic.
Handle to a Component instance.
Definition: Component.h:67
ComponentType * resolveComponent() const
Definition: Component.h:90
size_t size() const
Gets the size of the buffer in number of elements of type T.
Definition: Buffer.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:202
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:62
ComponentModel::ComponentHandle createComponent() override
void destroyComponent(ComponentHandle component) override
void initialize(Context *context) override
Initialize the system.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38