Added method EntityFirstHitByRay, takes an Octree or a vector of sorted entities and outputs the frist entity hit by a ray.

This commit is contained in:
William Moberg
2016-02-11 00:11:37 +01:00
parent ed06eacf6f
commit 73f59b89cc
4 changed files with 151 additions and 38 deletions
+18 -5
View File
@@ -20,6 +20,9 @@
class World; class World;
struct ComponentWrapper; struct ComponentWrapper;
template<typename T>
class Octree;
namespace Collision namespace Collision
{ {
//Return true if the ray hits the box. //Return true if the ray hits the box.
@@ -44,21 +47,24 @@ bool RayVsTriangle(const Ray& ray,
float& outVCoord, float& outVCoord,
bool trueOnNegativeDistance = false); bool trueOnNegativeDistance = false);
//Return true if the ray hits any of the triangles in the model. Stops checking when a hit is detected. //Return true if the ray hits any of the triangles in the model. Stops checking when a hit is detected.
bool RayVsModel(const Ray& ray, bool RayVsModel(const Ray& ray,
const std::vector<RawModel::Vertex>& modelVertices, const RawModel::Vertex* modelVertices,
const std::vector<unsigned int>& modelIndices); const std::vector<unsigned int>& modelIndices,
const glm::mat4& modelMatrix);
//Return true if the ray hits any of the triangles in the model. //Return true if the ray hits any of the triangles in the model.
//Also returns the position of the intersection point. Will loop through all the whole model indices. //Also returns the position of the intersection point. Will loop through all the whole model indices.
bool RayVsModel(const Ray& ray, bool RayVsModel(const Ray& ray,
const std::vector<RawModel::Vertex>& modelVertices, const RawModel::Vertex* modelVertices,
const std::vector<unsigned int>& modelIndices, const std::vector<unsigned int>& modelIndices,
const glm::mat4& modelMatrix,
glm::vec3& outHitPosition); glm::vec3& outHitPosition);
//Return true if the ray hits any of the triangles in the model. //Return true if the ray hits any of the triangles in the model.
//Also returns the distance from the ray origin to the closest //Also returns the distance from the ray origin to the closest
//intersection point, and the barycentric u,v-coordinates. Will loop through all the whole model indices. //intersection point, and the barycentric u,v-coordinates. Will loop through all the whole model indices.
bool RayVsModel(const Ray& ray, bool RayVsModel(const Ray& ray,
const std::vector<RawModel::Vertex>& modelVertices, const RawModel::Vertex* modelVertices,
const std::vector<unsigned int>& modelIndices, const std::vector<unsigned int>& modelIndices,
const glm::mat4& modelMatrix,
float& outDistance, float& outDistance,
float& outUCoord, float& outUCoord,
float& outVCoord); float& outVCoord);
@@ -81,6 +87,13 @@ bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation);
// Calculates an absolute AABB from an entity AABB component // Calculates an absolute AABB from an entity AABB component
boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox = false); boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox = false);
boost::optional<EntityAABB> AbsoluteAABBExplosionEffect(EntityWrapper& entity); boost::optional<EntityAABB> AbsoluteAABBExplosionEffect(EntityWrapper& entity);
//Returns the first entity hit by the input ray. entitiesPotentiallyHitSorted needs to be sorted
//by their distance to the ray, e.g. result from Octree<EntityAABB>::ObjectsPossiblyHitByRay.
//Returns boost::none if none was hit. outDistance will be the distance to the intersection point if the ray intersects.
boost::optional<EntityAABB> EntityFirstHitByRay(const Ray& ray, std::vector<EntityAABB> entitiesPotentiallyHitSorted, float outDistance, glm::vec3& outIntersectPos);
//Returns the first entity hit by the input ray that exists in the octree.
//outDistance will be the distance to the intersection point if the ray intersects.
boost::optional<EntityAABB> EntityFirstHitByRay(const Ray& ray, Octree<EntityAABB>* octree, float outDistance, glm::vec3& outIntersectPos);
} }
+75
View File
@@ -43,6 +43,8 @@ public:
void ObjectsInSameRegion(const Box& box, std::vector<T>& outObjects); void ObjectsInSameRegion(const Box& box, std::vector<T>& outObjects);
//Get the objects that are inside the frustum, the objects are put in outObjects. //Get the objects that are inside the frustum, the objects are put in outObjects.
void ObjectsInFrustum(const Frustum& frustum, std::vector<T>& outObjects); void ObjectsInFrustum(const Frustum& frustum, std::vector<T>& outObjects);
//Get objects, which AABB the input ray intersects, the objects are put in outObjects.
void ObjectsPossiblyHitByRay(const Ray& ray, std::vector<T>& outObjects);
//Empty the tree of all objects, static and dynamic. //Empty the tree of all objects, static and dynamic.
void ClearObjects(); void ClearObjects();
//Empty the tree of all dynamic objects. Static objects remain in the tree. //Empty the tree of all dynamic objects. Static objects remain in the tree.
@@ -102,6 +104,8 @@ struct Child
void ObjectsInSameRegion(const Box& box, std::vector<T>& outObjects) const; void ObjectsInSameRegion(const Box& box, std::vector<T>& outObjects) const;
template<typename T> template<typename T>
void ObjectsInFrustum(const Frustum& frustum, std::vector<T>& outObjects, bool takeAllDontTest) const; void ObjectsInFrustum(const Frustum& frustum, std::vector<T>& outObjects, bool takeAllDontTest) const;
template<typename T>
void ObjectsPossiblyHitByRay(const Ray& ray, std::vector<T>& outObjects) const;
void ClearObjects(); void ClearObjects();
void ClearDynamicObjects(); void ClearDynamicObjects();
bool RayCollides(const Ray& ray, Output& data) const; bool RayCollides(const Ray& ray, Output& data) const;
@@ -121,6 +125,15 @@ struct Child
std::vector<int> childIndicesContainingBox(const AABB& box) const; std::vector<int> childIndicesContainingBox(const AABB& box) const;
}; };
//To be able to sort child nodes and contained objects based on distance to ray origin.
struct RaySorterInfo
{
int Index;
float Distance;
};
bool isFirstLower(const RaySorterInfo& first, const RaySorterInfo& second);
} }
template<typename T> template<typename T>
@@ -166,6 +179,13 @@ void Octree<T>::ObjectsInFrustum(const Frustum& frustum, std::vector<T>& outObje
m_Root->ObjectsInFrustum(frustum, outObjects, false); m_Root->ObjectsInFrustum(frustum, outObjects, false);
} }
template<typename T>
void Octree<T>::ObjectsPossiblyHitByRay(const Ray& ray, std::vector<T>& outObjects)
{
falsifyObjectChecks();
m_Root->ObjectsPossiblyHitByRay(ray, outObjects);
}
template<typename T> template<typename T>
void Octree<T>::ClearObjects() void Octree<T>::ClearObjects()
{ {
@@ -284,4 +304,59 @@ void OctSpace::Child::ObjectsInFrustum(const Frustum& frustum, std::vector<T>& o
} }
} }
template<typename T>
void OctSpace::Child::ObjectsPossiblyHitByRay(const Ray& ray, std::vector<T>& outObjects) const
{
//If the node AABB is missed, everything it contains is missed.
if (Collision::RayAABBIntr(ray, m_Box)) {
//If the ray shoots the tree, and it is a parent.
if (hasChildren()) {
//Sort children according to their distance from the ray origin.
std::vector<RaySorterInfo> childInfos;
childInfos.resize(8);
for (int i = 0; i < 8; ++i) {
childInfos[i] = { i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Origin()) };
}
std::sort(childInfos.begin(), childInfos.end(), isFirstLower);
//Loop through the children, starting with the one closest to the ray origin. I.e the first to be hit.
for (const RaySorterInfo& info : childInfos) {
m_Children[info.Index]->ObjectsPossiblyHitByRay(ray, outObjects);
}
} else {
//Check against boxes in the node.
bool intersected = false;
float dist;
//Sort all contained objects according to the distance from the ray origin to
//the intersection, if they are intersecting.
std::vector<RaySorterInfo> objectHitInfos;
objectHitInfos.reserve(m_StaticObjIndices.size() + m_DynamicObjIndices.size());
for (int i : m_StaticObjIndices) {
//If we haven't tested against this object before, and the ray hits.
if (!m_StaticObjectsRef[i].Checked &&
Collision::RayVsAABB(ray, *m_StaticObjectsRef[i].Box, dist)) {
objectHitInfos.push_back({ i, dist });
}
m_StaticObjectsRef[i].Checked = true;
}
for (int i : m_DynamicObjIndices) {
//If we haven't tested against this object before, and the ray hits.
if (!m_DynamicObjectsRef[i].Checked &&
Collision::RayVsAABB(ray, *m_DynamicObjectsRef[i].Box, dist)) {
objectHitInfos.push_back({ i + (int)m_StaticObjIndices.size(), dist });
}
m_DynamicObjectsRef[i].Checked = true;
}
std::sort(objectHitInfos.begin(), objectHitInfos.end(), isFirstLower);
int startSize = (int)outObjects.size();
outObjects.resize(startSize + objectHitInfos.size());
for (int i = 0; i < objectHitInfos.size(); ++i) {
outObjects[startSize + i] = (objectHitInfos[i].Index < m_StaticObjIndices.size()) ?
*static_cast<T*>(m_StaticObjectsRef[objectHitInfos[i].Index].Box.get()) :
*static_cast<T*>(m_DynamicObjectsRef[objectHitInfos[i].Index - m_StaticObjIndices.size()].Box.get());
}
}
}
}
#endif #endif
+51 -15
View File
@@ -6,6 +6,7 @@
#include "Core/World.h" #include "Core/World.h"
#include "Rendering/Model.h" #include "Rendering/Model.h"
#include "imgui/imgui.h" #include "imgui/imgui.h"
#include "Core/Octree.h"
namespace Collision namespace Collision
{ {
@@ -145,13 +146,14 @@ bool RayVsTriangle(const Ray& ray,
} }
bool RayVsModel(const Ray& ray, bool RayVsModel(const Ray& ray,
const std::vector<RawModel::Vertex>& modelVertices, const RawModel::Vertex* modelVertices,
const std::vector<unsigned int>& modelIndices) const std::vector<unsigned int>& modelIndices,
const glm::mat4& modelMatrix)
{ {
for (int i = 0; i < modelIndices.size(); ++i) { for (int i = 0; i < modelIndices.size();) {
glm::vec3 v0 = modelVertices[modelIndices[i]].Position; glm::vec3 v0 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
glm::vec3 v1 = modelVertices[modelIndices[++i]].Position; glm::vec3 v1 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
glm::vec3 v2 = modelVertices[modelIndices[++i]].Position; glm::vec3 v2 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
if (RayVsTriangle(ray, v0, v1, v2)) { if (RayVsTriangle(ray, v0, v1, v2)) {
return true; return true;
} }
@@ -192,19 +194,20 @@ bool RayVsTriangle(const Ray& ray,
} }
bool RayVsModel(const Ray& ray, bool RayVsModel(const Ray& ray,
const std::vector<RawModel::Vertex>& modelVertices, const RawModel::Vertex* modelVertices,
const std::vector<unsigned int>& modelIndices, const std::vector<unsigned int>& modelIndices,
const glm::mat4& modelMatrix,
float& outDistance, float& outDistance,
float& outUCoord, float& outUCoord,
float& outVCoord) float& outVCoord)
{ {
outDistance = INFINITY; outDistance = INFINITY;
bool hit = false; bool hit = false;
for (int i = 0; i < modelIndices.size(); ++i) { for (int i = 0; i < modelIndices.size();) {
glm::vec3 v0 = modelVertices[modelIndices[i]].Position; glm::vec3 v0 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
glm::vec3 v1 = modelVertices[modelIndices[++i]].Position; glm::vec3 v1 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
glm::vec3 v2 = modelVertices[modelIndices[++i]].Position; glm::vec3 v2 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
float dist; float dist = INFINITY;
float u; float u;
float v; float v;
if (RayVsTriangle(ray, v0, v1, v2, dist, u, v)) { if (RayVsTriangle(ray, v0, v1, v2, dist, u, v)) {
@@ -218,14 +221,15 @@ bool RayVsModel(const Ray& ray,
} }
bool RayVsModel(const Ray& ray, bool RayVsModel(const Ray& ray,
const std::vector<RawModel::Vertex>& modelVertices, const RawModel::Vertex* modelVertices,
const std::vector<unsigned int>& modelIndices, const std::vector<unsigned int>& modelIndices,
const glm::mat4& modelMatrix,
glm::vec3& outHitPosition) glm::vec3& outHitPosition)
{ {
float u; float u;
float v; float v;
float dist; float dist;
bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v); bool hit = RayVsModel(ray, modelVertices, modelIndices, modelMatrix, dist, u, v);
outHitPosition = ray.Origin() + dist * ray.Direction(); outHitPosition = ray.Origin() + dist * ray.Direction();
return hit; return hit;
} }
@@ -572,11 +576,11 @@ boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeM
ComponentWrapper& cAABB = entity["AABB"]; ComponentWrapper& cAABB = entity["AABB"];
modelSpaceBox = EntityAABB::FromOriginSize((glm::vec3)cAABB["Origin"], (glm::vec3)cAABB["Size"]); modelSpaceBox = EntityAABB::FromOriginSize((glm::vec3)cAABB["Origin"], (glm::vec3)cAABB["Size"]);
} else if (entity.HasComponent("Model")) { } else if (entity.HasComponent("Model")) {
Model* model;
std::string res = entity["Model"]["Resource"]; std::string res = entity["Model"]["Resource"];
if (res.empty()) { if (res.empty()) {
return boost::none; return boost::none;
} }
Model* model;
try { try {
model = ResourceManager::Load<::Model, true>(res); model = ResourceManager::Load<::Model, true>(res);
} catch (const Resource::StillLoadingException&) { } catch (const Resource::StillLoadingException&) {
@@ -638,4 +642,36 @@ boost::optional<EntityAABB> AbsoluteAABBExplosionEffect(EntityWrapper& entity)
return aabb; return aabb;
} }
boost::optional<EntityAABB> EntityFirstHitByRay(const Ray& ray, std::vector<EntityAABB> entitiesPotentiallyHitSorted, float outDistance, glm::vec3& outIntersectPos)
{
for (EntityAABB& entityBox : entitiesPotentiallyHitSorted) {
if (!entityBox.Entity.HasComponent("Model")) {
continue;
}
std::string res = entityBox.Entity["Model"]["Resource"];
if (res.empty()) {
continue;
}
Model* model;
try {
model = ResourceManager::Load<::Model, true>(res);
} catch (const std::exception&) {
continue;
}
float u, v;
if (RayVsModel(ray, model->Vertices(), model->m_RawModel->m_Indices, Transform::ModelMatrix(entityBox.Entity), outDistance, u, v)) {
outIntersectPos = ray.Origin() + outDistance * ray.Direction();
return entityBox;
}
}
return boost::none;
}
boost::optional<EntityAABB> EntityFirstHitByRay(const Ray& ray, Octree<EntityAABB>* octree, float outDistance, glm::vec3& outIntersectPos)
{
std::vector<EntityAABB> outObjects;
octree->ObjectsPossiblyHitByRay(ray, outObjects);
return Collision::EntityFirstHitByRay(ray, outObjects, outDistance, outIntersectPos);
}
} }
+7 -18
View File
@@ -5,22 +5,6 @@
#include "Core/Octree.h" #include "Core/Octree.h"
#include "Collision/Collision.h" #include "Collision/Collision.h"
namespace
{
//To be able to sort nodes based on distance to ray origin.
struct ChildInfo
{
int Index;
float Distance;
};
bool isFirstLower(const ChildInfo& first, const ChildInfo& second)
{
return first.Distance < second.Distance;
}
}
namespace OctSpace namespace OctSpace
{ {
@@ -123,14 +107,14 @@ bool Child::RayCollides(const Ray& ray, OctSpace::Output& data) const
//If the ray shoots the tree, and it is a parent to 8 children :o //If the ray shoots the tree, and it is a parent to 8 children :o
if (hasChildren()) { if (hasChildren()) {
//Sort children according to their distance from the ray origin. //Sort children according to their distance from the ray origin.
std::vector<ChildInfo> childInfos; std::vector<RaySorterInfo> childInfos;
childInfos.reserve(8); childInfos.reserve(8);
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
childInfos.push_back({ i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Origin()) }); childInfos.push_back({ i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Origin()) });
} }
std::sort(childInfos.begin(), childInfos.end(), isFirstLower); std::sort(childInfos.begin(), childInfos.end(), isFirstLower);
//Loop through the children, starting with the one closest to the ray origin. I.e the first to be hit. //Loop through the children, starting with the one closest to the ray origin. I.e the first to be hit.
for (const ChildInfo& info : childInfos) { for (const RaySorterInfo& info : childInfos) {
if (m_Children[info.Index]->RayCollides(ray, data)) { if (m_Children[info.Index]->RayCollides(ray, data)) {
return true; return true;
} }
@@ -275,4 +259,9 @@ std::vector<int> Child::childIndicesContainingBox(const AABB& box) const
} }
} }
bool isFirstLower(const RaySorterInfo& first, const RaySorterInfo& second)
{
return first.Distance < second.Distance;
}
} }