#include #include #include "Collision/Collision.h" #include "Engine/GLM.h" #include "Core/World.h" #include "Rendering/Model.h" #include "imgui/imgui.h" #include "Core/Octree.h" namespace Collision { //note: this one hasnt been delta adjusted like RayVsAABB has bool RayAABBIntr(const Ray& ray, const AABB& box) { glm::vec3 w = 75.0f * ray.Direction(); glm::vec3 v = glm::abs(w); glm::vec3 c = ray.Origin() - box.Origin() + w; glm::vec3 half = box.HalfSize(); if (abs(c.x) > v.x + half.x) { return false; } if (abs(c.y) > v.y + half.y) { return false; } if (abs(c.z) > v.z + half.z) { return false; } if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) { return false; } if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) { return false; } return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x); } bool RayVsAABB(const Ray& ray, const AABB& box) { float dummy; return RayVsAABB(ray, box, dummy); } bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) { glm::vec3 invdir = 1.0f / ray.Direction(); glm::vec3 origin = ray.Origin(); float t1 = (box.MinCorner().x - origin.x)*invdir.x; float t2 = (box.MaxCorner().x - origin.x)*invdir.x; float t3 = (box.MinCorner().y - origin.y)*invdir.y; float t4 = (box.MaxCorner().y - origin.y)*invdir.y; float t5 = (box.MinCorner().z - origin.z)*invdir.z; float t6 = (box.MaxCorner().z - origin.z)*invdir.z; float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); //if (tmax < 0 || tmin > tmax) //if tmin,tmax are almost the same (i.e. hitting exactly in the corner) then tmin might be slightly //greater than tmax becuase of floating-precision problems. fixed by adding a small delta to tmax if (tmax < 0 || tmin>(tmax + 0.0001f)) return false; outDistance = (tmin > 0) ? tmin : tmax; return true; } bool AABBVsAABB(const AABB& a, const AABB& b) { const glm::vec3& aCenter = a.Origin(); const glm::vec3& bCenter = b.Origin(); const glm::vec3& aHSize = a.HalfSize(); const glm::vec3& bHSize = b.HalfSize(); //Test will probably exit because of the X and Z axes more often, so test them first. if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) { return false; } if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) { return false; } return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); } bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation) { minimumTranslation = glm::vec3(0, 0, 0); const glm::vec3& aMax = a.MaxCorner(); const glm::vec3& bMax = b.MaxCorner(); const glm::vec3& aMin = a.MinCorner(); const glm::vec3& bMin = b.MinCorner(); const glm::vec3& bSize = b.Size(); const glm::vec3& aSize = a.Size(); float minOffset = INFINITY; float off; auto axisesIntersecting = glm::tvec3(false, false, false); for (int i = 0; i < 3; ++i) { off = bMax[i] - aMin[i]; if (off > 0 && off < bSize[i] + aSize[i]) { if (off < minOffset) { minimumTranslation = glm::vec3(); minimumTranslation[i] = minOffset = off; } axisesIntersecting[i] = true; } off = aMax[i] - bMin[i]; if (off > 0 && off < bSize[i] + aSize[i]) { if (off < minOffset) { minOffset = off; minimumTranslation = glm::vec3(); minimumTranslation[i] = -off; } axisesIntersecting[i] = true; } } return glm::all(axisesIntersecting); } bool RayVsTriangle(const Ray& ray, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, bool trueOnNegativeDistance) { glm::vec3 e1 = v1 - v0; //v1 - v0 glm::vec3 e2 = v2 - v0; //v2 - v0 glm::vec3 m = ray.Origin() - v0; glm::vec3 MxE1 = glm::cross(m, e1); glm::vec3 DxE2 = glm::cross(ray.Direction(), e2); float DetInv = glm::dot(e1, DxE2); if (std::abs(DetInv) < FLT_EPSILON) { return false; } DetInv = 1.0f / DetInv; float u = glm::dot(m, DxE2) * DetInv; float v = glm::dot(ray.Direction(), MxE1) * DetInv; //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) { return false; } //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. return trueOnNegativeDistance || 0 <= glm::dot(e2, MxE1) * DetInv; } bool RayVsModel(const Ray& ray, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix) { for (int i = 0; i < modelIndices.size();) { glm::vec3 v0 = TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix); glm::vec3 v1 = TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix); glm::vec3 v2 = TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix); if (RayVsTriangle(ray, v0, v1, v2)) { return true; } } return false; } bool RayVsTriangle(const Ray& ray, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& outDistance, float& outUCoord, float& outVCoord, bool trueOnNegativeDistance) { glm::vec3 e1 = v1 - v0; //v1 - v0 glm::vec3 e2 = v2 - v0; //v2 - v0 glm::vec3 m = ray.Origin() - v0; glm::vec3 MxE1 = glm::cross(m, e1); glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);//pVec float DetInv = glm::dot(e1, DxE2); if (std::abs(DetInv) < FLT_EPSILON) { return false; } DetInv = 1.0f / DetInv; float dist = glm::dot(e2, MxE1) * DetInv; if (dist >= outDistance) { return false; } outDistance = dist; outUCoord = glm::dot(m, DxE2) * DetInv; outVCoord = glm::dot(ray.Direction(), MxE1) * DetInv; //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem //If u and v are positive, u+v <= 1, dist is positive, and less than closest. return (0 <= (outUCoord + 0.001f) && 0 <= (outVCoord + 0.001f) && outUCoord + outVCoord <= 1 && (trueOnNegativeDistance || 0 <= dist)); } bool RayVsModel(const Ray& ray, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, float& outDistance, float& outUCoord, float& outVCoord) { outDistance = INFINITY; bool hit = false; for (int i = 0; i < modelIndices.size();) { glm::vec3 v0 = TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix); glm::vec3 v1 = TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix); glm::vec3 v2 = TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix); float dist = outDistance; float u; float v; if (RayVsTriangle(ray, v0, v1, v2, dist, u, v)) { outDistance = dist; outUCoord = u; outVCoord = v; hit = true; } } return hit; } bool RayVsModel(const Ray& ray, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outHitPosition) { float u; float v; float dist; bool hit = RayVsModel(ray, modelVertices, modelIndices, modelMatrix, dist, u, v); outHitPosition = ray.Origin() + dist * ray.Direction(); return hit; } constexpr inline int signNonZero(float x) { return x < 0 ? -1 : 1; } inline glm::vec3 signNonZero(const glm::vec3& x) { glm::vec3 r; for (int i = 0; i < 3; ++i) { r[i] = (float)signNonZero(x[i]); } return r; } template bool vectorHasLength(const T& vec) { return glm::any(glm::greaterThan(glm::abs(vec), T(0.0001f))); } bool rectangleVsTriangle(const glm::vec2& boxMin, const glm::vec2& boxMax, const std::array& triPos, glm::vec2& resolutionDirection, float& resolutionDistanceSq, bool& pushedFromTriNormal) { pushedFromTriNormal = false; resolutionDistanceSq = INFINITY; //Project along box normals (coordinate axes, since it's axis-aligned). for (int ax = 0; ax < 2; ++ax) { float minTri = INFINITY; float maxTri = -INFINITY; for (const glm::vec2& t : triPos) { minTri = std::min(t[ax], minTri); maxTri = std::max(t[ax], maxTri); } if (boxMax[ax] <= minTri || maxTri <= boxMin[ax]) { return false; } //Here: maxBox > minTri && minBox < maxTri //Left is negative. float leftRes = minTri - boxMax[ax]; float rightRes = maxTri - boxMin[ax]; float push = rightRes < -leftRes ? rightRes : leftRes; float absPushSq = abs(push); absPushSq *= absPushSq; if (absPushSq < resolutionDistanceSq) { resolutionDistanceSq = absPushSq; resolutionDirection[1 - ax] = 0.f; resolutionDirection[ax] = push; } } //Project along triangle normals. //Put edges into normal vector, make normals in the loop. std::array triNormals = { triPos[1] - triPos[0], triPos[2] - triPos[1], triPos[0] - triPos[2] }; std::array boxPos = { boxMax, glm::vec2(boxMax.x, boxMin.y), glm::vec2(boxMin.x, boxMax.y), boxMin }; for (auto& normal : triNormals) { if (!vectorHasLength(normal)) { continue; } //Rotate edge to a normal. normal = glm::normalize(glm::vec2(-normal.y, normal.x)); //Project triangle onto the normal. float minTri = INFINITY; float maxTri = -INFINITY; for (const glm::vec2& point : triPos) { float dot = glm::dot(normal, point); minTri = std::min(dot, minTri); maxTri = std::max(dot, maxTri); } //Project box onto the normal. float minBox = INFINITY; float maxBox = -INFINITY; for (const glm::vec2& point : boxPos) { float dot = glm::dot(normal, point); minBox = std::min(dot, minBox); maxBox = std::max(dot, maxBox); } if (maxBox <= minTri || maxTri <= minBox) { return false; } //Here: maxBox > minTri && minBox < maxTri //Left is negative. float leftRes = minTri - maxBox; float rightRes = maxTri - minBox; float push = rightRes < -leftRes ? rightRes : leftRes; float absPushSq = abs(push); absPushSq *= absPushSq; if (absPushSq < resolutionDistanceSq) { resolutionDistanceSq = absPushSq; resolutionDirection = push * normal; pushedFromTriNormal = true; } } return true; } constexpr float SlopeConstant(float degrees) { return (1.0f - degrees / 90.f); } //Returns true if the angle between horizon and the collision surface is less than 45 degrees. constexpr bool FaceIsGround(float faceNormalY) { //TODO: Perhaps the 45 degrees could be saved in a component or in the config.. return faceNormalY > SlopeConstant(45.0f); } //An array containing 3 int pairs { 0, 2 }, { 0, 1 }, { 1, 2 } constexpr std::array, 3> dimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); enum class BoxTriRes { Front, Behind, Intersect }; BoxTriRes AABBvsTriangle(const AABB& box, const std::array& triPos, const glm::vec3& originalBoxVelocity, float verticalStepHeight, bool& isOnGround, glm::vec3& boxVelocity, glm::vec3& outResolution, bool resolveCollision) { //Check so we don't have a zero area triangle when calculating the normal. //Also, don't check a triangle facing away from the player. //Less checks, and we should be able to walk out from models if we are trapped inside. glm::vec3 triNormal = glm::cross(triPos[1] - triPos[0], triPos[2] - triPos[0]); if (!vectorHasLength(triNormal) || (glm::dot(triNormal, originalBoxVelocity) > 0)) { return BoxTriRes::Behind; } triNormal = glm::normalize(triNormal); enum BoxTriResolveCase { ResolveDimX, ResolveDimY, ResolveDimZ, Line, //Box edge colliding with triangle line. Corner //Box corner colliding with the triangle face. }; struct Resolution { Resolution() : DistanceSq(INFINITY) , Vector(0.f) { } BoxTriResolveCase Case; float DistanceSq; glm::vec3 Vector; }; //The smallest resolution that solves the collision. Resolution resolveShortest; //The smallest resolution that solves the collision, that resolves upwards. Resolution resolveUpwards; //If player stands on the ground and collides with a ground triangle, //we might step up onto it if the step is small enough. bool canStairStepUp = isOnGround && FaceIsGround(triNormal.y); const glm::vec3& origin = box.Origin(); const glm::vec3& half = box.HalfSize(); const glm::vec3& min = box.MinCorner(); const glm::vec3& max = box.MaxCorner(); // If there is no intersection, whether the box center is in front of or behind the triangle. BoxTriRes noIntersection = glm::dot(triNormal, origin - triPos[0]) > 0 ? BoxTriRes::Front : BoxTriRes::Behind; //For each projection in xy-, xz-, and yx-planes. for (std::pair dim : dimensionPairs) { //2D Triangle. //Project triangle. std::array t2D = { glm::vec2(triPos[0][dim.first], triPos[0][dim.second]), glm::vec2(triPos[1][dim.first], triPos[1][dim.second]), glm::vec2(triPos[2][dim.first], triPos[2][dim.second]) }; //Project box. glm::vec2 boxMin(min[dim.first], min[dim.second]); glm::vec2 boxMax(max[dim.first], max[dim.second]); glm::vec2 resolutionVector; float resolutionDist; bool pushedFromTriangleLine; //if projections don't overlap, return false. if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist, pushedFromTriangleLine)) { return noIntersection; } else if (resolveCollision) { //Overwrite the smallest resolution if this is smaller. if (resolutionDist < resolveShortest.DistanceSq) { resolveShortest.Vector = glm::vec3(0.f); resolveShortest.Vector[dim.first] = resolutionVector.x; resolveShortest.Vector[dim.second] = resolutionVector.y; resolveShortest.DistanceSq = resolutionDist; //If we pushed away from triangle line (edge), or if we //move the player along one coordinate axis (pick the dimension that isn't zero). resolveShortest.Case = pushedFromTriangleLine ? Line : static_cast((abs(resolveShortest.Vector[dim.first]) < 0.0001f) ? dim.second : dim.first); } //Overwrite the smallest upward resolution if this is smaller, and resolves upwards. constexpr int yAxis = 1; bool resIsUpwardsIn3D = dim.first == yAxis && resolutionVector.x > 0 || dim.second == yAxis && resolutionVector.y > 0; if (canStairStepUp && resIsUpwardsIn3D && resolutionDist < resolveUpwards.DistanceSq) { resolveUpwards.Vector = glm::vec3(0.f); resolveUpwards.Vector[dim.first] = resolutionVector.x; resolveUpwards.Vector[dim.second] = resolutionVector.y; resolveUpwards.DistanceSq = resolutionDist; //If we pushed away from triangle line (edge), or if we //move the player along one coordinate axis (pick the dimension that isn't zero). resolveUpwards.Case = pushedFromTriangleLine ? Line : static_cast((abs(resolveUpwards.Vector[dim.first]) < 0.0001f) ? dim.second : dim.first); } } } //If the triangle does intersect any of the cube diagonals, it will //intersect the cube diagonal that comes //closest to being perpendicular to the plane of the triangle. glm::vec3 diagonal = signNonZero(triNormal) * half; //The triangle plane contains all points P in dot(triNormal, P) == dot(triNormal, v0) //The diagonal line contains all points P in P = origin + diagonal * t. float t = glm::dot(triNormal, triPos[0] - origin) / glm::dot(triNormal, diagonal); //If intersection point between plane and diagonal is within the box. if (glm::abs(t) > 1) { return noIntersection; } if (!resolveCollision) { return BoxTriRes::Intersect; } glm::vec3 cornerResolution = (1+t) * diagonal; cornerResolution = glm::dot(cornerResolution, triNormal) * triNormal; //Overwrite the smallest resolution if cornerResolution is smaller. float lenSq = glm::length2(cornerResolution); if (lenSq < resolveShortest.DistanceSq) { resolveShortest.Vector = cornerResolution; resolveShortest.Case = Corner; } if (canStairStepUp && cornerResolution.y > 0 && lenSq < resolveUpwards.DistanceSq) { resolveUpwards.Vector = cornerResolution; resolveUpwards.Case = Corner; resolveUpwards.DistanceSq = lenSq; } //Force the resolution upwards if it is smaller than the threshold verticalStepHeight. //Else take the shortest resolution. bool takeUp = resolveUpwards.Vector.y > 0 && resolveUpwards.Vector.y < verticalStepHeight; Resolution& bestResolve = takeUp ? resolveUpwards : resolveShortest; outResolution = bestResolve.Vector; glm::vec3 projNorm; switch (bestResolve.Case) { case ResolveDimY: boxVelocity.y = 0.f; if (outResolution.y > 0) isOnGround = true; case ResolveDimX: case ResolveDimZ: //If we get here, the resolution is along one coordinate axis. //set velocity to 0 in y if it is along y-axis. return BoxTriRes::Intersect; case Line: projNorm = glm::normalize(outResolution); break; case Corner: projNorm = triNormal; break; default: break; } //If the collision was not on steep wall or similarly (e.g. walking on the ground), force resolution in y only. if (FaceIsGround(projNorm.y)) { //Ensure that the player always is moved upwards, instead of sliding down. float len = glm::length(outResolution); float ang = glm::half_pi() - glm::acos(outResolution.y / len); if (len > 0.0000001f && ang > 0.0000001f) { outResolution.x = 0; outResolution.y = len / glm::sin(ang); outResolution.z = 0; } //Also zero the vertical velocity, if it is positive, else project it onto the normal. //Project the velocity onto the normal of the hit line/face. //w = v - *n, |n|==1. boxVelocity.y = std::min(boxVelocity.y - glm::dot(boxVelocity, projNorm) * projNorm.y, 0.f); isOnGround = true; } else { //Enter here if the triangle is a steep slope, and it is not facing downwards. //Project the velocity onto the normal of the hit line/face. //w = v - *n, |n|==1. //"ice cream"-effect, air resistance + projected velocity. if (!isOnGround) { boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; } } return BoxTriRes::Intersect; } Output AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& boxVelocity, float verticalStepHeight, bool& isOnGround, glm::vec3& outResolutionVector, bool resolveCollision) { bool intersect = false; Output out = Output::OutContained; bool everHitTheGround = false; AABB newBox = box; outResolutionVector = glm::vec3(0.f); glm::vec3 originalBoxVelocity(boxVelocity); for (int i = 0; i < modelIndices.size(); ) { std::array triVertices = { TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix), TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix), TransformSystem::TransformPoint(modelVertices[modelIndices[i++]], modelMatrix) }; glm::vec3 outVec; bool collideWithGround = isOnGround; switch (AABBvsTriangle(newBox, triVertices, originalBoxVelocity, verticalStepHeight, collideWithGround, boxVelocity, outVec, resolveCollision)) { case Collision::BoxTriRes::Front: out = Output::OutSeparated; break; case Collision::BoxTriRes::Intersect: intersect = true; outResolutionVector += outVec; newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); if (collideWithGround) { everHitTheGround = isOnGround = true; } break; default: break; } } if (!everHitTheGround) { isOnGround = false; } return intersect ? Output::OutIntersecting : out; } bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& boxVelocity, float verticalStepHeight, bool& isOnGround, glm::vec3& outResolutionVector) { return AABBvsTriangles(box, modelVertices, modelIndices, modelMatrix, boxVelocity, verticalStepHeight, isOnGround, outResolutionVector, true) == Output::OutIntersecting; } bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix) { glm::vec3 vel, outres; bool g; return AABBvsTriangles(box, modelVertices, modelIndices, modelMatrix, vel, 0.f, g, outres, false) == Output::OutIntersecting; } Output AABBvsTrianglesWContainment(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix) { glm::vec3 vel, outres; bool g; return AABBvsTriangles(box, modelVertices, modelIndices, modelMatrix, vel, 0.f, g, outres, false); } boost::optional EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox) { AABB modelSpaceBox; if (entity.HasComponent("AABB") && !takeModelBox) { ComponentWrapper& cAABB = entity["AABB"]; modelSpaceBox = EntityAABB::FromOriginSize((const glm::vec3&)cAABB["Origin"], (const glm::vec3&)cAABB["Size"]); } else if (entity.HasComponent("Model")) { const std::string& res = entity["Model"]["Resource"]; if (res.empty()) { return boost::none; } Model* model; try { model = ResourceManager::Load<::Model, true>(res); } catch (const Resource::StillLoadingException&) { return boost::none; } catch (const std::exception&) { return boost::none; } modelSpaceBox = model->Box(); } else { return boost::none; } glm::mat4 modelMat = TransformSystem::ModelMatrix(entity); glm::vec3 mini(INFINITY); glm::vec3 maxi(-INFINITY); glm::vec3 maxCorner = modelSpaceBox.MaxCorner(); glm::vec3 minCorner = modelSpaceBox.MinCorner(); for (int i = 0; i < 8; ++i) { std::bitset<3> bits(i); glm::vec3 corner; corner.x = bits.test(0) ? maxCorner.x : minCorner.x; corner.y = bits.test(1) ? maxCorner.y : minCorner.y; corner.z = bits.test(2) ? maxCorner.z : minCorner.z; corner = TransformSystem::TransformPoint(corner, modelMat); mini = glm::min(mini, corner); maxi = glm::max(maxi, corner); } EntityAABB aabb; aabb = AABB(mini, maxi); aabb.Entity = entity; return aabb; } boost::optional AbsoluteAABBExplosionEffect(EntityWrapper& entity) { boost::optional modelBox = EntityAbsoluteAABB(entity, true); if (!modelBox) { return boost::none; } Field isRandom = entity["ExplosionEffect"]["Randomness"]; float random = isRandom ? (float)(double)entity["ExplosionEffect"]["RandomnessScalar"] : 0; glm::vec3 origin = (glm::vec3)entity["ExplosionEffect"]["ExplosionOrigin"]; glm::vec3 randomVel = (glm::vec3)entity["ExplosionEffect"]["Velocity"]; randomVel *= (random + 1); float endVelocity = randomVel.y; if ((bool)entity["ExplosionEffect"]["ExponentialAccelaration"]) { endVelocity *= endVelocity / 2.f; } float maxRadius = (float)(double)entity["ExplosionEffect"]["ExplosionDuration"] * endVelocity; glm::vec3 size; AABB explosionBox(origin - (size / 2.f), origin + (size / 2.f)); glm::vec3 mini = glm::min(explosionBox.MinCorner(), (*modelBox).MinCorner()); glm::vec3 maxi = glm::max(explosionBox.MaxCorner(), (*modelBox).MaxCorner()); EntityAABB aabb = AABB(mini, maxi); aabb.Entity = entity; return aabb; } boost::optional EntityFirstHitByRay(const Ray& ray, std::vector entitiesPotentiallyHitSorted, float& outDistance, glm::vec3& outIntersectPos) { for (EntityAABB& entityBox : entitiesPotentiallyHitSorted) { if (!entityBox.Entity.HasComponent("Model")) { continue; } auto& cModel = entityBox.Entity["Model"]; std::string res = cModel["Resource"]; if (res.empty() || (bool)cModel["Transparent"] || !((bool)cModel["Visible"])) { continue; } Model* model; try { model = ResourceManager::Load<::Model, true>(res); } catch (const std::exception&) { continue; } float u, v; if (RayVsModel(ray, model->m_Vertices, model->m_Indices, TransformSystem::ModelMatrix(entityBox.Entity), outDistance, u, v)) { outIntersectPos = ray.Origin() + outDistance * ray.Direction(); return entityBox; } } return boost::none; } boost::optional EntityFirstHitByRay(const Ray& ray, Octree* octree, float& outDistance, glm::vec3& outIntersectPos) { std::vector outObjects; octree->ObjectsPossiblyHitByRay(ray, outObjects); return Collision::EntityFirstHitByRay(ray, outObjects, outDistance, outIntersectPos); } }