merged from silentevermore

Commit 6353b3612cd3 · mgecumshooter · 2026-10-21 17:27 +0300

Changeset
6353b3612cd34010382fb23d257409db546b15c4
Parents
25:57d803541ec5 26:15b48e29f214

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/shaders/frag.glsl b/shaders/frag.glsl
--- a/shaders/frag.glsl
+++ b/shaders/frag.glsl
@@ -13,17 +13,10 @@
     vec4 texel = texture(texture0, fragTexCoord) * colDiffuse * fragColor;
 
     vec3 normal = normalize(fragNormal);
-    vec3 lightDir = normalize(vec3(-0.5, 1.0, 0.5));
+    vec3 lightDir = normalize(vec3(-0.5, 1.0, -0.5));
 
     float diffuse = max(dot(normal, lightDir), 0.0);
-
-    float shade;
-    if (diffuse > 0.65)
-        shade = 1.0;
-    else if (diffuse > 0.25)
-        shade = 0.65;
-    else
-        shade = 0.3;
+    float shade = min(diffuse + 0.15, 1.0);
 
     finalColor = vec4(texel.rgb * shade, texel.a);
 }
diff --git a/shaders/post.glsl b/shaders/post.glsl
--- a/shaders/post.glsl
+++ b/shaders/post.glsl
@@ -7,28 +7,25 @@
 
 uniform sampler2D texture0;
 uniform sampler2D depth_tex;
+uniform sampler2D normal_tex;
+uniform float tan_half_fov;
+uniform float aspect;
 
 out vec4 finalColor;
 
 // dither
 const mat4 bayerMatrix = mat4(
-		0.0/16.0, 12.0/16.0,  3.0/16.0, 15.0/16.0,
-		8.0/16.0,  4.0/16.0, 11.0/16.0,  7.0/16.0,
-		2.0/16.0, 14.0/16.0,  1.0/16.0, 13.0/16.0,
-		10.0/16.0,  6.0/16.0,  9.0/16.0,  5.0/16.0
+	0.0/16.0, 12.0/16.0,  3.0/16.0, 15.0/16.0,
+	8.0/16.0,  4.0/16.0, 11.0/16.0,  7.0/16.0,
+	2.0/16.0, 14.0/16.0,  1.0/16.0, 13.0/16.0,
+	10.0/16.0,  6.0/16.0,  9.0/16.0,  5.0/16.0
 );
 
 // ssao
-const vec2 OFFSETS[8] = vec2[8](
-    vec2( 1.0,  0.0),
-    vec2(-1.0,  0.0),
-    vec2( 0.0,  1.0),
-    vec2( 0.0, -1.0),
-    vec2( 0.707,  0.707),
-    vec2(-0.707,  0.707),
-    vec2( 0.707, -0.707),
-    vec2(-0.707, -0.707)
-);
+const int   AO_SAMPLES  = 16;
+const float AO_RADIUS   = 1.5;
+const float AO_BIAS     = 0.05;
+const float AO_STRENGTH = 0.6;
 
 // pixelization + other
 float pixel_size = 1.0;
@@ -47,6 +44,59 @@
 	return clamp(lin / fog_far, 0.0, 1.0);
 }
 
+float view_dist(float d) {
+    float z = d * 2.0 - 1.0;
+    return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
+}
+
+vec3 view_pos(vec2 uv, float d) {
+    float lz = view_dist(d);
+    vec2 ndc = uv * 2.0 - 1.0;
+    return vec3(ndc * vec2(aspect, 1.0) * tan_half_fov * lz, -lz);
+}
+
+float hash(vec2 p) {
+    return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
+}
+
+float ssao(vec2 uv, float depth, vec2 texsize) {
+    vec3 P = view_pos(uv, depth);
+    vec3 N = normalize(texture(normal_tex, uv).xyz * 2.0 - 1.0);
+
+    float ang = hash(floor(uv * texsize)) * 6.2831853;
+    vec3 rvec = vec3(cos(ang), sin(ang), 0.0);
+    vec3 T = normalize(rvec - N * dot(rvec, N));
+    vec3 B = cross(N, T);
+    mat3 TBN = mat3(T, B, N);
+
+    float occ = 0.0;
+    for (int i = 0; i < AO_SAMPLES; i++) {
+        float e = (float(i) + 0.5) / float(AO_SAMPLES);
+        float phi = float(i) * 2.399963;
+        vec3 dir = vec3(cos(phi) * sqrt(e), sin(phi) * sqrt(e), sqrt(1.0 - e));
+
+        float s = fract(float(i) * 0.618034 + 0.1);
+        float scale = mix(0.1, 1.0, s * s);
+
+        vec3 sp = P + TBN * dir * (AO_RADIUS * scale);
+        if (sp.z >= -near_plane) continue;
+
+        vec2 suv = (sp.xy / (-sp.z * vec2(aspect, 1.0) * tan_half_fov)) * 0.5 + 0.5;
+        if (any(lessThan(suv, vec2(0.0))) || any(greaterThan(suv, vec2(1.0)))) continue;
+
+        float sd = texture(depth_tex, suv).r;
+        if (sd >= 0.99999) continue;
+
+        float scene_dist  = view_dist(sd);
+        float sample_dist = -sp.z;
+
+        float hit = (scene_dist <= sample_dist - AO_BIAS) ? 1.0 : 0.0;
+        float range = smoothstep(0.0, 1.0, AO_RADIUS / abs(view_dist(depth) - scene_dist));
+        occ += hit * range;
+    }
+    return 1.0 - AO_STRENGTH * occ / float(AO_SAMPLES);
+}
+
 void main() {
 	// pixelization
 	vec2 texsize = textureSize(texture0, 0);
@@ -59,8 +109,14 @@
 	pixel_uv = dist * 0.5 + vec2(0.5);
 
 	float depth = texture(depth_tex, pixel_uv).r;
+	vec3 normal = texture(normal_tex, pixel_uv).xyz * 2.0 - 1.0;
 	vec4 color = texture(texture0, pixel_uv) * fragColor;
 
+	// ssao
+	if (depth < 1) {
+		color.rgb *= ssao(pixel_uv, depth, texsize);
+	}
+	
 	// dither
 	{
 		float lum = dot(color.rgb, vec3(0.299, 0.587, 0.114));
@@ -87,62 +143,5 @@
 	color.rgb *= 1.0 - diff;
 	color.rgb *= pow(1.0 - linearize_depth(depth), 4.0);
 
-	if (depth < 1) {
-		float strength = 1.0;
-		float radius = 4.0;
-		int ao_idx = 1;
-
-		// ssao
-		if (ao_idx == 1) {
-			// simple
-			vec2 px = radius / texsize;
-		    float ao = 0.0;
-
-		    float d1 = texture(depth_tex, pixel_uv + vec2( px.x, 0.0)).r;
-		    float d2 = texture(depth_tex, pixel_uv + vec2(-px.x, 0.0)).r;
-		    float d3 = texture(depth_tex, pixel_uv + vec2(0.0,  px.y)).r;
-		    float d4 = texture(depth_tex, pixel_uv + vec2(0.0, -px.y)).r;
-
-		    ao += smoothstep(0.001, 0.008, depth - d1);
-		    ao += smoothstep(0.001, 0.008, depth - d2);
-		    ao += smoothstep(0.001, 0.008, depth - d3);
-		    ao += smoothstep(0.001, 0.008, depth - d4);
-		    ao *= 0.25;
-		    
-			color.rgb *= 1.0 - ao * strength;
-		} else {
-			float occlusion = 0.0;
-		    vec2 texelSize = 1.0 / texsize;
-
-		    for (int i = 0; i < 8; i++) {
-		        vec2 sampleUV =
-		            pixel_uv + OFFSETS[i] * radius * texelSize;
-
-		        if (any(lessThan(sampleUV, vec2(0.0))) ||
-		            any(greaterThan(sampleUV, vec2(1.0)))) continue;
-
-		        float sampleDepth = texture(depth_tex, sampleUV).r;
-
-		        if (sampleDepth >= 0.99999) continue;
-
-		        float difference = depth - sampleDepth;
-		        float bias = max(0.0005, depth * 0.002);
-
-		        occlusion += smoothstep(
-		            bias,
-		            bias * 4.0,
-		            difference
-		        );
-		    }
-
-		    float ao = clamp(
-		        1.0 - strength * (occlusion / 8.0),
-		        0.0,
-		        1.0
-		    );
-
-			color.rgb *= ao;
-		}
-	}
 	finalColor = color;
 }
diff --git a/shaders/post_vert.glsl b/shaders/post_vert.glsl
new file mode 100644
--- /dev/null
+++ b/shaders/post_vert.glsl
@@ -0,0 +1,16 @@
+#version 330
+
+in vec3 vertexPosition;
+in vec2 vertexTexCoord;
+in vec3 vertexNormal;
+in vec4 vertexColor;
+
+out vec2 fragTexCoord;
+out vec4 fragColor;
+out vec3 fragNormal;
+
+void main() {
+    fragTexCoord = vertexTexCoord;
+    fragColor = vertexColor;
+    fragNormal = vertexNormal;
+}
diff --git a/shaders/vert.glsl b/shaders/vert.glsl
--- a/shaders/vert.glsl
+++ b/shaders/vert.glsl
@@ -7,6 +7,7 @@
 
 uniform mat4 mvp;
 uniform mat4 matModel;
+uniform mat4 matView;
 
 out vec2 fragTexCoord;
 out vec4 fragColor;
@@ -16,6 +17,7 @@
     fragTexCoord = vertexTexCoord;
     fragColor = vertexColor;
     fragNormal = mat3(transpose(inverse(matModel))) * vertexNormal;
+    // fragNormal = mat3(matView) * mat3(matModel) * vertexNormal;
 
     gl_Position = mvp * vec4(vertexPosition, 1.0);
 }
