//! Finds the collision point of a line and lots of triangles, if there is one. bool CSceneCollisionManager::getCollisionPoint(const core::line3d<f32>& ray, ITriangleSelector* selector, core::vector3df& outIntersection, core::triangle3df& outTriangle) { if (!selector) { _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return false; } s32 totalcnt = selector->getTriangleCount(); Triangles.set_used(totalcnt); s32 cnt = 0; selector->getTriangles(Triangles.pointer(), totalcnt, cnt, ray); const core::vector3df linevect = ray.getVector().normalize(); core::vector3df intersection; f32 nearest = 9999999999999.0f; bool found = false; const f32 raylength = ray.getLengthSQ(); for (s32 i=0; i<cnt; ++i) { if (Triangles[i].getIntersectionWithLine(ray.start, linevect, intersection)) { const f32 tmp = intersection.getDistanceFromSQ(ray.start); const f32 tmp2 = intersection.getDistanceFromSQ(ray.end); if (tmp < raylength && tmp2 < raylength && tmp < nearest) { nearest = tmp; outTriangle = Triangles[i]; outIntersection = intersection; found = true; } } } _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return found; }
static bool testGetIntersectionWithLine(core::triangle3d<T>& triangle, const core::line3d<T>& ray) { bool allExpected=true; const vector3d<T> linevect = ray.getVector().normalize(); vector3d<T> intersection; for (u32 i=0; i<100; ++i) { if (!triangle.getIntersectionOfPlaneWithLine(ray.start, linevect, intersection)) { allExpected=false; logTestString("triangle3d plane test %d failed\n", i); } if (!triangle.isPointInsideFast(intersection)) { allExpected=false; logTestString("triangle3d fast point test %d failed\n", i); } if (!triangle.isPointInside(intersection)) { allExpected=false; logTestString("triangle3d point test %d failed\n", i); if (!isOnSameSide(intersection, triangle.pointA, triangle.pointB, triangle.pointC)) logTestString("triangle3d side1 test %d failed\n", i); if (!isOnSameSide(intersection, triangle.pointB, triangle.pointA, triangle.pointC)) logTestString("triangle3d side2 test %d failed\n", i); if (!isOnSameSide(intersection, triangle.pointC, triangle.pointA, triangle.pointB)) logTestString("triangle3d side3 test %d failed\n", i); } if (!triangle.getIntersectionWithLine(ray.start, linevect, intersection)) { allExpected=false; logTestString("triangle3d tri test %d failed\n", i); } triangle.pointB.Y += 1; } return allExpected; }
PointedThing ClientEnvironment::getPointedThing( core::line3d<f32> shootline, bool liquids_pointable, bool look_for_object) { PointedThing result; INodeDefManager *nodedef = m_map->getNodeDefManager(); core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion(); // The code needs to search these nodes core::aabbox3d<s16> search_range(-maximal_exceed.MaxEdge, -maximal_exceed.MinEdge); // If a node is found, there might be a larger node behind. // To find it, we have to go further. s16 maximal_overcheck = std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); const v3f original_vector = shootline.getVector(); const f32 original_length = original_vector.getLength(); f32 min_distance = original_length; // First try to find an active object if (look_for_object) { ClientActiveObject *selected_object = getSelectedActiveObject( shootline, &result.intersection_point, &result.intersection_normal); if (selected_object != NULL) { min_distance = (result.intersection_point - shootline.start).getLength(); result.type = POINTEDTHING_OBJECT; result.object_id = selected_object->getId(); } } // Reduce shootline if (original_length > 0) { shootline.end = shootline.start + shootline.getVector() / original_length * min_distance; } // Try to find a node that is closer than the selected active // object (if it exists). voxalgo::VoxelLineIterator iterator(shootline.start / BS, shootline.getVector() / BS); v3s16 oldnode = iterator.m_current_node_pos; // Indicates that a node was found. bool is_node_found = false; // If a node is found, it is possible that there's a node // behind it with a large nodebox, so continue the search. u16 node_foundcounter = 0; // If a node is found, this is the center of the // first nodebox the shootline meets. v3f found_boxcenter(0, 0, 0); // The untested nodes are in this range. core::aabbox3d<s16> new_nodes; while (true) { // Test the nodes around the current node in search_range. new_nodes = search_range; new_nodes.MinEdge += iterator.m_current_node_pos; new_nodes.MaxEdge += iterator.m_current_node_pos; // Only check new nodes v3s16 delta = iterator.m_current_node_pos - oldnode; if (delta.X > 0) new_nodes.MinEdge.X = new_nodes.MaxEdge.X; else if (delta.X < 0) new_nodes.MaxEdge.X = new_nodes.MinEdge.X; else if (delta.Y > 0) new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; else if (delta.Y < 0) new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; else if (delta.Z > 0) new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; else if (delta.Z < 0) new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; // For each untested node for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { MapNode n; v3s16 np(x, y, z); bool is_valid_position; n = m_map->getNodeNoEx(np, &is_valid_position); if (!(is_valid_position && isPointableNode(n, nodedef, liquids_pointable))) { continue; } std::vector<aabb3f> boxes; n.getSelectionBoxes(nodedef, &boxes, n.getNeighbors(np, m_map)); v3f npf = intToFloat(np, BS); for (std::vector<aabb3f>::const_iterator i = boxes.begin(); i != boxes.end(); ++i) { aabb3f box = *i; box.MinEdge += npf; box.MaxEdge += npf; v3f intersection_point; v3s16 intersection_normal; if (!boxLineCollision(box, shootline.start, shootline.getVector(), &intersection_point, &intersection_normal)) { continue; } f32 distance = (intersection_point - shootline.start).getLength(); if (distance >= min_distance) { continue; } result.type = POINTEDTHING_NODE; result.node_undersurface = np; result.intersection_point = intersection_point; result.intersection_normal = intersection_normal; found_boxcenter = box.getCenter(); min_distance = distance; is_node_found = true; } } } } if (is_node_found) { node_foundcounter++; if (node_foundcounter > maximal_overcheck) { break; } } // Next node if (iterator.hasNext()) { oldnode = iterator.m_current_node_pos; iterator.next(); } else { break; } } if (is_node_found) { // Set undersurface and abovesurface nodes f32 d = 0.002 * BS; v3f fake_intersection = result.intersection_point; // Move intersection towards its source block. if (fake_intersection.X < found_boxcenter.X) fake_intersection.X += d; else fake_intersection.X -= d; if (fake_intersection.Y < found_boxcenter.Y) fake_intersection.Y += d; else fake_intersection.Y -= d; if (fake_intersection.Z < found_boxcenter.Z) fake_intersection.Z += d; else fake_intersection.Z -= d; result.node_real_undersurface = floatToInt(fake_intersection, BS); result.node_abovesurface = result.node_real_undersurface + result.intersection_normal; } return result; }