
 /*
   ###########################################################################
   MAIN LUNEX (LUNAR EXPLORER) PROGRAM CORE API.
   Revised: 2025-May-03-Sat
   This POV-Ray source code is released to the public domain.

// -------------------------------------------------------------------
// THIS VERSION IS NOT BACKWARDS COMPATIBLE WITH ANY PREVIOUS VERSIONS

   This block sets up the main program initializations and contains the 
   computational core logic and data for the lunar calculations assuming 
   natural scale in miles.
   ###########################################################################
 */



/* --------------------------------------------------------------------------
   SET PATH TO LUNAR SURFACE DEM (3D Digital Elevation Model) MAP.  THIS MAP
   APPLIES THE OPTIONAL 3D PHASE SHADED RELIEF TO ALL SURFACE TEXTURE MAPS.
*/
  #declare LunarDEMPath = "MAPS/lunar-surface-DEM.png"; // Default DEM


/*
  Set maximum trace level and assumed gamma.

  The max trace level is set to 1 here because there are no refractions or
  reflections that need to be considered in rendering the moon.  It would
  be set higher for a scene involving semi-transparent, shiny, reflective
  or refractive transparent surfaces, situations which do not apply here.  
  Setting it to 1 saves time by not doing extra, unnecessary work that
  has no visible effect.
*/
  #global_settings {max_trace_level 1 assumed_gamma 1}

// Negate transparent background here because it only works from INI.
  #declare TransBgSwitch = "OFF"; // Transparent background     Default=OFF

// Set default solar brightness adjustment. (Removable)
  #declare SunAdj = 1.0;

// Special dummy height patch.  Doesn't actually do anything yet.
  #declare MarkHeight = 0; 
  
// Internally adjust the relative phase angle for the cam view longitude.
// This makes the phase angle relative to the cam/eye location.
  #declare PhaseAng = PhaseAng - CamLon;


/*
  ============================================================================
  THE DATA

  Set geocentric parameters to natural scale in miles. These are the constants
  used in the computations. 
  
  ============================================================================  
*/

// Actual values used for computations.
  #declare SunDist      = 92955807; // Miles in 1.0 AU (Astronomical Unit)
  #declare MoonRadius   = 1079.35;  // Mean lunar radius in miles.
  #declare MoonDist     = 238856;   // Mean geocentric lunar distance in miles.
  #declare EarthRadius  = 3963.19;  // Mean Earth radius in miles.
  #declare SurfSurfDist = 238856 - EarthRadius - MoonRadius; 
  
/*
 ============================================================================
 FOR QUICK LUNAR CALCULATIONS:

 R = Mean radius = 1079.35 mi = 1737.05 km

 C = Mean circumference = 2*pi * R 
   = 10914.18 km
   =  6781.76 mi 

 1.0 deg lat = 30.317 km per degree 
             = 18.838 mi per degree 
             
 1.0 deg lng = 30.317 * cos(lat) km/deg
             = 18.838 * cos(lat) mi/deg

 ------------------------------------------------------------------
 Mean surface distance in miles and kilometers per degree latitude:
 This value is treated as constant and independent of the longitude
 value.

 s = R * N * 18.838 = Surface distance in miles per deg lat

 For 1 degree:
 s = 18.838 mi = 30.317 km per degree latitude

 ----------------------------------------------------------------------
 Mean surface distance in miles and kilometers per N degrees longitude:
 This value varies according to the latitude value.

 s = Surface distance per N degrees lng (horizontal)
 
 For 1 degree:
 s = 18.838 * cos(lat) mi = 30.317 * cos(lat) km per deg lng

  ============================================================================
*/



/*
   This block defines the space background color, sun and earthshine values.
   These attributes are controled via the program SCENE DEFINITION PANEL as
   defined within each rendering script.
*/


