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; } }
bool CDynamics2DCylinderEntity::CheckIntersectionWithRay(Real& f_t_on_ray, const CRay& c_ray) const { cpSegmentQueryInfo tInfo; if(cpShapeSegmentQuery(m_ptShape, cpv(c_ray.GetStart().GetX(), c_ray.GetStart().GetY()), cpv(c_ray.GetEnd().GetX() , c_ray.GetEnd().GetY() ), &tInfo)) { CVector3 cIntersectionPoint; c_ray.GetPoint(cIntersectionPoint, tInfo.t); if((cIntersectionPoint.GetZ() >= GetEmbodiedEntity().GetPosition().GetZ() - m_fHalfHeight) && (cIntersectionPoint.GetZ() <= GetEmbodiedEntity().GetPosition().GetZ() + m_fHalfHeight) ) { f_t_on_ray = tInfo.t; return true; } else { return false; } } else { return false; } }
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; }
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; }