コード例 #1
0
ファイル: Collisions.cpp プロジェクト: Cheesezi/FPS
bool CheckRay(const CRay& ray, const CBoundingBox& bb, Vector3* hitPoint)
{
    //This algorithm is adapted from: http://tavianator.com/fast-branchless-raybounding-box-intersections/
    //Its based on the "Slab Technique" which treats a bounding box as a series of 2 planes in each axis.
    //It finds where the ray lies on each of these planes and compares those values across each axis.
	float tmin = -INFINITY;
	float tmax = INFINITY;

	float tx1 = (bb.GetMin().x - ray.GetOrigin().x) / ray.GetDirection().x;
	float tx2 = (bb.GetMax().x - ray.GetOrigin().x) / ray.GetDirection().x;

	tmin = max(tmin, min(tx1, tx2));
	tmax = min(tmax, max(tx1, tx2));
	
	float ty1 = (bb.GetMin().y - ray.GetOrigin().y) / ray.GetDirection().y;
	float ty2 = (bb.GetMax().y - ray.GetOrigin().y) / ray.GetDirection().y;

	tmin = max(tmin, min(ty1, ty2));
	tmax = min(tmax, max(ty1, ty2));
	
	float tz1 = (bb.GetMin().z - ray.GetOrigin().z) / ray.GetDirection().z;
	float tz2 = (bb.GetMax().z - ray.GetOrigin().z) / ray.GetDirection().z;

	tmin = max(tmin, min(tz1, tz2));
	tmax = min(tmax, max(tz1, tz2));
	
    //We end up with two hit values just like the sphere, if the max hit is less than 0 then it all happened behind the ray origin
	if (tmax < 0)
		return false;

    //If the max hit value is greater than the min hit (as it should be) then we have a hit!
	if (tmax >= tmin)
	{
		if (hitPoint)
		{
            //If we need to know the first hit point on the box then we should use tmin, unless it's negative then we use tmax.
            //This would come up in situations where the origin of the ray is within the box.
			float firstHitDistance = tmin < 0 ? tmax : tmin;
			Vector3 pointOnBox = (ray.GetDirection() * firstHitDistance) + ray.GetOrigin();
		
			hitPoint->x = pointOnBox.x;
			hitPoint->y = pointOnBox.y;
			hitPoint->z = pointOnBox.z;
		}
		return true;
	}
	return false;
}
コード例 #2
0
 bool CDynamics3DEntity::CheckIntersectionWithRay(Real& f_t_on_ray,
                                                  const CRay& c_ray) const {
    /* Create an ODE ray from ARGoS ray */
    Real fRayLength = c_ray.GetLength();
    dGeomID tRay = dCreateRay(m_cEngine.GetSpaceID(), fRayLength);
    CVector3 cDirection;
    c_ray.GetDirection(cDirection);
    dGeomRaySet(tRay,
                c_ray.GetStart().GetX(),
                c_ray.GetStart().GetY(),
                c_ray.GetStart().GetZ(),
                cDirection.GetX(),
                cDirection.GetY(),
                cDirection.GetZ());
    /* Create the structure to contain info about the possible
       ray/geom intersection */
    dContactGeom tIntersection;
    /* Check for intersection between the ray and the object local space */
    if(dCollide(tRay, reinterpret_cast<dGeomID>(m_tEntitySpace), 1, &tIntersection, sizeof(dContactGeom)) > 0) {
       /* There is an intersecton */
       f_t_on_ray = tIntersection.depth / fRayLength;
       return true;
    }
    else {
       /* No intersection detected */
       return false;
    }
 }