diff --git a/src/chunk/mod.rs b/src/chunk/mod.rs
--- a/src/chunk/mod.rs
+++ b/src/chunk/mod.rs
@@ -8,30 +8,27 @@
 use std::fs;
 
 // internal deps
-use crate::voxel::{ self, Voxel, VOXEL_SIZE };
 use crate::util::*;
-
+use crate::voxel::{self, VOXEL_SIZE, Voxel};
 
 // constants
 pub const CHUNK_SIZE: usize = 32;
 
-
 pub struct Chunk {
     pub chunk_pos: Vector3,
-    pub voxels: [Voxel; CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE],
+    pub voxels: [Voxel; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE],
     pub dirty: bool,
-    pub mesh: Option<Mesh>
+    pub mesh: Option<Mesh>,
 } // pending: add events stack
 
-
 impl Chunk {
     // constructor
     pub fn new(pos: Vector3) -> Self {
         let mut s = Self {
             chunk_pos: pos,
-            voxels: [Voxel::Air; CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE],
+            voxels: [Voxel::Air; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE],
             dirty: true,
-            mesh: None
+            mesh: None,
         };
         for x in 0..CHUNK_SIZE {
             for z in 0..CHUNK_SIZE {
@@ -81,237 +78,243 @@
 
 	cos_angle.acos() <= half_fov + angular_margin
     }
-
+    
     pub fn generate_mesh(&mut self, rl: &RaylibThread) {
-	let mut vertices: Vec<Vector3> = Vec::new();
-	let mut normals: Vec<Vector3> = Vec::new();
-	let mut indices: Vec<u16> = Vec::new();
+        let mut vertices: Vec<Vector3> = Vec::new();
+        let mut normals: Vec<Vector3> = Vec::new();
+        let mut indices: Vec<u16> = Vec::new();
+
+        let mut vertex_offset: i32 = 0;
+        for x in 0..CHUNK_SIZE {
+            for y in 0..CHUNK_SIZE {
+                for z in 0..CHUNK_SIZE {
+                    if let Some(Voxel::Air) = self.get_voxel(x, y, z) {
+                        continue;
+                    }
+                    if !self.is_exposed(x, y, z) {
+                        continue;
+                    }
 
-	let mut vertex_offset: i32 = 0;
-	for x in 0..CHUNK_SIZE {
-	    for y in 0..CHUNK_SIZE {
-		for z in 0..CHUNK_SIZE {
-		    if let Some(Voxel::Air) = self.get_voxel(x, y, z) {
-			continue;
-		    }
-		    if !self.is_exposed(x, y, z) {
-			continue;
-		    }
-
+                    // first side
+                    vertices.push(Vector3::new(x as f32, y as f32, z as f32));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, 0f32, -1f32));
 
-		    // first side
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32 + VOXEL_SIZE as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32 + VOXEL_SIZE as f32, z as f32)
-		    );
-		    push_quad_normal(&mut normals, Vector3::new(0f32, 0f32, -1f32));
-		    
-		    // second side
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32 + VOXEL_SIZE as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32 + VOXEL_SIZE as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    push_quad_normal(&mut normals, Vector3::new(-1f32, 0f32, 0f32));
-		    
-		    // third side
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    push_quad_normal(&mut normals, Vector3::new(0f32, -1f32, 0f32));
+                    // second side
+                    vertices.push(Vector3::new(x as f32, y as f32, z as f32));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(-1f32, 0f32, 0f32));
 
-		    // fourth side
-		    vertices.push(
-			Vector3::
-			new(x as f32+VOXEL_SIZE as f32, y as f32+VOXEL_SIZE as f32, z as f32+VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32 + VOXEL_SIZE as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32 + VOXEL_SIZE as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32 + VOXEL_SIZE as f32, z as f32)
-		    );
-		    push_quad_normal(&mut normals, Vector3::new(0f32, 1f32, 0f32));
+                    // third side
+                    vertices.push(Vector3::new(x as f32, y as f32, z as f32));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, -1f32, 0f32));
+
+                    // fourth side
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, 1f32, 0f32));
 
-		    // fifth side
-		    vertices.push(
-			Vector3::
-			new(x as f32+VOXEL_SIZE as f32, y as f32+VOXEL_SIZE as f32, z as f32+VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32 + VOXEL_SIZE as f32, z as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32, z as f32)
-		    );
-		    push_quad_normal(&mut normals, Vector3::new(1f32, 0f32, 0f32));
+                    // fifth side
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(1f32, 0f32, 0f32));
 
-		    // sixth side
-		    vertices.push(
-			Vector3::
-			new(x as f32+VOXEL_SIZE as f32, y as f32+VOXEL_SIZE as f32, z as f32+VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32 + VOXEL_SIZE as f32, y as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32 + VOXEL_SIZE as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    vertices.push(
-			Vector3::
-			new(x as f32, y as f32, z as f32 + VOXEL_SIZE as f32)
-		    );
-		    push_quad_normal(&mut normals, Vector3::new(0f32, 0f32, 1f32));
+                    // sixth side
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, 0f32, 1f32));
 