/*
  ==========================================================================
  SPACE BACKGROUND COLOR / THE SUN / EARTHSHINE
  ==========================================================================
  Define the space background color, which can can be any color, with the
  option of a transparent background available via external INI execution.

  Then define the XYZ-coordinates of the virtual sun, based on the phase
  altitude, phase angle and sun distance parameters and place the sun at
  that location in space.

  The virtual sun is simply a plain white, point-light source at the same
  mean distance as the real sun.

  The earthshine is simulated by a faint glow produced by a light source
  located at the position of the earth in space at mean geocentric lunar
  distance.
*/

// ------------------------------------------------------
// Compute working values of phase parameters in radians.

  #declare B = PhaseAlt * pi/180;
  #declare L = PhaseAng * pi/180;


/* --------------------------------------------------
   Compute rectangular XYZ-coordinates of the virtual 
   sun and then place it at those coordinates.
*/
  #declare SunX = SunDist * cos(B) * sin(L);
  #declare SunY = SunDist * sin(B);
  #declare SunZ = SunDist * cos(B) * cos(L);

/* -----------------------------------------------------------------------
   Patch to adjust the solar brightness because a scene rendered in mode 2
   generally requires a little more light than the same scene rendered in
   mode 1. (Removable)
*/
  #if (RenderMode = 2) 
      #declare SunAdj = SunlightLevel * 1.125; 
  #else 
      #declare SunAdj = SunlightLevel * 1.300;
  #end

   light_source {<SunX,SunY,SunZ> rgb<1,1,1> * SunlightLevel * SunAdj}


/* ----------------------------------------------
   If executed via INI and the TransBgndSwitch is
   set to ON, then the moon is rendered against a
   transparent background. Otherwise, the default
   background color (black) is used.
*/
   #if (strupr(TransBgSwitch) != "ON")
   background {rgb<0,0,0>} // Background color (Default = black)
   #end


/* --------------------------------------
   Set the earthshine to the given level.
   This value is in the range: 0 to 255
*/
   light_source {<0, 0, -MoonDist>   rgb <1,1,1> * EarthshineLevel/255}


/* ------------------------------------------
   Execute the necessary modules according to
   the selected (RenderMode = 1 or 2).
  
   Mode Logic: If RenderMode not 1 then 2
*/

 #if (RenderMode = 1)

/*
  ============================================================================
  CAMERA FOR RENDERING SPHERICAL BUMP MAP PROJECTION IN MODE 1
  ============================================================================
  Define mobile camera with zoom, rotation and AutoFOV features.  The latitude
  and longitude coordinates of the camera correlate exactly to the seleno-
  graphic coordinates directly below it.  The camera has its own sphere and
  shares the same selographic coordinates system as the moon below it.
*/

  camera
{
// --------------------------------------------------
// Patch for that blasted POV-Ray polar view anomaly.

   #if (abs(CamLat) = 90) #declare CamLat = CamLat * 0.9999999; #end


/* -----------------------------------------------------------
   Set conversion constant so that this BM version can use the
   same table of view control settings as the old V8 version.
*/
 #declare k = 307/200; // UDLR BM to VX conversion scaler

 #declare B =  CamLat * pi/180;
 #declare L = (180 - CamLon) * pi/180;
 
 #declare CamX = CamDist * cos(B) * sin(L) ;
 #declare CamY = CamDist * sin(B);
 #declare CamZ = CamDist * cos(B) * cos(L) ;

 location <CamX,CamY,CamZ>

 look_at <k*CamMoveLRX, k*CamMoveUDY, 0>

 rotate <0, 0, -CamRotAngZ>

 #if (strupr(AutoFOVSwitch) = "ON")
 #declare AutoFOV =
 acos(sqrt(CamDist*CamDist - MoonRadius*MoonRadius) / CamDist) * 360/pi;
 angle AutoFOV * 80/59/CamZoom
 #else
 angle 141/200/CamZoom
 #end
}




