Moon surface map
This commit is contained in:
1 parent
0e8134aa59
commit
f8b515e198
36 files changed
+1710
-222
No files matched your search
@@ -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!(
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ fn render(scene: &str, adapter: &str) -> image::ImageBuffer<image::Rgb<u16>, Vec
|
||||
render_at(scene, adapter, "L0")
|
||||
}
|
||||
|
||||
/// A data directory whose Earth images are small and uniformly gray, for tests at
|
||||
/// lighting L1 that do not need Blue Marble: loading the real 16384 x 8192 images takes
|
||||
/// A data directory whose Earth images and Moon map are small and uniform, for tests at
|
||||
/// lighting L1 that do not need the real ones: loading those (16384 x 8192 each) takes
|
||||
/// seconds in a debug build, and they are only there once fetched.
|
||||
fn gray_earth() -> PathBuf {
|
||||
static DIR: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
|
||||
@@ -44,6 +44,11 @@ fn gray_earth() -> PathBuf {
|
||||
.save(dir.join(format!("earth/bmng-2004-{month:02}.jpg")))
|
||||
.unwrap();
|
||||
}
|
||||
// A uniform Moon: I/F 0.06 (16-bit values of I/F x 100,000).
|
||||
std::fs::create_dir_all(dir.join("moon")).unwrap();
|
||||
image::ImageBuffer::<image::Luma<u16>, _>::from_pixel(64, 32, image::Luma([6000]))
|
||||
.save(dir.join("moon/wac-hapke-643.png"))
|
||||
.unwrap();
|
||||
dir
|
||||
})
|
||||
.clone()
|
||||
@@ -164,6 +169,35 @@ fn solar_eclipse_puts_the_umbra_at_greatest_eclipse() {
|
||||
assert!(corner.iter().all(|&v| v > 70), "{corner:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lighting_l1_names_a_missing_moon_map() {
|
||||
// Earth's images but no Moon map: the error names the file and the script.
|
||||
let dir = out_dir().join("earth-only");
|
||||
std::fs::create_dir_all(dir.join("earth")).unwrap();
|
||||
std::fs::copy(
|
||||
gray_earth().join("earth/bmng-2004-07.jpg"),
|
||||
dir.join("earth/bmng-2004-07.jpg"),
|
||||
)
|
||||
.unwrap();
|
||||
let result = resolved(&[
|
||||
"render",
|
||||
"--scene",
|
||||
"examples/scenes/landsat8.toml",
|
||||
"--lighting",
|
||||
"L1",
|
||||
"--data",
|
||||
dir.to_str().unwrap(),
|
||||
"--preview",
|
||||
"x.png",
|
||||
]);
|
||||
assert!(!result.status.success());
|
||||
let stderr = String::from_utf8_lossy(&result.stderr);
|
||||
assert!(
|
||||
stderr.contains("moon/wac-hapke-643.png") && stderr.contains("fetch-moon.py"),
|
||||
"{stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_are_reported() {
|
||||
let missing = resolved(&["render", "--scene", "no/such.toml", "--preview", "x.png"]);
|
||||
|
||||
Reference in new issue
Block a user