Fixed depth sorting of labels for solar system objects. The labels are now

correctly ordered with respect to other objects, and there's some special
handling to prevent labels of low-orbiting objects from being partially
occluded.
ver1_5_1
Chris Laurel 2007-12-06 17:15:53 +00:00
parent 26c3ffa59f
commit 68eb351b56
2 changed files with 101 additions and 11 deletions

View File

@ -2784,7 +2784,7 @@ void Renderer::render(const Observer& observer,
}
// Render labels in this interval
label = renderSortedLabels(label, -depthPartitions[interval].nearZ, FontNormal);
label = renderSortedLabels(label, -depthPartitions[interval].nearZ, -depthPartitions[interval].farZ, FontNormal);
glDisable(GL_DEPTH_TEST);
}
#if 0
@ -7083,6 +7083,9 @@ void Renderer::buildRenderLists(const Star& sun,
Mat3f viewMat = observer.getOrientation().toMatrix3();
Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
Body* lastPrimary = NULL;
Sphered primarySphere;
int nBodies = solSystem != NULL ? solSystem->getSystemSize() : 0;
for (int i = 0; i < nBodies; i++)
{
@ -7308,6 +7311,78 @@ void Renderer::buildRenderLists(const Star& sun,
if (showLabel && !body->getName().empty())
{
bool isBehindPrimary = false;
Body* primary = body->getSystem()->getPrimaryBody();
// Position the label slightly in front of the object along a line from
// object center to viewer.
pos = pos * (1.0f - body->getRadius() * 1.01f / pos.length());
// Try and position the label so that it's not partially
// occluded by other objects. We'll consider just the object
// that the labeled body is orbiting (its primary) as a
// potential occluder. If a ray from the viewer to labeled
// object center intersects the occluder first, skip
// rendering the object label. Otherwise, ensure that the
// label is completely in front of the primary by projecting
// it onto the plane tangent to the primary at the
// viewer-primary intersection point. Whew. Don't do any of
// this if the primary isn't an ellipsoid.
//
// This only handles the problem of partial label occlusion
// for low orbiting and surface positioned objects, but that
// case is *much* more common than other possibilities.
if (primary != NULL && primary->getModel() == InvalidResource)
{
// In the typical case, we're rendering labels for many
// objects that orbit the same primary. Avoid repeatedly
// calling getPosition() by caching the last primary
// position.
if (primary != lastPrimary)
{
Vec3d v = primary->getHeliocentricPosition(now) - observerPos;
primarySphere = Sphered(Point3d(v.x, v.y, v.z),
primary->getRadius());
lastPrimary = primary;
}
Ray3d testRay(Point3d(0.0, 0.0, 0.0), Vec3d(pos.x, pos.y, pos.z));
// Test the viewer-to-labeled object ray against
// the primary sphere (TODO: handle ellipsoids)
double t = 0.0;
if (testIntersection(testRay, primarySphere, t))
{
// Center of labeled object is behind primary
// sphere; mark it for rejection.
isBehindPrimary = t < 1.0;
}
if (!isBehindPrimary)
{
// Not rejected. Compute the plane tangent to
// the primary at the viewer-to-primary
// intersection point.
Vec3d primaryVec(primarySphere.center.x,
primarySphere.center.y,
primarySphere.center.z);
double distToPrimary = primaryVec.length();
Planed primaryTangentPlane(primaryVec, primaryVec * (primaryVec * (1.0 - primarySphere.radius / distToPrimary)));
// Compute the intersection of the viewer-to-labeled
// object ray with the tangent plane.
float u = (float) (primaryTangentPlane.d / (primaryTangentPlane.normal * Vec3d(pos.x, pos.y, pos.z)));
// If the intersection point is closer to the viewer
// than the label, then project the label onto the
// tangent plane.
if (u < 1.0f && u > 0.0f)
{
pos = pos * u;
}
}
}
addSortedLabel(body->getName(true), labelColor,
Point3f(pos.x, pos.y, pos.z));
}
@ -8494,12 +8569,12 @@ void Renderer::renderLabels(FontStyle fs, LabelAlignment la)
vector<Renderer::Label>::iterator
Renderer::renderSortedLabels(vector<Label>::iterator iter, float depth, FontStyle fs)
Renderer::renderSortedLabels(vector<Label>::iterator iter, float nearDist, float farDist, FontStyle fs)
{
if (font[fs] == NULL)
return iter;
glDisable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
font[fs]->bind();
glEnable(GL_BLEND);
@ -8515,13 +8590,28 @@ Renderer::renderSortedLabels(vector<Label>::iterator iter, float depth, FontStyl
glTranslatef(GLfloat((int) (windowWidth / 2)),
GLfloat((int) (windowHeight / 2)), 0);
for (; iter != depthSortedLabels.end() && iter->position.z > depth; iter++)
// Precompute values that will be used to generate the normalized device z value;
// we're effectively just handling the projection instead of OpenGL. We use an orthographic
// projection matrix in order to get the label text position exactly right but need to mimic
// the depth coordinate generation of a perspective projection.
float d1 = -(farDist + nearDist) / (farDist - nearDist);
float d2 = -2.0f * nearDist * farDist / (farDist - nearDist);
for (; iter != depthSortedLabels.end() && iter->position.z > nearDist; iter++)
{
// Compute normalized device z
float ndc_z = d1 + d2 / -iter->position.z;
ndc_z = min(1.0f, max(-1.0f, ndc_z)); // Clamp to [-1,1]
// Offsets to left align label
int labelHOffset = 0;
int labelVOffset = 0;
glColor(iter->color);
glPushMatrix();
glTranslatef((int) iter->position.x + PixelOffset + 2.0f,
(int) iter->position.y + PixelOffset,
0.0f);
glTranslatef((int) iter->position.x + PixelOffset + labelHOffset,
(int) iter->position.y + PixelOffset + labelVOffset,
ndc_z);
font[fs]->render(iter->text);
glPopMatrix();
}
@ -8916,6 +9006,5 @@ bool Renderer::settingsHaveChanged() const
void Renderer::markSettingsChanged()
{
clog << "settings changed: " << frameCount << endl;
settingsChanged = true;
}
}

View File

@ -508,8 +508,9 @@ class Renderer
Quatf orientation);
void renderLabels(FontStyle fs, LabelAlignment la);
std::vector<Label>::iterator renderSortedLabels(std::vector<Label>::iterator,
float depth,
FontStyle fs);
float nearDist,
float farDist,
FontStyle fs);
void renderMarkers(const MarkerList&,
const UniversalCoord& position,
const Quatf& orientation,