void Octree::CollectNodes(Vector<Pair<OctreeNode*, float> >& result, const Octant* octant, const Ray& ray, unsigned short nodeFlags, float maxDistance, unsigned layerMask) const { float octantDist = ray.HitDistance(octant->cullingBox); if (octantDist >= maxDistance) return; const Vector<OctreeNode*>& octantNodes = octant->nodes; for (auto it = octantNodes.Begin(); it != octantNodes.End(); ++it) { OctreeNode* node = *it; if ((node->Flags() & nodeFlags) == nodeFlags && (node->LayerMask() & layerMask)) { float distance = ray.HitDistance(node->WorldBoundingBox()); if (distance < maxDistance) result.Push(MakePair(node, distance)); } } for (size_t i = 0; i < NUM_OCTANTS; ++i) { if (octant->children[i]) CollectNodes(result, octant->children[i], ray, nodeFlags, maxDistance, layerMask); } }
void Light::OnRaycast(Vector<RaycastResult>& dest, const Ray& ray, float maxDistance) { if (lightType == LIGHT_SPOT) { float distance = ray.HitDistance(WorldFrustum()); if (distance <= maxDistance) { RaycastResult res; res.position = ray.origin + distance * ray.direction; res.normal = -ray.direction; res.distance = distance; res.node = this; res.subObject = 0; dest.Push(res); } } else if (lightType == LIGHT_POINT) { float distance = ray.HitDistance(WorldSphere()); if (distance <= maxDistance) { RaycastResult res; res.position = ray.origin + distance * ray.direction; res.normal = -ray.direction; res.distance = distance; res.node = this; res.subObject = 0; dest.Push(res); } } }
float Geometry::GetHitDistance(const Ray& ray, Vector3* outNormal, Vector2* outUV) const { const unsigned char* vertexData; const unsigned char* indexData; unsigned vertexSize; unsigned indexSize; const PODVector<VertexElement>* elements; GetRawData(vertexData, vertexSize, indexData, indexSize, elements); if (!vertexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0) return M_INFINITY; unsigned uvOffset = VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR2, SEM_TEXCOORD); if (outUV && uvOffset == M_MAX_UNSIGNED) { // requested UV output, but no texture data in vertex buffer ATOMIC_LOGWARNING("Illegal GetHitDistance call: UV return requested on vertex buffer without UV coords"); *outUV = Vector2::ZERO; outUV = 0; } return indexData ? ray.HitDistance(vertexData, vertexSize, indexData, indexSize, indexStart_, indexCount_, outNormal, outUV, uvOffset) : ray.HitDistance(vertexData, vertexSize, vertexStart_, vertexCount_, outNormal, outUV, uvOffset); }
void TerrainPatch::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { RayQueryLevel level = query.level_; switch (level) { case RAY_AABB_NOSUBOBJECTS: case RAY_AABB: Drawable::ProcessRayQuery(query, results); break; case RAY_OBB: case RAY_TRIANGLE: Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); float distance = localRay.HitDistance(boundingBox_); if (level == RAY_TRIANGLE && distance < query.maxDistance_) distance = geometry_->GetHitDistance(localRay); if (distance < query.maxDistance_) { RayQueryResult result; result.drawable_ = this; result.node_ = node_; result.distance_ = distance; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); } break; } }
float Geometry::GetHitDistance(const Ray& ray, Vector3* outNormal) const { const unsigned char* vertexData; const unsigned char* indexData; unsigned vertexSize; unsigned indexSize; unsigned elementMask; GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask); if (vertexData && indexData) return ray.HitDistance(vertexData, vertexSize, indexData, indexSize, indexStart_, indexCount_, outNormal); else if (vertexData) return ray.HitDistance(vertexData, vertexSize, vertexStart_, vertexCount_, outNormal); else return M_INFINITY; }
void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { RayQueryLevel level = query.level_; switch (level) { case RAY_AABB: Drawable::ProcessRayQuery(query, results); break; case RAY_OBB: case RAY_TRIANGLE: { Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); float distance = localRay.HitDistance(boundingBox_); Vector3 normal = -query.ray_.direction_; if (level == RAY_TRIANGLE && distance < query.maxDistance_) { distance = M_INFINITY; for (unsigned i = 0; i < batches_.Size(); ++i) { Geometry* geometry = batches_[i].geometry_; if (geometry) { Vector3 geometryNormal; float geometryDistance = geometry->GetHitDistance(localRay, &geometryNormal); if (geometryDistance < query.maxDistance_ && geometryDistance < distance) { distance = geometryDistance; normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized(); } } } } if (distance < query.maxDistance_) { RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = normal; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); } } break; case RAY_TRIANGLE_UV: ATOMIC_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for CustomGeometry component"); break; } }
void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { RayQueryLevel level = query.level_; switch (level) { case RAY_AABB: Drawable::ProcessRayQuery(query, results); break; case RAY_OBB: case RAY_TRIANGLE: case RAY_TRIANGLE_UV: Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); float distance = localRay.HitDistance(boundingBox_); Vector3 normal = -query.ray_.direction_; Vector2 geometryUV; unsigned hitBatch = M_MAX_UNSIGNED; if (level >= RAY_TRIANGLE && distance < query.maxDistance_) { distance = M_INFINITY; for (unsigned i = 0; i < batches_.Size(); ++i) { Geometry* geometry = batches_[i].geometry_; if (geometry) { Vector3 geometryNormal; float geometryDistance = level == RAY_TRIANGLE ? geometry->GetHitDistance(localRay, &geometryNormal) : geometry->GetHitDistance(localRay, &geometryNormal, &geometryUV); if (geometryDistance < query.maxDistance_ && geometryDistance < distance) { distance = geometryDistance; normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized(); hitBatch = i; } } } } if (distance < query.maxDistance_) { RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = normal; result.textureUV_ = geometryUV; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = hitBatch; results.Push(result); } break; } }
void StaticModelGroup::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // If no bones or no bone-level testing, use the Drawable test RayQueryLevel level = query.level_; if (level < RAY_AABB) { Drawable::ProcessRayQuery(query, results); return; } // Check ray hit distance to AABB before proceeding with more accurate tests // GetWorldBoundingBox() updates the world transforms if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_) return; for (unsigned i = 0; i < numWorldTransforms_; ++i) { // Initial test using AABB float distance = query.ray_.HitDistance(boundingBox_.Transformed(worldTransforms_[i])); // Then proceed to OBB and triangle-level tests if necessary if (level >= RAY_OBB && distance < query.maxDistance_) { Matrix3x4 inverse = worldTransforms_[i].Inverse(); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(boundingBox_); if (level == RAY_TRIANGLE && distance < query.maxDistance_) { distance = M_INFINITY; for (unsigned j = 0; j < batches_.Size(); ++j) { Geometry* geometry = batches_[j].geometry_; if (geometry) { float geometryDistance = geometry->GetHitDistance(localRay); if (geometryDistance < query.maxDistance_ && geometryDistance < distance) distance = geometryDistance; } } } } if (distance < query.maxDistance_) { RayQueryResult result; result.drawable_ = this; result.node_ = node_; result.distance_ = distance; result.subObject_ = i; results.Push(result); } } }
void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // Do not record a raycast result for a directional light, as it would block all other results if (lightType_ == LIGHT_DIRECTIONAL) return; float distance = query.maxDistance_; switch (query.level_) { case RAY_AABB: Drawable::ProcessRayQuery(query, results); return; case RAY_OBB: { Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(GetWorldBoundingBox().Transformed(inverse)); if (distance >= query.maxDistance_) return; } break; case RAY_TRIANGLE: if (lightType_ == LIGHT_SPOT) { distance = query.ray_.HitDistance(GetFrustum()); if (distance >= query.maxDistance_) return; } else { distance = query.ray_.HitDistance(Sphere(node_->GetWorldPosition(), range_)); if (distance >= query.maxDistance_) return; } break; case RAY_TRIANGLE_UV: LOGWARNING("RAY_TRIANGLE_UV query level is not supported for Light component"); return; } // If the code reaches here then we have a hit RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = -query.ray_.direction_; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); }
void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { RayQueryLevel level = query.level_; switch (level) { case RAY_AABB_NOSUBOBJECTS: case RAY_AABB: Drawable::ProcessRayQuery(query, results); break; case RAY_OBB: case RAY_TRIANGLE: Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); float distance = localRay.HitDistance(boundingBox_); if (distance < query.maxDistance_) { if (level == RAY_TRIANGLE) { for (unsigned i = 0; i < batches_.Size(); ++i) { Geometry* geometry = batches_[i].geometry_; if (geometry) { distance = geometry->GetHitDistance(localRay); if (distance < query.maxDistance_) { RayQueryResult result; result.drawable_ = this; result.node_ = node_; result.distance_ = distance; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); break; } } } } else { RayQueryResult result; result.drawable_ = this; result.node_ = node_; result.distance_ = distance; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); } } break; } }
void OctreeNode::OnRaycast(Vector<RaycastResult>& dest, const Ray& ray, float maxDistance) { float distance = ray.HitDistance(WorldBoundingBox()); if (distance < maxDistance) { RaycastResult res; res.position = ray.origin + distance * ray.direction; res.normal = -ray.direction; res.distance = distance; res.node = this; res.subObject = 0; dest.Push(res); } }
void TerrainPatch::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { RayQueryLevel level = query.level_; switch (level) { case RAY_AABB: Drawable::ProcessRayQuery(query, results); break; case RAY_OBB: case RAY_TRIANGLE: { Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); float distance = localRay.HitDistance(boundingBox_); Vector3 normal = -query.ray_.direction_; if (level == RAY_TRIANGLE && distance < query.maxDistance_) { Vector3 geometryNormal; distance = geometry_->GetHitDistance(localRay, &geometryNormal); normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized(); } if (distance < query.maxDistance_) { RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = normal; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); } } break; case RAY_TRIANGLE_UV: URHO3D_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for TerrainPatch component"); break; } }
void Octree::CollectNodes(Vector<RaycastResult>& result, const Octant* octant, const Ray& ray, unsigned short nodeFlags, float maxDistance, unsigned layerMask) const { float octantDist = ray.HitDistance(octant->cullingBox); if (octantDist >= maxDistance) return; const Vector<OctreeNode*>& octantNodes = octant->nodes; for (auto it = octantNodes.Begin(); it != octantNodes.End(); ++it) { OctreeNode* node = *it; if ((node->Flags() & nodeFlags) == nodeFlags && (node->LayerMask() & layerMask)) node->OnRaycast(result, ray, maxDistance); } for (size_t i = 0; i < NUM_OCTANTS; ++i) { if (octant->children[i]) CollectNodes(result, octant->children[i], ray, nodeFlags, maxDistance, layerMask); } }
void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // If no bones or no bone-level testing, use the StaticModel test RayQueryLevel level = query.level_; if (level < RAY_TRIANGLE || !skeleton_.GetNumBones()) { StaticModel::ProcessRayQuery(query, results); return; } // Check ray hit distance to AABB before proceeding with bone-level tests if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_) return; const Vector<Bone>& bones = skeleton_.GetBones(); Sphere boneSphere; for (unsigned i = 0; i < bones.Size(); ++i) { const Bone& bone = bones[i]; if (!bone.node_) continue; float distance; // Use hitbox if available if (bone.collisionMask_ & BONECOLLISION_BOX) { // Do an initial crude test using the bone's AABB const BoundingBox& box = bone.boundingBox_; const Matrix3x4& transform = bone.node_->GetWorldTransform(); distance = query.ray_.HitDistance(box.Transformed(transform)); if (distance >= query.maxDistance_) continue; if (level != RAY_AABB) { // Follow with an OBB test if required Matrix3x4 inverse = transform.Inverse(); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(box); if (distance >= query.maxDistance_) continue; } } else if (bone.collisionMask_ & BONECOLLISION_SPHERE) { boneSphere.center_ = bone.node_->GetWorldPosition(); boneSphere.radius_ = bone.radius_; distance = query.ray_.HitDistance(boneSphere); if (distance >= query.maxDistance_) continue; } else continue; // If the code reaches here then we have a hit RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = -query.ray_.direction_; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = i; results.Push(result); } }