Moon surface map

This commit is contained in:
connor committed 2026-10-15 10:04:13 -04:00
1 parent 0e8134aa59
commit f8b515e198
36 files changed
+1710 -222

No files matched your search

+39 -13
View File
@@ -10,7 +10,7 @@ use std::time::Instant;
use clap::{Args, Parser, Subcommand};
use resolved::gpu::AdapterChoice;
use resolved::{
CameraTier, EarthTexture, Fidelity, Frame, LightingTier, Renderer, RendererOptions,
CameraTier, EarthTexture, Fidelity, Frame, LightingTier, MoonTexture, Renderer, RendererOptions,
};
use crate::scene_file::SceneFile;
@@ -60,8 +60,8 @@ struct RenderArgs {
#[arg(long, value_name = "FRAMES")]
timings: Option<usize>,
/// Directory of data the tiers need (earth/bmng-2004-MM.jpg from
/// scripts/fetch-earth.sh). Without it: RESOLVED_DATA, then ./data, then the
/// repository's data/.
/// scripts/fetch-earth.sh, moon/wac-hapke-643.png from scripts/fetch-moon.py).
/// Without it: RESOLVED_DATA, then ./data, then the repository's data/.
#[arg(long, value_name = "DIR")]
data: Option<PathBuf>,
/// Directory of astrokit's kernels, for scenes given by epoch. Without it:
@@ -108,15 +108,28 @@ fn data_directories(explicit: Option<&Path>) -> Vec<PathBuf> {
/// The Earth image for `month` (1-12), from the first data directory that has it.
fn earth_image(data: Option<&Path>, month: u32) -> Result<PathBuf, String> {
let name = format!("earth/bmng-2004-{month:02}.jpg");
data_file(
data,
&format!("earth/bmng-2004-{month:02}.jpg"),
"fetch-earth.sh",
)
}
/// `name` from the first data directory that has it; the error names `script`, which
/// makes it.
fn data_file(data: Option<&Path>, name: &str, script: &str) -> Result<PathBuf, String> {
let tried = data_directories(data);
tried.iter().map(|dir| dir.join(&name)).find(|p| p.is_file()).ok_or_else(|| {
let tried: Vec<_> = tried.iter().map(|d| d.display().to_string()).collect();
format!(
"lighting L1 needs {name} (run scripts/fetch-earth.sh, or pass --data DIR); looked in {}",
tried.join(", ")
)
})
tried
.iter()
.map(|dir| dir.join(name))
.find(|p| p.is_file())
.ok_or_else(|| {
let tried: Vec<_> = tried.iter().map(|d| d.display().to_string()).collect();
format!(
"lighting L1 needs {name} (run scripts/{script}, or pass --data DIR); looked in {}",
tried.join(", ")
)
})
}
fn render(args: &RenderArgs) -> Result<(), String> {
@@ -141,6 +154,18 @@ fn render(args: &RenderArgs) -> Result<(), String> {
} else {
None
};
let moon_texture = if args.lighting >= LightingTier::L1 {
let image = data_file(
args.data.as_deref(),
"moon/wac-hapke-643.png",
"fetch-moon.py",
)?;
Some(Arc::new(
MoonTexture::load(&image).map_err(|e| e.to_string())?,
))
} else {
None
};
let texturing = start.elapsed();
let options = RendererOptions {
@@ -155,6 +180,7 @@ fn render(args: &RenderArgs) -> Result<(), String> {
camera: args.camera,
},
earth_texture,
moon_texture,
appearance: file.appearance(),
read_radiance: false,
profile: timings.is_some(),
@@ -189,11 +215,11 @@ fn render(args: &RenderArgs) -> Result<(), String> {
println!("adapter {}", renderer.adapter_info().name);
println!("tiers {} {}", args.lighting, args.camera);
println!(
"setup {:.1} ms (includes the Earth texture's upload)",
"setup {:.1} ms (includes the Earth and Moon maps' upload)",
setup.as_secs_f64() * 1e3
);
println!(
"earth {:.1} ms (texture decode)",
"maps {:.1} ms (Earth and Moon image decode)",
texturing.as_secs_f64() * 1e3
);
println!(
+14 -2
View File
@@ -33,6 +33,9 @@ pub struct SceneFile {
pub moon: Option<[f64; 3]>,
/// Earth-fixed to inertial rotation, if not given by `epoch`; identity by default.
pub earth_attitude: Option<[f64; 4]>,
/// Moon-fixed to inertial rotation, if not given by `epoch`; by default the Moon
/// faces Earth (`Scene::moon_facing_earth`, within about 10 degrees).
pub moon_attitude: Option<[f64; 4]>,
/// Earth's and the Moon's albedo.
#[serde(default)]
pub appearance: AppearanceFile,
@@ -242,8 +245,12 @@ impl SceneFile {
#[cfg(feature = "astrokit")]
fn bodies(&self, targets: Vec<Target>, kernels: Option<&Path>) -> Result<Scene> {
if let Some(epoch) = &self.epoch {
if self.sun.is_some() || self.moon.is_some() || self.earth_attitude.is_some() {
return Err("give either epoch or sun, moon and earth_attitude, not both".into());
if self.sun.is_some()
|| self.moon.is_some()
|| self.earth_attitude.is_some()
|| self.moon_attitude.is_some()
{
return Err("give either epoch or sun, moon and the attitudes, not both".into());
}
let epoch: hifitime::Epoch =
epoch.parse().map_err(|e| format!("epoch {epoch:?}: {e}"))?;
@@ -274,6 +281,11 @@ impl SceneFile {
.unwrap_or_else(UnitQuaternion::identity),
sun: Vector3::from(sun),
moon: Vector3::from(moon),
moon_attitude: self
.moon_attitude
.map(quaternion)
.transpose()?
.unwrap_or_else(|| Scene::moon_facing_earth(Vector3::from(moon))),
targets,
})
}