// pointstarrenderer.cpp // // Copyright (C) 2001-2019, the Celestia Development Team // Original version by Chris Laurel // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. #include #include #include #include "pointstarvertexbuffer.h" #include "render.h" #include "pointstarrenderer.h" using namespace std; using namespace Eigen; // Convert a position in the universal coordinate system to astrocentric // coordinates, taking into account possible orbital motion of the star. static Vector3d astrocentricPosition(const UniversalCoord& pos, const Star& star, double t) { return pos.offsetFromKm(star.getPosition(t)); } PointStarRenderer::PointStarRenderer() : ObjectRenderer(StarDistanceLimit) { } void PointStarRenderer::process(const Star& star, float distance, float appMag) { if (distance > distanceLimit) return; Vector3f starPos = star.getPosition(); // Calculate the difference at double precision *before* converting to float. // This is very important for stars that are far from the origin. Vector3f relPos = (starPos.cast() - obsPos).cast(); float orbitalRadius = star.getOrbitalRadius(); bool hasOrbit = orbitalRadius > 0.0f; // 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.dot(viewNormal) > 0.0f || relPos.x() * relPos.x() < 0.1f || hasOrbit) { Color starColor = colorTemp->lookupColor(star.getTemperature()); 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 a solar system size 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 < SolarSystemMaxDistance || 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. Vector3d hPos = astrocentricPosition(observer->getPosition(), star, observer->getTime()); relPos = hPos.cast() * -astro::kilometersToLightYears(1.0f); distance = relPos.norm(); // Recompute apparent magnitude using new distance computation appMag = star.getApparentMagnitude(distance); discSizeInPixels = star.getRadius() / astro::lightYearsToKilometers(distance) / pixelSize; } // 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 > SolarSystemMaxDistance) { float pointSize, alpha, glareSize, glareAlpha; float size = BaseStarDiscSize * static_cast(renderer->getScreenDpi()) / 96.0f; renderer->calculatePointSize(appMag, size, pointSize, alpha, glareSize, glareAlpha); if (glareSize != 0.0f) glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), glareSize); if (pointSize != 0.0f) starVertexBuffer->addStar(relPos, Color(starColor, alpha), pointSize); // Place labels for stars brighter than the specified label threshold brightness if (((labelMode & Renderer::StarLabels) != 0) && appMag < labelThresholdMag) { Vector3f starDir = relPos.normalized(); if (starDir.dot(viewNormal) > cosFOV) { float distr = min(1.0f, 3.5f * (labelThresholdMag - appMag)/labelThresholdMag); Color color = Color(Renderer::StarLabelColor, distr * Renderer::StarLabelColor.alpha()); renderer->addBackgroundAnnotation(nullptr, starDB->getStarName(star, true), color, relPos); } } } else { Matrix3f viewMat = observer->getOrientationf().toRotationMatrix(); Vector3f viewMatZ = viewMat.row(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 = relPos * scale; rle.centerZ = rle.position.dot(viewMatZ); rle.distance = rle.position.norm(); rle.radius = star.getRadius(); rle.discSizeInPixels = discSizeInPixels; rle.appMag = appMag; rle.isOpaque = true; renderList->push_back(rle); if ((labelMode & Renderer::StarLabels) != 0) { // Position the label slightly in front of the object along a line from // object center to viewer. Vector3f pos = rle.position; pos = pos * (1.0f - star.getRadius() * 1.01f / pos.norm()); renderer->addSortedAnnotation(nullptr, starDB->getStarName(star, true), Renderer::StarLabelColor, pos); } } } }