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 && !renderComp->isVisibleInLayer(filter.layerMask)) {
127 return false;
128 }
129
130
131 auto *transComp = component->getComponent<TransformComponent>();
132 auto &worldMatrix = context->transformSystem->getLocalToWorld(transComp);
133
134 auto & cameraData = context->cameraSystem->getData(&camera);
135 auto M = cameraData.inverseViewMatrix*glm::inverse(cameraData.rawProjectionMatrix);
136
137 auto aH = M * glm::vec4(normPosition, 0.0f, 1.f);
138 auto bH = M * glm::vec4(normPosition, 0.5f, 1.f);
139 auto cam_pos = (1.f / aH.w)*glm::vec3(aH);
140 auto forward_point = (1.f / bH.w)*glm::vec3(bH);
141 auto cam_dir = forward_point - cam_pos;
142
143 glm::vec3 cam_dir_n = glm::normalize(cam_dir);
144
145 glm::vec3 intersection;
146 if(!Geometry::intersect(data.bbox, cam_pos, cam_dir, intersection, true)) {
147 return false;
148 }
149
150 size_t count = component->positions.size();
151 for(size_t i=0; i<count; i++){
152 glm::vec3 cp = component->positions[i];
153 glm::vec3 cd = component->directions[i];
154 glm::vec3 pos = cp;
155 if(component->animation){
156 float ih = fmod(sin(i/(float)count)*43758.5453f, 1.0f);
157 float t = fmod(data.time+ih, 1.0f);
158 if(component->animation && component->animationUseSpeed)
159 t = data.time_offset_speed.size() ? data.time_offset_speed[i] : 0.0f;
160 float anim_scale = component->scale*component->length*component->animationScale;
161 pos += cd*anim_scale*(t-0.5f);
162 }
163 glm::vec4 pos_w_a = worldMatrix*glm::vec4(pos, 1.0);
164 glm::vec3 dir = glm::mat3(worldMatrix)*(cd*component->scale*component->length);
165 glm::vec3 pos_a = glm::vec3(pos_w_a)/pos_w_a.w;
166 glm::vec3 dir_norm = glm::normalize(dir);
167
168 float r = 0.05f*component->scale*component->tailScale;
169
170 // Distance between lines
171 float l = glm::length(cd)*component->scale*component->length;
172 if(std::isnan(r) || std::isinf(r)) continue;
173 if(std::isnan(l) || std::isinf(l)) continue;
174 if(r < 0.01 || l < 0.01) continue;
175
176 glm::vec3 p_norm = glm::cross(cam_dir_n, dir_norm);
177 float pn = glm::length(p_norm);
178 if(pn < 0.05f) continue;
179 p_norm = p_norm/pn;
180 float d1 = glm::dot(p_norm, pos_a);
181 float d2 = glm::dot(p_norm, cam_pos);
182 float dist = fabsf(d1-d2);
183 if(dist > r) continue;
184
185 // Off the end of cyliner
186 glm::vec3 n_norm = glm::normalize(glm::cross(cam_dir_n, p_norm));
187 glm::mat3 vs = glm::transpose(glm::mat3(cam_dir_n, p_norm, n_norm));
188 glm::vec3 vs_pos = vs*(pos_a - cam_pos);
189 glm::vec3 vs_dir = vs*dir;
190
191 float t = -vs_pos.z/vs_dir.z;
192 if(t > 1.0 || t < 0.0) continue;
193 intersection = pos_a+t*dir;
194
195 glm::vec4 clipPos = cameraData.rawViewProjection * glm::vec4(intersection, 1.f);
196 if ((-clipPos.w > clipPos.z) && (clipPos.z > clipPos.w)) {
197 return false;
198 }
199
200 glm::vec4 viewPos = cameraData.viewMatrix * glm::vec4(glm::vec3(intersection), 1.f);
201 float viewDist = -viewPos.z;
202
203 if (returnFlag == PicksReturned::Closest && !hits.empty()) {
204 if (viewDist < hits[0].distance) {
205 hits[0] = {*component, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, intersection, viewDist};
206 return true;
207 }
208 // Else the intersection we found is further, so continue searching
209 continue;
210 }
211
212 // Returning just the first intersected object should be enough for vector fields.
213 hits.emplace_back(*component, (pickingFlags & PickingFlags::ReturnChildEntity) == PickingFlags::ReturnChildEntity, intersection, viewDist);
214 return true;
215 }
216
217 return false;
218 }
220 {
222 vector_field_renderer = new VectorFieldRenderer; // TODO fix mem-leak?
223 context->renderer->registerExtension(vector_field_renderer);
224
225 }
227 {
228 for(VectorFieldComponent &component : pool){
229 VectorFieldData &data = getData(&component);
230 data.getBounds->getBounds(context, data.bbox);
231 }
232 }
234 {
235 auto material = context->materialManager->loadMaterial("Materials/VectorMaterial.material");
236 context->materialManager->processLoading();
237 material_instance_cap = context->materialInstanceManager->createMaterialInstance(material);
238 material_instance_cap->setPermutation("Cap");
239 material_instance_base = context->materialInstanceManager->createMaterialInstance(material);
240 material_instance_base->setPermutation("Base");
241 model_cap = context->modelManager->loadModel("Models/Cap.cogsbin", NoResourceId, ModelLoadFlags::None);
242 model_base = context->modelManager->loadModel("Models/Base.cogsbin", NoResourceId, ModelLoadFlags::None);
243
245 VectorFieldComponent *component = comp.resolveComponent<VectorFieldComponent>();
246 VectorFieldData &data = getData(component);
247 data.system = this;
248 data.handle = comp;
249
250 data.pos_element = Cogs::VertexElement{ 0, Cogs::Format::R32G32B32_FLOAT, Cogs::ElementSemantic::InstanceVector, 0, Cogs::InputType::InstanceData, 1 };
251 data.color_element = Cogs::VertexElement{ offsetof(InstanceData, color), Cogs::Format::R8G8B8A8_UNORM, Cogs::ElementSemantic::InstanceVector, 1, Cogs::InputType::InstanceData, 1 };
252 data.rot_element = Cogs::VertexElement{ offsetof(InstanceData, rot), Cogs::Format::R32G32B32A32_FLOAT, Cogs::ElementSemantic::InstanceVector, 2, Cogs::InputType::InstanceData, 1 };
253 data.scale_element = Cogs::VertexElement{ offsetof(InstanceData, scale), Cogs::Format::R32G32B32A32_FLOAT, Cogs::ElementSemantic::InstanceVector, 3, Cogs::InputType::InstanceData, 1 };
254
255 std::vector<VertexElement> format{
256 data.pos_element,
257 data.color_element,
258 data.rot_element,
259 data.scale_element
260 };
261 data.pos_format = Cogs::VertexFormats::createVertexFormat(format.data(), format.size());
262
263 data.buffer_count = 0;
264 data.getBounds = std::make_unique<VectorFieldBounds>(this, comp);
265 data.rayPick = std::make_unique<VectorFieldPick>(this, comp);
266 data.time = 0.0f;
267 context->bounds->addBoundsExtension(data.getBounds.get());
268 context->rayPicking->addPickable(data.rayPick.get());
269 return comp;
270 }
272 {
273 VectorFieldData &data = getData(component.resolveComponent<VectorFieldComponent>());
274 if(data.getBounds) context->bounds->removeBoundsExtension(data.getBounds.get());
275 if(data.rayPick) context->rayPicking->removePickable(data.rayPick.get());
277 }
278}// 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:187
RenderLayers layerMask
Limit picking to the specified render layers. Pick all layers by default.
Definition: RayPick.h:70
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