//---------------------------------------------------------------------------- void AVector::Orthonormalize (AVector& vec0, AVector& vec1, AVector& vec2) { // If the input vectors are v0, v1, and v2, then the Gram-Schmidt // orthonormalization produces vectors u0, u1, and u2 as follows, // // u0 = v0/|v0| // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0| // u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1| // // where |A| indicates length of vector A and A*B indicates dot // product of vectors A and B. // Compute u0. vec0.Normalize(); // Compute u1. float dot0 = vec0.Dot(vec1); vec1 -= dot0*vec0; vec1.Normalize(); // Compute u2. float dot1 = vec1.Dot(vec2); dot0 = vec0.Dot(vec2); vec2 -= dot0*vec0 + dot1*vec1; vec2.Normalize(); }
//---------------------------------------------------------------------------- void EditRenderView_Scene::_RolateCamera(float horz, float vert) { Scene *scene = PX2_PROJ.GetScene(); CameraActor *camActor = scene->GetUseCameraActor(); if (VT_PERSPECTIVE == mViewType) { AVector rVector; AVector dVector; AVector uVector; camActor->GetRDUVector(rVector, dVector, uVector); // horz HMatrix incrH(AVector::UNIT_Z, -horz*0.5f); dVector = incrH * dVector; uVector = incrH * uVector; rVector = incrH * rVector; // vert Matrix3f kIncrV(rVector, -vert*0.5f); dVector = kIncrV * dVector; uVector = kIncrV * uVector; dVector.Normalize(); float dVectorAdj = dVector.Dot(AVector::UNIT_Z); float dVectorAdj1 = dVector.Dot(-AVector::UNIT_Z); if (dVectorAdj > 0.9f || dVectorAdj1 > 0.9f) return; AVector::Orthonormalize(dVector, uVector, rVector); camActor->LocalTransform.SetRotate(HMatrix(rVector, dVector, uVector, AVector::ZERO, true)); } }
//---------------------------------------------------------------------------- AVector CurveSegment::Normal (float u) const { AVector velocity = PU(u); AVector acceleration = PUU(u); float VDotV = velocity.Dot(velocity); float VDotA = velocity.Dot(acceleration); AVector normal = VDotV*acceleration - VDotA*velocity; normal.Normalize(); return normal; }
//---------------------------------------------------------------------------- void CurveSegment::GetFrame (float u, APoint& position, AVector& tangent, AVector& normal, AVector& binormal) const { position = P(u); AVector velocity = PU(u); AVector acceleration = PUU(u); float VDotV = velocity.Dot(velocity); float VDotA = velocity.Dot(acceleration); normal = VDotV*acceleration - VDotA*velocity; normal.Normalize(); tangent = velocity; tangent.Normalize(); binormal = tangent.Cross(normal); }
//---------------------------------------------------------------------------- bool Bound::TestIntersection (const Bound& bound, float tmax, const AVector& velocity0, const AVector& velocity1) const { if (bound.GetRadius() == 0.0f || GetRadius() == 0.0f) { return false; } AVector relVelocity = velocity1 - velocity0; // 相对速度 AVector cenDiff = bound.mCenter - mCenter; // 相对位移 float a = relVelocity.SquaredLength(); float c = cenDiff.SquaredLength(); float rSum = bound.mRadius + mRadius; float rSumSqr = rSum*rSum; if (a > 0.0f) { float b = cenDiff.Dot(relVelocity); if (b <= 0.0f) { if (-tmax*a <= b) { return a*c - b*b <= a*rSumSqr; } else { return tmax*(tmax*a + 2.0f*b) + c <= rSumSqr; } } } return c <= rSumSqr; }
//---------------------------------------------------------------------------- void HMatrix::MakePerspectiveProjection (const APoint& origin, const AVector& normal, const APoint& eye) { // +- -+ // M = | Dot(N,E-P)*I - E*N^T -(Dot(N,E-P)*I - E*N^T)*E | // | -N^t Dot(N,E) | // +- -+ // // where E is the eye point, P is a point on the plane, and N is a // unit-length plane normal. float dotND = normal.Dot(eye - origin); mEntry[ 0] = dotND - eye[0]*normal[0]; mEntry[ 1] = -eye[0]*normal[1]; mEntry[ 2] = -eye[0]*normal[2]; mEntry[ 3] = -(mEntry[0]*eye[0] + mEntry[1]*eye[1] + mEntry[2]*eye[2]); mEntry[ 4] = -eye[1]*normal[0]; mEntry[ 5] = dotND - eye[1]*normal[1]; mEntry[ 6] = -eye[1]*normal[2]; mEntry[ 7] = -(mEntry[4]*eye[0] + mEntry[5]*eye[1] + mEntry[6]*eye[2]); mEntry[ 8] = -eye[2]*normal[0]; mEntry[ 9] = -eye[2]*normal[1]; mEntry[10] = dotND- eye[2]*normal[2]; mEntry[11] = -(mEntry[8]*eye[0] + mEntry[9]*eye[1] + mEntry[10]*eye[2]); mEntry[12] = -normal[0]; mEntry[13] = -normal[1]; mEntry[14] = -normal[2]; mEntry[15] = eye.Dot(normal); }
//---------------------------------------------------------------------------- void ClodMeshes::UpdateClods () { // Adjust the triangle quantities for the clod meshes. The distance along // the camera direction controls the triangle quantities. A nonlinear // drop-off is used. for (int i = 0; i < 2; ++i) { AVector diff = mClod[i]->WorldBound.GetCenter() - mCamera->GetPosition(); float depth = diff.Dot(mCamera->GetDVector()); int targetRecord; if (depth <= mCamera->GetDMin()) { targetRecord = 0; } else if (depth >= mCamera->GetDMax()) { targetRecord = mClod[i]->GetNumRecords() - 1; } else { float dmin = mCamera->GetDMin(); float dmax = mCamera->GetDMax(); float ratio = Mathf::Pow((depth - dmin)/(dmax - dmin), 0.5f); targetRecord = (int)((mClod[i]->GetNumRecords() - 1)*ratio); } mClod[i]->TargetRecord() = targetRecord; } }
//---------------------------------------------------------------------------- bool PlanarShadowEffect::GetProjectionMatrix (int i, HMatrix& projection) { // Compute the equation for the shadow plane in world coordinates. APoint vertex[3]; mPlanes[i]->GetWorldTriangle(0, vertex); HPlane worldPlane(vertex[0], vertex[1], vertex[2]); // This is a conservative test to see whether a shadow should be cast. // This can cause incorrect results if the caster is large and intersects // the plane, but ordinarily we are not trying to cast shadows in such // situations. if (mShadowCaster->WorldBound.WhichSide(worldPlane) < 0) { // The shadow caster is on the far side of plane, so it cannot cast // a shadow. return false; } // Compute the projection matrix for the light source. Light* projector = mProjectors[i]; AVector normal = worldPlane.GetNormal(); if (projector->GetType() == Light::LT_DIRECTIONAL) { float NdD = normal.Dot(projector->DVector); if (NdD >= 0.0f) { // The projection must be onto the "positive side" of the plane. return false; } projection.MakeObliqueProjection(vertex[0], normal, projector->DVector); } else if (projector->GetType() == Light::LT_POINT || projector->GetType() == Light::LT_SPOT) { float NdE = projector->Position.Dot(normal); if (NdE <= 0.0f) { // The projection must be onto the "positive side" of the plane. return false; } projection.MakePerspectiveProjection(vertex[0], normal, projector->Position); } else { assertion(false, "Light type not supported.\n"); return false; } return true; }
//---------------------------------------------------------------------------- void AmbientRegionActor::_UpdateDirLightCamera() { AVector dir = AVector::AnglesToDirection(Mathf::DEG_TO_RAD*mHorAngle, Mathf::DEG_TO_RAD*mVerAngle); dir.Normalize(); Scene *scene = DynamicCast<Scene>(GetTopestParent()); if (scene) { EnvirParam *envirParam = scene->GetEnvirParam(); Light *lightDir = envirParam->GetLight_Dir(); Projector *projector = envirParam->GetLight_Dir_Projector(); lightDir->Ambient = Float4(mAmbientColor[0], mAmbientColor[1], mAmbientColor[2], mIntensity); lightDir->Intensity = mIntensity; lightDir->Diffuse = Float4(mDiffuseColor[0], mDiffuseColor[1], mDiffuseColor[2], 1.0f); lightDir->Specular = Float4(mSpecularColor[0], mSpecularColor[1], mSpecularColor[2], mSpecularPow); float upDot = dir.Dot(-AVector::UNIT_Z); if (upDot >= 0.99f) { } else { AVector upTemp = AVector::UNIT_Z; AVector right = dir.UnitCross(upTemp); AVector up = right.UnitCross(dir); lightDir->DVector = dir; lightDir->UVector = up; lightDir->RVector = right; APoint camPos = mLightCameraLookPosition - dir*mLightCameraLookDistance; projector->SetFrame(camPos, lightDir->DVector, lightDir->UVector, lightDir->RVector); } if (!projector->IsPerspective()) { projector->SetFrustum(0.1f, 100.0f, -mLightCameraExtent, mLightCameraExtent, -mLightCameraExtent, mLightCameraExtent); } else { projector->SetFrustum(mLightCameraExtent, 1.0f, 1.0f, 100.0f); } } }
//---------------------------------------------------------------------------- void Renderable::GetVisibleSet (Culler& culler, bool) { AdjustTransparent(); const Camera *camera = culler.GetCamera(); assertion(camera!=0, "camera must not be 0."); AVector cameraDir = camera->GetDVector(); AVector diff = WorldBound.GetCenter() - camera->GetPosition(); mEyeDistance = cameraDir.Dot(diff); culler.Insert(this); }
//---------------------------------------------------------------------------- void AVector::Orthonormalize (AVector& vec0, AVector& vec1, AVector& vec2) { // 输入的向量为v0,v1,v2,按照下面的方式进行Gram-Schmidt正交化 // // u0 = v0/|v0| // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0| // u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1| // // |A|代表向量A的长度;A*B代表两个向量“点乘” // 单位化 vec0.Normalize(); // 计算 u1 float dot0 = vec0.Dot(vec1); vec1 -= dot0*vec0; vec1.Normalize(); // 计算 u2 float dot1 = vec1.Dot(vec2); dot0 = vec0.Dot(vec2); vec2 -= dot0*vec0 + dot1*vec1; vec2.Normalize(); }
//---------------------------------------------------------------------------- void EU_CanvasStage::_RolateCamera(float horz, float vert) { Scene *scene = PX2_PROJ.GetScene(); horz *= 0.4f; vert *= 0.25f; if (VT_PERSPECTIVE == mViewType) { AVector rVector; AVector dVector; AVector uVector; mStageCameraNode->LocalTransform.GetRDUVector(rVector, dVector, uVector); // horz HMatrix incrH(AVector::UNIT_Z, -horz); dVector = incrH * dVector; uVector = incrH * uVector; rVector = incrH * rVector; // vert Matrix3f kIncrV(rVector, -vert); dVector = kIncrV * dVector; uVector = kIncrV * uVector; dVector.Normalize(); float dVectorAdj = dVector.Dot(AVector::UNIT_Z); float dVectorAdj1 = dVector.Dot(-AVector::UNIT_Z); if (dVectorAdj > 0.9f || dVectorAdj1 > 0.9f) return; AVector::Orthonormalize(dVector, uVector, rVector); mStageCameraNode->LocalTransform.SetRotate(HMatrix(rVector, dVector, uVector, AVector::ZERO, true)); } }
//---------------------------------------------------------------------------- int FramesMesh::_GetDirIndex(const AVector &dir) { AVector animVec = dir; animVec.Normalize(); AVector up = AVector::UNIT_Y; float dotVal = up.Dot(animVec); float rad = Mathf::ACos(dotVal); float degree = Mathf::RAD_TO_DEG * rad; bool isRight = (dir.X() > 0.0f); if (0 <= degree && degree < 22.5f) { return 0; } else if (22.5f <= degree && degree < 67.5f) { if (isRight) return 1; else return 7; } else if (67.5 <= degree && degree < 112.5f) { if (isRight) return 2; else return 6; } else if (112.5f <= degree && degree < 157.5f) { if (isRight) return 3; else return 5; } else if (157.5f <= degree && degree <= 180.0f) { return 4; } return 0; }
//---------------------------------------------------------------------------- float CurveSegment::Torsion (float u) const { AVector velocity = PU(u); AVector acceleration = PUU(u); AVector cross = velocity.Cross(acceleration); float denom = cross.SquaredLength(); if (denom >= Mathf::ZERO_TOLERANCE) { AVector jerk = PUUU(u); float numer = cross.Dot(jerk); return numer/denom; } else { // Torsion is indeterminate, just return 0. return 0.0f; } }
//---------------------------------------------------------------------------- void HMatrix::MakeObliqueProjection (const APoint& origin, const AVector& normal, const AVector& direction) { // The projection plane is Dot(N,X-P) = 0 where N is a 3-by-1 unit-length // normal vector and P is a 3-by-1 point on the plane. The projection // is oblique to the plane, in the direction of the 3-by-1 vector D. // Necessarily Dot(N,D) is not zero for this projection to make sense. // Given a 3-by-1 point U, compute the intersection of the line U+t*D // with the plane to obtain t = -Dot(N,U-P)/Dot(N,D). Then // // projection(U) = P + [I - D*N^T/Dot(N,D)]*(U-P) // // A 4-by-4 homogeneous transformation representing the projection is // // +- -+ // M = | D*N^T - Dot(N,D)*I -Dot(N,P)D | // | 0^T -Dot(N,D) | // +- -+ // // where M applies to [U^T 1]^T by M*[U^T 1]^T. The matrix is chosen so // that M[3][3] > 0 whenever Dot(N,D) < 0 (projection is onto the // "positive side" of the plane). float dotND = normal.Dot(direction); float dotNO = origin.Dot(normal); mEntry[ 0] = direction[0]*normal[0] - dotND; mEntry[ 1] = direction[0]*normal[1]; mEntry[ 2] = direction[0]*normal[2]; mEntry[ 3] = -dotNO*direction[0]; mEntry[ 4] = direction[1]*normal[0]; mEntry[ 5] = direction[1]*normal[1] - dotND; mEntry[ 6] = direction[1]*normal[2]; mEntry[ 7] = -dotNO*direction[1]; mEntry[ 8] = direction[2]*normal[0]; mEntry[ 9] = direction[2]*normal[1]; mEntry[10] = direction[2]*normal[2] - dotND; mEntry[11] = -dotNO*direction[2]; mEntry[12] = 0.0f; mEntry[13] = 0.0f; mEntry[14] = 0.0f; mEntry[15] = -dotND; }
//---------------------------------------------------------------------------- bool Bound::TestIntersection (const APoint& origin, const AVector& direction, float tmin, float tmax) const { if (GetRadius() == 0.0f) { return false; } AVector diff; float a0, a1, discr; if (tmin == -Mathf::MAX_REAL) { assertion(tmax == Mathf::MAX_REAL, "tmax must be infinity for a line.\n"); // Test for sphere-line intersection. diff = origin - mCenter; a0 = diff.Dot(diff) - mRadius*mRadius; a1 = direction.Dot(diff); discr = a1*a1 - a0; return discr >= 0.0f; } if (tmax == Mathf::MAX_REAL) { assertion(tmin == 0.0f, "tmin must be zero for a ray.\n"); // Test for sphere-ray intersection. AVector diff = origin - mCenter; a0 = diff.Dot(diff) - mRadius*mRadius; if (a0 <= 0.0f) { // The ray origin is inside the sphere. return true; } // else: The ray origin is outside the sphere. a1 = direction.Dot(diff); if (a1 >= 0.0f) { // The ray forms an acute angle with diff, and so the ray is // directed from the sphere. Thus, the ray origin is outside // the sphere, and points P+t*D for t >= 0 are even farther // away from the sphere. return false; } discr = a1*a1 - a0; return discr >= 0.0f; } assertion(tmax > tmin, "tmin < tmax is required for a segment.\n"); // Test for sphere-segment intersection. float segExtent = 0.5f*(tmin + tmax); APoint segOrigin = origin + segExtent*direction; diff = segOrigin - mCenter; a0 = diff.Dot(diff) - mRadius*mRadius; a1 = direction.Dot(diff); discr = a1*a1 - a0; if (discr < 0.0f) { return false; } float tmp0 = segExtent*segExtent + a0; float tmp1 = 2.0f*a1*segExtent; float qm = tmp0 - tmp1; float qp = tmp0 + tmp1; if (qm*qp <= 0.0f) { return true; } return qm > 0.0f && Mathf::FAbs(a1) < segExtent; }
//---------------------------------------------------------------------------- void SceneNodeCtrl::OnMotion(bool leftDown, RenderStep *renderStep, PX2::APoint posNow, PX2::APoint posBefore) { PX2_UNUSED(leftDown); PX2_UNUSED(renderStep); Renderer *renderer = renderStep->GetRenderer(); Camera *camera = renderStep->GetCamera(); // 光标移动更新 if (DT_NONE == mDragType) { GeoObjFactory factory; DragType dt = GetDragType(renderStep, posNow); Movable *ctrlMov = 0; Float4 colorYellowAlpha = Float4(1.0f, 1.0f, 0.0f, 0.3f); if (DT_X == dt) { ctrlMov = GetCurrentCtrlX(); factory.UpdateCtrlColor(renderer, ctrlMov, Float4::YELLOW); } else if (DT_Y == dt) { ctrlMov = GetCurrentCtrlY(); factory.UpdateCtrlColor(renderer, ctrlMov, Float4::YELLOW); } else if (DT_Z == dt) { ctrlMov = GetCurrentCtrlZ(); factory.UpdateCtrlColor(renderer, ctrlMov, Float4::YELLOW); } else if (DT_XY == dt) { factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXY(), colorYellowAlpha); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlYZ(), Float4::ZERO); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXZ(), Float4::ZERO); } else if (DT_YZ == dt) { factory.UpdateCtrlColor1(renderer, GetCurrentCtrlYZ(), colorYellowAlpha); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXY(), Float4::ZERO); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXZ(), Float4::ZERO); } else if (DT_XZ == dt) { factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXZ(), colorYellowAlpha); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXY(), Float4::ZERO); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlYZ(), Float4::ZERO); } else if (DT_XYZ == dt) { factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXYZ(), Float4::YELLOW); } else if (DT_NONE == dt) { factory.UpdateCtrlColor(renderer, GetCurrentCtrlX(), Float4::RED); factory.UpdateCtrlColor(renderer, GetCurrentCtrlY(), Float4::GREEN); factory.UpdateCtrlColor(renderer, GetCurrentCtrlZ(), Float4::BLUE); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXY(), Float4::ZERO); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlYZ(), Float4::ZERO); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXZ(), Float4::ZERO); factory.UpdateCtrlColor1(renderer, GetCurrentCtrlXYZ(), Float4::WHITE); } if (DT_NONE == dt) { Event *ent = EditEventSpace::CreateEventX(EditEventSpace::SceneNodeDrag); ent->SetData<int>(0); EventWorld::GetSingleton().BroadcastingLocalEvent(ent); } else { Event *ent = EditEventSpace::CreateEventX(EditEventSpace::SceneNodeDrag); ent->SetData<int>(1); EventWorld::GetSingleton().BroadcastingLocalEvent(ent); } } if (DT_NONE == mDragType) return; else { Event *ent = EditEventSpace::CreateEventX(EditEventSpace::SceneNodeDrag); ent->SetData<int>(1); EventWorld::GetSingleton().BroadcastingLocalEvent(ent); } int numObjs = PX2_SELECTION.GetNumObjects(); if (0 == numObjs) return; // get pickPoint with the plane TriMesh *meshHelp = PX2_GR.GetXYPlane(); if (DT_X == mDragType) { if (LT_PERSPECTIVE == mLookType || LT_TOP == mLookType) meshHelp = PX2_GR.GetXYPlane(); else if (LT_FRONT == mLookType) meshHelp = PX2_GR.GetXZPlane(); } else if (DT_Y == mDragType) { meshHelp = PX2_GR.GetXYPlane(); } else if (DT_Z == mDragType) { AVector cameraDir = camera->GetDVector(); cameraDir.Normalize(); float dotVal = Mathf::FAbs(cameraDir.Dot(AVector::UNIT_X)); if (dotVal > 0.7f) { meshHelp = PX2_GR.GetYZPlane(); } else { meshHelp = PX2_GR.GetXZPlane(); } } else if (DT_XY == mDragType) { meshHelp = PX2_GR.GetXYPlane(); } else if (DT_YZ == mDragType) { meshHelp = PX2_GR.GetYZPlane(); } else if (DT_XZ == mDragType) { meshHelp = PX2_GR.GetXZPlane(); } else if (DT_XYZ == mDragType) { meshHelp = PX2_GR.GetXYPlane(); } meshHelp->WorldTransform.SetTranslate(GetPosition()); // get pick ray APoint rayOrigin_Now; AVector rayDir_Now; renderStep->GetPickRay(posNow.X(), posNow.Z(), rayOrigin_Now, rayDir_Now); APoint rayOrigin_Before; AVector rayDir_Before; renderStep->GetPickRay(posBefore.X(), posBefore.Z(), rayOrigin_Before, rayDir_Before); // pick Picker pickerNow; pickerNow.Execute(meshHelp, rayOrigin_Now, rayDir_Now, 0.0f, Mathf::MAX_REAL); float lengthNow = pickerNow.GetClosestToZero().T; APoint positionNow(rayOrigin_Now + rayDir_Now*lengthNow); Picker pickerOrigin; pickerOrigin.Execute(meshHelp, rayOrigin_Before, rayDir_Before, 0.0f, Mathf::MAX_REAL); float lengthBefore = pickerOrigin.GetClosestToZero().T; APoint positionBefore(rayOrigin_Before + rayDir_Before*lengthBefore); if (pickerNow.Records.empty() || pickerOrigin.Records.empty()) return; AVector transMoved = positionNow - positionBefore; AVector transDir = transMoved; transDir.Normalize(); float transValue = 0.0f; float transValue1 = 0.0f; AVector transVec; AVector rolateVec; AVector dirX = mDirX; AVector dirY = mDirY; AVector dirZ = mDirZ; if (DT_X == mDragType) { transValue = transMoved.Dot(dirX); transVec = dirX * transValue; rolateVec.X() = transMoved.Length() *(1.0f - Mathf::FAbs(transDir.Dot(dirX))); AVector vec = transDir.Cross(dirX); rolateVec.X() *= Mathf::Sign(vec.Z()); } else if (DT_Y == mDragType) { transValue = transMoved.Dot(dirY); transVec = dirY * transValue; rolateVec.Y() = transMoved.Length() *(1.0f - Mathf::FAbs(transDir.Dot(dirY))); AVector vec = transDir.Cross(dirY); rolateVec.Y() *= Mathf::Sign(vec.Z()); } else if (DT_Z == mDragType) { transValue = transMoved.Dot(dirZ); transVec = dirZ * transValue; rolateVec.Z() = transMoved.Length() *(1.0f - Mathf::FAbs(transDir.Dot(dirZ))); rolateVec.Z() *= Mathf::Sign(posNow.X() - posBefore.X()); } else if (DT_XY == mDragType) { transValue = transMoved.Dot(dirX); transValue1 = transMoved.Dot(dirY); transVec = dirX * transValue + dirY * transValue1; } else if (DT_YZ == mDragType) { transValue = transMoved.Dot(dirY); transValue1 = transMoved.Dot(dirZ); transVec = dirY * transValue + dirZ * transValue1; } else if (DT_XZ == mDragType) { transValue = transMoved.Dot(dirX); transValue1 = transMoved.Dot(dirZ); transVec = dirX * transValue + dirZ * transValue1; } else if (DT_XYZ == mDragType) { float transValue0 = Mathf::FAbs(transMoved.Dot(dirX)); float transValue1 = Mathf::FAbs(transMoved.Dot(dirY)); float transValue2 = Mathf::FAbs(transMoved.Dot(dirZ)); float trans = (transValue0 + transValue1 + transValue2) / 3.0f; trans *= Mathf::Sign(transMoved.Y()); transVec = AVector(trans, trans, trans); } if (CT_SCALE == mCtrlType) transVec *= 0.5f; HMatrix parentMat = mParentRotateMat.Inverse(); transVec = parentMat * transVec; if (CT_TRANSLATE == mCtrlType) { PX2_SELECTION.Translate(transVec); UpdateCtrlTrans(); } else if (CT_ROLATE == mCtrlType) { PX2_SELECTION.AddRolate(rolateVec); } else if (CT_SCALE == mCtrlType) { if (DT_XYZ == mDragType) PX2_SELECTION.AddScale(transVec); } Object *obj = PX2_SELECTION.GetFirstObject(); if (obj) { Event *ent = EditEventSpace::CreateEventX( EditEventSpace::ObjectTransformChanged); ent->SetData<Object*>(obj); EventWorld::GetSingleton().BroadcastingLocalEvent(ent); } mCtrlsGroup->Update(Time::GetTimeInSeconds(), false); }
//---------------------------------------------------------------------------- void SurfacePatch::ComputePrincipalCurvatureInfo (float u, float v, float& curv0, float& curv1, AVector& dir0, AVector& dir1) { // Tangents: T0 = dP/du = (x_u,y_u,z_u), T1 = dP/dv = (x_v,y_v,z_v) // Normal: N = Cross(T0,T1)/Length(Cross(T0,T1)) // Metric Tensor: G = +- -+ // | Dot(T0,T0) Dot(T0,T1) | // | Dot(T1,T0) Dot(T1,T1) | // +- -+ // // Curvature Tensor: B = +- -+ // | -Dot(N,T0_u) -Dot(N,T0_v) | // | -Dot(N,T1_u) -Dot(N,T1_v) | // +- -+ // // Principal curvatures k are the generalized eigenvalues of // // Bw = kGw // // If k is a curvature and w=(a,b) is the corresponding solution to // Bw = kGw, then the principal direction as a 3D vector is d = a*U+b*V. // // Let k1 and k2 be the principal curvatures. The mean curvature // is (k1+k2)/2 and the Gaussian curvature is k1*k2. // Compute the derivatives. AVector derU = PU(u, v); AVector derV = PV(u, v); AVector derUU = PUU(u, v); AVector derUV = PUV(u, v); AVector derVV = PVV(u, v); // Compute the metric tensor. float metricTensor[2][2]; metricTensor[0][0] = derU.Dot(derU); metricTensor[0][1] = derU.Dot(derV); metricTensor[1][0] = metricTensor[0][1]; metricTensor[1][1] = derV.Dot(derV); // Compute the curvature tensor. AVector normal = derU.UnitCross(derV); float curvatureTensor[2][2]; curvatureTensor[0][0] = -normal.Dot(derUU); curvatureTensor[0][1] = -normal.Dot(derUV); curvatureTensor[1][0] = curvatureTensor[0][1]; curvatureTensor[1][1] = -normal.Dot(derVV); // Characteristic polynomial is 0 = det(B-kG) = c2*k^2+c1*k+c0. float c0 = curvatureTensor[0][0]*curvatureTensor[1][1] - curvatureTensor[0][1]*curvatureTensor[1][0]; float c1 = 2.0f*curvatureTensor[0][1]* metricTensor[0][1] - curvatureTensor[0][0]*metricTensor[1][1] - curvatureTensor[1][1]*metricTensor[0][0]; float c2 = metricTensor[0][0]*metricTensor[1][1] - metricTensor[0][1]*metricTensor[1][0]; // Principal curvatures are roots of characteristic polynomial. float temp = Mathf::Sqrt(Mathf::FAbs(c1*c1 - 4.0f*c0*c2)); curv0 = -0.5f*(c1+temp); curv1 = 0.5f*(-c1+temp); // Principal directions are solutions to (B-kG)w = 0, // w1 = (b12-k1*g12,-(b11-k1*g11)) OR (b22-k1*g22,-(b12-k1*g12)) float a0 = curvatureTensor[0][1] - curv0*metricTensor[0][1]; float a1 = curv0*metricTensor[0][0] - curvatureTensor[0][0]; float length = Mathf::Sqrt(a0*a0 + a1*a1); if (length >= Mathf::ZERO_TOLERANCE) { dir0 = a0*derU + a1*derV; } else { a0 = curvatureTensor[1][1] - curv0*metricTensor[1][1]; a1 = curv0*metricTensor[0][1] - curvatureTensor[0][1]; length = Mathf::Sqrt(a0*a0 + a1*a1); if (length >= Mathf::ZERO_TOLERANCE) { dir0 = a0*derU + a1*derV; } else { // Umbilic (surface is locally sphere, any direction principal). dir0 = derU; } } dir0.Normalize(); // Second tangent is cross product of first tangent and normal. dir1 = dir0.Cross(normal); }
//---------------------------------------------------------------------------- bool Portal::ReducedFrustum (const Culler& culler, float reducedFrustum[6]) { // The portal polygon is transformed into the camera coordinate system // and projected onto the near plane. An axis-aligned bounding rectangle // is computed for the projected points and clipped against the left, // right, bottom, and top frustum planes. The result is itself an // axis-aligned bounding rectangle that is used to define a "reduced // frustum" to be used for drawing what is visible through the portal // polygon. // // The algorithm must handle the situation when portal polygon vertices // are behind the observer. Imagine standing in a room with a doorway // immediately to your left. Part of the doorway frame is in front of // you (and visible) and part of it is behind you (and not visible). // A portal point is represented by P = E + d*D + u*U + r*R, where E is // the world location for the eye point, D is the camera's world direction // vector, U is the camera's world up vector, and R is the camera's world // right vector. The camera coordinates for the portal point are (d,u,r). // If d > 0, P is in front of the eye point and has a projection onto the // near plane d = n. If d < 0, P is behind the eye point and does not // have a projection onto the near plane. If d = 0, P projects to // "infinity" on the near plane, a problematic case to deal with. // // To avoid dealing with d = 0, choose a small value e such that // 0 < e < n. The portal polygon is clipped against the plane d = e, // keeping only that portion whose points satisfy d >= e. The clipped // polygon always has a projection onto the near plane. The axis-aligned // bounding box for this projection is computed; clipped against the // left, right, bottom, and top of the frustum; and the result used to // define the reduced frustum. All this is designed for an inexact // culling of the objects in the adjacent room, so it is useful to avoid // preserving the topology of the portal polygon as it is clipped. // Instead, the portal polygon vertices with d > e are projected and // the intersection points of portal polygon edges with d = e are // computed and projected. The axis-aligned bounding box is computed for // the projections, a process that does not require knowing the polygon // topology. The algorithm is essentially the one used for clipping a // convex polygon against the view frustum in the software renderer. The // polygon vertices are traversed in-order and the signs of the d values // are updated accordingly. This avoids computing d-signs twice per // vertex. const Camera* camera = culler.GetCamera(); const float* frustum = culler.GetFrustum(); float rmin = +Mathf::MAX_REAL; // left float rmax = -Mathf::MAX_REAL; // right float umin = +Mathf::MAX_REAL; // bottom float umax = -Mathf::MAX_REAL; // top AVector diff; APoint vertexCam; int i; if (camera->IsPerspective()) { const float epsilon = 1e-6f, invEpsilon = 1e+6f; int firstSign = 0, lastSign = 0; // in {-1,0,1} bool signChange = false; APoint firstVertex = APoint::ORIGIN; APoint lastVertex = APoint::ORIGIN; float NdD, UdD, RdD, t; for (i = 0; i < mNumVertices; i++) { diff = mWorldVertices[i] - camera->GetPosition(); vertexCam[0] = diff.Dot(camera->GetDVector()); vertexCam[1] = diff.Dot(camera->GetUVector()); vertexCam[2] = diff.Dot(camera->GetRVector()); vertexCam[3] = 1.0f; if (vertexCam[0] > epsilon) { if (firstSign == 0) { firstSign = 1; firstVertex = vertexCam; } NdD = frustum[Camera::VF_DMIN]/vertexCam[0]; UdD = vertexCam[1]*NdD; RdD = vertexCam[2]*NdD; if (UdD < umin) { umin = UdD; } if (UdD > umax) { umax = UdD; } if (RdD < rmin) { rmin = RdD; } if (RdD > rmax) { rmax = RdD; } if (lastSign < 0) { signChange = true; } lastSign = 1; } else { if (firstSign == 0) { firstSign = -1; firstVertex = vertexCam; } if (lastSign > 0) { signChange = true; } lastSign = -1; } if (signChange) { diff = vertexCam - lastVertex; t = (epsilon - lastVertex[0])/diff[0]; NdD = frustum[Camera::VF_DMIN]*invEpsilon; UdD = (lastVertex[1] + t*diff[1])*NdD; RdD = (lastVertex[2] + t*diff[2])*NdD; if (UdD < umin) { umin = UdD; } if (UdD > umax) { umax = UdD; } if (RdD < rmin) { rmin = RdD; } if (RdD > rmax) { rmax = RdD; } signChange = false; } lastVertex = vertexCam; } if (firstSign*lastSign < 0) { // Process the last polygon edge. diff = firstVertex - lastVertex; t = (epsilon - lastVertex[0])/diff[0]; UdD = (lastVertex[1] + t*diff[1])*invEpsilon; RdD = (lastVertex[2] + t*diff[2])*invEpsilon; if (UdD < umin) { umin = UdD; } if (UdD > umax) { umax = UdD; } if (RdD < rmin) { rmin = RdD; } if (RdD > rmax) { rmax = RdD; } } } else { for (i = 0; i < mNumVertices; i++) { diff = mWorldVertices[i] - camera->GetPosition(); vertexCam[1] = diff.Dot(camera->GetUVector()); vertexCam[2] = diff.Dot(camera->GetRVector()); if (vertexCam[1] < umin) { umin = vertexCam[1]; } if (vertexCam[1] > umax) { umax = vertexCam[1]; } if (vertexCam[2] < rmin) { rmin = vertexCam[2]; } if (vertexCam[2] > rmax) { rmax = vertexCam[2]; } } } // Test whether the axis-aligned bounding rectangle is outside the current // frustum. If it is, the adjoining room need not be visited. if (frustum[Camera::VF_RMIN] >= rmax || frustum[Camera::VF_RMAX] <= rmin || frustum[Camera::VF_UMIN] >= umax || frustum[Camera::VF_UMAX] <= umin) { return false; } // The axis-aligned bounding rectangle intersects the current frustum. // Reduce the frustum for use in drawing the adjoining room. for (int j = 0; j < 6; ++j) { reducedFrustum[j] = frustum[j]; } if (reducedFrustum[Camera::VF_RMIN] < rmin) { reducedFrustum[Camera::VF_RMIN] = rmin; } if (reducedFrustum[Camera::VF_RMAX] > rmax) { reducedFrustum[Camera::VF_RMAX] = rmax; } if (reducedFrustum[Camera::VF_UMIN] < umin) { reducedFrustum[Camera::VF_UMIN] = umin; } if (reducedFrustum[Camera::VF_UMAX] > umax) { reducedFrustum[Camera::VF_UMAX] = umax; } return true; }
//---------------------------------------------------------------------------- void SimpleBumpMapEffect::ComputeLightVectors (Triangles* mesh, const AVector& worldLightDirection) { // The tangent-space coordinates for the light direction vector at each // vertex is stored in the color0 channel. The computations use the // vertex normals and the texture coordinates for the base mesh, which // are stored in the tcoord0 channel. Thus, the mesh must have positions, // normals, colors (unit 0), and texture coordinates (unit 0). // The light direction D is in world-space coordinates. Negate it, // transform it to model-space coordinates, and then normalize it. The // world-space direction is unit-length, but the geometric primitive // might have non-unit scaling in its model-to-world transformation, in // which case the normalization is necessary. AVector modelLightDirection = -mesh->WorldTransform.Inverse()*worldLightDirection; // Set the light vectors to (0,0,0) as a flag that the quantity has not // yet been computed. The probability that a light vector is actually // (0,0,0) should be small, so the flag system should save computation // time overall. VertexBufferAccessor vba(mesh); Float3 black(0.0f, 0.0f, 0.0f); int i; for (i = 0; i < vba.GetNumVertices(); ++i) { vba.Color<Float3>(0, i) = black; } int numTriangles = mesh->GetNumTriangles(); for (int t = 0; t < numTriangles; ++t) { // Get the triangle vertices and attributes. int v0, v1, v2; if (!mesh->GetTriangle(t, v0, v1, v2)) { continue; } APoint position[3] = { vba.Position<Float3>(v0), vba.Position<Float3>(v1), vba.Position<Float3>(v2) }; AVector normal[3] = { vba.Normal<Float3>(v0), vba.Normal<Float3>(v1), vba.Normal<Float3>(v2) }; Float3* color[3] = { &vba.Color<Float3>(0, v0), &vba.Color<Float3>(0, v1), &vba.Color<Float3>(0, v2) }; Float2 tcoord[3] = { vba.TCoord<Float2>(0, v0), vba.TCoord<Float2>(0, v1), vba.TCoord<Float2>(0, v2) }; for (i = 0; i < 3; ++i) { Float3& colorref = *color[i]; if (colorref != black) { continue; } int iP = (i == 0) ? 2 : i - 1; int iN = (i + 1) % 3; AVector tangent; if (!ComputeTangent(position[i], tcoord[i], position[iN], tcoord[iN], position[iP], tcoord[iP], tangent)) { // The texture coordinate mapping is not properly defined for // this. Just say that the tangent space light vector points // in the same direction as the surface normal. colorref[0] = normal[i][0]; colorref[1] = normal[i][1]; colorref[2] = normal[i][2]; continue; } // Project T into the tangent plane by projecting out the surface // normal N, and then make it unit length. tangent -= normal[i].Dot(tangent)*(normal[i]); tangent.Normalize(); // Compute the bitangent B, another tangent perpendicular to T. AVector bitangent = normal[i].UnitCross(tangent); // The set {T,B,N} is a right-handed orthonormal set. The // negated light direction U = -D is represented in this // coordinate system as // U = Dot(U,T)*T + Dot(U,B)*B + Dot(U,N)*N float dotUT = modelLightDirection.Dot(tangent); float dotUB = modelLightDirection.Dot(bitangent); float dotUN = modelLightDirection.Dot(normal[i]); // Transform the light vector into [0,1]^3 to make it a valid // Float3 object. colorref[0] = 0.5f*(dotUT + 1.0f); colorref[1] = 0.5f*(dotUB + 1.0f); colorref[2] = 0.5f*(dotUN + 1.0f); } } }
//---------------------------------------------------------------------------- void Triangles::UpdateModelTangentsUseTCoords (VertexBufferAccessor& vba) { // Each vertex can be visited multiple times, so compute the tangent // space only on the first visit. Use the zero vector as a flag for the // tangent vector not being computed. const int numVertices = vba.GetNumVertices(); bool hasTangent = vba.HasTangent(); Float3 zero(0.0f, 0.0f, 0.0f); int i; if (hasTangent) { for (i = 0; i < numVertices; ++i) { vba.Tangent<Float3>(i) = zero; } } else { for (i = 0; i < numVertices; ++i) { vba.Binormal<Float3>(i) = zero; } } const int numTriangles = GetNumTriangles(); for (i = 0; i < numTriangles; i++) { // Get the triangle vertices' positions, normals, tangents, and // texture coordinates. int v0, v1, v2; if (!GetTriangle(i, v0, v1, v2)) { continue; } APoint locPosition[3] = { vba.Position<Float3>(v0), vba.Position<Float3>(v1), vba.Position<Float3>(v2) }; AVector locNormal[3] = { vba.Normal<Float3>(v0), vba.Normal<Float3>(v1), vba.Normal<Float3>(v2) }; AVector locTangent[3] = { (hasTangent ? vba.Tangent<Float3>(v0) : vba.Binormal<Float3>(v0)), (hasTangent ? vba.Tangent<Float3>(v1) : vba.Binormal<Float3>(v1)), (hasTangent ? vba.Tangent<Float3>(v2) : vba.Binormal<Float3>(v2)) }; Float2 locTCoord[3] = { vba.TCoord<Float2>(0, v0), vba.TCoord<Float2>(0, v1), vba.TCoord<Float2>(0, v2) }; for (int curr = 0; curr < 3; ++curr) { Float3 currLocTangent = (Float3)locTangent[curr]; if (currLocTangent != zero) { // This vertex has already been visited. continue; } // Compute the tangent space at the vertex. AVector norvec = locNormal[curr]; int prev = ((curr + 2) % 3); int next = ((curr + 1) % 3); AVector tanvec = ComputeTangent( locPosition[curr], locTCoord[curr], locPosition[next], locTCoord[next], locPosition[prev], locTCoord[prev]); // Project T into the tangent plane by projecting out the surface // normal N, and then making it unit length. tanvec -= norvec.Dot(tanvec)*norvec; tanvec.Normalize(); // Compute the bitangent B, another tangent perpendicular to T. AVector binvec = norvec.UnitCross(tanvec); if (vba.HasTangent()) { locTangent[curr] = tanvec; if (vba.HasBinormal()) { vba.Binormal<Float3>(curr) = binvec; } } else { vba.Binormal<Float3>(curr) = tanvec; } } } }
//---------------------------------------------------------------------------- void Triangles::UpdateModelTangentsUseGeometry (VertexBufferAccessor& vba) { // Compute the matrix of normal derivatives. const int numVertices = vba.GetNumVertices(); HMatrix* dNormal = new1<HMatrix>(numVertices); HMatrix* wwTrn = new1<HMatrix>(numVertices); HMatrix* dwTrn = new1<HMatrix>(numVertices); memset(wwTrn, 0, numVertices*sizeof(HMatrix)); memset(dwTrn, 0, numVertices*sizeof(HMatrix)); const int numTriangles = GetNumTriangles(); int i, row, col; for (i = 0; i < numTriangles; ++i) { // Get the vertex indices for the triangle. int v[3]; if (!GetTriangle(i, v[0], v[1], v[2])) { continue; } for (int j = 0; j < 3; j++) { // Get the vertex positions and normals. int v0 = v[j]; int v1 = v[(j + 1) % 3]; int v2 = v[(j + 2) % 3]; APoint pos0 = vba.Position<Float3>(v0); APoint pos1 = vba.Position<Float3>(v1); APoint pos2 = vba.Position<Float3>(v2); AVector nor0 = vba.Normal<Float3>(v0); AVector nor1 = vba.Normal<Float3>(v1); AVector nor2 = vba.Normal<Float3>(v2); // Compute the edge from pos0 to pos1, project it to the tangent // plane of the vertex, and compute the difference of adjacent // normals. AVector edge = pos1 - pos0; AVector proj = edge - edge.Dot(nor0)*nor0; AVector diff = nor1 - nor0; for (row = 0; row < 3; ++row) { for (col = 0; col < 3; ++col) { wwTrn[v0][row][col] += proj[row]*proj[col]; dwTrn[v0][row][col] += diff[row]*proj[col]; } } // Compute the edge from pos0 to pos2, project it to the tangent // plane of the vertex, and compute the difference of adjacent // normals. edge = pos2 - pos0; proj = edge - edge.Dot(nor0)*nor0; diff = nor2 - nor0; for (row = 0; row < 3; ++row) { for (col = 0; col < 3; ++col) { wwTrn[v0][row][col] += proj[row]*proj[col]; dwTrn[v0][row][col] += diff[row]*proj[col]; } } } } // Add N*N^T to W*W^T for numerical stability. In theory 0*0^T is added // to D*W^T, but of course no update is needed in the implementation. // Compute the matrix of normal derivatives. for (i = 0; i < numVertices; ++i) { AVector nor = vba.Normal<Float3>(i); for (row = 0; row < 3; ++row) { for (col = 0; col < 3; ++col) { wwTrn[i][row][col] = 0.5f*wwTrn[i][row][col] + nor[row]*nor[col]; dwTrn[i][row][col] *= 0.5f; } } wwTrn[i].SetColumn(3, APoint::ORIGIN); dNormal[i] = dwTrn[i]*wwTrn[i].Inverse(); } delete1(wwTrn); delete1(dwTrn); // If N is a unit-length normal at a vertex, let U and V be unit-length // tangents so that {U, V, N} is an orthonormal set. Define the matrix // J = [U | V], a 3-by-2 matrix whose columns are U and V. Define J^T // to be the transpose of J, a 2-by-3 matrix. Let dN/dX denote the // matrix of first-order derivatives of the normal vector field. The // shape matrix is // S = (J^T * J)^{-1} * J^T * dN/dX * J = J^T * dN/dX * J // where the superscript of -1 denotes the inverse. (The formula allows // for J built from non-perpendicular vectors.) The matrix S is 2-by-2. // The principal curvatures are the eigenvalues of S. If k is a principal // curvature and W is the 2-by-1 eigenvector corresponding to it, then // S*W = k*W (by definition). The corresponding 3-by-1 tangent vector at // the vertex is called the principal direction for k, and is J*W. The // principal direction for the minimum principal curvature is stored as // the mesh tangent. The principal direction for the maximum principal // curvature is stored as the mesh bitangent. for (i = 0; i < numVertices; ++i) { // Compute U and V given N. AVector norvec = vba.Normal<Float3>(i); AVector uvec, vvec; AVector::GenerateComplementBasis(uvec, vvec, norvec); // Compute S = J^T * dN/dX * J. In theory S is symmetric, but // because we have estimated dN/dX, we must slightly adjust our // calculations to make sure S is symmetric. float s01 = uvec.Dot(dNormal[i]*vvec); float s10 = vvec.Dot(dNormal[i]*uvec); float sAvr = 0.5f*(s01 + s10); float smat[2][2] = { { uvec.Dot(dNormal[i]*uvec), sAvr }, { sAvr, vvec.Dot(dNormal[i]*vvec) } }; // Compute the eigenvalues of S (min and max curvatures). float trace = smat[0][0] + smat[1][1]; float det = smat[0][0]*smat[1][1] - smat[0][1]*smat[1][0]; float discr = trace*trace - 4.0f*det; float rootDiscr = Mathf::Sqrt(Mathf::FAbs(discr)); float minCurvature = 0.5f*(trace - rootDiscr); // float maxCurvature = 0.5f*(trace + rootDiscr); // Compute the eigenvectors of S. AVector evec0(smat[0][1], minCurvature - smat[0][0], 0.0f); AVector evec1(minCurvature - smat[1][1], smat[1][0], 0.0f); AVector tanvec, binvec; if (evec0.SquaredLength() >= evec1.SquaredLength()) { evec0.Normalize(); tanvec = evec0.X()*uvec + evec0.Y()*vvec; binvec = norvec.Cross(tanvec); } else { evec1.Normalize(); tanvec = evec1.X()*uvec + evec1.Y()*vvec; binvec = norvec.Cross(tanvec); } if (vba.HasTangent()) { vba.Tangent<Float3>(i) = tanvec; } if (vba.HasBinormal()) { vba.Binormal<Float3>(i) = binvec; } } delete1(dNormal); }
//---------------------------------------------------------------------------- int Culler::WhichSide (const HPlane& plane) const { // The plane is N*(X-C) = 0 where the * indicates dot product. The signed // distance from the camera location E to the plane is N*(E-C). float NdEmC = plane.DistanceTo(mCamera->GetPosition()); AVector normal = plane.GetNormal(); float NdD = normal.Dot(mCamera->GetDVector()); float NdU = normal.Dot(mCamera->GetUVector()); float NdR = normal.Dot(mCamera->GetRVector()); float FdN = mFrustum[Camera::VF_DMAX]/mFrustum[Camera::VF_DMIN]; int positive = 0, negative = 0; float sgnDist; // Check near-plane vertices. float PDMin = mFrustum[Camera::VF_DMIN]*NdD; float NUMin = mFrustum[Camera::VF_UMIN]*NdU; float NUMax = mFrustum[Camera::VF_UMAX]*NdU; float NRMin = mFrustum[Camera::VF_RMIN]*NdR; float NRMax = mFrustum[Camera::VF_RMAX]*NdR; // V = E + dmin*D + umin*U + rmin*R // N*(V-C) = N*(E-C) + dmin*(N*D) + umin*(N*U) + rmin*(N*R) sgnDist = NdEmC + PDMin + NUMin + NRMin; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } // V = E + dmin*D + umin*U + rmax*R // N*(V-C) = N*(E-C) + dmin*(N*D) + umin*(N*U) + rmax*(N*R) sgnDist = NdEmC + PDMin + NUMin + NRMax; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } // V = E + dmin*D + umax*U + rmin*R // N*(V-C) = N*(E-C) + dmin*(N*D) + umax*(N*U) + rmin*(N*R) sgnDist = NdEmC + PDMin + NUMax + NRMin; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } // V = E + dmin*D + umax*U + rmax*R // N*(V-C) = N*(E-C) + dmin*(N*D) + umax*(N*U) + rmax*(N*R) sgnDist = NdEmC + PDMin + NUMax + NRMax; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } // check far-plane vertices (s = dmax/dmin) float PDMax = mFrustum[Camera::VF_DMAX]*NdD; float FUMin = FdN*NUMin; float FUMax = FdN*NUMax; float FRMin = FdN*NRMin; float FRMax = FdN*NRMax; // V = E + dmax*D + umin*U + rmin*R // N*(V-C) = N*(E-C) + dmax*(N*D) + s*umin*(N*U) + s*rmin*(N*R) sgnDist = NdEmC + PDMax + FUMin + FRMin; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } // V = E + dmax*D + umin*U + rmax*R // N*(V-C) = N*(E-C) + dmax*(N*D) + s*umin*(N*U) + s*rmax*(N*R) sgnDist = NdEmC + PDMax + FUMin + FRMax; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } // V = E + dmax*D + umax*U + rmin*R // N*(V-C) = N*(E-C) + dmax*(N*D) + s*umax*(N*U) + s*rmin*(N*R) sgnDist = NdEmC + PDMax + FUMax + FRMin; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } // V = E + dmax*D + umax*U + rmax*R // N*(V-C) = N*(E-C) + dmax*(N*D) + s*umax*(N*U) + s*rmax*(N*R) sgnDist = NdEmC + PDMax + FUMax + FRMax; if (sgnDist > 0.0f) { positive++; } else if (sgnDist < 0.0f) { negative++; } if (positive > 0) { if (negative > 0) { // Frustum straddles the plane. return 0; } // Frustum is fully on the positive side. return +1; } // Frustum is fully on the negative side. return -1; }