Refactor `lorem-ipsum`

This commit is contained in:
Patrick Walton 2017-02-28 12:29:42 -08:00
parent a9c4c7aa3e
commit 45ade674be
6 changed files with 330 additions and 251 deletions

View File

@ -16,8 +16,8 @@ use compute_shader::image::{Color, ExternalImage, Format, Image};
use compute_shader::instance::{Instance, ShadingLanguage};
use euclid::{Point2D, Rect, Size2D};
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
use glfw::{Action, Context, Key, OpenGlProfileHint, SwapInterval, WindowEvent};
use glfw::{WindowHint, WindowMode};
use glfw::{Action, Context, Key, OpenGlProfileHint, SwapInterval, Window, WindowEvent};
use glfw::{Glfw, WindowHint, WindowMode};
use memmap::{Mmap, Protection};
use pathfinder::atlas::{AtlasBuilder, AtlasOptions, GlyphRasterizationOptions};
use pathfinder::charmap::CodepointRanges;
@ -34,6 +34,7 @@ use std::io::Read;
use std::mem;
use std::os::raw::c_void;
use std::path::{Path, PathBuf};
use std::sync::mpsc::Receiver;
const ATLAS_SIZE: u32 = 2048;
const WIDTH: u32 = 640;
@ -50,7 +51,9 @@ const MAX_POINT_SIZE: f32 = 512.0;
const FPS_DISPLAY_POINT_SIZE: f32 = 24.0;
const FPS_PADDING: i32 = 6;
static SHADER_PATH: &'static str = "resources/shaders/";
static PATHFINDER_SHADER_PATH: &'static str = "resources/shaders/";
static EXAMPLE_SHADER_PATH: &'static str = "resources/examples/lorem-ipsum/";
static DEFAULT_TEXT_PATH: &'static str = "resources/examples/lorem-ipsum/default.txt";
static FPS_BACKGROUND_COLOR: [f32; 3] = [0.3, 0.3, 0.3];
static FPS_FOREGROUND_COLOR: [f32; 3] = [1.0, 1.0, 1.0];
@ -59,176 +62,256 @@ static TEXT_COLOR: [f32; 3] = [0.0, 0.0, 0.0];
static ATLAS_DUMP_FILENAME: &'static str = "lorem-ipsum-atlas.png";
static RECT_INDICES: [u16; 6] = [0, 1, 3, 1, 2, 3];
fn main() {
let index_arg = Arg::with_name("index").short("i")
.long("index")
.help("Select an index within a font collection")
.takes_value(true);
let subpixel_antialiasing_arg =
Arg::with_name("subpixel-aa").short("s")
.long("subpixel-aa")
.help("Enable subpixel antialiasing");
let font_arg = Arg::with_name("FONT-FILE").help("Select the font file (`.ttf`, `.otf`, etc.)")
.required(true)
.index(1);
let text_arg = Arg::with_name("TEXT-FILE").help("Select a file containing text to display")
.index(2);
let matches = App::new("lorem-ipsum").arg(index_arg)
.arg(subpixel_antialiasing_arg)
.arg(font_arg)
.arg(text_arg)
.get_matches();
DemoApp::new(AppOptions::parse()).run()
}
let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap();
glfw.window_hint(WindowHint::ContextVersion(3, 3));
glfw.window_hint(WindowHint::OpenGlForwardCompat(true));
glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
let context = glfw.create_window(WIDTH, HEIGHT, "lorem-ipsum", WindowMode::Windowed);
struct DemoApp {
options: AppOptions,
renderer: Renderer,
glfw: Glfw,
window: Window,
events: Receiver<(f64, WindowEvent)>,
point_size: f32,
translation: Point2D<i32>,
device_pixel_size: Size2D<u32>,
}
let (mut window, events) = context.expect("Couldn't create a window!");
window.make_current();
window.set_key_polling(true);
window.set_scroll_polling(true);
window.set_size_polling(true);
window.set_framebuffer_size_polling(true);
glfw.set_swap_interval(SwapInterval::Sync(1));
impl DemoApp {
fn new(options: AppOptions) -> DemoApp {
let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap();
glfw.window_hint(WindowHint::ContextVersion(3, 3));
glfw.window_hint(WindowHint::OpenGlForwardCompat(true));
glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
let context = glfw.create_window(WIDTH, HEIGHT, "lorem-ipsum", WindowMode::Windowed);
gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void);
let (mut window, events) = context.expect("Couldn't create a window!");
window.make_current();
window.set_key_polling(true);
window.set_scroll_polling(true);
window.set_size_polling(true);
window.set_framebuffer_size_polling(true);
glfw.set_swap_interval(SwapInterval::Sync(1));
let (width, height) = window.get_framebuffer_size();
let mut device_pixel_size = Size2D::new(width as u32, height as u32);
gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void);
let mut text = "".to_string();
match matches.value_of("TEXT-FILE") {
Some(path) => drop(File::open(path).unwrap().read_to_string(&mut text).unwrap()),
None => text.push_str(TEXT),
let (width, height) = window.get_framebuffer_size();
let device_pixel_size = Size2D::new(width as u32, height as u32);
let renderer = Renderer::new(options.subpixel_aa);
DemoApp {
renderer: renderer,
glfw: glfw,
window: window,
events: events,
device_pixel_size: device_pixel_size,
point_size: INITIAL_POINT_SIZE,
translation: Point2D::zero(),
options: options,
}
}
text = text.replace(&['\n', '\r', '\t'][..], " ");
let font_index = match matches.value_of("index") {
Some(index) => index.parse().unwrap(),
None => 0,
};
fn run(&mut self) {
let file = Mmap::open_path(&self.options.font_path, Protection::Read).unwrap();
let mut buffer = vec![];
let font = unsafe {
Font::from_collection_index(file.as_slice(),
self.options.font_index,
&mut buffer).unwrap()
};
let subpixel_aa = matches.is_present("subpixel-aa");
let page_width = self.device_pixel_size.width as f32 *
font.units_per_em() as f32 / INITIAL_POINT_SIZE;
let file = Mmap::open_path(matches.value_of("FONT-FILE").unwrap(), Protection::Read).unwrap();
let mut buffer = vec![];
let font = unsafe {
Font::from_collection_index(file.as_slice(), font_index, &mut buffer).unwrap()
};
let mut typesetter = Typesetter::new(page_width, &font, font.units_per_em() as f32);
typesetter.add_text(&font, font.units_per_em() as f32, &self.options.text);
let units_per_em = font.units_per_em() as f32;
let page_width = device_pixel_size.width as f32 * units_per_em / INITIAL_POINT_SIZE;
let mut typesetter = Typesetter::new(page_width, &font, units_per_em);
typesetter.add_text(&font, font.units_per_em() as f32, &text);
let glyph_stores = GlyphStores::new(&typesetter, &font);
let renderer = Renderer::new(subpixel_aa);
let mut point_size = INITIAL_POINT_SIZE;
let mut translation = Point2D::new(0, 0);
let mut dirty = true;
let glyph_store = typesetter.create_glyph_store(&font).unwrap();
// Set up the FPS glyph store.
let mut fps_chars: Vec<char> = vec![];
fps_chars.extend(" ./,:()".chars());
fps_chars.extend(('A' as u32..('Z' as u32 + 1)).flat_map(char::from_u32));
fps_chars.extend(('a' as u32..('z' as u32 + 1)).flat_map(char::from_u32));
fps_chars.extend(('0' as u32..('9' as u32 + 1)).flat_map(char::from_u32));
fps_chars.sort();
let fps_codepoint_ranges = CodepointRanges::from_sorted_chars(&fps_chars);
let fps_glyph_store = GlyphStore::from_codepoints(&fps_codepoint_ranges, &font).unwrap();
while !window.should_close() {
if dirty {
let redraw_result = renderer.redraw(point_size,
&font,
&glyph_store,
&typesetter,
&device_pixel_size,
&translation);
let (draw_time, accum_time);
match redraw_result.events {
Some(events) => {
let mut draw_nanos = 0u64;
unsafe {
gl::Flush();
gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_nanos);
}
draw_time = draw_nanos as f64;
accum_time = events.accum.time_elapsed().unwrap() as f64;
}
None => {
draw_time = 0.0;
accum_time = 0.0;
}
let mut dirty = true;
while !self.window.should_close() {
if dirty {
self.redraw(&glyph_stores, &typesetter, &font);
dirty = false
}
let timing = renderer.get_timing_in_ms();
self.glfw.wait_events();
let events: Vec<_> = glfw::flush_messages(&self.events).map(|(_, e)| e).collect();
for event in events {
dirty = self.handle_window_event(event) || dirty
}
}
}
renderer.draw_fps(&font,
&fps_glyph_store,
&device_pixel_size,
draw_time,
accum_time,
timing,
redraw_result.glyphs_drawn);
fn redraw(&mut self, glyph_stores: &GlyphStores, typesetter: &Typesetter, font: &Font) {
let redraw_result = self.renderer.redraw(self.point_size,
&font,
&glyph_stores.main,
typesetter,
&self.device_pixel_size,
&self.translation);
window.swap_buffers();
let (draw_time, accum_time);
match redraw_result.events {
Some(events) => {
let mut draw_nanos = 0u64;
unsafe {
gl::Flush();
gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_nanos);
}
dirty = false
draw_time = draw_nanos as f64;
accum_time = events.accum.time_elapsed().unwrap() as f64;
}
None => {
draw_time = 0.0;
accum_time = 0.0;
}
}
let timing = self.renderer.get_timing_in_ms();
glfw.wait_events();
for (_, event) in glfw::flush_messages(&events) {
match event {
WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
window.set_should_close(true)
}
WindowEvent::Key(Key::S, _, Action::Press, _) => {
renderer.take_screenshot();
println!("wrote screenshot to: {}", ATLAS_DUMP_FILENAME);
}
WindowEvent::Scroll(x, y) => {
if window.get_key(Key::LeftAlt) == Action::Press ||
window.get_key(Key::RightAlt) == Action::Press {
let old_point_size = point_size;
point_size = old_point_size + y as f32;
self.renderer.draw_fps(&font,
&glyph_stores.fps,
&self.device_pixel_size,
draw_time,
accum_time,
timing,
redraw_result.glyphs_drawn);
if point_size < MIN_POINT_SIZE {
point_size = MIN_POINT_SIZE
} else if point_size > MAX_POINT_SIZE {
point_size = MAX_POINT_SIZE
}
self.window.swap_buffers();
}
let mut center = Point2D::new(
translation.x as f32 - device_pixel_size.width as f32 * 0.5,
translation.y as f32 - device_pixel_size.height as f32 * 0.5);
center.x = center.x * point_size / old_point_size;
center.y = center.y * point_size / old_point_size;
// Returns true if the window needs to be redrawn.
fn handle_window_event(&mut self, event: WindowEvent) -> bool {
match event {
WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
self.window.set_should_close(true);
false
}
WindowEvent::Key(Key::S, _, Action::Press, _) => {
self.renderer.take_screenshot();
println!("wrote screenshot to: {}", ATLAS_DUMP_FILENAME);
false
}
WindowEvent::Scroll(x, y) => {
if self.window.get_key(Key::LeftAlt) == Action::Press ||
self.window.get_key(Key::RightAlt) == Action::Press {
let old_point_size = self.point_size;
self.point_size = old_point_size + y as f32;
translation.x = (center.x + device_pixel_size.width as f32 * 0.5).round()
as i32;
translation.y = (center.y + device_pixel_size.height as f32 * 0.5).round()
as i32;
} else {
translation.x += (x * SCROLL_SPEED).round() as i32;
translation.y += (y * SCROLL_SPEED).round() as i32;
if self.point_size < MIN_POINT_SIZE {
self.point_size = MIN_POINT_SIZE
} else if self.point_size > MAX_POINT_SIZE {
self.point_size = MAX_POINT_SIZE
}
dirty = true
let mut center =
Point2D::new(self.translation.x as f32 -
self.device_pixel_size.width as f32 *
0.5,
self.translation.y as f32 -
self.device_pixel_size.height as f32 *
0.5);
center.x = center.x * self.point_size / old_point_size;
center.y = center.y * self.point_size / old_point_size;
self.translation.x =
(center.x + self.device_pixel_size.width as f32 * 0.5).round() as i32;
self.translation.y =
(center.y + self.device_pixel_size.height as f32 * 0.5).round() as i32;
} else {
self.translation.x += (x * SCROLL_SPEED).round() as i32;
self.translation.y += (y * SCROLL_SPEED).round() as i32;
}
WindowEvent::Size(_, _) | WindowEvent::FramebufferSize(_, _) => {
let (width, height) = window.get_framebuffer_size();
device_pixel_size = Size2D::new(width as u32, height as u32);
dirty = true
}
_ => {}
true
}
WindowEvent::Size(_, _) | WindowEvent::FramebufferSize(_, _) => {
let (width, height) = self.window.get_framebuffer_size();
self.device_pixel_size = Size2D::new(width as u32, height as u32);
true
}
_ => false,
}
}
}
struct AppOptions {
font_path: String,
font_index: u32,
text: String,
subpixel_aa: bool,
}
impl AppOptions {
fn parse() -> AppOptions {
let index_arg = Arg::with_name("index").short("i")
.long("index")
.help("Select an index within a font collection")
.takes_value(true);
let subpixel_antialiasing_arg =
Arg::with_name("subpixel-aa").short("s")
.long("subpixel-aa")
.help("Enable subpixel antialiasing");
let font_arg =
Arg::with_name("FONT-FILE").help("Select the font file (`.ttf`, `.otf`, etc.)")
.required(true)
.index(1);
let text_arg = Arg::with_name("TEXT-FILE").help("Select a file containing text to display")
.index(2);
let matches = App::new("lorem-ipsum").arg(index_arg)
.arg(subpixel_antialiasing_arg)
.arg(font_arg)
.arg(text_arg)
.get_matches();
let mut text = "".to_string();
let path = matches.value_of("TEXT-FILE").unwrap_or(DEFAULT_TEXT_PATH);
File::open(path).unwrap().read_to_string(&mut text).unwrap();
text = text.replace(&['\n', '\r', '\t'][..], " ");
let font_index = match matches.value_of("index") {
Some(index) => index.parse().unwrap(),
None => 0,
};
let font_path = matches.value_of("FONT-FILE").unwrap();
let subpixel_aa = matches.is_present("subpixel-aa");
AppOptions {
text: text,
font_index: font_index,
font_path: font_path.to_string(),
subpixel_aa: subpixel_aa,
}
}
}
struct GlyphStores {
main: GlyphStore,
fps: GlyphStore,
}
impl GlyphStores {
fn new(typesetter: &Typesetter, font: &Font) -> GlyphStores {
let main_glyph_store = typesetter.create_glyph_store(&font).unwrap();
let mut fps_chars: Vec<char> = vec![];
fps_chars.extend(" ./,:()".chars());
fps_chars.extend(('A' as u32..('Z' as u32 + 1)).flat_map(char::from_u32));
fps_chars.extend(('a' as u32..('z' as u32 + 1)).flat_map(char::from_u32));
fps_chars.extend(('0' as u32..('9' as u32 + 1)).flat_map(char::from_u32));
fps_chars.sort();
let fps_codepoint_ranges = CodepointRanges::from_sorted_chars(&fps_chars);
let fps_glyph_store = GlyphStore::from_codepoints(&fps_codepoint_ranges, &font).unwrap();
GlyphStores {
main: main_glyph_store,
fps: fps_glyph_store,
}
}
}
@ -277,7 +360,7 @@ impl Renderer {
let mut rasterizer_options = RasterizerOptions::from_env().unwrap();
if env::var("PATHFINDER_SHADER_PATH").is_err() {
rasterizer_options.shader_path = PathBuf::from(SHADER_PATH)
rasterizer_options.shader_path = PathBuf::from(PATHFINDER_SHADER_PATH)
}
let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap();
@ -291,7 +374,7 @@ impl Renderer {
let (mut solid_color_vertex_buffer, mut solid_color_index_buffer) = (0, 0);
let mut solid_color_vertex_array = 0;
unsafe {
composite_program = create_program(COMPOSITE_VERTEX_SHADER, COMPOSITE_FRAGMENT_SHADER);
composite_program = create_program("composite");
composite_position_attribute =
gl::GetAttribLocation(composite_program, "aPosition\0".as_ptr() as *const GLchar);
composite_tex_coord_attribute =
@ -311,8 +394,7 @@ impl Renderer {
gl::GetUniformLocation(composite_program,
"uBackgroundColor\0".as_ptr() as *const GLchar);
solid_color_program = create_program(SOLID_COLOR_VERTEX_SHADER,
SOLID_COLOR_FRAGMENT_SHADER);
solid_color_program = create_program("solid_color");
solid_color_position_attribute =
gl::GetAttribLocation(solid_color_program,
"aPosition\0".as_ptr() as *const GLchar);
@ -370,7 +452,7 @@ impl Renderer {
gl::STATIC_DRAW);
}
// FIXME(pcwalton)
// FIXME(pcwalton): Dynamically resizing atlas.
let atlas_size = Size2D::new(ATLAS_SIZE, ATLAS_SIZE);
let coverage_buffer_options = CoverageBufferOptions {
size: atlas_size,
@ -870,8 +952,21 @@ impl CompositeVertexArray {
}
}
fn create_program(vertex_shader_source: &str, fragment_shader_source: &str) -> GLuint {
struct RedrawResult {
events: Option<DrawAtlasProfilingEvents>,
glyphs_drawn: u32,
}
fn create_program(name: &str) -> GLuint {
unsafe {
let (mut vertex_shader_source, mut fragment_shader_source) = (vec![], vec![]);
File::open(&format!("{}/{}.vs.glsl",
EXAMPLE_SHADER_PATH,
name)).unwrap().read_to_end(&mut vertex_shader_source).unwrap();
File::open(&format!("{}/{}.fs.glsl",
EXAMPLE_SHADER_PATH,
name)).unwrap().read_to_end(&mut fragment_shader_source).unwrap();
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
gl::ShaderSource(vertex_shader,
@ -915,102 +1010,3 @@ fn create_image(rasterizer: &Rasterizer, atlas_size: &Size2D<u32>) -> (Image, GL
(compute_image, gl_texture)
}
struct RedrawResult {
events: Option<DrawAtlasProfilingEvents>,
glyphs_drawn: u32,
}
static COMPOSITE_VERTEX_SHADER: &'static str = "\
#version 330
uniform mat2 uTransform;
uniform vec2 uTranslation;
in vec2 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
vTexCoord = aTexCoord;
gl_Position = vec4(uTransform * aPosition + uTranslation, 0.0f, 1.0f);
}
";
static COMPOSITE_FRAGMENT_SHADER: &'static str = "\
#version 330
uniform sampler2DRect uAtlas;
uniform vec3 uForegroundColor;
uniform vec3 uBackgroundColor;
in vec2 vTexCoord;
out vec4 oFragColor;
void main() {
vec3 value = texture(uAtlas, vTexCoord).rgb;
vec3 color = mix(uBackgroundColor, uForegroundColor, value);
oFragColor = vec4(color, 1.0f);
}
";
static SOLID_COLOR_VERTEX_SHADER: &'static str = "\
#version 330
in vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0f, 1.0f);
}
";
static SOLID_COLOR_FRAGMENT_SHADER: &'static str = "\
#version 330
uniform vec3 uColor;
out vec4 oFragColor;
void main() {
oFragColor = vec4(uColor, 1.0f);
}
";
static RECT_INDICES: [u16; 6] = [0, 1, 3, 1, 2, 3];
static TEXT: &'static str = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur scelerisque pellentesque risus \
quis vehicula. Ut sollicitudin aliquet diam, vel lobortis orci porta in. Sed eu nisi egestas odio \
tincidunt cursus eget ut lorem. Fusce lacinia ex nec lectus rutrum mollis. Donec in ultrices \
purus. Integer id suscipit magna. Suspendisse congue pulvinar neque id ultrices. Curabitur nec \
tellus et est pellentesque posuere. Duis ut metus euismod, feugiat arcu vitae, posuere libero. \
Curabitur nunc urna, rhoncus vitae scelerisque quis, viverra et odio. Suspendisse accumsan \
pretium mi, nec fringilla metus condimentum id. Duis dignissim quam eu felis lobortis, eget \
dignissim lectus fermentum. Nunc et massa id orci pellentesque rutrum. Nam imperdiet quam vel \
ligula efficitur ultricies vel eu tellus. Maecenas luctus risus a erat euismod ultricies. \
Pellentesque neque mauris, laoreet vitae finibus quis, molestie ut velit. Donec laoreet justo \
risus. In id mi sed odio placerat interdum ut vitae erat. Fusce quis mollis mauris, sit amet \
efficitur libero. In efficitur tortor nulla, sollicitudin sodales mi tempor in. In egestas \
ultrices fermentum. Quisque mattis egestas nulla. Interdum et malesuada fames ac ante ipsum \
primis in faucibus. Etiam in tempus sapien, in dignissim arcu. Quisque diam nulla, rhoncus et \
tempor nec, facilisis porta purus. Nulla ut eros laoreet, placerat dolor ut, interdum orci. Sed \
posuere eleifend mollis. Integer at nunc ex. Vestibulum aliquet risus quis lacinia convallis. \
Fusce et metus viverra, varius nulla in, rutrum justo. Interdum et malesuada fames ac ante ipsum \
primis in faucibus. Praesent non est vel lectus suscipit malesuada id ut nisl. Aenean sem ipsum, \
tincidunt non orci non, varius consectetur purus. Aenean sed mollis turpis, sit amet vestibulum \
risus. Nunc ut hendrerit urna, sit amet lacinia arcu. Curabitur laoreet a enim et eleifend. Etiam \
consectetur pharetra massa, sed elementum quam molestie nec. Integer eu justo lectus. Vestibulum \
sed vulputate sapien. Curabitur pretium luctus orci et interdum. Quisque ligula nisi, varius id \
sodales id, volutpat et lorem. Pellentesque ex urna, malesuada at ex non, elementum ultricies \
nulla. Nunc sodales, turpis at maximus bibendum, neque lorem laoreet felis, eget convallis sem \
mauris ac quam. Mauris non pretium nulla. Nam semper pulvinar convallis. Suspendisse ultricies \
odio vitae tortor congue, rutrum finibus nisl malesuada. Interdum et malesuada fames ac ante \
ipsum primis in faucibus. Vestibulum aliquam et lacus sit amet lobortis. In sed ligula quis urna \
accumsan vehicula sit amet id magna. Cras mollis orci vitae turpis porta, sed gravida nunc \
aliquam. Phasellus nec facilisis nunc. Suspendisse volutpat leo felis, in iaculis nisi dignissim \
et. Phasellus at urna purus. Nullam vitae metus ante. Praesent porttitor libero quis velit \
fermentum rhoncus. Cras vitae rhoncus nulla. In efficitur risus sapien, sed viverra neque \
scelerisque at. Morbi fringilla odio massa. Donec tincidunt magna diam, eget congue leo tristique \
eget. Cras et sapien nulla.";

View File

@ -0,0 +1,16 @@
#version 330
uniform sampler2DRect uAtlas;
uniform vec3 uForegroundColor;
uniform vec3 uBackgroundColor;
in vec2 vTexCoord;
out vec4 oFragColor;
void main() {
vec3 value = texture(uAtlas, vTexCoord).rgb;
vec3 color = mix(uBackgroundColor, uForegroundColor, value);
oFragColor = vec4(color, 1.0f);
}

View File

@ -0,0 +1,15 @@
#version 330
uniform mat2 uTransform;
uniform vec2 uTranslation;
in vec2 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
vTexCoord = aTexCoord;
gl_Position = vec4(uTransform * aPosition + uTranslation, 0.0f, 1.0f);
}

View File

@ -0,0 +1,34 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur scelerisque pellentesque risus
quis vehicula. Ut sollicitudin aliquet diam, vel lobortis orci porta in. Sed eu nisi egestas odio
tincidunt cursus eget ut lorem. Fusce lacinia ex nec lectus rutrum mollis. Donec in ultrices
purus. Integer id suscipit magna. Suspendisse congue pulvinar neque id ultrices. Curabitur nec
tellus et est pellentesque posuere. Duis ut metus euismod, feugiat arcu vitae, posuere libero.
Curabitur nunc urna, rhoncus vitae scelerisque quis, viverra et odio. Suspendisse accumsan
pretium mi, nec fringilla metus condimentum id. Duis dignissim quam eu felis lobortis, eget
dignissim lectus fermentum. Nunc et massa id orci pellentesque rutrum. Nam imperdiet quam vel
ligula efficitur ultricies vel eu tellus. Maecenas luctus risus a erat euismod ultricies.
Pellentesque neque mauris, laoreet vitae finibus quis, molestie ut velit. Donec laoreet justo
risus. In id mi sed odio placerat interdum ut vitae erat. Fusce quis mollis mauris, sit amet
efficitur libero. In efficitur tortor nulla, sollicitudin sodales mi tempor in. In egestas
ultrices fermentum. Quisque mattis egestas nulla. Interdum et malesuada fames ac ante ipsum
primis in faucibus. Etiam in tempus sapien, in dignissim arcu. Quisque diam nulla, rhoncus et
tempor nec, facilisis porta purus. Nulla ut eros laoreet, placerat dolor ut, interdum orci. Sed
posuere eleifend mollis. Integer at nunc ex. Vestibulum aliquet risus quis lacinia convallis.
Fusce et metus viverra, varius nulla in, rutrum justo. Interdum et malesuada fames ac ante ipsum
primis in faucibus. Praesent non est vel lectus suscipit malesuada id ut nisl. Aenean sem ipsum,
tincidunt non orci non, varius consectetur purus. Aenean sed mollis turpis, sit amet vestibulum
risus. Nunc ut hendrerit urna, sit amet lacinia arcu. Curabitur laoreet a enim et eleifend. Etiam
consectetur pharetra massa, sed elementum quam molestie nec. Integer eu justo lectus. Vestibulum
sed vulputate sapien. Curabitur pretium luctus orci et interdum. Quisque ligula nisi, varius id
sodales id, volutpat et lorem. Pellentesque ex urna, malesuada at ex non, elementum ultricies
nulla. Nunc sodales, turpis at maximus bibendum, neque lorem laoreet felis, eget convallis sem
mauris ac quam. Mauris non pretium nulla. Nam semper pulvinar convallis. Suspendisse ultricies
odio vitae tortor congue, rutrum finibus nisl malesuada. Interdum et malesuada fames ac ante
ipsum primis in faucibus. Vestibulum aliquam et lacus sit amet lobortis. In sed ligula quis urna
accumsan vehicula sit amet id magna. Cras mollis orci vitae turpis porta, sed gravida nunc
aliquam. Phasellus nec facilisis nunc. Suspendisse volutpat leo felis, in iaculis nisi dignissim
et. Phasellus at urna purus. Nullam vitae metus ante. Praesent porttitor libero quis velit
fermentum rhoncus. Cras vitae rhoncus nulla. In efficitur risus sapien, sed viverra neque
scelerisque at. Morbi fringilla odio massa. Donec tincidunt magna diam, eget congue leo tristique
eget. Cras et sapien nulla.

View File

@ -0,0 +1,10 @@
#version 330
uniform vec3 uColor;
out vec4 oFragColor;
void main() {
oFragColor = vec4(uColor, 1.0f);
}

View File

@ -0,0 +1,8 @@
#version 330
in vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0f, 1.0f);
}