use std::error::Error as StdError;
use std::fmt;
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
use crate::{CaptureError, CaptureOptions, CapturePngError, CaptureRgba8, capture_rgba8};
#[cfg(feature = "inspection")]
use crate::{RenderIntrospectionOptions, RenderIntrospectionReportV1};
use super::{FirstRender, HeadlessGltfViewer, HeadlessGltfViewerBuilder, InteractiveGltfViewer};
#[derive(Debug, Clone, PartialEq)]
pub enum ViewerCaptureError {
Capture(CaptureError),
InvalidFrameBuffer {
width: u32,
height: u32,
expected_len: usize,
actual_len: usize,
},
EncodePng {
reason: String,
},
Io {
path: String,
reason: String,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum ViewerPngError {
Render(crate::Error),
Capture(ViewerCaptureError),
}
impl HeadlessGltfViewerBuilder {
pub async fn render_png_bytes(self) -> Result<Vec<u8>, ViewerPngError> {
let first = self.render().await?;
Ok(first.capture_png_bytes()?)
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn render_png(self, path: impl AsRef<Path>) -> Result<(), ViewerPngError> {
let path = path.as_ref().to_path_buf();
let bytes = self.render_png_bytes().await?;
std::fs::write(&path, bytes)
.map_err(|error| ViewerCaptureError::Io {
path: path.display().to_string(),
reason: error.to_string(),
})
.map_err(ViewerPngError::from)
}
}
impl FirstRender {
pub fn capture(&self) -> Result<CaptureRgba8, CaptureError> {
capture_rgba8(
&self.scene,
&self.renderer,
capture_options_for_import(&self.import, &self.scene),
)
}
#[cfg(feature = "inspection")]
pub fn render_introspection(
&self,
options: RenderIntrospectionOptions,
) -> Result<RenderIntrospectionReportV1, CaptureError> {
let capture = self.capture()?;
let inspection = self
.scene
.inspect_with_assets(&self.assets)
.to_schema_report();
Ok(self
.renderer
.introspect_capture(&capture, &inspection, options))
}
#[cfg(feature = "inspection")]
pub fn render_introspection_json(&self, detail: bool) -> Result<String, CaptureError> {
let options = if detail {
RenderIntrospectionOptions::detail()
} else {
RenderIntrospectionOptions::summary()
};
let report = self.render_introspection(options)?;
Ok(serde_json::to_string(&report).expect("render introspection report is serializable"))
}
pub fn capture_png_bytes(&self) -> Result<Vec<u8>, ViewerCaptureError> {
self.capture()?
.to_png_bytes()
.map_err(ViewerCaptureError::from)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn capture_png(&self, path: impl AsRef<Path>) -> Result<(), ViewerCaptureError> {
self.capture()?
.write_png(path)
.map_err(ViewerCaptureError::from)
}
}
impl HeadlessGltfViewer {
pub fn capture(&self) -> Result<CaptureRgba8, CaptureError> {
capture_rgba8(
&self.scene,
&self.renderer,
capture_options_for_import(&self.import, &self.scene),
)
}
#[cfg(feature = "inspection")]
pub fn render_introspection(
&self,
options: RenderIntrospectionOptions,
) -> Result<RenderIntrospectionReportV1, CaptureError> {
let capture = self.capture()?;
let inspection = self
.scene
.inspect_with_assets(&self.assets)
.to_schema_report();
Ok(self
.renderer
.introspect_capture(&capture, &inspection, options))
}
#[cfg(feature = "inspection")]
pub fn render_introspection_json(&self, detail: bool) -> Result<String, CaptureError> {
let options = if detail {
RenderIntrospectionOptions::detail()
} else {
RenderIntrospectionOptions::summary()
};
let report = self.render_introspection(options)?;
Ok(serde_json::to_string(&report).expect("render introspection report is serializable"))
}
pub fn capture_png_bytes(&self) -> Result<Vec<u8>, ViewerCaptureError> {
self.capture()?
.to_png_bytes()
.map_err(ViewerCaptureError::from)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn capture_png(&self, path: impl AsRef<Path>) -> Result<(), ViewerCaptureError> {
self.capture()?
.write_png(path)
.map_err(ViewerCaptureError::from)
}
}
impl InteractiveGltfViewer {
pub fn capture(&self) -> Result<CaptureRgba8, CaptureError> {
capture_rgba8(
&self.scene,
&self.renderer,
capture_options_for_import(&self.import, &self.scene),
)
}
#[cfg(feature = "inspection")]
pub fn render_introspection(
&self,
options: RenderIntrospectionOptions,
) -> Result<RenderIntrospectionReportV1, CaptureError> {
let capture = self.capture()?;
let inspection = self
.scene
.inspect_with_assets(&self.assets)
.to_schema_report();
Ok(self
.renderer
.introspect_capture(&capture, &inspection, options))
}
#[cfg(feature = "inspection")]
pub fn render_introspection_json(&self, detail: bool) -> Result<String, CaptureError> {
let options = if detail {
RenderIntrospectionOptions::detail()
} else {
RenderIntrospectionOptions::summary()
};
let report = self.render_introspection(options)?;
Ok(serde_json::to_string(&report).expect("render introspection report is serializable"))
}
pub fn capture_png_bytes(&self) -> Result<Vec<u8>, ViewerCaptureError> {
self.capture()?
.to_png_bytes()
.map_err(ViewerCaptureError::from)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn capture_png(&self, path: impl AsRef<Path>) -> Result<(), ViewerCaptureError> {
self.capture()?
.write_png(path)
.map_err(ViewerCaptureError::from)
}
}
fn capture_options_for_import(import: &crate::SceneImport, scene: &crate::Scene) -> CaptureOptions {
import
.bounds_world(scene)
.map_or_else(CaptureOptions::default, |bounds| {
CaptureOptions::default().with_auto_frame_bounds(bounds)
})
}
impl fmt::Display for ViewerCaptureError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Capture(error) => error.fmt(formatter),
Self::InvalidFrameBuffer {
width,
height,
expected_len,
actual_len,
} => write!(
formatter,
"renderer frame buffer for {width}x{height} has {actual_len} bytes; expected {expected_len} RGBA8 bytes"
),
Self::EncodePng { reason } => write!(formatter, "failed to encode PNG: {reason}"),
Self::Io { path, reason } => write!(formatter, "failed to write PNG {path}: {reason}"),
}
}
}
impl std::error::Error for ViewerCaptureError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::Capture(error) => Some(error),
Self::InvalidFrameBuffer { .. } | Self::EncodePng { .. } | Self::Io { .. } => None,
}
}
}
impl From<CaptureError> for ViewerCaptureError {
fn from(error: CaptureError) -> Self {
Self::Capture(error)
}
}
impl From<CapturePngError> for ViewerCaptureError {
fn from(error: CapturePngError) -> Self {
match error {
CapturePngError::Capture(error) => Self::Capture(error),
CapturePngError::InvalidPixelBuffer {
width,
height,
expected_len,
actual_len,
} => Self::InvalidFrameBuffer {
width,
height,
expected_len,
actual_len,
},
CapturePngError::Encode { reason } => Self::EncodePng { reason },
CapturePngError::Io { path, reason } => Self::Io { path, reason },
}
}
}
impl fmt::Display for ViewerPngError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Render(error) => write!(formatter, "failed to render glTF PNG: {error}"),
Self::Capture(error) => error.fmt(formatter),
}
}
}
impl std::error::Error for ViewerPngError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::Render(error) => Some(error),
Self::Capture(error) => Some(error),
}
}
}
impl From<crate::Error> for ViewerPngError {
fn from(error: crate::Error) -> Self {
Self::Render(error)
}
}
impl From<ViewerCaptureError> for ViewerPngError {
fn from(error: ViewerCaptureError) -> Self {
Self::Capture(error)
}
}