stevenarella/src/render/mod.rs

1567 lines
50 KiB
Rust
Raw Normal View History

2016-03-16 14:25:35 -04:00
// Copyright 2016 Matthew Collins
2015-09-17 11:21:56 -04:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2015-09-17 11:04:25 -04:00
mod atlas;
pub mod glsl;
2015-10-06 18:49:52 -04:00
#[macro_use]
pub mod shaders;
2016-04-01 15:00:13 -04:00
pub mod clouds;
pub mod model;
pub mod ui;
2015-09-17 11:04:25 -04:00
use crate::gl;
use crate::resources;
use crate::world;
use byteorder::{NativeEndian, WriteBytesExt};
use cgmath::prelude::*;
use image::{GenericImage, GenericImageView};
use log::{error, trace};
use std::collections::HashMap;
use std::io::Write;
use std::sync::{Arc, RwLock};
2015-09-17 11:04:25 -04:00
use crate::types::hash::FNVHash;
use std::hash::BuildHasherDefault;
2016-04-07 10:55:03 -04:00
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::mpsc;
use std::thread;
2016-03-24 11:39:57 -04:00
2015-09-17 11:04:25 -04:00
const ATLAS_SIZE: usize = 1024;
2016-03-18 13:16:03 -04:00
pub struct Camera {
pub pos: cgmath::Point3<f64>,
pub yaw: f64,
pub pitch: f64,
}
2015-09-17 11:04:25 -04:00
pub struct Renderer {
2015-10-07 14:36:59 -04:00
resource_version: usize,
pub resources: Arc<RwLock<resources::Manager>>,
textures: Arc<RwLock<TextureManager>>,
pub ui: ui::UIState,
2016-03-27 18:31:57 -04:00
pub model: model::Manager,
pub clouds: Option<clouds::Clouds>,
2015-09-17 11:04:25 -04:00
2015-10-07 14:36:59 -04:00
gl_texture: gl::Texture,
texture_layers: usize,
2015-09-17 11:04:25 -04:00
2016-03-16 13:33:06 -04:00
chunk_shader: ChunkShader,
chunk_shader_alpha: ChunkShaderAlpha,
2016-03-18 11:19:21 -04:00
trans_shader: TransShader,
2016-03-16 13:33:06 -04:00
2016-03-19 13:34:12 -04:00
element_buffer: gl::Buffer,
element_buffer_size: usize,
element_buffer_type: gl::Type,
pub camera: Camera,
2016-03-16 13:33:06 -04:00
perspective_matrix: cgmath::Matrix4<f32>,
2016-03-24 17:47:11 -04:00
camera_matrix: cgmath::Matrix4<f32>,
pub frustum: collision::Frustum<f32>,
pub view_vector: cgmath::Vector3<f32>,
2016-03-16 13:33:06 -04:00
2016-03-24 19:27:22 -04:00
pub frame_id: u32,
2016-03-16 13:33:06 -04:00
trans: Option<TransInfo>,
pub width: u32,
pub height: u32,
// Light renderering
pub light_level: f32,
pub sky_offset: f32,
2016-04-07 10:55:03 -04:00
skin_request: mpsc::Sender<String>,
skin_reply: mpsc::Receiver<(String, Option<image::DynamicImage>)>,
2015-09-17 11:04:25 -04:00
}
#[derive(Default)]
2016-03-24 17:13:24 -04:00
pub struct ChunkBuffer {
2016-03-19 13:34:12 -04:00
solid: Option<ChunkRenderInfo>,
trans: Option<ChunkRenderInfo>,
}
2016-03-24 17:13:24 -04:00
impl ChunkBuffer {
pub fn new() -> ChunkBuffer {
Default::default()
}
2016-03-24 17:13:24 -04:00
}
2016-03-19 13:34:12 -04:00
struct ChunkRenderInfo {
array: gl::VertexArray,
buffer: gl::Buffer,
buffer_size: usize,
count: usize,
}
2016-03-16 13:33:06 -04:00
init_shader! {
Program ChunkShader {
vert = "chunk_vertex",
frag = "chunk_frag",
attribute = {
required position => "aPosition",
required texture_info => "aTextureInfo",
required texture_offset => "aTextureOffset",
required color => "aColor",
required lighting => "aLighting",
2016-03-16 13:33:06 -04:00
},
uniform = {
required perspective_matrix => "perspectiveMatrix",
required camera_matrix => "cameraMatrix",
required offset => "offset",
required texture => "textures",
required light_level => "lightLevel",
required sky_offset => "skyOffset",
2016-03-16 13:33:06 -04:00
},
}
}
init_shader! {
Program ChunkShaderAlpha {
vert = "chunk_vertex",
frag = "chunk_frag", #alpha
attribute = {
required position => "aPosition",
required texture_info => "aTextureInfo",
required texture_offset => "aTextureOffset",
required color => "aColor",
required lighting => "aLighting",
2016-03-16 13:33:06 -04:00
},
uniform = {
required perspective_matrix => "perspectiveMatrix",
required camera_matrix => "cameraMatrix",
required offset => "offset",
required texture => "textures",
required light_level => "lightLevel",
required sky_offset => "skyOffset",
2016-03-16 13:33:06 -04:00
},
}
}
2015-09-17 11:04:25 -04:00
impl Renderer {
Use web-sys for web backend (#444) A small step for #446 🕸️ Web support, use web-sys to interface to the web. Previously, we would try to use glutin on the web, which is not supported; now glutin is only used on native: fixes #171 could not find Context in platform_impl. winit is still used on both, but the GL context is created with web-sys and glow (on the web), and created with glutin and used with glow (on native). stdweb is no longer used, being replaced by web-sys. Substantial refactoring to allow reusing the code between web/native: * settings: use VirtualKeyCode from winit, not reexported from glutin * std_or_web: remove broken localstoragefs/stdweb, add File placeholder * render: disable skin_thread on wasm since we don't have threads * gl: use glow types in gl wrapper (integers in native, but Web*Key in web) * gl: web-sys WebGlUniformLocation does not implement Copy trait, so glow::UniformLocation doesn't so gl::Uniform can't * gl: refactor context initialization, pass glow::Context to gl::init for consistency between native/web * gl: update to glow with panicking tex_image_2d_multisample web-sys wrapper * glsl: use shader version in GLSL for WebGL 2 and OpenGL 3.2 * shaders: add explicit float/int type conversions, required for WebGL * shaders: specify mediump precision, required for WebGL * shaders: specify fragment shader output locations for WebGL * main: refactor handle_window_event to take a winit window, not glutin context * main: handle resize outside of handle_window_event since it updates the glutin window (the only event which does this) * main: use winit events in handle_window_event not reexported glutin events * main: refactor game loop handling into tick_all() * main: create winit window for WebGL, and use winit_window from glutin * main: restore console_error_panic_hook, mistakingly removed in (#260) * main: remove force setting env RUST_BACKTRACE=1, no longer can set env on web * www: index.js: fix wasm import path * www: npm update, npm audit fix * www: update readme to link to status on #446 🕸️ Web support
2020-12-26 16:42:37 -05:00
pub fn new(res: Arc<RwLock<resources::Manager>>, shader_version: &str) -> Renderer {
let version = { res.read().unwrap().version() };
2015-10-07 14:36:59 -04:00
let tex = gl::Texture::new();
tex.bind(gl::TEXTURE_2D_ARRAY);
tex.image_3d(
gl::TEXTURE_2D_ARRAY,
0,
ATLAS_SIZE as u32,
ATLAS_SIZE as u32,
1,
gl::RGBA,
gl::UNSIGNED_BYTE,
&[0; ATLAS_SIZE * ATLAS_SIZE * 4],
);
2015-10-07 14:36:59 -04:00
tex.set_parameter(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_MAG_FILTER, gl::NEAREST);
tex.set_parameter(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_MIN_FILTER, gl::NEAREST);
tex.set_parameter(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE);
tex.set_parameter(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE);
2016-04-07 10:55:03 -04:00
let (textures, skin_req, skin_reply) = TextureManager::new(res.clone());
let textures = Arc::new(RwLock::new(textures));
2015-10-07 14:36:59 -04:00
Use web-sys for web backend (#444) A small step for #446 🕸️ Web support, use web-sys to interface to the web. Previously, we would try to use glutin on the web, which is not supported; now glutin is only used on native: fixes #171 could not find Context in platform_impl. winit is still used on both, but the GL context is created with web-sys and glow (on the web), and created with glutin and used with glow (on native). stdweb is no longer used, being replaced by web-sys. Substantial refactoring to allow reusing the code between web/native: * settings: use VirtualKeyCode from winit, not reexported from glutin * std_or_web: remove broken localstoragefs/stdweb, add File placeholder * render: disable skin_thread on wasm since we don't have threads * gl: use glow types in gl wrapper (integers in native, but Web*Key in web) * gl: web-sys WebGlUniformLocation does not implement Copy trait, so glow::UniformLocation doesn't so gl::Uniform can't * gl: refactor context initialization, pass glow::Context to gl::init for consistency between native/web * gl: update to glow with panicking tex_image_2d_multisample web-sys wrapper * glsl: use shader version in GLSL for WebGL 2 and OpenGL 3.2 * shaders: add explicit float/int type conversions, required for WebGL * shaders: specify mediump precision, required for WebGL * shaders: specify fragment shader output locations for WebGL * main: refactor handle_window_event to take a winit window, not glutin context * main: handle resize outside of handle_window_event since it updates the glutin window (the only event which does this) * main: use winit events in handle_window_event not reexported glutin events * main: refactor game loop handling into tick_all() * main: create winit window for WebGL, and use winit_window from glutin * main: restore console_error_panic_hook, mistakingly removed in (#260) * main: remove force setting env RUST_BACKTRACE=1, no longer can set env on web * www: index.js: fix wasm import path * www: npm update, npm audit fix * www: update readme to link to status on #446 🕸️ Web support
2020-12-26 16:42:37 -05:00
let mut greg = glsl::Registry::new(shader_version);
2015-10-07 14:36:59 -04:00
shaders::add_shaders(&mut greg);
let ui = ui::UIState::new(&greg, textures.clone(), res.clone());
gl::enable(gl::DEPTH_TEST);
gl::enable(gl::CULL_FACE_FLAG);
gl::cull_face(gl::BACK);
gl::front_face(gl::CLOCK_WISE);
2015-10-06 18:49:52 -04:00
2016-03-16 13:33:06 -04:00
// Shaders
let chunk_shader = ChunkShader::new(&greg);
let chunk_shader_alpha = ChunkShaderAlpha::new(&greg);
2016-03-18 11:19:21 -04:00
let trans_shader = TransShader::new(&greg);
2015-10-06 18:49:52 -04:00
2016-03-16 13:33:06 -04:00
// UI
// Line Drawer
// Clouds
2015-10-06 18:49:52 -04:00
2015-10-07 14:36:59 -04:00
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
2016-03-27 19:50:30 -04:00
gl::depth_func(gl::LESS_OR_EQUAL);
2015-10-07 14:36:59 -04:00
#[cfg(not(target_arch = "wasm32"))]
let clouds = Some(clouds::Clouds::new(&greg, textures.clone()));
// No clouds on wasm since no geo shaders on WebGL
// TODO: setting to disable clouds on native, too, if desired
#[cfg(target_arch = "wasm32")]
let clouds = None;
2015-10-07 14:36:59 -04:00
Renderer {
resource_version: version,
2016-03-27 18:31:57 -04:00
model: model::Manager::new(&greg),
clouds,
textures,
ui,
2015-10-07 14:36:59 -04:00
resources: res,
gl_texture: tex,
texture_layers: 1,
2016-03-16 13:33:06 -04:00
chunk_shader,
chunk_shader_alpha,
trans_shader,
2016-03-16 13:33:06 -04:00
2016-03-19 13:34:12 -04:00
element_buffer: gl::Buffer::new(),
element_buffer_size: 0,
element_buffer_type: gl::UNSIGNED_BYTE,
width: 0,
height: 0,
2016-03-16 13:33:06 -04:00
2016-03-18 13:16:03 -04:00
camera: Camera {
pos: cgmath::Point3::new(0.0, 0.0, 0.0),
yaw: 0.0,
pitch: ::std::f64::consts::PI,
},
2016-03-24 17:47:11 -04:00
perspective_matrix: cgmath::Matrix4::identity(),
camera_matrix: cgmath::Matrix4::identity(),
frustum: collision::Frustum::from_matrix4(cgmath::Matrix4::identity()).unwrap(),
view_vector: cgmath::Vector3::zero(),
2016-03-16 13:33:06 -04:00
2016-03-24 19:27:22 -04:00
frame_id: 1,
2016-03-16 13:33:06 -04:00
trans: None,
light_level: 0.8,
sky_offset: 1.0,
2016-04-07 10:55:03 -04:00
skin_request: skin_req,
skin_reply,
2015-10-07 14:36:59 -04:00
}
}
2016-03-24 17:47:11 -04:00
pub fn update_camera(&mut self, width: u32, height: u32) {
2016-03-18 13:16:03 -04:00
use std::f64::consts::PI as PI64;
2016-03-24 20:25:34 -04:00
// Not a sane place to put this but it works
{
let rm = self.resources.read().unwrap();
if rm.version() != self.resource_version {
self.resource_version = rm.version();
trace!("Updating textures to {}", self.resource_version);
self.textures
.write()
.unwrap()
.update_textures(self.resource_version);
2016-03-27 18:31:57 -04:00
self.model
.rebuild_models(self.resource_version, &self.textures);
2016-03-24 20:25:34 -04:00
}
}
2015-10-07 14:36:59 -04:00
if self.height != height || self.width != width {
self.width = width;
self.height = height;
2015-10-07 14:36:59 -04:00
gl::viewport(0, 0, width as i32, height as i32);
2016-03-16 13:33:06 -04:00
let fovy = cgmath::Rad::from(cgmath::Deg(90.0_f32));
let aspect = (width as f32 / height as f32).max(1.0);
self.perspective_matrix = cgmath::Matrix4::from(cgmath::PerspectiveFov {
fovy,
aspect,
near: 0.1f32,
far: 500.0f32,
});
2016-03-16 13:33:06 -04:00
self.init_trans(width, height);
2015-10-07 14:36:59 -04:00
}
2016-03-24 17:47:11 -04:00
self.view_vector = cgmath::Vector3::new(
((self.camera.yaw - PI64 / 2.0).cos() * -self.camera.pitch.cos()) as f32,
2016-03-24 17:47:11 -04:00
(-self.camera.pitch.sin()) as f32,
(-(self.camera.yaw - PI64 / 2.0).sin() * -self.camera.pitch.cos()) as f32,
);
let camera = cgmath::Point3::new(
-self.camera.pos.x as f32,
-self.camera.pos.y as f32,
self.camera.pos.z as f32,
2016-03-24 17:47:11 -04:00
);
let camera_matrix = cgmath::Matrix4::look_at(
camera,
camera
+ cgmath::Point3::new(-self.view_vector.x, -self.view_vector.y, self.view_vector.z)
.to_vec(),
cgmath::Vector3::new(0.0, -1.0, 0.0),
2016-03-24 17:47:11 -04:00
);
self.camera_matrix = camera_matrix * cgmath::Matrix4::from_nonuniform_scale(-1.0, 1.0, 1.0);
self.frustum =
collision::Frustum::from_matrix4(self.perspective_matrix * self.camera_matrix).unwrap();
2016-03-24 17:47:11 -04:00
}
pub fn tick(
&mut self,
world: &mut world::World,
delta: f64,
width: u32,
height: u32,
physical_width: u32,
physical_height: u32,
) {
2016-03-24 17:47:11 -04:00
self.update_textures(delta);
2016-03-18 11:19:21 -04:00
let trans = self.trans.as_mut().unwrap();
trans.main.bind();
2015-10-07 14:36:59 -04:00
gl::active_texture(0);
self.gl_texture.bind(gl::TEXTURE_2D_ARRAY);
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
#[cfg(not(target_arch = "wasm32"))]
2016-03-16 13:33:06 -04:00
gl::enable(gl::MULTISAMPLE);
let time_offset = self.sky_offset * 0.9;
gl::clear_color(
(122.0 / 255.0) * time_offset,
(165.0 / 255.0) * time_offset,
(247.0 / 255.0) * time_offset,
1.0,
);
2015-09-17 11:04:25 -04:00
gl::clear(gl::ClearFlags::Color | gl::ClearFlags::Depth);
2016-03-18 11:19:21 -04:00
// Chunk rendering
2016-03-18 13:16:03 -04:00
self.chunk_shader.program.use_program();
self.chunk_shader
.perspective_matrix
.set_matrix4(&self.perspective_matrix);
self.chunk_shader
.camera_matrix
.set_matrix4(&self.camera_matrix);
2016-03-18 13:16:03 -04:00
self.chunk_shader.texture.set_int(0);
self.chunk_shader.light_level.set_float(self.light_level);
self.chunk_shader.sky_offset.set_float(self.sky_offset);
2016-03-18 11:19:21 -04:00
2016-03-24 17:47:11 -04:00
for (pos, info) in world.get_render_list() {
2016-03-19 13:34:12 -04:00
if let Some(solid) = info.solid.as_ref() {
2016-03-24 19:27:22 -04:00
if solid.count > 0 {
self.chunk_shader
.offset
.set_int3(pos.0, pos.1 * 4096, pos.2);
2016-03-24 19:27:22 -04:00
solid.array.bind();
gl::draw_elements(
gl::TRIANGLES,
solid.count as i32,
self.element_buffer_type,
0,
);
2016-03-24 19:27:22 -04:00
}
2016-03-19 13:34:12 -04:00
}
}
2016-03-18 11:19:21 -04:00
// Line rendering
2016-03-18 13:16:03 -04:00
// Model rendering
self.model.draw(
&self.frustum,
&self.perspective_matrix,
&self.camera_matrix,
self.light_level,
self.sky_offset,
);
if let Some(clouds) = &mut self.clouds {
if world.copy_cloud_heightmap(&mut clouds.heightmap_data) {
clouds.dirty = true;
}
clouds.draw(
&self.camera.pos,
&self.perspective_matrix,
&self.camera_matrix,
self.light_level,
self.sky_offset,
delta,
);
2016-04-01 15:00:13 -04:00
}
2016-03-18 11:19:21 -04:00
// Trans chunk rendering
2016-03-24 17:13:24 -04:00
self.chunk_shader_alpha.program.use_program();
self.chunk_shader_alpha
.perspective_matrix
.set_matrix4(&self.perspective_matrix);
self.chunk_shader_alpha
.camera_matrix
.set_matrix4(&self.camera_matrix);
2016-03-24 17:13:24 -04:00
self.chunk_shader_alpha.texture.set_int(0);
self.chunk_shader_alpha
.light_level
.set_float(self.light_level);
self.chunk_shader_alpha
.sky_offset
.set_float(self.sky_offset);
2016-03-18 11:19:21 -04:00
// Copy the depth buffer
trans.main.bind_read();
trans.trans.bind_draw();
gl::blit_framebuffer(
0,
0,
physical_width as i32,
physical_height as i32,
0,
0,
physical_width as i32,
physical_height as i32,
gl::ClearFlags::Depth,
gl::NEAREST,
2016-03-18 11:19:21 -04:00
);
gl::enable(gl::BLEND);
gl::depth_mask(false);
2016-03-24 21:17:03 -04:00
trans.trans.bind();
2016-03-18 11:19:21 -04:00
gl::clear_color(0.0, 0.0, 0.0, 1.0);
gl::clear(gl::ClearFlags::Color);
Use glow, GL on Whatever (#262) Replaces the use of gl_generator with the glow wrapper: * Add glow dependency, based on glow 0.6.1 * Pin version of glow fork for https://github.com/iceiix/glow/pull/1 until #442 renderbuffer * Remove gl module, steven_gl Porting details: * Initialize glow in src/gl/mod.rs * Call gl methods on glow context * glow uses camelcase * Import glow::HasContext trait, finds draw_elements etc. * Fix mismatched types, glow uses Option and &str instead of raw pointers * Fix uniform_location, glow already returns Some(u32) * uniform_location: convert i32 to u32 for Uniform * Fix attribute_location * Fix shader creation Result u32 type * Fix passing GLvoid and 2d/3d * Fix missing Options type mismatches * Offsets are i32 in glow, not GLvoid * Fix clear_buffer using _f32_slice * Delete methods are singular not plural * glBufferData -> buffer_data_u8_slice * buffer_sub_data_u8_slice * Update more glow method wrapper names found by reviewing glow native platform * Remove unused multi_draw_elements, can be replaced by draw_elements in a loop and it has no WebGL equivalent * glow implements glMapBufferRange * Remove unused read_buffer * glow's deletes automatically pass 1 and take no reference * shader_source() accepts &str directly; removes last of std::ptr * Pass uniform Option<u32> * Fix bool passing normalized parameter * Fix draw_buffers parameter * Stop unnecessarily returning context from gl::init * Getting shader info is unsafe * Unwrapping static mut is unsafe * Use unsafe raw pointers for global mutable context * Fix initializing GL objects wrappers from glow wrappers * Unbinding framebuffers uses None * Uppercase global to fix warning * Shaders return Some instead of None * Unbox the context to a raw pointer * Use tex_image_2d_multisample added in glow fork * Implement uniform_location, fixing unwrap None failed * Add tex_sub_image_3d, using PixelUnpackData::Slice * set_matrix4: transmute the Matrix4 since it is repr(C) * get_pixels -> get_tex_image -> glGetTexImage, with PixelPackData::Slice * Wrap sub_image_2d (glTexSubImage2D) and fix warnings * Implement set_float_multi_raw and set_matrix4_multi, using from_raw_parts
2020-12-25 13:00:22 -05:00
gl::clear_buffer(gl::COLOR, 0, &mut [0.0, 0.0, 0.0, 1.0]);
gl::clear_buffer(gl::COLOR, 1, &mut [0.0, 0.0, 0.0, 0.0]);
gl::blend_func_separate(
gl::ONE_FACTOR,
gl::ONE_FACTOR,
gl::ZERO_FACTOR,
gl::ONE_MINUS_SRC_ALPHA,
);
2016-03-18 11:19:21 -04:00
for (pos, info) in world.get_render_list().iter().rev() {
2016-03-24 21:17:03 -04:00
if let Some(trans) = info.trans.as_ref() {
if trans.count > 0 {
self.chunk_shader_alpha
.offset
.set_int3(pos.0, pos.1 * 4096, pos.2);
2016-03-24 21:17:03 -04:00
trans.array.bind();
gl::draw_elements(
gl::TRIANGLES,
trans.count as i32,
self.element_buffer_type,
0,
);
2016-03-24 21:17:03 -04:00
}
}
}
2016-03-18 11:19:21 -04:00
gl::check_framebuffer_status();
2016-03-18 11:19:21 -04:00
gl::unbind_framebuffer();
gl::disable(gl::DEPTH_TEST);
gl::clear(gl::ClearFlags::Color);
gl::disable(gl::BLEND);
trans.draw(&self.trans_shader);
gl::enable(gl::DEPTH_TEST);
gl::depth_mask(true);
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
#[cfg(not(target_arch = "wasm32"))]
2016-03-18 11:19:21 -04:00
gl::disable(gl::MULTISAMPLE);
2015-09-17 11:04:25 -04:00
self.ui.tick(width, height);
2016-03-24 19:27:22 -04:00
gl::check_gl_error();
2016-03-24 19:27:22 -04:00
self.frame_id = self.frame_id.wrapping_add(1);
2015-10-07 14:36:59 -04:00
}
2016-03-19 13:34:12 -04:00
fn ensure_element_buffer(&mut self, size: usize) {
if self.element_buffer_size < size {
let (data, ty) = self::generate_element_buffer(size);
self.element_buffer_type = ty;
self.element_buffer.bind(gl::ELEMENT_ARRAY_BUFFER);
self.element_buffer
.set_data(gl::ELEMENT_ARRAY_BUFFER, &data, gl::DYNAMIC_DRAW);
2016-03-19 13:34:12 -04:00
self.element_buffer_size = size;
}
}
2016-03-24 17:13:24 -04:00
pub fn update_chunk_solid(&mut self, buffer: &mut ChunkBuffer, data: &[u8], count: usize) {
2016-03-19 13:34:12 -04:00
self.ensure_element_buffer(count);
if count == 0 {
if buffer.solid.is_some() {
buffer.solid = None;
}
return;
}
let new = buffer.solid.is_none();
if buffer.solid.is_none() {
buffer.solid = Some(ChunkRenderInfo {
array: gl::VertexArray::new(),
buffer: gl::Buffer::new(),
buffer_size: 0,
count: 0,
});
}
let info = buffer.solid.as_mut().unwrap();
info.array.bind();
self.chunk_shader.position.enable();
self.chunk_shader.texture_info.enable();
self.chunk_shader.texture_offset.enable();
self.chunk_shader.color.enable();
self.chunk_shader.lighting.enable();
self.element_buffer.bind(gl::ELEMENT_ARRAY_BUFFER);
info.buffer.bind(gl::ARRAY_BUFFER);
if new || info.buffer_size < data.len() {
info.buffer_size = data.len();
info.buffer
.set_data(gl::ARRAY_BUFFER, data, gl::DYNAMIC_DRAW);
2016-03-19 13:34:12 -04:00
} else {
2016-03-19 16:35:31 -04:00
info.buffer.re_set_data(gl::ARRAY_BUFFER, data);
2016-03-19 13:34:12 -04:00
}
self.chunk_shader
.position
.vertex_pointer(3, gl::FLOAT, false, 40, 0);
self.chunk_shader
.texture_info
.vertex_pointer(4, gl::UNSIGNED_SHORT, false, 40, 12);
self.chunk_shader
.texture_offset
.vertex_pointer(3, gl::SHORT, false, 40, 20);
self.chunk_shader
.color
.vertex_pointer(3, gl::UNSIGNED_BYTE, true, 40, 28);
self.chunk_shader
.lighting
.vertex_pointer(2, gl::UNSIGNED_SHORT, false, 40, 32);
2016-03-19 13:34:12 -04:00
info.count = count;
}
2016-03-24 21:17:03 -04:00
pub fn update_chunk_trans(&mut self, buffer: &mut ChunkBuffer, data: &[u8], count: usize) {
self.ensure_element_buffer(count);
if count == 0 {
if buffer.trans.is_some() {
buffer.trans = None;
}
return;
}
let new = buffer.trans.is_none();
if buffer.trans.is_none() {
buffer.trans = Some(ChunkRenderInfo {
array: gl::VertexArray::new(),
buffer: gl::Buffer::new(),
buffer_size: 0,
count: 0,
});
}
let info = buffer.trans.as_mut().unwrap();
info.array.bind();
self.chunk_shader_alpha.position.enable();
self.chunk_shader_alpha.texture_info.enable();
self.chunk_shader_alpha.texture_offset.enable();
self.chunk_shader_alpha.color.enable();
self.chunk_shader_alpha.lighting.enable();
self.element_buffer.bind(gl::ELEMENT_ARRAY_BUFFER);
info.buffer.bind(gl::ARRAY_BUFFER);
if new || info.buffer_size < data.len() {
info.buffer_size = data.len();
info.buffer
.set_data(gl::ARRAY_BUFFER, data, gl::DYNAMIC_DRAW);
2016-03-24 21:17:03 -04:00
} else {
info.buffer.re_set_data(gl::ARRAY_BUFFER, data);
}
self.chunk_shader_alpha
.position
.vertex_pointer(3, gl::FLOAT, false, 40, 0);
self.chunk_shader_alpha
.texture_info
.vertex_pointer(4, gl::UNSIGNED_SHORT, false, 40, 12);
self.chunk_shader_alpha
.texture_offset
.vertex_pointer(3, gl::SHORT, false, 40, 20);
self.chunk_shader_alpha
.color
.vertex_pointer(3, gl::UNSIGNED_BYTE, true, 40, 28);
self.chunk_shader_alpha
.lighting
.vertex_pointer(2, gl::UNSIGNED_SHORT, false, 40, 32);
2016-03-24 21:17:03 -04:00
info.count = count;
}
2016-03-16 13:33:06 -04:00
fn do_pending_textures(&mut self) {
2015-10-07 14:36:59 -04:00
let len = {
let tex = self.textures.read().unwrap();
2016-03-16 13:33:06 -04:00
// Rebuild the texture if it needs resizing
2015-10-07 14:36:59 -04:00
if self.texture_layers != tex.atlases.len() {
let len = ATLAS_SIZE * ATLAS_SIZE * 4 * tex.atlases.len();
let mut data = Vec::with_capacity(len);
unsafe {
data.set_len(len);
}
self.gl_texture.get_pixels(
gl::TEXTURE_2D_ARRAY,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
&mut data[..],
);
self.gl_texture.image_3d(
gl::TEXTURE_2D_ARRAY,
0,
ATLAS_SIZE as u32,
ATLAS_SIZE as u32,
tex.atlases.len() as u32,
gl::RGBA,
gl::UNSIGNED_BYTE,
&data[..],
);
2015-10-07 14:36:59 -04:00
self.texture_layers = tex.atlases.len();
}
tex.pending_uploads.len()
};
if len > 0 {
2016-03-16 13:33:06 -04:00
// Upload pending changes
2015-10-07 14:36:59 -04:00
let mut tex = self.textures.write().unwrap();
for upload in &tex.pending_uploads {
let atlas = upload.0;
let rect = upload.1;
let img = &upload.2;
self.gl_texture.sub_image_3d(
gl::TEXTURE_2D_ARRAY,
0,
rect.x as u32,
rect.y as u32,
atlas as u32,
rect.width as u32,
rect.height as u32,
1,
gl::RGBA,
gl::UNSIGNED_BYTE,
&img[..],
);
2015-10-07 14:36:59 -04:00
}
tex.pending_uploads.clear();
}
2016-03-16 13:33:06 -04:00
}
fn update_textures(&mut self, delta: f64) {
2016-04-07 10:55:03 -04:00
{
let mut tex = self.textures.write().unwrap();
while let Ok((hash, img)) = self.skin_reply.try_recv() {
if let Some(img) = img {
2016-04-07 10:55:03 -04:00
tex.update_skin(hash, img);
}
}
let mut old_skins = vec![];
for (skin, refcount) in &tex.skins {
if refcount.load(Ordering::Relaxed) == 0 {
old_skins.push(skin.clone());
}
}
for skin in old_skins {
tex.skins.remove(&skin);
tex.remove_dynamic(&format!("skin-{}", skin));
}
}
2016-03-16 13:33:06 -04:00
self.gl_texture.bind(gl::TEXTURE_2D_ARRAY);
self.do_pending_textures();
2015-10-07 14:36:59 -04:00
for ani in &mut self.textures.write().unwrap().animated_textures {
if ani.remaining_time <= 0.0 {
ani.current_frame = (ani.current_frame + 1) % ani.frames.len();
ani.remaining_time += ani.frames[ani.current_frame].time as f64;
let offset =
ani.texture.width * ani.texture.width * ani.frames[ani.current_frame].index * 4;
2015-10-07 14:36:59 -04:00
let offset2 = offset + ani.texture.width * ani.texture.width * 4;
self.gl_texture.sub_image_3d(
gl::TEXTURE_2D_ARRAY,
0,
ani.texture.get_x() as u32,
ani.texture.get_y() as u32,
ani.texture.atlas as u32,
ani.texture.get_width() as u32,
ani.texture.get_height() as u32,
1,
gl::RGBA,
gl::UNSIGNED_BYTE,
&ani.data[offset..offset2],
);
2015-10-07 14:36:59 -04:00
} else {
ani.remaining_time -= delta / 3.0;
}
}
}
2016-03-16 13:33:06 -04:00
fn init_trans(&mut self, width: u32, height: u32) {
self.trans = None;
self.trans = Some(TransInfo::new(
width,
height,
&self.chunk_shader_alpha,
&self.trans_shader,
));
2016-03-16 13:33:06 -04:00
}
2015-10-07 14:36:59 -04:00
pub fn get_textures(&self) -> Arc<RwLock<TextureManager>> {
self.textures.clone()
}
pub fn get_textures_ref(&self) -> &RwLock<TextureManager> {
&self.textures
}
pub fn check_texture(&self, tex: Texture) -> Texture {
if tex.version == self.resource_version {
tex
} else {
let mut new = Renderer::get_texture(&self.textures, &tex.name);
new.rel_x = tex.rel_x;
new.rel_y = tex.rel_y;
new.rel_width = tex.rel_width;
new.rel_height = tex.rel_height;
new.is_rel = tex.is_rel;
new
}
}
pub fn get_texture(textures: &RwLock<TextureManager>, name: &str) -> Texture {
let tex = { textures.read().unwrap().get_texture(name) };
2015-10-07 14:36:59 -04:00
match tex {
Some(val) => val,
None => {
let mut t = textures.write().unwrap();
2016-03-16 13:33:06 -04:00
// Make sure it hasn't already been loaded since we switched
// locks.
2015-10-07 14:36:59 -04:00
if let Some(val) = t.get_texture(name) {
val
} else {
t.load_texture(name);
t.get_texture(name).unwrap()
}
}
}
}
2016-04-07 10:55:03 -04:00
pub fn get_skin(&self, textures: &RwLock<TextureManager>, url: &str) -> Texture {
let tex = { textures.read().unwrap().get_skin(url) };
2016-04-07 10:55:03 -04:00
match tex {
Some(val) => val,
None => {
let mut t = textures.write().unwrap();
// Make sure it hasn't already been loaded since we switched
// locks.
if let Some(val) = t.get_skin(url) {
val
} else {
t.load_skin(self, url);
t.get_skin(url).unwrap()
}
}
}
}
2015-09-17 11:04:25 -04:00
}
2016-03-16 13:33:06 -04:00
struct TransInfo {
main: gl::Framebuffer,
2016-03-16 14:01:33 -04:00
fb_color: gl::Texture,
2016-03-26 09:28:14 -04:00
_fb_depth: gl::Texture,
2016-03-16 13:33:06 -04:00
trans: gl::Framebuffer,
accum: gl::Texture,
revealage: gl::Texture,
2016-03-26 09:28:14 -04:00
_depth: gl::Texture,
2016-03-16 13:33:06 -04:00
array: gl::VertexArray,
2016-03-26 09:28:14 -04:00
_buffer: gl::Buffer,
2016-03-16 13:33:06 -04:00
}
init_shader! {
Program TransShader {
vert = "trans_vertex",
frag = "trans_frag",
attribute = {
required position => "aPosition",
2016-03-16 13:33:06 -04:00
},
uniform = {
required accum => "taccum",
required revealage => "trevealage",
required color => "tcolor",
2016-03-16 13:33:06 -04:00
},
}
}
impl TransInfo {
pub fn new(
width: u32,
height: u32,
chunk_shader: &ChunkShaderAlpha,
shader: &TransShader,
) -> TransInfo {
2016-03-16 13:33:06 -04:00
let trans = gl::Framebuffer::new();
trans.bind();
let accum = gl::Texture::new();
accum.bind(gl::TEXTURE_2D);
accum.image_2d_ex(
gl::TEXTURE_2D,
0,
width,
height,
gl::RGBA16F,
gl::RGBA,
gl::FLOAT,
None,
);
2016-03-18 11:19:21 -04:00
accum.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
accum.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
2016-03-18 11:19:21 -04:00
trans.texture_2d(gl::COLOR_ATTACHMENT_0, gl::TEXTURE_2D, &accum, 0);
let revealage = gl::Texture::new();
revealage.bind(gl::TEXTURE_2D);
revealage.image_2d_ex(
gl::TEXTURE_2D,
0,
width,
height,
gl::R16F,
gl::RED,
gl::FLOAT,
None,
);
2016-03-18 11:19:21 -04:00
revealage.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
revealage.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
2016-03-18 11:19:21 -04:00
trans.texture_2d(gl::COLOR_ATTACHMENT_1, gl::TEXTURE_2D, &revealage, 0);
let trans_depth = gl::Texture::new();
trans_depth.bind(gl::TEXTURE_2D);
trans_depth.image_2d_ex(
gl::TEXTURE_2D,
0,
width,
height,
gl::DEPTH_COMPONENT24,
gl::DEPTH_COMPONENT,
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
gl::UNSIGNED_INT,
None,
);
2016-03-18 11:19:21 -04:00
trans_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
trans_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
2016-03-18 11:19:21 -04:00
trans.texture_2d(gl::DEPTH_ATTACHMENT, gl::TEXTURE_2D, &trans_depth, 0);
2016-03-24 21:17:03 -04:00
chunk_shader.program.use_program();
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
#[cfg(not(target_arch = "wasm32"))] // bound with layout(location=)
{
gl::bind_frag_data_location(&chunk_shader.program, 0, "accum");
gl::bind_frag_data_location(&chunk_shader.program, 1, "revealage");
}
gl::check_framebuffer_status();
gl::draw_buffers(&[gl::COLOR_ATTACHMENT_0, gl::COLOR_ATTACHMENT_1]);
2016-03-18 11:19:21 -04:00
let main = gl::Framebuffer::new();
main.bind();
// TODO: support rendering to a multisample renderbuffer for MSAA, using glRenderbufferStorageMultisample
// https://github.com/iceiix/stevenarella/pull/442
2016-03-18 11:19:21 -04:00
let fb_color = gl::Texture::new();
fb_color.bind(gl::TEXTURE_2D);
fb_color.image_2d(
gl::TEXTURE_2D,
0,
width,
height,
gl::RGBA,
gl::UNSIGNED_BYTE,
None,
);
fb_color.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
fb_color.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
2016-03-18 11:19:21 -04:00
main.texture_2d(gl::COLOR_ATTACHMENT_0, gl::TEXTURE_2D, &fb_color, 0);
2016-03-18 11:19:21 -04:00
let fb_depth = gl::Texture::new();
fb_depth.bind(gl::TEXTURE_2D);
fb_depth.image_2d_ex(
gl::TEXTURE_2D,
0,
width,
height,
gl::DEPTH_COMPONENT24,
gl::DEPTH_COMPONENT,
WebGL fixes after disabling threaded chunk builder (#451) At this point, the UI renders in the browser through WebGL, with no GL errors. Progress towards #446 🕸️ Web support * main: enable render loop on wasm, disable events_loop on wasm for now Allow for testing rendering on WebGL * chunk_builder: disable on wasm due to no threads on wasm Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread * chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers * std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing * www: update installation instructions * render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo * render: correct type for internalFormat DEPTH_COMPONENT24 Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format. Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401 and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. * render: gl::MULTISAMPLE (0x809d) is not available on WebGL Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21 * gl: replace set_float_multi_raw with a safer set_float_multi Removes use of passing raw pointers in set_float_multi_raw parameters Instead, casts raw pointers to flatten, similar to set_matrix_multi Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>. * render: model: send BYTE to id attrib, fixes type mismatch Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
gl::UNSIGNED_INT,
None,
);
fb_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
fb_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
main.texture_2d(gl::DEPTH_ATTACHMENT, gl::TEXTURE_2D, &fb_depth, 0);
gl::check_framebuffer_status();
2016-03-18 11:19:21 -04:00
gl::unbind_framebuffer();
2016-03-24 21:17:03 -04:00
shader.program.use_program();
2016-03-18 11:19:21 -04:00
let array = gl::VertexArray::new();
array.bind();
let buffer = gl::Buffer::new();
buffer.bind(gl::ARRAY_BUFFER);
let mut data = vec![];
for f in [
-1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0,
]
.iter()
{
2016-03-18 11:19:21 -04:00
data.write_f32::<NativeEndian>(*f).unwrap();
}
buffer.set_data(gl::ARRAY_BUFFER, &data, gl::STATIC_DRAW);
shader.position.enable();
shader.position.vertex_pointer(2, gl::FLOAT, false, 8, 0);
TransInfo {
main,
fb_color,
2016-03-26 09:28:14 -04:00
_fb_depth: fb_depth,
trans,
accum,
revealage,
2016-03-26 09:28:14 -04:00
_depth: trans_depth,
2016-03-18 11:19:21 -04:00
array,
2016-03-26 09:28:14 -04:00
_buffer: buffer,
2016-03-18 11:19:21 -04:00
}
}
2016-03-16 13:33:06 -04:00
2016-03-18 11:19:21 -04:00
fn draw(&mut self, shader: &TransShader) {
gl::active_texture(0);
self.accum.bind(gl::TEXTURE_2D);
gl::active_texture(1);
self.revealage.bind(gl::TEXTURE_2D);
gl::active_texture(2);
self.fb_color.bind(gl::TEXTURE_2D);
2016-03-18 11:19:21 -04:00
shader.program.use_program();
shader.accum.set_int(0);
shader.revealage.set_int(1);
shader.color.set_int(2);
self.array.bind();
gl::draw_arrays(gl::TRIANGLES, 0, 6);
2016-03-16 13:33:06 -04:00
}
}
2015-09-17 11:04:25 -04:00
pub struct TextureManager {
2016-03-24 11:39:57 -04:00
textures: HashMap<String, Texture, BuildHasherDefault<FNVHash>>,
2015-10-07 14:36:59 -04:00
version: usize,
resources: Arc<RwLock<resources::Manager>>,
atlases: Vec<atlas::Atlas>,
2015-09-17 11:04:25 -04:00
2015-10-07 14:36:59 -04:00
animated_textures: Vec<AnimatedTexture>,
pending_uploads: Vec<(i32, atlas::Rect, Vec<u8>)>,
2015-09-25 09:00:49 -04:00
dynamic_textures: HashMap<String, (Texture, image::DynamicImage), BuildHasherDefault<FNVHash>>,
free_dynamics: Vec<Texture>,
2016-04-07 10:55:03 -04:00
skins: HashMap<String, AtomicIsize, BuildHasherDefault<FNVHash>>,
Use web-sys for web backend (#444) A small step for #446 🕸️ Web support, use web-sys to interface to the web. Previously, we would try to use glutin on the web, which is not supported; now glutin is only used on native: fixes #171 could not find Context in platform_impl. winit is still used on both, but the GL context is created with web-sys and glow (on the web), and created with glutin and used with glow (on native). stdweb is no longer used, being replaced by web-sys. Substantial refactoring to allow reusing the code between web/native: * settings: use VirtualKeyCode from winit, not reexported from glutin * std_or_web: remove broken localstoragefs/stdweb, add File placeholder * render: disable skin_thread on wasm since we don't have threads * gl: use glow types in gl wrapper (integers in native, but Web*Key in web) * gl: web-sys WebGlUniformLocation does not implement Copy trait, so glow::UniformLocation doesn't so gl::Uniform can't * gl: refactor context initialization, pass glow::Context to gl::init for consistency between native/web * gl: update to glow with panicking tex_image_2d_multisample web-sys wrapper * glsl: use shader version in GLSL for WebGL 2 and OpenGL 3.2 * shaders: add explicit float/int type conversions, required for WebGL * shaders: specify mediump precision, required for WebGL * shaders: specify fragment shader output locations for WebGL * main: refactor handle_window_event to take a winit window, not glutin context * main: handle resize outside of handle_window_event since it updates the glutin window (the only event which does this) * main: use winit events in handle_window_event not reexported glutin events * main: refactor game loop handling into tick_all() * main: create winit window for WebGL, and use winit_window from glutin * main: restore console_error_panic_hook, mistakingly removed in (#260) * main: remove force setting env RUST_BACKTRACE=1, no longer can set env on web * www: index.js: fix wasm import path * www: npm update, npm audit fix * www: update readme to link to status on #446 🕸️ Web support
2020-12-26 16:42:37 -05:00
_skin_thread: Option<thread::JoinHandle<()>>,
2015-09-17 11:04:25 -04:00
}
impl TextureManager {
#[allow(clippy::let_and_return)]
#[allow(clippy::type_complexity)]
fn new(
res: Arc<RwLock<resources::Manager>>,
) -> (
TextureManager,
mpsc::Sender<String>,
mpsc::Receiver<(String, Option<image::DynamicImage>)>,
) {
2016-04-07 10:55:03 -04:00
let (tx, rx) = mpsc::channel();
let (stx, srx) = mpsc::channel();
Use web-sys for web backend (#444) A small step for #446 🕸️ Web support, use web-sys to interface to the web. Previously, we would try to use glutin on the web, which is not supported; now glutin is only used on native: fixes #171 could not find Context in platform_impl. winit is still used on both, but the GL context is created with web-sys and glow (on the web), and created with glutin and used with glow (on native). stdweb is no longer used, being replaced by web-sys. Substantial refactoring to allow reusing the code between web/native: * settings: use VirtualKeyCode from winit, not reexported from glutin * std_or_web: remove broken localstoragefs/stdweb, add File placeholder * render: disable skin_thread on wasm since we don't have threads * gl: use glow types in gl wrapper (integers in native, but Web*Key in web) * gl: web-sys WebGlUniformLocation does not implement Copy trait, so glow::UniformLocation doesn't so gl::Uniform can't * gl: refactor context initialization, pass glow::Context to gl::init for consistency between native/web * gl: update to glow with panicking tex_image_2d_multisample web-sys wrapper * glsl: use shader version in GLSL for WebGL 2 and OpenGL 3.2 * shaders: add explicit float/int type conversions, required for WebGL * shaders: specify mediump precision, required for WebGL * shaders: specify fragment shader output locations for WebGL * main: refactor handle_window_event to take a winit window, not glutin context * main: handle resize outside of handle_window_event since it updates the glutin window (the only event which does this) * main: use winit events in handle_window_event not reexported glutin events * main: refactor game loop handling into tick_all() * main: create winit window for WebGL, and use winit_window from glutin * main: restore console_error_panic_hook, mistakingly removed in (#260) * main: remove force setting env RUST_BACKTRACE=1, no longer can set env on web * www: index.js: fix wasm import path * www: npm update, npm audit fix * www: update readme to link to status on #446 🕸️ Web support
2020-12-26 16:42:37 -05:00
#[cfg(target_arch = "wasm32")]
let skin_thread = None;
#[cfg(not(target_arch = "wasm32"))]
let skin_thread = Some(thread::spawn(|| Self::process_skins(srx, tx)));
2015-10-07 14:36:59 -04:00
let mut tm = TextureManager {
2016-03-24 11:39:57 -04:00
textures: HashMap::with_hasher(BuildHasherDefault::default()),
2016-04-07 10:55:03 -04:00
version: {
// TODO: fix borrow and remove clippy::let_and_return above
2016-04-07 10:55:03 -04:00
let ver = res.read().unwrap().version();
ver
},
2015-10-07 14:36:59 -04:00
resources: res,
atlases: Vec::new(),
animated_textures: Vec::new(),
pending_uploads: Vec::new(),
2016-03-24 11:39:57 -04:00
dynamic_textures: HashMap::with_hasher(BuildHasherDefault::default()),
2015-10-07 14:36:59 -04:00
free_dynamics: Vec::new(),
2016-04-07 10:55:03 -04:00
skins: HashMap::with_hasher(BuildHasherDefault::default()),
_skin_thread: skin_thread,
2015-10-07 14:36:59 -04:00
};
tm.add_defaults();
2016-04-07 10:55:03 -04:00
(tm, stx, rx)
2015-10-07 14:36:59 -04:00
}
fn add_defaults(&mut self) {
self.put_texture(
"steven",
"missing_texture",
2,
2,
vec![
0, 0, 0, 255, 255, 0, 255, 255, 255, 0, 255, 255, 0, 0, 0, 255,
],
);
self.put_texture("steven", "solid", 1, 1, vec![255, 255, 255, 255]);
2015-10-07 14:36:59 -04:00
}
Add support for compiling WebAssembly wasm32-unknown-unknown target (#92) Note this only is the first step in web support, although the project compiles, it doesn't run! Merging now to avoid branch divergence, until dependencies can be updated for wasm support. * Add instructions to build for wasm32-unknown-unknown with wasm-pack in www/ * Update to rust-clipboard fork to compile with emscripten https://github.com/aweinstock314/rust-clipboard/pull/62 * Exclude reqwest dependency in wasm32 * Exclude compiling clipboard pasting on wasm32 * Exclude reqwest-using code from wasm32 * Install wasm target with rustup in Travis CI * Update to collision 0.19.0 Fixes wasm incompatibility in deprecated rustc-serialize crate: https://github.com/rustgd/collision-rs/issues/106 error[E0046]: not all trait items implemented, missing: `encode` --> github.com-1ecc6299db9ec823/rustc-serialize-0.3.24/src/serialize.rs:1358:1 * Increase travis_wait time even further, try 120 minutes * Set RUST_BACKTRACE=1 in main * Remove unused unneeded bzip2 features in zip crate To fix wasm32-unknown-unknown target compile error: error[E0432]: unresolved imports `libc::c_int`, `libc::c_uint`, `libc::c_void`, `libc::c_char` --> src/github.com-1ecc6299db9ec823/bzip2-sys-0.1.7/lib.rs:5:12 | 5 | use libc::{c_int, c_uint, c_void, c_char}; | ^^^^^ ^^^^^^ ^^^^^^ ^^^^^^ no `c_char` in the root | | | | | | | no `c_void` in the root | | no `c_uint` in the root | no `c_int` in the root * flate2 use Rust backend * Add console_error_panic_hook module for wasm backtraces * Build using wasm-pack, wasm-bindgen, run with wasm-app * Update to miniz_oxide 0.2.1, remove patch for https://github.com/Frommi/miniz_oxide/issues/42 * Update to official clipboard crate since https://github.com/aweinstock314/rust-clipboard/pull/62 was merged, but git revision pending release * Update to branch of glutin attempting to build for wasm https://github.com/iceiix/glutin/pull/1 * Update winit dependency of glutin to git master https://github.com/iceiix/winit/pull/2 * Update to glutin branch with working (compiles, doesn't run) wasm_stub * Add app name in title on web page * Add wasm to Travis-CI test matrix * Update glutin to fix Windows EGL compilation on AppVeyor https://github.com/iceiix/glutin/pull/1/commits/97797352b5242436cb82d8ecfb44242b69766e4c
2019-03-03 11:32:36 -05:00
#[cfg(target_arch = "wasm32")]
fn process_skins(
recv: mpsc::Receiver<String>,
reply: mpsc::Sender<(String, Option<image::DynamicImage>)>,
) {
Add support for compiling WebAssembly wasm32-unknown-unknown target (#92) Note this only is the first step in web support, although the project compiles, it doesn't run! Merging now to avoid branch divergence, until dependencies can be updated for wasm support. * Add instructions to build for wasm32-unknown-unknown with wasm-pack in www/ * Update to rust-clipboard fork to compile with emscripten https://github.com/aweinstock314/rust-clipboard/pull/62 * Exclude reqwest dependency in wasm32 * Exclude compiling clipboard pasting on wasm32 * Exclude reqwest-using code from wasm32 * Install wasm target with rustup in Travis CI * Update to collision 0.19.0 Fixes wasm incompatibility in deprecated rustc-serialize crate: https://github.com/rustgd/collision-rs/issues/106 error[E0046]: not all trait items implemented, missing: `encode` --> github.com-1ecc6299db9ec823/rustc-serialize-0.3.24/src/serialize.rs:1358:1 * Increase travis_wait time even further, try 120 minutes * Set RUST_BACKTRACE=1 in main * Remove unused unneeded bzip2 features in zip crate To fix wasm32-unknown-unknown target compile error: error[E0432]: unresolved imports `libc::c_int`, `libc::c_uint`, `libc::c_void`, `libc::c_char` --> src/github.com-1ecc6299db9ec823/bzip2-sys-0.1.7/lib.rs:5:12 | 5 | use libc::{c_int, c_uint, c_void, c_char}; | ^^^^^ ^^^^^^ ^^^^^^ ^^^^^^ no `c_char` in the root | | | | | | | no `c_void` in the root | | no `c_uint` in the root | no `c_int` in the root * flate2 use Rust backend * Add console_error_panic_hook module for wasm backtraces * Build using wasm-pack, wasm-bindgen, run with wasm-app * Update to miniz_oxide 0.2.1, remove patch for https://github.com/Frommi/miniz_oxide/issues/42 * Update to official clipboard crate since https://github.com/aweinstock314/rust-clipboard/pull/62 was merged, but git revision pending release * Update to branch of glutin attempting to build for wasm https://github.com/iceiix/glutin/pull/1 * Update winit dependency of glutin to git master https://github.com/iceiix/winit/pull/2 * Update to glutin branch with working (compiles, doesn't run) wasm_stub * Add app name in title on web page * Add wasm to Travis-CI test matrix * Update glutin to fix Windows EGL compilation on AppVeyor https://github.com/iceiix/glutin/pull/1/commits/97797352b5242436cb82d8ecfb44242b69766e4c
2019-03-03 11:32:36 -05:00
}
#[cfg(not(target_arch = "wasm32"))]
fn process_skins(
recv: mpsc::Receiver<String>,
reply: mpsc::Sender<(String, Option<image::DynamicImage>)>,
) {
let client = reqwest::blocking::Client::new();
2016-04-07 10:55:03 -04:00
loop {
let hash = match recv.recv() {
Ok(val) => val,
Err(_) => return, // Most likely shutting down
};
match Self::obtain_skin(&client, &hash) {
Ok(img) => {
let _ = reply.send((hash, Some(img)));
}
Err(err) => {
error!("Failed to get skin {:?}: {}", hash, err);
let _ = reply.send((hash, None));
}
2016-04-07 11:12:33 -04:00
}
}
}
Add support for compiling WebAssembly wasm32-unknown-unknown target (#92) Note this only is the first step in web support, although the project compiles, it doesn't run! Merging now to avoid branch divergence, until dependencies can be updated for wasm support. * Add instructions to build for wasm32-unknown-unknown with wasm-pack in www/ * Update to rust-clipboard fork to compile with emscripten https://github.com/aweinstock314/rust-clipboard/pull/62 * Exclude reqwest dependency in wasm32 * Exclude compiling clipboard pasting on wasm32 * Exclude reqwest-using code from wasm32 * Install wasm target with rustup in Travis CI * Update to collision 0.19.0 Fixes wasm incompatibility in deprecated rustc-serialize crate: https://github.com/rustgd/collision-rs/issues/106 error[E0046]: not all trait items implemented, missing: `encode` --> github.com-1ecc6299db9ec823/rustc-serialize-0.3.24/src/serialize.rs:1358:1 * Increase travis_wait time even further, try 120 minutes * Set RUST_BACKTRACE=1 in main * Remove unused unneeded bzip2 features in zip crate To fix wasm32-unknown-unknown target compile error: error[E0432]: unresolved imports `libc::c_int`, `libc::c_uint`, `libc::c_void`, `libc::c_char` --> src/github.com-1ecc6299db9ec823/bzip2-sys-0.1.7/lib.rs:5:12 | 5 | use libc::{c_int, c_uint, c_void, c_char}; | ^^^^^ ^^^^^^ ^^^^^^ ^^^^^^ no `c_char` in the root | | | | | | | no `c_void` in the root | | no `c_uint` in the root | no `c_int` in the root * flate2 use Rust backend * Add console_error_panic_hook module for wasm backtraces * Build using wasm-pack, wasm-bindgen, run with wasm-app * Update to miniz_oxide 0.2.1, remove patch for https://github.com/Frommi/miniz_oxide/issues/42 * Update to official clipboard crate since https://github.com/aweinstock314/rust-clipboard/pull/62 was merged, but git revision pending release * Update to branch of glutin attempting to build for wasm https://github.com/iceiix/glutin/pull/1 * Update winit dependency of glutin to git master https://github.com/iceiix/winit/pull/2 * Update to glutin branch with working (compiles, doesn't run) wasm_stub * Add app name in title on web page * Add wasm to Travis-CI test matrix * Update glutin to fix Windows EGL compilation on AppVeyor https://github.com/iceiix/glutin/pull/1/commits/97797352b5242436cb82d8ecfb44242b69766e4c
2019-03-03 11:32:36 -05:00
#[cfg(not(target_arch = "wasm32"))]
fn obtain_skin(
client: &::reqwest::blocking::Client,
hash: &str,
) -> Result<image::DynamicImage, ::std::io::Error> {
use std::io::Read;
use std::io::{Error, ErrorKind};
use std::path::Path;
use std_or_web::fs;
let path = format!("skin-cache/{}/{}.png", &hash[..2], hash);
let cache_path = Path::new(&path);
fs::create_dir_all(cache_path.parent().unwrap())?;
let mut buf = vec![];
if fs::metadata(cache_path).is_ok() {
// We have a cached image
let mut file = fs::File::open(cache_path)?;
file.read_to_end(&mut buf)?;
} else {
// Need to download it
Replace hyper with reqwest (#7) An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well * Begin updating to hyper 0.12.11 https://github.com/iceiix/steven/issues/4#issuecomment-425759778 * Use type variables for hyper::Client * Fix setting header syntax, Content-Type: application/json, 17->13 * Parse strings into URLs with url.parse::<hyper::Uri>().unwrap() https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25 * Use hyper::Request::post() then client.request() since client.post() removed * wait() on the ResponseFuture to get the Result * try! to unwrap the Result * status() is now a method * Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile * Replace send() with wait() on ResponseFuture * Parse HeaderValue to u64 * Slices implement std::io::Read trait * Read into_bytes() instead of read_to_end() * Disable boxed logger for now to workaround 'expected function, found macro' * Remove unnecessary mutability, warnings * Hack to parse twice to avoid double move * Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper * Start converting to reqwest: add Protocol::Error and reqwest::Error conversion * Use reqwest, replacing hyper, in protocol * Convert resources to use reqwest instead of hyper * Convert skin download to reqwest, instead of hyper * Remove hyper * Revert unnecessary variable name change req/body to reduce diff * Revert unnecessary whitespace change to reduce diff, align indentation on . * Fix authenticating to server, wrong method and join URL * Update Cargo.lock
2018-10-27 20:03:34 -04:00
let url = &format!("http://textures.minecraft.net/texture/{}", hash);
let mut res = match client.get(url).send() {
2016-04-07 10:55:03 -04:00
Ok(val) => val,
Err(err) => {
return Err(Error::new(ErrorKind::ConnectionAborted, err));
2016-04-07 10:55:03 -04:00
}
};
Replace hyper with reqwest (#7) An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well * Begin updating to hyper 0.12.11 https://github.com/iceiix/steven/issues/4#issuecomment-425759778 * Use type variables for hyper::Client * Fix setting header syntax, Content-Type: application/json, 17->13 * Parse strings into URLs with url.parse::<hyper::Uri>().unwrap() https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25 * Use hyper::Request::post() then client.request() since client.post() removed * wait() on the ResponseFuture to get the Result * try! to unwrap the Result * status() is now a method * Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile * Replace send() with wait() on ResponseFuture * Parse HeaderValue to u64 * Slices implement std::io::Read trait * Read into_bytes() instead of read_to_end() * Disable boxed logger for now to workaround 'expected function, found macro' * Remove unnecessary mutability, warnings * Hack to parse twice to avoid double move * Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper * Start converting to reqwest: add Protocol::Error and reqwest::Error conversion * Use reqwest, replacing hyper, in protocol * Convert resources to use reqwest instead of hyper * Convert skin download to reqwest, instead of hyper * Remove hyper * Revert unnecessary variable name change req/body to reduce diff * Revert unnecessary whitespace change to reduce diff, align indentation on . * Fix authenticating to server, wrong method and join URL * Update Cargo.lock
2018-10-27 20:03:34 -04:00
let mut buf = vec![];
match res.read_to_end(&mut buf) {
Ok(_) => {}
Replace hyper with reqwest (#7) An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well * Begin updating to hyper 0.12.11 https://github.com/iceiix/steven/issues/4#issuecomment-425759778 * Use type variables for hyper::Client * Fix setting header syntax, Content-Type: application/json, 17->13 * Parse strings into URLs with url.parse::<hyper::Uri>().unwrap() https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25 * Use hyper::Request::post() then client.request() since client.post() removed * wait() on the ResponseFuture to get the Result * try! to unwrap the Result * status() is now a method * Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile * Replace send() with wait() on ResponseFuture * Parse HeaderValue to u64 * Slices implement std::io::Read trait * Read into_bytes() instead of read_to_end() * Disable boxed logger for now to workaround 'expected function, found macro' * Remove unnecessary mutability, warnings * Hack to parse twice to avoid double move * Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper * Start converting to reqwest: add Protocol::Error and reqwest::Error conversion * Use reqwest, replacing hyper, in protocol * Convert resources to use reqwest instead of hyper * Convert skin download to reqwest, instead of hyper * Remove hyper * Revert unnecessary variable name change req/body to reduce diff * Revert unnecessary whitespace change to reduce diff, align indentation on . * Fix authenticating to server, wrong method and join URL * Update Cargo.lock
2018-10-27 20:03:34 -04:00
Err(err) => {
// TODO: different error for failure to read?
return Err(Error::new(ErrorKind::InvalidData, err));
}
}
// Save to cache
let mut file = fs::File::create(cache_path)?;
file.write_all(&buf)?;
}
let mut img = match image::load_from_memory(&buf) {
Ok(val) => val,
Err(err) => {
return Err(Error::new(ErrorKind::InvalidData, err));
}
};
let (_, height) = img.dimensions();
if height == 32 {
// Needs changing to the new format
let mut new = image::DynamicImage::new_rgba8(64, 64);
new.copy_from(&img, 0, 0)
.expect("Invalid png image in skin");
for xx in 0..4 {
for yy in 0..16 {
for section in 0..4 {
let os = match section {
0 => 2,
1 => 1,
2 => 0,
3 => 3,
_ => unreachable!(),
};
new.put_pixel(
16 + (3 - xx) + section * 4,
48 + yy,
img.get_pixel(xx + os * 4, 16 + yy),
);
new.put_pixel(
32 + (3 - xx) + section * 4,
48 + yy,
img.get_pixel(xx + 40 + os * 4, 16 + yy),
);
}
}
2016-04-07 15:39:48 -04:00
}
img = new;
}
// Block transparent pixels in blacklisted areas
let blacklist = [
// X, Y, W, H
(0, 0, 32, 16),
(16, 16, 24, 16),
(0, 16, 16, 16),
(16, 48, 16, 16),
(32, 48, 16, 16),
(40, 16, 16, 16),
];
for bl in blacklist.iter() {
for x in bl.0..(bl.0 + bl.2) {
for y in bl.1..(bl.1 + bl.3) {
let mut col = img.get_pixel(x, y);
col.0[3] = 255;
img.put_pixel(x, y, col);
2016-04-07 15:39:48 -04:00
}
}
2016-04-07 10:55:03 -04:00
}
Ok(img)
2016-04-07 10:55:03 -04:00
}
2015-10-07 14:36:59 -04:00
fn update_textures(&mut self, version: usize) {
self.pending_uploads.clear();
self.atlases.clear();
self.animated_textures.clear();
self.version = version;
let map = self.textures.clone();
self.textures.clear();
self.free_dynamics.clear();
2015-10-07 14:36:59 -04:00
self.add_defaults();
for name in map.keys() {
if let Some(n) = name.strip_prefix("steven-dynamic:") {
let (width, height, data) = {
let dynamic_texture = match self.dynamic_textures.get(n) {
Some(val) => val,
None => continue,
};
let img = &dynamic_texture.1;
let (width, height) = img.dimensions();
2020-11-24 15:12:56 -05:00
(width, height, img.to_rgba8().into_vec())
};
let new_tex =
self.put_texture("steven-dynamic", n, width as u32, height as u32, data);
self.dynamic_textures.get_mut(n).unwrap().0 = new_tex;
} else if !self.textures.contains_key(name) {
self.load_texture(name);
}
2015-10-07 14:36:59 -04:00
}
}
2016-04-07 10:55:03 -04:00
fn get_skin(&self, url: &str) -> Option<Texture> {
let hash = &url["http://textures.minecraft.net/texture/".len()..];
if let Some(skin) = self.skins.get(hash) {
skin.fetch_add(1, Ordering::Relaxed);
}
self.get_texture(&format!("steven-dynamic:skin-{}", hash))
}
pub fn release_skin(&self, url: &str) {
let hash = &url["http://textures.minecraft.net/texture/".len()..];
if let Some(skin) = self.skins.get(hash) {
skin.fetch_sub(1, Ordering::Relaxed);
}
}
fn load_skin(&mut self, renderer: &Renderer, url: &str) {
let hash = &url["http://textures.minecraft.net/texture/".len()..];
let res = self.resources.clone();
// TODO: This shouldn't be hardcoded to steve but instead
// have a way to select alex as a default.
let img = if let Some(mut val) = res
.read()
.unwrap()
.open("minecraft", "textures/entity/steve.png")
{
let mut data = Vec::new();
val.read_to_end(&mut data).unwrap();
image::load_from_memory(&data).unwrap()
} else {
image::DynamicImage::new_rgba8(64, 64)
};
2016-04-07 10:55:03 -04:00
self.put_dynamic(&format!("skin-{}", hash), img);
self.skins.insert(hash.to_owned(), AtomicIsize::new(0));
renderer.skin_request.send(hash.to_owned()).unwrap();
}
fn update_skin(&mut self, hash: String, img: image::DynamicImage) {
if !self.skins.contains_key(&hash) {
return;
}
let name = format!("steven-dynamic:skin-{}", hash);
let tex = self.get_texture(&name).unwrap();
2016-04-07 10:55:03 -04:00
let rect = atlas::Rect {
x: tex.x,
y: tex.y,
width: tex.width,
height: tex.height,
};
self.pending_uploads
2020-11-24 15:12:56 -05:00
.push((tex.atlas, rect, img.to_rgba8().into_vec()));
self.dynamic_textures
.get_mut(&format!("skin-{}", hash))
.unwrap()
.1 = img;
2016-04-07 10:55:03 -04:00
}
2015-10-07 14:36:59 -04:00
fn get_texture(&self, name: &str) -> Option<Texture> {
if name.find(':').is_some() {
2016-03-26 10:24:26 -04:00
self.textures.get(name).cloned()
2016-03-19 12:32:13 -04:00
} else {
2016-03-26 10:24:26 -04:00
self.textures.get(&format!("minecraft:{}", name)).cloned()
2016-03-19 12:32:13 -04:00
}
2015-10-07 14:36:59 -04:00
}
fn load_texture(&mut self, name: &str) {
let (plugin, name) = if let Some(pos) = name.find(':') {
(&name[..pos], &name[pos + 1..])
} else {
("minecraft", name)
};
let path = format!("textures/{}.png", name);
let res = self.resources.clone();
if let Some(mut val) = res.read().unwrap().open(plugin, &path) {
let mut data = Vec::new();
val.read_to_end(&mut data).unwrap();
if let Ok(img) = image::load_from_memory(&data) {
let (width, height) = img.dimensions();
2016-03-16 13:33:06 -04:00
// Might be animated
2015-10-07 14:36:59 -04:00
if (name.starts_with("blocks/") || name.starts_with("items/")) && width != height {
2020-11-24 15:12:56 -05:00
let id = img.to_rgba8().into_vec();
2015-10-07 14:36:59 -04:00
let frame = id[..(width * width * 4) as usize].to_owned();
if let Some(mut ani) = self.load_animation(plugin, name, &img, id) {
ani.texture = self.put_texture(plugin, name, width, width, frame);
self.animated_textures.push(ani);
return;
}
}
2020-11-24 15:12:56 -05:00
self.put_texture(plugin, name, width, height, img.to_rgba8().into_vec());
2015-10-07 14:36:59 -04:00
return;
}
}
self.insert_texture_dummy(plugin, name);
}
fn load_animation(
&mut self,
plugin: &str,
name: &str,
img: &image::DynamicImage,
data: Vec<u8>,
) -> Option<AnimatedTexture> {
2015-10-07 14:36:59 -04:00
let path = format!("textures/{}.png.mcmeta", name);
let res = self.resources.clone();
if let Some(val) = res.read().unwrap().open(plugin, &path) {
let meta: serde_json::Value = serde_json::from_reader(val).unwrap();
let animation = meta.get("animation").unwrap();
let frame_time = animation
.get("frametime")
.and_then(|v| v.as_i64())
.unwrap_or(1);
let interpolate = animation
.get("interpolate")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let frames = if let Some(frames) = animation.get("frames").and_then(|v| v.as_array()) {
2015-10-07 14:36:59 -04:00
let mut out = Vec::with_capacity(frames.len());
for frame in frames {
if let Some(index) = frame.as_i64() {
out.push(AnimationFrame {
index: index as usize,
time: frame_time,
})
} else {
out.push(AnimationFrame {
index: frame.get("index").unwrap().as_i64().unwrap() as usize,
time: frame_time * frame.get("frameTime").unwrap().as_i64().unwrap(),
2016-03-16 13:33:06 -04:00
})
2015-10-07 14:36:59 -04:00
}
}
out
} else {
let (width, height) = img.dimensions();
let count = height / width;
let mut frames = Vec::with_capacity(count as usize);
for i in 0..count {
frames.push(AnimationFrame {
index: i as usize,
time: frame_time,
})
}
frames
};
return Some(AnimatedTexture {
frames,
data,
interpolate,
2015-10-07 14:36:59 -04:00
current_frame: 0,
remaining_time: 0.0,
texture: self.get_texture("steven:missing_texture").unwrap(),
});
}
None
}
fn put_texture(
&mut self,
plugin: &str,
name: &str,
width: u32,
height: u32,
data: Vec<u8>,
) -> Texture {
2015-10-07 14:36:59 -04:00
let (atlas, rect) = self.find_free(width as usize, height as usize);
self.pending_uploads.push((atlas, rect, data));
let mut full_name = String::new();
2016-03-19 12:32:13 -04:00
full_name.push_str(plugin);
full_name.push(':');
2015-10-07 14:36:59 -04:00
full_name.push_str(name);
let tex = Texture {
name: full_name.clone(),
version: self.version,
atlas,
2015-10-07 14:36:59 -04:00
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
rel_x: 0.0,
rel_y: 0.0,
rel_width: 1.0,
rel_height: 1.0,
is_rel: false,
};
self.textures.insert(full_name, tex.clone());
tex
}
fn find_free(&mut self, width: usize, height: usize) -> (i32, atlas::Rect) {
let mut index = 0;
for atlas in &mut self.atlases {
if let Some(rect) = atlas.add(width, height) {
return (index, rect);
}
index += 1;
}
let mut atlas = atlas::Atlas::new(ATLAS_SIZE, ATLAS_SIZE);
let rect = atlas.add(width, height);
self.atlases.push(atlas);
(index, rect.unwrap())
}
fn insert_texture_dummy(&mut self, plugin: &str, name: &str) -> Texture {
let missing = self.get_texture("steven:missing_texture").unwrap();
let mut full_name = String::new();
2016-03-19 13:34:12 -04:00
full_name.push_str(plugin);
full_name.push(':');
2015-10-07 14:36:59 -04:00
full_name.push_str(name);
let t = Texture {
name: full_name.to_owned(),
version: self.version,
atlas: missing.atlas,
x: missing.x,
y: missing.y,
width: missing.width,
height: missing.height,
rel_x: 0.0,
rel_y: 0.0,
rel_width: 1.0,
rel_height: 1.0,
is_rel: false,
};
2020-06-29 21:48:07 -04:00
self.textures.insert(full_name, t.clone());
2015-10-07 14:36:59 -04:00
t
}
pub fn put_dynamic(&mut self, name: &str, img: image::DynamicImage) -> Texture {
2016-04-07 14:51:05 -04:00
use std::mem;
2015-10-07 14:36:59 -04:00
let (width, height) = img.dimensions();
let (width, height) = (width as usize, height as usize);
2016-04-07 14:51:05 -04:00
let mut rect_pos = None;
2015-10-07 14:36:59 -04:00
for (i, r) in self.free_dynamics.iter().enumerate() {
if r.width == width && r.height == height {
2016-04-07 14:51:05 -04:00
rect_pos = Some(i);
2015-10-07 14:36:59 -04:00
break;
} else if r.width >= width && r.height >= height {
2016-04-07 14:51:05 -04:00
rect_pos = Some(i);
2015-10-07 14:36:59 -04:00
}
}
2020-11-24 15:12:56 -05:00
let data = img.to_rgba8().into_vec();
2015-10-07 14:36:59 -04:00
2016-04-07 14:51:05 -04:00
if let Some(rect_pos) = rect_pos {
let mut tex = self.free_dynamics.remove(rect_pos);
2015-10-07 14:36:59 -04:00
let rect = atlas::Rect {
x: tex.x,
y: tex.y,
width,
height,
2015-10-07 14:36:59 -04:00
};
self.pending_uploads.push((tex.atlas, rect, data));
let mut t = tex.relative(
0.0,
0.0,
(width as f32) / (tex.width as f32),
(height as f32) / (tex.height as f32),
);
2016-04-07 14:51:05 -04:00
let old_name = mem::replace(&mut tex.name, format!("steven-dynamic:{}", name));
2020-06-29 21:48:07 -04:00
self.dynamic_textures.insert(name.to_owned(), (tex, img));
2016-04-07 10:55:03 -04:00
// We need to rename the texture itself so that get_texture calls
// work with the new name
2016-04-07 14:51:05 -04:00
let mut old = self.textures.remove(&old_name).unwrap();
old.name = format!("steven-dynamic:{}", name);
2016-04-24 07:22:04 -04:00
t.name = old.name.clone();
self.textures
.insert(format!("steven-dynamic:{}", name), old);
2015-10-07 14:36:59 -04:00
t
} else {
let tex = self.put_texture("steven-dynamic", name, width as u32, height as u32, data);
self.dynamic_textures
.insert(name.to_owned(), (tex.clone(), img));
tex
2015-10-07 14:36:59 -04:00
}
}
pub fn remove_dynamic(&mut self, name: &str) {
let desc = self.dynamic_textures.remove(name).unwrap();
self.free_dynamics.push(desc.0);
2015-10-07 14:36:59 -04:00
}
2015-09-17 11:04:25 -04:00
}
2015-09-25 10:20:55 -04:00
#[allow(dead_code)]
2015-09-17 11:04:25 -04:00
struct AnimatedTexture {
2015-10-07 14:36:59 -04:00
frames: Vec<AnimationFrame>,
data: Vec<u8>,
interpolate: bool,
current_frame: usize,
remaining_time: f64,
texture: Texture,
2015-09-17 11:04:25 -04:00
}
struct AnimationFrame {
2015-10-07 14:36:59 -04:00
index: usize,
time: i64,
2015-09-17 11:04:25 -04:00
}
#[derive(Clone, Debug)]
pub struct Texture {
2016-04-24 07:22:04 -04:00
pub name: String,
2015-10-07 14:36:59 -04:00
version: usize,
pub atlas: i32,
x: usize,
y: usize,
width: usize,
height: usize,
2016-03-22 10:42:10 -04:00
is_rel: bool, // Save some cycles for non-relative textures
2015-10-07 14:36:59 -04:00
rel_x: f32,
rel_y: f32,
rel_width: f32,
rel_height: f32,
2015-09-17 11:04:25 -04:00
}
impl Texture {
2015-10-07 14:36:59 -04:00
pub fn get_x(&self) -> usize {
if self.is_rel {
self.x + ((self.width as f32) * self.rel_x) as usize
} else {
self.x
}
}
pub fn get_y(&self) -> usize {
if self.is_rel {
self.y + ((self.height as f32) * self.rel_y) as usize
} else {
self.y
}
}
pub fn get_width(&self) -> usize {
if self.is_rel {
((self.width as f32) * self.rel_width) as usize
} else {
self.width
}
}
pub fn get_height(&self) -> usize {
if self.is_rel {
((self.height as f32) * self.rel_height) as usize
} else {
self.height
}
}
pub fn relative(&self, x: f32, y: f32, width: f32, height: f32) -> Texture {
Texture {
name: self.name.clone(),
version: self.version,
x: self.x,
y: self.y,
atlas: self.atlas,
width: self.width,
height: self.height,
is_rel: true,
rel_x: self.rel_x + x * self.rel_width,
rel_y: self.rel_y + y * self.rel_height,
rel_width: width * self.rel_width,
rel_height: height * self.rel_height,
}
}
2015-09-17 11:04:25 -04:00
}
#[allow(unused_must_use)]
pub fn generate_element_buffer(size: usize) -> (Vec<u8>, gl::Type) {
2015-10-07 14:36:59 -04:00
let mut ty = gl::UNSIGNED_SHORT;
let mut data = if (size / 6) * 4 * 3 >= u16::max_value() as usize {
ty = gl::UNSIGNED_INT;
Vec::with_capacity(size * 4)
} else {
Vec::with_capacity(size * 2)
};
for i in 0..size / 6 {
for val in &[0, 1, 2, 2, 1, 3] {
if ty == gl::UNSIGNED_INT {
data.write_u32::<NativeEndian>((i as u32) * 4 + val);
} else {
data.write_u16::<NativeEndian>((i as u16) * 4 + (*val as u16));
}
}
}
(data, ty)
}