-
-		    indices.push(0 as u16 + vertex_offset as u16);
-		    indices.push(1 as u16 + vertex_offset as u16);
-		    indices.push(2 as u16 + vertex_offset as u16);
-		    indices.push(2 as u16 + vertex_offset as u16);
-		    indices.push(1 as u16 + vertex_offset as u16);
-		    indices.push(3 as u16 + vertex_offset as u16);
+                    indices.push(0 as u16 + vertex_offset as u16);
+                    indices.push(1 as u16 + vertex_offset as u16);
+                    indices.push(2 as u16 + vertex_offset as u16);
+                    indices.push(2 as u16 + vertex_offset as u16);
+                    indices.push(1 as u16 + vertex_offset as u16);
+                    indices.push(3 as u16 + vertex_offset as u16);
 
-		    indices.push(4 as u16 + vertex_offset as u16);
-		    indices.push(6 as u16 + vertex_offset as u16);
-		    indices.push(5 as u16 + vertex_offset as u16);
-		    indices.push(5 as u16 + vertex_offset as u16);
-		    indices.push(6 as u16 + vertex_offset as u16);
-		    indices.push(7 as u16 + vertex_offset as u16);
-
-		    indices.push(8 as u16 + vertex_offset as u16);
-		    indices.push(9 as u16 + vertex_offset as u16);
-		    indices.push(10 as u16 + vertex_offset as u16);
-		    indices.push(10 as u16 + vertex_offset as u16);
-		    indices.push(9 as u16 + vertex_offset as u16);
-		    indices.push(11 as u16 + vertex_offset as u16);
+                    indices.push(4 as u16 + vertex_offset as u16);
+                    indices.push(6 as u16 + vertex_offset as u16);
+                    indices.push(5 as u16 + vertex_offset as u16);
+                    indices.push(5 as u16 + vertex_offset as u16);
+                    indices.push(6 as u16 + vertex_offset as u16);
+                    indices.push(7 as u16 + vertex_offset as u16);
 
-		    indices.push(12 as u16 + vertex_offset as u16);
-		    indices.push(14 as u16 + vertex_offset as u16);
-		    indices.push(13 as u16 + vertex_offset as u16);
-		    indices.push(13 as u16 + vertex_offset as u16);
-		    indices.push(14 as u16 + vertex_offset as u16);
-		    indices.push(15 as u16 + vertex_offset as u16);
+                    indices.push(8 as u16 + vertex_offset as u16);
+                    indices.push(9 as u16 + vertex_offset as u16);
+                    indices.push(10 as u16 + vertex_offset as u16);
+                    indices.push(10 as u16 + vertex_offset as u16);
+                    indices.push(9 as u16 + vertex_offset as u16);
+                    indices.push(11 as u16 + vertex_offset as u16);
 
-		    indices.push(16 as u16 + vertex_offset as u16);
-		    indices.push(17 as u16 + vertex_offset as u16);
-		    indices.push(18 as u16 + vertex_offset as u16);
-		    indices.push(18 as u16 + vertex_offset as u16);
-		    indices.push(17 as u16 + vertex_offset as u16);
-		    indices.push(19 as u16 + vertex_offset as u16);
+                    indices.push(12 as u16 + vertex_offset as u16);
+                    indices.push(14 as u16 + vertex_offset as u16);
+                    indices.push(13 as u16 + vertex_offset as u16);
+                    indices.push(13 as u16 + vertex_offset as u16);
+                    indices.push(14 as u16 + vertex_offset as u16);
+                    indices.push(15 as u16 + vertex_offset as u16);
 
-		    indices.push(20 as u16 + vertex_offset as u16);
-		    indices.push(22 as u16 + vertex_offset as u16);
-		    indices.push(21 as u16 + vertex_offset as u16);
-		    indices.push(21 as u16 + vertex_offset as u16);
-		    indices.push(22 as u16 + vertex_offset as u16);
-		    indices.push(23 as u16 + vertex_offset as u16);
+                    indices.push(16 as u16 + vertex_offset as u16);
+                    indices.push(17 as u16 + vertex_offset as u16);
+                    indices.push(18 as u16 + vertex_offset as u16);
+                    indices.push(18 as u16 + vertex_offset as u16);
+                    indices.push(17 as u16 + vertex_offset as u16);
+                    indices.push(19 as u16 + vertex_offset as u16);
 
-
-
-		    vertex_offset += 24;
+                    indices.push(20 as u16 + vertex_offset as u16);
+                    indices.push(22 as u16 + vertex_offset as u16);
+                    indices.push(21 as u16 + vertex_offset as u16);
+                    indices.push(21 as u16 + vertex_offset as u16);
+                    indices.push(22 as u16 + vertex_offset as u16);
+                    indices.push(23 as u16 + vertex_offset as u16);
 
-		}
-	    }
-	}
+                    vertex_offset += 24;
+                }
+            }
+        }
 
-	let vertex_count = vertices.len();
-	let index_count = indices.len();
-	let normals_count = normals.len();
+        let vertex_count = vertices.len();
+        let index_count = indices.len();
+        let normals_count = normals.len();
 
-	if vertex_count == 0 {
-	    self.mesh = None;
-	    return;
-	}
+        if vertex_count == 0 {
+            self.mesh = None;
+            return;
+        }
 
-	unsafe {
-	    let v_size = vertex_count * std::mem::size_of::<Vector3>();
+        unsafe {
+            let v_size = vertex_count * std::mem::size_of::<Vector3>();
             let i_size = index_count * std::mem::size_of::<u16>();
-	    let n_size = normals_count * std::mem::size_of::<Vector3>();
+            let n_size = normals_count * std::mem::size_of::<Vector3>();
 
             let v_ptr = rlb::ffi::MemAlloc(v_size as u32) as *mut Vector3;
             let i_ptr = rlb::ffi::MemAlloc(i_size as u32) as *mut u16;
-	    let n_ptr = rlb::ffi::MemAlloc(n_size as u32) as *mut Vector3;
+            let n_ptr = rlb::ffi::MemAlloc(n_size as u32) as *mut Vector3;
 
             std::ptr::copy_nonoverlapping(vertices.as_ptr(), v_ptr, vertex_count);
             std::ptr::copy_nonoverlapping(indices.as_ptr(), i_ptr, index_count);
-	    std::ptr::copy_nonoverlapping(normals.as_ptr(), n_ptr, normals_count);
+            std::ptr::copy_nonoverlapping(normals.as_ptr(), n_ptr, normals_count);
 
             let raw_mesh = rlb::ffi::Mesh {
-		vertexCount: vertex_count as i32,
-		triangleCount: (index_count / 3) as i32,
-		vertices: v_ptr as *mut std::ffi::c_float,
-		indices: i_ptr,
-		normals: n_ptr as *mut std::ffi::c_float,
-		texcoords: std::ptr::null_mut(),
-		texcoords2: std::ptr::null_mut(),
-		colors: std::ptr::null_mut(),
-		tangents: std::ptr::null_mut(),
-		animVertices: std::ptr::null_mut(),
-		animNormals: std::ptr::null_mut(),
-		boneCount: 0,
-		boneIndices: std::ptr::null_mut(),
-		boneWeights: std::ptr::null_mut(),
-		vaoId: 0,
-		vboId: std::ptr::null_mut(),
-	    };
+                vertexCount: vertex_count as i32,
+                triangleCount: (index_count / 3) as i32,
+                vertices: v_ptr as *mut std::ffi::c_float,
+                indices: i_ptr,
+                normals: n_ptr as *mut std::ffi::c_float,
+                texcoords: std::ptr::null_mut(),
+                texcoords2: std::ptr::null_mut(),
+                colors: std::ptr::null_mut(),
+                tangents: std::ptr::null_mut(),
+                animVertices: std::ptr::null_mut(),
+                animNormals: std::ptr::null_mut(),
+                boneCount: 0,
+                boneIndices: std::ptr::null_mut(),
+                boneWeights: std::ptr::null_mut(),
+                vaoId: 0,
+                vboId: std::ptr::null_mut(),
+            };
 
-	    let mut mesh = Mesh::from_raw(raw_mesh);
-
-	    mesh.upload(false);
+            let mut mesh = Mesh::from_raw(raw_mesh);
 
-	    self.mesh = Some(mesh);
-	}
-	
+            mesh.upload(false);
+
+            self.mesh = Some(mesh);
+        }
     }
 
     // returns voxel by chunk coordinates
