MF_API void MFView_TransformPoint3DTo2D(const MFVector& point, MFVector *pResult) { MFMatrix proj, viewProj, view; // get the perspective projection matrix proj.SetPerspective(pCurrentView->fov, pCurrentView->nearPlane, pCurrentView->farPlane, pCurrentView->aspectRatio); // in this special case, we'll make the projection matrix produce a 0-1 value in z across all platforms (some platforms project into different 'z' spaces) float zn = pCurrentView->nearPlane; float zf = pCurrentView->farPlane; float zd = zf-zn; float zs = zf/zd; proj.m[10] = zs; proj.m[14] = -zn*zs; // get the view matrix (which we will need to calculate if we are in ortho mode) if(!MFView_IsOrtho()) view = MFView_GetWorldToViewMatrix(); else view.Inverse(MFView_GetCameraMatrix()); viewProj.Multiply4x4(view, proj); // apply the projection and perform the perspective divide MFVector transformed; transformed = ApplyMatrix(point, viewProj); transformed *= MFRcp(transformed.w); // and shift the result into the ortho rect transformed.x += 1.0f; transformed.y = -transformed.y + 1.0f; *pResult = transformed * MakeVector(pCurrentView->orthoRect.width*0.5f, pCurrentView->orthoRect.height*0.5f) + MakeVector(pCurrentView->orthoRect.x, pCurrentView->orthoRect.y); }
bool MFCollision_RaySphereTest(const MFVector& rayPos, const MFVector& rayDir, const MFVector& spherePos, float radius, MFRayIntersectionResult *pResult) { MFVector diff = rayPos - spherePos; // calcuate the coefficients float a = rayDir.MagSquared3(); float b = (2.0f*rayDir).Dot3(diff); float c = diff.MagSquared3() - radius*radius; // calculate the stuff under the root sign, if it's negative no (real) solutions exist float d = b*b - 4.0f*a*c; if(d < 0.0f) // this means ray misses cylinder return false; float root = MFSqrt(d); float rcp2a = MFRcp(2.0f*a); float t1 = (-b - root)*rcp2a; float t2 = (-b + root)*rcp2a; if(t2 < 0.0f || t1 > 1.0f) return false; if(pResult) { pResult->time = MFMax(t1, 0.0f); pResult->surfaceNormal.Mad3(rayDir, pResult->time, diff); pResult->surfaceNormal.Normalise3(); } return true; }
MF_API void MFView_TransformPoint2DTo3D(const MFVector& point, MFVector *pResult, MFVector *pResultRayDir) { MFMatrix proj, viewProj, view; // get the perspective projection matrix proj.SetPerspective(pCurrentView->fov, pCurrentView->nearPlane, pCurrentView->farPlane, pCurrentView->aspectRatio); // in this special case, we'll make the projection matrix produce a 0-1 value in z across all platforms (some platforms project into different 'z' spaces) float zn = pCurrentView->nearPlane; float zf = pCurrentView->farPlane; float zd = zf-zn; float zs = zf/zd; proj.m[10] = zs; proj.m[14] = -zn*zs; // get the view matrix (which we will need to calculate if we are in ortho mode) if(!MFView_IsOrtho()) view = MFView_GetWorldToViewMatrix(); else view.Inverse(MFView_GetCameraMatrix()); viewProj.Multiply4x4(view, proj); // inverse projection viewProj.Inverse(); // which the point from ortho space back into homogeneous space *pResult = point; *pResult -= MakeVector(pCurrentView->orthoRect.x, pCurrentView->orthoRect.y); *pResult *= MakeVector(MFRcp(pCurrentView->orthoRect.width*0.5f), MFRcp(pCurrentView->orthoRect.height*0.5f)); pResult->x -= 1.0f; pResult->y = -pResult->y + 1.0f; // and un-project // TODO: undo the perspective divide (f**k) *pResult = ApplyMatrix(*pResult, viewProj); if(pResultRayDir) { // calculate the pixels rays direction.. } }
bool MFCollision_RaySlabTest(const MFVector& rayPos, const MFVector& rayDir, const MFVector& plane, float slabHalfWidth, MFRayIntersectionResult *pResult) { float a = plane.Dot3(rayDir); // if ray is parallel to plane if(a > -MFALMOST_ZERO && a < MFALMOST_ZERO) { // TODO: this is intentionally BROKEN // this is a near impossible case, and it adds a lot of junk to the function /* if(MFAbs(rayPos.DotH(plane)) <= slabHalfWidth) { if(pResult) { pResult->time = 0.0f; } return true; } */ return false; } // otherwise we can do the conventional test float inva = MFRcp(a); float t = -rayPos.DotH(plane); float t1 = (t + slabHalfWidth) * inva; float t2 = (t - slabHalfWidth) * inva; t = MFMin(t1, t2); t2 = MFMax(t1, t2); if(t > 1.0f || t2 < 0.0f) return false; if(pResult) { pResult->time = MFMax(t, 0.0f); pResult->surfaceNormal = a > 0.0f ? -plane : plane; } return true; }
bool MFCollision_RayCylinderTest(const MFVector& rayPos, const MFVector& rayDir, const MFVector& cylinderPos, const MFVector& cylinderDir, float cylinderRadius, bool capped, MFRayIntersectionResult *pResult, float *pCylinderTime) { MFVector local = rayPos - cylinderPos; float rayD = rayDir.Dot3(cylinderDir); float T0 = local.Dot3(cylinderDir); // bring T0 into 0.0-1.0 range float invMagSq = MFRcp(cylinderDir.MagSquared3()); rayD *= invMagSq; T0 *= invMagSq; // calculate some intermediate vectors MFVector v1 = rayDir - rayD*cylinderDir; MFVector v2 = local - T0*cylinderDir; // calculate coeff in quadratic formula float a = v1.MagSquared3(); float b = (2.0f*v1).Dot3(v2); float c = v2.MagSquared3() - cylinderRadius*cylinderRadius; // calculate the stuff under the root sign, if it's negative no (real) solutions exist float d = b*b - 4.0f*a*c; if(d < 0.0f) // this means ray misses cylinder return false; float root = MFSqrt(d); float rcp2a = MFRcp(2.0f*a); float t1 = (-b - root)*rcp2a; float t2 = (-b + root)*rcp2a; if(t1 > 1.0f || t2 < 0.0f) return false; // the cylinder is beyond the ray.. if(capped || pCylinderTime || pResult) { float t = MFMax(t1, 0.0f); // get the t for the cylinders ray MFVector intersectedRay; intersectedRay.Mad3(rayDir, t, local); float ct = intersectedRay.Dot3(cylinderDir) * invMagSq; if(capped && (ct < 0.0f || ct > 1.0f)) { // we need to test the caps // TODO: this is REALLY slow!! can be majorly improved!! // generate a plane for the cap MFVector point, plane; if(rayD > 0.0f) { // the near one point = cylinderPos; plane = MFCollision_MakePlaneFromPointAndNormal(point, -cylinderDir); } else { // the far one point = cylinderPos + cylinderDir; plane = MFCollision_MakePlaneFromPointAndNormal(point, cylinderDir); } // test the ray against the plane bool collide = MFCollision_RayPlaneTest(rayPos, rayDir, plane, pResult); if(collide) { // calculate the intersection point intersectedRay.Mad3(rayDir, pResult->time, rayPos); intersectedRay.Sub3(intersectedRay, point); // and see if its within the cylinders radius if(intersectedRay.MagSquared3() <= cylinderRadius * cylinderRadius) { return true; } } return false; } if(pResult) { pResult->time = t; pResult->surfaceNormal.Mad3(cylinderDir, -ct, intersectedRay); pResult->surfaceNormal.Normalise3(); } if(pCylinderTime) { *pCylinderTime = ct; } } return true; }