Skip to content

Commit 683fe2e

Browse files
authored
Merge pull request #12 from ecumene/webify
Webify's pretty "stable"
2 parents 84f8502 + 54f7cce commit 683fe2e

File tree

10 files changed

+2490
-157
lines changed

10 files changed

+2490
-157
lines changed

README.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,36 @@
1-
# Sloth
2-
![](models/demo/pikachu.gif)
3-
A one-of-a-kind command line 3D software rasterizer made with crossterm, tobj, stl, and nalgebra. Currently it
4-
supports OBJ file formats without textures or materials, or standard STL files. Here's a really simple command for you to get started.
5-
`cargo build --release`
6-
`./target/release/sloth models/Pikachu.obj`
7-
For multiple models:
8-
`./target/release/sloth "models/Pikachu.obj models/suzy.obj"`
1+
# sloth - A one-of-a-kind Rust 3D Renderer for the CLI
2+
![pikachu](models/demo/pikachu.gif)
3+
4+
A one-of-a-kind command line 3D software rasterizer made with termion, tobj, and nalgebra. Currently it
5+
supports OBJ file formats without textures.
6+
7+
[Javascript Export Demonstration](http://ecumene.xyz/sloth-demo)
8+
9+
## Getting Started / Uses
10+
---
11+
Here's a few really simple commands for you to get started.
12+
13+
You can replace `sloth <args>` with `cargo run --release -- <args>` anywhere
14+
15+
#### Render pikachu
16+
```
17+
sloth models/Pikachu.obj
18+
```
19+
#### For multiple models:
20+
```
21+
sloth "models/suzy.obj models/suzy.obj"
22+
```
23+
#### You can also generate a static image:
24+
```
25+
sloth models/Pikachu.obj image -w <width_in_pixels> -h <height_in_pixels>
26+
```
27+
#### You can also generate a portable Javascript render like this:
28+
```
29+
sloth models/Pikachu.obj image -j <number_of_frames> -w <width_in_pixels> -h <height_in_pixels> > src-webify/data.js
30+
```
31+
32+
Thank you, contributors!
33+
---
34+
[Maxgy](https://github.com/Maxgy) – Rustfmt lint
35+
[donbright](https://github.com/donbright) – STL model loading added, Rustfmt lint
36+
[jonathandturner](https://github.com/jonathandturner) – Crossterm port

src-webify/data.js

Lines changed: 2101 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-webify/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<html>
2+
<head>
3+
<script src="data.js"> </script>
4+
<script src="render.js"> </script>
5+
</head>
6+
7+
<body>
8+
<p>Made by Mitchell [email protected]</p>
9+
<p id="sloth-frame" style="white-space: pre; font-family: Courier, Monaco, monospace;"> </p>
10+
</body>
11+
</html>

src-webify/render.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// requestAnimationFrame() shim by Paul Irish
2+
window.requestAnimFrame = (function() {
3+
return window.requestAnimationFrame ||
4+
window.webkitRequestAnimationFrame ||
5+
window.mozRequestAnimationFrame ||
6+
window.oRequestAnimationFrame ||
7+
window.msRequestAnimationFrame ||
8+
function(/* function */ callback, /* DOMElement */ element){
9+
window.setTimeout(callback, 1000 / 60);
10+
};
11+
})();
12+
13+
/**
14+
* Behaves the same as setInterval except uses requestAnimationFrame() where possible for better performance
15+
* @param {function} fn The callback function
16+
* @param {int} delay The delay in milliseconds
17+
*/
18+
window.requestInterval = function(fn, delay) {
19+
if( !window.requestAnimationFrame &&
20+
!window.webkitRequestAnimationFrame &&
21+
!(window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame) && // Firefox 5 ships without cancel support
22+
!window.oRequestAnimationFrame &&
23+
!window.msRequestAnimationFrame )
24+
return window.setInterval(fn, delay);
25+
26+
var start = new Date().getTime(),
27+
handle = new Object();
28+
29+
function loop() {
30+
var current = new Date().getTime(),
31+
delta = current - start;
32+
33+
if(delta >= delay) {
34+
fn.call();
35+
start = new Date().getTime();
36+
}
37+
38+
handle.value = requestAnimFrame(loop);
39+
};
40+
41+
handle.value = requestAnimFrame(loop);
42+
return handle;
43+
}
44+
45+
let x = 0;
46+
function render() {
47+
x+=1
48+
document.getElementById("sloth-frame").innerHTML = frames[x%frames.length]
49+
}
50+
51+
window.requestInterval(render, 200)

src/base/mod.rs

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/base/context.rs renamed to src/context.rs

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::base::SimpleMesh;
1+
use crate::geometry::SimpleMesh;
22
use crossterm::{cursor, terminal, Attribute, Color, Colored};
33
use nalgebra::Matrix4;
44
use std::error::Error;
@@ -10,10 +10,11 @@ pub struct Context {
1010
pub height: usize,
1111
pub frame_buffer: Vec<(char, (u8, u8, u8))>,
1212
pub z_buffer: Vec<f32>,
13+
pub image: bool
1314
}
1415

1516
impl Context {
16-
pub fn blank() -> Context {
17+
pub fn blank(image: bool) -> Context {
1718
//TODO: Make this a constant struct
1819
Context {
1920
utransform: Matrix4::new(
@@ -23,49 +24,73 @@ impl Context {
2324
height: 0,
2425
frame_buffer: vec![],
2526
z_buffer: vec![],
27+
image: image
2628
}
2729
}
2830
pub fn clear(&mut self) {
29-
self.frame_buffer = vec![(' ', (0, 0, 0)); self.width * self.height as usize];
31+
self.frame_buffer = vec![(' ', (0, 0, 0));
32+
if self.image {
33+
self.width * self.height + self.height
34+
} else {
35+
self.width * self.height
36+
} as usize];
3037
self.z_buffer = vec![f32::MAX; self.width * self.height as usize]; //f32::MAX is written to the z-buffer as an infinite back-wall to render with
3138
}
3239
pub fn camera(&mut self, proj: Matrix4<f32>, view: Matrix4<f32>) -> &Matrix4<f32> {
3340
self.utransform = proj * view;
3441
&self.utransform
3542
}
36-
pub fn flush(&self) -> Result<(), Box<Error>> {
37-
let cursor = cursor();
38-
cursor.goto(0, 0)?;
39-
43+
pub fn flush(&self, color: bool, webify: bool) -> Result<(), Box<Error>> {
4044
let mut prev_color = None;
4145

42-
for pixel in &self.frame_buffer {
43-
match prev_color {
44-
Some(c) if c == pixel.1 => {
45-
print!("{}", pixel.0);
46+
if !self.image {
47+
cursor().goto(0, 0)?;
48+
}
49+
50+
if color {
51+
for pixel in &self.frame_buffer {
52+
match prev_color {
53+
Some(c) if c == pixel.1 => {
54+
print!("{}", pixel.0);
55+
}
56+
_ => {
57+
prev_color = Some(pixel.1);
58+
if webify {
59+
print!(
60+
"<span style=\"color:rgb({},{},{})\">{}",
61+
(pixel.1).0, (pixel.1).1, (pixel.1).2, pixel.0
62+
)
63+
} else {
64+
print!(
65+
"{}{}{}",
66+
Colored::Fg(Color::Rgb {
67+
r: (pixel.1).0,
68+
g: (pixel.1).1,
69+
b: (pixel.1).2
70+
}),
71+
Colored::Bg(Color::Rgb {
72+
r: 25,
73+
g: 25,
74+
b: 25
75+
}),
76+
pixel.0
77+
)
78+
}
79+
}
4680
}
47-
_ => {
48-
prev_color = Some(pixel.1);
49-
print!(
50-
"{}{}{}",
51-
Colored::Fg(Color::Rgb {
52-
r: (pixel.1).0,
53-
g: (pixel.1).1,
54-
b: (pixel.1).2
55-
}),
56-
Colored::Bg(Color::Rgb {
57-
r: 25,
58-
g: 25,
59-
b: 25
60-
}),
61-
pixel.0
62-
)
81+
82+
if !self.image {
83+
println!("{}", Attribute::Reset);
6384
}
6485
}
86+
} else {
87+
let mut frame = String::from("");
88+
for pixel in &self.frame_buffer {
89+
frame.push(pixel.0);
90+
}
91+
println!("{}", frame)
6592
}
6693

67-
println!("{}", Attribute::Reset);
68-
6994
Ok(())
7095
}
7196
pub fn update(
@@ -74,14 +99,13 @@ impl Context {
7499
meshes: &[SimpleMesh],
75100
) -> Result<(), Box<Error>> {
76101
let terminal = terminal();
77-
let terminal_size = terminal.terminal_size();
102+
let terminal_size = if self.image {
103+
(self.width as u16, self.height as u16)
104+
} else {
105+
terminal.terminal_size()
106+
};
78107

79108
if old_size != terminal_size {
80-
// Check if the size changed
81-
let cursor = cursor();
82-
83-
//re-hide the cursor
84-
cursor.hide()?;
85109
old_size = terminal_size; // It changed! Set new size
86110
old_size.0 += 1;
87111
let mut scale: f32 = 0.0; // The scene's scale
@@ -112,8 +136,10 @@ impl Context {
112136
1.0,
113137
);
114138
self.utransform = t;
115-
self.width = old_size.0 as usize;
116-
self.height = (old_size.1) as usize;
139+
if !self.image {
140+
self.width = old_size.0 as usize;
141+
self.height = (old_size.1) as usize;
142+
}
117143
}
118144

119145
Ok(())
File renamed without changes.

0 commit comments

Comments
 (0)