Expand description
Gemini’s implementation of 3D rendering. Capable of rendering full 3D meshes as wireframes, solid colours or with lighting
§Example
Let’s write a simple example program to draw a spinning cube. This example is available in examples/spinning-cube.rs
//! An example of a spinning cube with `view3d`
use gemini_engine::{
core::ColChar,
fps_gameloop,
mesh3d::{Mesh3D, Transform3D, Vec3D},
view::View,
view3d::{DisplayMode, Light, Viewport},
};
use std::time::Duration;
const FPS: f32 = 30.0;
const FOV: f64 = 80.0;
fn main() {
let mut view = View::new(100, 50, ColChar::EMPTY);
let mut viewport = Viewport::new(
Transform3D::look_at_lh(Vec3D::new(0.0, -1.5, 4.3), Vec3D::ZERO, Vec3D::Y),
FOV,
view.center(),
);
viewport.objects.push(Mesh3D::default_cube());
viewport.display_mode = DisplayMode::Illuminated {
lights: vec![
Light::new_ambient(0.3),
Light::new_directional(0.6, Vec3D::new(0.5, 1.0, 1.0)),
],
};
fps_gameloop!(
{
viewport.objects[0].transform = viewport.objects[0]
.transform
.mul_mat4(&Transform3D::from_rotation_y(-0.05));
},
{
view.clear();
view.draw(&viewport);
let _ = view.display_render();
},
FPS,
|elapsed: Duration, frame_skip| {
println!(
"Elapsed: {:.2?}µs | Frame skip: {}",
elapsed.as_micros(),
frame_skip
);
}
);
}
There is a lot of code here, but here we’ll only focus on the parts that are different from the gameloop
example:
§Initialisation
let mut view = View::new(100, 50, ColChar::EMPTY);
let mut viewport = Viewport::new(
Transform3D::look_at_lh(Vec3D::new(0.0, -1.5, 4.3), Vec3D::ZERO, Vec3D::Y),
FOV,
view.center(),
);
viewport.objects.push(Mesh3D::default_cube());
viewport.display_mode = DisplayMode::Illuminated {
lights: vec![
Light::new_ambient(0.3),
Light::new_directional(0.6, Vec3D::new(0.5, 1.0, 1.0)),
],
};
main()
begins with the creation of all the necessary objects to render 3D images:
View
to handle the canvas and printing to the screenViewport
to handle drawing 3d objects to the canvas- The actual objects you intend to use in the scene, as
Mesh3D
In this scenario, we create a View
of width 100 and height 50 (you may have to zoom out and expand your terminal to fit the whole image), a Viewport
with a camera positioned at (0,-1.5,4.3) pointing at (0,0,0), our desired FOV and origin point (the centre of the view we’re printing to) in the middle of the View
. We add a single default cube, which is 2 units tall, wide and long to the Viewport
s list of objects, and set the Viewport
’s display_mode
to DisplayMode::Illuminated
with a simple lighting setup
§Gameloop process logic
viewport.objects[0].transform = viewport.objects[0]
.transform
.mul_mat4(&Transform3D::from_rotation_y(-0.05));
This part of the code is where we would put all our physics, collisions, events etc. code, but in this case the only thing we do is rotate the cube 0.05 radians anticlockwise in the Y axis.
§Drawing/Rendering
view.clear();
view.draw(&viewport);
let _ = view.display_render();
This part of the code renders and draws all the 3d stuff to the View
before rendering with display_render
as usual. Viewport
implements CanDraw
, so when it is draw to the View
, it fully renders our scene based on its stored transform, display mode, objects, etc.
Structs§
- Light
- A light object used to define a scene’s lighting. Used by
DisplayMode::Illuminated
- Viewport
- The
Viewport
handles drawing 3D objects to a 2DCanvas
, and also acts as the scene’s camera.
Enums§
- Display
Mode DisplayMode
determines how theViewport
renders our 3D objects. This is the Gemini equivalent of Blender’s Viewport Shading options- Light
Type - Type of light, determines how the normal of a face affects its illumination
Constants§
- BRIGHTNESS_
CHARS - Characters for brightness. The first character is the darkest and the last character is the brightest