@@ -345,39 +348,44 @@
     }
 
     pub fn get_chunk_aabb(&self) -> Aabb {
-	Aabb {
-	    min: self.chunk_pos * CHUNK_SIZE as f32,
-	    max: self.chunk_pos * CHUNK_SIZE as f32 + Vector3::new
-		(CHUNK_SIZE as f32, CHUNK_SIZE as f32, CHUNK_SIZE as f32),
-	}
+        Aabb {
+            min: self.chunk_pos * CHUNK_SIZE as f32,
+            max: self.chunk_pos * CHUNK_SIZE as f32
+                + Vector3::new(CHUNK_SIZE as f32, CHUNK_SIZE as f32, CHUNK_SIZE as f32),
+        }
     }
 
     pub fn get_voxel_global_aabb(&self, idx: usize) -> Aabb {
-	Aabb {
-	    min: self.global_voxel_pos(idx),
-	    max: self.global_voxel_pos(idx) + Vector3::new
-		(VOXEL_SIZE as f32, VOXEL_SIZE as f32, VOXEL_SIZE as f32),
-	}
+        Aabb {
+            min: self.global_voxel_pos(idx),
+            max: self.global_voxel_pos(idx)
+                + Vector3::new(VOXEL_SIZE as f32, VOXEL_SIZE as f32, VOXEL_SIZE as f32),
+        }
     }
 
     pub fn get_voxel_pos_glb(&self, x: i32, y: i32, z: i32) -> (i32, i32, i32) {
-	let chunk_x: i32;
-	let chunk_y: i32;
-	let chunk_z: i32;
+        let chunk_x: i32;
+        let chunk_y: i32;
+        let chunk_z: i32;
 
-	chunk_x = (x as f32 - self.chunk_pos.x * CHUNK_SIZE as f32) as i32;
-	chunk_y = (y as f32 - self.chunk_pos.y * CHUNK_SIZE as f32) as i32;
-	chunk_z = (z as f32 - self.chunk_pos.z * CHUNK_SIZE as f32) as i32;
+        chunk_x = (x as f32 - self.chunk_pos.x * CHUNK_SIZE as f32) as i32;
+        chunk_y = (y as f32 - self.chunk_pos.y * CHUNK_SIZE as f32) as i32;
+        chunk_z = (z as f32 - self.chunk_pos.z * CHUNK_SIZE as f32) as i32;
 
-	(chunk_x, chunk_y, chunk_z)
+        (chunk_x, chunk_y, chunk_z)
     }
 
     // detects, whether voxel is exoped or not
