#include "CSceneCollisionManager.h"
#include "ISceneNode.h"
#include "ICameraSceneNode.h"
#include "ITriangleSelector.h"
#include "SViewFrustum.h"
#include "os.h"
#include "irrMath.h"
namespace irr
{
namespace scene
{
CSceneCollisionManager::CSceneCollisionManager(ISceneManager* smanager, video::IVideoDriver* driver)
: SceneManager(smanager), Driver(driver)
{
#ifdef _DEBUG
setDebugName("CSceneCollisionManager");
#endif
if (Driver)
Driver->grab();
}
CSceneCollisionManager::~CSceneCollisionManager()
{
if (Driver)
Driver->drop();
}
ISceneNode* CSceneCollisionManager::getSceneNodeFromScreenCoordinatesBB(
const core::position2d<s32>& pos, s32 idBitMask, bool noDebugObjects, scene::ISceneNode* root)
{
const core::line3d<f32> ln = getRayFromScreenCoordinates(pos, 0);
if ( ln.start == ln.end )
return 0;
return getSceneNodeFromRayBB(ln, idBitMask, noDebugObjects, root);
}
ISceneNode* CSceneCollisionManager::getSceneNodeFromRayBB(
const core::line3d<f32>& ray,
s32 idBitMask, bool noDebugObjects, scene::ISceneNode* root)
{
ISceneNode* best = 0;
f32 dist = FLT_MAX;
core::line3d<f32> truncatableRay(ray);
getPickedNodeBB((root==0)?SceneManager->getRootSceneNode():root, truncatableRay,
idBitMask, noDebugObjects, dist, best);
return best;
}
void CSceneCollisionManager::getPickedNodeBB(ISceneNode* root,
core::line3df& ray, s32 bits, bool noDebugObjects,
f32& outbestdistance, ISceneNode*& outbestnode)
{
const ISceneNodeList& children = root->getChildren();
const core::vector3df rayVector = ray.getVector().normalize();
ISceneNodeList::ConstIterator it = children.begin();
for (; it != children.end(); ++it)
{
ISceneNode* current = *it;
if (current->isVisible())
{
if((noDebugObjects ? !current->isDebugObject() : true) &&
(bits==0 || (bits != 0 && (current->getID() & bits))))
{
core::matrix4 worldToObject;
if (!current->getAbsoluteTransformation().getInverse(worldToObject))
continue;
core::line3df objectRay(ray);
worldToObject.transformVect(objectRay.start);
worldToObject.transformVect(objectRay.end);
const core::aabbox3df & objectBox = current->getBoundingBox();
if(objectBox.isPointInside(objectRay.start))
{
const core::vector3df dir = (objectRay.end-objectRay.start).normalize();
const core::vector3df minDist = (objectBox.MinEdge - objectRay.start)/dir;
const core::vector3df maxDist = (objectBox.MaxEdge - objectRay.start)/dir;
const core::vector3df realMin(core::min_(minDist.X, maxDist.X),core::min_(minDist.Y, maxDist.Y),core::min_(minDist.Z, maxDist.Z));
const core::vector3df realMax(core::max_(minDist.X, maxDist.X),core::max_(minDist.Y, maxDist.Y),core::max_(minDist.Z, maxDist.Z));
const f32 minmax = core::min_(realMax.X, realMax.Y, realMax.Z);
const f32 maxmin = core::max_(realMin.X, realMin.Y, realMin.Z);
const f32 toIntersectionSq = (maxmin>0?maxmin*maxmin:minmax*minmax);
if (toIntersectionSq < outbestdistance)
{
outbestdistance = toIntersectionSq;
outbestnode = current;
ray.end = ray.start + (rayVector * sqrtf(toIntersectionSq));
}
}
else
if (objectBox.intersectsWithLine(objectRay))
{
core::aabbox3df worldBox(objectBox);
current->getAbsoluteTransformation().transformBox(worldBox);
core::vector3df edges[8];
worldBox.getEdges(edges);
static const s32 faceEdges[6][3] =
{
{ 0, 1, 5 },
{ 6, 7, 3 },
{ 2, 3, 1 },
{ 4, 5, 7 },
{ 1, 3, 7 },
{ 2, 0, 4 }
};
core::vector3df intersection;
core::plane3df facePlane;
f32 bestDistToBoxBorder = FLT_MAX;
f32 bestToIntersectionSq = FLT_MAX;
for(s32 face = 0; face < 6; ++face)
{
facePlane.setPlane(edges[faceEdges[face][0]],
edges[faceEdges[face][1]],
edges[faceEdges[face][2]]);
if(facePlane.classifyPointRelation(ray.start) != core::ISREL3D_FRONT)
continue;
if(facePlane.getIntersectionWithLine(ray.start, rayVector, intersection))
{
const f32 toIntersectionSq = ray.start.getDistanceFromSQ(intersection);
if(toIntersectionSq < outbestdistance)
{
worldToObject.transformVect(intersection);
f32 distToBorder = core::max_ ( core::min_ (core::abs_(objectBox.MinEdge.X-intersection.X), core::abs_(objectBox.MaxEdge.X-intersection.X)),
core::min_ (core::abs_(objectBox.MinEdge.Y-intersection.Y), core::abs_(objectBox.MaxEdge.Y-intersection.Y)),
core::min_ (core::abs_(objectBox.MinEdge.Z-intersection.Z), core::abs_(objectBox.MaxEdge.Z-intersection.Z)) );
if ( distToBorder < bestDistToBoxBorder )
{
bestDistToBoxBorder = distToBorder;
bestToIntersectionSq = toIntersectionSq;
}
}
}
if (!(face & 0x01))
++face;
}
if ( bestDistToBoxBorder < FLT_MAX )
{
outbestdistance = bestToIntersectionSq;
outbestnode = current;
ray.end = ray.start + (rayVector * sqrtf(outbestdistance));
}
}
}
getPickedNodeBB(current, ray, bits, noDebugObjects, outbestdistance, outbestnode);
}
}
}
ISceneNode* CSceneCollisionManager::getSceneNodeAndCollisionPointFromRay(
core::line3df ray,
core::vector3df & outCollisionPoint,
core::triangle3df & outTriangle,
s32 idBitMask,
ISceneNode * collisionRootNode,
bool noDebugObjects)
{
ISceneNode* bestNode = 0;
f32 bestDistanceSquared = FLT_MAX;
if(0 == collisionRootNode)
collisionRootNode = SceneManager->getRootSceneNode();
getPickedNodeFromBBAndSelector(collisionRootNode, ray, idBitMask,
noDebugObjects, bestDistanceSquared, bestNode,
outCollisionPoint, outTriangle);
return bestNode;
}
void CSceneCollisionManager::getPickedNodeFromBBAndSelector(
ISceneNode * root,
core::line3df & ray,
s32 bits,
bool noDebugObjects,
f32 & outBestDistanceSquared,
ISceneNode * & outBestNode,
core::vector3df & outBestCollisionPoint,
core::triangle3df & outBestTriangle)
{
const ISceneNodeList& children = root->getChildren();
ISceneNodeList::ConstIterator it = children.begin();
for (; it != children.end(); ++it)
{
ISceneNode* current = *it;
ITriangleSelector * selector = current->getTriangleSelector();
if (selector && current->isVisible() &&
(noDebugObjects ? !current->isDebugObject() : true) &&
(bits==0 || (bits != 0 && (current->getID() & bits))))
{
core::matrix4 mat;
if (!current->getAbsoluteTransformation().getInverse(mat))
continue;
core::line3df line(ray);
mat.transformVect(line.start);
mat.transformVect(line.end);
const core::aabbox3df& box = current->getBoundingBox();
core::vector3df candidateCollisionPoint;
core::triangle3df candidateTriangle;
ISceneNode * hitNode = 0;
if (box.intersectsWithLine(line) &&
getCollisionPoint(ray, selector, candidateCollisionPoint, candidateTriangle, hitNode))
{
const f32 distanceSquared = (candidateCollisionPoint - ray.start).getLengthSQ();
if(distanceSquared < outBestDistanceSquared)
{
outBestDistanceSquared = distanceSquared;
outBestNode = current;
outBestCollisionPoint = candidateCollisionPoint;
outBestTriangle = candidateTriangle;
const core::vector3df rayVector = ray.getVector().normalize();
ray.end = ray.start + (rayVector * sqrtf(distanceSquared));
}
}
}
getPickedNodeFromBBAndSelector(current, ray, bits, noDebugObjects,
outBestDistanceSquared, outBestNode,
outBestCollisionPoint, outBestTriangle);
}
}
ISceneNode* CSceneCollisionManager::getSceneNodeFromCameraBB(
ICameraSceneNode* camera, s32 idBitMask, bool noDebugObjects)
{
if (!camera)
return 0;
const core::vector3df start = camera->getAbsolutePosition();
core::vector3df end = camera->getTarget();
end = start + ((end - start).normalize() * camera->getFarValue());
return getSceneNodeFromRayBB(core::line3d<f32>(start, end), idBitMask, noDebugObjects);
}
bool CSceneCollisionManager::getCollisionPoint(const core::line3d<f32>& ray,
ITriangleSelector* selector, core::vector3df& outIntersection,
core::triangle3df& outTriangle,
ISceneNode*& outNode)
{
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 = FLT_MAX;
bool found = false;
const f32 raylength = ray.getLengthSQ();
const f32 minX = core::min_(ray.start.X, ray.end.X);
const f32 maxX = core::max_(ray.start.X, ray.end.X);
const f32 minY = core::min_(ray.start.Y, ray.end.Y);
const f32 maxY = core::max_(ray.start.Y, ray.end.Y);
const f32 minZ = core::min_(ray.start.Z, ray.end.Z);
const f32 maxZ = core::max_(ray.start.Z, ray.end.Z);
for (s32 i=0; i<cnt; ++i)
{
const core::triangle3df & triangle = Triangles[i];
if(minX > triangle.pointA.X && minX > triangle.pointB.X && minX > triangle.pointC.X)
continue;
if(maxX < triangle.pointA.X && maxX < triangle.pointB.X && maxX < triangle.pointC.X)
continue;
if(minY > triangle.pointA.Y && minY > triangle.pointB.Y && minY > triangle.pointC.Y)
continue;
if(maxY < triangle.pointA.Y && maxY < triangle.pointB.Y && maxY < triangle.pointC.Y)
continue;
if(minZ > triangle.pointA.Z && minZ > triangle.pointB.Z && minZ > triangle.pointC.Z)
continue;
if(maxZ < triangle.pointA.Z && maxZ < triangle.pointB.Z && maxZ < triangle.pointC.Z)
continue;
if (triangle.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 = triangle;
outIntersection = intersection;
outNode = selector->getSceneNodeForTriangle(i);
found = true;
}
}
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return found;
}
core::vector3df CSceneCollisionManager::getCollisionResultPosition(
ITriangleSelector* selector,
const core::vector3df &position, const core::vector3df& radius,
const core::vector3df& direction,
core::triangle3df& triout,
core::vector3df& hitPosition,
bool& outFalling,
ISceneNode*& outNode,
f32 slidingSpeed,
const core::vector3df& gravity)
{
return collideEllipsoidWithWorld(selector, position,
radius, direction, slidingSpeed, gravity, triout, hitPosition, outFalling, outNode);
}
bool CSceneCollisionManager::testTriangleIntersection(SCollisionData* colData,
const core::triangle3df& triangle)
{
const core::plane3d<f32> trianglePlane = triangle.getPlane();
if ( !trianglePlane.isFrontFacing(colData->normalizedVelocity) )
return false;
f32 t1, t0;
bool embeddedInPlane = false;
f32 signedDistToTrianglePlane = trianglePlane.getDistanceTo(
colData->basePoint);
f32 normalDotVelocity =
trianglePlane.Normal.dotProduct(colData->velocity);
if ( core::iszero ( normalDotVelocity ) )
{
if (fabs(signedDistToTrianglePlane) >= 1.0f)
return false;
else
{
embeddedInPlane = true;
t0 = 0.0;
t1 = 1.0;
}
}
else
{
normalDotVelocity = core::reciprocal ( normalDotVelocity );
t0 = (-1.f - signedDistToTrianglePlane) * normalDotVelocity;
t1 = (1.f - signedDistToTrianglePlane) * normalDotVelocity;
if (t0 > t1) { f32 tmp = t1; t1 = t0; t0 = tmp; }
if (t0 > 1.0f || t1 < 0.0f)
return false;
t0 = core::clamp ( t0, 0.f, 1.f );
t1 = core::clamp ( t1, 0.f, 1.f );
}
core::vector3df collisionPoint;
bool foundCollision = false;
f32 t = 1.0f;
if (!embeddedInPlane)
{
core::vector3df planeIntersectionPoint =
(colData->basePoint - trianglePlane.Normal)
+ (colData->velocity * t0);
if (triangle.isPointInside(planeIntersectionPoint))
{
foundCollision = true;
t = t0;
collisionPoint = planeIntersectionPoint;
}
}
if (!foundCollision)
{
core::vector3df velocity = colData->velocity;
core::vector3df base = colData->basePoint;
f32 velocitySqaredLength = velocity.getLengthSQ();
f32 a,b,c;
f32 newT;
a = velocitySqaredLength;
b = 2.0f * (velocity.dotProduct(base - triangle.pointA));
c = (triangle.pointA-base).getLengthSQ() - 1.f;
if (getLowestRoot(a,b,c,t, &newT))
{
t = newT;
foundCollision = true;
collisionPoint = triangle.pointA;
}
if (!foundCollision)
{
b = 2.0f * (velocity.dotProduct(base - triangle.pointB));
c = (triangle.pointB-base).getLengthSQ() - 1.f;
if (getLowestRoot(a,b,c,t, &newT))
{
t = newT;
foundCollision = true;
collisionPoint = triangle.pointB;
}
}
if (!foundCollision)
{
b = 2.0f * (velocity.dotProduct(base - triangle.pointC));
c = (triangle.pointC-base).getLengthSQ() - 1.f;
if (getLowestRoot(a,b,c,t, &newT))
{
t = newT;
foundCollision = true;
collisionPoint = triangle.pointC;
}
}
core::vector3df edge = triangle.pointB - triangle.pointA;
core::vector3df baseToVertex = triangle.pointA - base;
f32 edgeSqaredLength = edge.getLengthSQ();
f32 edgeDotVelocity = edge.dotProduct(velocity);
f32 edgeDotBaseToVertex = edge.dotProduct(baseToVertex);
a = edgeSqaredLength* -velocitySqaredLength +
edgeDotVelocity*edgeDotVelocity;
b = edgeSqaredLength* (2.f *velocity.dotProduct(baseToVertex)) -
2.0f*edgeDotVelocity*edgeDotBaseToVertex;
c = edgeSqaredLength* (1.f -baseToVertex.getLengthSQ()) +
edgeDotBaseToVertex*edgeDotBaseToVertex;
if (getLowestRoot(a,b,c,t,&newT))
{
f32 f = (edgeDotVelocity*newT - edgeDotBaseToVertex) / edgeSqaredLength;
if (f >=0.0f && f <= 1.0f)
{
t = newT;
foundCollision = true;
collisionPoint = triangle.pointA + (edge*f);
}
}
edge = triangle.pointC-triangle.pointB;
baseToVertex = triangle.pointB - base;
edgeSqaredLength = edge.getLengthSQ();
edgeDotVelocity = edge.dotProduct(velocity);
edgeDotBaseToVertex = edge.dotProduct(baseToVertex);
a = edgeSqaredLength* -velocitySqaredLength +
edgeDotVelocity*edgeDotVelocity;
b = edgeSqaredLength* (2*velocity.dotProduct(baseToVertex)) -
2.0f*edgeDotVelocity*edgeDotBaseToVertex;
c = edgeSqaredLength* (1-baseToVertex.getLengthSQ()) +
edgeDotBaseToVertex*edgeDotBaseToVertex;
if (getLowestRoot(a,b,c,t,&newT))
{
f32 f = (edgeDotVelocity*newT-edgeDotBaseToVertex) /
edgeSqaredLength;
if (f >=0.0f && f <= 1.0f)
{
t = newT;
foundCollision = true;
collisionPoint = triangle.pointB + (edge*f);
}
}
edge = triangle.pointA-triangle.pointC;
baseToVertex = triangle.pointC - base;
edgeSqaredLength = edge.getLengthSQ();
edgeDotVelocity = edge.dotProduct(velocity);
edgeDotBaseToVertex = edge.dotProduct(baseToVertex);
a = edgeSqaredLength* -velocitySqaredLength +
edgeDotVelocity*edgeDotVelocity;
b = edgeSqaredLength* (2*velocity.dotProduct(baseToVertex)) -
2.0f*edgeDotVelocity*edgeDotBaseToVertex;
c = edgeSqaredLength* (1-baseToVertex.getLengthSQ()) +
edgeDotBaseToVertex*edgeDotBaseToVertex;
if (getLowestRoot(a,b,c,t,&newT))
{
f32 f = (edgeDotVelocity*newT-edgeDotBaseToVertex) /
edgeSqaredLength;
if (f >=0.0f && f <= 1.0f)
{
t = newT;
foundCollision = true;
collisionPoint = triangle.pointC + (edge*f);
}
}
}
if (foundCollision)
{
f32 distToCollision = t*colData->velocity.getLength();
if (!colData->foundCollision ||
distToCollision < colData->nearestDistance)
{
colData->nearestDistance = distToCollision;
colData->intersectionPoint = collisionPoint;
colData->foundCollision = true;
colData->intersectionTriangle = triangle;
++colData->triangleHits;
return true;
}
}
return false;
}
core::vector3df CSceneCollisionManager::collideEllipsoidWithWorld(
ITriangleSelector* selector, const core::vector3df &position,
const core::vector3df& radius, const core::vector3df& velocity,
f32 slidingSpeed,
const core::vector3df& gravity,
core::triangle3df& triout,
core::vector3df& hitPosition,
bool& outFalling,
ISceneNode*& outNode)
{
if (!selector || radius.X == 0.0f || radius.Y == 0.0f || radius.Z == 0.0f)
return position;
SCollisionData colData;
colData.R3Position = position;
colData.R3Velocity = velocity;
colData.eRadius = radius;
colData.nearestDistance = FLT_MAX;
colData.selector = selector;
colData.slidingSpeed = slidingSpeed;
colData.triangleHits = 0;
colData.triangleIndex = -1;
core::vector3df eSpacePosition = colData.R3Position / colData.eRadius;
core::vector3df eSpaceVelocity = colData.R3Velocity / colData.eRadius;
core::vector3df finalPos = collideWithWorld(
0, colData, eSpacePosition, eSpaceVelocity);
outFalling = false;
if (gravity != core::vector3df(0,0,0))
{
colData.R3Position = finalPos * colData.eRadius;
colData.R3Velocity = gravity;
colData.triangleHits = 0;
eSpaceVelocity = gravity/colData.eRadius;
finalPos = collideWithWorld(0, colData,
finalPos, eSpaceVelocity);
outFalling = (colData.triangleHits == 0);
}
if (colData.triangleHits)
{
triout = colData.intersectionTriangle;
triout.pointA *= colData.eRadius;
triout.pointB *= colData.eRadius;
triout.pointC *= colData.eRadius;
outNode = selector->getSceneNodeForTriangle(colData.triangleIndex);
}
finalPos *= colData.eRadius;
hitPosition = colData.intersectionPoint * colData.eRadius;
return finalPos;
}
core::vector3df CSceneCollisionManager::collideWithWorld(s32 recursionDepth,
SCollisionData &colData, core::vector3df pos, core::vector3df vel)
{
f32 veryCloseDistance = colData.slidingSpeed;
if (recursionDepth > 5)
return pos;
colData.velocity = vel;
colData.normalizedVelocity = vel;
colData.normalizedVelocity.normalize();
colData.basePoint = pos;
colData.foundCollision = false;
colData.nearestDistance = FLT_MAX;
core::aabbox3d<f32> box(colData.R3Position);
box.addInternalPoint(colData.R3Position + colData.R3Velocity);
box.MinEdge -= colData.eRadius;
box.MaxEdge += colData.eRadius;
s32 totalTriangleCnt = colData.selector->getTriangleCount();
Triangles.set_used(totalTriangleCnt);
core::matrix4 scaleMatrix;
scaleMatrix.setScale(
core::vector3df(1.0f / colData.eRadius.X,
1.0f / colData.eRadius.Y,
1.0f / colData.eRadius.Z));
s32 triangleCnt = 0;
colData.selector->getTriangles(Triangles.pointer(), totalTriangleCnt, triangleCnt, box, &scaleMatrix);
for (s32 i=0; i<triangleCnt; ++i)
if(testTriangleIntersection(&colData, Triangles[i]))
colData.triangleIndex = i;
if (!colData.foundCollision)
return pos + vel;
const core::vector3df destinationPoint = pos + vel;
core::vector3df newBasePoint = pos;
if (colData.nearestDistance >= veryCloseDistance)
{
core::vector3df v = vel;
v.setLength( colData.nearestDistance - veryCloseDistance );
newBasePoint = colData.basePoint + v;
v.normalize();
colData.intersectionPoint -= (v * veryCloseDistance);
}
const core::vector3df slidePlaneOrigin = colData.intersectionPoint;
const core::vector3df slidePlaneNormal = (newBasePoint - colData.intersectionPoint).normalize();
core::plane3d<f32> slidingPlane(slidePlaneOrigin, slidePlaneNormal);
core::vector3df newDestinationPoint =
destinationPoint -
(slidePlaneNormal * slidingPlane.getDistanceTo(destinationPoint));
const core::vector3df newVelocityVector = newDestinationPoint -
colData.intersectionPoint;
if (newVelocityVector.getLength() < veryCloseDistance)
return newBasePoint;
return collideWithWorld(recursionDepth+1, colData,
newBasePoint, newVelocityVector);
}
core::line3d<f32> CSceneCollisionManager::getRayFromScreenCoordinates(
const core::position2d<s32> & pos, ICameraSceneNode* camera)
{
core::line3d<f32> ln(0,0,0,0,0,0);
if (!SceneManager)
return ln;
if (!camera)
camera = SceneManager->getActiveCamera();
if (!camera)
return ln;
const scene::SViewFrustum* f = camera->getViewFrustum();
core::vector3df farLeftUp = f->getFarLeftUp();
core::vector3df lefttoright = f->getFarRightUp() - farLeftUp;
core::vector3df uptodown = f->getFarLeftDown() - farLeftUp;
const core::rect<s32>& viewPort = Driver->getViewPort();
core::dimension2d<u32> screenSize(viewPort.getWidth(), viewPort.getHeight());
f32 dx = pos.X / (f32)screenSize.Width;
f32 dy = pos.Y / (f32)screenSize.Height;
if (camera->isOrthogonal())
ln.start = f->cameraPosition + (lefttoright * (dx-0.5f)) + (uptodown * (dy-0.5f));
else
ln.start = f->cameraPosition;
ln.end = farLeftUp + (lefttoright * dx) + (uptodown * dy);
return ln;
}
core::position2d<s32> CSceneCollisionManager::getScreenCoordinatesFrom3DPosition(
const core::vector3df & pos3d, ICameraSceneNode* camera, bool useViewPort)
{
if (!SceneManager || !Driver)
return core::position2d<s32>(-1000,-1000);
if (!camera)
camera = SceneManager->getActiveCamera();
if (!camera)
return core::position2d<s32>(-1000,-1000);
core::dimension2d<u32> dim;
if (useViewPort)
dim.set(Driver->getViewPort().getWidth(), Driver->getViewPort().getHeight());
else
dim=(Driver->getScreenSize());
dim.Width /= 2;
dim.Height /= 2;
core::matrix4 trans = camera->getProjectionMatrix();
trans *= camera->getViewMatrix();
f32 transformedPos[4] = { pos3d.X, pos3d.Y, pos3d.Z, 1.0f };
trans.multiplyWith1x4Matrix(transformedPos);
if (transformedPos[3] < 0)
return core::position2d<s32>(-10000,-10000);
const f32 zDiv = transformedPos[3] == 0.0f ? 1.0f :
core::reciprocal(transformedPos[3]);
return core::position2d<s32>(
dim.Width + core::round32(dim.Width * (transformedPos[0] * zDiv)),
dim.Height - core::round32(dim.Height * (transformedPos[1] * zDiv)));
}
inline bool CSceneCollisionManager::getLowestRoot(f32 a, f32 b, f32 c, f32 maxR, f32* root)
{
f32 determinant = b*b - 4.0f*a*c;
if (determinant < 0.0f || a == 0.f ) return false;
f32 sqrtD = (f32)sqrt(determinant);
f32 r1 = (-b - sqrtD) / (2*a);
f32 r2 = (-b + sqrtD) / (2*a);
if (r1 > r2) { f32 tmp=r2; r2=r1; r1=tmp; }
if (r1 > 0 && r1 < maxR)
{
*root = r1;
return true;
}
if (r2 > 0 && r2 < maxR)
{
*root = r2;
return true;
}
return false;
}
}
}