/* Find the best (smallest) zone that contains a point */ PCZone * PCZSceneManager::findZoneForPoint(Vector3 & point) { PCZone * zone; PCZone * bestZone = mDefaultZone; Real bestVolume = Ogre::Math::POS_INFINITY; ZoneMap::iterator zit = mZones.begin(); while ( zit != mZones.end() ) { zone = zit->second; AxisAlignedBox aabb; zone->getAABB(aabb); SceneNode * enclosureNode = zone->getEnclosureNode(); if (enclosureNode != 0) { // since this is the "local" AABB, add in world translation of the enclosure node aabb.setMinimum(aabb.getMinimum() + enclosureNode->_getDerivedPosition()); aabb.setMaximum(aabb.getMaximum() + enclosureNode->_getDerivedPosition()); } if (aabb.contains(point)) { if (aabb.volume() < bestVolume) { // this zone is "smaller" than the current best zone, so make it // the new best zone bestZone = zone; bestVolume = aabb.volume(); } } // proceed to next zone in the list ++zit; } return bestZone; }
// ------------------------------------------------------------------------ static bool isBoundOkForMcGuire(const AxisAlignedBox& lightCapBounds, const Ogre::Vector3& lightPosition) { // If light position is inside light cap bound then extrusion could be in opposite directions // and McGuire cap could intersect near clip plane of camera frustum without being noticed if(lightCapBounds.contains(lightPosition)) return false; // If angular size of object is too high then extrusion could be in almost opposite directions, // interpolated points would be extruded by shorter distance, and strange geometry of McGuire cap // could be visible even for well tesselated meshes. As a heuristic we will avoid McGuire cap if // angular size is larger than 60 degrees - it guarantees that interpolated points would be // extruded by at least cos(60deg/2) ~ 86% of the original extrusion distance. if(lightCapBounds.getHalfSize().length() / (lightCapBounds.getCenter() - lightPosition).length() > 0.5) // if boundingSphereAngularSize > 60deg { // Calculate angular size one more time using edge corners angular distance comparision, // Determine lit sides of the bound, store in mask enum { L = 1, R = 2, B = 4, T = 8, F = 16, N = 32 }; // left, right, bottom, top, far, near unsigned lightSidesMask = (lightPosition.x < lightCapBounds.getMinimum().x ? L : 0) | // left (lightPosition.x > lightCapBounds.getMaximum().x ? R : 0) | // right (lightPosition.y < lightCapBounds.getMinimum().y ? B : 0) | // bottom (lightPosition.y > lightCapBounds.getMaximum().y ? T : 0) | // top (lightPosition.z < lightCapBounds.getMinimum().z ? F : 0) | // far (lightPosition.z > lightCapBounds.getMaximum().z ? N : 0); // near // find corners on lit/unlit edge (should not be more than 6 simultaneously, but better be safe than sorry) Ogre::Vector3 edgeCorners[8]; unsigned edgeCornersCount = 0; std::pair<unsigned, AxisAlignedBox::CornerEnum> cornerMap[8] = { { F|L|B, AxisAlignedBox::FAR_LEFT_BOTTOM }, { F|R|B, AxisAlignedBox::FAR_RIGHT_BOTTOM }, { F|L|T, AxisAlignedBox::FAR_LEFT_TOP }, { F|R|T, AxisAlignedBox::FAR_RIGHT_TOP }, { N|L|B, AxisAlignedBox::NEAR_LEFT_BOTTOM },{ N|R|B, AxisAlignedBox::NEAR_RIGHT_BOTTOM }, { N|L|T, AxisAlignedBox::NEAR_LEFT_TOP }, { N|R|T, AxisAlignedBox::NEAR_RIGHT_TOP }}; for(auto& c : cornerMap) if((lightSidesMask & c.first) != 0 && (lightSidesMask & c.first) != c.first) // if adjacent sides not all lit or all unlit edgeCorners[edgeCornersCount++] = lightCapBounds.getCorner(c.second); // find max angular size in range [0..pi] by finding min cos of angular size, range [1..-1] Real cosAngle = 1.0; for(unsigned i0 = 0; i0 + 1 < edgeCornersCount; ++i0) for(unsigned i1 = i0 + 1; i1 < edgeCornersCount; ++i1) { // 4~6 edge corners, 6~15 angular distance calculations Vector3 a = (edgeCorners[i0] - lightPosition).normalisedCopy(); Vector3 b = (edgeCorners[i1] - lightPosition).normalisedCopy(); Real cosAB = a.dotProduct(b); if(cosAngle > cosAB) cosAngle = cosAB; } if(cosAngle < 0.5) // angularSize > 60 degrees return false; } return true; }
Real MathUtil::distance(const AxisAlignedBox& b, const Vector3& v) { if (b.contains(v)) { return 0.0f; } else { Vector3 dv; const Vector3& min1 = b.getMinimum(); const Vector3& max1 = b.getMaximum(); dv.x = min1.x > v.x ? min1.x - v.x : v.x > max1.x ? v.x - max1.x : 0.0f; dv.y = min1.y > v.y ? min1.y - v.y : v.y > max1.y ? v.y - max1.y : 0.0f; dv.z = min1.z > v.z ? min1.z - v.z : v.z > max1.z ? v.z - max1.z : 0.0f; return dv.length(); } }
std::pair<bool, Vector3> TerrainInfo::rayIntersects(const Ray& ray) const { AxisAlignedBox box = getExtents(); Vector3 point = ray.getOrigin(); Vector3 dir = ray.getDirection(); // first, does the ray start from inside the terrain extents? if (!box.contains(point)) { // not inside the box, so let's see if we are actually // colliding with it pair<bool, Real> res = ray.intersects(box); if (!res.first) return make_pair(false, Vector3::ZERO); // update point to the collision position point = ray.getPoint(res.second); } // now move along the ray until we intersect or leave the bounding box while (true) { // have we arived at or under the terrain height? // note that this approach means that ray queries from below won't work // correctly, but then again, that shouldn't be a usual case... float height = getHeightAt(point.x, point.z); if (point.y <= height) { point.y = height; return make_pair(true, point); } // move further... point += dir; // check if we are still inside the boundaries if (point.x < box.getMinimum().x || point.z < box.getMinimum().z || point.x > box.getMaximum().x || point.z > box.getMaximum().z) return make_pair(false, Vector3::ZERO); } }