// Copyright (C) 2014 Jérôme Leclercq // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #define F(a) static_cast(a) template NzRay::NzRay(T X, T Y, T Z, T DirectionX, T DirectionY, T DirectionZ) { Set(X, Y, Z, DirectionX, DirectionY, DirectionZ); } template NzRay::NzRay(const T Origin[3], const T Direction[3]) { Set(Origin, Direction); } template NzRay::NzRay(const NzVector3& Origin, const NzVector3& Direction) { Set(Origin, Direction); } template NzRay::NzRay(const NzPlane& planeOne, const NzPlane& planeTwo) { Set(planeOne, planeTwo); } template template NzRay::NzRay(const NzVector3& Origin, const NzVector3& Direction) { Set(Origin, Direction); } template template NzRay::NzRay(const NzRay& ray) { Set(ray); } template NzVector3 NzRay::GetClosestPoint(const NzVector3& point) const { NzVector3 delta = point - origin; T vsq = direction.GetSquaredLength(); T proj = delta.DotProduct(direction); return GetPoint(proj/vsq); } template NzVector3 NzRay::GetPoint(T lambda) const { return NzVector3(origin + direction * lambda); } template bool NzRay::Intersect(const NzBox& box, NzVector3 * hitPoint, NzVector3 * hitSecondPoint) const { // Slab method #if NAZARA_MATH_SAFE if (NzNumberEquals(direction.x, F(0.0)) || NzNumberEquals(direction.y, F(0.0)) || NzNumberEquals(direction.z, F(0.0))) { NazaraWarning("Division by zero !"); // The algorithm is still correct. } #endif T tx1 = (box.x - origin.x) / direction.x; T tx2 = (box.x + box.width - origin.x) / direction.x; T tmin = std::min(tx1, tx2); T tmax = std::max(tx1, tx2); T ty1 = (box.y - origin.y) / direction.y; T ty2 = (box.y + box.height - origin.y) / direction.y; tmin = std::max(tmin, std::min(ty1, ty2)); tmax = std::min(tmax, std::max(ty1, ty2)); T tz1 = (box.z - origin.z) / direction.z; T tz2 = (box.z + box.depth - origin.z) / direction.z; tmin = std::max(tmin, std::min(tz1, tz2)); tmax = std::min(tmax, std::max(tz1, tz2)); if (hitPoint) hitPoint->Set(GetPoint(tmin)); if (hitSecondPoint) hitSecondPoint->Set(GetPoint(tmax)); return tmax >= std::max(F(0.0), tmin) && tmin < INFINITY; } template bool NzRay::Intersect(const NzOrientedBox& orientedBox, NzVector3 * hitPoint, NzVector3 * hitSecondPoint) const { NzVector3 width = (orientedBox.GetCorner(nzCorner_NearLeftBottom) - orientedBox.GetCorner(nzCorner_FarLeftBottom)).Normalize(); NzVector3 height = (orientedBox.GetCorner(nzCorner_FarLeftTop) - orientedBox.GetCorner(nzCorner_FarLeftBottom)).Normalize(); NzVector3 depth = (orientedBox.GetCorner(nzCorner_FarRightBottom) - orientedBox.GetCorner(nzCorner_FarLeftBottom)).Normalize(); // Construction of the inverse of the matrix who did the rotation -> orthogonal matrix. NzMatrix4 transformation(width.x, height.x, depth.x, F(0.0), width.y, height.y, depth.y, F(0.0), width.z, height.z, depth.z, F(0.0), F(0.0), F(0.0), F(0.0), F(1.0)); // Reduction to aabb problem NzVector3 newOrigin = transformation.Transform(origin); NzVector3 newDirection = transformation.Transform(direction); NzVector3 tmp, tmp2; if (NzRay(newOrigin, newDirection).Intersect(NzBox(orientedBox.GetCorner(nzCorner_NearRightTop), orientedBox.GetCorner(nzCorner_FarLeftBottom)), &tmp, &tmp2)) { if (hitPoint) { transformation.Transpose(); hitPoint->Set(transformation.Transform(tmp)); if (hitSecondPoint) hitSecondPoint->Set(transformation.Transform(tmp2)); } return true; } return false; } template bool NzRay::Intersect(const NzPlane& plane, NzVector3 * hitPoint) const { T divisor = plane.normal.DotProduct(direction); if (NzNumberEquals(divisor, F(0.0))) return false; // perpendicular if (!hitPoint) return true; T lambda = - (plane.normal.DotProduct(origin) - plane.distance) / divisor; // The plane is ax+by+cz=d hitPoint->Set(GetPoint(lambda)); return true; } template bool NzRay::Intersect(const NzSphere& sphere, NzVector3 * hitPoint, NzVector3 * hitSecondPoint) const { NzVector3 distanceCenterOrigin = sphere.GetPosition() - origin; T length = distanceCenterOrigin.DotProduct(direction); if (length < F(0.0)) return false; // ray is perpendicular to the vector origin - center T squaredDistance = distanceCenterOrigin.GetSquaredLength() - length * length; T squaredRadius = sphere.GetRadius() * sphere.GetRadius(); if (squaredDistance > squaredRadius) return false; // if the ray is further than the radius if (!hitPoint) return true; T deltaLambda = std::sqrt(squaredRadius - squaredDistance); if (hitPoint) hitPoint->Set(GetPoint(length - deltaLambda)); if (hitSecondPoint) hitSecondPoint->Set(GetPoint(length + deltaLambda)); return true; } template NzVector3 NzRay::operator*(T lambda) const { return GetPoint(lambda); } template NzRay& NzRay::Set(T X, T Y, T Z, T directionX, T directionY, T directionZ) { direction = NzVector3(directionX, directionY, directionZ); origin = NzVector3(X, Y, Z); return *this; } template NzRay& NzRay::Set(const T Origin[3], const T Direction[3]) { direction = NzVector3(Direction); origin = NzVector3(Origin); return *this; } template NzRay& NzRay::Set(const NzVector3& Origin, const NzVector3& Direction) { direction = Direction; origin = Origin; return *this; } template NzRay& NzRay::Set(const NzPlane& planeOne, const NzPlane& planeTwo) { T termOne = planeOne.normal.GetLength(); T termTwo = planeOne.normal.DotProduct(planeTwo.normal); T termFour = planeTwo.normal.GetLength(); T det = termOne * termFour - termTwo * termTwo; #if NAZARA_MATH_SAFE if (NzNumberEquals(det, F(0.0))) { NzString error("Planes are parallel."); NazaraError(error); throw std::domain_error(error); } #endif T invdet = F(1.0) / det; T fc0 = (termFour * -planeOne.distance + termTwo * planeTwo.distance) * invdet; T fc1 = (termOne * -planeTwo.distance + termTwo * planeOne.distance) * invdet; direction = planeOne.normal.CrossProduct(planeTwo.normal); origin = planeOne.normal * fc0 + planeTwo.normal * fc1; return *this; } template template NzRay& NzRay::Set(const NzVector3& Origin, const NzVector3& Direction) { direction = NzVector3(Direction); origin = NzVector3(Origin); return *this; } template template NzRay& NzRay::Set(const NzRay& ray) { direction = NzVector3(ray.direction); origin = NzVector3(ray.origin); return *this; } template NzRay& NzRay::Set(const NzRay& ray) { std::memcpy(this, &ray, sizeof(NzRay)); return *this; } template NzRay& NzRay::SetDirection(const NzVector3& Direction) { direction = Direction; return *this; } template NzRay& NzRay::SetOrigin(const NzVector3& Origin) { origin = Origin; return *this; } template NzString NzRay::ToString() const { NzStringStream ss; return ss << "Ray(origin: " << origin.ToString() << ", direction: " << direction.ToString() << ")"; } template NzRay NzRay::Lerp(const NzRay& from, const NzRay& to, T interpolation) { return NzRay(from.origin.Lerp(to.origin, interpolation), from.direction.Lerp(to.direction, interpolation)); } template NzRay NzRay::UnitX() { return NzRay(NzVector3::Zero(), NzVector3::UnitX()); } template NzRay NzRay::UnitY() { return NzRay(NzVector3::Zero(), NzVector3::UnitY()); } template NzRay NzRay::UnitZ() { return NzRay(NzVector3::Zero(), NzVector3::UnitZ()); } template std::ostream& operator<<(std::ostream& out, const NzRay& ray) { return out << ray.ToString(); } #undef F #include