-    fn is_exposed(&self, x: usize, y: usize, z: usize) -> bool { // pending: check not only current chunk,
-        let directions = [                                       //          but also surrounding chunks
-            (1, 0, 0), (-1, 0, 0),
-            (0, 1, 0), (0, -1, 0),
-            (0, 0, 1), (0, 0, -1),
+    fn is_exposed(&self, x: usize, y: usize, z: usize) -> bool {
+        // pending: check not only current chunk,
+        let directions = [
+            //          but also surrounding chunks
+            (1, 0, 0),
+            (-1, 0, 0),
+            (0, 1, 0),
+            (0, -1, 0),
+            (0, 0, 1),
+            (0, 0, -1),
         ];
 
         for (dx, dy, dz) in directions.iter() {
@@ -385,8 +393,13 @@
             let ny = y as isize + dy;
             let nz = z as isize + dz;
 
-            if nx < 0 || ny < 0 || nz < 0 || nx >= CHUNK_SIZE as isize ||
-		ny >= CHUNK_SIZE as isize || nz >= CHUNK_SIZE as isize {
+            if nx < 0
+                || ny < 0
+                || nz < 0
+                || nx >= CHUNK_SIZE as isize
+                || ny >= CHUNK_SIZE as isize
+                || nz >= CHUNK_SIZE as isize
+            {
                 return true;
             }
 
@@ -405,15 +418,18 @@
         for x in 0..CHUNK_SIZE {
             for y in 0..CHUNK_SIZE {
                 for z in 0..CHUNK_SIZE {
-                    if let Some(Voxel::Air) = self.get_voxel(x, y, z) { continue; }
+                    if let Some(Voxel::Air) = self.get_voxel(x, y, z) {
+                        continue;
+                    }
 
                     if self.is_exposed(x, y, z) {
                         ctx.draw_cube(
-                            self.chunk_pos * CHUNK_SIZE as f32 + Vector3::new(x as f32, y as f32, z as f32), 
-                            voxel::VOXEL_SIZE as f32, 
-                            voxel::VOXEL_SIZE as f32, 
-                            voxel::VOXEL_SIZE as f32, 
-                            rlb::color::Color::GRAY
+                            self.chunk_pos * CHUNK_SIZE as f32
+                                + Vector3::new(x as f32, y as f32, z as f32),
+                            voxel::VOXEL_SIZE as f32,
+                            voxel::VOXEL_SIZE as f32,
+                            voxel::VOXEL_SIZE as f32,
+                            rlb::color::Color::GRAY,
                         );
                     }
                 }
@@ -423,54 +439,52 @@
     }
 
     pub fn save(&self) {
-	let dir = "saves";
-	let _ = fs::create_dir_all(dir);
-	let path = format!("{}/chunk_{}_{}_{}.bin", dir,
-			   self.chunk_pos.x as i32,
-			   self.chunk_pos.y as i32,
-			   self.chunk_pos.z as i32,
-	);
+        let dir = "saves";
+        let _ = fs::create_dir_all(dir);
+        let path = format!(
+            "{}/chunk_{}_{}_{}.bin",
+            dir, self.chunk_pos.x as i32, self.chunk_pos.y as i32, self.chunk_pos.z as i32,
+        );
 
-	let mut bytes = Vec::with_capacity(self.voxels.len());
-	for v in &self.voxels {
-	    bytes.push(match v {
-		Voxel::Air => 0u8,
-		Voxel::Stone => 1u8,
-	    });
-	}
+        let mut bytes = Vec::with_capacity(self.voxels.len());
+        for v in &self.voxels {
+            bytes.push(match v {
+                Voxel::Air => 0u8,
+                Voxel::Stone => 1u8,
+            });
+        }
 
-	let _ = fs::write(path, bytes);
+        let _ = fs::write(path, bytes);
     }
 
     pub fn load_from_disk(pos: Vector3) -> Option<Self> {
-	let path = format!("saves/chunk_{}_{}_{}.bin",
-			   pos.x as i32, pos.y as i32, pos.z as i32,
-	);
-	if let Ok(bytes) = fs::read(path) {
-	    if bytes.len() == CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE {
-		let mut voxels = [Voxel::Air; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
-		for (i, &b) in bytes.iter().enumerate() {
-		    voxels[i] = match b {
-			1u8 => Voxel::Stone,
-			_ => Voxel::Air,
-		    };
-		}
-		return Some(Self{
-		    chunk_pos: pos,
-		    voxels,
-		    dirty: true,
-		    mesh: None,
-		});
-	    }
-	}
-	None
+        let path = format!(
+            "saves/chunk_{}_{}_{}.bin",
+            pos.x as i32, pos.y as i32, pos.z as i32,
+        );
+        if let Ok(bytes) = fs::read(path) {
+            if bytes.len() == CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE {
+                let mut voxels = [Voxel::Air; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
+                for (i, &b) in bytes.iter().enumerate() {
+                    voxels[i] = match b {
+                        1u8 => Voxel::Stone,
+                        _ => Voxel::Air,
+                    };
+                }
+                return Some(Self {
+                    chunk_pos: pos,
+                    voxels,
+                    dirty: true,
+                    mesh: None,
+                });
+            }
+        }
+        None
     }
 }
 
-
 fn push_quad_normal(normals: &mut Vec<Vector3>, n: Vector3) {
     for _ in 0..4 {
         normals.push(n);
     }
 }
-
diff --git a/src/chunk/mod.rs.orig b/src/chunk/mod.rs.orig
new file mode 100644
--- /dev/null
+++ b/src/chunk/mod.rs.orig
@@ -0,0 +1,490 @@
+// external deps
+use super::rlb;
+use rlb::{
+    prelude::*,
+    // core::*,
+    // consts::*,
+};
+use std::fs;
+
+// internal deps
+use crate::util::*;
+use crate::voxel::{self, VOXEL_SIZE, Voxel};
+
+// constants
+pub const CHUNK_SIZE: usize = 32;
+
+pub struct Chunk {
+    pub chunk_pos: Vector3,
+    pub voxels: [Voxel; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE],
+    pub dirty: bool,
+    pub mesh: Option<Mesh>,
+} // pending: add events stack
+
+impl Chunk {
+    // constructor
+    pub fn new(pos: Vector3) -> Self {
+        let mut s = Self {
+            chunk_pos: pos,
+            voxels: [Voxel::Air; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE],
+            dirty: true,
+            mesh: None,
+        };
+        for x in 0..CHUNK_SIZE {
+            for z in 0..CHUNK_SIZE {
+                let y = (x + z).min(CHUNK_SIZE - 1);
+                let i = x + (y * CHUNK_SIZE) + (z * CHUNK_SIZE * CHUNK_SIZE);
+                s.voxels[i] = Voxel::Stone;
+            }
+        }
+        s
+    }
+
+    pub fn is_visible(&self, cam: &Camera3D) -> bool {
+	let aabb = self.get_chunk_aabb();
+	let center = Vector3::new(
+	    (aabb.min.x + aabb.max.x) *0.5f32,
+	    (aabb.min.y + aabb.max.y) *0.5f32,
+	    (aabb.min.z + aabb.max.z) *0.5f32,
+	);
+
+	let dx = aabb.max.x - aabb.min.x;
+	let dy = aabb.max.y - aabb.min.y;
+	let dz = aabb.max.z - aabb.min.z;
+
+	let radius = (dx*dx + dy*dy + dz*dz).sqrt() * 0.5;
+
+	
+	let ddx = center.x - cam.position.x;
+	let ddy = center.y - cam.position.y;
+	let ddz = center.z - cam.position.z;
+
+	let dist = (ddx*ddx + ddy*ddy + ddz*ddz).sqrt();
+
+	let fx = cam.target.x - cam.position.x;
+	let fy = cam.target.y - cam.position.y;
+	let fz = cam.target.z - cam.position.z;
+
+	let flen = (fx*fx + fy*fy + fz*fz).sqrt();
+
+	let cos_angle = (ddx*fx + ddy*fy + ddz*fz) / (dist*flen);
+
+	let half_fov = (cam.fovy.to_radians() / 2f32) * 1f32;
+	let angular_margin = (radius / dist).atan();
+
+	if dist <= CHUNK_SIZE as f32 {
+	    return true;
+	}
+
+	cos_angle.acos() <= half_fov + angular_margin
+    }
+    
+    pub fn generate_mesh(&mut self, rl: &RaylibThread) {
+        let mut vertices: Vec<Vector3> = Vec::new();
+        let mut normals: Vec<Vector3> = Vec::new();
+        let mut indices: Vec<u16> = Vec::new();
+
+        let mut vertex_offset: i32 = 0;
+        for x in 0..CHUNK_SIZE {
+            for y in 0..CHUNK_SIZE {
+                for z in 0..CHUNK_SIZE {
+                    if let Some(Voxel::Air) = self.get_voxel(x, y, z) {
+                        continue;
+                    }
+                    if !self.is_exposed(x, y, z) {
+                        continue;
+                    }
+
+                    // first side
+                    vertices.push(Vector3::new(x as f32, y as f32, z as f32));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, 0f32, -1f32));
+
+                    // second side
+                    vertices.push(Vector3::new(x as f32, y as f32, z as f32));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(-1f32, 0f32, 0f32));
+
+                    // third side
+                    vertices.push(Vector3::new(x as f32, y as f32, z as f32));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, -1f32, 0f32));
+
+                    // fourth side
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, 1f32, 0f32));
+
+                    // fifth side
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(1f32, 0f32, 0f32));
+
+                    // sixth side
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32 + VOXEL_SIZE as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32 + VOXEL_SIZE as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    vertices.push(Vector3::new(
+                        x as f32,
+                        y as f32,
+                        z as f32 + VOXEL_SIZE as f32,
+                    ));
+                    push_quad_normal(&mut normals, Vector3::new(0f32, 0f32, 1f32));
+
+                    indices.push(0 as u16 + vertex_offset as u16);
+                    indices.push(1 as u16 + vertex_offset as u16);
+                    indices.push(2 as u16 + vertex_offset as u16);
+                    indices.push(2 as u16 + vertex_offset as u16);
+                    indices.push(1 as u16 + vertex_offset as u16);
+                    indices.push(3 as u16 + vertex_offset as u16);
+
+                    indices.push(4 as u16 + vertex_offset as u16);
+                    indices.push(6 as u16 + vertex_offset as u16);
+                    indices.push(5 as u16 + vertex_offset as u16);
+                    indices.push(5 as u16 + vertex_offset as u16);
+                    indices.push(6 as u16 + vertex_offset as u16);
+                    indices.push(7 as u16 + vertex_offset as u16);
+
+                    indices.push(8 as u16 + vertex_offset as u16);
+                    indices.push(9 as u16 + vertex_offset as u16);
+                    indices.push(10 as u16 + vertex_offset as u16);
+                    indices.push(10 as u16 + vertex_offset as u16);
+                    indices.push(9 as u16 + vertex_offset as u16);
+                    indices.push(11 as u16 + vertex_offset as u16);
+
+                    indices.push(12 as u16 + vertex_offset as u16);
+                    indices.push(14 as u16 + vertex_offset as u16);
+                    indices.push(13 as u16 + vertex_offset as u16);
+                    indices.push(13 as u16 + vertex_offset as u16);
+                    indices.push(14 as u16 + vertex_offset as u16);
+                    indices.push(15 as u16 + vertex_offset as u16);
+
+                    indices.push(16 as u16 + vertex_offset as u16);
+                    indices.push(17 as u16 + vertex_offset as u16);
+                    indices.push(18 as u16 + vertex_offset as u16);
+                    indices.push(18 as u16 + vertex_offset as u16);
+                    indices.push(17 as u16 + vertex_offset as u16);
+                    indices.push(19 as u16 + vertex_offset as u16);
+
+                    indices.push(20 as u16 + vertex_offset as u16);
+                    indices.push(22 as u16 + vertex_offset as u16);
+                    indices.push(21 as u16 + vertex_offset as u16);
+                    indices.push(21 as u16 + vertex_offset as u16);
+                    indices.push(22 as u16 + vertex_offset as u16);
+                    indices.push(23 as u16 + vertex_offset as u16);
+
+                    vertex_offset += 24;
+                }
+            }
+        }
+
+        let vertex_count = vertices.len();
+        let index_count = indices.len();
+        let normals_count = normals.len();
+
+        if vertex_count == 0 {
+            self.mesh = None;
+            return;
+        }
+
+        unsafe {
+            let v_size = vertex_count * std::mem::size_of::<Vector3>();
+            let i_size = index_count * std::mem::size_of::<u16>();
+            let n_size = normals_count * std::mem::size_of::<Vector3>();
+
+            let v_ptr = rlb::ffi::MemAlloc(v_size as u32) as *mut Vector3;
+            let i_ptr = rlb::ffi::MemAlloc(i_size as u32) as *mut u16;
+            let n_ptr = rlb::ffi::MemAlloc(n_size as u32) as *mut Vector3;
+
+            std::ptr::copy_nonoverlapping(vertices.as_ptr(), v_ptr, vertex_count);
+            std::ptr::copy_nonoverlapping(indices.as_ptr(), i_ptr, index_count);
+            std::ptr::copy_nonoverlapping(normals.as_ptr(), n_ptr, normals_count);
+
+            let raw_mesh = rlb::ffi::Mesh {
+                vertexCount: vertex_count as i32,
+                triangleCount: (index_count / 3) as i32,
+                vertices: v_ptr as *mut std::ffi::c_float,
+                indices: i_ptr,
+                normals: n_ptr as *mut std::ffi::c_float,
+                texcoords: std::ptr::null_mut(),
+                texcoords2: std::ptr::null_mut(),
+                colors: std::ptr::null_mut(),
+                tangents: std::ptr::null_mut(),
+                animVertices: std::ptr::null_mut(),
+                animNormals: std::ptr::null_mut(),
+                boneCount: 0,
+                boneIndices: std::ptr::null_mut(),
+                boneWeights: std::ptr::null_mut(),
+                vaoId: 0,
+                vboId: std::ptr::null_mut(),
+            };
+
+            let mut mesh = Mesh::from_raw(raw_mesh);
+
+            mesh.upload(false);
+
+            self.mesh = Some(mesh);
+        }
+    }
+
+    // returns voxel by chunk coordinates
+    pub fn get_voxel(&self, x: usize, y: usize, z: usize) -> Option<&Voxel> {
+        if x >= CHUNK_SIZE || y >= CHUNK_SIZE || z >= CHUNK_SIZE {
+            return None;
+        }
+        let idx = x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
+        Some(&self.voxels[idx])
+    }
+
+    // returns voxel chunk index in chunk by chunk coordinates
+    pub fn voxel_idx(&self, x: usize, y: usize, z: usize) -> usize {
+        x + (y * CHUNK_SIZE) + (z * CHUNK_SIZE * CHUNK_SIZE)
+    }
+
+    // returns voxel chunk position by its chunk index
+    pub fn voxel_pos(&self, idx: usize) -> Vector3 {
+        assert!(idx < CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE);
+
+        let x = (idx % CHUNK_SIZE) as f32;
+        let y = ((idx / CHUNK_SIZE) % CHUNK_SIZE) as f32;
+        let z = (idx / (CHUNK_SIZE * CHUNK_SIZE)) as f32;
+
+        Vector3::new(x, y, z)
+    }
+
+    // returns global voxel position by its chunk index
+    pub fn global_voxel_pos(&self, idx: usize) -> Vector3 {
+        self.chunk_pos * CHUNK_SIZE as f32 + self.voxel_pos(idx)
+    }
+
+    pub fn get_chunk_aabb(&self) -> Aabb {
+        Aabb {
+            min: self.chunk_pos * CHUNK_SIZE as f32,
+            max: self.chunk_pos * CHUNK_SIZE as f32
+                + Vector3::new(CHUNK_SIZE as f32, CHUNK_SIZE as f32, CHUNK_SIZE as f32),
+        }
+    }
+
+    pub fn get_voxel_global_aabb(&self, idx: usize) -> Aabb {
+        Aabb {
+            min: self.global_voxel_pos(idx),
+            max: self.global_voxel_pos(idx)
+                + Vector3::new(VOXEL_SIZE as f32, VOXEL_SIZE as f32, VOXEL_SIZE as f32),
+        }
+    }
+
+    pub fn get_voxel_pos_glb(&self, x: i32, y: i32, z: i32) -> (i32, i32, i32) {
+        let chunk_x: i32;
+        let chunk_y: i32;
+        let chunk_z: i32;
+
+        chunk_x = (x as f32 - self.chunk_pos.x * CHUNK_SIZE as f32) as i32;
+        chunk_y = (y as f32 - self.chunk_pos.y * CHUNK_SIZE as f32) as i32;
+        chunk_z = (z as f32 - self.chunk_pos.z * CHUNK_SIZE as f32) as i32;
+
+        (chunk_x, chunk_y, chunk_z)
+    }
+
+    // detects, whether voxel is exoped or not
+    fn is_exposed(&self, x: usize, y: usize, z: usize) -> bool {
+        // pending: check not only current chunk,
+        let directions = [
+            //          but also surrounding chunks
+            (1, 0, 0),
+            (-1, 0, 0),
+            (0, 1, 0),
+            (0, -1, 0),
+            (0, 0, 1),
+            (0, 0, -1),
+        ];
+
+        for (dx, dy, dz) in directions.iter() {
+            let nx = x as isize + dx;
+            let ny = y as isize + dy;
+            let nz = z as isize + dz;
+
+            if nx < 0
+                || ny < 0
+                || nz < 0
+                || nx >= CHUNK_SIZE as isize
+                || ny >= CHUNK_SIZE as isize
+                || nz >= CHUNK_SIZE as isize
+            {
+                return true;
+            }
+
+            if let Some(Voxel::Air) = self.get_voxel(nx as usize, ny as usize, nz as usize) {
+                return true;
+            }
+        }
+        false
+    }
+
+    // renders chunk; MUST be used inside raylib's draw function
+    pub fn draw(
+        &self,
+        ctx: &mut RaylibMode3D<'_, RaylibTextureMode<'_, RaylibDrawHandle<'_>>>,
+    ) -> &Self {
+        for x in 0..CHUNK_SIZE {
+            for y in 0..CHUNK_SIZE {
+                for z in 0..CHUNK_SIZE {
+                    if let Some(Voxel::Air) = self.get_voxel(x, y, z) {
+                        continue;
+                    }
+
+                    if self.is_exposed(x, y, z) {
+                        ctx.draw_cube(
+                            self.chunk_pos * CHUNK_SIZE as f32
+                                + Vector3::new(x as f32, y as f32, z as f32),
+                            voxel::VOXEL_SIZE as f32,
+                            voxel::VOXEL_SIZE as f32,
+                            voxel::VOXEL_SIZE as f32,
+                            rlb::color::Color::GRAY,
+                        );
+                    }
+                }
+            }
+        }
+        self
+    }
+
+    pub fn save(&self) {
+        let dir = "saves";
+        let _ = fs::create_dir_all(dir);
+        let path = format!(
+            "{}/chunk_{}_{}_{}.bin",
+            dir, self.chunk_pos.x as i32, self.chunk_pos.y as i32, self.chunk_pos.z as i32,
+        );
+
+        let mut bytes = Vec::with_capacity(self.voxels.len());
+        for v in &self.voxels {
+            bytes.push(match v {
+                Voxel::Air => 0u8,
+                Voxel::Stone => 1u8,
+            });
+        }
+
+        let _ = fs::write(path, bytes);
+    }
+
+    pub fn load_from_disk(pos: Vector3) -> Option<Self> {
+        let path = format!(
+            "saves/chunk_{}_{}_{}.bin",
+            pos.x as i32, pos.y as i32, pos.z as i32,
+        );
+        if let Ok(bytes) = fs::read(path) {
+            if bytes.len() == CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE {
+                let mut voxels = [Voxel::Air; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
+                for (i, &b) in bytes.iter().enumerate() {
+                    voxels[i] = match b {
+                        1u8 => Voxel::Stone,
+                        _ => Voxel::Air,
+                    };
+                }
+                return Some(Self {
+                    chunk_pos: pos,
+                    voxels,
+                    dirty: true,
+                    mesh: None,
+                });
+            }
+        }
+        None
+    }
+}
+
+fn push_quad_normal(normals: &mut Vec<Vector3>, n: Vector3) {
+    for _ in 0..4 {
+        normals.push(n);
+    }
+}
diff --git a/src/main.rs b/src/main.rs
--- a/src/main.rs
+++ b/src/main.rs
@@ -31,20 +31,37 @@
     rl.set_trace_log(TraceLogLevel::LOG_WARNING);
 
     // shader stuff
