Implement models and a sun/moon

This commit is contained in:
Thinkofname 2016-03-27 23:31:57 +01:00
parent 93c58aad89
commit 2b26f841d0
11 changed files with 573 additions and 8 deletions

View File

@ -46,9 +46,15 @@ pub fn draw_arrays(ty: DrawType, offset: usize, count: usize) {
}
}
pub fn draw_elements(ty: DrawType, count: usize, dty: Type, offset: usize) {
pub fn draw_elements(ty: DrawType, count: i32, dty: Type, offset: usize) {
unsafe {
gl::DrawElements(ty, count as i32, dty, offset as *const gl::types::GLvoid);
gl::DrawElements(ty, count, dty, offset as *const gl::types::GLvoid);
}
}
pub fn multi_draw_elements(ty: DrawType, count: &[i32], dty: Type, offsets: &[usize]) {
unsafe {
gl::MultiDrawElements(ty, count.as_ptr(), dty, offsets.as_ptr() as *const _, count.len() as i32);
}
}
@ -530,12 +536,25 @@ impl Uniform {
}
}
pub fn set_float_mutli_raw(&self, data: *const f32, len: usize) {
unsafe {
gl::Uniform4fv(self.0, len as i32, data);
}
}
pub fn set_matrix4(&self, m: &::cgmath::Matrix4<f32>) {
use cgmath::Matrix;
unsafe {
gl::UniformMatrix4fv(self.0, 1, false as u8, m.as_ptr());
}
}
pub fn set_matrix4_multi(&self, m: &[::cgmath::Matrix4<f32>]) {
use cgmath::Matrix;
unsafe {
gl::UniformMatrix4fv(self.0, m.len() as i32, false as u8, m.as_ptr() as *const _); // TODO: Most likely isn't safe
}
}
}
pub struct Attribute(i32);

View File

@ -51,6 +51,7 @@ pub struct Renderer {
pub resources: Arc<RwLock<resources::Manager>>,
textures: Arc<RwLock<TextureManager>>,
pub ui: ui::UIState,
pub model: model::Manager,
gl_texture: gl::Texture,
texture_layers: usize,
@ -185,13 +186,13 @@ impl Renderer {
// UI
// Line Drawer
// Models
// Clouds
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
Renderer {
resource_version: version,
model: model::Manager::new(&greg),
textures: textures,
ui: ui,
resources: res,
@ -237,6 +238,8 @@ impl Renderer {
self.resource_version = rm.version();
trace!("Updating textures to {}", self.resource_version);
self.textures.write().unwrap().update_textures(self.resource_version);
self.model.rebuild_models(self.resource_version, &self.textures);
}
}
@ -306,13 +309,14 @@ impl Renderer {
if solid.count > 0 {
self.chunk_shader.offset.set_int3(pos.0, pos.1 * 4096, pos.2);
solid.array.bind();
gl::draw_elements(gl::TRIANGLES, solid.count, self.element_buffer_type, 0);
gl::draw_elements(gl::TRIANGLES, solid.count as i32, self.element_buffer_type, 0);
}
}
}
// Line rendering
// Model rendering
self.model.draw(&self.frustum, &self.perspective_matrix, &self.camera_matrix, self.light_level, self.sky_offset);
// Cloud rendering
// Trans chunk rendering
@ -346,7 +350,7 @@ impl Renderer {
if trans.count > 0 {
self.chunk_shader_alpha.offset.set_int3(pos.0, pos.1 * 4096, pos.2);
trans.array.bind();
gl::draw_elements(gl::TRIANGLES, trans.count, self.element_buffer_type, 0);
gl::draw_elements(gl::TRIANGLES, trans.count as i32, self.element_buffer_type, 0);
}
}
}

View File

