summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fshader_main.glsl2
-rw-r--r--src/main.rs110
-rw-r--r--src/sdf.rs1
-rw-r--r--src/sdl.rs10
-rw-r--r--src/vshader_post.glsl2
-rw-r--r--src/win.rs43
6 files changed, 117 insertions, 51 deletions
diff --git a/src/fshader_main.glsl b/src/fshader_main.glsl
index 0f13a01..2a6864a 100644
--- a/src/fshader_main.glsl
+++ b/src/fshader_main.glsl
@@ -20,6 +20,7 @@ uniform int u_flash_icon;
#define ICON_PLAY 2
#define ICON_PAUSE 3
#define ICON_REWIND 4
+#define ICON_SCREENSHOT 5
%COMMON%
%SDF%
@@ -131,6 +132,7 @@ void main() {
switch (u_flash_icon) {
case 0: break;
case ICON_COPY:
+ case ICON_SCREENSHOT:
icon = abs(pos.x) > u_aspect_ratio - 0.1 || abs(pos.y) > 0.9;
break;
case ICON_PLAY:
diff --git a/src/main.rs b/src/main.rs
index 2651c9b..782ef32 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,11 +1,9 @@
/*
@TODO:
-- screenshot
- pause screen
-- start time at -3 seconds
- autoplay setting
- strip ' ' and '\n' from *inside* string
-- flash error on bad string
+- flash error on bad string (see @TODO(error handling))
- RnToRn functions (& add back in RToR)
- also add PerComponent(Box<RToR>,Box<RToR>,Box<RToR>) in R3ToR3
- ProjectX, ProjectY, ProjectZ in R3ToR?
@@ -20,18 +18,21 @@
*/
#![windows_subsystem = "windows"]
+extern crate chrono;
extern crate nalgebra;
+extern crate png;
pub mod sdf;
mod sdl;
pub mod win;
+use chrono::prelude::*;
use nalgebra::{Matrix3, Matrix4, Rotation3, Vector3};
use sdf::ImportExport;
use std::{
collections::HashMap,
fs::File,
- io::{prelude::*, BufReader},
+ io::{prelude::*, BufReader, BufWriter},
time::Instant,
};
use win::{ColorF32, ColorGrayscaleF32, ColorU8};
@@ -49,6 +50,7 @@ enum Icon {
Play = 2,
Pause = 3,
Rewind = 4,
+ Screenshot = 5,
}
#[derive(Clone)]
@@ -160,7 +162,7 @@ impl Programs {
.link_program(&mut self.test, vsource_test, &fsource_test)
.map_err(|e| format!("Error compiling shader:\n{e}"))?;
window
- .link_program(&mut self.post, vsource_post, &fsource_post)
+ .link_program(&mut self.post, vsource_post, fsource_post)
.map_err(|e| format!("Error compiling shader:\n{e}"))?;
Ok(())
}
@@ -275,15 +277,14 @@ impl State {
win::FramebufferAttachment::Color0,
&test_framebuffer_texture,
);
-
-
+
let main_texconfig = win::TextureParams {
mag_filter: win::TextureFilter::Nearest,
..Default::default()
};
let main_framebuffer_texture = window.create_texture(&main_texconfig);
let main_framebuffer = window.create_framebuffer();
-
+
let mut main_buffer = window.create_buffer();
let mut test_buffer = window.create_buffer();
let mut post_buffer = window.create_buffer();
@@ -368,8 +369,8 @@ impl State {
self.window.bind_framebuffer(None);
let mut sdf_values: Vec<f32> = self
- .window
- .get_texture_data_vec::<ColorGrayscaleF32>(&self.test_framebuffer_texture)
+ .test_framebuffer_texture
+ .get_data_vec::<ColorGrayscaleF32>()
.iter()
.map(|c| c.value)
.collect();
@@ -394,20 +395,64 @@ impl State {
fn flash(&mut self, icon: Icon) {
self.flash = match icon {
Icon::None => ColorF32::BLACK,
- Icon::Copy => ColorF32::GREEN,
+ Icon::Copy | Icon::Screenshot => ColorF32::GREEN,
_ => ColorF32::rgb(1.0, 0.5, 0.0),
};
self.flash_icon = icon;
}
-
+
fn render_resolution(&self) -> (i32, i32) {
let scale = self.settings.get_f32("scale").unwrap_or(1.0);
- if scale <= 0.0 || scale > 1.0 {
+ if scale <= 0.0 || scale > 100.0 {
win::display_error_message(&format!("bad scale: {scale}"));
std::process::exit(1);
}
let (w, h) = self.window.size();
- ((w as f32 * scale) as i32, (h as f32 * scale) as i32)
+ let w = (w as f32 * scale) as i32;
+ let h = (h as f32 * scale) as i32;
+ assert!(w >= 0);
+ assert!(h >= 0);
+ (w, h)
+ }
+
+ /// save a screenshot
+ fn take_screenshot(&mut self) -> Result<(), String> {
+ let texture = &self.main_framebuffer_texture;
+ let size = (texture.width(), texture.height());
+ let texture_data = texture.get_data_vec();
+ if size.0 == 0 || size.1 == 0 {
+ // there isnt anything to save . why did you set the scale so small...
+ return Ok(());
+ }
+ let time = Utc::now();
+ let filename = time
+ .format("screenshots/autosdf-%Y-%m-%d-%H-%M-%S.png")
+ .to_string();
+ if std::fs::create_dir("screenshots").is_err() {
+ // (do nothing.)
+ // we get an error if it already exists.
+ // even if this is another error, that will just make File::create fail.
+ }
+ let file =
+ File::create(&filename).map_err(|e| format!("error creating {filename}: {e}"))?;
+ let mut writer = BufWriter::new(file);
+ let mut encoder = png::Encoder::new(&mut writer, size.0 as u32, size.1 as u32);
+ encoder.set_color(png::ColorType::Rgba);
+ encoder.set_depth(png::BitDepth::Eight);
+ encoder
+ .add_text_chunk(
+ "\n\n\n\nAutoSDF scene".to_string(),
+ "\n".to_string() + &self.scene.export_string() + "\n\n\n\n",
+ )
+ .map_err(|e| format!("error adding PNG tEXt chunk for {filename}: {e}"))?;
+ let mut png_writer = encoder
+ .write_header()
+ .map_err(|e| format!("error writing PNG header for {filename}: {e}"))?;
+ png_writer
+ .write_image_data(ColorU8::slice_to_bytes(&texture_data))
+ .map_err(|e| format!("error writing {filename}: {e}"))?;
+ self.flash(Icon::Screenshot);
+ Ok(())
}
// returns false if we should quit
@@ -441,6 +486,7 @@ impl State {
match self.window.set_clipboard_text(&self.scene.export_string()) {
Ok(()) => {}
Err(e) => {
+ // @TODO(error handling)
eprintln!("couldn't copy text to clipboard: {e}")
}
}
@@ -460,16 +506,27 @@ impl State {
self.load_scene(new_scene);
}
None => {
+ // @TODO(error handling)
eprintln!("bad string")
}
},
Err(e) => {
- // very unlikely to happen
+ // @TODO(error handling)
eprintln!("couldn't get clipboard text: {e}")
}
}
}
KeyDown { key: N0, .. } => self.view = self.initial_view.clone(),
+ KeyDown { key: F10, .. } => {
+ // screenshot
+ match self.take_screenshot() {
+ Ok(()) => {}
+ Err(e) => {
+ // @TODO(error handling)
+ eprintln!("screenshot fail: {e}");
+ }
+ }
+ }
KeyDown {
key: Space,
modifier,
@@ -564,20 +621,24 @@ impl State {
let render_resolution = self.render_resolution();
if render_resolution != self.main_framebuffer_size {
- let result = self.main_framebuffer_texture.set_data::<ColorU8>(None, render_resolution.0 as usize, render_resolution.1 as usize);
-
+ let result = self.main_framebuffer_texture.set_data::<ColorU8>(
+ None,
+ render_resolution.0 as usize,
+ render_resolution.1 as usize,
+ );
+
match result {
- Ok(()) => {},
+ Ok(()) => {}
Err(e) => eprintln!("warning:{e}"),
}
-
+
self.main_framebuffer.set_texture(
win::FramebufferAttachment::Color0,
&self.main_framebuffer_texture,
);
self.main_framebuffer_size = render_resolution;
}
-
+
let window = &mut self.window;
let view = &self.view;
window.viewport(0, 0, render_resolution.0, render_resolution.1);
@@ -585,7 +646,10 @@ impl State {
window.clear_screen(win::ColorF32::BLACK);
window.use_program(&self.programs.main);
window.bind_framebuffer(Some(&self.main_framebuffer));
- window.uniform1f("u_aspect_ratio", render_resolution.0 as f32 / render_resolution.1 as f32);
+ window.uniform1f(
+ "u_aspect_ratio",
+ render_resolution.0 as f32 / render_resolution.1 as f32,
+ );
{
let (w, h) = window.size();
window.uniform2f("u_screen_size", w as f32, h as f32);
@@ -623,7 +687,7 @@ impl State {
}
self.main_array.draw();
-
+
window.bind_framebuffer(None);
window.viewport_full_screen();
window.use_program(&self.programs.post);
@@ -632,7 +696,7 @@ impl State {
self.post_array.draw();
window.swap();
-
+
if self.show_debug_info {
println!("frame time = {:?}ms", frame_dt * 1000.0);
}
diff --git a/src/sdf.rs b/src/sdf.rs
index 9a92905..e84c2f0 100644
--- a/src/sdf.rs
+++ b/src/sdf.rs
@@ -30,7 +30,6 @@ impl GenRandomParams for SdfParams {
fn inc_depth(self) -> Self {
Self {
max_depth: self.max_depth - 1,
- ..self
}
}
}
diff --git a/src/sdl.rs b/src/sdl.rs
index 5574373..0b06ed4 100644
--- a/src/sdl.rs
+++ b/src/sdl.rs
@@ -738,16 +738,18 @@ impl SDL_Surface {
self.flags
}
pub fn width(&self) -> i32 {
- self.w.try_into().unwrap()
+ assert!(self.w >= 0);
+ self.w
}
pub fn height(&self) -> i32 {
- self.h.try_into().unwrap()
+ assert!(self.h >= 0);
+ self.h
}
pub fn pitch(&self) -> i32 {
- self.pitch.try_into().unwrap()
+ self.pitch
}
pub fn locked(&self) -> i32 {
- self.locked.try_into().unwrap()
+ self.locked
}
pub fn pixel_format(&self) -> &SDL_PixelFormat {
// SAFETY: this should be a valid pointer as long as self is a valid SDL_Surface
diff --git a/src/vshader_post.glsl b/src/vshader_post.glsl
index 89e76ff..964ace3 100644
--- a/src/vshader_post.glsl
+++ b/src/vshader_post.glsl
@@ -1,4 +1,4 @@
-attribute vec2 v_pos;
+IN vec2 v_pos;
OUT vec2 uv;
void main() {
diff --git a/src/win.rs b/src/win.rs
index 18f3757..3835730 100644
--- a/src/win.rs
+++ b/src/win.rs
@@ -561,6 +561,15 @@ impl ColorU8 {
assert_eq!(suffix.len(), 0);
colors
}
+
+ pub fn slice_to_bytes(slice: &[ColorU8]) -> &[u8] {
+ // SAFETY: it is safe to transmute since ColorU8 is repr(C)
+ let (prefix, bytes, suffix) = unsafe { slice.align_to() };
+ // these should never panic since align_of(u8) == 1
+ assert_eq!(prefix.len(), 0);
+ assert_eq!(suffix.len(), 0);
+ bytes
+ }
}
impl From<u32> for ColorU8 {
@@ -1003,19 +1012,21 @@ impl Texture {
}
/// panicks if `data` is the wrong length (should be exactly `self.width() * self.height()`).
- unsafe fn get_data<T: Color>(&self, data: &mut [T]) {
+ pub fn get_data<T: Color>(&self, data: &mut [T]) {
assert_eq!(data.len(), self.width * self.height, "Bad data size.");
self.bind();
- gl::GetTexImage(
- gl::TEXTURE_2D,
- 0,
- T::GL_FORMAT,
- T::GL_TYPE,
- data.as_ptr() as *mut GLvoid,
- );
+ unsafe {
+ gl::GetTexImage(
+ gl::TEXTURE_2D,
+ 0,
+ T::GL_FORMAT,
+ T::GL_TYPE,
+ data.as_ptr() as *mut GLvoid,
+ );
+ }
}
- unsafe fn get_data_vec<T: Color>(&self) -> Vec<T> {
+ pub fn get_data_vec<T: Color>(&self) -> Vec<T> {
let mut data = vec![T::default(); self.width * self.height];
self.get_data(&mut data);
data
@@ -1120,7 +1131,7 @@ impl Framebuffer {
pub fn set_texture(&mut self, attachment: FramebufferAttachment, texture: &Texture) {
self.bind();
texture.bind();
- unsafe {
+ unsafe {
gl::FramebufferTexture2D(
gl::FRAMEBUFFER,
attachment.to_gl(),
@@ -1366,18 +1377,6 @@ impl Window {
unsafe { Texture::new(params) }
}
- /// get texture image
- ///
- /// panicks if `data.len() != texture.width() * texture.height()`
- pub fn get_texture_data<T: Color>(&mut self, texture: &Texture, data: &mut [T]) {
- unsafe { texture.get_data(data) };
- }
-
- /// get texture image as a newly-allocated `Vec`
- pub fn get_texture_data_vec<T: Color>(&mut self, texture: &Texture) -> Vec<T> {
- unsafe { texture.get_data_vec() }
- }
-
pub fn set_audio_callback(&mut self, callback: AudioCallback) -> Result<(), String> {
if self.audio_data.is_some() {
return Err("audio callback already set.".into());