/*
  ==========================================================================
  DEFINE THE LUNAR SPHERE FOR QUICK BUMP MAP RENDERING MODE 1
  ==========================================================================
  Define a lunar sphere with 3D relief simulated via bump-map projection.

  This does NOT provide REAL 3D relief, so there is no true height to the
  lunar surface features, the terminator cuts off sharply and there are
  no irregular mountain peaks rising above the smooth horizon.

  However, for scenes that do not show the terminator or horizon, this
  method of providing simulated 3D relief is reasonably quick, very
  realistic and does not generally require much rendering time.

  This method is also ideal for generating thumbnail images as might be
  displayed in a daily phase calendar for a given month.  In very small
  images like that, the irregularities along the terminator or horizon
  are insignificant.
*/

  sphere
{

// ------------------------------------------
// Put lunar sphere at origin of coordinates.

  <0,0,0>, MoonRadius

/* -------------------------------------------------------
   Apply selected surface image map to the lunar sphere.
   The selected image file type is handled automatically
   and covers JPG, PNG, GIF, BMP and TGA file formats.
*/
   texture
 {
  pigment
 {
 #declare FileType =
 strupr(substr(SurfaceMap, strlen(SurfaceMap)-2, 3));

 #if (FileType = "BMP")
  image_map{bmp SurfaceMap map_type 1}

 #elseif (FileType = "GIF")
 image_map{gif SurfaceMap map_type 1}

 #elseif (FileType = "JPG" | FileType = "PEG")
 image_map{jpeg SurfaceMap map_type 1}

 #elseif (FileType = "PNG")
 image_map{png SurfaceMap map_type 1}

 #elseif (FileType = "TGA")
 image_map{tga SurfaceMap map_type 1}
 #end
 }


// ---------------------------------------------------------------
// Set 3D relief OFF/ON=Default according to ReliefSwitch setting.

   #if (strupr(ReliefSwitch) = "ON")
   #declare BumpValue = 32; // Relief value - Can be changed.
   #else
   #declare BumpValue = 0;
   #end


/* -----------------------------------------------------
   Apply bump map to create simulated 3D surface relief
   if 3D relief is ON=Default.
*/
   normal

 {bump_map{png LunarDEMPath map_type 1 bump_size BumpValue}}

 }
  rotate (MoonRotAngY - 90) * y

  finish {diffuse 1 ambient SurfAmbiLevel}
}



// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

/*
  Add a surface coordinates marker or pointer to
  to indiate a particular spot on the surface,
  such as the coordinates of a lunar lander
  or a particular crater.

  Stick a colored marker into the lunar sphere at
  the given marker coordinates.

  This code is executed ONLY if (MarkSwitch)
  is set to ON state.
*/

 #if (strupr(MarkSwitch) = "ON")

  cone
{
 #declare w = 1;
 #if (RenderMode = 1) #declare w = 1.54;
 #end

 #declare B = MarkLat * pi/180;
 #declare L = (180 - MarkLon) * pi/180;
 #declare R = w * (MoonRadius + MarkHeight);
 #declare r = 0.95 * w * (MoonRadius - 400);

 #declare px = r * cos(B) * sin(L);
 #declare py = r * sin(B);
 #declare pz = r * cos(B) * cos(L);

 #declare pX = R * cos(B) * sin(L);
 #declare pY = R * sin(B);
 #declare pZ = R * cos(B) * cos(L);

<px, py, pz>, 0 // Center and radius of cone inner end
<pX, pY, pZ>,  w * MarkWidth // Center and radius of cone outer end

texture {pigment{MarkColor}}

finish  {diffuse 1  ambient MarkAmbi}
}

 #end





// Define N/S polar axes markers (long color-coded cylinders).

/*
   -----------------------------------------------
   Draw blue N pole and red S pole axes markers if
   PolarAxSwitch = ON.  Otherwise, do nothing.

   It alters and uses an internal MarkWidth value smaller
   than the given MarkWidth value.  That is why its measure
   is called 'units', since they are not uniformly defined.

   Long, thin cylinders are used to mark the polar axes.

   The (R) value may need to be adjusted, depending on the
   zoom factor and is applied equally to both polar markers.
*/

 #if (strupr(PolarAxSwitch) = "ON")

 #declare MarkWidth = 0.05 * MarkWidth;
 #declare R = 789;


