Skip to content

Instantly share code, notes, and snippets.

@CryZe
Last active February 9, 2019 18:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CryZe/1882c3726c1f91fb5aeb44f5998c5030 to your computer and use it in GitHub Desktop.
Save CryZe/1882c3726c1f91fb5aeb44f5998c5030 to your computer and use it in GitHub Desktop.
use {
derive_more::{Add, Mul},
euc::{buffer::Buffer2d, rasterizer, Pipeline, Target},
image::RgbaImage,
livesplit_core::{
self as livesplit_core,
layout::{Layout, LayoutSettings, LayoutState},
rendering::{Backend, Mesh, Renderer, Rgba as LSColor, Transform},
run::parser::composite,
Run, Segment, TimeSpan, Timer, TimingMethod,
},
std::{fs::File, io::BufReader, rc::Rc},
vek::{Mat3, Rgba, Vec2, Vec3},
};
struct SoftwareBackend {
dims: [usize; 2],
color: AlphaBlended,
}
impl Backend for SoftwareBackend {
type Mesh = Vec<Vertex>;
type Texture = Rc<(Vec<u8>, f32, f32, usize)>;
fn create_mesh(&mut self, Mesh { vertices, indices }: &Mesh) -> Self::Mesh {
indices
.iter()
.map(|&index| {
let v = vertices[index as usize];
Vertex {
position: Vec2::new(v.x, v.y),
texcoord: Vec2::new(v.u, v.v),
}
})
.collect()
}
fn render_mesh(
&mut self,
mesh: &Self::Mesh,
transform: Transform,
[tl, tr, br, bl]: [LSColor; 4],
texture: Option<&Self::Texture>,
) {
let [x1, y1, z1, x2, y2, z2] = transform.to_column_major_array();
MyPipeline::draw::<rasterizer::Triangles<_>, _>(
&Uniforms {
transform: Mat3::new(x1, x2, 0.0, y1, y2, 0.0, z1, z2, 0.0),
color_tl: Rgba::new(tl[0], tl[1], tl[2], tl[3]),
color_tr: Rgba::new(tr[0], tr[1], tr[2], tr[3]),
color_bl: Rgba::new(bl[0], bl[1], bl[2], bl[3]),
color_br: Rgba::new(br[0], br[1], br[2], br[3]),
texture: texture.cloned(),
},
mesh,
&mut self.color,
&mut NoDepth(self.dims),
);
}
fn free_mesh(&mut self, _: Self::Mesh) {}
fn create_texture(&mut self, width: u32, height: u32, data: &[u8]) -> Self::Texture {
Rc::new((
data.to_owned(),
width as f32,
height as f32,
width as usize * 4,
))
}
fn free_texture(&mut self, _: Self::Texture) {}
fn resize(&mut self, _: f32) {}
}
struct MyPipeline;
struct NoDepth([usize; 2]);
impl Target for NoDepth {
type Item = f32;
fn size(&self) -> [usize; 2] {
self.0
}
unsafe fn set(&mut self, _pos: [usize; 2], _item: Self::Item) {}
unsafe fn get(&self, _pos: [usize; 2]) -> &Self::Item {
&1.0
}
fn clear(&mut self, _fill: Self::Item) {}
}
struct AlphaBlended(Buffer2d<Rgba<f32>>);
impl Target for AlphaBlended {
type Item = Rgba<f32>;
fn size(&self) -> [usize; 2] {
self.0.size()
}
unsafe fn set(&mut self, pos: [usize; 2], src: Self::Item) {
let dst = self.0.get(pos);
self.0.set(
pos,
Rgba::new(
src.a * src.r + (1.0 - src.a) * dst.r,
src.a * src.g + (1.0 - src.a) * dst.g,
src.a * src.b + (1.0 - src.a) * dst.b,
src.a + (1.0 - src.a) * dst.a,
),
);
}
unsafe fn get(&self, pos: [usize; 2]) -> &Self::Item {
self.0.get(pos)
}
fn clear(&mut self, fill: Self::Item) {
self.0.clear(fill)
}
}
struct Uniforms {
transform: Mat3<f32>,
color_tl: Rgba<f32>,
color_tr: Rgba<f32>,
color_bl: Rgba<f32>,
color_br: Rgba<f32>,
texture: Option<Rc<(Vec<u8>, f32, f32, usize)>>,
}
struct Vertex {
position: Vec2<f32>,
texcoord: Vec2<f32>,
}
#[derive(Clone, Mul, Add)]
struct VsOut {
color: Rgba<f32>,
texcoord: Vec2<f32>,
}
impl Pipeline for MyPipeline {
type Uniform = Uniforms;
type Vertex = Vertex;
type VsOut = VsOut;
type Pixel = Rgba<f32>;
fn vert(uniforms: &Self::Uniform, vertex: &Self::Vertex) -> ([f32; 3], Self::VsOut) {
let left =
uniforms.color_tl * (1.0 - vertex.texcoord.y) + uniforms.color_bl * vertex.texcoord.y;
let right =
uniforms.color_tr * (1.0 - vertex.texcoord.y) + uniforms.color_br * vertex.texcoord.y;
let color = left * (1.0 - vertex.texcoord.x) + right * vertex.texcoord.x;
let pos = Vec3::new(vertex.position.x, vertex.position.y, 1.0) * uniforms.transform;
(
[2.0 * pos.x - 1.0, 2.0 * pos.y - 1.0, 0.0],
VsOut {
color,
texcoord: vertex.texcoord,
},
)
}
fn frag(uniforms: &Self::Uniform, vsout: &Self::VsOut) -> Self::Pixel {
if let Some(texture) = &uniforms.texture {
let (ref tex_data, width, height, stride) = **texture;
let x = vsout.texcoord.x * width;
let y = vsout.texcoord.y * height;
let pixel = &tex_data[stride * y as usize + x as usize * 4..];
let r = pixel[0];
let g = pixel[1];
let b = pixel[2];
let a = pixel[3];
return Rgba::new(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
a as f32 / 255.0,
) * vsout.color;
}
vsout.color
}
}
fn render(state: &LayoutState, [width, height]: [usize; 2]) -> RgbaImage {
let mut backend = SoftwareBackend {
color: AlphaBlended(Buffer2d::new(
[width, height],
Rgba::new(0.0, 0.0, 0.0, 0.0),
)),
dims: [width, height],
};
Renderer::new().render(&mut backend, (width as _, height as _), &state);
let mut buf = Vec::with_capacity(width * height * 4);
for pixel in backend.color.0.as_ref() {
buf.extend(
pixel
.map(|e| (e * 255.0) as u8)
.into_array()
.iter()
.cloned(),
)
}
RgbaImage::from_raw(width as _, height as _, buf).unwrap()
}
fn main() {
let path = r"C:\Users\Christopher Serr\Documents\Splits\8dj.lss";
let file = BufReader::new(File::open(path).unwrap());
let mut run = composite::parse(file, Some(path.into()), true).unwrap().run;
// let mut run = Run::new();
// run.set_game_name("Game");
// run.set_category_name("Category");
// run.push_segment(Segment::new("Time"));
run.fix_splits();
let mut timer = Timer::new(run).unwrap();
// timer.set_current_timing_method(TimingMethod::GameTime);
timer.start();
// timer.set_game_time(TimeSpan::from_seconds(83.45));
timer.split();
let mut file = BufReader::new(
File::open(r"C:\Users\Christopher Serr\Documents\Splits\cool_columns.ls1l").unwrap(),
);
let mut layout = Layout::from_settings(LayoutSettings::from_json(&mut file).unwrap());
// let mut layout = Layout::default_layout();
// layout.general_settings_mut().background = livesplit_core::settings::Gradient::Plain(
// livesplit_core::settings::Color::hsla(0.0, 0.0, 0.06, 0.75),
// );
let state = layout.state(&timer);
let super_sample_factor = 4;
let image = render(
&state,
[300 * super_sample_factor, 500 * super_sample_factor],
);
let image = image::imageops::thumbnail(
&image,
image.width() / super_sample_factor as u32,
image.height() / super_sample_factor as u32,
);
image.save("livesplit.png").unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment