//---------------------------------------------------------------------------- void Node::GetVisibleSetRenderObject(Culler& rCuller, Bool noCull) { const Camera* pCamera = rCuller.GetCamera(); // Add this RenderObject if it's not culled. (Its bounding volume is // smaller or equal the bounding volume of this node). if (mspRenderObject && ((pCamera->GetLayerMask() & mLayerMask) != 0)) { if (GetQuantity() == 0) { const Vector3F& rCamPos = rCuller.GetCamera()->GetLocation(); Vector3F pos = mspRenderObjectWorldBound->GetCenter() - rCamPos; rCuller.Insert(mspRenderObject, &World, pos); } else { UInt savePlaneState = rCuller.GetPlaneState(); if (noCull || rCuller.IsVisible(mspRenderObjectWorldBound, true)) { const Vector3F& rCamPos = rCuller.GetCamera()->GetLocation(); Vector3F pos = mspRenderObjectWorldBound->GetCenter() - rCamPos; rCuller.Insert(mspRenderObject, &World, pos); } rCuller.SetPlaneState(savePlaneState); } } }
//---------------------------------------------------------------------------- void NodeSkybox::GetVisibleSet(Culler& rCuller, Bool noCull) { const Float sqrtOfThree = 1.7320508F; const Camera* const pCamera = rCuller.GetCamera(); Float scale = pCamera->GetDMax() * 0.25F * sqrtOfThree; World.SetTranslate(pCamera->GetLocation()); World.SetUniformScale(scale); Node::UpdateWorldData(mAppTime, true); Node::GetVisibleSet(rCuller, noCull); }
//---------------------------------------------------------------------------- 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 Portal::GetVisibleSet (Culler& culler, bool noCull) { // Visit only the adjacent region if the portal is open. if (!Open) { return; } // Traverse only through visible portals. if (!culler.IsVisible(mNumVertices, mWorldVertices, true)) { return; } // It is possible that this portal is visited along a path of portals // from the current room containing the camera. Such portals might // have a back-facing polygon relative to the camera. It is not possible // to see through these, so cull them. const Camera* camera = culler.GetCamera(); if (mWorldPlane.WhichSide(camera->GetPosition()) < 0) { return; } // Save the current frustum. float saveFrustum[6]; const float* frustum = culler.GetFrustum(); for (int j = 0; j < 6; ++j) { saveFrustum[j] = frustum[j]; } // If the observer can see through the portal, the culler's frustum may // be reduced in size based on the portal geometry. float reducedFrustum[6]; if (ReducedFrustum(culler, reducedFrustum)) { // Use the reduced frustum for drawing the adjacent region. culler.SetFrustum(reducedFrustum); // Visit the adjacent region and any nonculled objects in it. AdjacentRegion->GetVisibleSet(culler, noCull); // Restore the previous frustum. culler.SetFrustum(saveFrustum); } }
//---------------------------------------------------------------------------- void Particles::GetVisibleSet (Culler& culler, bool noCull) { GenerateParticles(culler.GetCamera()); TriMesh::GetVisibleSet(culler, noCull); }
//---------------------------------------------------------------------------- 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 BspNode::GetVisibleSet (Culler& culler, bool noCull) { // Get visible Geometry in back-to-front order. If a global effect is // active, the Geometry objects in the subtree will be drawn using it. SpatialPtr posChild = GetPositiveChild(); SpatialPtr copChild = GetCoplanarChild(); SpatialPtr negChild = GetNegativeChild(); const Camera* camera = culler.GetCamera(); int positionSide = mWorldPlane.WhichSide(camera->GetPosition()); int frustumSide = culler.WhichSide(mWorldPlane); if (positionSide > 0) { // Camera origin on positive side of plane. if (frustumSide <= 0) { // The frustum is on the negative side of the plane or straddles // the plane. In either case, the negative child is potentially // visible. if (negChild) { negChild->OnGetVisibleSet(culler, noCull); } } if (frustumSide == 0) { // The frustum straddles the plane. The coplanar child is // potentially visible. if (copChild) { copChild->OnGetVisibleSet(culler, noCull); } } if (frustumSide >= 0) { // The frustum is on the positive side of the plane or straddles // the plane. In either case, the positive child is potentially // visible. if (posChild) { posChild->OnGetVisibleSet(culler, noCull); } } } else if (positionSide < 0) { // Camera origin on negative side of plane. if (frustumSide >= 0) { // The frustum is on the positive side of the plane or straddles // the plane. In either case, the positive child is potentially // visible. if (posChild) { posChild->OnGetVisibleSet(culler, noCull); } } if (frustumSide == 0) { // The frustum straddles the plane. The coplanar child is // potentially visible. if (copChild) { copChild->OnGetVisibleSet(culler, noCull); } } if (frustumSide <= 0) { // The frustum is on the negative side of the plane or straddles // the plane. In either case, the negative child is potentially // visible. if (negChild) { negChild->OnGetVisibleSet(culler, noCull); } } } else { // Camera origin on plane itself. Both sides of the plane are // potentially visible as well as the plane itself. Select the // first-to-be-drawn half space to be the one to which the camera // direction points. float NdD = mWorldPlane.GetNormal().Dot(camera->GetDVector()); if (NdD >= 0.0f) { if (posChild) { posChild->OnGetVisibleSet(culler, noCull); } if (copChild) { copChild->OnGetVisibleSet(culler, noCull); } if (negChild) { negChild->OnGetVisibleSet(culler, noCull); } } else { if (negChild) { negChild->OnGetVisibleSet(culler, noCull); } if (copChild) { copChild->OnGetVisibleSet(culler, noCull); } if (posChild) { posChild->OnGetVisibleSet(culler, noCull); } } } }
//---------------------------------------------------------------------------- void DlodNode::GetVisibleSet (Culler& culler, bool noCull) { SelectLevelOfDetail(culler.GetCamera()); SwitchNode::GetVisibleSet(culler, noCull); }