@ -0,0 +1,320 @@
use super::glsl;
use super::shaders;
use gl;
use cgmath::{Point3, Matrix4, SquareMatrix};
use collision::{self, Frustum, Sphere};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::sync::{Arc, RwLock};
use types::hash::FNVHash;
use byteorder::{WriteBytesExt, NativeEndian};
pub struct Manager {
collections: Vec<Collection>,
index_buffer: gl::Buffer,
index_type: gl::Type,
max_index: usize,
}
pub const DEFAULT: CollectionKey = CollectionKey(0);
pub const SUN: CollectionKey = CollectionKey(1);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct CollectionKey(usize);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModelKey(CollectionKey, usize);
impl Manager {
pub fn new(greg: &glsl::Registry) -> Manager {
let mut m = Manager {
collections: vec![],
index_buffer: gl::Buffer::new(),
index_type: gl::UNSIGNED_SHORT,
max_index: 0,
};
m.add_collection(
&greg.get("model_vertex"),
&greg.get("model_frag"),
gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA
);
m.add_collection(
&greg.get("sun_vertex"),
&greg.get("sun_frag"),
gl::SRC_ALPHA, gl::ONE_FACTOR
);
m
}
pub fn add_collection(&mut self, vert: &str, frag: &str, blend_s: gl::Factor, blend_d: gl::Factor) -> CollectionKey {
let collection = Collection {
shader: ModelShader::new_manual(vert, frag),
models: HashMap::with_hasher(BuildHasherDefault::default()),
blend_s: blend_s,
blend_d: blend_d,
next_id: 0,
};
self.collections.push(collection);
CollectionKey(self.collections.len())
}
pub fn get_model(&mut self, key: ModelKey) -> Option<&mut Model> {
let collection = &mut self.collections[(key.0).0];
collection.models.get_mut(&key)
}
pub fn create_model(&mut self, ckey: CollectionKey, parts: Vec<Vec<Vertex>>) -> ModelKey {
let array = gl::VertexArray::new();
array.bind();
self.index_buffer.bind(gl::ELEMENT_ARRAY_BUFFER);
let buffer = gl::Buffer::new();
buffer.bind(gl::ARRAY_BUFFER);
let mut model = {
let collection = &mut self.collections[ckey.0];
collection.shader.program.use_program();
collection.shader.position.enable();
collection.shader.texture_info.enable();
collection.shader.texture_offset.enable();
collection.shader.color.enable();
collection.shader.id.enable();
collection.shader.position.vertex_pointer(3, gl::FLOAT, false, 36, 0);
collection.shader.texture_info.vertex_pointer(4, gl::UNSIGNED_SHORT, false, 36, 12);
collection.shader.texture_offset.vertex_pointer_int(3, gl::SHORT, 36, 20);
collection.shader.color.vertex_pointer(4, gl::UNSIGNED_BYTE, true, 36, 28);
collection.shader.id.vertex_pointer_int(4, gl::UNSIGNED_BYTE, 36, 32);
let mut model = Model {
// For culling only
x: 0.0,
y: 0.0,
z: 0.0,
radius: 0.0,
// Per a part
matrix: Vec::with_capacity(parts.len()),
colors: Vec::with_capacity(parts.len()),
block_light: 15.0,
sky_light: 15.0,
array: array,
buffer: buffer,
buffer_size: 0,
count: 0,
counts: Vec::with_capacity(parts.len()),
offsets: Vec::with_capacity(parts.len()),
verts: vec![],
};
for (i, part) in parts.into_iter().enumerate() {
model.matrix.push(Matrix4::identity());
model.colors.push([1.0, 1.0, 1.0, 1.0]);
model.counts.push(((part.len() / 4) * 6) as i32);
model.offsets.push((model.verts.len() / 4) * 6);
for mut pp in part {
pp.id = i as u8;
model.verts.push(pp);
}
}
model
};
Self::rebuild_model(&mut model);
if self.max_index < model.count {
let (data, ty) = super::generate_element_buffer(model.count);
self.index_buffer.bind(gl::ELEMENT_ARRAY_BUFFER);
self.index_buffer.set_data(gl::ELEMENT_ARRAY_BUFFER, &data, gl::DYNAMIC_DRAW);
self.max_index = model.count;
self.index_type = ty;
}
let collection = &mut self.collections[ckey.0];
let key = ModelKey(ckey, collection.next_id);
collection.next_id += 1;
collection.models.insert(key, model);
key
}
pub fn remove_model(&mut self, key: ModelKey) {
let collection = &mut self.collections[(key.0).0];
collection.models.remove(&key);
}
fn rebuild_model(model: &mut Model) {
model.array.bind();
model.count = (model.verts.len() / 4) * 6;
let mut buffer = Vec::with_capacity(36 * model.verts.len());
for vert in &model.verts {
let _ = buffer.write_f32::<NativeEndian>(vert.x);
let _ = buffer.write_f32::<NativeEndian>(vert.y);
let _ = buffer.write_f32::<NativeEndian>(vert.z);
let _ = buffer.write_u16::<NativeEndian>(vert.texture.get_x() as u16);
let _ = buffer.write_u16::<NativeEndian>(vert.texture.get_y() as u16);
let _ = buffer.write_u16::<NativeEndian>(vert.texture.get_width() as u16);
let _ = buffer.write_u16::<NativeEndian>(vert.texture.get_height() as u16);
let _ = buffer.write_i16::<NativeEndian>(((vert.texture.get_width() as f64) * 16.0 * vert.texture_x) as i16);
let _ = buffer.write_i16::<NativeEndian>(((vert.texture.get_height() as f64) * 16.0 * vert.texture_y) as i16);
let _ = buffer.write_i16::<NativeEndian>(vert.texture.atlas as i16);
let _ = buffer.write_i16::<NativeEndian>(0);
let _ = buffer.write_u8(vert.r);
let _ = buffer.write_u8(vert.g);
let _ = buffer.write_u8(vert.b);
let _ = buffer.write_u8(vert.a);
let _ = buffer.write_u8(vert.id);
let _ = buffer.write_u8(0);
let _ = buffer.write_u8(0);
let _ = buffer.write_u8(0);
}
model.buffer.bind(gl::ARRAY_BUFFER);
if buffer.len() < model.buffer_size {
model.buffer.re_set_data(gl::ARRAY_BUFFER, &buffer);
} else {
model.buffer.set_data(gl::ARRAY_BUFFER, &buffer, gl::DYNAMIC_DRAW);
model.buffer_size = buffer.len();
}
}
pub fn rebuild_models(&mut self, version: usize, textures: &Arc<RwLock<super::TextureManager>>) {
for collection in &mut self.collections {
for (_, model) in &mut collection.models {
for vert in &mut model.verts {
vert.texture = if vert.texture.version == version {
vert.texture.clone()
} else {
let mut new = super::Renderer::get_texture(&textures, &vert.texture.name);
new.rel_x = vert.texture.rel_x;
new.rel_y = vert.texture.rel_y;
new.rel_width = vert.texture.rel_width;
new.rel_height = vert.texture.rel_height;
new.is_rel = vert.texture.is_rel;
new
};
}
Self::rebuild_model(model);
}
}
}
pub fn draw(&mut self, frustum: &Frustum<f32>, perspective_matrix: &Matrix4<f32>, camera_matrix: &Matrix4<f32>, sky_offset: f32, light_level: f32) {
let m = if self.index_type == gl::UNSIGNED_SHORT {
2
} else {
4
};
gl::enable(gl::BLEND);
for collection in &self.collections {
collection.shader.program.use_program();
collection.shader.perspective_matrix.set_matrix4(perspective_matrix);
collection.shader.camera_matrix.set_matrix4(camera_matrix);
collection.shader.texture.set_int(0);
collection.shader.sky_offset.set_float(sky_offset);
collection.shader.light_level.set_float(light_level);
gl::blend_func(collection.blend_s, collection.blend_d);
for (_, model) in &collection.models {
if model.radius > 0.0 && frustum.contains(Sphere {
center: Point3::new(model.x, -model.y, model.z),
radius: model.radius
}) == collision::Relation::Out {
continue;
}
model.array.bind();
collection.shader.lighting.set_float2(model.block_light, model.sky_light);
if model.counts.len() > 1 {
let mut offsets = model.offsets.clone();
for offset in &mut offsets {
*offset *= m;
}
collection.shader.model_matrix.set_matrix4_multi(&model.matrix);
collection.shader.color_mul.set_float_mutli_raw(model.colors.as_ptr() as *const _, model.colors.len());
gl::multi_draw_elements(gl::TRIANGLES, &model.counts, self.index_type, &offsets);
} else {
collection.shader.model_matrix.set_matrix4_multi(&model.matrix);
collection.shader.color_mul.set_float_mutli_raw(model.colors.as_ptr() as *const _, model.colors.len());
gl::draw_elements(gl::TRIANGLES, model.counts[0], self.index_type, model.offsets[0] * m);
}
}
}
gl::disable(gl::BLEND);
}
}
struct Collection {
shader: ModelShader,
models: HashMap<ModelKey, Model, BuildHasherDefault<FNVHash>>,
blend_s: gl::Factor,
blend_d: gl::Factor,
next_id: usize,
}
pub struct Model {
// For culling only
pub x: f32,
pub y: f32,
pub z: f32,
pub radius: f32,
// Per a part
pub matrix: Vec<Matrix4<f32>>,
pub colors: Vec<[f32; 4]>,
pub block_light: f32,
pub sky_light: f32,
array: gl::VertexArray,
buffer: gl::Buffer,
buffer_size: usize,
count: usize,
counts: Vec<i32>,
offsets: Vec<usize>,
pub verts: Vec<Vertex>,
}
#[derive(Clone)]
pub struct Vertex {
pub x: f32,
pub y: f32,
pub z: f32,
pub texture: super::Texture,
pub texture_x: f64,
pub texture_y: f64,
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
pub id: u8,
}
init_shader! {
Program ModelShader {
vert = "model_vertex",
frag = "model_frag",
attribute = {
position => "aPosition",
texture_info => "aTextureInfo",
texture_offset => "aTextureOffset",
color => "aColor",
id => "id",
},
uniform = {
perspective_matrix => "perspectiveMatrix",
camera_matrix => "cameraMatrix",
model_matrix => "modelMatrix[]",
texture => "textures",
light_level => "lightLevel",
sky_offset => "skyOffset",
lighting => "lighting",
color_mul => "colorMul[]",
},
}
}

