bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& start, const glm::vec3& end, float radius, float& distance) { if (start == end) { return findRaySphereIntersection(origin, direction, start, radius, distance); // handle degenerate case } glm::vec3 relativeOrigin = origin - start; glm::vec3 relativeEnd = end - start; float capsuleLength = glm::length(relativeEnd); relativeEnd /= capsuleLength; float originProjection = glm::dot(relativeEnd, relativeOrigin); glm::vec3 constant = relativeOrigin - relativeEnd * originProjection; float c = glm::dot(constant, constant) - radius * radius; if (c < 0.0f) { // starts inside cylinder if (originProjection < 0.0f) { // below start return findRaySphereIntersection(origin, direction, start, radius, distance); } else if (originProjection > capsuleLength) { // above end return findRaySphereIntersection(origin, direction, end, radius, distance); } else { // between start and end distance = 0.0f; return true; } } glm::vec3 coefficient = direction - relativeEnd * glm::dot(relativeEnd, direction); float a = glm::dot(coefficient, coefficient); if (a == 0.0f) { return false; // parallel to enclosing cylinder } float b = 2.0f * glm::dot(constant, coefficient); float radicand = b * b - 4.0f * a * c; if (radicand < 0.0f) { return false; // doesn't hit the enclosing cylinder } float t = (-b - sqrtf(radicand)) / (2.0f * a); if (t < 0.0f) { return false; // doesn't hit the enclosing cylinder } glm::vec3 intersection = relativeOrigin + direction * t; float intersectionProjection = glm::dot(relativeEnd, intersection); if (intersectionProjection < 0.0f) { // below start return findRaySphereIntersection(origin, direction, start, radius, distance); } else if (intersectionProjection > capsuleLength) { // above end return findRaySphereIntersection(origin, direction, end, radius, distance); } distance = t; // between start and end return true; }
bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { // determine the ray in the frame of the entity transformed from a unit sphere glm::mat4 translation = glm::translate(getPosition()); glm::mat4 rotation = glm::mat4_cast(getRotation()); glm::mat4 scale = glm::scale(getDimensions()); glm::mat4 registration = glm::translate(glm::vec3(0.5f, 0.5f, 0.5f) - getRegistrationPoint()); glm::mat4 entityToWorldMatrix = translation * rotation * scale * registration; glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); float localDistance; // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) { // determine where on the unit sphere the hit point occured glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); // then translate back to work coordinates glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); distance = glm::distance(origin,hitAt); return true; } return false; }
bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const { // determine the ray in the frame of the entity transformed from a unit sphere glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix(); glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); float localDistance; // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) { // determine where on the unit sphere the hit point occured glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); // then translate back to work coordinates glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); distance = glm::distance(origin, hitAt); bool success; surfaceNormal = glm::normalize(hitAt - getCenterPosition(success)); if (!success) { return false; } return true; } return false; }