// ------------------------------
// Render blue north pole marker.

   cylinder
{
 #declare B = pi/2;
 #declare L = pi;

 #declare  pX = R * cos(B) * sin(L);
 #declare  pY = R * sin(B);
 #declare  pZ = R * cos(B) * cos(L);

<0,0,0>, <pX,pY,pZ>, MarkWidth

finish  {diffuse 1 ambient 0.25}
texture {pigment {rgb<0, 0.25, 1>}}
}


// -----------------------------
// Render red south pole marker.

   cylinder
{
 #declare B = -pi/2;
 #declare L =  pi;

 #declare  pX = R * cos(B) * sin(L);
 #declare  pY = R * sin(B);
 #declare  pZ = R * cos(B) * cos(L);

<0,0,0>, <pX,pY,pZ>, MarkWidth

finish  {diffuse 1 ambient 0.25}
texture {pigment {rgb<1,0,0>}}
}

 #end



// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@




 #else


/*
  CAMERA FOR RENDERING MODE 2 - USING SPHERICAL ISOSURFACE WITH HEIGHT FIELD
  ==============================================================================
  Define mobile camera with zoom, rotation and AutoFOV features.  The latitude
  and longitude coordinates of the camera correlate exactly to the seleno-
  graphic coordinates directly below it.
*/

  camera
{
// --------------------------------------------------
// Patch for that blasted POV-Ray polar view anomaly.
   #if (abs(CamLat) = 90) #declare CamLat = CamLat * 0.9999999; #end

   #declare B =  CamLat * pi/180;
   #declare L = (180 - CamLon) * pi/180;

   #declare CamX = CamDist * cos(B) * sin(L);
   #declare CamY = CamDist * sin(B);
   #declare CamZ = CamDist * cos(B) * cos(L);

   location <CamX,CamY,CamZ>

  look_at <CamMoveLRX, CamMoveUDY, 0>

 rotate <0,0, -CamRotAngZ>

 #if (strupr(AutoFOVSwitch) = "ON")
 #declare AutoFOV =
 acos(sqrt(CamDist*CamDist - MoonRadius*MoonRadius) / CamDist) * 360/pi;
 angle 177/200*AutoFOV/CamZoom
 #else
 angle 459/1000/CamZoom
 #end
}




/*
  DEFINE MODE 2 SPHERICAL LUNAR ISOSURFACE WITH HEIGHT FIELD
  ==========================================================================
  Define the lunar sphere as a spherical iso-surface with height field.

  This is more complicated than 'bump-mapping', but it produces a much more
  realistic 3D relief along the terminator line instead of cutting it off
  with a sharp edge and the irregular mountain peaks along the horizon
  are also visible.

  This method can be quite time-consuming, depending on the settings used
  and the resources and speed of the CPU running POV-Ray.  Scenes could
  take several minutes to render in some cases, but the quality of the
  results makes it worth the wait.
*/

#if (strupr(ReliefSwitch) = "ON")
    #declare ReliefSwitch = 1;
#else
    #declare ReliefSwitch = 0;
#end

#declare ReliefLevel  = 14;  // Tweak as deemed necessary (Default = 11)
#declare MaxGradLevel = 1.5; // Tweak as deemed necessary (Default = 1.5)

#declare Lunar_Sphere = function {internal(61)}

#declare Lunar_Height_Field =
 function
{
 pigment
{image_map{png LunarDEMPath map_type 1 interpolate 2}}
}

