Created
August 1, 2017 08:54
-
-
Save umurgdk/2814a7fea4ff93864577206908ca46db to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate cgmath; | |
extern crate specs; | |
#[macro_use] | |
extern crate gfx; | |
extern crate gfx_window_glutin; | |
extern crate glutin; | |
// Logging | |
#[macro_use] | |
extern crate log; | |
extern crate log_panics; | |
#[cfg(target_os = "android")] | |
extern crate android_logger; | |
#[cfg(not(target_os = "android"))] | |
extern crate env_logger; | |
#[cfg(target_os = "android")] | |
extern crate android_glue; | |
use std::f32; | |
use std::time; | |
use cgmath::*; | |
use gfx::Factory; | |
use gfx::traits::FactoryExt; | |
use gfx::Device; | |
use gfx::handle::Buffer; | |
use gfx::IndexBuffer; | |
use glutin::GlContext; | |
type ColorFormat = gfx::format::Rgba8; | |
type DepthFormat = gfx::format::DepthStencil; | |
#[cfg(target_os = "android")] | |
const MAX_SPRITES: usize = 1_000; | |
#[cfg(not(target_os = "android"))] | |
const MAX_SPRITES: usize = 50_000; | |
const NUM_VERTICES: usize = MAX_SPRITES * 4; | |
const NUM_INDICES: usize = MAX_SPRITES * 6; | |
gfx_defines! { | |
vertex Vertex { | |
position: [f32; 2] = "a_pos", | |
color: [f32; 3] = "a_color", | |
} | |
constant Mvp { | |
model: [[f32; 4]; 4] = "u_model", | |
view: [[f32; 4]; 4] = "u_view", | |
projection: [[f32; 4]; 4] = "u_projection", | |
} | |
pipeline pipe { | |
vbuf: gfx::VertexBuffer<Vertex> = (), | |
model: gfx::Global<[[f32; 4]; 4]> = "u_model", | |
view: gfx::Global<[[f32; 4]; 4]> = "u_view", | |
projection: gfx::Global<[[f32; 4]; 4]> = "u_projection", | |
out: gfx::RenderTarget<ColorFormat> = "target0", | |
} | |
} | |
fn create_quad(pos: [f32; 2], size: [f32; 2], color: [f32; 3]) -> [Vertex; 4] { | |
[ | |
Vertex { position: [pos[0], pos[1] + size[1]], color }, | |
Vertex { position: [pos[0] + size[0], pos[1] + size[1]], color }, | |
Vertex { position: [pos[0] + size[0], pos[1]], color }, | |
Vertex { position: [pos[0], pos[1]], color }, | |
] | |
} | |
fn get_dimensions(window: &glutin::GlWindow) -> (u32, u32) { | |
window.get_inner_size().expect("Failed to get window dimensions") | |
} | |
fn projection_from_window_dimensions(window: &glutin::GlWindow) -> [[f32; 4]; 4] { | |
let (width, height) = get_dimensions(window); | |
ortho(0.0, width as f32, 0.0, height as f32, -5.0, 5.0).into() | |
} | |
fn calculate_sprite_size(width: u32, num_columns: usize) -> (f32, f32) { | |
let sprite_gap = (width as f32 / num_columns as f32) / 2.0; | |
let sprite_size = width as f32 / num_columns as f32 - sprite_gap; | |
(sprite_size.max(5.0), sprite_gap.max(2.5)) | |
} | |
fn transform_vertex(vertex: &mut Vertex, mat: &cgmath::Matrix4<f32>) { | |
let mut position = mat * vec4(vertex.position[0], vertex.position[1], 0.0, 1.0); | |
vertex.position[0] = position.x; | |
vertex.position[1] = position.y; | |
} | |
fn main() { | |
println!("hunger: main()"); | |
#[cfg(target_os = "android")] | |
android_logger::init_once(log::LogLevel::Trace); | |
#[cfg(not(target_os = "android"))] | |
env_logger::init().expect("Failed to initialize env_logger"); | |
println!("hunger: initializing log_panics"); | |
info!("hunger: initializing log_panics"); | |
error!("hunger: err initializing log_panics"); | |
log_panics::init_namespace(Some("hunger")); | |
info!("Hunger start working..."); | |
error!("Hunger start working..."); | |
#[cfg(target_os = "android")] | |
android_glue::write_log("Creating event loop..."); | |
info!("Creating event loop."); | |
let mut events_loop = glutin::EventsLoop::new(); | |
// Initialize window | |
let (window, mut device, mut factory, main_color, mut main_depth) = { | |
let builder = glutin::WindowBuilder::new() | |
.with_title("Hunger") | |
.with_dimensions(1600, 1200); | |
let api_request = glutin::GlRequest::GlThenGles { | |
opengl_version: (3, 2), | |
opengles_version: (3, 0) | |
}; | |
let context = glutin::ContextBuilder::new() | |
.with_vsync(true) | |
.with_gl(api_request); | |
gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop) | |
}; | |
info!("Window created!"); | |
// Initialize rendering | |
let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into(); | |
let pso = factory.create_pipeline_simple( | |
include_bytes!("../assets/shaders/gles2/sprite.vert"), | |
include_bytes!("../assets/shaders/gles2/sprite.frag"), | |
pipe::new() | |
).expect("Failed to create pipeline state object"); | |
info!("Pipeline created."); | |
let upload_buffer: Buffer<_, Vertex> = factory.create_upload_buffer(NUM_VERTICES) | |
.expect("Failed to create upload buffer"); | |
let vertex_buffer: Buffer<_, Vertex> = | |
factory.create_buffer( | |
NUM_VERTICES, | |
gfx::buffer::Role::Vertex, | |
gfx::memory::Usage::Data, | |
gfx::TRANSFER_DST).unwrap(); | |
let index_buffer: IndexBuffer<_> = { | |
let mut indices: Vec<u32> = vec![0u32; NUM_INDICES]; | |
let mut vertex_offset = 0; | |
for quad in indices.as_mut_slice().chunks_mut(6) { | |
quad[0] = vertex_offset + 0; | |
quad[1] = vertex_offset + 3; | |
quad[2] = vertex_offset + 2; | |
quad[3] = vertex_offset + 0; | |
quad[4] = vertex_offset + 2; | |
quad[5] = vertex_offset + 1; | |
vertex_offset += 4; | |
} | |
factory.create_index_buffer(indices.as_slice()) | |
}; | |
let mut data = pipe::Data { | |
vbuf: vertex_buffer.clone(), | |
model: Matrix4::identity().into(), | |
view: Matrix4::identity().into(), | |
projection: projection_from_window_dimensions(&window), | |
out: main_color | |
}; | |
// let mut mvp = Mvp { | |
// model: cgmath::Matrix4::identity().into(), | |
// view: cgmath::Matrix4::identity().into(), | |
// projection: projection_from_window_dimensions(&window), | |
// }; | |
// encoder.update_constant_buffer(&data.mvp, &mvp); | |
let mut slice = gfx::Slice { | |
start: 0u32, | |
end: 0u32, | |
base_vertex: 0, | |
instances: None, | |
buffer: index_buffer | |
}; | |
let mut num_sprites = 0usize; | |
let mut num_columns = 100usize; | |
let (mut sprite_size, mut sprite_gap) = { | |
let (width, _) = get_dimensions(&window); | |
calculate_sprite_size(width, num_columns) | |
}; | |
let mut frames = 0; | |
let mut beginning = time::Instant::now(); | |
let mut every_second = time::Instant::now(); | |
let mut fast_time = time::Instant::now(); | |
let mut t_time = 0.0f32; | |
let mut running = true; | |
info!("Game loop starting..."); | |
while running { | |
info!("Polling events..."); | |
events_loop.poll_events(|ev| { | |
if let glutin::Event::WindowEvent { event: glutin::WindowEvent::Closed, .. } = ev { | |
running = false; | |
} else if let glutin::Event::WindowEvent { event: glutin::WindowEvent::Resized(width, height), .. } = ev { | |
gfx_window_glutin::update_views(&window, &mut data.out, &mut main_depth); | |
data.projection = projection_from_window_dimensions(&window); | |
// encoder.update_constant_buffer(&data.mvp, &mvp); | |
let (width, _) = get_dimensions(&window); | |
let (size, gap) = calculate_sprite_size(width, num_columns); | |
sprite_size = size; | |
sprite_gap = gap; | |
} | |
}); | |
// Each second | |
let now = time::Instant::now(); | |
if now.duration_since(every_second).as_secs() >= 1 { | |
info!("FPS: {}", frames); | |
info!("Number of sprites: {}", num_sprites); | |
frames = 0; | |
every_second = now.clone(); | |
} | |
if num_sprites < MAX_SPRITES - 10 { | |
num_sprites += 10; | |
} | |
if t_time < f32::MAX - 2.0 { | |
t_time += 0.16; | |
} else { | |
t_time = 0.0; | |
} | |
// Fill vertex buffer | |
info!("Going to fill the vertex buffer..."); | |
{ | |
// TODO: Implement ring buffers | |
// Fill upload buffer | |
{ | |
info!("Creating upload buffer mapper..."); | |
let mut writer = factory.write_mapping(&upload_buffer).unwrap(); | |
let mut vertices: &mut [Vertex] = &mut writer; | |
info!("Writing some data into it..."); | |
let mut vertex_index = 0; | |
for i in 0..num_sprites { | |
let col = (i % num_columns) as f32; | |
let row = (i as f32 / num_columns as f32).floor(); | |
let position = [ | |
col * sprite_size + col * sprite_gap, | |
row * sprite_size + row * sprite_gap | |
]; | |
let mut quad_vertices = create_quad(position, [sprite_size, sprite_size], [1.0, 1.0, 1.0]); | |
let rotation = Matrix4::from_axis_angle(Vector3::unit_z(), Rad((0.05 * t_time).sin() * (f32::consts::PI * 2.0))); | |
transform_vertex(&mut quad_vertices[0], &rotation); | |
transform_vertex(&mut quad_vertices[1], &rotation); | |
transform_vertex(&mut quad_vertices[2], &rotation); | |
transform_vertex(&mut quad_vertices[3], &rotation); | |
vertices[vertex_index + 0] = quad_vertices[0]; | |
vertices[vertex_index + 1] = quad_vertices[1]; | |
vertices[vertex_index + 2] = quad_vertices[2]; | |
vertices[vertex_index + 3] = quad_vertices[3]; | |
vertex_index += 4; | |
info!("{} vertices written...", vertex_index); | |
} | |
} | |
// Copy upload buffer to vertex buffer | |
info!("Copying upload buffer to vertex buffer..."); | |
encoder.copy_buffer(&upload_buffer, &vertex_buffer, 0, 0, (num_sprites * 4) as usize); | |
slice.end = num_sprites as u32 * 6; | |
} | |
info!("Clear, draw, swap..."); | |
encoder.clear(&data.out, [0.1, 0.07, 0.01, 1.0]); | |
encoder.draw(&slice, &pso, &data); | |
encoder.flush(&mut device); | |
window.swap_buffers().unwrap(); | |
device.cleanup(); | |
frames += 1; | |
} | |
println!("Hello, world!"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment