diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/n1ck.rs | 306 | ||||
-rw-r--r-- | src/shaders/wave-fs.glsl | 16 | ||||
-rw-r--r-- | src/shaders/wave-vs.glsl | 11 |
4 files changed, 281 insertions, 56 deletions
diff --git a/src/main.rs b/src/main.rs index 5cb2e3a..320aa8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,7 +65,7 @@ fn main() { stream.play(); let mut tom = Tom::new(waveform); - let mut n1ck = N1ck::new(); + let mut n1ck = N1ck::new(&mut surface); let viewports = gen_viewports(); @@ -93,7 +93,7 @@ fn main() { if i == 0 || i == 3 { surface = tom.draw(surface, &back_buffer, &program, &pipeline_state); } else if i == 1 || i == 2 { - surface = n1ck.draw(surface, &back_buffer, &program, &pipeline_state); + surface = n1ck.draw(surface, &back_buffer, &pipeline_state); } } diff --git a/src/n1ck.rs b/src/n1ck.rs index 721e755..8046b2a 100644 --- a/src/n1ck.rs +++ b/src/n1ck.rs @@ -1,68 +1,229 @@ + use luminance::context::GraphicsContext; use luminance::framebuffer::Framebuffer; -use luminance::pipeline::PipelineState; +use luminance::pipeline::{BoundTexture, PipelineState}; use luminance::render_state::RenderState; -use luminance::shader::program::Program; -use luminance::tess::{Mode, Tess, TessBuilder, TessSliceIndex}; -use luminance::texture::Dim2; - -use crate::vertex::{Vertex, VertexPosition, VertexRGB, VertexSemantics}; - -fn gen_vertices() -> Vec<Vertex> { - let mut vertices: Vec<Vertex> = Vec::new(); - - vertices.push(Vertex { - position: VertexPosition::new([-0.5, -0.5]), - color: VertexRGB::new([255, 0, 0]), - }); - vertices.push(Vertex { - position: VertexPosition::new([0.5, -0.5]), - color: VertexRGB::new([0, 255, 0]), - }); - vertices.push(Vertex { - position: VertexPosition::new([0.0, 0.5]), - color: VertexRGB::new([0, 0, 255]), - }); - - vertices +use luminance::tess::{Mode, Tess, TessBuilder}; +use luminance::texture::{Dim2, GenMipmaps, Sampler, Texture}; +use luminance::pixel::{NormRGB8UI, NormUnsigned}; +use luminance_derive::UniformInterface; +use luminance_derive::{Semantics, Vertex}; +use luminance::linear::M44; + + +use luminance::shader::program::{Program, Uniform}; +use luminance_glfw::{GlfwSurface}; +use std::time::Instant; +use cgmath::{perspective, EuclideanSpace, Matrix4, Point3, Rad, Vector3}; +use std::path::Path; + +use crate::constants; + + +const VS: &'static str = include_str!("./shaders/wave-vs.glsl"); +const FS: &'static str = include_str!("./shaders/wave-fs.glsl"); + +#[derive(UniformInterface)] +struct ShaderInterface { + // the 'static lifetime acts as “anything” here + tex: Uniform<&'static BoundTexture<'static, Dim2, NormUnsigned>>, + position: Uniform<[f32; 2]>, + intensity: Uniform<f32>, + time: Uniform<f32>, + + #[uniform(unbound)] + projection: Uniform<M44>, + #[uniform(unbound)] + view: Uniform<M44>, } -fn alter_vertices(vertices: &mut Vec<Vertex>) { - for vertex in vertices { - if vertex.color[1] < 255 { - vertex.color[1] += 1; - } - } +#[derive(Clone, Copy, Debug, Eq, PartialEq, Semantics)] +pub enum Semantics { + #[sem(name = "pos", repr = "[f32; 2]", wrapper = "VertexPosition")] + Position, + #[sem(name = "uv", repr = "[f32; 2]", wrapper = "UVCoordinate")] + UV, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Vertex)] +#[vertex(sem = "Semantics")] +struct Vertex { + pos: VertexPosition, + uv: UVCoordinate, +} + +#[repr(C)] +struct Beat { + time: f32, + position: [f32; 2], + intensity: f32 } -#[derive(Default)] +const BEATS: [Beat; 15] = [ + Beat { + time: 1.03, + position: [0.5, -0.5], + intensity: 0.06, + }, + Beat { + time: 3.47, + position: [-0.5, -0.5], + intensity: 0.08, + }, + Beat{ + time: 5.84, + position: [0.5, 0.5], + intensity: 0.03, + }, + Beat{ + time: 8.30, + position: [0.5, -0.5], + intensity: 0.04, + }, + Beat{ + time: 10.76, + position: [-0.5, 0.5], + intensity: 0.06, + }, + Beat{ + time: 13.17, + position: [-0.5, -0.5], + intensity: 0.08, + }, + Beat{ + time: 15.60, + position: [0.5, -0.5], + intensity: 0.09, + }, + Beat{ + time: 18.03, + position: [-0.5, -0.5], + intensity: 0.03, + }, + Beat{ + time: 20.48, + position: [-0.5, 0.5], + intensity: 0.07, + }, + Beat{ + time: 22.9, + position: [0.5, -0.5], + intensity: 0.02, + }, + Beat{ + time: 25.30, + position: [-0.5, -0.5], + intensity: 0.12, + }, + Beat{ + time: 27.76, + position: [0.5, 0.5], + intensity: 0.05, + }, + Beat{ + time: 30.20, + position: [-0.5, 0.5], + intensity: 0.07, + }, + Beat{ + time: 32.6, + position: [0.5, 0.5], + intensity: 0.08, + }, + Beat{ + time: 35.07, + position: [0.5, -0.5], + intensity: 0.04, + }, +]; + +const TRI_VERTICES: [Vertex; 4] = [ + // First triangle – an RGB one. + Vertex::new( + VertexPosition::new([-1.0, 1.0]), + UVCoordinate::new([0.0, 1.0]), + ), + Vertex::new( + VertexPosition::new([1.0, 1.0]), + UVCoordinate::new([1.0, 1.0]), + ), + Vertex::new( + VertexPosition::new([-1.0, -1.0]), + UVCoordinate::new([0.0, 0.0]), + ), + Vertex::new( + VertexPosition::new([1.0, -1.0]), + UVCoordinate::new([1.0, 0.0]), + ), +]; + + + + +//#[derive(Default)] pub struct N1ck { - vertices: Vec<Vertex>, - tessalations: Vec<Tess>, + index: usize, + program: Program<Semantics, (), ShaderInterface>, + projection: Matrix4<f32>, + start_time: Instant, + //surface: GraphicsContext, + tess: Vec<Tess>, + tex: Texture<luminance::texture::Dim2, luminance::pixel::NormRGB8UI>, + view: Matrix4::<f32>, } impl N1ck { - pub fn new() -> N1ck { - let vertices = gen_vertices(); - let tessalations = Vec::new(); + pub fn new( surface: &mut GlfwSurface, + ) -> N1ck { + let img = read_image(Path::new("data/djbt.jpg")).expect("error while reading image on disk"); + let tex = load_from_disk(surface, img); + + let program = Program::<Semantics, (), ShaderInterface>::from_strings(None, VS, None, FS) + .expect("program creation") + .ignore_warnings(); + const FOVY: Rad<f32> = Rad(std::f32::consts::PI / 2.); + const Z_NEAR: f32 = 0.1; + const Z_FAR: f32 = 10.; + + let projection = perspective( + FOVY, + constants::WIDTH as f32 / constants::WIDTH as f32, + Z_NEAR, + Z_FAR, + ); + + let view = Matrix4::<f32>::look_at(Point3::new(0., 0., 1.), Point3::origin(), Vector3::unit_y()); + N1ck { - vertices, - tessalations, + index: 0, + program, + projection, + start_time: Instant::now(), + tess: Vec::new(), + tex, + view, } } pub fn update<T: GraphicsContext>(&mut self, mut surface: T) -> T { - alter_vertices(&mut self.vertices); - - self.tessalations.clear(); - self.tessalations.push( + //let elapsed = &self.start_time.elapsed(); + //let time = elapsed.as_secs() as f64 + (f64::from(elapsed.subsec_millis()) * 1e-3); + self.tess.clear(); + + self.tess.push( TessBuilder::new(&mut surface) - .add_vertices(&self.vertices) - .set_mode(Mode::Triangle) - .build() - .unwrap(), + .add_vertices(TRI_VERTICES) + .set_mode(Mode::TriangleStrip) + .build() + .unwrap() ); + let elapsed = self.start_time.elapsed(); + let time = elapsed.as_secs() as f64 + (f64::from(elapsed.subsec_millis()) * 1e-3); + if time as f32 > BEATS[self.index].time { + self.index = if self.index < 14 { self.index + 1 } else {14}; + } surface } @@ -71,23 +232,60 @@ impl N1ck { &self, mut surface: T, back_buffer: &Framebuffer<Dim2, (), ()>, - program: &Program<VertexSemantics, (), ()>, pipeline_state: &PipelineState, ) -> T { surface.pipeline_builder().pipeline( &back_buffer, &pipeline_state, |_pipeline, mut shd_gate| { - shd_gate.shade(&program, |_, mut rdr_gate| { - rdr_gate.render(&RenderState::default(), |mut tess_gate| { - for tessalation in self.tessalations.iter() { - tess_gate.render(tessalation.slice(..)); - } - }); - }); + let bound_tex = _pipeline.bind_texture(&self.tex); + + shd_gate.shade(&self.program, |interface, mut rdr_gate| { + + let elapsed = self.start_time.elapsed(); + let time = elapsed.as_secs() as f64 + (f64::from(elapsed.subsec_millis()) * 1e-3); + + interface.tex.update(&bound_tex); + interface.position.update(BEATS[self.index].position); + interface.intensity.update(BEATS[self.index].intensity); + interface.time.update(time as f32); + interface.projection.update(self.projection.into()); + interface.view.update(self.view.into()); + + rdr_gate.render(&RenderState::default(), |mut tess_gate| { + for tesselation in self.tess.iter() { + tess_gate.render(tesselation); + } + }); + }); }, ); surface } } + + +// read the texture into memory as a whole bloc (i.e. no streaming) +fn read_image(path: &Path) -> Option<image::RgbImage> { + image::open(path).map(|img| img.flipv().to_rgb()).ok() +} + +fn load_from_disk( + surface: &mut GlfwSurface, + img: image::RgbImage, +) -> Texture<Dim2, NormRGB8UI> { + let (width, height) = img.dimensions(); + let texels = img.into_raw(); + + // create the luminance texture; the third argument is the number of mipmaps we want (leave it + // to 0 for now) and the latest is the sampler to use when sampling the texels in the + // shader (we’ll just use the default one) + let tex = Texture::new(surface, [width, height], 0, Sampler::default()) + .expect("luminance texture creation"); + + // the first argument disables mipmap generation (we don’t care so far) + tex.upload_raw(GenMipmaps::No, &texels).unwrap(); + + tex +}
\ No newline at end of file diff --git a/src/shaders/wave-fs.glsl b/src/shaders/wave-fs.glsl new file mode 100644 index 0000000..4923f97 --- /dev/null +++ b/src/shaders/wave-fs.glsl @@ -0,0 +1,16 @@ +in vec2 v_uv; +out vec4 frag; + +uniform sampler2D tex; +uniform vec2 position; +uniform float intensity; +uniform float time; + +void main() { + vec2 p =-1.+2.*v_uv / vec2(1,1)-position; + float cLength=length(p); + vec2 uv=v_uv+(p/cLength)*cos(cLength*32.0-time*4.0)*intensity; + vec3 col=smoothstep(0.1,.91,texture(tex,uv).xyz); + + frag = texture(tex,uv); +}
\ No newline at end of file diff --git a/src/shaders/wave-vs.glsl b/src/shaders/wave-vs.glsl new file mode 100644 index 0000000..c2584f2 --- /dev/null +++ b/src/shaders/wave-vs.glsl @@ -0,0 +1,11 @@ +in vec3 pos; +in vec2 uv; +out vec2 v_uv; + +uniform mat4 projection; +uniform mat4 view; + +void main() { + gl_Position = projection * view * vec4(pos, 1.); + v_uv = uv; +} |