616 lines
22 KiB
Rust
616 lines
22 KiB
Rust
use crate::{
|
|
camera::Camera,
|
|
light::Light,
|
|
material::*,
|
|
node::*,
|
|
primitive::*,
|
|
scene::*,
|
|
state::{RaytracingOption, INIT_FILE, SAVE_FILE},
|
|
};
|
|
use imgui::*;
|
|
use nalgebra::{Point3, Vector3};
|
|
use pixels::{wgpu, PixelsContext};
|
|
use rhai::Engine;
|
|
use std::time::{Duration, Instant};
|
|
|
|
//BUFFER CONSTANTS
|
|
const BUFFER_PROPORTION_MIN: f32 = 0.1;
|
|
const BUFFER_PROPORTION_MAX: f32 = 1.0;
|
|
|
|
//RAY CONSTANTS
|
|
const MIN_THREADS: u32 = 1;
|
|
const MAX_THREADS: u32 = 12;
|
|
const RAYS_MIN: u32 = 100;
|
|
const RAYS_MAX: u32 = 10000;
|
|
const MIN_DEPTH: u8 = 1;
|
|
const MAX_DEPTH: u8 = 10;
|
|
const MIN_SAMPLES: u32 = 1;
|
|
const MAX_SAMPLES: u32 = 10;
|
|
const MIN_RANDOM: f64 = 100.0;
|
|
const MAX_RANDOM: f64 = 1000.0;
|
|
const MIN_EPSILON: f64 = 1e-11;
|
|
const MAX_EPSILON: f64 = 1.0;
|
|
|
|
//DIFFUSE CONSTANTS
|
|
const MIN_DIFFUSE_RAYS: u8 = 1;
|
|
const MAX_DIFFUSE_RAYS: u8 = 10;
|
|
const MIN_DIFFUSE_COEFFICIENT: f32 = 0.0;
|
|
const MAX_DIFFUSE_COEFFICIENT: f32 = 1.0;
|
|
|
|
//MATERIAL CONSTANTS
|
|
const MIN_SHINE: f32 = 0.0;
|
|
const MAX_SHINE: f32 = 50.0;
|
|
|
|
//TRANSFORMATION CONSTANTS
|
|
const MIN_FALLOFF: f32 = 0.0;
|
|
const MIN_SCALE: f64 = 0.0;
|
|
//const MIN_POSITION: f64 = -10.0;
|
|
const MIN_ROTATION: f64 = -180.0;
|
|
const MIN_TRANSLATE: f64 = -10.0;
|
|
//--
|
|
const MAX_FALLOFF: f32 = 1.0;
|
|
const MAX_SCALE: f64 = 3.0;
|
|
//const MAX_POSITION: f64 = 10.0;
|
|
const MAX_ROTATION: f64 = 180.0;
|
|
const MAX_TRANSLATE: f64 = 10.0;
|
|
|
|
// CAMERA CONSTANTS
|
|
const MIN_FOV: f64 = 10.0;
|
|
const MAX_FOV: f64 = 160.0;
|
|
//const CAMERA_INIT: f32 = 5.0;
|
|
|
|
/// Manages all state required for rendering Dear ImGui over `Pixels`test.
|
|
pub enum GuiEvent {
|
|
RaytracerOption(RaytracingOption),
|
|
CameraUpdate(Camera),
|
|
SceneLoad(Scene),
|
|
SaveImage(String),
|
|
}
|
|
pub struct Gui {
|
|
imgui: imgui::Context,
|
|
platform: imgui_winit_support::WinitPlatform,
|
|
renderer: imgui_wgpu::Renderer,
|
|
last_frame: Instant,
|
|
last_cursor: Option<imgui::MouseCursor>,
|
|
|
|
pub event: Option<GuiEvent>,
|
|
|
|
render_start: Option<Instant>,
|
|
render_elapsed: Option<Duration>,
|
|
|
|
script_filename: String,
|
|
script: String,
|
|
engine: Engine,
|
|
scene: Scene,
|
|
|
|
raytracing_option: RaytracingOption,
|
|
|
|
camera: Camera,
|
|
|
|
image_filename: String,
|
|
}
|
|
|
|
impl Gui {
|
|
/// Create Dear ImGui.
|
|
pub fn new(window: &winit::window::Window, pixels: &pixels::Pixels) -> Self {
|
|
// Create Dear ImGui context
|
|
let mut imgui = imgui::Context::create();
|
|
imgui.set_ini_filename(None);
|
|
|
|
// Initialize winit platform support
|
|
let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui);
|
|
platform.attach_window(
|
|
imgui.io_mut(),
|
|
window,
|
|
imgui_winit_support::HiDpiMode::Default,
|
|
);
|
|
|
|
// Configure Dear ImGui fonts
|
|
let hidpi_factor = window.scale_factor();
|
|
let font_size = (16.0 * hidpi_factor) as f32;
|
|
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
|
imgui
|
|
.fonts()
|
|
.add_font(&[imgui::FontSource::DefaultFontData {
|
|
config: Some(imgui::FontConfig {
|
|
oversample_h: 1,
|
|
pixel_snap_h: true,
|
|
size_pixels: font_size,
|
|
..Default::default()
|
|
}),
|
|
}]);
|
|
|
|
// Create Dear ImGui WGPU renderer
|
|
let device = pixels.device();
|
|
let queue = pixels.queue();
|
|
let config = imgui_wgpu::RendererConfig {
|
|
texture_format: pixels.render_texture_format(),
|
|
..Default::default()
|
|
};
|
|
let renderer = imgui_wgpu::Renderer::new(&mut imgui, device, queue, config);
|
|
|
|
// Return GUI context
|
|
let mut gui = Self {
|
|
imgui,
|
|
platform,
|
|
renderer,
|
|
last_frame: Instant::now(),
|
|
last_cursor: None,
|
|
event: None,
|
|
|
|
render_start: None,
|
|
render_elapsed: None,
|
|
|
|
script_filename: String::from(INIT_FILE),
|
|
script: String::new(),
|
|
engine: init_engine(),
|
|
scene: Scene::empty(),
|
|
|
|
raytracing_option: RaytracingOption::default(),
|
|
|
|
camera: Camera::unit(),
|
|
|
|
image_filename: String::from(SAVE_FILE),
|
|
};
|
|
|
|
// ------------ TESTING CODE (LOAD SCENE ON START) -----------------
|
|
match std::fs::read_to_string(&mut gui.script_filename) {
|
|
Ok(script) => {
|
|
gui.script = script;
|
|
}
|
|
Err(e) => println!("{}", e),
|
|
}
|
|
match gui.engine.eval(&gui.script) {
|
|
Ok(scene) => {
|
|
gui.scene = scene;
|
|
gui.event = Some(GuiEvent::SceneLoad(gui.scene.clone()));
|
|
}
|
|
Err(e) => println!("{e}"),
|
|
}
|
|
// ------------ TESTING CODE (LOAD SCENE ON START) -----------------
|
|
gui
|
|
}
|
|
|
|
pub fn start_render_timer(&mut self) {
|
|
self.render_start = Some(Instant::now());
|
|
self.render_elapsed = None;
|
|
}
|
|
|
|
pub fn stop_render_timer(&mut self) {
|
|
if let Some(start) = self.render_start.take() {
|
|
self.render_elapsed = Some(start.elapsed());
|
|
}
|
|
}
|
|
|
|
/// Prepare Dear ImGui.
|
|
pub fn prepare(
|
|
&mut self,
|
|
window: &winit::window::Window,
|
|
) -> Result<(), winit::error::ExternalError> {
|
|
let now = Instant::now();
|
|
self.imgui.io_mut().update_delta_time(now - self.last_frame);
|
|
self.last_frame = now;
|
|
self.platform.prepare_frame(self.imgui.io_mut(), window)
|
|
}
|
|
|
|
/// Render Dear ImGui.
|
|
pub fn render(
|
|
&mut self,
|
|
window: &winit::window::Window,
|
|
encoder: &mut wgpu::CommandEncoder,
|
|
render_target: &wgpu::TextureView,
|
|
context: &PixelsContext,
|
|
) -> imgui_wgpu::RendererResult<()> {
|
|
// Start a new Dear ImGui frame and update the cursor
|
|
let ui = self.imgui.new_frame();
|
|
|
|
let mouse_cursor = ui.mouse_cursor();
|
|
if self.last_cursor != mouse_cursor {
|
|
self.last_cursor = mouse_cursor;
|
|
self.platform.prepare_render(ui, window);
|
|
}
|
|
|
|
//Top Menu Bar
|
|
// let mut about_open = false;
|
|
// ui.main_menu_bar(|| {
|
|
// ui.menu("Help", || {
|
|
// about_open = ui.menu_item("About...");
|
|
// });
|
|
// });
|
|
|
|
//Raytracing options -------------------------------------------
|
|
if CollapsingHeader::new("Raytracer").build(ui) {
|
|
ui.slider(
|
|
"Threads",
|
|
MIN_THREADS,
|
|
MAX_THREADS,
|
|
&mut self.raytracing_option.threads,
|
|
);
|
|
// Numbers of rays to render per pass
|
|
Drag::new("Rays Per Pass")
|
|
.range(RAYS_MIN, RAYS_MAX)
|
|
.speed(50.0)
|
|
.build(ui, &mut self.raytracing_option.pixels_per_thread);
|
|
// Proportion of the window the buffer occupies
|
|
Drag::new("% Buffer: ")
|
|
.range(BUFFER_PROPORTION_MIN, BUFFER_PROPORTION_MAX)
|
|
.speed(0.005)
|
|
.display_format("%.2f")
|
|
.build(ui, &mut self.raytracing_option.buffer_proportion);
|
|
//Clear colour for scene
|
|
let mut clear_f32 = [
|
|
self.raytracing_option.clear_color[0] as f32 / 255.0,
|
|
self.raytracing_option.clear_color[1] as f32 / 255.0,
|
|
self.raytracing_option.clear_color[2] as f32 / 255.0,
|
|
self.raytracing_option.clear_color[3] as f32 / 255.0,
|
|
];
|
|
if ui.color_edit4_config("Clear Colour", &mut clear_f32).alpha_bar(true).build() {
|
|
self.raytracing_option.clear_color = [
|
|
(clear_f32[0] * 255.0) as u8, (clear_f32[1] * 255.0) as u8,
|
|
(clear_f32[2] * 255.0) as u8, (clear_f32[3] * 255.0) as u8,
|
|
];
|
|
}
|
|
//Clear colour if no intersect
|
|
let mut pixel_clear_f32 = [
|
|
self.raytracing_option.pixel_clear[0] as f32 / 255.0,
|
|
self.raytracing_option.pixel_clear[1] as f32 / 255.0,
|
|
self.raytracing_option.pixel_clear[2] as f32 / 255.0,
|
|
self.raytracing_option.pixel_clear[3] as f32 / 255.0,
|
|
];
|
|
if ui.color_edit4_config("Pixel Clear Colour", &mut pixel_clear_f32).alpha_bar(true).build() {
|
|
self.raytracing_option.pixel_clear = [
|
|
(pixel_clear_f32[0] * 255.0) as u8, (pixel_clear_f32[1] * 255.0) as u8,
|
|
(pixel_clear_f32[2] * 255.0) as u8, (pixel_clear_f32[3] * 255.0) as u8,
|
|
];
|
|
}
|
|
//Ray depth slider
|
|
ui.slider(
|
|
"Ray Depth",
|
|
MIN_DEPTH,
|
|
MAX_DEPTH,
|
|
&mut self.raytracing_option.ray_depth,
|
|
);
|
|
//Ray samples slider
|
|
ui.slider(
|
|
"Ray Samples",
|
|
MIN_SAMPLES,
|
|
MAX_SAMPLES,
|
|
&mut self.raytracing_option.ray_samples,
|
|
);
|
|
//Ray randomness
|
|
Drag::new("Ray Randomness")
|
|
.range(MIN_RANDOM, MAX_RANDOM)
|
|
.speed(5.0)
|
|
.display_format("%.1f")
|
|
.build(ui, &mut self.raytracing_option.ray_randomness);
|
|
//Number of diffuse rays
|
|
ui.slider(
|
|
"Diffuse Rays",
|
|
MIN_DIFFUSE_RAYS,
|
|
MAX_DIFFUSE_RAYS,
|
|
&mut self.raytracing_option.diffuse_rays,
|
|
);
|
|
//Diffuse Coefficient
|
|
Drag::new("Diffuse Coefficient")
|
|
.range(MIN_DIFFUSE_COEFFICIENT, MAX_DIFFUSE_COEFFICIENT)
|
|
.speed(0.005)
|
|
.display_format("%.3f")
|
|
.build(ui, &mut self.raytracing_option.diffuse_coefficient);
|
|
// Fov of the buffer
|
|
ui.slider(
|
|
"fov",
|
|
MIN_FOV,
|
|
MAX_FOV,
|
|
&mut self.raytracing_option.buffer_fov,
|
|
);
|
|
// Enable BVH
|
|
ui.checkbox("Enable BVH", &mut self.raytracing_option.bvh_active);
|
|
ui.checkbox("Enable Shadows", &mut self.raytracing_option.shadows);
|
|
ui.checkbox("Enable Reflections", &mut self.raytracing_option.reflect);
|
|
ui.checkbox("Enable Specular", &mut self.raytracing_option.specular);
|
|
ui.checkbox("Enable Diffuse", &mut self.raytracing_option.diffuse);
|
|
// Render timer display
|
|
ui.separator();
|
|
if let Some(start) = &self.render_start {
|
|
let elapsed = start.elapsed().as_secs_f64();
|
|
ui.text(format!("Rendering: {:.2}s", elapsed));
|
|
} else if let Some(elapsed) = &self.render_elapsed {
|
|
ui.text(format!("Render time: {:.2}s", elapsed.as_secs_f64()));
|
|
}
|
|
ui.separator();
|
|
// Apply stored changes
|
|
if ui.button("Apply") {
|
|
self.event = Some(GuiEvent::RaytracerOption(self.raytracing_option.clone()));
|
|
};
|
|
}
|
|
// CAMERA OPTIONS ----------------------------------------
|
|
if CollapsingHeader::new("Camera").build(ui) {
|
|
// Eye, target and up vector inputs
|
|
ui.text("Camera options:");
|
|
Drag::new("Eye")
|
|
.range(MIN_TRANSLATE, MAX_TRANSLATE)
|
|
.speed(0.05)
|
|
.display_format("%.2f")
|
|
.build_array(ui, self.camera.eye.coords.as_mut_slice());
|
|
Drag::new("Target")
|
|
.range(MIN_TRANSLATE, MAX_TRANSLATE)
|
|
.speed(0.05)
|
|
.display_format("%.2f")
|
|
.build_array(ui, self.camera.target.coords.as_mut_slice());
|
|
Drag::new("Up")
|
|
.range(0.0, 1.0)
|
|
.speed(0.005)
|
|
.display_format("%.3f")
|
|
.build_array(ui, self.camera.up.as_mut_slice());
|
|
if ui.button("Apply Camera") {
|
|
println!("Camera changed");
|
|
self.event = Some(GuiEvent::CameraUpdate(self.camera.clone()));
|
|
}
|
|
}
|
|
// SCRIPTING --------------------------------------------
|
|
if CollapsingHeader::new("Scripting").build(ui) {
|
|
// Import file into multiline script
|
|
ui.input_text("Scene file", &mut self.script_filename)
|
|
.build();
|
|
if ui.button("Import from File") {
|
|
match std::fs::read_to_string(&mut self.script_filename) {
|
|
Ok(script) => {
|
|
self.script = script;
|
|
}
|
|
Err(e) => println!("{}", e),
|
|
}
|
|
}
|
|
ui.same_line();
|
|
// Load scene from multiline script using engine
|
|
if ui.button("Load scene") {
|
|
match self.engine.eval(&self.script) {
|
|
Ok(scene) => {
|
|
self.scene = scene;
|
|
self.event = Some(GuiEvent::SceneLoad(self.scene.clone()));
|
|
}
|
|
Err(e) => println!("{e}"),
|
|
}
|
|
}
|
|
ui.same_line();
|
|
// Save script to file
|
|
if ui.button("Save script") {
|
|
match std::fs::write(&self.script_filename, &self.script) {
|
|
Ok(_) => println!("Script saved successfully"),
|
|
Err(e) => println!("{}", e),
|
|
}
|
|
}
|
|
// Multiline script
|
|
ui.input_text_multiline("##", &mut self.script, [900., 300.])
|
|
.build();
|
|
}
|
|
// IMAGE --------------------------------------------
|
|
if CollapsingHeader::new("Image").build(ui) {
|
|
// Image filename
|
|
ui.input_text("Image file", &mut self.image_filename)
|
|
.build();
|
|
// Save image to file
|
|
if ui.button("Save Image") {
|
|
self.event = Some(GuiEvent::SaveImage(self.image_filename.clone()));
|
|
}
|
|
}
|
|
// SCENE --------------------------------------------
|
|
if CollapsingHeader::new("Scene").build(ui) {
|
|
if ui.button("Update Scene") {
|
|
self.scene.compute();
|
|
self.event = Some(GuiEvent::SceneLoad(self.scene.clone()));
|
|
}
|
|
// Edit transformation of nodes
|
|
if let Some(_t) = ui.tree_node("Nodes") {
|
|
for (label, node) in &mut self.scene.nodes {
|
|
ui.checkbox(format!("##active{label}"), &mut node.active);
|
|
ui.same_line();
|
|
if let Some(_t) = ui.tree_node(label) {
|
|
Drag::new("Translation")
|
|
.range(MIN_TRANSLATE, MAX_TRANSLATE)
|
|
.speed(0.05)
|
|
.display_format("%.2f")
|
|
.build_array(ui, &mut node.translation);
|
|
Drag::new("Rotation")
|
|
.range(MIN_ROTATION, MAX_ROTATION)
|
|
.speed(1.0)
|
|
.display_format("%.1f")
|
|
.build_array(ui, &mut node.rotation);
|
|
Drag::new("Scale")
|
|
.range(MIN_SCALE, MAX_SCALE)
|
|
.speed(0.01)
|
|
.display_format("%.3f")
|
|
.build_array(ui, &mut node.scale);
|
|
}
|
|
}
|
|
}
|
|
// Edit materials
|
|
if let Some(_t) = ui.tree_node("Materials") {
|
|
for (label, material) in &mut self.scene.materials {
|
|
if let Some(_t) = ui.tree_node(label) {
|
|
let mut ks_arr: [f32; 3] = material.ks.into();
|
|
if ui.color_edit3("ks", &mut ks_arr) {
|
|
material.ks = Vector3::from(ks_arr);
|
|
}
|
|
let mut kd_arr: [f32; 3] = material.kd.into();
|
|
if ui.color_edit3("kd", &mut kd_arr) {
|
|
material.kd = Vector3::from(kd_arr);
|
|
}
|
|
Drag::new("shine")
|
|
.range(MIN_SHINE, MAX_SHINE)
|
|
.speed(0.5)
|
|
.display_format("%.1f")
|
|
.build(ui, &mut material.shininess);
|
|
}
|
|
}
|
|
}
|
|
//Edit color, position and falloff of lights
|
|
if let Some(_t) = ui.tree_node("Lights") {
|
|
for (label, light) in &mut self.scene.lights {
|
|
ui.checkbox(format!("##activelight{label}"), &mut light.active);
|
|
ui.same_line();
|
|
if let Some(_t) = ui.tree_node(label) {
|
|
let mut colour_arr: [f32; 3] = light.colour.into();
|
|
if ui.color_edit3("Colour", &mut colour_arr) {
|
|
light.colour = Vector3::from(colour_arr);
|
|
}
|
|
Drag::new("Position")
|
|
.range(MIN_TRANSLATE, MAX_TRANSLATE)
|
|
.speed(0.05)
|
|
.display_format("%.2f")
|
|
.build_array(ui, light.position.coords.as_mut_slice());
|
|
Drag::new("Falloff")
|
|
.range(MIN_FALLOFF, MAX_FALLOFF)
|
|
.speed(0.005)
|
|
.display_format("%.3f")
|
|
.build_array(ui, light.falloff.as_mut_slice());
|
|
}
|
|
}
|
|
}
|
|
//Use different cameras in the scene
|
|
if let Some(_t) = ui.tree_node("Cameras") {
|
|
for (label, camera) in &self.scene.cameras {
|
|
if ui.button(label) {
|
|
self.camera = camera.clone();
|
|
self.event = Some(GuiEvent::CameraUpdate(camera.clone()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Render Dear ImGui with WGPU
|
|
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
label: Some("imgui"),
|
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
view: render_target,
|
|
resolve_target: None,
|
|
ops: wgpu::Operations {
|
|
load: wgpu::LoadOp::Load,
|
|
store: true,
|
|
},
|
|
})],
|
|
depth_stencil_attachment: None,
|
|
});
|
|
|
|
self.renderer.render(
|
|
self.imgui.render(),
|
|
&context.queue,
|
|
&context.device,
|
|
&mut rpass,
|
|
)
|
|
}
|
|
|
|
/// Update the GUI's camera to reflect external changes (e.g. from keyboard/mouse movement)
|
|
pub fn update_camera(&mut self, camera: &Camera) {
|
|
self.camera = camera.clone();
|
|
}
|
|
|
|
/// Handle any outstanding events.
|
|
pub fn handle_event(
|
|
&mut self,
|
|
window: &winit::window::Window,
|
|
event: &winit::event::Event<()>,
|
|
) {
|
|
self.platform
|
|
.handle_event(self.imgui.io_mut(), window, event);
|
|
}
|
|
}
|
|
|
|
pub fn init_engine() -> Engine {
|
|
let mut engine = Engine::new();
|
|
|
|
engine
|
|
.register_type::<Vector3<f64>>()
|
|
.register_fn("V", Vector3::<f64>::new);
|
|
engine
|
|
.register_type::<Point3<f64>>()
|
|
.register_fn("P", Point3::<f64>::new);
|
|
engine
|
|
.register_type::<Camera>()
|
|
.register_fn("Camera", Camera::new);
|
|
engine
|
|
.register_type::<Scene>()
|
|
.register_fn("Scene", Scene::empty)
|
|
.register_fn("addNode", Scene::add_node)
|
|
.register_fn("addLight", Scene::add_light)
|
|
.register_fn("addCamera", Scene::add_camera)
|
|
.register_fn("addMaterial", Scene::add_material);
|
|
|
|
engine
|
|
.register_type::<Node>()
|
|
.register_fn("Node", Node::new)
|
|
.register_fn("translate", Node::translate)
|
|
.register_fn("rotate", Node::rotate)
|
|
.register_fn("scale", Node::scale)
|
|
.register_fn("child", Node::child)
|
|
.register_fn("active", Node::set_active);
|
|
engine
|
|
.register_type::<Light>()
|
|
.register_fn("Light", Light::new)
|
|
.register_fn("Ambient", Light::ambient)
|
|
.register_fn("active", Light::set_active);
|
|
engine
|
|
.register_type::<Material>()
|
|
.register_fn("Material", Material::new)
|
|
.register_fn("MaterialRed", Material::red)
|
|
.register_fn("MaterialBlue", Material::blue)
|
|
.register_fn("MaterialGreen", Material::green)
|
|
.register_fn("MaterialMagenta", Material::magenta)
|
|
.register_fn("MaterialTurquoise", Material::turquoise);
|
|
engine
|
|
.register_type::<Sphere>()
|
|
.register_fn("Sphere", Sphere::new)
|
|
.register_fn("SphereUnit", Sphere::unit);
|
|
engine
|
|
.register_type::<Cube>()
|
|
.register_fn("Cube", Cube::new)
|
|
.register_fn("CubeUnit", Cube::unit);
|
|
engine
|
|
.register_type::<Triangle>()
|
|
.register_fn("Triangle", Triangle::new)
|
|
.register_fn("TriangleUnit", Triangle::unit);
|
|
engine
|
|
.register_type::<Cone>()
|
|
.register_fn("Cone", Cone::new)
|
|
.register_fn("ConeUnit", Cone::unit);
|
|
engine
|
|
.register_type::<Cylinder>()
|
|
.register_fn("Cylinder", Cylinder::new);
|
|
engine
|
|
.register_type::<Circle>()
|
|
.register_fn("Circle", Circle::new)
|
|
.register_fn("CircleUnit", Circle::unit);
|
|
engine
|
|
.register_type::<Cube>()
|
|
.register_fn("Cube", Cube::new)
|
|
.register_fn("CubeUnit", Cube::unit);
|
|
engine
|
|
.register_type::<Steiner>()
|
|
.register_fn("Steiner", Steiner::new);
|
|
engine
|
|
.register_type::<Steiner2>()
|
|
.register_fn("Steiner2", Steiner2::new);
|
|
engine
|
|
.register_type::<Roman>()
|
|
.register_fn("Roman", Roman::new);
|
|
engine
|
|
.register_type::<CrossCap>()
|
|
.register_fn("CrossCap", CrossCap::new);
|
|
engine
|
|
.register_type::<CrossCap2>()
|
|
.register_fn("CrossCap2", CrossCap2::new);
|
|
engine
|
|
.register_type::<Torus>()
|
|
.register_fn("Torus", Torus::new);
|
|
engine
|
|
.register_type::<Gnonom>()
|
|
.register_fn("Gnonom", Gnonom::new);
|
|
engine
|
|
.register_type::<Mesh>()
|
|
.register_fn("Mesh", Mesh::from_file);
|
|
engine
|
|
.register_type::<RectangleXY>()
|
|
.register_fn("Rectange", RectangleXY::new)
|
|
.register_fn("RectangleUnit", RectangleXY::unit);
|
|
engine
|
|
}
|