render.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:390k
- v0.normalize();
- v1 = v0;
- }
- else
- {
- v0 = v1 = cometPoints[i] - cometPoints[i - 1];
- sectionLength = v0.length();
- v0.normalize();
- v1 = v0;
- }
- float radius = (float) i / (float) nTailPoints *
- dustTailRadius;
- float dr = (dustTailRadius / (float) nTailPoints) /
- sectionLength;
- float w0 = (float) atan(dr);
- float d = std::sqrt(1.0f + w0 * w0);
- float w1 = 1.0f / d;
- w0 = w0 / d;
- // Special case the first vertex in the comet tail
- if (i == 0)
- {
- w0 = 1;
- w1 = 0.0f;
- }
- for (int j = 0; j < nTailSlices; j++)
- {
- float theta = (float) (2 * PI * (float) j / nTailSlices);
- float s = (float) sin(theta);
- float c = (float) cos(theta);
- CometTailVertex* vtx = &cometTailVertices[i * nTailSlices + j];
- vtx->normal = u * (s * w1) + w * (c * w1) + v * w0;
- s *= radius;
- c *= radius;
- vtx->point = Point3f(cometPoints[i].x + u.x * s + w.x * c,
- cometPoints[i].y + u.y * s + w.y * c,
- cometPoints[i].z + u.z * s + w.z * c);
- vtx->brightness = brightness;
- vtx->paraboloidPoint =
- Point3f(c, s, square((float) i / (float) MaxCometTailPoints));
- }
- }
- Vec3f viewDir = pos - Point3f(0.0f, 0.0f, 0.0f);
- viewDir.normalize();
- glDisable(GL_CULL_FACE);
- for (i = 0; i < nTailPoints - 1; i++)
- {
- glBegin(GL_QUAD_STRIP);
- int n = i * nTailSlices;
- for (int j = 0; j < nTailSlices; j++)
- {
- ProcessCometTailVertex(cometTailVertices[n + j], viewDir, fadeDistance);
- ProcessCometTailVertex(cometTailVertices[n + j + nTailSlices],
- viewDir, fadeDistance);
- }
- ProcessCometTailVertex(cometTailVertices[n], viewDir, fadeDistance);
- ProcessCometTailVertex(cometTailVertices[n + nTailSlices],
- viewDir, fadeDistance);
- glEnd();
- }
- glEnable(GL_CULL_FACE);
- glBegin(GL_LINE_STRIP);
- for (i = 0; i < nTailPoints; i++)
- {
- glVertex(cometPoints[i]);
- }
- glEnd();
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glPopMatrix();
- }
- // Render a reference mark
- void Renderer::renderReferenceMark(const ReferenceMark& refMark,
- Point3f pos,
- float distance,
- double now,
- float nearPlaneDistance)
- {
- float altitude = distance - refMark.boundingSphereRadius();
- float discSizeInPixels = refMark.boundingSphereRadius() /
- (max(nearPlaneDistance, altitude) * pixelSize);
- if (discSizeInPixels <= 1)
- return;
- // Apply the modelview transform for the object
- glPushMatrix();
- glTranslate(pos);
- refMark.render(this, pos, discSizeInPixels, now);
- glPopMatrix();
- glDisable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- }
- // Helper function to compute the luminosity of a perfectly
- // reflective disc with the specified radius. This is used as an upper
- // bound for the apparent brightness of an object when culling
- // invisible objects.
- static float luminosityAtOpposition(float sunLuminosity,
- float distanceFromSun,
- float objRadius)
- {
- // Compute the total power of the star in Watts
- double power = astro::SOLAR_POWER * sunLuminosity;
- // Compute the irradiance at the body's distance from the star
- double irradiance = power / sphereArea(distanceFromSun * 1000);
- // Compute the total energy hitting the planet; assume an albedo of 1.0, so
- // reflected energy = incident energy.
- double incidentEnergy = irradiance * circleArea(objRadius * 1000);
-
- // Compute the luminosity (i.e. power relative to solar power)
- return (float) (incidentEnergy / astro::SOLAR_POWER);
- }
- void Renderer::addRenderListEntries(RenderListEntry& rle,
- Body& body,
- bool isLabeled)
- {
- bool visibleAsPoint = rle.appMag < faintestPlanetMag && body.isVisibleAsPoint();
- if (rle.discSizeInPixels > 1 || visibleAsPoint || isLabeled)
- {
- rle.renderableType = RenderListEntry::RenderableBody;
- rle.body = &body;
- if (body.getGeometry() != InvalidResource && rle.discSizeInPixels > 1)
- {
- Geometry* geometry = GetGeometryManager()->find(body.getGeometry());
- if (geometry == NULL)
- rle.isOpaque = true;
- else
- rle.isOpaque = geometry->isOpaque();
- }
- else
- {
- rle.isOpaque = true;
- }
- rle.radius = body.getRadius();
- renderList.push_back(rle);
- }
- if (body.getClassification() == Body::Comet && (renderFlags & ShowCometTails) != 0)
- {
- float radius = cometDustTailLength(rle.sun.length(), body.getRadius());
- float discSize = (radius / (float) rle.distance) / pixelSize;
- if (discSize > 1)
- {
- rle.renderableType = RenderListEntry::RenderableCometTail;
- rle.body = &body;
- rle.isOpaque = false;
- rle.radius = radius;
- rle.discSizeInPixels = discSize;
- renderList.push_back(rle);
- }
- }
- const list<ReferenceMark*>* refMarks = body.getReferenceMarks();
- if (refMarks != NULL)
- {
- for (list<ReferenceMark*>::const_iterator iter = refMarks->begin();
- iter != refMarks->end(); ++iter)
- {
- const ReferenceMark* rm = *iter;
- rle.renderableType = RenderListEntry::RenderableReferenceMark;
- rle.refMark = rm;
- rle.isOpaque = rm->isOpaque();
- rle.radius = rm->boundingSphereRadius();
- renderList.push_back(rle);
- }
- }
- }
- void Renderer::buildRenderLists(const Point3d& astrocentricObserverPos,
- const Frustum& viewFrustum,
- const Vec3d& viewPlaneNormal,
- const Vec3d& frameCenter,
- const FrameTree* tree,
- const Observer& observer,
- double now)
- {
- int labelClassMask = translateLabelModeToClassMask(labelMode);
- Mat3f viewMat = observer.getOrientationf().toMatrix3();
- Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
- double invCosViewAngle = 1.0 / cosViewConeAngle;
- double sinViewAngle = sqrt(1.0 - square(cosViewConeAngle));
- unsigned int nChildren = tree != NULL ? tree->childCount() : 0;
- for (unsigned int i = 0; i < nChildren; i++)
- {
- const TimelinePhase* phase = tree->getChild(i);
- // No need to do anything if the phase isn't active now
- if (!phase->includes(now))
- continue;
- Body* body = phase->body();
- // pos_s: sun-relative position of object
- // pos_v: viewer-relative position of object
- // Get the position of the body relative to the sun.
- Point3d p = phase->orbit()->positionAtTime(now);
- ReferenceFrame* frame = phase->orbitFrame();
- Vec3d pos_s = frameCenter + Vec3d(p.x, p.y, p.z) * frame->getOrientation(now).toMatrix3();
- // We now have the positions of the observer and the planet relative
- // to the sun. From these, compute the position of the body
- // relative to the observer.
- Vec3d pos_v = Point3d(pos_s.x, pos_s.y, pos_s.z) - astrocentricObserverPos;
- // dist_vn: distance along view normal from the viewer to the
- // projection of the object's center.
- double dist_vn = viewPlaneNormal * pos_v;
- // Vector from object center to its projection on the view normal.
- Vec3d toViewNormal = pos_v - dist_vn * viewPlaneNormal;
- float cullingRadius = body->getCullingRadius();
- // The result of the planetshine test can be reused for the view cone
- // test, but only when the object's light influence sphere is larger
- // than the geometry. This is not
- bool viewConeTestFailed = false;
- if (body->isSecondaryIlluminator())
- {
- float influenceRadius = body->getBoundingRadius() + (body->getRadius() * PLANETSHINE_DISTANCE_LIMIT_FACTOR);
- if (dist_vn > -influenceRadius)
- {
- double maxPerpDist = (influenceRadius + dist_vn * sinViewAngle) * invCosViewAngle;
- double perpDistSq = toViewNormal * toViewNormal;
- if (perpDistSq < maxPerpDist * maxPerpDist)
- {
- if ((body->getRadius() / (float) pos_v.length()) / pixelSize > PLANETSHINE_PIXEL_SIZE_LIMIT)
- {
- // add to planetshine list if larger than 1/10 pixel
- #if DEBUG_SECONDARY_ILLUMINATION
- clog << "Planetshine: " << body->getName()
- << ", " << body->getRadius() / (float) pos_v.length() / pixelSize << endl;
- #endif
- SecondaryIlluminator illum;
- illum.body = body;
- illum.position_v = pos_v;
- illum.radius = body->getRadius();
- secondaryIlluminators.push_back(illum);
- }
- }
- else
- {
- viewConeTestFailed = influenceRadius > cullingRadius;
- }
- }
- else
- {
- viewConeTestFailed = influenceRadius > cullingRadius;
- }
- }
- bool insideViewCone = false;
- if (!viewConeTestFailed)
- {
- float radius = body->getCullingRadius();
- if (dist_vn > -radius)
- {
- double maxPerpDist = (radius + dist_vn * sinViewAngle) * invCosViewAngle;
- double perpDistSq = toViewNormal * toViewNormal;
- insideViewCone = perpDistSq < maxPerpDist * maxPerpDist;
- }
- }
- if (insideViewCone)
- {
- // Calculate the distance to the viewer
- double dist_v = pos_v.length();
- // Calculate the size of the planet/moon disc in pixels
- float discSize = (body->getCullingRadius() / (float) dist_v) / pixelSize;
- // Compute the apparent magnitude; instead of summing the reflected
- // light from all nearby stars, we just consider the one with the
- // highest apparent brightness.
- float appMag = 100.0f;
- for (unsigned int li = 0; li < lightSourceList.size(); li++)
- {
- Vec3d sunPos = pos_v - lightSourceList[li].position;
- appMag = min(appMag, body->getApparentMagnitude(lightSourceList[li].luminosity, sunPos, pos_v));
- }
- bool visibleAsPoint = appMag < faintestPlanetMag && body->isVisibleAsPoint();
- bool isLabeled = (body->getOrbitClassification() & labelClassMask) != 0;
- bool visible = body->isVisible();
- if ((discSize > 1 || visibleAsPoint || isLabeled) && visible)
- {
- RenderListEntry rle;
- rle.position = Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z);
- rle.distance = (float) dist_v;
- rle.centerZ = Vec3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z) * viewMatZ;
- rle.appMag = appMag;
- rle.discSizeInPixels = body->getRadius() / ((float) dist_v * pixelSize);
- // TODO: Remove this. It's only used in two places: for calculating comet tail
- // length, and for calculating sky brightness to adjust the limiting magnitude.
- // In both cases, it's the wrong quantity to use (e.g. for objects with orbits
- // defined relative to the SSB.)
- rle.sun = Vec3f((float) -pos_s.x, (float) -pos_s.y, (float) -pos_s.z);
- addRenderListEntries(rle, *body, isLabeled);
- }
- }
- const FrameTree* subtree = body->getFrameTree();
- if (subtree != NULL)
- {
- double dist_v = pos_v.length();
- bool traverseSubtree = false;
- // There are two different tests available to determine whether we can reject
- // the object's subtree. If the subtree contains no light reflecting objects,
- // then render the subtree only when:
- // - the subtree bounding sphere intersects the view frustum, and
- // - the subtree contains an object bright or large enough to be visible.
- // Otherwise, render the subtree when any of the above conditions are
- // true or when a subtree object could potentially illuminate something
- // in the view cone.
- float minPossibleDistance = (float) (dist_v - subtree->boundingSphereRadius());
- float brightestPossible = 0.0;
- float largestPossible = 0.0;
- // If the viewer is not within the subtree bounding sphere, see if we can cull it because
- // it contains no objects brighter than the limiting magnitude and no objects that will
- // be larger than one pixel in size.
- if (minPossibleDistance > 1.0f)
- {
- // Figure out the magnitude of the brightest possible object in the subtree.
- // Compute the luminosity from reflected light of the largest object in the subtree
- float lum = 0.0f;
- for (unsigned int li = 0; li < lightSourceList.size(); li++)
- {
- Vec3d sunPos = pos_v - lightSourceList[li].position;
- lum += luminosityAtOpposition(lightSourceList[li].luminosity, (float) sunPos.length(), (float) subtree->maxChildRadius());
- }
- brightestPossible = astro::lumToAppMag(lum, astro::kilometersToLightYears(minPossibleDistance));
- largestPossible = (float) subtree->maxChildRadius() / (float) minPossibleDistance / pixelSize;
- }
- else
- {
- // Viewer is within the bounding sphere, so the object could be very close.
- // Assume that an object in the subree could be very bright or large,
- // so no culling will occur.
- brightestPossible = -100.0f;
- largestPossible = 100.0f;
- }
- if (brightestPossible < faintestPlanetMag || largestPossible > 1.0f)
- {
- // See if the object or any of its children are within the view frustum
- if (viewFrustum.testSphere(Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z), (float) subtree->boundingSphereRadius()) != Frustum::Outside)
- {
- traverseSubtree = true;
- }
- }
- // If the subtree contains secondary illuminators, do one last check if it hasn't
- // already been determined if we need to traverse the subtree: see if something
- // in the subtree could possibly contribute significant illumination to an
- // object in the view cone.
- if (subtree->containsSecondaryIlluminators() &&
- !traverseSubtree &&
- largestPossible > PLANETSHINE_PIXEL_SIZE_LIMIT)
- {
- float influenceRadius = (float) (subtree->boundingSphereRadius() +
- (subtree->maxChildRadius() * PLANETSHINE_DISTANCE_LIMIT_FACTOR));
- if (dist_vn > -influenceRadius)
- {
- double maxPerpDist = (influenceRadius + dist_vn * sinViewAngle) * invCosViewAngle;
- double perpDistSq = toViewNormal * toViewNormal;
- if (perpDistSq < maxPerpDist * maxPerpDist)
- traverseSubtree = true;
- }
- }
- if (traverseSubtree)
- {
- buildRenderLists(astrocentricObserverPos,
- viewFrustum,
- viewPlaneNormal,
- pos_s,
- subtree,
- observer,
- now);
- }
- } // end subtree traverse
- }
- }
- void Renderer::buildOrbitLists(const Point3d& astrocentricObserverPos,
- const Quatf& observerOrientation,
- const Frustum& viewFrustum,
- const FrameTree* tree,
- double now)
- {
- Mat3f viewMat = observerOrientation.toMatrix3();
- Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
- unsigned int nChildren = tree != NULL ? tree->childCount() : 0;
- for (unsigned int i = 0; i < nChildren; i++)
- {
- const TimelinePhase* phase = tree->getChild(i);
- // No need to do anything if the phase isn't active now
- if (!phase->includes(now))
- continue;
- Body* body = phase->body();
- // pos_s: sun-relative position of object
- // pos_v: viewer-relative position of object
- // Get the position of the body relative to the sun.
- Point3d pos_s = body->getAstrocentricPosition(now);
- // We now have the positions of the observer and the planet relative
- // to the sun. From these, compute the position of the body
- // relative to the observer.
- Vec3d pos_v = pos_s - astrocentricObserverPos;
- // Only show orbits for major bodies or selected objects.
- Body::VisibilityPolicy orbitVis = body->getOrbitVisibility();
- if (body->isVisible() &&
- (body == highlightObject.body() ||
- orbitVis == Body::AlwaysVisible ||
- (orbitVis == Body::UseClassVisibility && (body->getOrbitClassification() & orbitMask) != 0)))
- {
- Point3d orbitOrigin(0.0, 0.0, 0.0);
- Selection centerObject = phase->orbitFrame()->getCenter();
- if (centerObject.body() != NULL)
- {
- orbitOrigin = centerObject.body()->getAstrocentricPosition(now);
- }
- // Calculate the origin of the orbit relative to the observer
- Vec3d relOrigin = orbitOrigin - astrocentricObserverPos;
- Vec3f origf((float) relOrigin.x, (float) relOrigin.y, (float) relOrigin.z);
- // Compute the size of the orbit in pixels
- double originDistance = pos_v.length();
- double boundingRadius = body->getOrbit(now)->getBoundingRadius();
- float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
- if (orbitRadiusInPixels > minOrbitSize)
- {
- // Add the orbit of this body to the list of orbits to be rendered
- OrbitPathListEntry path;
- path.body = body;
- path.star = NULL;
- path.centerZ = origf * viewMatZ;
- path.radius = (float) boundingRadius;
- path.origin = Point3f(origf.x, origf.y, origf.z);
- path.opacity = sizeFade(orbitRadiusInPixels, minOrbitSize, 2.0f);
- orbitPathList.push_back(path);
- }
- }
- const FrameTree* subtree = body->getFrameTree();
- if (subtree != NULL)
- {
- // Only try to render orbits of child objects when:
- // - The apparent size of the subtree bounding sphere is large enough that
- // orbit paths will be visible, and
- // - The subtree bounding sphere isn't outside the view frustum
- double dist_v = pos_v.length();
- float distanceToBoundingSphere = (float) (dist_v - subtree->boundingSphereRadius());
- bool traverseSubtree = false;
- if (distanceToBoundingSphere > 0.0f)
- {
- // We're inside the subtree's bounding sphere
- traverseSubtree = true;
- }
- else
- {
- float maxPossibleOrbitSize = (float) subtree->boundingSphereRadius() / ((float) dist_v * pixelSize);
- if (maxPossibleOrbitSize > minOrbitSize)
- traverseSubtree = true;
- }
- if (traverseSubtree)
- {
- // See if the object or any of its children are within the view frustum
- if (viewFrustum.testSphere(Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z), (float) subtree->boundingSphereRadius()) != Frustum::Outside)
- {
- buildOrbitLists(astrocentricObserverPos,
- observerOrientation,
- viewFrustum,
- subtree,
- now);
- }
- }
- } // end subtree traverse
- }
- }
- void Renderer::buildLabelLists(const Frustum& viewFrustum,
- double now)
- {
- int labelClassMask = translateLabelModeToClassMask(labelMode);
- Body* lastPrimary = NULL;
- Sphered primarySphere;
- for (vector<RenderListEntry>::const_iterator iter = renderList.begin();
- iter != renderList.end(); iter++)
- {
- int classification = iter->body->getOrbitClassification();
- if (iter->renderableType == RenderListEntry::RenderableBody &&
- (classification & labelClassMask) &&
- viewFrustum.testSphere(iter->position, iter->radius) != Frustum::Outside)
- {
- const Body* body = iter->body;
- Vec3f pos(iter->position.x, iter->position.y, iter->position.z);
- float boundingRadiusSize = (float) (body->getOrbit(now)->getBoundingRadius() / iter->distance) / pixelSize;
- if (boundingRadiusSize > minOrbitSize)
- {
- Color labelColor;
- float opacity = sizeFade(boundingRadiusSize, minOrbitSize, 2.0f);
- switch (classification)
- {
- case Body::Planet:
- labelColor = PlanetLabelColor;
- break;
- case Body::DwarfPlanet:
- labelColor = DwarfPlanetLabelColor;
- break;
- case Body::Moon:
- labelColor = MoonLabelColor;
- break;
- case Body::MinorMoon:
- labelColor = MinorMoonLabelColor;
- break;
- case Body::Asteroid:
- labelColor = AsteroidLabelColor;
- break;
- case Body::Comet:
- labelColor = CometLabelColor;
- break;
- case Body::Spacecraft:
- labelColor = SpacecraftLabelColor;
- break;
- default:
- labelColor = Color::Black;
- break;
- }
- labelColor = Color(labelColor, opacity * labelColor.alpha());
- if (!body->getName().empty())
- {
- bool isBehindPrimary = false;
- const TimelinePhase* phase = body->getTimeline()->findPhase(now);
- Body* primary = phase->orbitFrame()->getCenter().body();
- if (primary != NULL && (primary->getClassification() & Body::Invisible) != 0)
- {
- Body* parent = phase->orbitFrame()->getCenter().body();
- if (parent != NULL)
- primary = parent;
- }
- // Position the label slightly in front of the object along a line from
- // object center to viewer.
- pos = pos * (1.0f - body->getBoundingRadius() * 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->isEllipsoid())
- {
- // 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)
- {
- Point3d p = phase->orbit()->positionAtTime(now) *
- phase->orbitFrame()->getOrientation(now).toMatrix3();
- Vec3d v(iter->position.x - p.x, iter->position.y - p.y, iter->position.z - p.z);
-
- 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;
- }
- }
- }
- addSortedAnnotation(NULL, body->getName(true), labelColor,
- Point3f(pos.x, pos.y, pos.z));
- }
- }
- }
- } // for each render list entry
- }
- // Add a star orbit to the render list
- void Renderer::addStarOrbitToRenderList(const Star& star,
- const Observer& observer,
- double now)
- {
- // If the star isn't fixed, add its orbit to the render list
- if ((renderFlags & ShowOrbits) != 0 &&
- ((orbitMask & Body::Stellar) != 0 || highlightObject.star() == &star))
- {
- Mat3f viewMat = observer.getOrientationf().toMatrix3();
- Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
- if (star.getOrbit() != NULL)
- {
- // Get orbit origin relative to the observer
- Vec3d orbitOrigin = star.getOrbitBarycenterPosition(now) - observer.getPosition();
- orbitOrigin *= astro::microLightYearsToKilometers(1.0);
- Vec3f origf((float) orbitOrigin.x, (float) orbitOrigin.y, (float) orbitOrigin.z);
- // Compute the size of the orbit in pixels
- double originDistance = orbitOrigin.length();
- double boundingRadius = star.getOrbit()->getBoundingRadius();
- float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
- if (orbitRadiusInPixels > minOrbitSize)
- {
- // Add the orbit of this body to the list of orbits to be rendered
- OrbitPathListEntry path;
- path.star = ☆
- path.body = NULL;
- path.centerZ = origf * viewMatZ;
- path.radius = (float) boundingRadius;
- path.origin = Point3f(origf.x, origf.y, origf.z);
- path.opacity = sizeFade(orbitRadiusInPixels, minOrbitSize, 2.0f);
- orbitPathList.push_back(path);
- }
- }
- }
- }
- template <class OBJ, class PREC> class ObjectRenderer : public OctreeProcessor<OBJ, PREC>
- {
- public:
- ObjectRenderer(const PREC distanceLimit);
- void process(const OBJ&, PREC, float) {};
- public:
- const Observer* observer;
- GLContext* context;
- Renderer* renderer;
- Vec3f viewNormal;
- float fov;
- float size;
- float pixelSize;
- float faintestMag;
- float faintestMagNight;
- float saturationMag;
- #ifdef USE_HDR
- float exposure;
- #endif
- float brightnessScale;
- float brightnessBias;
- float distanceLimit;
- // Objects brighter than labelThresholdMag will be labeled
- float labelThresholdMag;
- // These are not fully used by this template's descendants
- // but we place them here just in case a more sophisticated
- // rendering scheme is implemented:
- int nRendered;
- int nClose;
- int nBright;
- int nProcessed;
- int nLabelled;
- int renderFlags;
- int labelMode;
- };
- template <class OBJ, class PREC>
- ObjectRenderer<OBJ, PREC>::ObjectRenderer(const PREC _distanceLimit) :
- distanceLimit((float) _distanceLimit),
- #ifdef USE_HDR
- exposure (0.0f),
- #endif
- nRendered (0),
- nClose (0),
- nBright (0),
- nProcessed (0),
- nLabelled (0)
- {
- }
- class StarRenderer : public ObjectRenderer<Star, float>
- {
- public:
- StarRenderer();
- void process(const Star& star, float distance, float appMag);
- public:
- Point3d obsPos;
- vector<Renderer::Particle>* glareParticles;
- vector<RenderListEntry>* renderList;
- Renderer::StarVertexBuffer* starVertexBuffer;
- Renderer::PointStarVertexBuffer* pointStarVertexBuffer;
- const StarDatabase* starDB;
- bool useScaledDiscs;
- GLenum starPrimitive;
- float maxDiscSize;
- float cosFOV;
- const ColorTemperatureTable* colorTemp;
- #ifdef DEBUG_HDR_ADAPT
- float minMag;
- float maxMag;
- float minAlpha;
- float maxAlpha;
- float maxSize;
- float above;
- unsigned long countAboveN;
- unsigned long total;
- #endif
- };
- StarRenderer::StarRenderer() :
- ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
- starVertexBuffer (NULL),
- pointStarVertexBuffer(NULL),
- useScaledDiscs (false),
- maxDiscSize (1.0f),
- cosFOV (1.0f),
- colorTemp (NULL)
- {
- }
- void StarRenderer::process(const Star& star, float distance, float appMag)
- {
- nProcessed++;
- Point3f starPos = star.getPosition();
- Vec3f relPos((float) ((double) starPos.x - obsPos.x),
- (float) ((double) starPos.y - obsPos.y),
- (float) ((double) starPos.z - obsPos.z));
- float orbitalRadius = star.getOrbitalRadius();
- bool hasOrbit = orbitalRadius > 0.0f;
- if (distance > distanceLimit)
- return;
- if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
- {
- #ifdef HDR_COMPRESS
- Color starColorFull = colorTemp->lookupColor(star.getTemperature());
- Color starColor(starColorFull.red() * 0.5f,
- starColorFull.green() * 0.5f,
- starColorFull.blue() * 0.5f);
- #else
- Color starColor = colorTemp->lookupColor(star.getTemperature());
- #endif
- float renderDistance = distance;
- float s = renderDistance * size;
- float discSizeInPixels = 0.0f;
- float orbitSizeInPixels = 0.0f;
- if (hasOrbit)
- orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
- // Special handling for stars less than one light year away . . .
- // We can't just go ahead and render a nearby star in the usual way
- // for two reasons:
- // * It may be clipped by the near plane
- // * It may be large enough that we should render it as a mesh
- // instead of a particle
- // It's possible that the second condition might apply for stars
- // further than one light year away if the star is huge, the fov is
- // very small and the resolution is high. We'll ignore this for now
- // and use the most inexpensive test possible . . .
- if (distance < 1.0f || orbitSizeInPixels > 1.0f)
- {
- // Compute the position of the observer relative to the star.
- // This is a much more accurate (and expensive) distance
- // calculation than the previous one which used the observer's
- // position rounded off to floats.
- Point3d hPos = astrocentricPosition(observer->getPosition(),
- star,
- observer->getTime());
- relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
- -astro::kilometersToLightYears(1.0f),
- distance = relPos.length();
- // Recompute apparent magnitude using new distance computation
- appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
- float f = RenderDistance / distance;
- renderDistance = RenderDistance;
- starPos = obsPos + relPos * f;
- float radius = star.getRadius();
- discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
- ++nClose;
- }
- // Place labels for stars brighter than the specified label threshold brightness
- if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
- {
- Vec3f starDir = relPos;
- starDir.normalize();
- if (dot(starDir, viewNormal) > cosFOV)
- {
- char nameBuffer[Renderer::MaxLabelLength];
- starDB->getStarName(star, nameBuffer, sizeof(nameBuffer), true);
- float distr = 3.5f * (labelThresholdMag - appMag)/labelThresholdMag;
- if (distr > 1.0f)
- distr = 1.0f;
- renderer->addBackgroundAnnotation(NULL, nameBuffer,
- Color(Renderer::StarLabelColor, distr * Renderer::StarLabelColor.alpha()),
- Point3f(relPos.x, relPos.y, relPos.z));
- nLabelled++;
- }
- }
- // Stars closer than the maximum solar system size are actually
- // added to the render list and depth sorted, since they may occlude
- // planets.
- if (distance > MaxSolarSystemSize)
- {
- #ifdef USE_HDR
- float alpha = exposure*(faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
- #else
- float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
- #endif
- #ifdef DEBUG_HDR_ADAPT
- minMag = max(minMag, appMag);
- maxMag = min(maxMag, appMag);
- minAlpha = min(minAlpha, alpha);
- maxAlpha = max(maxAlpha, alpha);
- ++total;
- if (alpha > above)
- {
- ++countAboveN;
- }
- #endif
- float pointSize;
- if (useScaledDiscs)
- {
- float discSize = size;
- if (alpha < 0.0f)
- {
- alpha = 0.0f;
- }
- else if (alpha > 1.0f)
- {
- discSize = min(discSize * (2.0f * alpha - 1.0f), maxDiscSize);
- alpha = 1.0f;
- }
- pointSize = discSize;
- }
- else
- {
- alpha = clamp(alpha);
- pointSize = size;
- #ifdef DEBUG_HDR_ADAPT
- maxSize = max(maxSize, pointSize);
- #endif
- }
- if (starPrimitive == GL_POINTS)
- {
- pointStarVertexBuffer->addStar(relPos,
- Color(starColor, alpha),
- pointSize);
- }
- else
- {
- starVertexBuffer->addStar(relPos,
- Color(starColor, alpha),
- pointSize * renderDistance);
- }
- ++nRendered;
- // If the star is brighter than the saturation magnitude, add a
- // halo around it to make it appear more brilliant. This is a
- // hack to compensate for the limited dynamic range of monitors.
- if (appMag < saturationMag)
- {
- Renderer::Particle p;
- p.center = Point3f(relPos.x, relPos.y, relPos.z);
- p.size = size;
- p.color = Color(starColor, alpha);
- alpha = GlareOpacity * clamp((appMag - saturationMag) * -0.8f);
- s = renderDistance * 0.001f * (3 - (appMag - saturationMag)) * 2;
- if (s > p.size * 3)
- {
- p.size = s * 2.0f/(1.0f + FOV/fov);
- }
- else
- {
- if (s > p.size * 3)
- p.size = s * 2.0f; //2.0f/(1.0f +FOV/fov);
- else
- p.size = p.size * 3;
- p.size *= 1.6f;
- }
- p.color = Color(starColor, alpha);
- glareParticles->insert(glareParticles->end(), p);
- ++nBright;
- }
- }
- else
- {
- Mat3f viewMat = observer->getOrientationf().toMatrix3();
- Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
- RenderListEntry rle;
- rle.renderableType = RenderListEntry::RenderableStar;
- rle.star = ☆
- rle.isOpaque = true;
- // Objects in the render list are always rendered relative to
- // a viewer at the origin--this is different than for distant
- // stars.
- float scale = astro::lightYearsToKilometers(1.0f);
- rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
- rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
- rle.distance = rle.position.distanceFromOrigin();
- rle.radius = star.getRadius();
- rle.discSizeInPixels = discSizeInPixels;
- rle.appMag = appMag;
- renderList->insert(renderList->end(), rle);
- }
- }
- }
- class PointStarRenderer : public ObjectRenderer<Star, float>
- {
- public:
- PointStarRenderer();
- void process(const Star& star, float distance, float appMag);
- public:
- Point3d obsPos;
- vector<RenderListEntry>* renderList;
- Renderer::PointStarVertexBuffer* starVertexBuffer;
- Renderer::PointStarVertexBuffer* glareVertexBuffer;
- const StarDatabase* starDB;
- bool useScaledDiscs;
- GLenum starPrimitive;
- float maxDiscSize;
- float cosFOV;
- const ColorTemperatureTable* colorTemp;
- #ifdef DEBUG_HDR_ADAPT
- float minMag;
- float maxMag;
- float minAlpha;
- float maxAlpha;
- float maxSize;
- float above;
- unsigned long countAboveN;
- unsigned long total;
- #endif
- };
- PointStarRenderer::PointStarRenderer() :
- ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
- starVertexBuffer (NULL),
- useScaledDiscs (false),
- maxDiscSize (1.0f),
- cosFOV (1.0f),
- colorTemp (NULL)
- {
- }
- void PointStarRenderer::process(const Star& star, float distance, float appMag)
- {
- nProcessed++;
- Point3f starPos = star.getPosition();
- Vec3f relPos((float) ((double) starPos.x - obsPos.x),
- (float) ((double) starPos.y - obsPos.y),
- (float) ((double) starPos.z - obsPos.z));
- float orbitalRadius = star.getOrbitalRadius();
- bool hasOrbit = orbitalRadius > 0.0f;
- if (distance > distanceLimit)
- return;
- // A very rough check to see if the star may be visible: is the star in
- // front of the viewer? If the star might be close (relPos.x^2 < 0.1) or
- // is moving in an orbit, we'll always regard it as potentially visible.
- // TODO: consider normalizing relPos and comparing relPos*viewNormal against
- // cosFOV--this will cull many more stars than relPos*viewNormal, at the
- // cost of a normalize per star.
- if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
- {
- #ifdef HDR_COMPRESS
- Color starColorFull = colorTemp->lookupColor(star.getTemperature());
- Color starColor(starColorFull.red() * 0.5f,
- starColorFull.green() * 0.5f,
- starColorFull.blue() * 0.5f);
- #else
- Color starColor = colorTemp->lookupColor(star.getTemperature());
- #endif
- float renderDistance = distance;
- /*float s = renderDistance * size; Unused*/
- float discSizeInPixels = 0.0f;
- float orbitSizeInPixels = 0.0f;
- if (hasOrbit)
- orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
- // Special handling for stars less than one light year away . . .
- // We can't just go ahead and render a nearby star in the usual way
- // for two reasons:
- // * It may be clipped by the near plane
- // * It may be large enough that we should render it as a mesh
- // instead of a particle
- // It's possible that the second condition might apply for stars
- // further than one light year away if the star is huge, the fov is
- // very small and the resolution is high. We'll ignore this for now
- // and use the most inexpensive test possible . . .
- if (distance < 1.0f || orbitSizeInPixels > 1.0f)
- {
- // Compute the position of the observer relative to the star.
- // This is a much more accurate (and expensive) distance
- // calculation than the previous one which used the observer's
- // position rounded off to floats.
- Point3d hPos = astrocentricPosition(observer->getPosition(),
- star,
- observer->getTime());
- relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
- -astro::kilometersToLightYears(1.0f),
- distance = relPos.length();
- // Recompute apparent magnitude using new distance computation
- appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
- float f = RenderDistance / distance;
- renderDistance = RenderDistance;
- starPos = obsPos + relPos * f;
- float radius = star.getRadius();
- discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
- ++nClose;
- }
- // Place labels for stars brighter than the specified label threshold brightness
- if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
- {
- Vec3f starDir = relPos;
- starDir.normalize();
- if (dot(starDir, viewNormal) > cosFOV)
- {
- char nameBuffer[Renderer::MaxLabelLength];
- starDB->getStarName(star, nameBuffer, sizeof(nameBuffer), true);
- float distr = 3.5f * (labelThresholdMag - appMag)/labelThresholdMag;
- if (distr > 1.0f)
- distr = 1.0f;
- renderer->addBackgroundAnnotation(NULL, nameBuffer,
- Color(Renderer::StarLabelColor, distr * Renderer::StarLabelColor.alpha()),
- Point3f(relPos.x, relPos.y, relPos.z));
- nLabelled++;
- }
- }
- // Stars closer than the maximum solar system size are actually
- // added to the render list and depth sorted, since they may occlude
- // planets.
- if (distance > MaxSolarSystemSize)
- {
- #ifdef USE_HDR
- float satPoint = saturationMag;
- float alpha = exposure*(faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
- #else
- float satPoint = faintestMag - (1.0f - brightnessBias) / brightnessScale; // TODO: precompute this value
- float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
- #endif
- #ifdef DEBUG_HDR_ADAPT
- minMag = max(minMag, appMag);
- maxMag = min(maxMag, appMag);
- minAlpha = min(minAlpha, alpha);
- maxAlpha = max(maxAlpha, alpha);
- ++total;
- if (alpha > above)
- {
- ++countAboveN;
- }
- #endif
- if (useScaledDiscs)
- {
- float discSize = size;
- if (alpha < 0.0f)
- {
- alpha = 0.0f;
- }
- else if (alpha > 1.0f)
- {
- float discScale = min(MaxScaledDiscStarSize, (float) pow(2.0f, 0.3f * (satPoint - appMag)));
- discSize *= discScale;
- float glareAlpha = min(0.5f, discScale / 4.0f);
- glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), discSize * 3.0f);
- alpha = 1.0f;
- }
- starVertexBuffer->addStar(relPos, Color(starColor, alpha), discSize);
- }
- else
- {
- if (alpha < 0.0f)
- {
- alpha = 0.0f;
- }
- else if (alpha > 1.0f)
- {
- float discScale = min(100.0f, satPoint - appMag + 2.0f);
- float glareAlpha = min(GlareOpacity, (discScale - 2.0f) / 4.0f);
- glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), 2.0f * discScale * size);
- #ifdef DEBUG_HDR_ADAPT
- maxSize = max(maxSize, 2.0f * discScale * size);
- #endif
- }
- starVertexBuffer->addStar(relPos, Color(starColor, alpha), size);
- }
- ++nRendered;
- }
- else
- {
- Mat3f viewMat = observer->getOrientationf().toMatrix3();
- Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
- RenderListEntry rle;
- rle.renderableType = RenderListEntry::RenderableStar;
- rle.star = ☆
- // Objects in the render list are always rendered relative to
- // a viewer at the origin--this is different than for distant
- // stars.
- float scale = astro::lightYearsToKilometers(1.0f);
- rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
- rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
- rle.distance = rle.position.distanceFromOrigin();
- rle.radius = star.getRadius();
- rle.discSizeInPixels = discSizeInPixels;
- rle.appMag = appMag;
- renderList->insert(renderList->end(), rle);
- }
- }
- }
- template<class T> static Point3<T> microLYToLY(const Point3<T>& p)
- {
- return Point3<T>(p.x * (T) 1e-6, p.y * (T) 1e-6, p.z * (T) 1e-6);
- }
- // Calculate the maximum field of view (from top left corner to bottom right) of
- // a frustum with the specified aspect ratio (width/height) and vertical field of
- // view. We follow the convention used elsewhere and use units of degrees for
- // the field of view angle.
- static double calcMaxFOV(double fovY_degrees, double aspectRatio)
- {
- double l = 1.0 / tan(degToRad(fovY_degrees / 2.0));
- return radToDeg(atan(sqrt(aspectRatio * aspectRatio + 1.0) / l)) * 2.0;
- }
- void Renderer::renderStars(const StarDatabase& starDB,
- float faintestMagNight,
- const Observer& observer)
- {
- StarRenderer starRenderer;
- Point3d obsPos = microLYToLY((Point3d) observer.getPosition());
- starRenderer.context = context;
- starRenderer.renderer = this;
- starRenderer.starDB = &starDB;
- starRenderer.observer = &observer;
- starRenderer.obsPos = obsPos;
- starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
- starRenderer.glareParticles = &glareParticles;
- starRenderer.renderList = &renderList;
- starRenderer.starVertexBuffer = starVertexBuffer;
- starRenderer.pointStarVertexBuffer = pointStarVertexBuffer;
- starRenderer.fov = fov;
- starRenderer.cosFOV = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
- // size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
- starRenderer.size = pixelSize * 1.6f / corrFac;
- starRenderer.pixelSize = pixelSize;
- starRenderer.brightnessScale = brightnessScale * corrFac;
- starRenderer.brightnessBias = brightnessBias;
- starRenderer.faintestMag = faintestMag;
- starRenderer.faintestMagNight = faintestMagNight;
- starRenderer.saturationMag = saturationMag;
- #ifdef USE_HDR
- starRenderer.exposure = exposure + brightPlus;
- #endif
- #ifdef DEBUG_HDR_ADAPT
- starRenderer.minMag = -100.f;
- starRenderer.maxMag = 100.f;
- starRenderer.minAlpha = 1.f;
- starRenderer.maxAlpha = 0.f;
- starRenderer.maxSize = 0.f;
- starRenderer.above = 1.f;
- starRenderer.countAboveN = 0L;
- starRenderer.total = 0L;
- #endif
- starRenderer.distanceLimit = distanceLimit;
- starRenderer.labelMode = labelMode;
- // = 1.0 at startup
- float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
- starRenderer.labelThresholdMag = max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
- if (starStyle == PointStars || useNewStarRendering)
- {
- starRenderer.starPrimitive = GL_POINTS;
- //starRenderer.size = 3.2f;
- }
- else
- {
- starRenderer.starPrimitive = GL_QUADS;
- }
- if (starStyle == ScaledDiscStars)
- {
- starRenderer.useScaledDiscs = true;
- starRenderer.brightnessScale *= 2.0f;
- starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
- }
- starRenderer.colorTemp = colorTemp;
- glareParticles.clear();
- starVertexBuffer->setBillboardOrientation(observer.getOrientationf());
- glEnable(GL_TEXTURE_2D);
- if (useNewStarRendering)
- gaussianDiscTex->bind();
- else
- starTex->bind();
- if (starRenderer.starPrimitive == GL_POINTS)
- {
- // Point primitives (either real points or point sprites)
- if (starStyle == PointStars)
- starRenderer.pointStarVertexBuffer->startPoints(*context);
- else
- starRenderer.pointStarVertexBuffer->startSprites(*context);
- }
- else
- {
- // Use quad primitives
- starRenderer.starVertexBuffer->start();
- }
- starDB.findVisibleStars(starRenderer,
- Point3f((float) obsPos.x, (float) obsPos.y, (float) obsPos.z),
- observer.getOrientationf(),
- degToRad(fov),
- (float) windowWidth / (float) windowHeight,
- faintestMagNight);
- #ifdef DEBUG_HDR_ADAPT
- HDR_LOG <<
- "* minMag = " << starRenderer.minMag << ", " <<
- "maxMag = " << starRenderer.maxMag << ", " <<
- "percent above " << starRenderer.above << " = " <<
- (100.0*(double)starRenderer.countAboveN/(double)starRenderer.total) << endl;
- #endif
- if (starRenderer.starPrimitive == GL_POINTS)
- starRenderer.pointStarVertexBuffer->finish();
- else
- starRenderer.starVertexBuffer->finish();
- gaussianGlareTex->bind();
- renderParticles(glareParticles, observer.getOrientationf());
- }
- void Renderer::renderPointStars(const StarDatabase& starDB,
- float faintestMagNight,
- const Observer& observer)
- {
- Point3d obsPos = microLYToLY((Point3d) observer.getPosition());
- PointStarRenderer starRenderer;
- starRenderer.context = context;
- starRenderer.renderer = this;
- starRenderer.starDB = &starDB;
- starRenderer.observer = &observer;
- starRenderer.obsPos = obsPos;
- starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
- starRenderer.renderList = &renderList;
- starRenderer.starVertexBuffer = pointStarVertexBuffer;
- starRenderer.glareVertexBuffer = glareVertexBuffer;
- starRenderer.fov = fov;
- starRenderer.cosFOV = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
- starRenderer.pixelSize = pixelSize;
- starRenderer.brightnessScale = brightnessScale * corrFac;
- starRenderer.brightnessBias = brightnessBias;
- starRenderer.faintestMag = faintestMag;
- starRenderer.faintestMagNight = faintestMagNight;
- starRenderer.saturationMag = saturationMag;
- #ifdef USE_HDR
- starRenderer.exposure = exposure + brightPlus;
- #endif
- #ifdef DEBUG_HDR_ADAPT
- starRenderer.minMag = -100.f;
- starRenderer.maxMag = 100.f;
- starRenderer.minAlpha = 1.f;
- starRenderer.maxAlpha = 0.f;
- starRenderer.maxSize = 0.f;
- starRenderer.above = 1.0f;
- starRenderer.countAboveN = 0L;
- starRenderer.total = 0L;
- #endif
- starRenderer.distanceLimit = distanceLimit;
- starRenderer.labelMode = labelMode;
- // = 1.0 at startup
- float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
- starRenderer.labelThresholdMag = 1.2f * max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
- starRenderer.size = BaseStarDiscSize;
- if (starStyle == ScaledDiscStars)
- {
- starRenderer.useScaledDiscs = true;
- starRenderer.brightnessScale *= 2.0f;
- starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
- }
- else if (starStyle == FuzzyPointStars)
- {
- starRenderer.brightnessScale *= 1.0f;
- }
- starRenderer.colorTemp = colorTemp;
- glEnable(GL_TEXTURE_2D);
- gaussianDiscTex->bind();
- starRenderer.starVertexBuffer->setTexture(gaussianDiscTex);
- starRenderer.glareVertexBuffer->setTexture(gaussianGlareTex);
- starRenderer.glareVertexBuffer->startSprites(*context);
- if (starStyle == PointStars)
- starRenderer.starVertexBuffer->startPoints(*context);
- else
- starRenderer.starVertexBuffer->startSprites(*context);
- starDB.findVisibleStars(starRenderer,
- Point3f((float) obsPos.x, (float) obsPos.y, (float) obsPos.z),
- observer.getOrientationf(),
- degToRad(fov),
- (float) windowWidth / (float) windowHeight,
- faintestMagNight);
- starRenderer.starVertexBuffer->render();
- starRenderer.glareVertexBuffer->render();
- starRenderer.starVertexBuffer->finish();
- starRenderer.glareVertexBuffer->finish();
- }
- class DSORenderer : public ObjectRenderer<DeepSkyObject*, double>
- {
- public:
- DSORenderer();
- void process(DeepSkyObject* const &, double, float);
- public:
- Point3d obsPos;
- DSODatabase* dsoDB;
- Frustum frustum;
- Mat3f orientationMatrix;
- int wWidth;
- int wHeight;
- double avgAbsMag;
- };
- DSORenderer::DSORenderer() :
- ObjectRenderer<DeepSkyObject*, double>(DSO_OCTREE_ROOT_SIZE),
- frustum(degToRad(45.0f), 1.0f, 1.0f)
- {
- }
- void DSORenderer::process(DeepSkyObject* const & dso,
- double distanceToDSO,
- float absMag)
- {
- if (distanceToDSO > distanceLimit)
- return;
-
- Point3d dsoPos = dso->getPosition();
- Vec3f relPos = Vec3f((float)(dsoPos.x - obsPos.x),
- (float)(dsoPos.y - obsPos.y),
- (float)(dsoPos.z - obsPos.z));
- Point3d center = Point3d(0.0f, 0.0f, 0.0f) + relPos * orientationMatrix;
-
- double enhance = 4.0, pc10 = 32.6167;
-
- // The parameter 'enhance' adjusts the DSO brightness as viewed from "inside"
- // (e.g. MilkyWay as seen from Earth). It provides an enhanced apparent core
- // brightness appMag ~ absMag - enhance. 'enhance' thus serves to uniformly
- // enhance the too low sprite luminosity at close distance.
- float appMag = (distanceToDSO >= pc10)? (float) astro::absToAppMag((double) absMag, distanceToDSO): absMag + (float) (enhance * tanh(distanceToDSO/pc10 - 1.0));
-
- // Test the object's bounding sphere against the view frustum. If we
- // avoid this stage, overcrowded octree cells may hit performance badly:
- // each object (even if it's not visible) would be sent to the OpenGL
- // pipeline.
-
-
- if ((renderFlags & dso->getRenderMask()) && dso->isVisible())
- {
- double dsoRadius = dso->getBoundingSphereRadius();
- if (frustum.testSphere(center, dsoRadius) != Frustum::Outside)
- {
- // Input: display looks satisfactory for 0.2 < brightness < O(1.0)
- // Ansatz: brightness = a - b * appMag(distanceToDSO), emulating eye sensitivity...
- // determine a,b such that
- // a - b * absMag = absMag / avgAbsMag ~ 1; a - b * faintestMag = 0.2.
- // The 2nd eq. guarantees that the faintest galaxies are still visible.
-
- if(!strcmp(dso->getObjTypeName(),"globular"))
- avgAbsMag = -6.86; // average over 150 globulars in globulars.dsc.
- else if (!strcmp(dso->getObjTypeName(),"galaxy"))
- avgAbsMag = -19.04; // average over 10937 galaxies in galaxies.dsc.
-
-
- float r = absMag / (float) avgAbsMag;
- float brightness = r - (r - 0.2f) * (absMag - appMag) / (absMag - faintestMag);
-
- // obviously, brightness(appMag = absMag) = r and
- // brightness(appMag = faintestMag) = 0.2, as desired.
- brightness = 2.3f * brightness * (faintestMag - 4.75f) / renderer->getFaintestAM45deg();
- #ifdef USE_HDR
- brightness *= exposure;
- #endif
- if (brightness < 0)
- brightness = 0;
-
- if (dsoRadius < 1000.0)
- {
- // Small objects may be prone to clipping; give them special
- // handling. We don't want to always set the projection
- // matrix, since that could be expensive with large galaxy
- // catalogs.
- float nearZ = (float) (distanceToDSO / 2);
- float farZ = (float) (distanceToDSO + dsoRadius * 2 * CubeCornerToCenterDistance);
- if (nearZ < dsoRadius * 0.001)
- {
- nearZ = (float) (dsoRadius * 0.001);
- farZ = nearZ * 10000.0f;
- }
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- gluPerspective(fov,
- (float) wWidth / (float) wHeight,
- nearZ,
- farZ);
- glMatrixMode(GL_MODELVIEW);
- }
- glPushMatrix();
- glTranslate(relPos);
- dso->render(*context,
- relPos,
- observer->getOrientationf(),
- (float) brightness,
- pixelSize);
- glPopMatrix();
- #if 1
- if (dsoRadius < 1000.0)
- {
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- }
- #endif
- } // frustum test
- } // renderFlags check
- // Only render those labels that are in front of the camera:
- // Place labels for DSOs brighter than the specified label threshold brightness
- //
- unsigned int labelMask = dso->getLabelMask();
- if ((labelMask & labelMode) && dot(relPos, viewNormal) > 0 && dso->isVisible())
- {
- Color labelColor;
- float appMagEff = 6.0f;
- float step = 6.0f;
- float symbolSize = 0.0f;
- MarkerRepresentation* rep = NULL;
- // Use magnitude based fading for galaxies, and distance based
- // fading for nebulae and open clusters.
- switch (labelMask)
- {
- case Renderer::NebulaLabels:
- rep = &renderer->nebulaRep;
- labelColor = Renderer::NebulaLabelColor;
- appMagEff = astro::absToAppMag(-7.5f, (float) distanceToDSO);
- symbolSize = (float) (dso->getRadius() / distanceToDSO) / pixelSize;
- step = 6.0f;
- break;
- case Renderer::OpenClusterLabels:
- rep = &renderer->openClusterRep;
- labelColor = Renderer::OpenClusterLabelColor;
- appMagEff = astro::absToAppMag(-6.0f, (float) distanceToDSO);
- symbolSize = (float) (dso->getRadius() / distanceToDSO) / pixelSize;
- step = 4.0f;
- break;
- case Renderer::GalaxyLabels:
- labelColor = Renderer::GalaxyLabelColor;
- appMagEff = appMag;
- step = 6.0f;
- break;
- case Renderer::GlobularLabels:
- labelColor = Renderer::GlobularLabelColor;
- appMagEff = appMag;
- step = 3.0f;
- break;
- default:
- // Unrecognized object class
- labelColor = Color::White;
- appMagEff = appMag;
- step = 6.0f;
- break;
- }
- if (appMagEff < labelThresholdMag)
- {
- // introduce distance dependent label transparency.
- float distr = step * (labelThresholdMag - appMagEff) / labelThresholdMag;
- if (distr > 1.0f)
- distr = 1.0f;
- renderer->addBackgroundAnnotation(rep,
- dsoDB->getDSOName(dso, true),
- Color(labelColor, distr * labelColor.alpha()),
- Point3f(relPos.x, relPos.y, relPos.z),
- Renderer::AlignLeft, Renderer::VerticalAlignCenter, symbolSize);
- }
- }
- }
- void Renderer::renderDeepSkyObjects(const Universe& universe,
- const Observer& observer,
- const float faintestMagNight)
- {
- DSORenderer dsoRenderer;
- Point3d obsPos = (Point3d) observer.getPosition();
- obsPos.x *= 1e-6;
- obsPos.y *= 1e-6;
- obsPos.z *= 1e-6;
- DSODatabase* dsoDB = universe.getDSOCatalog();
- dsoRenderer.context = context;
- dsoRenderer.renderer = this;
- dsoRenderer.dsoDB = dsoDB;
- dsoRenderer.orientationMatrix = conjugate(observer.getOrientationf()).toMatrix3();
- dsoRenderer.observer = &observer;
- dsoRenderer.obsPos = obsPos;
- dsoRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
- dsoRenderer.fov = fov;
- // size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
- dsoRenderer.size = pixelSize * 1.6f / corrFac;
- dsoRenderer.pixelSize = pixelSize;
- dsoRenderer.brightnessScale = brightnessScale * corrFac;
- dsoRenderer.brightnessBias = brightnessBias;
- dsoRenderer.avgAbsMag = dsoDB->getAverageAbsoluteMagnitude();
- dsoRenderer.faintestMag = faintestMag;
- dsoRenderer.faintestMagNight = faintestMagNight;
- dsoRenderer.saturationMag = saturationMag;
- #ifdef USE_HDR
- dsoRenderer.exposure = exposure + brightPlus;
- #endif
- dsoRenderer.renderFlags = renderFlags;
- dsoRenderer.labelMode = labelMode;
- dsoRenderer.wWidth = windowWidth;
- dsoRenderer.wHeight = windowHeight;
- dsoRenderer.frustum = Frustum(degToRad(fov),
- (float) windowWidth / (float) windowHeight,
- MinNearPlaneDistance);
- // Use pixelSize * screenDpi instead of FoV, to eliminate windowHeight dependence.
- // = 1.0 at startup
- float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
- dsoRenderer.labelThresholdMag = 2.0f * max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
- galaxyRep = MarkerRepresentation(MarkerRepresentation::Triangle, 8.0f, GalaxyLabelColor);
- nebulaRep = MarkerRepresentation(MarkerRepresentation::Square, 8.0f, NebulaLabelColor);
- openClusterRep = MarkerRepresentation(MarkerRepresentation::Circle, 8.0f, OpenClusterLabelColor);
- globularRep = MarkerRepresentation(MarkerRepresentation::Circle, 8.0f, OpenClusterLabelColor);
- // Render any line primitives with smooth lines
- // (mostly to make graticules look good.)
- if ((renderFlags & ShowSmoothLines) != 0)
- enableSmoothLines();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- dsoDB->findVisibleDSOs(dsoRenderer,
- obsPos,
- observer.getOrientationf(),
- degToRad(fov),
- (float) windowWidth / (float) windowHeight,
- 2 * faintestMagNight);
- if ((renderFlags & ShowSmoothLines) != 0)
- disableSmoothLines();
- }
- static Vec3d toStandardCoords(const Vec3d& v)
- {
- return Vec3d(v.x, -v.z, v.y);
- }
- void Renderer::renderSkyGrids(const Observer& observer)
- {
- if (renderFlags & ShowCelestialSphere)
- {
- SkyGrid grid;
- grid.setOrientation(Quatd::xrotation(astro::J2000Obliquity));
- grid.setLineColor(EquatorialGridColor);
- grid.setLabelColor(EquatorialGridLabelColor);
- grid.render(*this, observer, windowWidth, windowHeight);
- }
- if (renderFlags & ShowGalacticGrid)
- {
- SkyGrid galacticGrid;
- galacticGrid.setOrientation(~(astro::eclipticToEquatorial() * astro::equatorialToGalactic()));
- galacticGrid.setLineColor(GalacticGridColor);
- galacticGrid.setLabelColor(GalacticGridLabelColor);
- galacticGrid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
- galacticGrid.render(*this, observer, windowWidth, windowHeight);
- }
- if (renderFlags & ShowEclipticGrid)
- {
- SkyGrid grid;
- grid.setOrientation(Quatd(1.0));
- grid.setLineColor(EclipticGridColor);
- grid.setLabelColor(EclipticGridLabelColor);
- grid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
- grid.render(*this, observer, windowWidth, windowHeight);
- }
- if (renderFlags & ShowHorizonGrid)
- {
- double tdb = observer.getTime();
- const ObserverFrame* frame = observer.getFrame();
- Body* body = frame->getRefObject().body();
-
- if (body)
- {
- SkyGrid grid;
- grid.setLineColor(HorizonGridColor);
- grid.setLabelColor(HorizonGridLabelColor);
- grid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
- grid.setLongitudeDirection(SkyGrid::IncreasingClockwise);
-
- Vec3d zenithDirection = observer.getPosition() - body->getPosition(tdb);
- zenithDirection.normalize();
-
- Vec3d northPole = Vec3d(0.0, 1.0, 0.0) * (body->getEclipticToEquatorial(tdb)).toMatrix3();
- zenithDirection = toStandardCoords(zenithDirection);
- northPole = toStandardCoords(northPole);
-
- Vec3d v = zenithDirection ^ northPole;
- // Horizontal coordinate system not well defined when observer
- // is at a pole.
- double tolerance = 1.0e-10;
- if (v.length() > tolerance && v.length() < 1.0 - tolerance)
- {
- v.normalize();
- Vec3d u = v ^ zenithDirection;
-
- Mat3d m = Mat3d(u, v, zenithDirection);
- Quatd q = Quatd::matrixToQuaternion(m);
- grid.setOrientation(q);
-
- grid.render(*this, observer, windowWidth, windowHeight);
- }
- }
- }
- if (renderFlags & ShowEcliptic)
- {
- // Draw the J2000.0 ecliptic; trivial, since this forms the basis for
- // Celestia's coordinate system.
- const int subdivision = 200;
- glColor(EclipticColor);
- glBegin(GL_LINE_LOOP);
- for (int i = 0; i < subdivision; i++)
- {
- double theta = (double) i / (double) subdivision * 2 * PI;
- glVertex3f((float) cos(theta) * 1000.0f, 0.0f, (float) sin(theta) * 1000.0f);
- }
- glEnd();
- }
- }
- /*! Draw an arrow at the view border pointing to an offscreen selection. This method
- * should only be called when the selection lies outside the view frustum.
- */
- void Renderer::renderSelectionPointer(const Observer& observer,
- double now,
- const Frustum& viewFrustum,
- const Selection& sel)
- {
- const float cursorDistance = 20.0f;
- if (sel.empty())
- return;
- Mat3f m = observer.getOrientationf().toMatrix3();
- Vec3f u = Vec3f(1, 0, 0) * m;
- Vec3f v = Vec3f(0, 1, 0) * m;
- // Get the position of the cursor relative to the eye
- Vec3d position = (sel.getPosition(now) - observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
- double distance = position.length();
- bool isVisible = viewFrustum.testSphere(Point3d(0, 0, 0) + position, sel.radius()) != Frustum::Outside;
- position *= cursorDistance / distance;
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- #endif
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- if (!isVisible)
- {
- double viewAspectRatio = (double) windowWidth / (double) windowHeight;
- double vfov = observer.getFOV();
- float h = (float) tan(vfov / 2);
- float w = (float) (h * viewAspectRatio);
- float diag = std::sqrt(h * h + w * w);
- Vec3f posf((float) position.x, (float) position.y, (float) position.z);
- posf *= (1.0f / cursorDistance);
- float x = u * posf;
- float y = v * posf;
- float angle = std::atan2(y, x);
- float c = std::cos(angle);
- float s = std::sin(angle);
- float t = 1.0f;
- float x0 = c * diag;
- float y0 = s * diag;
- if (std::abs(x0) < w)
- t = h / std::abs(y0);
- else
- t = w / std::abs(x0);
- x0 *= t;
- y0 *= t;
- glColor(SelectionCursorColor, 0.6f);
- Vec3f center = Vec3f(0, 0, -1) * m;
- glPushMatrix();
- glTranslatef((float) center.x, (float) center.y, (float) center.z);
- Vec3f p0(0.0f, 0.0f, 0.0f);
- Vec3f p1(-20.0f * pixelSize, 6.0f * pixelSize, 0.0f);
- Vec3f p2(-20.0f * pixelSize, -6.0f * pixelSize, 0.0f);
- glBegin(GL_TRIANGLES);
- glVertex((p0.x * c - p0.y * s + x0) * u + (p0.x * s + p0.y * c + y0) * v);
- glVertex((p1.x * c - p1.y * s + x0) * u + (p1.x * s + p1.y * c + y0) * v);
- glVertex((p2.x * c - p2.y * s + x0) * u + (p2.x * s + p2.y * c + y0) * v);
- glEnd();
- glPopMatrix();
- }
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- #endif
- glEnable(GL_TEXTURE_2D);
- }
- void Renderer::labelConstellations(const AsterismList& asterisms,
- const Observer& observer)
- {
- Point3f observerPos = (Point3f) observer.getPosition();
- for (AsterismList::const_iterator iter = asterisms.begin();
- iter != asterisms.end(); iter++)
- {
- Asterism* ast = *iter;
- if (ast->getChainCount() > 0 && ast->getActive())
- {
- const Asterism::Chain& chain = ast->getChain(0);
- if (chain.size() > 0)
- {
- // The constellation label is positioned at the average
- // position of all stars in the first chain. This usually
- // gives reasonable results.
- Vec3f avg(0, 0, 0);
- for (Asterism::Chain::const_iterator iter = chain.begin();
- iter != chain.end(); iter++)
- avg += (*iter - Point3f(0, 0, 0));
- avg = avg / (float) chain.size();
- // Draw all constellation labels at the same distance
- avg.normalize();
- avg = avg * 1.0e10f;
- Vec3f rpos = Point3f(avg.x, avg.y, avg.z) - observerPos;
- if ((observer.getOrientationf().toMatrix3() * rpos).z < 0)
- {
- // We'll linearly fade the labels as a function of the
- // observer's distance to the origin of coordinates:
- float opacity = 1.0f;
- float dist = observerPos.distanceFromOrigin();
- if (dist > MaxAsterismLabelsConstDist)
- {
- opacity = clamp((MaxAsterismLabelsConstDist - dist) /
- (MaxAsterismLabelsDist - MaxAsterismLabelsConstDist) + 1);
- }
- // Use the default label color unless the constellation has an
- // override color set.
- Color labelColor = ConstellationLabelColor;
- if (ast->isColorOverridden())
- labelColor = ast->getOverrideColor();
- addBackgroundAnnotation(NULL,
- ast->getName((labelMode & I18nConstellationLabels) != 0),
- Color(labelColor, opacity),
- Point3f(rpos.x, rpos.y, rpos.z),
- AlignCenter, VerticalAlignCenter);
- }
- }
- }
- }
- }
- void Renderer::renderParticles(const vector<Particle>& particles,
- Quatf orientation)
- {
- int nParticles = particles.size();
- {
- Mat3f m = orientation.toMatrix3();
- Vec3f v0 = Vec3f(-1, -1, 0) * m;
- Vec3f v1 = Vec3f( 1, -1, 0) * m;
- Vec3f v2 = Vec3f( 1, 1, 0) * m;
- Vec3f v3 = Vec3f(-1, 1, 0) * m;
- glBegin(GL_QUADS);
- for (int i = 0; i < nParticles; i++)
- {
- Point3f center = particles[i].center;
- float size = particles[i].size;
- glColor(particles[i].color);
- glTexCoord2f(0, 1);
- glVertex(center + (v0 * size));
- glTexCoord2f(1, 1);
- glVertex(center + (v1 * size));
- glTexCoord2f(1, 0);
- glVertex(center + (v2 * size));
- glTexCoord2f(0, 0);
- glVertex(center + (v3 * size));
- }
- glEnd();
- }
- }
- static void renderCrosshair(float pixelSize, double tsec)
- {
- const float cursorMinRadius = 6.0f;
- const float cursorRadiusVariability = 4.0f;
- const float minCursorWidth = 7.0f;
- const float cursorPulsePeriod = 1.5f;
- float selectionSizeInPixels = pixelSize;
- float cursorRadius = selectionSizeInPixels + cursorMinRadius;
- cursorRadius += cursorRadiusVariability * (float) (0.5 + 0.5 * std::sin(tsec * 2 * PI / cursorPulsePeriod));
- // Enlarge the size of the cross hair sligtly when the selection
- // has a large apparent size
- float cursorGrow = max(1.0f, min(2.5f, (selectionSizeInPixels - 10.0f) / 100.0f));
- float h = 2.0f * cursorGrow;
- float cursorWidth = minCursorWidth * cursorGrow;
- float r0 = cursorRadius;
- float r1 = cursorRadius + cursorWidth;
- const unsigned int markCount = 4;
- Vec3f p0(r0, 0.0f, 0.0f);
- Vec3f p1(r1, -h, 0.0f);
- Vec3f p2(r1, h, 0.0f);
- glBegin(GL_TRIANGLES);
- for (unsigned int i = 0; i < markCount; i++)
- {
- float theta = (float) (PI / 4.0) + (float) i / (float) markCount * (float) (2 * PI);
- float c = std::cos(theta);
- float s = std::sin(theta);
- glVertex3f(p0.x * c - p0.y * s, p0.x * s + p0.y * c, 0.0f);
- glVertex3f(p1.x * c - p1.y * s, p1.x * s + p1.y * c, 0.0f);
- glVertex3f(p2.x * c - p2.y * s, p2.x * s + p2.y * c, 0.0f);
- }
- glEnd();
- }
- void Renderer::renderAnnotations(const vector<Annotation>& annotations, FontStyle fs)
- {
- if (font[fs] == NULL)
- return;
- // Enable line smoothing for rendering symbols
- if ((renderFlags & ShowSmoothLines) != 0)
- enableSmoothLines();
-
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
- #endif
- glEnable(GL_TEXTURE_2D);
- font[fs]->bind();
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- gluOrtho2D(0, windowWidth, 0, windowHeight);
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- glTranslatef(GLfloat((int) (windowWidth / 2)),
- GLfloat((int) (windowHeight / 2)), 0);
- for (int i = 0; i < (int) annotations.size(); i++)
- {
- if (annotations[i].markerRep != NULL)
- {
- glPushMatrix();
- const MarkerRepresentation& markerRep = *annotations[i].markerRep;
- float size = markerRep.size();
- if (annotations[i].size > 0.0f)
- {
- size = annotations[i].size;
- }
- glColor(annotations[i].color);
- glTranslatef((GLfloat) (int) annotations[i].position.x,
- (GLfloat) (int) annotations[i].position.y, 0.0f);
- glDisable(GL_TEXTURE_2D);
- if (markerRep.symbol() == MarkerRepresentation::Crosshair)
- renderCrosshair(size, realTime);
- else
- markerRep.render(size);
- glEnable(GL_TEXTURE_2D);
-
- if (!markerRep.label().empty())
- {
- int labelOffset = (int) markerRep.size() / 2;
- glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
- font[fs]->render(markerRep.label());
- }
- glPopMatrix();
- }
- if (annotations[i].labelText[0] != ' ')
- {
- glPushMatrix();
- int labelWidth = 0;
- int hOffset = 2;
- int vOffset = 0;
-
- switch (annotations[i].halign)
- {
- case AlignCenter:
- labelWidth = (font[fs]->getWidth(annotations[i].labelText));
- hOffset = -labelWidth / 2;
- break;
-
- case AlignRight:
- labelWidth = (font[fs]->getWidth(annotations[i].labelText));
- hOffset = -(labelWidth + 2);
- break;
-
- case AlignLeft:
- if (annotations[i].markerRep != NULL)
- hOffset = 2 + (int) annotations[i].markerRep->size() / 2;
- break;
- }
-
- switch (annotations[i].valign)
- {
- case AlignCenter:
- vOffset = -font[fs]->getHeight() / 2;
- break;
- case VerticalAlignTop:
- vOffset = -font[fs]->getHeight();
- break;
- case VerticalAlignBottom:
- vOffset = 0;
- break;
- }
-
- glColor(annotations[i].color);
- glTranslatef((int) annotations[i].position.x + hOffset + PixelOffset,
- (int) annotations[i].position.y + vOffset + PixelOffset, 0.0f);
- // EK TODO: Check where to replace (see '_(' above)
- font[fs]->render(annotations[i].labelText);
- glPopMatrix();
- }
- }
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- #ifdef USE_HDR
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- #endif
- if ((renderFlags & ShowSmoothLines) != 0)
- disableSmoothLines();
- }
- void
- Renderer::renderBackgroundAnnotations(FontStyle fs)
- {
- glEnable(GL_DEPTH_TEST);
- renderAnnotations(backgroundAnnotations, fs);
- glDisable(GL_DEPTH_TEST);
-
- clearAnnotations(backgroundAnnotations);
- }
- void
- Renderer::renderForegroundAnnotations(FontStyle fs)
- {
- glDisable(GL_DEPTH_TEST);
- renderAnnotations(foregroundAnnotations, fs);
-
- clearAnnotations(foregroundAnnotations);
- }
- vector<Renderer::Annotation>::iterator
- Renderer::renderSortedAnnotations(vector<Annotation>::iterator iter,
- float nearDist,
- float farDist,
- FontStyle fs)
- {
- if (font[fs] == NULL)
- return iter;
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_TEXTURE_2D);
- font[fs]->bind();
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- gluOrtho2D(0, windowWidth, 0, windowHeight);
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- glTranslatef(GLfloat((int) (windowWidth / 2)),
- GLfloat((int) (windowHeight / 2)), 0);
- // 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 != depthSortedAnnotations.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;
- glPushMatrix();
- if (iter->markerRep != NULL)
- {
- const MarkerRepresentation& markerRep = *iter->markerRep;
- float size = markerRep.size();
- if (iter->size > 0.0f)
- {
- size = iter->size;
- }
-
- glTranslatef((GLfloat) (int) iter->position.x, (GLfloat) (int) iter->position.y, ndc_z);
- glColor(iter->color);
-
- glDisable(GL_TEXTURE_2D);
- if (markerRep.symbol() == MarkerRepresentation::Crosshair)
- renderCrosshair(size, realTime);
- else
- markerRep.render(size);
- glEnable(GL_TEXTURE_2D);
-
- if (!markerRep.label().empty())
- {
- int labelOffset = (int) markerRep.size() / 2;
- glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
- font[fs]->render(markerRep.label());
- }
- }
- else
- {
- glTranslatef((int) iter->position.x + PixelOffset + labelHOffset,
- (int) iter->position.y + PixelOffset + labelVOffset,
- ndc_z);
- glColor(iter->color);
- font[fs]->render(iter->labelText);
- }
- glPopMatrix();
- }
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glDisable(GL_DEPTH_TEST);
- return iter;
- }
- vector<Renderer::Annotation>::iterator
- Renderer::renderAnnotations(vector<Annotation>::iterator startIter,
- vector<Annotation>::iterator endIter,
- float nearDist,
- float farDist,
- FontStyle fs)
- {
- if (font[fs] == NULL)
- return endIter;
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_TEXTURE_2D);
- font[fs]->bind();
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- gluOrtho2D(0, windowWidth, 0, windowHeight);
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- glTranslatef(GLfloat((int) (windowWidth / 2)),
- GLfloat((int) (windowHeight / 2)), 0);
- // 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);
- vector<Annotation>::iterator iter = startIter;
- for (; iter != endIter && 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;
- if (iter->markerRep != NULL)
- {
- glPushMatrix();
- const MarkerRepresentation& markerRep = *iter->markerRep;
- float size = markerRep.size();
- if (iter->size > 0.0f)
- {
- size = iter->size;
- }
-
- glTranslatef((GLfloat) (int) iter->position.x, (GLfloat) (int) iter->position.y, ndc_z);
- glColor(iter->color);
-
- glDisable(GL_TEXTURE_2D);
- if (markerRep.symbol() == MarkerRepresentation::Crosshair)
- renderCrosshair(size, realTime);
- else
- markerRep.render(size);
- glEnable(GL_TEXTURE_2D);
-
- if (!markerRep.label().empty())
- {
- int labelOffset = (int) markerRep.size() / 2;
- glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
- font[fs]->render(markerRep.label());
- }
- glPopMatrix();
- }
-
- if (iter->labelText[0] != ' ')
- {
- if (iter->markerRep != NULL)
- labelHOffset += (int) iter->markerRep->size() / 2 + 3;
- glPushMatrix();
- glTranslatef((int) iter->position.x + PixelOffset + labelHOffset,
- (int) iter->position.y + PixelOffset + labelVOffset,
- ndc_z);
- glColor(iter->color);
- font[fs]->render(iter->labelText);
- glPopMatrix();
- }
- }
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glDisable(GL_DEPTH_TEST);
- return iter;
- }
- void Renderer::renderMarkers(const MarkerList& markers,
- const UniversalCoord& cameraPosition,
- const Quatd& cameraOrientation,
- double jd)
- {
- // Calculate the cosine of half the maximum field of view. We'll use this for
- // fast testing of marker visibility. The stored field of view is the
- // vertical field of view; we want the field of view as measured on the
- // diagonal between viewport corners.
- double h = tan(degToRad(fov / 2));
- double diag = sqrt(1.0 + square(h) + square(h * (double) windowWidth / (double) windowHeight));
- double cosFOV = 1.0 / diag;
-
- Vec3d viewVector = Vec3d(0.0, 0.0, -1.0) * cameraOrientation.toMatrix3();
-
- for (MarkerList::const_iterator iter = markers.begin(); iter != markers.end(); iter++)
- {
- UniversalCoord uc = iter->position(jd);
- Vec3d offset = (uc - cameraPosition) * astro::microLightYearsToKilometers(1.0);
-
- // Only render those markers that lie withing the field of view.
- if ((offset * viewVector) > cosFOV * offset.length())
- {
- double distance = offset.length();
- float symbolSize = 0.0f;
- if (iter->sizing() == DistanceBasedSize)
- {
- symbolSize = (float) (iter->representation().size() / distance) / pixelSize;
- }
- if (iter->occludable())
- {
- // If the marker is occludable, add it to the sorted annotation list if it's relatively
- // nearby, and to the background list if it's very distant.
- if (distance < astro::lightYearsToKilometers(1.0))
- {
- // Modify the marker position so that it is always in front of the marked object.
- double boundingRadius;
- if (iter->object().body() != NULL)
- boundingRadius = iter->object().body()->getBoundingRadius();
- else
- boundingRadius = iter->object().radius();
- offset *= (1.0 - boundingRadius * 1.01 / distance);
-
- addSortedAnnotation(&(iter->representation()), EMPTY_STRING, iter->representation().color(),
- Point3f((float) offset.x, (float) offset.y, (float) offset.z),
- AlignLeft, VerticalAlignTop, symbolSize);
- }
- else
- {
- addAnnotation(backgroundAnnotations,
- &(iter->representation()), EMPTY_STRING, iter->representation().color(),
- Point3f((float) offset.x, (float) offset.y, (float) offset.z),
- AlignLeft, VerticalAlignTop, symbolSize);
- }
- }
- else
- {
- addAnnotation(foregroundAnnotations,
- &(iter->representation()), EMPTY_STRING, iter->representation().color(),
- Point3f((float) offset.x, (float) offset.y, (float) offset.z),
- AlignLeft, VerticalAlignTop, symbolSize);
- }
- }
- }
- }
- void Renderer::setStarStyle(StarStyle style)
- {
- starStyle = style;
- markSettingsChanged();
- }
- Renderer::StarStyle Renderer::getStarStyle() const
- {
- return starStyle;
- }
- Renderer::StarVertexBuffer::StarVertexBuffer(unsigned int _capacity) :
- capacity(_capacity),
- vertices(NULL),
- texCoords(NULL),
- colors(NULL)
- {
- nStars = 0;
- vertices = new float[capacity * 12];
- texCoords = new float[capacity * 8];
- colors = new unsigned char[capacity * 16];
- // Fill the texture coordinate array now, since it will always have
- // the same contents.
- for (unsigned int i = 0; i < capacity; i++)
- {
- unsigned int n = i * 8;
- texCoords[n ] = 0; texCoords[n + 1] = 0;
- texCoords[n + 2] = 1; texCoords[n + 3] = 0;
- texCoords[n + 4] = 1; texCoords[n + 5] = 1;
- texCoords[n + 6] = 0; texCoords[n + 7] = 1;
- }
- }
- Renderer::StarVertexBuffer::~StarVertexBuffer()
- {
- if (vertices != NULL)
- delete[] vertices;
- if (colors != NULL)
- delete[] colors;
- if (texCoords != NULL)
- delete[] texCoords;
- }
- void Renderer::StarVertexBuffer::start()
- {
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(3, GL_FLOAT, 0, vertices);
- glEnableClientState(GL_COLOR_ARRAY);
- glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
- glDisableClientState(GL_NORMAL_ARRAY);
- }
- void Renderer::StarVertexBuffer::render()
- {
- if (nStars != 0)
- {
- glDrawArrays(GL_QUADS, 0, nStars * 4);
- nStars = 0;
- }
- }
- void Renderer::StarVertexBuffer::finish()
- {
- render();
- glDisableClientState(GL_COLOR_ARRAY);
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- }
- void Renderer::StarVertexBuffer::addStar(const Vec3f& pos,
- const Color& color,
- float size)
- {
- if (nStars < capacity)
- {
- int n = nStars * 12;
- vertices[n + 0] = pos.x + v0.x * size;
- vertices[n + 1] = pos.y + v0.y * size;
- vertices[n + 2] = pos.z + v0.z * size;
- vertices[n + 3] = pos.x + v1.x * size;
- vertices[n + 4] = pos.y + v1.y * size;
- vertices[n + 5] = pos.z + v1.z * size;
- vertices[n + 6] = pos.x + v2.x * size;
- vertices[n + 7] = pos.y + v2.y * size;
- vertices[n + 8] = pos.z + v2.z * size;
- vertices[n + 9] = pos.x + v3.x * size;
- vertices[n + 10] = pos.y + v3.y * size;
- vertices[n + 11] = pos.z + v3.z * size;
- n = nStars * 16;
- color.get(colors + n);
- color.get(colors + n + 4);
- color.get(colors + n + 8);
- color.get(colors + n + 12);
- nStars++;
- }
- if (nStars == capacity)
- {
- render();
- nStars = 0;
- }
- }
- void Renderer::StarVertexBuffer::setBillboardOrientation(const Quatf& q)
- {
- Mat3f m = q.toMatrix3();
- v0 = Vec3f(-1, -1, 0) * m;
- v1 = Vec3f( 1, -1, 0) * m;
- v2 = Vec3f( 1, 1, 0) * m;
- v3 = Vec3f(-1, 1, 0) * m;
- }
- Renderer::PointStarVertexBuffer::PointStarVertexBuffer(unsigned int _capacity) :
- capacity(_capacity),
- nStars(0),
- vertices(NULL),
- context(NULL),
- useSprites(false),
- texture(NULL)
- {
- vertices = new StarVertex[capacity];
- }
- Renderer::PointStarVertexBuffer::~PointStarVertexBuffer()
- {
- if (vertices != NULL)
- delete[] vertices;
- }
- void Renderer::PointStarVertexBuffer::startSprites(const GLContext& _context)
- {
- context = &_context;
- assert(context->getVertexProcessor() != NULL || !useSprites); // vertex shaders required for new star rendering
- unsigned int stride = sizeof(StarVertex);
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
- glEnableClientState(GL_COLOR_ARRAY);
- glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
- VertexProcessor* vproc = context->getVertexProcessor();
- vproc->enable();
- vproc->use(vp::starDisc);
- vproc->enableAttribArray(6);
- vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisableClientState(GL_NORMAL_ARRAY);
- glEnable(GL_POINT_SPRITE_ARB);
- glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
- useSprites = true;
- }
- void Renderer::PointStarVertexBuffer::startPoints(const GLContext& _context)
- {
- context = &_context;
- unsigned int stride = sizeof(StarVertex);
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
- glEnableClientState(GL_COLOR_ARRAY);
- glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
- // An option to control the size of the stars would be helpful.
- // Which size looks best depends a lot on the resolution and the
- // type of display device.
- // glPointSize(2.0f);
- // glEnable(GL_POINT_SMOOTH);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisable(GL_TEXTURE_2D);
- glDisableClientState(GL_NORMAL_ARRAY);
- useSprites = false;
- }
- void Renderer::PointStarVertexBuffer::render()
- {
- if (nStars != 0)
- {
- unsigned int stride = sizeof(StarVertex);
- if (useSprites)
- {
- glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
- glEnable(GL_TEXTURE_2D);
- }
- else
- {
- glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
- glDisable(GL_TEXTURE_2D);
- glPointSize(1.0f);
- }
- glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
- glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
- if (useSprites)
- {
- VertexProcessor* vproc = context->getVertexProcessor();
- vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
- }
- if (texture != NULL)
- texture->bind();
- glDrawArrays(GL_POINTS, 0, nStars);
- nStars = 0;
- }
- }
- void Renderer::PointStarVertexBuffer::finish()
- {
- render();
- glDisableClientState(GL_COLOR_ARRAY);
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- if (useSprites)
- {
- VertexProcessor* vproc = context->getVertexProcessor();
- vproc->disableAttribArray(6);
- vproc->disable();
- glDisable(GL_POINT_SPRITE_ARB);
- }
- else
- {
- glEnable(GL_TEXTURE_2D);
- }
- }
- void Renderer::PointStarVertexBuffer::addStar(const Vec3f& pos,
- const Color& color,
- float size)
- {
- if (nStars < capacity)
- {
- vertices[nStars].position = pos;
- vertices[nStars].size = size;
- color.get(vertices[nStars].color);
- nStars++;
- }
- if (nStars == capacity)
- {
- render();
- nStars = 0;
- }
- }
- void Renderer::PointStarVertexBuffer::setTexture(Texture* _texture)
- {
- texture = _texture;
- }
- void Renderer::loadTextures(Body* body)
- {
- Surface& surface = body->getSurface();
- if (surface.baseTexture.tex[textureResolution] != InvalidResource)
- surface.baseTexture.find(textureResolution);
- if ((surface.appearanceFlags & Surface::ApplyBumpMap) != 0 &&
- context->bumpMappingSupported() &&
- surface.bumpTexture.tex[textureResolution] != InvalidResource)
- surface.bumpTexture.find(textureResolution);
- if ((surface.appearanceFlags & Surface::ApplyNightMap) != 0 &&
- (renderFlags & ShowNightMaps) != 0)
- surface.nightTexture.find(textureResolution);
- if ((surface.appearanceFlags & Surface::SeparateSpecularMap) != 0 &&
- surface.specularTexture.tex[textureResolution] != InvalidResource)
- surface.specularTexture.find(textureResolution);
- if ((renderFlags & ShowCloudMaps) != 0 &&
- body->getAtmosphere() != NULL &&
- body->getAtmosphere()->cloudTexture.tex[textureResolution] != InvalidResource)
- {
- body->getAtmosphere()->cloudTexture.find(textureResolution);
- }
- if (body->getRings() != NULL &&
- body->getRings()->texture.tex[textureResolution] != InvalidResource)
- {
- body->getRings()->texture.find(textureResolution);
- }
-
- if (body->getGeometry() != InvalidResource)
- {
- GetGeometryManager()->find(body->getGeometry());
- }
- }
- void Renderer::invalidateOrbitCache()
- {
- orbitCache.clear();
- }
- bool Renderer::settingsHaveChanged() const
- {
- return settingsChanged;
- }
- void Renderer::markSettingsChanged()
- {
- settingsChanged = true;
- notifyWatchers();
- }
- void Renderer::addWatcher(RendererWatcher* watcher)
- {
- assert(watcher != NULL);
- watchers.insert(watchers.end(), watcher);
- }
- void Renderer::removeWatcher(RendererWatcher* watcher)
- {
- list<RendererWatcher*>::iterator iter =
- find(watchers.begin(), watchers.end(), watcher);
- if (iter != watchers.end())
- watchers.erase(iter);
- }
- void Renderer::notifyWatchers() const
- {
- for (list<RendererWatcher*>::const_iterator iter = watchers.begin();
- iter != watchers.end(); iter++)
- {
- (*iter)->notifyRenderSettingsChanged(this);
- }
- }