-    let mut shader = rl.load_shader(&rl_thd, Some("shaders/vert.glsl"), Some("shaders/frag.glsl")).unwrap();
-    let mut post_shader = rl.load_shader(&rl_thd, None, Some("shaders/post.glsl")).unwrap();
+    let mut shader = rl
+        .load_shader(
+            &rl_thd,
+            Some("shaders/vert.glsl"),
+            Some("shaders/frag.glsl"),
+        )
+        .unwrap();
+    let mut post_shader = rl
+        .load_shader(&rl_thd, None, Some("shaders/post.glsl"))
+        .unwrap();
 
     let depth_loc = post_shader.get_shader_location("depth_tex");
+    let normal_loc = post_shader.get_shader_location("normal_tex");
+    let loc_tan = post_shader.get_shader_location("tan_half_fov");
+    let loc_asp = post_shader.get_shader_location("aspect");
 
-    assert!(depth_loc >= 0, "Uniform depth_tex not found");
-    
+    // assert!(depth_loc >= 0, "Uniform depth_tex not found: {}", depth_loc);
+    // assert!(
+    //     normal_loc >= 0,
+    //     "Uniform normal_tex not found: {}",
+    //     normal_loc
+    // );
+
     // let mut rend_tex = rl.load_render_texture
     // 	(&rl_thd, rl.get_render_width() as u32, rl.get_render_height() as u32).unwrap();
 
     let mut chunk_material = rl.load_material_default(&rl_thd);
