use super::*;
impl RayTraceRenderer {
pub async fn init(
canvas: HtmlCanvasElement,
backend: RayTraceBackend,
scene: &RayTraceScene,
) -> Result<Self, JsValue> {
let width: u32 = canvas.width();
let height: u32 = canvas.height();
validate_surface(width, height, None)?;
if let Err(reason) = scene.encode_gpu() {
return Err(backend_error(ERROR_SCENE_PACK, reason));
}
let mut renderer: RayTraceRenderer = RayTraceRenderer {
canvas,
backend,
adapter: None,
scene: RayTraceScene::new(Vec::new()),
packed_frame: Vec::new(),
pixels: Vec::new(),
width,
height,
disposed: false,
};
renderer.set_adapter(Some(
RayTraceBackendAdapter::attach(renderer.get_canvas(), backend, width, height).await?,
));
if let Err(reason) = renderer.resize(width, height) {
renderer.release_backend();
return Err(reason);
}
if let Err(reason) = renderer.update_scene(scene) {
renderer.release_backend();
return Err(reason);
}
Ok(renderer)
}
pub fn update_scene(&mut self, scene: &RayTraceScene) -> Result<(), JsValue> {
if self.get_disposed() {
return Err(backend_error(ERROR_DISPOSED, ERROR_DISPOSED.to_string()));
}
let staged: Vec<f32> = match scene.encode_gpu() {
Ok(packed) => packed,
Err(reason) => return Err(backend_error(ERROR_SCENE_PACK, reason)),
};
let outcome: Result<(), JsValue> = match self.get_mut_adapter().as_mut() {
Some(adapter) => adapter.upload_scene(&staged),
None => Err(backend_error(ERROR_DISPOSED, ERROR_DISPOSED.to_string())),
};
outcome?;
self.set_scene(scene.clone());
Ok(())
}
pub fn render(
&mut self,
camera: &Camera3D,
lights: &LightingUniforms,
settings: &RayTraceSettings,
) -> Result<(), JsValue> {
if self.get_disposed() {
return Err(backend_error(ERROR_DISPOSED, ERROR_DISPOSED.to_string()));
}
match self.get_backend() {
RayTraceBackend::Canvas2D => self.render_canvas(camera, lights, settings),
RayTraceBackend::WebGl2 | RayTraceBackend::WebGpu => {
self.render_gpu(camera, lights, settings)
}
}
}
fn render_canvas(
&mut self,
camera: &Camera3D,
lights: &LightingUniforms,
settings: &RayTraceSettings,
) -> Result<(), JsValue> {
let mut pixels: Vec<u8> = mem::take(self.get_mut_pixels());
let traced: Result<(), String> = render_raytrace_rgba(
self.get_scene(),
camera,
lights,
settings,
self.get_width(),
self.get_height(),
&mut pixels,
);
let presented: Result<(), JsValue> = match self.get_mut_adapter().as_mut() {
Some(adapter) => match traced {
Ok(()) => adapter.present(&pixels),
Err(reason) => Err(backend_error(ERROR_FRAME_PACK, reason)),
},
None => Err(backend_error(ERROR_DISPOSED, ERROR_DISPOSED.to_string())),
};
*self.get_mut_pixels() = pixels;
presented
}
fn render_gpu(
&mut self,
camera: &Camera3D,
lights: &LightingUniforms,
settings: &RayTraceSettings,
) -> Result<(), JsValue> {
let mut frame: Vec<f32> = mem::take(self.get_mut_packed_frame());
let packed: Result<(), String> = pack_raytrace_frame(
&mut frame,
camera,
lights,
settings,
self.get_width(),
self.get_height(),
);
let drawn: Result<(), JsValue> = match (packed, self.get_mut_adapter().as_mut()) {
(Ok(()), Some(adapter)) => adapter.draw(&frame),
(Ok(()), None) => Err(backend_error(ERROR_DISPOSED, ERROR_DISPOSED.to_string())),
(Err(reason), _) => Err(backend_error(ERROR_FRAME_PACK, reason)),
};
*self.get_mut_packed_frame() = frame;
drawn
}
pub fn resize(&mut self, width: u32, height: u32) -> Result<(), JsValue> {
if self.get_disposed() {
return Err(backend_error(ERROR_DISPOSED, ERROR_DISPOSED.to_string()));
}
let byte_length: usize = match self.get_mut_adapter().as_mut() {
Some(adapter) => {
let limit: Option<u32> = adapter.dimension_limit();
let bytes: usize = validate_surface(width, height, limit)?;
adapter.resize(width, height)?;
bytes
}
None => {
validate_surface(width, height, None)?;
return Err(backend_error(ERROR_DISPOSED, ERROR_DISPOSED.to_string()));
}
};
self.set_width(width);
self.set_height(height);
self.get_mut_canvas().set_width(width);
self.get_mut_canvas().set_height(height);
resize_pixel_buffer(self.get_mut_pixels(), byte_length);
Ok(())
}
pub fn dispose(&mut self) {
if self.get_disposed() {
return;
}
self.set_disposed(true);
self.release_backend();
}
pub(crate) fn release_backend(&mut self) {
if let Some(mut adapter) = mem::take(self.get_mut_adapter()) {
adapter.release();
}
}
}
impl Drop for RayTraceRenderer {
fn drop(&mut self) {
if self.get_disposed() {
return;
}
self.set_disposed(true);
self.release_backend();
}
}