コード例 #3
0
ファイル: Collisions.cpp プロジェクト: Cheesezi/FPS
bool CheckRay(const CRay& ray, const CBoundingSphere& sphere, Vector3* hitPoint)
{
    //This algorithm is adatped from: http://www.cosinekitty.com/raytrace/chapter06_sphere.html
    //It breaks the problem down into the plane equations needed to find the hit points between the ray and sphere
    //To solve this correctly it makes use of a quadratic equation.
	Vector3 displacement = ray.GetOrigin() - sphere.GetCenter();
	float a = ray.GetDirection().LengthSquared();
	float b = 2.0f * displacement.Dot(ray.GetDirection());
	float c = displacement.LengthSquared() - sphere.GetRadius() * sphere.GetRadius();

	float randicand = b*b - 4.0f * a * c;
	//If the quadratic equation comes back as a negitive then there is no hit.
    if (randicand < 0.0f)
	{
		return false;
	}
	

	float root = sqrt(randicand);
	float denom = 2.0 * a;

    //Here we calculate the distance between ray origin and the two hit points (where the ray enters the sphere and where it exits)
	float hit1 = (-b + root) / denom;
	float hit2 = (-b - root) / denom;

    //If both of the hits are negitive then it means that the sphere is behind the origin of the ray so there is no hit
	if (hit1 < 0 && hit2 < 0)
	{
		return false;
	}

	
	if (hitPoint)
	{
        //If we need to know the first hit point on the sphere then we should use hit1, unless it's negative then we use hit2.
        //This would come up in situations where the origin of the ray is within the sphere.
        float firstHitDistance = hit1 < 0 ? hit2 : hit1;
		Vector3 pointOnSphere = (ray.GetDirection() * firstHitDistance) + ray.GetOrigin();
		hitPoint->x = pointOnSphere.x;
		hitPoint->y = pointOnSphere.y;
		hitPoint->z = pointOnSphere.z;
	}
	return true;
}
コード例 #4
0
ファイル: Ray.cpp プロジェクト: 7kia/CG
bool CPlane::Hit(const CRay &ray, SRayIntersection &intersection) const
{
    // Величина, меньше которой модуль скалярного произведения вектора направления луча и
    // нормали плоскости означает параллельность луча и плоскости
    const float EPSILON = std::numeric_limits<float>::epsilon();

    // Нормаль к плоскости в системе координат объекта
    const glm::vec3 normalInObjectSpace(m_planeEquation);

    // Скалярное произведение направления луча и нормали к плоскости
    const float normalDotDirection = glm::dot(ray.GetDirection(), normalInObjectSpace);

    // Если скалярное произведение близко к нулю, луч параллелен плоскости
    if (fabs(normalDotDirection) < EPSILON)
    {
        return false;
    }

    /*
    Находим время пересечения луча с плоскостью, подставляя в уравнение плоскости точку испускания луча
    и деление результата на ранее вычисленное сканярное произведение направления луча и нормали к плоскости
    */
    const float hitTime = -glm::dot(glm::vec4(ray.GetStart(), 1), m_planeEquation) / normalDotDirection;

    // Нас интересует только пересечение луча с плоскостью в положительный момент времени,
    // поэтому находящуюся "позади" точки испускания луча точку пересечения мы за точку пересечения не считаем
    // Сравнение с величиной EPSILON, а не с 0 нужно для того, чтобы позволить
    // лучам, испущенным с плоскости, оторваться от нее.
    // Это необходимо при обработке вторичных лучей для построения теней и отражений и преломлений
    if (hitTime <= EPSILON)
    {
        return false;
    }

    // Вычисляем точку столкновения с лучом в системе координат сцены в момент столкновения
    const glm::vec3 hitPoint = ray.GetPointAtTime(hitTime);
    intersection.m_time = hitTime;
    intersection.m_point = hitPoint;

    return true;
}
コード例 #5
0
bool CTriangleMesh::Hit(CRay const& ray, CIntersection & intersection) const
{
	// Вычисляем обратно преобразованный луч (вместо вполнения прямого преобразования объекта)
	CRay invRay = Transform(ray, GetInverseTransform());
	CVector3d const& invRayStart = invRay.GetStart();
	CVector3d const& invRayDirection = invRay.GetDirection();

	//////////////////////////////////////////////////////////////////////////
	// Здесь следует выполнить проверку на пересечение луча с ограничивающим
	// объемом (bounding volume) полигональной сетки.
	// При отсутствии такого пересечения луч гарантированно не будет пересекать
	// ни одну из граней сетки, что позволит избежать лишних проверок:
	// if (!ray_intesects_bounding_volume)
	//     return false;
	//////////////////////////////////////////////////////////////////////////
	
	// Получаем информацию о массиве треугольников сетки
	CTriangle const* const triangles = m_pMeshData->GetTriangles();
	const size_t numTriangles = m_pMeshData->GetTriangleCount();

	// Массив найденных пересечений луча с гранями сетки.
	std::vector<FaceHit> faceHits;

	//////////////////////////////////////////////////////////////////////////
	// Используется поиск пересечения луча со всеми гранями сетки.
	// Данный подход является очень неэффективным уже на полигональных сетках,
	// содержащих более нескольких десятков граней, а, тем более, сотни и тысячи граней.
	//
	// Для эффективного поиска столкновений следует представить полигональную сетку
	// не в виде массива граней, а в виде древовидной структуры (Oct-Tree или BSP-Tree),
	// что уменьшит вычислительную сложность поиска столкновений с O(N) до O(log N)
	//////////////////////////////////////////////////////////////////////////
	FaceHit hit;
	for (size_t i = 0; i < numTriangles; ++i)
	{
		CTriangle const& triangle = triangles[i];

		// Проверка на пересечение луча с треугольной гранью
		if (triangle.HitTest(invRayStart, invRayDirection, hit.hitTime, hit.hitPointInObjectSpace, hit.w0, hit.w1, hit.w2))
		{
			// Сохраняем индекс грани и добавляем информацию в массив найденных пересечений
			hit.faceIndex = i;

			if (faceHits.empty())
			{
				// При обнаружени первого пересечения резервируем
				// память сразу под 8 пересечений (для уменьшения количества операций выделения памяти)
				faceHits.reserve(8);
			}
			faceHits.push_back(hit);
		}
	}

	// При отсутствии пересечений выходим
	if (faceHits.empty())
	{
		return false;
	}

	//////////////////////////////////////////////////////////////////////////
	// Упорядочиваем найденные пересечения по возрастанию времени столкновения
	//////////////////////////////////////////////////////////////////////////
	size_t const numHits = faceHits.size();
	std::vector<FaceHit const *> hitPointers(numHits);
	{
		// Инициализируем массив указателей на точки пересечения
		for (size_t i = 0; i < numHits; ++i)
		{
			hitPointers[i] = &faceHits[i];
		}

		// Сортируем массив указателей по возрастанию времени столкновения
		// Сортируются указатели, а не сами объекты, чтобы сократить
		// издержки на обмен элементов массива:
		// На 32 битной платформе размер структуры FaceHit равен 64 байтам,
		// а размер указателя - всего 4 байта.
		if (numHits > 1)
		{
			std::sort(hitPointers.begin(), hitPointers.end(), HitPointerComparator());
			// Теперь указатели в массиве hitPointers отсортированы
			// в порядке возрастания времени столкновения луча с гранями
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Возвращаем информацию о найденных пересечениях
	//////////////////////////////////////////////////////////////////////////
	for (size_t i = 0; i < numHits; ++i)
	{
		// Получаем информацию о столкновении
		FaceHit const& faceHit = *hitPointers[i];

		// Сохраняем информацию только о первом пересечении
		if (i == 0)
		{
			ExtendedHitData *hd = (ExtendedHitData*)&m_lastHitData;
			hd->faceIndex = faceHit.faceIndex;
			hd->vc[0] = static_cast<float>(faceHit.w0);
			hd->vc[1] = static_cast<float>(faceHit.w1);
			hd->vc[2] = static_cast<float>(faceHit.w2);
		}

		// Точка столкновения в мировой системе координат
		CVector3d hitPoint = ray.GetPointAtTime(faceHit.hitTime);

		// Грань, с которой произошло столкновение
		CTriangle const& triangle = triangles[faceHit.faceIndex];

		// Нормаль "плоской грани" во всех точках столкновения равна нормали самой грани
		CVector3d normalInObjectSpace = triangle.GetPlaneEquation();

		if (!triangle.IsFlatShaded())
		{
			// Для неплоских граней выполняется интерполяция нормалей вершин треугольника
			// с учетом их весовых коэффициентов в точке пересечения
			Vertex const& v0 = triangle.GetVertex0();
			Vertex const& v1 = triangle.GetVertex1();
			Vertex const& v2 = triangle.GetVertex2();

			// Взвешенный вектор нормали
			normalInObjectSpace = 
				faceHit.w0 * v0.normal + 
				faceHit.w1 * v1.normal +
				faceHit.w2 * v2.normal;
		}

		// Нормаль в мировой системе координат
		CVector3d normal = GetNormalMatrix() * normalInObjectSpace;

		// Добавляем информацию о точке пересечения в объект intersection
		intersection.AddHit(
			CHitInfo(
				faceHit.hitTime, *this, 
				hitPoint,
				faceHit.hitPointInObjectSpace,
				normal, normalInObjectSpace
				)
			);
	}

	return true;
}