// -----------------------------------------
// Define the spherical isosurface that will
// become the 3D lunar surface relief.

 isosurface
{
 function
{
  Lunar_Sphere(x,y,z, MoonRadius)
- Lunar_Height_Field(x,y,z).gray*ReliefSwitch*ReliefLevel/1000*MoonRadius
+ 0.35*MoonRadius
}

contained_by {sphere{0, MoonRadius}}

max_gradient MaxGradLevel

accuracy 1/14500

/* -------------------------------------------------------
   Apply selected surface texture map to the lunar sphere.
   The selected image file type is handled automatically.
   Handled formats are: BMP,GIF,JPG,PNG and TGA.  These
   maps may be grids or special maps like altimetry or
   gravimetric data, mineral distribution overlays, etc.
*/
   texture
{
 pigment
{
 #declare FileType =
 strupr(substr(SurfaceMap, strlen(SurfaceMap)-2, 3));

 #if (FileType = "BMP")
  image_map{bmp SurfaceMap map_type 1}

 #elseif (FileType = "GIF")
 image_map{gif SurfaceMap map_type 1}

 #elseif (FileType = "JPG" | FileType = "PEG")
 image_map{jpeg SurfaceMap map_type 1}

 #elseif (FileType = "PNG")
 image_map{png SurfaceMap map_type 1}

 #elseif (FileType = "TGA")
 image_map{tga SurfaceMap map_type 1}
 #end
}
 finish {diffuse 1 ambient SurfAmbiLevel}
}
 rotate (MoonRotAngY - 90) * y
}
 #end





/*
  ----------------------------------------------
  Add a surface coordinates marker or pointer to
  to indiate a particular spot on the surface,
  such as the coordinates of a lunar lander
  or a particular crater.

  Stick a colored marker into the lunar sphere at
  the given marker coordinates.

  This code is executed ONLY if (MarkSwitch)
  is set to ON state.
*/

 #if (strupr(MarkSwitch) = "ON")

 cone
{
 #declare w = 1;
 #if (RenderMode = 1) #declare w = 1.54; 
 #end

 #declare B = MarkLat * pi/180;
 #declare L = (180 - MarkLon) * pi/180;
 #declare R = w * (MoonRadius + MarkHeight);
 #declare r = 0.95 * w * (MoonRadius - 400);

 #declare px = r * cos(B) * sin(L);
 #declare py = r * sin(B);
 #declare pz = r * cos(B) * cos(L);

 #declare pX = R * cos(B) * sin(L);
 #declare pY = R * sin(B);
 #declare pZ = R * cos(B) * cos(L);

<px, py, pz>, 0 // Center and radius of cone inner end
<pX, pY, pZ>,  w * MarkWidth // Center and radius of cone outer end

texture {pigment{MarkColor}}

finish {diffuse 1 ambient MarkAmbi}
}

 #end





// Define N/S polar axes markers (long color-coded cylinders).

/*
   -----------------------------------------------
   Draw blue N pole and red S pole axes markers if
   PolarAxSwitch = ON.  Otherwise, do nothing.

   It alters and uses an internal MarkWidth value smaller
   than the given MarkWidth value.  That is why its measure
   is called 'units', since they are not uniformly defined.

   Long, thin cylinders are used to mark the polar axes.

   The (R) value may need to be adjusted, depending on the
   zoom factor and is applied equally to both polar markers.
*/

#if (strupr(PolarAxSwitch) = "ON")

#declare MarkWidth = 0.05 * MarkWidth;
#declare R = 789;


// ------------------------------
// Render blue north pole marker.

 cylinder
{
#declare B = pi/2;
#declare L = pi;

#declare  pX = R * cos(B) * sin(L);
#declare  pY = R * sin(B);
#declare  pZ = R * cos(B) * cos(L);

<0,0,0>, <pX,pY,pZ>, MarkWidth

finish  {diffuse 1 ambient 0.25}
texture {pigment {rgb<0, 0.25, 1>}}
}



// -----------------------------
// Render red south pole marker.

   cylinder
{
#declare B = -pi/2;
#declare L =  pi;

#declare  pX = R * cos(B) * sin(L);
#declare  pY = R * sin(B);
#declare  pZ = R * cos(B) * cos(L);

<0,0,0>, <pX,pY,pZ>, MarkWidth

finish  {diffuse 1 ambient 0.25}
texture {pigment {rgb<1,0,0>}}
}

#end