View File

@ -16,8 +16,7 @@ use render::glsl;
use gl;
pub fn add_shaders(reg: &mut glsl::Registry) {
reg.register("lookup_texture",
include_str!("shaders/lookup_texture.glsl"));
reg.register("lookup_texture", include_str!("shaders/lookup_texture.glsl"));
reg.register("get_light", include_str!("shaders/get_light.glsl"));
reg.register("ui_vertex", include_str!("shaders/ui_vertex.glsl"));
@ -28,6 +27,12 @@ pub fn add_shaders(reg: &mut glsl::Registry) {
reg.register("trans_vertex", include_str!("shaders/trans_vertex.glsl"));
reg.register("trans_frag", include_str!("shaders/trans_frag.glsl"));
reg.register("model_vertex", include_str!("shaders/model_vertex.glsl"));
reg.register("model_frag", include_str!("shaders/model_frag.glsl"));
reg.register("sun_vertex", include_str!("shaders/sun_vertex.glsl"));
reg.register("sun_frag", include_str!("shaders/sun_frag.glsl"));
}
macro_rules! get_shader {
@ -69,9 +74,15 @@ macro_rules! init_shader {
}
impl $name {
#[allow(dead_code)]
pub fn new(reg: &glsl::Registry) -> $name {
let v = get_shader!(reg, $vert $(,stringify!($vdef))*);
let f = get_shader!(reg, $frag $(,stringify!($fdef))*);
$name::new_manual(&v, &f)
}
#[allow(dead_code)]
pub fn new_manual(v: &str, f: &str) -> $name {
let shader = shaders::create_program(&v, &f);
$name {
$(

View File

@ -0,0 +1,21 @@
uniform sampler2DArray textures;
uniform vec4 colorMul[10];
in vec4 vColor;
in vec4 vTextureInfo;
in vec2 vTextureOffset;
in float vAtlas;
in vec3 vLighting;
in float vID;
out vec4 fragColor;
#include lookup_texture
void main() {
vec4 col = atlasTexture();
if (col.a <= 0.05) discard;
col *= vColor;
col.rgb *= vLighting;
fragColor = col * colorMul[int(vID)];
}

View File

@ -0,0 +1,34 @@
in vec3 aPosition;
in vec4 aTextureInfo;
in ivec3 aTextureOffset;
in vec4 aColor;
in int id;
uniform mat4 perspectiveMatrix;
uniform mat4 cameraMatrix;
uniform mat4 modelMatrix[10];
uniform float lightLevel;
uniform float skyOffset;
uniform vec2 lighting;
out vec4 vColor;
out vec4 vTextureInfo;
out vec2 vTextureOffset;
out float vAtlas;
out float vID;
out vec3 vLighting;
#include get_light
void main() {
vec3 pos = vec3(aPosition.x, -aPosition.y, aPosition.z);
gl_Position = perspectiveMatrix * cameraMatrix * modelMatrix[id] * vec4(pos, 1.0);
vColor = aColor;
vTextureInfo = aTextureInfo;
vTextureOffset = aTextureOffset.xy / 16.0;
vAtlas = aTextureOffset.z;
vID = float(id);
vLighting = getLight(lighting);
}

View File

@ -0,0 +1,17 @@
uniform sampler2DArray textures;
uniform vec4 colorMul[10];
in vec4 vTextureInfo;
in vec2 vTextureOffset;
in float vAtlas;
in float vID;
out vec4 fragColor;
#include lookup_texture
void main() {
vec4 col = atlasTexture();
if (col.a <= 0.05) discard;
fragColor = col * colorMul[int(vID)];
}

View File

@ -0,0 +1,27 @@
in vec3 aPosition;
in vec4 aTextureInfo;
in ivec3 aTextureOffset;
in vec4 aColor;
in int id;
uniform mat4 perspectiveMatrix;
uniform mat4 cameraMatrix;
uniform mat4 modelMatrix[10];
uniform float lightLevel;
uniform float skyOffset;
uniform vec2 lighting;
out vec4 vTextureInfo;
out vec2 vTextureOffset;
out float vAtlas;
out float vID;
void main() {
vec3 pos = vec3(aPosition.x, -aPosition.y, aPosition.z);
gl_Position = perspectiveMatrix * cameraMatrix * modelMatrix[id] * vec4(pos, 1.0);
vTextureInfo = aTextureInfo;
vTextureOffset = aTextureOffset.xy / 16.0;
vAtlas = aTextureOffset.z;
vID = float(id);
}

View File

@ -170,7 +170,7 @@ impl UIState {
} else {
self.buffer.re_set_data(gl::ARRAY_BUFFER, &self.data);
}
gl::draw_elements(gl::TRIANGLES, self.count, self.index_type, 0);
gl::draw_elements(gl::TRIANGLES, self.count as i32, self.index_type, 0);
}
gl::disable(gl::BLEND);

View File

@ -34,6 +34,8 @@ use collision::Aabb;
use sdl2::keyboard::Keycode;
use types::Gamemode;
mod sun;
pub struct Server {
conn: Option<protocol::Conn>,
read_queue: Option<mpsc::Receiver<Result<packet::Packet, protocol::Error>>>,
@ -64,6 +66,8 @@ pub struct Server {
pressed_keys: HashMap<Keycode, bool, BuildHasherDefault<FNVHash>>,
tick_timer: f64,
entity_tick_timer: f64,
sun_model: Option<sun::SunModel>,
}
macro_rules! handle_packet {
@ -222,6 +226,7 @@ impl Server {
tick_timer: 0.0,
entity_tick_timer: 0.0,
sun_model: None,
}
}
@ -235,6 +240,10 @@ impl Server {
self.version = version;
self.world.flag_dirty_all();
}
// TODO: Check if the world type actually needs a sun
if self.sun_model.is_none() {
self.sun_model = Some(sun::SunModel::new(renderer));
}
self.entity_tick(renderer, delta);
@ -246,6 +255,10 @@ impl Server {
self.update_time(renderer, delta);
if let Some(sun_model) = self.sun_model.as_mut() {
sun_model.tick(renderer, self.world_time, self.world_age);
}
// Copy to camera
if let Some(player) = self.player {
let position = self.entities.get_component(player, self.position).unwrap();
@ -295,6 +308,9 @@ impl Server {
pub fn remove(&mut self, renderer: &mut render::Renderer) {
self.entities.remove_all_entities(&mut self.world, renderer);
if let Some(mut sun_model) = self.sun_model.take() {
sun_model.remove(renderer);
}
}
fn update_time(&mut self, renderer: &mut render::Renderer, delta: f64) {

96
src/server/sun.rs Normal file
View File

@ -0,0 +1,96 @@
use render;
use render::model;
use cgmath::{Vector3, Matrix4, Decomposed, Rotation3, Rad, Angle, Quaternion};
pub struct SunModel {
sun: model::ModelKey,
moon: model::ModelKey,
last_phase: i32,
}
const SIZE: f32 = 50.0;
impl SunModel {
pub fn new(renderer: &mut render::Renderer) -> SunModel {
SunModel {
sun: SunModel::generate_sun(renderer),
moon: SunModel::generate_moon(renderer, 0),
last_phase: 0,
}
}
pub fn tick(&mut self, renderer: &mut render::Renderer, world_time: f64, world_age: i64) {
use std::f64::consts::PI;
let phase = ((world_age / 24000) % 8) as i32;
if phase != self.last_phase {
renderer.model.remove_model(self.moon);
self.moon = SunModel::generate_moon(renderer, phase);
self.last_phase = phase;
}
let time = world_time / 12000.0;
let ox = (time * PI).cos() * 300.0;
let oy = (time * PI).sin() * 300.0;
{
let sun = renderer.model.get_model(self.sun).unwrap();
sun.matrix[0] = Matrix4::from(Decomposed {
scale: 1.0,
rot: Quaternion::from_angle_z(Rad::new(-(time * PI) as f32)),
disp: Vector3::new(
(renderer.camera.pos.x + ox) as f32,
-(renderer.camera.pos.y + oy) as f32,
renderer.camera.pos.z as f32,
),
});
}
{
let moon = renderer.model.get_model(self.moon).unwrap();
moon.matrix[0] = Matrix4::from(Decomposed {
scale: 1.0,
rot: Quaternion::from_angle_z(Rad::new((PI - (time * PI)) as f32)),
disp: Vector3::new(
(renderer.camera.pos.x - ox) as f32,
-(renderer.camera.pos.y - oy) as f32,
renderer.camera.pos.z as f32,
),
});
}
}
pub fn remove(&mut self, renderer: &mut render::Renderer) {
renderer.model.remove_model(self.sun);
renderer.model.remove_model(self.moon);
}
pub fn generate_sun(renderer: &mut render::Renderer) -> model::ModelKey {
let tex = render::Renderer::get_texture(renderer.get_textures_ref(), "environment/sun");
renderer.model.create_model(
model::SUN,
vec![vec![
model::Vertex{x: 0.0, y: -SIZE, z: -SIZE, texture_x: 0.0, texture_y: 1.0, texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0},
model::Vertex{x: 0.0, y: SIZE, z: -SIZE, texture_x: 0.0, texture_y: 0.0, texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0},
model::Vertex{x: 0.0, y: -SIZE, z: SIZE, texture_x: 1.0, texture_y: 1.0, texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0},
model::Vertex{x: 0.0, y: SIZE, z: SIZE, texture_x: 1.0, texture_y: 0.0, texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0}
]]
)
}
pub fn generate_moon(renderer: &mut render::Renderer, phase: i32) -> model::ModelKey {
let tex = render::Renderer::get_texture(renderer.get_textures_ref(), "environment/moon_phases");
let mpx = (phase % 4) as f64 * (1.0 / 4.0);
let mpy = (phase / 4) as f64 * (1.0 / 2.0);
renderer.model.create_model(
model::SUN,
vec![vec![
model::Vertex{x: 0.0, y: -SIZE, z: -SIZE, texture_x: mpx, texture_y: mpy + (1.0 / 2.0), texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0},
model::Vertex{x: 0.0, y: SIZE, z: -SIZE, texture_x: mpx, texture_y: mpy, texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0},
model::Vertex{x: 0.0, y: -SIZE, z: SIZE, texture_x: mpx + (1.0 / 4.0), texture_y: mpy + (1.0 / 2.0), texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0},
model::Vertex{x: 0.0, y: SIZE, z: SIZE, texture_x: mpx + (1.0 / 4.0), texture_y: mpy, texture: tex.clone(), r: 255, g: 255, b: 255, a: 0, id: 0}
]]
)
}
}