-    let mut rend_tex = unsafe { load_depth_tex(rl.get_render_width(), rl.get_render_height()) };
+    let (mut rend_tex, mut normal_tex_id) =
+        load_depth_tex(rl.get_render_width(), rl.get_render_height());
     unsafe {
-	*chunk_material.shader_mut() = shader.make_weak();	
+        *chunk_material.shader_mut() = shader.make_weak();
     }
     // chunk_material.set_shader(&shader);
 
@@ -62,61 +79,68 @@
 
     // main game loop
     while !rl.window_should_close() {
-	world.load_around_player(plr.get_eye_pos());
-	world.update_chunks(&rl_thd);
+        world.load_around_player(plr.get_eye_pos());
+        world.update_chunks(&rl_thd);
 
-	let active_chunks: Vec<&Chunk> = world.chunks.values().collect();
-	plr.update(&rl, &active_chunks);
-	plr.apply_to_camera(&mut cam);
+        let active_chunks: Vec<&Chunk> = world.chunks.values().collect();
+        plr.update(&rl, &active_chunks);
+        plr.apply_to_camera(&mut cam);
 
 	let fps = rl.get_fps();
-	
-        if rend_tex.width() != rl.get_render_width() || rend_tex.height() != rl.get_render_height() {
-	    rend_tex = unsafe { load_depth_tex(rl.get_render_width(), rl.get_render_height()) };
+
+        if rend_tex.width() != rl.get_render_width() || rend_tex.height() != rl.get_render_height()
+        {
+            unsafe { rlb::ffi::rlUnloadTexture(normal_tex_id) };
+            (rend_tex, normal_tex_id) =
+                load_depth_tex(rl.get_render_width(), rl.get_render_height());
             // rend_tex = rl.load_render_texture
-	    // 	(&rl_thd, rl.get_render_width() as u32, rl.get_render_height() as u32).unwrap();
+            // 	(&rl_thd, rl.get_render_width() as u32, rl.get_render_height() as u32).unwrap();
         }
 
+        post_shader.set_shader_value(loc_tan, (cam.fovy.to_radians() * 0.5).tan());
+        post_shader.set_shader_value(loc_asp, rend_tex.width() as f32 / rend_tex.height() as f32);
+
         rl.draw(&rl_thd, |mut d| {
             d.draw_texture_mode(&rl_thd, &mut rend_tex, |mut tx| {
                 tx.clear_background(Color::new(30, 30, 30, 255));
                 tx.draw_mode3D(cam, |mut d3, _| {
                     d3.draw_grid(100, 1f32);
-		    for chunk in world.chunks.values() {
-			if !chunk.is_visible(&cam) {
-			    continue;
-			}
-			if let Some(ref mesh) = chunk.mesh {
-			    d3.draw_mesh(mesh,
-					 chunk_material.clone(),
-					 Matrix::translate(
-					     chunk.chunk_pos.x * CHUNK_SIZE as f32,
-					     chunk.chunk_pos.y * CHUNK_SIZE as f32,
-					     chunk.chunk_pos.z * CHUNK_SIZE as f32,
-					 )
-			    );
-			}
-			// chunk.draw(&mut shd);
-		    }
+                    for chunk in world.chunks.values() {
+                        if let Some(ref mesh) = chunk.mesh {
+                            d3.draw_mesh(
+                                mesh,
+                                chunk_material.clone(),
+                                Matrix::translate(
+                                    chunk.chunk_pos.x * CHUNK_SIZE as f32,
+                                    chunk.chunk_pos.y * CHUNK_SIZE as f32,
+                                    chunk.chunk_pos.z * CHUNK_SIZE as f32,
+                                ),
+                            );
+                        }
+                        // chunk.draw(&mut shd);
+                    }
                 });
             });
-	    post_shader.set_shader_value_texture(
-		post_shader.get_shader_location("depth_tex"),
-		unsafe { Texture2D::from_raw(rend_tex.depth).make_weak() }
-	    );
+            post_shader
+                .set_shader_value_texture(post_shader.get_shader_location("depth_tex"), unsafe {
+                    Texture2D::from_raw(rend_tex.depth).make_weak()
+                });
             d.draw_shader_mode(&mut post_shader, |mut shd| {
-		unsafe {
-		    rlb::ffi::rlSetUniformSampler(
-			depth_loc,
-			rend_tex.depth.id
-		    );
-		}
-                shd.draw_texture_rec(&rend_tex, Rectangle{
-                    x: 0f32,
-                    y: 0f32,
-                    width: rend_tex.texture().width as f32,
-                    height: -rend_tex.texture().height as f32
-                }, Vector2::zero(), Color::WHITE);
+                unsafe {
+                    rlb::ffi::rlSetUniformSampler(depth_loc, rend_tex.depth.id);
+                    rlb::ffi::rlSetUniformSampler(normal_loc, normal_tex_id);
+                }
+                shd.draw_texture_rec(
+                    &rend_tex,
+                    Rectangle {
+                        x: 0f32,
+                        y: 0f32,
+                        width: rend_tex.texture().width as f32,
+                        height: -rend_tex.texture().height as f32,
+                    },
+                    Vector2::zero(),
+                    Color::WHITE,
+                );
             });
             d.draw_text(
                 &format!(
diff --git a/src/util/mod.rs b/src/util/mod.rs
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -1,5 +1,6 @@
 use super::rlb;
 use rlb::prelude::*;
+use sola_raylib::ffi::rlActiveDrawBuffers;
 // use rlb::core::*;
 
 pub struct Cube {
@@ -10,11 +11,11 @@
 
 impl Cube {
     pub fn new(params: Vector3) -> Self {
-	Self {
-	    width: params.x,
-	    height: params.y,
-	    length: params.z,
-	}
+        Self {
+            width: params.x,
+            height: params.y,
+            length: params.z,
+        }
     }
 }
 
@@ -25,68 +26,89 @@
 }
 
 impl Aabb {
-    pub fn overlaps (&self, other: &Aabb) -> bool {
-	self.min.x < other.max.x && self.max.x > other.min.x &&
-	    self.min.y < other.max.y && self.max.y > other.min.y &&
-	    self.min.z < other.max.z && self.max.z > other.min.z
+    pub fn overlaps(&self, other: &Aabb) -> bool {
+        self.min.x < other.max.x
+            && self.max.x > other.min.x
+            && self.min.y < other.max.y
+            && self.max.y > other.min.y
+            && self.min.z < other.max.z
+            && self.max.z > other.min.z
     }
 }
 
 // had to do this ;(
-pub fn load_depth_tex(width: i32, height: i32) -> RenderTexture2D {
+pub fn load_depth_tex(width: i32, height: i32) -> (RenderTexture2D, u32) {
     unsafe {
-	let fbo_id = rlb::ffi::rlLoadFramebuffer();
-	rlb::ffi::rlEnableFramebuffer(fbo_id);
+        let fbo_id = rlb::ffi::rlLoadFramebuffer();
+        rlb::ffi::rlEnableFramebuffer(fbo_id);
+
+        let col_tex_id = rlb::ffi::rlLoadTexture(
+            std::ptr::null(),
+            width,
+            height,
+            rlb::ffi::PixelFormat::PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 as i32,
+            1,
+        );
 
-	let col_tex_id = rlb::ffi::rlLoadTexture(
-	    std::ptr::null(),
-	    width,
-	    height,
-	    rlb::ffi::PixelFormat::PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 as i32,
-	    1
-	);
+        rlb::ffi::rlFramebufferAttach(
+            fbo_id,
+            col_tex_id,
+            rlb::ffi::rlFramebufferAttachType::RL_ATTACHMENT_COLOR_CHANNEL0 as i32,
+            rlb::ffi::rlFramebufferAttachTextureType::RL_ATTACHMENT_TEXTURE2D as i32,
+            0,
+        );
 
-	rlb::ffi::rlFramebufferAttach(
-	    fbo_id,
-	    col_tex_id,
-	    rlb::ffi::rlFramebufferAttachType::RL_ATTACHMENT_COLOR_CHANNEL0 as i32,
-	    rlb::ffi::rlFramebufferAttachTextureType::RL_ATTACHMENT_TEXTURE2D as i32,
-	    0
-	);
+        let depth_tex_id = rlb::ffi::rlLoadTextureDepth(width, height, false);
 
-	let depth_tex_id = rlb::ffi::rlLoadTextureDepth(width, height, false);
+        rlb::ffi::rlFramebufferAttach(
+            fbo_id,
+            depth_tex_id,
+            rlb::ffi::rlFramebufferAttachType::RL_ATTACHMENT_DEPTH as i32,
+            rlb::ffi::rlFramebufferAttachTextureType::RL_ATTACHMENT_TEXTURE2D as i32,
+            0,
+        );
 
-	rlb::ffi::rlFramebufferAttach(
-	    fbo_id,
-	    depth_tex_id,
-	    rlb::ffi::rlFramebufferAttachType::RL_ATTACHMENT_DEPTH as i32,
-	    rlb::ffi::rlFramebufferAttachTextureType::RL_ATTACHMENT_TEXTURE2D as i32,
-	    0
-	);
-	
-	let fbo_complete = rlb::ffi::rlFramebufferComplete(fbo_id);
-	rlb::ffi::rlDisableFramebuffer();
-	assert!(
-	    fbo_complete,
-	    "Framebuffer incomplete!"
-	);
-	
-	RenderTexture2D::from_raw(rlb::ffi::RenderTexture2D {
-	    id: fbo_id,
-	    texture: rlb::ffi::Texture2D {
-		id: col_tex_id,
-		width,
-		height,
-		mipmaps: 1,
-		format: rlb::ffi::PixelFormat::PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 as i32,
-	    },
-	    depth: rlb::ffi::Texture2D {
-		id: depth_tex_id,
-		width,
-		height,
-		mipmaps: 1,
-		format: 19,
-	    },
-	})
+        let normal_tex_id = rlb::ffi::rlLoadTexture(
+            std::ptr::null(),
+            width,
+            height,
+            rlb::ffi::PixelFormat::PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 as i32,
+            1,
+        );
+
+        rlb::ffi::rlFramebufferAttach(
+            fbo_id,
+            normal_tex_id,
+            rlb::ffi::rlFramebufferAttachType::RL_ATTACHMENT_COLOR_CHANNEL1 as i32,
+            rlb::ffi::rlFramebufferAttachTextureType::RL_ATTACHMENT_TEXTURE2D as i32,
+            0,
+        );
+
+        rlb::ffi::rlActiveDrawBuffers(2);
+
+        let fbo_complete = rlb::ffi::rlFramebufferComplete(fbo_id);
+        rlb::ffi::rlDisableFramebuffer();
+        assert!(fbo_complete, "Framebuffer incomplete!");
+
+        (
+            RenderTexture2D::from_raw(rlb::ffi::RenderTexture2D {
+                id: fbo_id,
+                texture: rlb::ffi::Texture2D {
+                    id: col_tex_id,
+                    width,
+                    height,
+                    mipmaps: 1,
+                    format: rlb::ffi::PixelFormat::PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 as i32,
+                },
+                depth: rlb::ffi::Texture2D {
+                    id: depth_tex_id,
+                    width,
+                    height,
+                    mipmaps: 1,
+                    format: 19,
+                },
+            }),
+            normal_tex_id,
+        )
     }
 }