2
0
Эх сурвалжийг харах

add initial GLES2 3D renderer

karroffel 7 жил өмнө
parent
commit
b64171e79c

+ 88 - 84
drivers/gles2/rasterizer_canvas_gles2.cpp

@@ -54,10 +54,26 @@ void RasterizerCanvasGLES2::_set_uniforms() {
 	state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX, state.uniforms.extra_matrix);
 
 	state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE, state.uniforms.final_modulate);
+
+	state.canvas_shader.set_uniform(CanvasShaderGLES2::TIME, storage->frame.time[0]);
+
+	if (storage->frame.current_rt) {
+		Vector2 screen_pixel_size;
+		screen_pixel_size.x = 1.0 / storage->frame.current_rt->width;
+		screen_pixel_size.y = 1.0 / storage->frame.current_rt->height;
+
+		state.canvas_shader.set_uniform(CanvasShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
+	}
 }
 
 void RasterizerCanvasGLES2::canvas_begin() {
 
+	state.canvas_shader.bind();
+	if (storage->frame.current_rt) {
+		glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo);
+		glColorMask(1, 1, 1, 1);
+	}
+
 	if (storage->frame.clear_request) {
 		glClearColor(storage->frame.clear_request_color.r,
 				storage->frame.clear_request_color.g,
@@ -67,10 +83,12 @@ void RasterizerCanvasGLES2::canvas_begin() {
 		storage->frame.clear_request = false;
 	}
 
+	/*
 	if (storage->frame.current_rt) {
 		glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo);
 		glColorMask(1, 1, 1, 1);
 	}
+	*/
 
 	reset_canvas();
 
@@ -312,7 +330,7 @@ void RasterizerCanvasGLES2::_draw_gui_primitive(int p_points, const Vector2 *p_v
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
 }
 
-void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip) {
+void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material) {
 
 	int command_count = p_item->commands.size();
 	Item::Command **commands = p_item->commands.ptrw();
@@ -329,9 +347,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
-				state.canvas_shader.bind();
-
-				_set_uniforms();
+				if (state.canvas_shader.bind()) {
+					_set_uniforms();
+					state.canvas_shader.use_material((void *)p_material, 2);
+				}
 
 				_bind_canvas_texture(RID(), RID());
 
@@ -359,7 +378,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 
 					_draw_gui_primitive(4, verts, NULL, NULL);
 				}
-
 			} break;
 
 			case Item::Command::TYPE_RECT: {
@@ -373,8 +391,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
-				if (state.canvas_shader.bind())
+				if (state.canvas_shader.bind()) {
 					_set_uniforms();
+					state.canvas_shader.use_material((void *)p_material, 2);
+				}
 
 				RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map);
 
@@ -454,8 +474,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true);
-				if (state.canvas_shader.bind())
+				if (state.canvas_shader.bind()) {
 					_set_uniforms();
+					state.canvas_shader.use_material((void *)p_material, 2);
+				}
 
 				glDisableVertexAttribArray(VS::ARRAY_COLOR);
 				glVertexAttrib4fv(VS::ARRAY_COLOR, np->color.components);
@@ -472,8 +494,16 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 				// state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix);
 				state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
 
+				Rect2 source = np->source;
+				if (source.size.x == 0 && source.size.y == 0) {
+					source.size.x = tex->width;
+					source.size.y = tex->height;
+				}
+
 				// prepare vertex buffer
 
+				// this buffer contains [ POS POS UV UV ] *
+
 				float buffer[16 * 2 + 16 * 2];
 
 				{
@@ -483,106 +513,106 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 					buffer[(0 * 4 * 4) + 0] = np->rect.position.x;
 					buffer[(0 * 4 * 4) + 1] = np->rect.position.y;
 
-					buffer[(0 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x;
-					buffer[(0 * 4 * 4) + 3] = np->source.position.y * texpixel_size.y;
+					buffer[(0 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
+					buffer[(0 * 4 * 4) + 3] = source.position.y * texpixel_size.y;
 
 					buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
 					buffer[(0 * 4 * 4) + 5] = np->rect.position.y;
 
-					buffer[(0 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
-					buffer[(0 * 4 * 4) + 7] = np->source.position.y * texpixel_size.y;
+					buffer[(0 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
+					buffer[(0 * 4 * 4) + 7] = source.position.y * texpixel_size.y;
 
 					buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
 					buffer[(0 * 4 * 4) + 9] = np->rect.position.y;
 
-					buffer[(0 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
-					buffer[(0 * 4 * 4) + 11] = np->source.position.y * texpixel_size.y;
+					buffer[(0 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
+					buffer[(0 * 4 * 4) + 11] = source.position.y * texpixel_size.y;
 
 					buffer[(0 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
 					buffer[(0 * 4 * 4) + 13] = np->rect.position.y;
 
-					buffer[(0 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x;
-					buffer[(0 * 4 * 4) + 15] = np->source.position.y * texpixel_size.y;
+					buffer[(0 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
+					buffer[(0 * 4 * 4) + 15] = source.position.y * texpixel_size.y;
 
 					// second row
 
 					buffer[(1 * 4 * 4) + 0] = np->rect.position.x;
 					buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP];
 
-					buffer[(1 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x;
-					buffer[(1 * 4 * 4) + 3] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+					buffer[(1 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
+					buffer[(1 * 4 * 4) + 3] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
 
 					buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
 					buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP];
 
-					buffer[(1 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
-					buffer[(1 * 4 * 4) + 7] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+					buffer[(1 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
+					buffer[(1 * 4 * 4) + 7] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
 
 					buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
 					buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP];
 
-					buffer[(1 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
-					buffer[(1 * 4 * 4) + 11] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+					buffer[(1 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
+					buffer[(1 * 4 * 4) + 11] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
 
 					buffer[(1 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
 					buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP];
 
-					buffer[(1 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x;
-					buffer[(1 * 4 * 4) + 15] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+					buffer[(1 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
+					buffer[(1 * 4 * 4) + 15] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
 
 					// thrid row
 
 					buffer[(2 * 4 * 4) + 0] = np->rect.position.x;
 					buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
 
-					buffer[(2 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x;
-					buffer[(2 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+					buffer[(2 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
+					buffer[(2 * 4 * 4) + 3] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
 
 					buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
 					buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
 
-					buffer[(2 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
-					buffer[(2 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+					buffer[(2 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
+					buffer[(2 * 4 * 4) + 7] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
 
 					buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
 					buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
 
-					buffer[(2 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
-					buffer[(2 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+					buffer[(2 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
+					buffer[(2 * 4 * 4) + 11] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
 
 					buffer[(2 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
 					buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
 
-					buffer[(2 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x;
-					buffer[(2 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+					buffer[(2 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
+					buffer[(2 * 4 * 4) + 15] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
 
 					// fourth row
 
 					buffer[(3 * 4 * 4) + 0] = np->rect.position.x;
 					buffer[(3 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y;
 
-					buffer[(3 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x;
-					buffer[(3 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y) * texpixel_size.y;
+					buffer[(3 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
+					buffer[(3 * 4 * 4) + 3] = (source.position.y + source.size.y) * texpixel_size.y;
 
 					buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
 					buffer[(3 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y;
 
-					buffer[(3 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
-					buffer[(3 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y) * texpixel_size.y;
+					buffer[(3 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
+					buffer[(3 * 4 * 4) + 7] = (source.position.y + source.size.y) * texpixel_size.y;
 
 					buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
 					buffer[(3 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y;
 
-					buffer[(3 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
-					buffer[(3 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y) * texpixel_size.y;
+					buffer[(3 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
+					buffer[(3 * 4 * 4) + 11] = (source.position.y + source.size.y) * texpixel_size.y;
 
 					buffer[(3 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
 					buffer[(3 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y;
 
-					buffer[(3 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x;
-					buffer[(3 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y) * texpixel_size.y;
+					buffer[(3 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
+					buffer[(3 * 4 * 4) + 15] = (source.position.y + source.size.y) * texpixel_size.y;
 
-					// print_line(String::num((np->source.position.y + np->source.size.y) * texpixel_size.y));
+					// print_line(String::num((source.position.y + source.size.y) * texpixel_size.y));
 				}
 
 				glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices);
@@ -610,8 +640,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
 
-				if (state.canvas_shader.bind())
+				if (state.canvas_shader.bind()) {
 					_set_uniforms();
+					state.canvas_shader.use_material((void *)p_material, 2);
+				}
 
 				static const int num_points = 32;
 
@@ -639,8 +671,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true);
 
-				if (state.canvas_shader.bind())
+				if (state.canvas_shader.bind()) {
 					_set_uniforms();
+					state.canvas_shader.use_material((void *)p_material, 2);
+				}
 
 				RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map);
 
@@ -658,8 +692,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
 
-				if (state.canvas_shader.bind())
+				if (state.canvas_shader.bind()) {
 					_set_uniforms();
+					state.canvas_shader.use_material((void *)p_material, 2);
+				}
 
 				_bind_canvas_texture(RID(), RID());
 
@@ -689,8 +725,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
 				state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true);
 
-				if (state.canvas_shader.bind())
+				if (state.canvas_shader.bind()) {
 					_set_uniforms();
+					state.canvas_shader.use_material((void *)p_material, 2);
+				}
 
 				ERR_CONTINUE(primitive->points.size() < 1);
 
@@ -759,42 +797,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
 void RasterizerCanvasGLES2::_copy_texscreen(const Rect2 &p_rect) {
 
 	// This isn't really working yet, so disabling for now.
-
-	/*
-	glDisable(GL_BLEND);
-
-	state.canvas_texscreen_used = true;
-
-	Vector2 wh(storage->frame.current_rt->width, storage->frame.current_rt->height);
-	Color copy_section(p_rect.position.x / wh.x, p_rect.position.y / wh.y, p_rect.size.x / wh.x, p_rect.size.y / wh.y);
-
-	if (p_rect != Rect2()) {
-		// only use section
-
-		storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, true);
-	}
-
-
-	storage->shaders.copy.bind();
-	storage->shaders.copy.set_uniform(CopyShaderGLES2::COPY_SECTION, copy_section);
-
-	_bind_quad_buffer();
-
-	glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->copy_screen_effect.fbo);
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color);
-
-	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-	glDisableVertexAttribArray(VS::ARRAY_VERTEX);
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-	glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo);
-
-	state.canvas_shader.bind();
-	_bind_canvas_texture(state.current_tex, state.current_normal);
-
-	glEnable(GL_BLEND);
-	*/
 }
 
 void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) {
@@ -850,10 +852,10 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
 		Item *material_owner = ci->material_owner ? ci->material_owner : ci;
 
 		RID material = material_owner->material;
+		RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material);
 
 		if (material != canvas_last_material || rebind_shader) {
 
-			RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material);
 			RasterizerStorageGLES2::Shader *shader_ptr = NULL;
 
 			if (material_ptr) {
@@ -880,7 +882,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
 				}
 
 				int tc = material_ptr->textures.size();
-				RID *textures = material_ptr->textures.ptrw();
+				Pair<StringName, RID> *textures = material_ptr->textures.ptrw();
 
 				ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = shader_ptr->texture_hints.ptrw();
 
@@ -888,7 +890,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
 
 					glActiveTexture(GL_TEXTURE2 + i);
 
-					RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i]);
+					RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i].second);
 
 					if (!t) {
 
@@ -919,10 +921,12 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
 
 					glBindTexture(t->target, t->tex_id);
 				}
+
 			} else {
 				state.canvas_shader.set_custom_shader(0);
 				state.canvas_shader.bind();
 			}
+			state.canvas_shader.use_material((void *)material_ptr, 2);
 
 			shader_cache = shader_ptr;
 
@@ -977,7 +981,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
 
 		_set_uniforms();
 
-		_canvas_item_render_commands(p_item_list, NULL, reclip);
+		_canvas_item_render_commands(p_item_list, NULL, reclip, material_ptr);
 
 		rebind_shader = true; // hacked in for now.
 

+ 1 - 1
drivers/gles2/rasterizer_canvas_gles2.h

@@ -103,7 +103,7 @@ public:
 	_FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
 	_FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
 
-	_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip);
+	_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material);
 	_FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect);
 
 	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);

+ 1 - 3
drivers/gles2/rasterizer_gles2.cpp

@@ -348,10 +348,10 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re
 	canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
 	canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
 
+	canvas->state.canvas_shader.set_custom_shader(0);
 	canvas->state.canvas_shader.bind();
 
 	canvas->canvas_begin();
-	canvas->state.canvas_shader.set_uniform(CanvasShaderGLES2::BLIT_PASS, true);
 	glDisable(GL_BLEND);
 	glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo);
 	glActiveTexture(GL_TEXTURE0);
@@ -361,8 +361,6 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re
 
 	canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1));
 
-	canvas->state.canvas_shader.set_uniform(CanvasShaderGLES2::BLIT_PASS, false);
-
 	glBindTexture(GL_TEXTURE_2D, 0);
 	canvas->canvas_end();
 }

+ 2087 - 14
drivers/gles2/rasterizer_scene_gles2.cpp

@@ -28,6 +28,7 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 #include "rasterizer_scene_gles2.h"
+#include "math/transform.h"
 #include "math_funcs.h"
 #include "os/os.h"
 #include "project_settings.h"
@@ -38,28 +39,385 @@
 #define glClearDepth glClearDepthf
 #endif
 
+static const GLenum _cube_side_enum[6] = {
+
+	GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+	GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+	GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+	GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+	GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
+	GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+
+};
+
 /* SHADOW ATLAS API */
 
 RID RasterizerSceneGLES2::shadow_atlas_create() {
 
-	return RID();
+	ShadowAtlas *shadow_atlas = memnew(ShadowAtlas);
+	shadow_atlas->fbo = 0;
+	shadow_atlas->depth = 0;
+	shadow_atlas->size = 0;
+	shadow_atlas->smallest_subdiv = 0;
+
+	for (int i = 0; i < 4; i++) {
+		shadow_atlas->size_order[i] = i;
+	}
+
+	return shadow_atlas_owner.make_rid(shadow_atlas);
 }
 
 void RasterizerSceneGLES2::shadow_atlas_set_size(RID p_atlas, int p_size) {
+	ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas);
+	ERR_FAIL_COND(!shadow_atlas);
+	ERR_FAIL_COND(p_size < 0);
+
+	p_size = next_power_of_2(p_size);
+
+	if (p_size == shadow_atlas->size)
+		return;
+
+	// erase the old atlast
+	if (shadow_atlas->fbo) {
+		glDeleteTextures(1, &shadow_atlas->depth);
+		glDeleteFramebuffers(1, &shadow_atlas->fbo);
+
+		shadow_atlas->fbo = 0;
+		shadow_atlas->depth = 0;
+	}
+
+	// erase shadow atlast references from lights
+	for (Map<RID, uint32_t>::Element *E = shadow_atlas->shadow_owners.front(); E; E = E->next()) {
+		LightInstance *li = light_instance_owner.getornull(E->key());
+		ERR_CONTINUE(!li);
+		li->shadow_atlases.erase(p_atlas);
+	}
+
+	shadow_atlas->shadow_owners.clear();
+
+	shadow_atlas->size = p_size;
+
+	if (shadow_atlas->size) {
+		glGenFramebuffers(1, &shadow_atlas->fbo);
+		glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo);
+
+		// create a depth texture
+		glActiveTexture(GL_TEXTURE0);
+		glGenTextures(1, &shadow_atlas->depth);
+		glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, shadow_atlas->size, shadow_atlas->size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadow_atlas->depth, 0);
+
+		glViewport(0, 0, shadow_atlas->size, shadow_atlas->size);
+
+		glDepthMask(GL_TRUE);
+
+		glClearDepth(0.0f);
+		glClear(GL_DEPTH_BUFFER_BIT);
+
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	}
 }
 
 void RasterizerSceneGLES2::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) {
+	ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas);
+	ERR_FAIL_COND(!shadow_atlas);
+	ERR_FAIL_INDEX(p_quadrant, 4);
+	ERR_FAIL_INDEX(p_subdivision, 16384);
+
+	uint32_t subdiv = next_power_of_2(p_subdivision);
+	if (subdiv & 0xaaaaaaaa) { // sqrt(subdiv) must be integer
+		subdiv <<= 1;
+	}
+
+	subdiv = int(Math::sqrt((float)subdiv));
+
+	if (shadow_atlas->quadrants[p_quadrant].shadows.size() == subdiv)
+		return;
+
+	// erase all data from the quadrant
+	for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) {
+		if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) {
+			shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
+
+			LightInstance *li = light_instance_owner.getornull(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
+			ERR_CONTINUE(!li);
+			li->shadow_atlases.erase(p_atlas);
+		}
+	}
+
+	shadow_atlas->quadrants[p_quadrant].shadows.resize(0);
+	shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv);
+	shadow_atlas->quadrants[p_quadrant].subdivision = subdiv;
+
+	// cache the smallest subdivision for faster allocations
+
+	shadow_atlas->smallest_subdiv = 1 << 30;
+
+	for (int i = 0; i < 4; i++) {
+		if (shadow_atlas->quadrants[i].subdivision) {
+			shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision);
+		}
+	}
+
+	if (shadow_atlas->smallest_subdiv == 1 << 30) {
+		shadow_atlas->smallest_subdiv = 0;
+	}
+
+	// re-sort the quadrants
+
+	int swaps = 0;
+	do {
+		swaps = 0;
+
+		for (int i = 0; i < 3; i++) {
+			if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) {
+				SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]);
+				swaps++;
+			}
+		}
+
+	} while (swaps > 0);
+}
+
+bool RasterizerSceneGLES2::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow) {
+
+	for (int i = p_quadrant_count - 1; i >= 0; i--) {
+		int qidx = p_in_quadrants[i];
+
+		if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) {
+			return false;
+		}
+
+		// look for an empty space
+
+		int sc = shadow_atlas->quadrants[qidx].shadows.size();
+
+		ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptrw();
+
+		int found_free_idx = -1; // found a free one
+		int found_used_idx = -1; // found an existing one, must steal it
+		uint64_t min_pass = 0; // pass of the existing one, try to use the least recently
+
+		for (int j = 0; j < sc; j++) {
+			if (!sarr[j].owner.is_valid()) {
+				found_free_idx = j;
+				break;
+			}
+
+			LightInstance *sli = light_instance_owner.getornull(sarr[j].owner);
+			ERR_CONTINUE(!sli);
+
+			if (sli->last_scene_pass != scene_pass) {
+
+				// was just allocated, don't kill it so soon, wait a bit...
+
+				if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) {
+					continue;
+				}
+
+				if (found_used_idx == -1 || sli->last_scene_pass < min_pass) {
+					found_used_idx = j;
+					min_pass = sli->last_scene_pass;
+				}
+			}
+		}
+
+		if (found_free_idx == -1 && found_used_idx == -1) {
+			continue; // nothing found
+		}
+
+		if (found_free_idx == -1 && found_used_idx != -1) {
+			found_free_idx = found_used_idx;
+		}
+
+		r_quadrant = qidx;
+		r_shadow = found_free_idx;
+
+		return true;
+	}
+
+	return false;
 }
 
 bool RasterizerSceneGLES2::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) {
+
+	ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas);
+	ERR_FAIL_COND_V(!shadow_atlas, false);
+
+	LightInstance *li = light_instance_owner.getornull(p_light_intance);
+	ERR_FAIL_COND_V(!li, false);
+
+	if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) {
+		return false;
+	}
+
+	uint32_t quad_size = shadow_atlas->size >> 1;
+	int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage));
+
+	int valid_quadrants[4];
+	int valid_quadrant_count = 0;
+	int best_size = -1;
+	int best_subdiv = -1;
+
+	for (int i = 0; i < 4; i++) {
+		int q = shadow_atlas->size_order[i];
+		int sd = shadow_atlas->quadrants[q].subdivision;
+
+		if (sd == 0) {
+			continue;
+		}
+
+		int max_fit = quad_size / sd;
+
+		if (best_size != -1 && max_fit > best_size) {
+			break; // what we asked for is bigger than this.
+		}
+
+		valid_quadrants[valid_quadrant_count] = q;
+		valid_quadrant_count++;
+
+		best_subdiv = sd;
+
+		if (max_fit >= desired_fit) {
+			best_size = max_fit;
+		}
+	}
+
+	ERR_FAIL_COND_V(valid_quadrant_count == 0, false); // no suitable block available
+
+	uint64_t tick = OS::get_singleton()->get_ticks_msec();
+
+	if (shadow_atlas->shadow_owners.has(p_light_intance)) {
+		// light was already known!
+
+		uint32_t key = shadow_atlas->shadow_owners[p_light_intance];
+		uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3;
+		uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK;
+
+		bool should_realloc = shadow_atlas->quadrants[q].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[q].shadows[s].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec);
+
+		bool should_redraw = shadow_atlas->quadrants[q].shadows[s].version != p_light_version;
+
+		if (!should_realloc) {
+			shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version;
+			return should_redraw;
+		}
+
+		int new_quadrant;
+		int new_shadow;
+
+		// find a better place
+
+		if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, shadow_atlas->quadrants[q].subdivision, tick, new_quadrant, new_shadow)) {
+			// found a better place
+
+			ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow];
+			if (sh->owner.is_valid()) {
+				// it is take but invalid, so we can take it
+
+				shadow_atlas->shadow_owners.erase(sh->owner);
+				LightInstance *sli = light_instance_owner.get(sh->owner);
+				sli->shadow_atlases.erase(p_atlas);
+			}
+
+			// erase previous
+			shadow_atlas->quadrants[q].shadows.write[s].version = 0;
+			shadow_atlas->quadrants[q].shadows.write[s].owner = RID();
+
+			sh->owner = p_light_intance;
+			sh->alloc_tick = tick;
+			sh->version = p_light_version;
+			li->shadow_atlases.insert(p_atlas);
+
+			// make a new key
+			key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT;
+			key |= new_shadow;
+
+			// update it in the map
+			shadow_atlas->shadow_owners[p_light_intance] = key;
+
+			// make it dirty, so we redraw
+			return true;
+		}
+
+		// no better place found, so we keep the current place
+
+		shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version;
+
+		return should_redraw;
+	}
+
+	int new_quadrant;
+	int new_shadow;
+
+	if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, -1, tick, new_quadrant, new_shadow)) {
+		// found a better place
+
+		ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow];
+		if (sh->owner.is_valid()) {
+			// it is take but invalid, so we can take it
+
+			shadow_atlas->shadow_owners.erase(sh->owner);
+			LightInstance *sli = light_instance_owner.get(sh->owner);
+			sli->shadow_atlases.erase(p_atlas);
+		}
+
+		sh->owner = p_light_intance;
+		sh->alloc_tick = tick;
+		sh->version = p_light_version;
+		li->shadow_atlases.insert(p_atlas);
+
+		// make a new key
+		uint32_t key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT;
+		key |= new_shadow;
+
+		// update it in the map
+		shadow_atlas->shadow_owners[p_light_intance] = key;
+
+		// make it dirty, so we redraw
+		return true;
+	}
+
 	return false;
 }
 
 void RasterizerSceneGLES2::set_directional_shadow_count(int p_count) {
+	directional_shadow.light_count = p_count;
+	directional_shadow.current_light = 0;
 }
 
 int RasterizerSceneGLES2::get_directional_light_shadow_size(RID p_light_intance) {
-	return 0;
+
+	ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0);
+
+	int shadow_size;
+
+	if (directional_shadow.light_count == 1) {
+		shadow_size = directional_shadow.size;
+	} else {
+		shadow_size = directional_shadow.size / 2; //more than 4 not supported anyway
+	}
+
+	LightInstance *light_instance = light_instance_owner.getornull(p_light_intance);
+	ERR_FAIL_COND_V(!light_instance, 0);
+
+	switch (light_instance->light_ptr->directional_shadow_mode) {
+		case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL:
+			break; //none
+		case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS:
+		case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS:
+			shadow_size /= 2;
+			break;
+	}
+
+	return shadow_size;
 }
 //////////////////////////////////////////////////////
 
@@ -105,86 +463,183 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
 
 RID RasterizerSceneGLES2::environment_create() {
 
-	return RID();
+	Environment *env = memnew(Environment);
+
+	return environment_owner.make_rid(env);
 }
 
 void RasterizerSceneGLES2::environment_set_background(RID p_env, VS::EnvironmentBG p_bg) {
+
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+	env->bg_mode = p_bg;
 }
 
 void RasterizerSceneGLES2::environment_set_sky(RID p_env, RID p_sky) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+
+	env->sky = p_sky;
 }
 
 void RasterizerSceneGLES2::environment_set_sky_custom_fov(RID p_env, float p_scale) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+
+	env->sky_custom_fov = p_scale;
 }
 
 void RasterizerSceneGLES2::environment_set_bg_color(RID p_env, const Color &p_color) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+
+	env->bg_color = p_color;
 }
 
 void RasterizerSceneGLES2::environment_set_bg_energy(RID p_env, float p_energy) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+
+	env->bg_energy = p_energy;
 }
 
 void RasterizerSceneGLES2::environment_set_canvas_max_layer(RID p_env, int p_max_layer) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+
+	env->canvas_max_layer = p_max_layer;
 }
 
 void RasterizerSceneGLES2::environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy, float p_sky_contribution) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+
+	env->ambient_color = p_color;
+	env->ambient_energy = p_energy;
+	env->ambient_sky_contribution = p_sky_contribution;
 }
 
 void RasterizerSceneGLES2::environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_bloom_threshold, VS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, bool p_bicubic_upscale) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 void RasterizerSceneGLES2::environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) {
+	Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
 }
 
 bool RasterizerSceneGLES2::is_environment(RID p_env) {
-	return false;
+	return environment_owner.owns(p_env);
 }
 
 VS::EnvironmentBG RasterizerSceneGLES2::environment_get_background(RID p_env) {
-	return VS::ENV_BG_CLEAR_COLOR;
+	const Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, VS::ENV_BG_MAX);
+
+	return env->bg_mode;
 }
 
 int RasterizerSceneGLES2::environment_get_canvas_max_layer(RID p_env) {
-	return 0;
+	const Environment *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, -1);
+
+	return env->canvas_max_layer;
 }
 
 RID RasterizerSceneGLES2::light_instance_create(RID p_light) {
-	return RID();
+
+	LightInstance *light_instance = memnew(LightInstance);
+
+	light_instance->last_scene_pass = 0;
+
+	light_instance->light = p_light;
+	light_instance->light_ptr = storage->light_owner.getornull(p_light);
+
+	ERR_FAIL_COND_V(!light_instance->light_ptr, RID());
+
+	light_instance->self = light_instance_owner.make_rid(light_instance);
+
+	return light_instance->self;
 }
 
 void RasterizerSceneGLES2::light_instance_set_transform(RID p_light_instance, const Transform &p_transform) {
+
+	LightInstance *light_instance = light_instance_owner.getornull(p_light_instance);
+	ERR_FAIL_COND(!light_instance);
+
+	light_instance->transform = p_transform;
 }
 
 void RasterizerSceneGLES2::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale) {
+
+	LightInstance *light_instance = light_instance_owner.getornull(p_light_instance);
+	ERR_FAIL_COND(!light_instance);
+
+	if (light_instance->light_ptr->type != VS::LIGHT_DIRECTIONAL) {
+		p_pass = 0;
+	}
+
+	ERR_FAIL_INDEX(p_pass, 4);
+
+	light_instance->shadow_transform[p_pass].camera = p_projection;
+	light_instance->shadow_transform[p_pass].transform = p_transform;
+	light_instance->shadow_transform[p_pass].farplane = p_far;
+	light_instance->shadow_transform[p_pass].split = p_split;
+	light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale;
 }
 
 void RasterizerSceneGLES2::light_instance_mark_visible(RID p_light_instance) {
+
+	LightInstance *light_instance = light_instance_owner.getornull(p_light_instance);
+	ERR_FAIL_COND(!light_instance);
+
+	light_instance->last_scene_pass = scene_pass;
 }
 
 //////////////////////
@@ -206,23 +661,1641 @@ void RasterizerSceneGLES2::gi_probe_instance_set_bounds(RID p_probe, const Vecto
 ////////////////////////////
 ////////////////////////////
 
-void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+void RasterizerSceneGLES2::_add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass) {
+
+	RasterizerStorageGLES2::Material *material = NULL;
+	RID material_src;
+
+	if (p_instance->material_override.is_valid()) {
+		material_src = p_instance->material_override;
+	} else if (p_material >= 0) {
+		material_src = p_instance->materials[p_material];
+	} else {
+		material_src = p_geometry->material;
+	}
+
+	if (material_src.is_valid()) {
+		material = storage->material_owner.getornull(material_src);
+
+		if (!material->shader || !material->shader->valid) {
+			material = NULL;
+		}
+	}
+
+	if (!material) {
+		material = storage->material_owner.getptr(default_material);
+	}
+
+	ERR_FAIL_COND(!material);
+
+	_add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass);
+
+	while (material->next_pass.is_valid()) {
+		material = storage->material_owner.getornull(material->next_pass);
+
+		if (!material || !material->shader || !material->shader->valid) {
+			break;
+		}
+
+		_add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass);
+	}
 }
+void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass, bool p_shadow_pass) {
 
-void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) {
+	bool has_base_alpha = (p_material->shader->spatial.uses_alpha && !p_material->shader->spatial.uses_alpha_scissor) || p_material->shader->spatial.uses_screen_texture || p_material->shader->spatial.uses_depth_texture;
+	bool has_blend_alpha = p_material->shader->spatial.blend_mode != RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX;
+	bool has_alpha = has_base_alpha || has_blend_alpha;
+
+	// TODO add this stuff
+	// bool mirror = p_instance->mirror;
+	// bool no_cull = false;
+
+	RenderList::Element *e = has_alpha ? render_list.add_alpha_element() : render_list.add_element();
+
+	if (!e) {
+		return;
+	}
+
+	e->geometry = p_geometry;
+	e->material = p_material;
+	e->instance = p_instance;
+	e->owner = p_owner;
+	e->sort_key = 0;
+
+	// TODO check render pass of geometry
+
+	// TODO check directional light flag
+
+	if (p_depth_pass) {
+		// if we are in the depth pass we can sort out a few things to improve performance
+
+		if (has_blend_alpha || p_material->shader->spatial.uses_depth_texture || (has_base_alpha && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) {
+			return;
+		}
+
+		if (p_material->shader->spatial.uses_alpha_scissor && !p_material->shader->spatial.writes_modelview_or_projection && !p_material->shader->spatial.uses_vertex && !p_material->shader->spatial.uses_discard && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {
+
+			// shader doesn't use discard or writes a custom vertex position,
+			// so we can use a stripped down shader instead
+
+			// TODO twosided and worldcoord stuff
+
+			p_material = storage->material_owner.getptr(default_material_twosided);
+		}
+
+		has_alpha = false;
+	}
+
+	e->sort_key |= uint64_t(e->geometry->index) << RenderList::SORT_KEY_GEOMETRY_INDEX_SHIFT;
+	e->sort_key |= uint64_t(e->instance->base_type) << RenderList::SORT_KEY_GEOMETRY_TYPE_SHIFT;
+
+	if (p_material->shader->spatial.unshaded) {
+		e->sort_key |= SORT_KEY_UNSHADED_FLAG;
+	}
+
+	if (!p_depth_pass) {
+		e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT;
+
+		e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT;
+	} else {
+		// TODO
+	}
+
+	if (p_material->shader->spatial.uses_time) {
+		VisualServerRaster::redraw_request();
+	}
 }
 
-void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) {
+void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass) {
+
+	for (int i = 0; i < p_cull_count; i++) {
+
+		InstanceBase *instance = p_cull_result[i];
+
+		switch (instance->base_type) {
+
+			case VS::INSTANCE_MESH: {
+
+				RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getornull(instance->base);
+				ERR_CONTINUE(!mesh);
+
+				int num_surfaces = mesh->surfaces.size();
+
+				for (int i = 0; i < num_surfaces; i++) {
+					int material_index = instance->materials[i].is_valid() ? i : -1;
+
+					RasterizerStorageGLES2::Surface *surface = mesh->surfaces[i];
+
+					_add_geometry(surface, instance, NULL, material_index, p_depth_pass, p_shadow_pass);
+				}
+
+			} break;
+
+			case VS::INSTANCE_MULTIMESH: {
+				RasterizerStorageGLES2::MultiMesh *multi_mesh = storage->multimesh_owner.getptr(instance->base);
+				ERR_CONTINUE(!multi_mesh);
+
+				if (multi_mesh->size == 0 || multi_mesh->visible_instances == 0)
+					continue;
+
+				RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getptr(multi_mesh->mesh);
+				if (!mesh)
+					continue;
+
+				int ssize = mesh->surfaces.size();
+
+				for (int i = 0; i < ssize; i++) {
+					RasterizerStorageGLES2::Surface *s = mesh->surfaces[i];
+					_add_geometry(s, instance, multi_mesh, -1, p_depth_pass, p_shadow_pass);
+				}
+			} break;
+
+			default: {
+
+			} break;
+		}
+	}
 }
 
-bool RasterizerSceneGLES2::free(RID p_rid) {
-	return true;
+static const GLenum gl_primitive[] = {
+	GL_POINTS,
+	GL_LINES,
+	GL_LINE_STRIP,
+	GL_LINE_LOOP,
+	GL_TRIANGLES,
+	GL_TRIANGLE_STRIP,
+	GL_TRIANGLE_FAN
+};
+
+void RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_material, bool p_use_radiance_map, bool p_reverse_cull, bool p_shadow_atlas, bool p_skeleton_tex, Size2i p_skeleton_tex_size) {
+
+	// material parameters
+
+	state.scene_shader.set_custom_shader(p_material->shader->custom_code_id);
+
+	state.scene_shader.bind();
+
+	if (p_material->shader->spatial.no_depth_test) {
+		glDisable(GL_DEPTH_TEST);
+	} else {
+		glEnable(GL_DEPTH_TEST);
+	}
+
+	// TODO whyyyyy????
+	p_reverse_cull = true;
+
+	switch (p_material->shader->spatial.cull_mode) {
+		case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED: {
+			glDisable(GL_CULL_FACE);
+		} break;
+
+		case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_BACK: {
+			glEnable(GL_CULL_FACE);
+			glCullFace(p_reverse_cull ? GL_FRONT : GL_BACK);
+		} break;
+		case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_FRONT: {
+			glEnable(GL_CULL_FACE);
+			glCullFace(p_reverse_cull ? GL_BACK : GL_FRONT);
+		} break;
+	}
+
+	int tc = p_material->textures.size();
+	Pair<StringName, RID> *textures = p_material->textures.ptrw();
+
+	ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = p_material->shader->texture_hints.ptrw();
+
+	int num_default_tex = p_use_radiance_map ? 1 : 0;
+
+	if (p_material->shader->spatial.uses_screen_texture) {
+		num_default_tex = MIN(num_default_tex, 2);
+	}
+
+	if (p_shadow_atlas) {
+		num_default_tex = MIN(num_default_tex, 3);
+	}
+
+	if (p_skeleton_tex) {
+		num_default_tex = MIN(num_default_tex, 4);
+
+		state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TEXTURE_SIZE, p_skeleton_tex_size);
+	}
+
+	for (int i = 0; i < tc; i++) {
+
+		glActiveTexture(GL_TEXTURE0 + num_default_tex + i);
+
+		RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i].second);
+
+		if (!t) {
+
+			switch (texture_hints[i]) {
+				case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO:
+				case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: {
+					glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex);
+				} break;
+				case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: {
+					glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex);
+				} break;
+				case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: {
+					glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex);
+				} break;
+				default: {
+					glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+				} break;
+			}
+
+			continue;
+		}
+
+		t = t->get_ptr();
+
+		glBindTexture(t->target, t->tex_id);
+	}
+	state.scene_shader.use_material((void *)p_material, num_default_tex);
 }
 
-void RasterizerSceneGLES2::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw) {
+void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton) {
+
+	state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, p_skeleton != NULL);
+	// state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, !storage->config.float_texture_supported);
+	state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, true);
+
+	switch (p_element->instance->base_type) {
+
+		case VS::INSTANCE_MESH: {
+			RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
+
+			state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false);
+			state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, s->attribs[VS::ARRAY_COLOR].enabled);
+			state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled);
+			state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled);
+
+		} break;
+
+		case VS::INSTANCE_MULTIMESH: {
+			RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner);
+			RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
+
+			state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, true);
+			state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, true);
+
+			state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled);
+			state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled);
+		} break;
+
+		default: {
+
+		} break;
+	}
+
+	if (false && storage->config.float_texture_supported) {
+		if (p_skeleton) {
+			glActiveTexture(GL_TEXTURE4);
+			glBindTexture(GL_TEXTURE_2D, p_skeleton->tex_id);
+		}
+
+		return;
+	}
+
+	if (p_skeleton) {
+		ERR_FAIL_COND(p_skeleton->use_2d);
+
+		PoolVector<float> &transform_buffer = storage->resources.skeleton_transform_cpu_buffer;
+
+		switch (p_element->instance->base_type) {
+			case VS::INSTANCE_MESH: {
+				RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
+
+				if (!s->attribs[VS::ARRAY_BONES].enabled || !s->attribs[VS::ARRAY_WEIGHTS].enabled) {
+					break; // the whole instance has a skeleton, but this surface is not affected by it.
+				}
+
+				// 3 * vec4 per vertex
+				if (transform_buffer.size() < s->array_len * 12) {
+					transform_buffer.resize(s->array_len * 12);
+				}
+
+				const size_t bones_offset = s->attribs[VS::ARRAY_BONES].offset;
+				const size_t bones_stride = s->attribs[VS::ARRAY_BONES].stride;
+				const size_t bone_weight_offset = s->attribs[VS::ARRAY_WEIGHTS].offset;
+				const size_t bone_weight_stride = s->attribs[VS::ARRAY_WEIGHTS].stride;
+
+				{
+					PoolVector<float>::Write write = transform_buffer.write();
+					float *buffer = write.ptr();
+
+					PoolVector<uint8_t>::Read vertex_array_read = s->data.read();
+					const uint8_t *vertex_data = vertex_array_read.ptr();
+
+					for (int i = 0; i < s->array_len; i++) {
+
+						// do magic
+
+						size_t bones[4];
+						float bone_weight[4];
+
+						if (s->attribs[VS::ARRAY_BONES].type == GL_UNSIGNED_BYTE) {
+							// read as byte
+							const uint8_t *bones_ptr = vertex_data + bones_offset + (i * bones_stride);
+							bones[0] = bones_ptr[0];
+							bones[1] = bones_ptr[1];
+							bones[2] = bones_ptr[2];
+							bones[3] = bones_ptr[3];
+						} else {
+							// read as short
+							const uint16_t *bones_ptr = (const uint16_t *)(vertex_data + bones_offset + (i * bones_stride));
+							bones[0] = bones_ptr[0];
+							bones[1] = bones_ptr[1];
+							bones[2] = bones_ptr[2];
+							bones[3] = bones_ptr[3];
+						}
+
+						if (s->attribs[VS::ARRAY_WEIGHTS].type == GL_FLOAT) {
+							// read as float
+							const float *weight_ptr = (const float *)(vertex_data + bone_weight_offset + (i * bone_weight_stride));
+							bone_weight[0] = weight_ptr[0];
+							bone_weight[1] = weight_ptr[1];
+							bone_weight[2] = weight_ptr[2];
+							bone_weight[3] = weight_ptr[3];
+						} else {
+							// read as half
+							const uint16_t *weight_ptr = (const uint16_t *)(vertex_data + bone_weight_offset + (i * bone_weight_stride));
+							bone_weight[0] = (weight_ptr[0] / (float)0xFFFF);
+							bone_weight[1] = (weight_ptr[1] / (float)0xFFFF);
+							bone_weight[2] = (weight_ptr[2] / (float)0xFFFF);
+							bone_weight[3] = (weight_ptr[3] / (float)0xFFFF);
+						}
+
+						size_t offset = i * 12;
+
+						Transform transform;
+
+						Transform bone_transforms[4] = {
+							storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[0]),
+							storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[1]),
+							storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[2]),
+							storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[3]),
+						};
+
+						transform.origin =
+								bone_weight[0] * bone_transforms[0].origin +
+								bone_weight[1] * bone_transforms[1].origin +
+								bone_weight[2] * bone_transforms[2].origin +
+								bone_weight[3] * bone_transforms[3].origin;
+
+						transform.basis =
+								bone_transforms[0].basis * bone_weight[0] +
+								bone_transforms[1].basis * bone_weight[1] +
+								bone_transforms[2].basis * bone_weight[2] +
+								bone_transforms[3].basis * bone_weight[3];
+
+						float row[3][4] = {
+							{ transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] },
+							{ transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] },
+							{ transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] },
+						};
+
+						size_t transform_buffer_offset = i * 12;
+
+						copymem(&buffer[transform_buffer_offset], row, sizeof(row));
+					}
+				}
+
+				storage->_update_skeleton_transform_buffer(transform_buffer, s->array_len * 12);
+			} break;
+
+			default: {
+
+			} break;
+		}
+	}
 }
 
-void RasterizerSceneGLES2::initialize() {
+void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) {
+
+	switch (p_element->instance->base_type) {
+
+		case VS::INSTANCE_MESH: {
+
+			RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
+
+			// set up
+
+			if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
+				glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
+
+				glEnableVertexAttribArray(VS::ARRAY_MAX + 0);
+				glEnableVertexAttribArray(VS::ARRAY_MAX + 1);
+				glEnableVertexAttribArray(VS::ARRAY_MAX + 2);
+
+				glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0));
+				glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1));
+				glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2));
+			} else {
+				// just to make sure
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
+
+				glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0);
+				glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0);
+				glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0);
+			}
+
+			glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id);
+
+			if (s->index_array_len > 0) {
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id);
+			}
+
+			for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
+				if (s->attribs[i].enabled) {
+					glEnableVertexAttribArray(i);
+					glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, (uint8_t *)0 + s->attribs[i].offset);
+				} else {
+					glDisableVertexAttribArray(i);
+				}
+			}
+
+			// drawing
+
+			if (s->index_array_len > 0) {
+				glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0);
+			} else {
+				glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
+			}
+
+			// tear down
+
+			for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
+				glDisableVertexAttribArray(i);
+			}
+
+			if (s->index_array_len > 0) {
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+			}
+
+			if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
+				glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
+
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
+			}
+
+			glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+		} break;
+
+		case VS::INSTANCE_MULTIMESH: {
+
+			RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner);
+			RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
+
+			int amount = MIN(multi_mesh->size, multi_mesh->visible_instances);
+			if (amount == -1) {
+				amount = multi_mesh->size;
+			}
+
+			if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
+				glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
+
+				glEnableVertexAttribArray(VS::ARRAY_MAX + 0);
+				glEnableVertexAttribArray(VS::ARRAY_MAX + 1);
+				glEnableVertexAttribArray(VS::ARRAY_MAX + 2);
+
+				glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0));
+				glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1));
+				glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2));
+			} else {
+				// just to make sure
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
+
+				glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0);
+				glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0);
+				glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0);
+			}
+
+			glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id);
+
+			if (s->index_array_len > 0) {
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id);
+			}
+
+			for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
+				if (s->attribs[i].enabled) {
+					glEnableVertexAttribArray(i);
+					glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, (uint8_t *)0 + s->attribs[i].offset);
+				} else {
+					glDisableVertexAttribArray(i);
+				}
+			}
+
+			glDisableVertexAttribArray(12); // transform 0
+			glDisableVertexAttribArray(13); // transform 1
+			glDisableVertexAttribArray(14); // transform 2
+			glDisableVertexAttribArray(15); // color
+			glDisableVertexAttribArray(8); // custom data
+
+			glVertexAttrib4f(15, 1, 1, 1, 1);
+			glVertexAttrib4f(8, 0, 0, 0, 0);
+
+			int stride = multi_mesh->color_floats + multi_mesh->custom_data_floats + multi_mesh->xform_floats;
+
+			int color_ofs = multi_mesh->xform_floats;
+			int custom_data_ofs = color_ofs + multi_mesh->color_floats;
+
+			// drawing
+
+			for (int i = 0; i < amount; i++) {
+				float *buffer = &multi_mesh->data.write[i * stride];
+
+				{
+					// inline of multimesh_get_transform since it's such a pain
+					// to get a RID from here...
+					Transform transform;
+
+					transform.basis.elements[0][0] = buffer[0];
+					transform.basis.elements[0][1] = buffer[1];
+					transform.basis.elements[0][2] = buffer[2];
+					transform.origin.x = buffer[3];
+					transform.basis.elements[1][0] = buffer[4];
+					transform.basis.elements[1][1] = buffer[5];
+					transform.basis.elements[1][2] = buffer[6];
+					transform.origin.y = buffer[7];
+					transform.basis.elements[2][0] = buffer[8];
+					transform.basis.elements[2][1] = buffer[9];
+					transform.basis.elements[2][2] = buffer[10];
+					transform.origin.z = buffer[11];
+
+					float row[3][4] = {
+						{ transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] },
+						{ transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] },
+						{ transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] },
+					};
+
+					glVertexAttrib4fv(12, row[0]);
+					glVertexAttrib4fv(13, row[1]);
+					glVertexAttrib4fv(14, row[2]);
+				}
+
+				if (multi_mesh->color_floats) {
+					glVertexAttrib4fv(15, buffer + color_ofs);
+				}
+
+				if (multi_mesh->custom_data_floats) {
+					glVertexAttrib4fv(8, buffer + custom_data_ofs);
+				}
+
+				if (s->index_array_len > 0) {
+					glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0);
+				} else {
+					glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
+				}
+			}
+
+			// tear down
+
+			for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
+				glDisableVertexAttribArray(i);
+			}
+
+			if (s->index_array_len > 0) {
+				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+			}
+
+			if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
+				glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
+
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
+				glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
+			}
+
+			glBindBuffer(GL_ARRAY_BUFFER, 0);
+		} break;
+	}
+}
+
+void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, int p_element_count, const RID *p_light_cull_result, int p_light_cull_count, const Transform &p_view_transform, const CameraMatrix &p_projection, RID p_shadow_atlas, Environment *p_env, GLuint p_base_env, float p_shadow_bias, float p_shadow_normal_bias, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add, bool p_directional_shadows) {
+
+	ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
+
+	Vector2 screen_pixel_size;
+	screen_pixel_size.x = 1.0 / storage->frame.current_rt->width;
+	screen_pixel_size.y = 1.0 / storage->frame.current_rt->height;
+
+	bool use_radiance_map = false;
+
+	for (int i = 0; i < p_element_count; i++) {
+		RenderList::Element *e = p_elements[i];
+
+		RasterizerStorageGLES2::Material *material = e->material;
+
+		RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton);
+
+		if (p_base_env) {
+			glActiveTexture(GL_TEXTURE0);
+			glBindTexture(GL_TEXTURE_CUBE_MAP, p_base_env);
+			use_radiance_map = true;
+		}
+		state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map);
+
+		if (material->shader->spatial.unshaded) {
+			state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false);
+		} else {
+			state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map);
+		}
+
+		// opaque pass
+
+		state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false);
+
+		_setup_geometry(e, skeleton);
+
+		_setup_material(material, use_radiance_map, p_reverse_cull, false, skeleton ? (skeleton->tex_id != 0) : 0, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
+
+		if (use_radiance_map) {
+			state.scene_shader.set_uniform(SceneShaderGLES2::RADIANCE_INVERSE_XFORM, p_view_transform);
+		}
+
+		if (p_shadow) {
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_BIAS, p_shadow_bias);
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_NORMAL_BIAS, p_shadow_normal_bias);
+		}
+
+		if (p_env) {
+			state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, p_env->bg_energy);
+			state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, p_env->ambient_sky_contribution);
+			state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, p_env->ambient_color);
+			state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, p_env->ambient_energy);
+
+		} else {
+			state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, 1.0);
+			state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, 1.0);
+			state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, Color(1.0, 1.0, 1.0, 1.0));
+			state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, 1.0);
+		}
+
+		glEnable(GL_BLEND);
+
+		if (p_alpha_pass || p_directional_add) {
+			int desired_blend_mode;
+			if (p_directional_add) {
+				desired_blend_mode = RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD;
+			} else {
+				desired_blend_mode = material->shader->spatial.blend_mode;
+			}
+
+			switch (desired_blend_mode) {
+
+				case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX: {
+					glBlendEquation(GL_FUNC_ADD);
+					if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
+						glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+					} else {
+						glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+					}
+
+				} break;
+				case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD: {
+
+					glBlendEquation(GL_FUNC_ADD);
+					glBlendFunc(p_alpha_pass ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
+
+				} break;
+				case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_SUB: {
+
+					glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+					glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+				} break;
+				case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MUL: {
+					glBlendEquation(GL_FUNC_ADD);
+					if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
+						glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO);
+					} else {
+						glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
+					}
+
+				} break;
+			}
+		} else {
+			// no blend mode given - assume mix
+			glBlendEquation(GL_FUNC_ADD);
+			if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
+				glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+			} else {
+				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+			}
+		}
+
+		state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse());
+		state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform);
+		state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection);
+		state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse());
+
+		state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
+
+		state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
+		state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror?
+		state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
+
+		_render_geometry(e);
+
+		// render lights
+
+		if (material->shader->spatial.unshaded)
+			continue;
+
+		if (p_shadow)
+			continue;
+
+		state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, true);
+
+		state.scene_shader.bind();
+
+		glBlendEquation(GL_FUNC_ADD);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+
+		{
+			bool has_shadow_atlas = shadow_atlas != NULL;
+			_setup_material(material, false, p_reverse_cull, has_shadow_atlas, skeleton ? (skeleton->tex_id != 0) : 0, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
+
+			if (has_shadow_atlas) {
+				glActiveTexture(GL_TEXTURE3);
+				glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
+			}
+
+			state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse());
+			state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform);
+			state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection);
+			state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse());
+
+			state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
+
+			state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
+			state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror?
+			state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
+		}
+
+		for (int j = 0; j < e->instance->light_instances.size(); j++) {
+			RID light_rid = e->instance->light_instances[j];
+			LightInstance *light = light_instance_owner.get(light_rid);
+
+			switch (light->light_ptr->type) {
+				case VS::LIGHT_DIRECTIONAL: {
+					continue;
+				} break;
+
+				case VS::LIGHT_OMNI: {
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)1);
+
+					Vector3 position = p_view_transform.inverse().xform(light->transform.origin);
+
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position);
+
+					float range = light->light_ptr->param[VS::LIGHT_PARAM_RANGE];
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range);
+
+					Color attenuation = Color(0.0, 0.0, 0.0, 0.0);
+					attenuation.a = light->light_ptr->param[VS::LIGHT_PARAM_ATTENUATION];
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation);
+
+					if (light->light_ptr->shadow && shadow_atlas->shadow_owners.has(light->self)) {
+
+						uint32_t key = shadow_atlas->shadow_owners[light->self];
+
+						uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03;
+						uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK;
+
+						ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size());
+
+						uint32_t atlas_size = shadow_atlas->size;
+						uint32_t quadrant_size = atlas_size >> 1;
+
+						uint32_t x = (quadrant & 1) * quadrant_size;
+						uint32_t y = (quadrant >> 1) * quadrant_size;
+
+						uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
+						x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
+						y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
+
+						uint32_t width = shadow_size;
+						uint32_t height = shadow_size;
+
+						if (light->light_ptr->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) {
+							height /= 2;
+						} else {
+							width /= 2;
+						}
+
+						Transform proj = (p_view_transform.inverse() * light->transform).inverse();
+
+						Color light_clamp;
+						light_clamp[0] = float(x) / atlas_size;
+						light_clamp[1] = float(y) / atlas_size;
+						light_clamp[2] = float(width) / atlas_size;
+						light_clamp[3] = float(height) / atlas_size;
+
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, proj);
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp);
+
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0);
+					} else {
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0);
+					}
+				} break;
+
+				case VS::LIGHT_SPOT: {
+					Vector3 position = p_view_transform.inverse().xform(light->transform.origin);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)2);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position);
+
+					Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized();
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction);
+					Color attenuation = Color(0.0, 0.0, 0.0, 0.0);
+					attenuation.a = light->light_ptr->param[VS::LIGHT_PARAM_ATTENUATION];
+					float range = light->light_ptr->param[VS::LIGHT_PARAM_RANGE];
+					float spot_attenuation = light->light_ptr->param[VS::LIGHT_PARAM_SPOT_ATTENUATION];
+					float angle = light->light_ptr->param[VS::LIGHT_PARAM_SPOT_ANGLE];
+					angle = Math::cos(Math::deg2rad(angle));
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ATTENUATION, spot_attenuation);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_RANGE, spot_attenuation);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ANGLE, angle);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range);
+
+					if (light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) {
+						uint32_t key = shadow_atlas->shadow_owners[light->self];
+
+						uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03;
+						uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK;
+
+						ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size());
+
+						uint32_t atlas_size = shadow_atlas->size;
+						uint32_t quadrant_size = atlas_size >> 1;
+
+						uint32_t x = (quadrant & 1) * quadrant_size;
+						uint32_t y = (quadrant >> 1) * quadrant_size;
+
+						uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
+						x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
+						y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
+
+						uint32_t width = shadow_size;
+						uint32_t height = shadow_size;
+
+						Rect2 rect(float(x) / atlas_size, float(y) / atlas_size, float(width) / atlas_size, float(height) / atlas_size);
+
+						Color light_clamp;
+						light_clamp[0] = rect.position.x;
+						light_clamp[1] = rect.position.y;
+						light_clamp[2] = rect.size.x;
+						light_clamp[3] = rect.size.y;
+
+						Transform modelview = (p_view_transform.inverse() * light->transform).inverse();
+
+						CameraMatrix bias;
+						bias.set_light_bias();
+
+						CameraMatrix rectm;
+						rectm.set_light_atlas_rect(rect);
+
+						CameraMatrix shadow_matrix = rectm * bias * light->shadow_transform[0].camera * modelview;
+
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0);
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, shadow_matrix);
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp);
+
+					} else {
+						state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0);
+					}
+
+				} break;
+
+				default: {
+					print_line("wat.");
+				} break;
+			}
+
+			float energy = light->light_ptr->param[VS::LIGHT_PARAM_ENERGY];
+			float specular = light->light_ptr->param[VS::LIGHT_PARAM_SPECULAR];
+
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy);
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, light->light_ptr->color.to_linear());
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular);
+
+			_render_geometry(e);
+		}
+
+		for (int j = 0; j < p_light_cull_count; j++) {
+			RID light_rid = p_light_cull_result[j];
+
+			LightInstance *light = light_instance_owner.getornull(light_rid);
+
+			RasterizerStorageGLES2::Light *light_ptr = light->light_ptr;
+
+			switch (light_ptr->type) {
+				case VS::LIGHT_DIRECTIONAL: {
+
+					switch (light_ptr->directional_shadow_mode) {
+						case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: {
+						} break;
+						case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: {
+							state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, true);
+							state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits);
+						} break;
+
+						case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: {
+							state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, true);
+							state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits);
+						} break;
+						default:
+							break;
+					}
+
+					{
+						_setup_material(material, false, p_reverse_cull, false, skeleton ? (skeleton->tex_id != 0) : 0, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
+
+						if (directional_shadow.depth) {
+							glActiveTexture(GL_TEXTURE3);
+							glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
+						}
+
+						state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse());
+						state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform);
+						state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection);
+						state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse());
+
+						state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
+
+						state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
+						state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror?
+						state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
+					}
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)0);
+					Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized();
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction);
+
+				} break;
+
+				default: {
+					continue;
+				} break;
+			}
+
+			float energy = light_ptr->param[VS::LIGHT_PARAM_ENERGY];
+			float specular = light_ptr->param[VS::LIGHT_PARAM_SPECULAR];
+
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy);
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular);
+
+			float sign = light_ptr->negative ? -1 : 1;
+
+			Color linear_col = light_ptr->color.to_linear();
+			Color color;
+			for (int c = 0; c < 3; c++)
+				color[c] = linear_col[c] * sign * energy * Math_PI;
+
+			color[3] = 0;
+
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, color);
+
+			CameraMatrix matrices[4];
+
+			if (light_ptr->shadow && directional_shadow.depth) {
+
+				int shadow_count = 0;
+				Color split_offsets;
+
+				switch (light_ptr->directional_shadow_mode) {
+					case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: {
+						shadow_count = 1;
+					} break;
+
+					case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: {
+						shadow_count = 2;
+					} break;
+
+					case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: {
+						shadow_count = 4;
+					} break;
+				}
+
+				for (int k = 0; k < shadow_count; k++) {
+
+					uint32_t x = light->directional_rect.position.x;
+					uint32_t y = light->directional_rect.position.y;
+					uint32_t width = light->directional_rect.size.x;
+					uint32_t height = light->directional_rect.size.y;
+
+					if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
+
+						width /= 2;
+						height /= 2;
+
+						if (k == 0) {
+
+						} else if (k == 1) {
+							x += width;
+						} else if (k == 2) {
+							y += height;
+						} else if (k == 3) {
+							x += width;
+							y += height;
+						}
+
+					} else if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
+
+						height /= 2;
+
+						if (k == 0) {
+
+						} else {
+							y += height;
+						}
+					}
+
+					split_offsets[k] = light->shadow_transform[k].split;
+
+					Transform modelview = (p_view_transform * light->shadow_transform[k].transform).inverse();
+
+					CameraMatrix bias;
+					bias.set_light_bias();
+					CameraMatrix rectm;
+					Rect2 atlas_rect = Rect2(float(x) / directional_shadow.size, float(y) / directional_shadow.size, float(width) / directional_shadow.size, float(height) / directional_shadow.size);
+					rectm.set_light_atlas_rect(atlas_rect);
+
+					CameraMatrix shadow_mtx = rectm * bias * light->shadow_transform[k].camera * modelview;
+					matrices[k] = shadow_mtx.inverse();
+
+					Color light_clamp;
+					light_clamp[0] = atlas_rect.position.x;
+					light_clamp[1] = atlas_rect.position.y;
+					light_clamp[2] = atlas_rect.size.x;
+					light_clamp[3] = atlas_rect.size.y;
+
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp);
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPLIT_OFFSETS, split_offsets);
+				}
+
+				state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX1, matrices[0]);
+				state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX2, matrices[1]);
+				state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX3, matrices[2]);
+				state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX4, matrices[3]);
+			} else {
+				state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0);
+			}
+
+			_render_geometry(e);
+		}
+
+		state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false);
+	}
+
+	state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false);
+	state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false);
+	state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false);
+	state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false);
+}
+
+void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy) {
+	ERR_FAIL_COND(!p_sky);
+
+	RasterizerStorageGLES2::Texture *tex = storage->texture_owner.getornull(p_sky->panorama);
+	ERR_FAIL_COND(!tex);
+
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(tex->target, tex->tex_id);
+
+	glDepthMask(GL_TRUE);
+	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_CULL_FACE);
+	glDisable(GL_BLEND);
+	glDepthFunc(GL_LEQUAL);
+	glColorMask(1, 1, 1, 1);
+
+	// Camera
+	CameraMatrix camera;
+
+	if (p_custom_fov) {
+
+		float near_plane = p_projection.get_z_near();
+		float far_plane = p_projection.get_z_far();
+		float aspect = p_projection.get_aspect();
+
+		camera.set_perspective(p_custom_fov, aspect, near_plane, far_plane);
+	} else {
+		camera = p_projection;
+	}
+
+	float flip_sign = p_vflip ? -1 : 1;
+
+	// If matrix[2][0] or matrix[2][1] we're dealing with an asymmetrical projection matrix. This is the case for stereoscopic rendering (i.e. VR).
+	// To ensure the image rendered is perspective correct we need to move some logic into the shader. For this the USE_ASYM_PANO option is introduced.
+	// It also means the uv coordinates are ignored in this mode and we don't need our loop.
+	bool asymmetrical = ((camera.matrix[2][0] != 0.0) || (camera.matrix[2][1] != 0.0));
+
+	Vector3 vertices[8] = {
+		Vector3(-1, -1 * flip_sign, 1),
+		Vector3(0, 1, 0),
+		Vector3(1, -1 * flip_sign, 1),
+		Vector3(1, 1, 0),
+		Vector3(1, 1 * flip_sign, 1),
+		Vector3(1, 0, 0),
+		Vector3(-1, 1 * flip_sign, 1),
+		Vector3(0, 0, 0),
+	};
+
+	if (!asymmetrical) {
+		float vw, vh, zn;
+		camera.get_viewport_size(vw, vh);
+		zn = p_projection.get_z_near();
+
+		for (int i = 0; i < 4; i++) {
+			Vector3 uv = vertices[i * 2 + 1];
+			uv.x = (uv.x * 2.0 - 1.0) * vw;
+			uv.y = -(uv.y * 2.0 - 1.0) * vh;
+			uv.z = -zn;
+			vertices[i * 2 + 1] = p_transform.basis.xform(uv).normalized();
+			vertices[i * 2 + 1].z = -vertices[i * 2 + 1].z;
+		}
+	}
+
+	glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts);
+	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vector3) * 8, vertices);
+
+	// bind sky vertex array....
+	glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, 0);
+	glVertexAttribPointer(VS::ARRAY_TEX_UV, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, ((uint8_t *)NULL) + sizeof(Vector3));
+	glEnableVertexAttribArray(VS::ARRAY_VERTEX);
+	glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
+
+	storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, true);
+	storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
+	storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, true);
+	storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false);
+	storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false);
+	storage->shaders.copy.bind();
+	storage->shaders.copy.set_uniform(CopyShaderGLES2::MULTIPLIER, p_energy);
+
+	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+	glDisableVertexAttribArray(VS::ARRAY_VERTEX);
+	glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false);
+	storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
+}
+
+void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+
+	glEnable(GL_BLEND);
+
+	GLuint current_fb = storage->frame.current_rt->fbo;
+	Environment *env = environment_owner.getornull(p_environment);
+
+	// render list stuff
+
+	render_list.clear();
+	_fill_render_list(p_cull_result, p_cull_count, false, false);
+
+	// other stuff
+
+	glBindFramebuffer(GL_FRAMEBUFFER, current_fb);
+
+	glDepthFunc(GL_LEQUAL);
+	glDepthMask(GL_TRUE);
+	glClearDepth(1.0f);
+	glEnable(GL_DEPTH_TEST);
+
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	storage->frame.clear_request = false;
+
+	glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
+
+	glBlendEquation(GL_FUNC_ADD);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	// render sky
+	RasterizerStorageGLES2::Sky *sky = NULL;
+	GLuint env_radiance_tex = 0;
+	if (env) {
+		switch (env->bg_mode) {
+
+			case VS::ENV_BG_COLOR_SKY:
+			case VS::ENV_BG_SKY: {
+				sky = storage->sky_owner.getornull(env->sky);
+
+				if (sky) {
+					env_radiance_tex = sky->radiance;
+				}
+			} break;
+
+			default: {
+				print_line("uhm");
+			} break;
+		}
+	}
+
+	if (env && env->bg_mode == VS::ENV_BG_SKY && (!storage->frame.current_rt || !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT])) {
+
+		if (sky && sky->panorama.is_valid()) {
+			_draw_sky(sky, p_cam_projection, p_cam_transform, false, env->sky_custom_fov, env->bg_energy);
+		}
+	}
+
+	// render opaque things first
+	render_list.sort_by_key(false);
+	_render_render_list(render_list.elements, render_list.element_count, p_light_cull_result, p_light_cull_count, p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, false, false, false, false);
+
+	// alpha pass
+
+	glBlendEquation(GL_FUNC_ADD);
+	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+	render_list.sort_by_key(true);
+	_render_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_light_cull_result, p_light_cull_count, p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, true, false, false, false);
+
+	glDepthMask(GL_FALSE);
+	glDisable(GL_DEPTH_TEST);
+
+	// #define GLES2_SHADOW_ATLAS_DEBUG_VIEW
+
+#ifdef GLES2_SHADOW_ATLAS_DEBUG_VIEW
+	ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
+	if (shadow_atlas) {
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
+
+		glViewport(0, 0, storage->frame.current_rt->width / 4, storage->frame.current_rt->height / 4);
+		storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
+		storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false);
+		storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false);
+		storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false);
+		storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, false);
+		storage->shaders.copy.bind();
+
+		storage->_copy_screen();
+	}
+#endif
+}
+
+void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) {
+
+	LightInstance *light_instance = light_instance_owner.getornull(p_light);
+	ERR_FAIL_COND(!light_instance);
+
+	RasterizerStorageGLES2::Light *light = light_instance->light_ptr;
+	ERR_FAIL_COND(!light);
+
+	uint32_t x;
+	uint32_t y;
+	uint32_t width;
+	uint32_t height;
+	uint32_t vp_height;
+
+	float zfar = 0;
+	bool flip_facing = false;
+	int custom_vp_size = 0;
+
+	GLuint fbo = 0;
+
+	int current_cubemap = -1;
+	float bias = 0;
+	float normal_bias = 0;
+
+	CameraMatrix light_projection;
+	Transform light_transform;
+
+	// TODO directional light
+
+	if (light->type == VS::LIGHT_DIRECTIONAL) {
+		// set pssm stuff
+
+		// TODO set this only when changed
+
+		light_instance->light_directional_index = directional_shadow.current_light;
+		light_instance->last_scene_shadow_pass = scene_pass;
+
+		directional_shadow.current_light++;
+
+		if (directional_shadow.light_count == 1) {
+			light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size);
+		} else if (directional_shadow.light_count == 2) {
+			light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size / 2);
+			if (light_instance->light_directional_index == 1) {
+				light_instance->directional_rect.position.x += light_instance->directional_rect.size.x;
+			}
+		} else { //3 and 4
+			light_instance->directional_rect = Rect2(0, 0, directional_shadow.size / 2, directional_shadow.size / 2);
+			if (light_instance->light_directional_index & 1) {
+				light_instance->directional_rect.position.x += light_instance->directional_rect.size.x;
+			}
+			if (light_instance->light_directional_index / 2) {
+				light_instance->directional_rect.position.y += light_instance->directional_rect.size.y;
+			}
+		}
+
+		light_projection = light_instance->shadow_transform[p_pass].camera;
+		light_transform = light_instance->shadow_transform[p_pass].transform;
+
+		x = light_instance->directional_rect.position.x;
+		y = light_instance->directional_rect.position.y;
+		width = light_instance->directional_rect.size.width;
+		height = light_instance->directional_rect.size.height;
+
+		if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
+
+			width /= 2;
+			height /= 2;
+
+			if (p_pass == 0) {
+
+			} else if (p_pass == 1) {
+				x += width;
+			} else if (p_pass == 2) {
+				y += height;
+			} else if (p_pass == 3) {
+				x += width;
+				y += height;
+			}
+
+		} else if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
+
+			height /= 2;
+
+			if (p_pass == 0) {
+
+			} else {
+				y += height;
+			}
+		}
+
+		float bias_mult = Math::lerp(1.0f, light_instance->shadow_transform[p_pass].bias_scale, light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE]);
+		zfar = light->param[VS::LIGHT_PARAM_RANGE];
+		bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS] * bias_mult;
+		normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] * bias_mult;
+
+		fbo = directional_shadow.fbo;
+		vp_height = directional_shadow.size;
+	} else {
+		ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
+		ERR_FAIL_COND(!shadow_atlas);
+		ERR_FAIL_COND(!shadow_atlas->shadow_owners.has(p_light));
+
+		fbo = shadow_atlas->fbo;
+		vp_height = shadow_atlas->size;
+
+		uint32_t key = shadow_atlas->shadow_owners[p_light];
+
+		uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03;
+		uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK;
+
+		ERR_FAIL_INDEX((int)shadow, shadow_atlas->quadrants[quadrant].shadows.size());
+
+		uint32_t quadrant_size = shadow_atlas->size >> 1;
+
+		x = (quadrant & 1) * quadrant_size;
+		y = (quadrant >> 1) * quadrant_size;
+
+		uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
+		x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
+		y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
+
+		width = shadow_size;
+		height = shadow_size;
+
+		if (light->type == VS::LIGHT_OMNI) {
+			// cubemap only
+			if (light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE) {
+				int cubemap_index = shadow_cubemaps.size() - 1;
+
+				// find an appropriate cubemap to render to
+				for (int i = shadow_cubemaps.size() - 1; i >= 0; i--) {
+					if (shadow_cubemaps[i].size > shadow_size * 2) {
+						break;
+					}
+
+					cubemap_index = i;
+				}
+
+				fbo = shadow_cubemaps[cubemap_index].fbo[p_pass];
+				light_projection = light_instance->shadow_transform[0].camera;
+				light_transform = light_instance->shadow_transform[0].transform;
+
+				custom_vp_size = shadow_cubemaps[cubemap_index].size;
+				zfar = light->param[VS::LIGHT_PARAM_RANGE];
+
+				current_cubemap = cubemap_index;
+			}
+		} else {
+			light_projection = light_instance->shadow_transform[0].camera;
+			light_transform = light_instance->shadow_transform[0].transform;
+
+			flip_facing = false;
+			zfar = light->param[VS::LIGHT_PARAM_RANGE];
+			bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS];
+			normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS];
+		}
+	}
+
+	render_list.clear();
+
+	_fill_render_list(p_cull_result, p_cull_count, true, true);
+
+	render_list.sort_by_depth(false);
+
+	glDisable(GL_BLEND);
+	glDisable(GL_DITHER);
+	glEnable(GL_DEPTH_TEST);
+
+	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+	glDepthMask(GL_TRUE);
+	glColorMask(0, 0, 0, 0);
+
+	if (custom_vp_size) {
+		glViewport(0, 0, custom_vp_size, custom_vp_size);
+		glScissor(0, 0, custom_vp_size, custom_vp_size);
+	} else {
+		glViewport(x, y, width, height);
+		glScissor(x, y, width, height);
+	}
+
+	glEnable(GL_SCISSOR_TEST);
+	glClearDepth(1.0f);
+	glClear(GL_DEPTH_BUFFER_BIT);
+	glDisable(GL_SCISSOR_TEST);
+
+	state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, true);
+
+	_render_render_list(render_list.elements, render_list.element_count, NULL, 0, light_transform, light_projection, RID(), NULL, 0, bias, normal_bias, false, false, true, false, false);
+
+	state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, false);
+
+	// convert cubemap to dual paraboloid if needed
+	if (light->type == VS::LIGHT_OMNI && light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE && p_pass == 5) {
+		ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
+
+		glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo);
+		state.cube_to_dp_shader.bind();
+
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_CUBE_MAP, shadow_cubemaps[current_cubemap].cubemap);
+
+		glDisable(GL_CULL_FACE);
+
+		for (int i = 0; i < 2; i++) {
+			state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FLIP, i == 1);
+			state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_NEAR, light_projection.get_z_near());
+			state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FAR, light_projection.get_z_far());
+			state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::BIAS, light->param[VS::LIGHT_PARAM_SHADOW_BIAS]);
+
+			uint32_t local_width = width;
+			uint32_t local_height = height;
+			uint32_t local_x = x;
+			uint32_t local_y = y;
+
+			if (light->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) {
+				local_height /= 2;
+				local_y += i * local_height;
+			} else {
+				local_width /= 2;
+				local_x += i * local_width;
+			}
+
+			glViewport(local_x, local_y, local_width, local_height);
+			glScissor(local_x, local_y, local_width, local_height);
+
+			glEnable(GL_SCISSOR_TEST);
+
+			glClearDepth(1.0f);
+
+			glClear(GL_DEPTH_BUFFER_BIT);
+			glDisable(GL_SCISSOR_TEST);
+
+			glDisable(GL_BLEND);
+
+			storage->_copy_screen();
+		}
+	}
+
+	glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height);
+}
+
+void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) {
+	scene_pass = p_pass;
+}
+
+bool RasterizerSceneGLES2::free(RID p_rid) {
+	return true;
+}
+
+void RasterizerSceneGLES2::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw) {
+}
+
+void RasterizerSceneGLES2::initialize() {
+	state.scene_shader.init();
+	state.cube_to_dp_shader.init();
+
+	render_list.init();
+
+	shadow_atlas_realloc_tolerance_msec = 500;
+
+	{
+		//default material and shader
+
+		default_shader = storage->shader_create();
+		storage->shader_set_code(default_shader, "shader_type spatial;\n");
+		default_material = storage->material_create();
+		storage->material_set_shader(default_material, default_shader);
+
+		default_shader_twosided = storage->shader_create();
+		default_material_twosided = storage->material_create();
+		storage->shader_set_code(default_shader_twosided, "shader_type spatial; render_mode cull_disabled;\n");
+		storage->material_set_shader(default_material_twosided, default_shader_twosided);
+	}
+
+	{
+		glGenBuffers(1, &state.sky_verts);
+		glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts);
+		glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 8, NULL, GL_DYNAMIC_DRAW);
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+	}
+
+	// cubemaps for shadows
+	{
+		int max_shadow_cubemap_sampler_size = 512;
+
+		int cube_size = max_shadow_cubemap_sampler_size;
+
+		glActiveTexture(GL_TEXTURE0);
+
+		while (cube_size >= 32) {
+
+			ShadowCubeMap cube;
+
+			cube.size = cube_size;
+
+			glGenTextures(1, &cube.cubemap);
+			glBindTexture(GL_TEXTURE_CUBE_MAP, cube.cubemap);
+
+			for (int i = 0; i < 6; i++) {
+				glTexImage2D(_cube_side_enum[i], 0, GL_DEPTH_COMPONENT16, cube_size, cube_size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
+			}
+
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+			glGenFramebuffers(6, cube.fbo);
+			for (int i = 0; i < 6; i++) {
+
+				glBindFramebuffer(GL_FRAMEBUFFER, cube.fbo[i]);
+				glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, _cube_side_enum[i], cube.cubemap, 0);
+			}
+
+			shadow_cubemaps.push_back(cube);
+
+			cube_size >>= 1;
+		}
+	}
+
+	{
+		// directional shadows
+
+		directional_shadow.light_count = 0;
+		directional_shadow.size = next_power_of_2(GLOBAL_GET("rendering/quality/directional_shadow/size"));
+
+		glGenFramebuffers(1, &directional_shadow.fbo);
+		glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
+
+		glGenTextures(1, &directional_shadow.depth);
+		glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
+
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, directional_shadow.size, directional_shadow.size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0);
+
+		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+		if (status != GL_FRAMEBUFFER_COMPLETE) {
+			ERR_PRINT("Directional shadow framebuffer status invalid");
+		}
+	}
 }
 
 void RasterizerSceneGLES2::iteration() {

+ 314 - 3
drivers/gles2/rasterizer_scene_gles2.h

@@ -33,10 +33,10 @@
 /* Must come before shaders or the Windows build fails... */
 #include "rasterizer_storage_gles2.h"
 
+#include "shaders/cube_to_dp.glsl.gen.h"
 #include "shaders/scene.glsl.gen.h"
 /*
 
-#include "drivers/gles3/shaders/cube_to_dp.glsl.gen.h"
 #include "drivers/gles3/shaders/effect_blur.glsl.gen.h"
 #include "drivers/gles3/shaders/exposure.glsl.gen.h"
 #include "drivers/gles3/shaders/resolve.glsl.gen.h"
@@ -52,6 +52,13 @@
 
 class RasterizerSceneGLES2 : public RasterizerScene {
 public:
+	RID default_material;
+	RID default_material_twosided;
+	RID default_shader;
+	RID default_shader_twosided;
+
+	uint64_t scene_pass;
+
 	RasterizerStorageGLES2 *storage;
 	struct State {
 
@@ -63,7 +70,10 @@ public:
 		GLuint current_main_tex;
 
 		SceneShaderGLES2 scene_shader;
-		// CubeToDpShaderGLES3 cube_to_dp_shader;
+		CubeToDpShaderGLES2 cube_to_dp_shader;
+
+		GLuint sky_verts;
+
 		// ResolveShaderGLES3 resolve_shader;
 		// ScreenSpaceReflectionShaderGLES3 ssr_shader;
 		// EffectBlurShaderGLES3 effect_blur_shader;
@@ -128,7 +138,6 @@ public:
 
 		GLuint env_radiance_ubo;
 
-		GLuint sky_verts;
 		GLuint sky_array;
 
 		GLuint directional_ubo;
@@ -169,11 +178,72 @@ public:
 
 	/* SHADOW ATLAS API */
 
+	uint64_t shadow_atlas_realloc_tolerance_msec;
+
+	struct ShadowAtlas : public RID_Data {
+		enum {
+			QUADRANT_SHIFT = 27,
+			SHADOW_INDEX_MASK = (1 << QUADRANT_SHIFT) - 1,
+			SHADOW_INVALID = 0xFFFFFFFF,
+		};
+
+		struct Quadrant {
+			uint32_t subdivision;
+
+			struct Shadow {
+				RID owner;
+				uint64_t version;
+				uint64_t alloc_tick;
+
+				Shadow() {
+					version = 0;
+					alloc_tick = 0;
+				}
+			};
+
+			Vector<Shadow> shadows;
+
+			Quadrant() {
+				subdivision = 0;
+			}
+		} quadrants[4];
+
+		int size_order[4];
+		uint32_t smallest_subdiv;
+
+		int size;
+
+		GLuint fbo;
+		GLuint depth;
+
+		Map<RID, uint32_t> shadow_owners;
+	};
+
+	struct ShadowCubeMap {
+		GLuint fbo[6];
+		GLuint cubemap;
+		uint32_t size;
+	};
+
+	Vector<ShadowCubeMap> shadow_cubemaps;
+
+	RID_Owner<ShadowAtlas> shadow_atlas_owner;
+
 	RID shadow_atlas_create();
 	void shadow_atlas_set_size(RID p_atlas, int p_size);
 	void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision);
+	bool _shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow);
 	bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version);
 
+	struct DirectionalShadow {
+		GLuint fbo;
+		GLuint depth;
+
+		int light_count;
+		int size;
+		int current_light;
+	} directional_shadow;
+
 	virtual int get_directional_light_shadow_size(RID p_light_intance);
 	virtual void set_directional_shadow_count(int p_count);
 
@@ -196,6 +266,36 @@ public:
 	virtual bool reflection_probe_instance_postprocess_step(RID p_instance);
 
 	/* ENVIRONMENT API */
+
+	struct Environment : public RID_Data {
+		VS::EnvironmentBG bg_mode;
+
+		RID sky;
+		float sky_custom_fov;
+
+		Color bg_color;
+		float bg_energy;
+		float sky_ambient;
+
+		Color ambient_color;
+		float ambient_energy;
+		float ambient_sky_contribution;
+
+		int canvas_max_layer;
+
+		Environment() {
+			bg_mode = VS::ENV_BG_CLEAR_COLOR;
+			sky_custom_fov = 0.0;
+			bg_energy = 1.0;
+			sky_ambient = 0;
+			ambient_energy = 1.0;
+			ambient_sky_contribution = 0.0;
+			canvas_max_layer = 0;
+		}
+	};
+
+	mutable RID_Owner<Environment> environment_owner;
+
 	virtual RID environment_create();
 
 	virtual void environment_set_background(RID p_env, VS::EnvironmentBG p_bg);
@@ -228,6 +328,43 @@ public:
 	virtual int environment_get_canvas_max_layer(RID p_env);
 
 	/* LIGHT INSTANCE */
+
+	struct LightInstance : public RID_Data {
+
+		struct ShadowTransform {
+			CameraMatrix camera;
+			Transform transform;
+			float farplane;
+			float split;
+			float bias_scale;
+		};
+
+		ShadowTransform shadow_transform[4];
+
+		RID self;
+		RID light;
+
+		RasterizerStorageGLES2::Light *light_ptr;
+		Transform transform;
+
+		Vector3 light_vector;
+		Vector3 spot_vector;
+		float linear_att;
+
+		// TODO passes and all that stuff ?
+		uint64_t last_scene_pass;
+		uint64_t last_scene_shadow_pass;
+
+		uint16_t light_index;
+		uint16_t light_directional_index;
+
+		Rect2 directional_rect;
+
+		Set<RID> shadow_atlases; // atlases where this light is registered
+	};
+
+	mutable RID_Owner<LightInstance> light_instance_owner;
+
 	virtual RID light_instance_create(RID p_light);
 	virtual void light_instance_set_transform(RID p_light_instance, const Transform &p_transform);
 	virtual void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale = 1.0);
@@ -242,6 +379,180 @@ public:
 
 	/* RENDER LIST */
 
+	struct RenderList {
+		enum {
+			DEFAULT_MAX_ELEMENTS = 65536,
+			SORT_FLAG_SKELETON = 1,
+			SORT_FLAG_INSTANCING = 2,
+			MAX_DIRECTIONAL_LIGHTS = 16,
+			MAX_LIGHTS = 4096,
+			MAX_REFLECTIONS = 1024,
+
+			SORT_KEY_PRIORITY_SHIFT = 56,
+			SORT_KEY_PRIORITY_MASK = 0xFF,
+			//depth layer for opaque (56-52)
+			SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT = 52,
+			SORT_KEY_OPAQUE_DEPTH_LAYER_MASK = 0xF,
+//64 bits unsupported in MSVC
+#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 49)
+#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 48)
+#define SORT_KEY_LIGHTMAP_CAPTURE_FLAG (uint64_t(1) << 47)
+#define SORT_KEY_LIGHTMAP_FLAG (uint64_t(1) << 46)
+#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 45)
+#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 44)
+			SORT_KEY_SHADING_SHIFT = 44,
+			SORT_KEY_SHADING_MASK = 63,
+			//44-28 material index
+			SORT_KEY_MATERIAL_INDEX_SHIFT = 28,
+			//28-8 geometry index
+			SORT_KEY_GEOMETRY_INDEX_SHIFT = 8,
+			//bits 5-7 geometry type
+			SORT_KEY_GEOMETRY_TYPE_SHIFT = 5,
+			//bits 0-5 for flags
+			SORT_KEY_OPAQUE_PRE_PASS = 8,
+			SORT_KEY_CULL_DISABLED_FLAG = 4,
+			SORT_KEY_SKELETON_FLAG = 2,
+			SORT_KEY_MIRROR_FLAG = 1
+		};
+
+		int max_elements;
+
+		struct Element {
+			RasterizerScene::InstanceBase *instance;
+
+			RasterizerStorageGLES2::Geometry *geometry;
+			RasterizerStorageGLES2::Material *material;
+			RasterizerStorageGLES2::GeometryOwner *owner;
+
+			uint64_t sort_key;
+		};
+
+		Element *base_elements;
+		Element **elements;
+
+		int element_count;
+		int alpha_element_count;
+
+		void clear() {
+			element_count = 0;
+			alpha_element_count = 0;
+		}
+
+		// sorts
+
+		struct SortByKey {
+			_FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const {
+				return A->sort_key < B->sort_key;
+			}
+		};
+
+		void sort_by_key(bool p_alpha) {
+			SortArray<Element *, SortByKey> sorter;
+
+			if (p_alpha) {
+				sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count);
+			} else {
+				sorter.sort(elements, element_count);
+			}
+		}
+
+		struct SortByDepth {
+
+			_FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const {
+				return A->instance->depth < B->instance->depth;
+			}
+		};
+
+		void sort_by_depth(bool p_alpha) { //used for shadows
+
+			SortArray<Element *, SortByDepth> sorter;
+			if (p_alpha) {
+				sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count);
+			} else {
+				sorter.sort(elements, element_count);
+			}
+		}
+
+		struct SortByReverseDepthAndPriority {
+
+			_FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const {
+				uint32_t layer_A = uint32_t(A->sort_key >> SORT_KEY_PRIORITY_SHIFT);
+				uint32_t layer_B = uint32_t(B->sort_key >> SORT_KEY_PRIORITY_SHIFT);
+				if (layer_A == layer_B) {
+					return A->instance->depth > B->instance->depth;
+				} else {
+					return layer_A < layer_B;
+				}
+			}
+		};
+
+		void sort_by_reverse_depth_and_priority(bool p_alpha) { //used for alpha
+
+			SortArray<Element *, SortByReverseDepthAndPriority> sorter;
+			if (p_alpha) {
+				sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count);
+			} else {
+				sorter.sort(elements, element_count);
+			}
+		}
+
+		// element adding and stuff
+
+		_FORCE_INLINE_ Element *add_element() {
+			if (element_count + alpha_element_count >= max_elements)
+				return NULL;
+
+			elements[element_count] = &base_elements[element_count];
+			return elements[element_count++];
+		}
+
+		_FORCE_INLINE_ Element *add_alpha_element() {
+			if (element_count + alpha_element_count >= max_elements) {
+				return NULL;
+			}
+
+			int idx = max_elements - alpha_element_count - 1;
+			elements[idx] = &base_elements[idx];
+			alpha_element_count++;
+			return elements[idx];
+		}
+
+		void init() {
+			element_count = 0;
+			alpha_element_count = 0;
+
+			elements = memnew_arr(Element *, max_elements);
+			base_elements = memnew_arr(Element, max_elements);
+
+			for (int i = 0; i < max_elements; i++) {
+				elements[i] = &base_elements[i];
+			}
+		}
+
+		RenderList() {
+			max_elements = DEFAULT_MAX_ELEMENTS;
+		}
+
+		~RenderList() {
+			memdelete_arr(elements);
+			memdelete_arr(base_elements);
+		}
+	};
+
+	RenderList render_list;
+
+	void _add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass);
+	void _add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass, bool p_shadow_pass);
+
+	void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass);
+	void _render_render_list(RenderList::Element **p_elements, int p_element_count, const RID *p_light_cull_result, int p_light_cull_count, const Transform &p_view_transform, const CameraMatrix &p_projection, RID p_shadow_atlas, Environment *p_env, GLuint p_base_env, float p_shadow_bias, float p_shadow_normal_bias, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add, bool p_directional_shadows);
+
+	void _draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy);
+
+	void _setup_material(RasterizerStorageGLES2::Material *p_material, bool p_use_radiance_map, bool p_reverse_cull, bool p_shadow_atlas = false, bool p_skeleton_tex = false, Size2i p_skeleton_tex_size = Size2i(0, 0));
+	void _setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton);
+	void _render_geometry(RenderList::Element *p_element);
+
 	virtual void render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
 	virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count);
 	virtual bool free(RID p_rid);

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 1517 - 127
drivers/gles2/rasterizer_storage_gles2.cpp


+ 319 - 7
drivers/gles2/rasterizer_storage_gles2.h

@@ -30,6 +30,7 @@
 #ifndef RASTERIZERSTORAGEGLES2_H
 #define RASTERIZERSTORAGEGLES2_H
 
+#include "dvector.h"
 #include "self_list.h"
 #include "servers/visual/rasterizer.h"
 #include "servers/visual/shader_language.h"
@@ -37,11 +38,11 @@
 #include "shader_gles2.h"
 
 #include "shaders/copy.glsl.gen.h"
+#include "shaders/cubemap_filter.glsl.gen.h"
 /*
 #include "shaders/blend_shape.glsl.gen.h"
 #include "shaders/canvas.glsl.gen.h"
 #include "shaders/copy.glsl.gen.h"
-#include "shaders/cubemap_filter.glsl.gen.h"
 #include "shaders/particles.glsl.gen.h"
 */
 
@@ -76,6 +77,10 @@ public:
 
 		Set<String> extensions;
 
+		bool float_texture_supported;
+		bool s3tc_supported;
+		bool etc1_supported;
+
 		bool keep_original_textures;
 
 		bool no_depth_prepass;
@@ -89,8 +94,13 @@ public:
 		GLuint normal_tex;
 		GLuint aniso_tex;
 
+		GLuint radical_inverse_vdc_cache_tex;
+
 		GLuint quadie;
-		GLuint quadie_array;
+
+		size_t skeleton_transform_buffer_size;
+		GLuint skeleton_transform_buffer;
+		PoolVector<float> skeleton_transform_cpu_buffer;
 
 	} resources;
 
@@ -99,6 +109,7 @@ public:
 		ShaderCompilerGLES2 compiler;
 
 		CopyShaderGLES2 copy;
+		CubemapFilterShaderGLES2 cubemap_filter;
 
 		ShaderCompilerGLES2::IdentifierActions actions_canvas;
 		ShaderCompilerGLES2::IdentifierActions actions_scene;
@@ -139,10 +150,72 @@ public:
 
 	} info;
 
+	void bind_quad_array() const;
+
 	/////////////////////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////DATA///////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////////////////////
 
+	struct Instanciable : public RID_Data {
+		SelfList<RasterizerScene::InstanceBase>::List instance_list;
+
+		_FORCE_INLINE_ void instance_change_notify() {
+			SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
+
+			while (instances) {
+				instances->self()->base_changed();
+				instances = instances->next();
+			}
+		}
+
+		_FORCE_INLINE_ void instance_material_change_notify() {
+			SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
+
+			while (instances) {
+				instances->self()->base_material_changed();
+				instances = instances->next();
+			}
+		}
+
+		_FORCE_INLINE_ void instance_remove_deps() {
+			SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
+
+			while (instances) {
+				instances->self()->base_removed();
+				instances = instances->next();
+			}
+		}
+
+		Instanciable() {}
+
+		virtual ~Instanciable() {}
+	};
+
+	struct GeometryOwner : public Instanciable {
+	};
+
+	struct Geometry : public Instanciable {
+
+		enum Type {
+			GEOMETRY_INVALID,
+			GEOMETRY_SURFACE,
+			GEOMETRY_IMMEDIATE,
+			GEOMETRY_MULTISURFACE
+		};
+
+		Type type;
+		RID material;
+		uint64_t last_pass;
+		uint32_t index;
+
+		virtual void material_changed_notify() {}
+
+		Geometry() {
+			last_pass = 0;
+			index = 0;
+		}
+	};
+
 	/////////////////////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////API////////////////////////////////////////////////////
 	/////////////////////////////////////////////////////////////////////////////////////////
@@ -171,6 +244,10 @@ public:
 		int total_data_size;
 		bool ignore_mipmaps;
 
+		bool compressed;
+
+		bool srgb;
+
 		int mipmaps;
 
 		bool active;
@@ -184,6 +261,15 @@ public:
 
 		bool redraw_if_visible;
 
+		VisualServer::TextureDetectCallback detect_3d;
+		void *detect_3d_ud;
+
+		VisualServer::TextureDetectCallback detect_srgb;
+		void *detect_srgb_ud;
+
+		VisualServer::TextureDetectCallback detect_normal;
+		void *detect_normal_ud;
+
 		Texture() {
 			flags = 0;
 			width = 0;
@@ -198,6 +284,8 @@ public:
 			total_data_size = 0;
 			ignore_mipmaps = false;
 
+			compressed = false;
+
 			active = false;
 
 			tex_id = 0;
@@ -236,7 +324,7 @@ public:
 
 	mutable RID_Owner<Texture> texture_owner;
 
-	Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type);
+	Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed);
 
 	virtual RID texture_create();
 	virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT);
@@ -272,6 +360,15 @@ public:
 
 	/* SKY API */
 
+	struct Sky : public RID_Data {
+
+		RID panorama;
+		GLuint radiance;
+		int radiance_size;
+	};
+
+	mutable RID_Owner<Sky> sky_owner;
+
 	virtual RID sky_create();
 	virtual void sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size);
 
@@ -332,7 +429,6 @@ public:
 
 		} canvas_item;
 
-		/*
 		struct Spatial {
 
 			enum BlendMode {
@@ -369,6 +465,7 @@ public:
 			bool uses_discard;
 			bool uses_sss;
 			bool uses_screen_texture;
+			bool uses_depth_texture;
 			bool uses_time;
 			bool writes_modelview_or_projection;
 			bool uses_vertex_lighting;
@@ -379,7 +476,6 @@ public:
 		struct Particles {
 
 		} particles;
-		*/
 
 		bool uses_vertex_time;
 		bool uses_fragment_time;
@@ -419,7 +515,7 @@ public:
 		Map<StringName, Variant> params;
 		SelfList<Material> list;
 		SelfList<Material> dirty_list;
-		Vector<RID> textures;
+		Vector<Pair<StringName, RID> > textures;
 		float line_width;
 		int render_priority;
 
@@ -449,6 +545,11 @@ public:
 	mutable SelfList<Material>::List _material_dirty_list;
 	void _material_make_dirty(Material *p_material) const;
 
+	void _material_add_geometry(RID p_material, Geometry *p_geometry);
+	void _material_remove_geometry(RID p_material, Geometry *p_geometry);
+
+	void _update_material(Material *p_material);
+
 	mutable RID_Owner<Material> material_owner;
 
 	virtual RID material_create();
@@ -473,6 +574,109 @@ public:
 	void update_dirty_materials();
 
 	/* MESH API */
+
+	struct Mesh;
+
+	struct Surface : public Geometry {
+
+		struct Attrib {
+			bool enabled;
+			bool integer;
+			GLuint index;
+			GLint size;
+			GLenum type;
+			GLboolean normalized;
+			GLsizei stride;
+			uint32_t offset;
+		};
+
+		Attrib attribs[VS::ARRAY_MAX];
+
+		Mesh *mesh;
+		uint32_t format;
+
+		GLuint vertex_id;
+		GLuint index_id;
+
+		struct BlendShape {
+			GLuint vertex_id;
+			GLuint array_id;
+		};
+
+		Vector<BlendShape> blend_shapes;
+
+		AABB aabb;
+
+		int array_len;
+		int index_array_len;
+		int max_bone;
+
+		int array_byte_size;
+		int index_array_byte_size;
+
+		VS::PrimitiveType primitive;
+
+		Vector<AABB> skeleton_bone_aabb;
+		Vector<bool> skeleton_bone_used;
+
+		bool active;
+
+		PoolVector<uint8_t> data;
+		PoolVector<uint8_t> index_data;
+
+		int total_data_size;
+
+		Surface() {
+			array_byte_size = 0;
+			index_array_byte_size = 0;
+
+			array_len = 0;
+			index_array_len = 0;
+
+			mesh = NULL;
+
+			primitive = VS::PRIMITIVE_POINTS;
+
+			active = false;
+
+			total_data_size = 0;
+		}
+	};
+
+	struct MultiMesh;
+
+	struct Mesh : public GeometryOwner {
+
+		bool active;
+
+		Vector<Surface *> surfaces;
+
+		int blend_shape_count;
+		VS::BlendShapeMode blend_shape_mode;
+
+		AABB custom_aabb;
+
+		mutable uint64_t last_pass;
+
+		SelfList<MultiMesh>::List multimeshes;
+
+		_FORCE_INLINE_ void update_multimeshes() {
+			SelfList<MultiMesh> *mm = multimeshes.first();
+
+			while (mm) {
+				mm->self()->instance_material_change_notify();
+				mm = mm->next();
+			}
+		}
+
+		Mesh() {
+			blend_shape_mode = VS::BLEND_SHAPE_MODE_NORMALIZED;
+			blend_shape_count = 0;
+		}
+	};
+
+	mutable RID_Owner<Mesh> mesh_owner;
+
 	virtual RID mesh_create();
 
 	virtual void mesh_add_surface(RID p_mesh, uint32_t p_format, VS::PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes = Vector<PoolVector<uint8_t> >(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>());
@@ -512,6 +716,55 @@ public:
 
 	/* MULTIMESH API */
 
+	struct MultiMesh : public GeometryOwner {
+
+		RID mesh;
+		int size;
+
+		VS::MultimeshTransformFormat transform_format;
+		VS::MultimeshColorFormat color_format;
+		VS::MultimeshCustomDataFormat custom_data_format;
+
+		Vector<float> data;
+
+		AABB aabb;
+
+		SelfList<MultiMesh> update_list;
+		SelfList<MultiMesh> mesh_list;
+
+		int visible_instances;
+
+		int xform_floats;
+		int color_floats;
+		int custom_data_floats;
+
+		bool dirty_aabb;
+		bool dirty_data;
+
+		MultiMesh() :
+				update_list(this),
+				mesh_list(this) {
+			dirty_aabb = true;
+			dirty_data = true;
+
+			xform_floats = 0;
+			color_floats = 0;
+			custom_data_floats = 0;
+
+			visible_instances = -1;
+
+			size = 0;
+
+			transform_format = VS::MULTIMESH_TRANSFORM_2D;
+			color_format = VS::MULTIMESH_COLOR_NONE;
+			custom_data_format = VS::MULTIMESH_CUSTOM_DATA_NONE;
+		}
+	};
+
+	mutable RID_Owner<MultiMesh> multimesh_owner;
+
+	SelfList<MultiMesh>::List multimesh_update_list;
+
 	virtual RID multimesh_create();
 
 	virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE);
@@ -521,7 +774,7 @@ public:
 	virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform);
 	virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform);
 	virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color);
-	virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color);
+	virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_custom_data);
 
 	virtual RID multimesh_get_mesh(RID p_multimesh) const;
 
@@ -557,6 +810,33 @@ public:
 
 	/* SKELETON API */
 
+	struct Skeleton : RID_Data {
+
+		bool use_2d;
+
+		int size;
+
+		// TODO use float textures for storage
+
+		Vector<float> bone_data;
+
+		GLuint tex_id;
+
+		SelfList<Skeleton> update_list;
+		Set<RasterizerScene::InstanceBase *> instances;
+
+		Skeleton() :
+				update_list(this) {
+			tex_id = 0;
+			size = 0;
+			use_2d = false;
+		}
+	};
+
+	mutable RID_Owner<Skeleton> skeleton_owner;
+
+	SelfList<Skeleton>::List skeleton_update_list;
+
 	void update_dirty_skeletons();
 
 	virtual RID skeleton_create();
@@ -568,8 +848,38 @@ public:
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
 
+	void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size);
+
 	/* Light API */
 
+	struct Light : Instanciable {
+		VS::LightType type;
+		float param[VS::LIGHT_PARAM_MAX];
+
+		Color color;
+		Color shadow_color;
+
+		RID projector;
+
+		bool shadow;
+		bool negative;
+		bool reverse_cull;
+
+		uint32_t cull_mask;
+
+		VS::LightOmniShadowMode omni_shadow_mode;
+		VS::LightOmniShadowDetail omni_shadow_detail;
+
+		VS::LightDirectionalShadowMode directional_shadow_mode;
+		VS::LightDirectionalShadowDepthRangeMode directional_range_mode;
+
+		bool directional_blend_splits;
+
+		uint64_t version;
+	};
+
+	mutable RID_Owner<Light> light_owner;
+
 	virtual RID light_create(VS::LightType p_type);
 
 	virtual void light_set_color(RID p_light, const Color &p_color);
@@ -831,6 +1141,8 @@ public:
 	void initialize();
 	void finalize();
 
+	void _copy_screen();
+
 	virtual bool has_os_feature(const String &p_feature) const;
 
 	virtual void update_dirty_resources();

+ 62 - 5
drivers/gles2/shader_compiler_gles2.cpp

@@ -325,7 +325,7 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
 				uniform_code += ";\n";
 
 				if (SL::is_sampler_type(E->get().type)) {
-					r_gen_code.texture_uniforms.write[E->get().texture_order] = _mkid(E->key());
+					r_gen_code.texture_uniforms.write[E->get().texture_order] = E->key();
 					r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint;
 				} else {
 					r_gen_code.uniforms.write[E->get().order] = E->key();
@@ -507,7 +507,6 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
 				case SL::OP_ASSIGN_DIV:
 				case SL::OP_ASSIGN_SHIFT_LEFT:
 				case SL::OP_ASSIGN_SHIFT_RIGHT:
-				case SL::OP_ASSIGN_MOD:
 				case SL::OP_ASSIGN_BIT_AND:
 				case SL::OP_ASSIGN_BIT_OR:
 				case SL::OP_ASSIGN_BIT_XOR: {
@@ -518,6 +517,16 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
 					code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
 				} break;
 
+				case SL::OP_ASSIGN_MOD: {
+					code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true);
+					code += " = ";
+					code += "mod(";
+					code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true);
+					code += ", ";
+					code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+					code += ")";
+				} break;
+
 				case SL::OP_BIT_INVERT:
 				case SL::OP_NEGATE:
 				case SL::OP_NOT:
@@ -552,6 +561,45 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
 								code += "textureCube";
 							}
 
+						} else if (var_node->name == "textureLod") {
+							// emit texture call
+
+							if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLER2D) {
+								code += "texture2DLod";
+							} else if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLERCUBE) {
+								code += "textureCubeLod";
+							}
+
+						} else if (var_node->name == "mix") {
+
+							switch (op_node->arguments[3]->get_datatype()) {
+
+								case SL::TYPE_BVEC2: {
+									code += "select2";
+								} break;
+
+								case SL::TYPE_BVEC3: {
+									code += "select3";
+								} break;
+
+								case SL::TYPE_BVEC4: {
+									code += "select4";
+								} break;
+
+								case SL::TYPE_VEC2:
+								case SL::TYPE_VEC3:
+								case SL::TYPE_VEC4:
+								case SL::TYPE_FLOAT: {
+
+									code += "mix";
+								} break;
+
+								default: {
+									SL::DataType type = op_node->arguments[3]->get_datatype();
+									print_line(String("uhhhh invalid mix with type: ") + itos(type));
+								} break;
+							}
+
 						} else if (p_default_actions.renames.has(var_node->name)) {
 							code += p_default_actions.renames[var_node->name];
 						} else if (internal_functions.has(var_node->name)) {
@@ -590,6 +638,15 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
 					code += _dump_node_code(op_node->arguments[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
 				} break;
 
+				case SL::OP_MOD: {
+
+					code += "mod(";
+					code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+					code += ", ";
+					code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+					code += ")";
+				} break;
+
 				default: {
 					code += "(";
 					code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
@@ -751,10 +808,10 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
 	/** SPATIAL SHADER **/
 
 	actions[VS::SHADER_SPATIAL].renames["WORLD_MATRIX"] = "world_transform";
-	actions[VS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_inverse_matrix";
-	actions[VS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_matrix";
+	actions[VS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_matrix";
+	actions[VS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_inverse_matrix";
 	actions[VS::SHADER_SPATIAL].renames["PROJECTION_MATRIX"] = "projection_matrix";
-	actions[VS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix";
+	actions[VS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "projection_inverse_matrix";
 	actions[VS::SHADER_SPATIAL].renames["MODELVIEW_MATRIX"] = "modelview";
 
 	actions[VS::SHADER_SPATIAL].renames["VERTEX"] = "vertex.xyz";

+ 383 - 6
drivers/gles2/shader_gles2.cpp

@@ -33,7 +33,10 @@
 #include "print_string.h"
 #include "string_builder.h"
 
-//#define DEBUG_OPENGL
+#include "rasterizer_gles2.h"
+#include "rasterizer_storage_gles2.h"
+
+// #define DEBUG_OPENGL
 
 // #include "shaders/copy.glsl.gen.h"
 
@@ -54,7 +57,7 @@
 
 ShaderGLES2 *ShaderGLES2::active = NULL;
 
-//#define DEBUG_SHADER
+// #define DEBUG_SHADER
 
 #ifdef DEBUG_SHADER
 
@@ -83,7 +86,10 @@ void ShaderGLES2::bind_uniforms() {
 			continue;
 		}
 
-		const Variant &v = E->value();
+		Variant v;
+
+		v = E->value();
+
 		_set_uniform_variant(location, v);
 		E = E->next();
 	}
@@ -128,6 +134,28 @@ bool ShaderGLES2::bind() {
 
 	glUseProgram(version->id);
 
+	// find out uniform names and locations
+
+	int count;
+	glGetProgramiv(version->id, GL_ACTIVE_UNIFORMS, &count);
+	version->uniform_names.resize(count);
+
+	for (int i = 0; i < count; i++) {
+		GLchar uniform_name[1024];
+		int len = 0;
+		GLint size = 0;
+		GLenum type;
+
+		glGetActiveUniform(version->id, i, 1024, &len, &size, &type, uniform_name);
+
+		uniform_name[len] = '\0';
+		String name = String((const char *)uniform_name);
+
+		version->uniform_names.write[i] = name;
+	}
+
+	bind_uniforms();
+
 	DEBUG_TEST_ERROR("use program");
 
 	active = this;
@@ -228,7 +256,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() {
 	}
 
 	if (!_v)
-		version_map[conditional_version];
+		version_map[conditional_version] = Version();
 
 	Version &v = version_map[conditional_version];
 
@@ -389,6 +417,10 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() {
 	strings.push_back(fragment_code3.get_data());
 
 #ifdef DEBUG_SHADER
+
+	if (cc) {
+		DEBUG_PRINT("\nFragment Code:\n\n" + String(cc->fragment_globals));
+	}
 	DEBUG_PRINT("\nFragment Code:\n\n" + String(code_string.get_data()));
 #endif
 
@@ -500,9 +532,18 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() {
 	}
 
 	if (cc) {
-		v.custom_uniform_locations.resize(cc->custom_uniforms.size());
+		// uniforms
 		for (int i = 0; i < cc->custom_uniforms.size(); i++) {
-			v.custom_uniform_locations.write[i] = glGetUniformLocation(v.id, String(cc->custom_uniforms[i]).ascii().get_data());
+			StringName native_uniform_name = "m_" + cc->custom_uniforms[i];
+			GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data());
+			v.custom_uniform_locations[cc->custom_uniforms[i]] = location;
+		}
+
+		// textures
+		for (int i = 0; i < cc->texture_uniforms.size(); i++) {
+			StringName native_uniform_name = "m_" + cc->texture_uniforms[i];
+			GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data());
+			v.custom_uniform_locations[cc->texture_uniforms[i]] = location;
 		}
 	}
 
@@ -660,6 +701,7 @@ void ShaderGLES2::set_custom_shader_code(uint32_t p_code_id,
 	cc->light = p_light;
 	cc->custom_uniforms = p_uniforms;
 	cc->custom_defines = p_custom_defines;
+	cc->texture_uniforms = p_texture_uniforms;
 	cc->version++;
 }
 
@@ -675,6 +717,341 @@ void ShaderGLES2::free_custom_shader(uint32_t p_code_id) {
 	custom_code_map.erase(p_code_id);
 }
 
+void ShaderGLES2::use_material(void *p_material, int p_num_predef_textures) {
+	RasterizerStorageGLES2::Material *material = (RasterizerStorageGLES2::Material *)p_material;
+
+	if (!material) {
+		return;
+	}
+
+	if (!material->shader) {
+		return;
+	}
+
+	Version *v = version_map.getptr(conditional_version);
+
+	CustomCode *cc = NULL;
+	if (v) {
+		cc = custom_code_map.getptr(v->code_version);
+	}
+
+	// bind uniforms
+	for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = material->shader->uniforms.front(); E; E = E->next()) {
+
+		if (E->get().texture_order >= 0)
+			continue; // this is a texture, doesn't go here
+
+		Map<StringName, Variant>::Element *V = material->params.find(E->key());
+
+		Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > value;
+
+		value.first = E->get().type;
+		value.second = E->get().default_value;
+
+		if (V) {
+			value.second = Vector<ShaderLanguage::ConstantNode::Value>();
+			value.second.resize(E->get().default_value.size());
+			switch (E->get().type) {
+				case ShaderLanguage::TYPE_BOOL: {
+					if (value.second.size() < 1)
+						value.second.resize(1);
+					value.second.write[0].boolean = V->get();
+				} break;
+
+				case ShaderLanguage::TYPE_BVEC2: {
+					if (value.second.size() < 2)
+						value.second.resize(2);
+					int flags = V->get();
+					value.second.write[0].boolean = flags & 1;
+					value.second.write[1].boolean = flags & 2;
+				} break;
+
+				case ShaderLanguage::TYPE_BVEC3: {
+					if (value.second.size() < 3)
+						value.second.resize(3);
+					int flags = V->get();
+					value.second.write[0].boolean = flags & 1;
+					value.second.write[1].boolean = flags & 2;
+					value.second.write[2].boolean = flags & 4;
+
+				} break;
+
+				case ShaderLanguage::TYPE_BVEC4: {
+					if (value.second.size() < 4)
+						value.second.resize(4);
+					int flags = V->get();
+					value.second.write[0].boolean = flags & 1;
+					value.second.write[1].boolean = flags & 2;
+					value.second.write[2].boolean = flags & 4;
+					value.second.write[3].boolean = flags & 8;
+
+				} break;
+
+				case ShaderLanguage::TYPE_INT: {
+					if (value.second.size() < 1)
+						value.second.resize(1);
+					int val = V->get();
+					value.second.write[0].sint = val;
+				} break;
+
+				case ShaderLanguage::TYPE_IVEC2: {
+					if (value.second.size() < 2)
+						value.second.resize(2);
+					PoolIntArray val = V->get();
+					for (int i = 0; i < val.size(); i++) {
+						value.second.write[i].sint = val[i];
+					}
+				} break;
+
+				case ShaderLanguage::TYPE_IVEC3: {
+					if (value.second.size() < 3)
+						value.second.resize(3);
+					PoolIntArray val = V->get();
+					for (int i = 0; i < val.size(); i++) {
+						value.second.write[i].sint = val[i];
+					}
+
+				} break;
+
+				case ShaderLanguage::TYPE_IVEC4: {
+					if (value.second.size() < 4)
+						value.second.resize(4);
+					PoolIntArray val = V->get();
+					for (int i = 0; i < val.size(); i++) {
+						value.second.write[i].sint = val[i];
+					}
+
+				} break;
+
+				case ShaderLanguage::TYPE_UINT: {
+					if (value.second.size() < 1)
+						value.second.resize(1);
+					uint32_t val = V->get();
+					value.second.write[0].uint = val;
+				} break;
+
+				case ShaderLanguage::TYPE_UVEC2: {
+					if (value.second.size() < 2)
+						value.second.resize(2);
+					PoolIntArray val = V->get();
+					for (int i = 0; i < val.size(); i++) {
+						value.second.write[i].uint = val[i];
+					}
+
+				} break;
+
+				case ShaderLanguage::TYPE_UVEC3: {
+					if (value.second.size() < 3)
+						value.second.resize(3);
+					PoolIntArray val = V->get();
+					for (int i = 0; i < val.size(); i++) {
+						value.second.write[i].uint = val[i];
+					}
+
+				} break;
+
+				case ShaderLanguage::TYPE_UVEC4: {
+					if (value.second.size() < 4)
+						value.second.resize(4);
+					PoolIntArray val = V->get();
+					for (int i = 0; i < val.size(); i++) {
+						value.second.write[i].uint = val[i];
+					}
+
+				} break;
+
+				case ShaderLanguage::TYPE_FLOAT: {
+					if (value.second.size() < 1)
+						value.second.resize(1);
+					value.second.write[0].real = V->get();
+
+				} break;
+
+				case ShaderLanguage::TYPE_VEC2: {
+					if (value.second.size() < 2)
+						value.second.resize(2);
+					Vector2 val = V->get();
+					value.second.write[0].real = val.x;
+					value.second.write[1].real = val.y;
+				} break;
+
+				case ShaderLanguage::TYPE_VEC3: {
+					if (value.second.size() < 3)
+						value.second.resize(3);
+					Vector3 val = V->get();
+					value.second.write[0].real = val.x;
+					value.second.write[1].real = val.y;
+					value.second.write[2].real = val.z;
+				} break;
+
+				case ShaderLanguage::TYPE_VEC4: {
+					if (value.second.size() < 4)
+						value.second.resize(4);
+					if (V->get().get_type() == Variant::PLANE) {
+						Plane val = V->get();
+						value.second.write[0].real = val.normal.x;
+						value.second.write[1].real = val.normal.y;
+						value.second.write[2].real = val.normal.z;
+						value.second.write[3].real = val.d;
+					} else {
+						Color val = V->get();
+						value.second.write[0].real = val.r;
+						value.second.write[1].real = val.g;
+						value.second.write[2].real = val.b;
+						value.second.write[3].real = val.a;
+					}
+
+				} break;
+
+				case ShaderLanguage::TYPE_MAT2: {
+					Transform2D val = V->get();
+
+					// TODO
+
+				} break;
+
+				case ShaderLanguage::TYPE_MAT3: {
+					Basis val = V->get();
+
+					// TODO
+				} break;
+
+				case ShaderLanguage::TYPE_MAT4: {
+					Transform val = V->get();
+
+					// TODO
+				} break;
+
+				case ShaderLanguage::TYPE_SAMPLER2D: {
+
+				} break;
+
+				case ShaderLanguage::TYPE_ISAMPLER2D: {
+
+				} break;
+
+				case ShaderLanguage::TYPE_USAMPLER2D: {
+
+				} break;
+
+				case ShaderLanguage::TYPE_SAMPLERCUBE: {
+
+				} break;
+			}
+		} else {
+			if (value.second.size() == 0) {
+				// No default value set... weird, let's just use zero for everything
+				size_t default_arg_size = 1;
+				bool is_float = false;
+				switch (E->get().type) {
+					case ShaderLanguage::TYPE_BOOL:
+					case ShaderLanguage::TYPE_INT:
+					case ShaderLanguage::TYPE_UINT: {
+						default_arg_size = 1;
+					} break;
+
+					case ShaderLanguage::TYPE_FLOAT: {
+						default_arg_size = 1;
+						is_float = true;
+					} break;
+
+					case ShaderLanguage::TYPE_BVEC2:
+					case ShaderLanguage::TYPE_IVEC2:
+					case ShaderLanguage::TYPE_UVEC2: {
+						default_arg_size = 2;
+					} break;
+
+					case ShaderLanguage::TYPE_VEC2: {
+						default_arg_size = 2;
+						is_float = true;
+					} break;
+
+					case ShaderLanguage::TYPE_BVEC3:
+					case ShaderLanguage::TYPE_IVEC3:
+					case ShaderLanguage::TYPE_UVEC3: {
+						default_arg_size = 3;
+					} break;
+
+					case ShaderLanguage::TYPE_VEC3: {
+						default_arg_size = 3;
+						is_float = true;
+					} break;
+
+					case ShaderLanguage::TYPE_BVEC4:
+					case ShaderLanguage::TYPE_IVEC4:
+					case ShaderLanguage::TYPE_UVEC4: {
+						default_arg_size = 4;
+					} break;
+
+					case ShaderLanguage::TYPE_VEC4: {
+						default_arg_size = 4;
+						is_float = true;
+					} break;
+
+					default: {
+						// TODO matricies and all that stuff
+						default_arg_size = 1;
+					} break;
+				}
+
+				value.second.resize(default_arg_size);
+
+				for (int i = 0; i < default_arg_size; i++) {
+					if (is_float) {
+						value.second.write[i].real = 0.0;
+					} else {
+						value.second.write[i].uint = 0;
+					}
+				}
+			}
+		}
+
+		// GLint location = get_uniform_location(E->key());
+
+		GLint location;
+		if (v->custom_uniform_locations.has(E->key())) {
+			location = v->custom_uniform_locations[E->key()];
+		} else {
+			int idx = v->uniform_names.find(E->key()); // TODO maybe put those in a Map?
+			if (idx < 0) {
+				location = -1;
+			} else {
+				location = v->uniform_location[idx];
+			}
+		}
+
+		_set_uniform_value(location, value);
+	}
+
+	// bind textures
+	int tc = material->textures.size();
+	Pair<StringName, RID> *textures = material->textures.ptrw();
+
+	ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = material->shader->texture_hints.ptrw();
+
+	for (int i = 0; i < tc; i++) {
+
+		Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > value;
+		value.first = ShaderLanguage::TYPE_INT;
+		value.second.resize(1);
+		value.second.write[0].sint = p_num_predef_textures + i;
+
+		// GLint location = get_uniform_location(textures[i].first);
+
+		// if (location < 0) {
+		//	location = material->shader->uniform_locations[textures[i].first];
+		// }
+		GLint location = -1;
+		if (v->custom_uniform_locations.has(textures[i].first)) {
+			location = v->custom_uniform_locations[textures[i].first];
+		} else {
+			location = get_uniform_location(textures[i].first);
+		}
+
+		_set_uniform_value(location, value);
+	}
+}
+
 void ShaderGLES2::set_base_material_tex_index(int p_idx) {
 }
 

+ 176 - 40
drivers/gles2/shader_gles2.h

@@ -44,6 +44,11 @@
 #include "map.h"
 #include "variant.h"
 
+#include "core/pair.h"
+#include "servers/visual/shader_language.h"
+
+class RasterizerStorageGLES2;
+
 class ShaderGLES2 {
 protected:
 	struct Enum {
@@ -105,9 +110,10 @@ private:
 		GLuint id;
 		GLuint vert_id;
 		GLuint frag_id;
+		Vector<StringName> uniform_names;
 		GLint *uniform_location;
 		Vector<GLint> texture_uniform_locations;
-		Vector<GLint> custom_uniform_locations;
+		Map<StringName, GLint> custom_uniform_locations;
 		uint32_t code_version;
 		bool ok;
 		Version() {
@@ -169,6 +175,168 @@ private:
 
 	int max_image_units;
 
+	Map<uint32_t, Variant> uniform_defaults;
+	Map<uint32_t, CameraMatrix> uniform_cameras;
+
+	Map<StringName, Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > > uniform_values;
+
+protected:
+	_FORCE_INLINE_ int _get_uniform(int p_which) const;
+	_FORCE_INLINE_ void _set_conditional(int p_which, bool p_value);
+
+	void setup(const char **p_conditional_defines,
+			int p_conditional_count,
+			const char **p_uniform_names,
+			int p_uniform_count,
+			const AttributePair *p_attribute_pairs,
+			int p_attribute_count,
+			const TexUnitPair *p_texunit_pairs,
+			int p_texunit_pair_count,
+			const char *p_vertex_code,
+			const char *p_fragment_code,
+			int p_vertex_code_start,
+			int p_fragment_code_start);
+
+	ShaderGLES2();
+
+public:
+	enum {
+		CUSTOM_SHADER_DISABLED = 0
+	};
+
+	GLint get_uniform_location(const String &p_name) const;
+	GLint get_uniform_location(int p_index) const;
+
+	static _FORCE_INLINE_ ShaderGLES2 *get_active() { return active; }
+	bool bind();
+	void unbind();
+	void bind_uniforms();
+
+	inline GLuint get_program() const { return version ? version->id : 0; }
+
+	void clear_caches();
+
+	_FORCE_INLINE_ void _set_uniform_value(GLint p_uniform, const Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > &value) {
+		if (p_uniform < 0)
+			return;
+
+		const Vector<ShaderLanguage::ConstantNode::Value> &values = value.second;
+
+		switch (value.first) {
+			case ShaderLanguage::TYPE_BOOL: {
+				glUniform1i(p_uniform, values[0].boolean);
+			} break;
+
+			case ShaderLanguage::TYPE_BVEC2: {
+				glUniform2i(p_uniform, values[0].boolean, values[1].boolean);
+			} break;
+
+			case ShaderLanguage::TYPE_BVEC3: {
+				glUniform3i(p_uniform, values[0].boolean, values[1].boolean, values[2].boolean);
+			} break;
+
+			case ShaderLanguage::TYPE_BVEC4: {
+				glUniform4i(p_uniform, values[0].boolean, values[1].boolean, values[2].boolean, values[3].boolean);
+			} break;
+
+			case ShaderLanguage::TYPE_INT: {
+				glUniform1i(p_uniform, values[0].sint);
+			} break;
+
+			case ShaderLanguage::TYPE_IVEC2: {
+				glUniform2i(p_uniform, values[0].sint, values[1].sint);
+			} break;
+
+			case ShaderLanguage::TYPE_IVEC3: {
+				glUniform3i(p_uniform, values[0].sint, values[1].sint, values[2].sint);
+			} break;
+
+			case ShaderLanguage::TYPE_IVEC4: {
+				glUniform4i(p_uniform, values[0].sint, values[1].sint, values[2].sint, values[3].sint);
+			} break;
+
+			case ShaderLanguage::TYPE_UINT: {
+				glUniform1i(p_uniform, values[0].uint);
+			} break;
+
+			case ShaderLanguage::TYPE_UVEC2: {
+				glUniform2i(p_uniform, values[0].uint, values[1].uint);
+			} break;
+
+			case ShaderLanguage::TYPE_UVEC3: {
+				glUniform3i(p_uniform, values[0].uint, values[1].uint, values[2].uint);
+			} break;
+
+			case ShaderLanguage::TYPE_UVEC4: {
+				glUniform4i(p_uniform, values[0].uint, values[1].uint, values[2].uint, values[3].uint);
+			} break;
+
+			case ShaderLanguage::TYPE_FLOAT: {
+				glUniform1f(p_uniform, values[0].real);
+			} break;
+
+			case ShaderLanguage::TYPE_VEC2: {
+				glUniform2f(p_uniform, values[0].real, values[1].real);
+			} break;
+
+			case ShaderLanguage::TYPE_VEC3: {
+				glUniform3f(p_uniform, values[0].real, values[1].real, values[2].real);
+			} break;
+
+			case ShaderLanguage::TYPE_VEC4: {
+				glUniform4f(p_uniform, values[0].real, values[1].real, values[2].real, values[3].real);
+			} break;
+
+			case ShaderLanguage::TYPE_MAT2: {
+				GLfloat mat[4];
+
+				for (int i = 0; i < 4; i++) {
+					mat[i] = values[i].real;
+				}
+
+				glUniformMatrix2fv(p_uniform, 1, GL_FALSE, mat);
+			} break;
+
+			case ShaderLanguage::TYPE_MAT3: {
+				GLfloat mat[9];
+
+				for (int i = 0; i < 0; i++) {
+					mat[i] = values[i].real;
+				}
+
+				glUniformMatrix3fv(p_uniform, 1, GL_FALSE, mat);
+
+			} break;
+
+			case ShaderLanguage::TYPE_MAT4: {
+				GLfloat mat[16];
+
+				for (int i = 0; i < 0; i++) {
+					mat[i] = values[i].real;
+				}
+
+				glUniformMatrix4fv(p_uniform, 1, GL_FALSE, mat);
+
+			} break;
+
+			case ShaderLanguage::TYPE_SAMPLER2D: {
+
+			} break;
+
+			case ShaderLanguage::TYPE_ISAMPLER2D: {
+
+			} break;
+
+			case ShaderLanguage::TYPE_USAMPLER2D: {
+
+			} break;
+
+			case ShaderLanguage::TYPE_SAMPLERCUBE: {
+
+			} break;
+		}
+	}
+
 	_FORCE_INLINE_ void _set_uniform_variant(GLint p_uniform, const Variant &p_value) {
 
 		if (p_uniform < 0)
@@ -261,50 +429,14 @@ private:
 				};
 
 				glUniformMatrix4fv(p_uniform, 1, false, matrix);
+			} break;
+			case Variant::OBJECT: {
+
 			} break;
 			default: { ERR_FAIL(); } // do nothing
 		}
 	}
 
-	Map<uint32_t, Variant> uniform_defaults;
-	Map<uint32_t, CameraMatrix> uniform_cameras;
-
-protected:
-	_FORCE_INLINE_ int _get_uniform(int p_which) const;
-	_FORCE_INLINE_ void _set_conditional(int p_which, bool p_value);
-
-	void setup(const char **p_conditional_defines,
-			int p_conditional_count,
-			const char **p_uniform_names,
-			int p_uniform_count,
-			const AttributePair *p_attribute_pairs,
-			int p_attribute_count,
-			const TexUnitPair *p_texunit_pairs,
-			int p_texunit_pair_count,
-			const char *p_vertex_code,
-			const char *p_fragment_code,
-			int p_vertex_code_start,
-			int p_fragment_code_start);
-
-	ShaderGLES2();
-
-public:
-	enum {
-		CUSTOM_SHADER_DISABLED = 0
-	};
-
-	GLint get_uniform_location(const String &p_name) const;
-	GLint get_uniform_location(int p_index) const;
-
-	static _FORCE_INLINE_ ShaderGLES2 *get_active() { return active; }
-	bool bind();
-	void unbind();
-	void bind_uniforms();
-
-	inline GLuint get_program() const { return version ? version->id : 0; }
-
-	void clear_caches();
-
 	uint32_t create_custom_shader();
 	void set_custom_shader_code(uint32_t p_code_id,
 			const String &p_vertex,
@@ -331,6 +463,10 @@ public:
 		uniforms_dirty = true;
 	}
 
+	// this void* is actually a RasterizerStorageGLES2::Material, but C++ doesn't
+	// like forward declared nested classes.
+	void use_material(void *p_material, int p_num_predef_textures);
+
 	uint32_t get_version() const { return new_conditional_version.version; }
 
 	void set_uniform_camera(int p_idx, const CameraMatrix &p_mat) {

+ 2 - 2
drivers/gles2/shaders/SCsub

@@ -8,8 +8,8 @@ if 'GLES2_GLSL' in env['BUILDERS']:
     env.GLES2_GLSL('canvas.glsl');
 #    env.GLES2_GLSL('canvas_shadow.glsl');
     env.GLES2_GLSL('scene.glsl');
-#    env.GLES2_GLSL('cubemap_filter.glsl');
-#    env.GLES2_GLSL('cube_to_dp.glsl');
+    env.GLES2_GLSL('cubemap_filter.glsl');
+    env.GLES2_GLSL('cube_to_dp.glsl');
 #    env.GLES2_GLSL('blend_shape.glsl');
 #    env.GLES2_GLSL('screen_space_reflection.glsl');
 #    env.GLES2_GLSL('effect_blur.glsl');

+ 6 - 2
drivers/gles2/shaders/canvas.glsl

@@ -27,7 +27,7 @@ uniform vec4 src_rect;
 
 #endif
 
-uniform bool blit_pass;
+uniform highp float time;
 
 VERTEX_SHADER_GLOBALS
 
@@ -103,7 +103,7 @@ uniform mediump sampler2D normal_texture; // texunit:1
 varying mediump vec2 uv_interp;
 varying mediump vec4 color_interp;
 
-uniform bool blit_pass;
+uniform highp float time;
 
 uniform vec4 final_modulate;
 
@@ -127,6 +127,10 @@ void main() {
 	vec4 color = color_interp;
 
 	color *= texture2D(color_texture, uv_interp);
+
+#ifdef SCREEN_UV_USED
+	vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
+#endif
 {
 
 FRAGMENT_SHADER_CODE

+ 60 - 3
drivers/gles2/shaders/copy.glsl

@@ -9,11 +9,20 @@ precision mediump int;
 #endif
 
 attribute highp vec4 vertex_attrib; // attrib:0
+
+#if defined(USE_CUBEMAP) || defined(USE_PANORAMA)
+attribute vec3 cube_in; // attrib:4
+#else
 attribute vec2 uv_in; // attrib:4
+#endif
+
 attribute vec2 uv2_in; // attrib:5
 
+#if defined(USE_CUBEMAP) || defined(USE_PANORAMA)
+varying vec3 cube_interp;
+#else
 varying vec2 uv_interp;
-
+#endif
 varying vec2 uv2_interp;
 
 #ifdef USE_COPY_SECTION
@@ -22,7 +31,12 @@ uniform vec4 copy_section;
 
 void main() {
 
+#if defined(USE_CUBEMAP) || defined(USE_PANORAMA)
+	cube_interp = cube_in;
+#else
 	uv_interp = uv_in;
+#endif
+
 	uv2_interp = uv2_in;
 	gl_Position = vertex_attrib;
 
@@ -34,6 +48,8 @@ void main() {
 
 [fragment]
 
+#define M_PI 3.14159265359
+
 #ifdef USE_GLES_OVER_GL
 #define mediump
 #define highp
@@ -42,21 +58,59 @@ precision mediump float;
 precision mediump int;
 #endif
 
-
+#if defined(USE_CUBEMAP) || defined(USE_PANORAMA)
+varying vec3 cube_interp;
+#else
 varying vec2 uv_interp;
+#endif
+
+#ifdef USE_CUBEMAP
+uniform samplerCube source_cube; // texunit:0
+#else
 uniform sampler2D source; // texunit:0
+#endif
 
 varying vec2 uv2_interp;
 
+#ifdef USE_MULTIPLIER
+uniform float multiplier;
+#endif
+
 #ifdef USE_CUSTOM_ALPHA
 uniform float custom_alpha;
 #endif
 
+#if defined(USE_PANORAMA) || defined(USE_ASYM_PANO)
+
+vec4 texturePanorama(sampler2D pano, vec3 normal) {
+
+	vec2 st = vec2(
+	        atan(normal.x, normal.z),
+	        acos(normal.y)
+	);
+
+	if(st.x < 0.0)
+		st.x += M_PI*2.0;
+
+	st/=vec2(M_PI*2.0,M_PI);
+
+	return texture2D(pano,st);
+
+}
+
+#endif
 
 void main() {
 
-	//vec4 color = color_interp;
+#ifdef USE_PANORAMA
+
+	vec4 color = texturePanorama(source, normalize(cube_interp));
+
+#elif defined(USE_CUBEMAP)
+	vec4 color = textureCube(source_cube, normalize(cube_interp));
+#else
 	vec4 color = texture2D( source,  uv_interp );
+#endif
 
 
 #ifdef USE_NO_ALPHA
@@ -67,6 +121,9 @@ void main() {
 	color.a=custom_alpha;
 #endif
 
+#ifdef USE_MULTIPLIER
+	color.rgb *= multiplier;
+#endif
 
 	gl_FragColor = color;
 }

+ 19 - 5
drivers/gles2/shaders/cube_to_dp.glsl

@@ -1,10 +1,17 @@
 [vertex]
 
+#ifdef USE_GLES_OVER_GL
+#define mediump
+#define highp
+#else
+precision mediump float;
+precision mediump int;
+#endif
 
-layout(location=0) in highp vec4 vertex_attrib;
-layout(location=4) in vec2 uv_in;
+attribute highp vec4 vertex_attrib; // attrib:0
+attribute vec2 uv_in; // attrib:4
 
-out vec2 uv_interp;
+varying vec2 uv_interp;
 
 void main() {
 
@@ -14,9 +21,16 @@ void main() {
 
 [fragment]
 
+#ifdef USE_GLES_OVER_GL
+#define mediump
+#define highp
+#else
+precision mediump float;
+precision mediump int;
+#endif
 
 uniform highp samplerCube source_cube; //texunit:0
-in vec2 uv_interp;
+varying vec2 uv_interp;
 
 uniform bool z_flip;
 uniform highp float z_far;
@@ -49,7 +63,7 @@ void main() {
 	}
 
 	//normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 ));
-	float depth = texture(source_cube,normal).r;
+	float depth = textureCube(source_cube,normal).r;
 
 	// absolute values for direction cosines, bigger value equals closer to basis axis
 	vec3 unorm = abs(normal);

+ 107 - 214
drivers/gles2/shaders/cubemap_filter.glsl

@@ -1,11 +1,17 @@
 [vertex]
 
+#ifdef USE_GLES_OVER_GL
+#define mediump
+#define highp
+#else
+precision mediump float;
+precision mediump int;
+#endif
 
-layout(location=0) in highp vec2 vertex;
-
-layout(location=4) in highp vec2 uv;
+attribute highp vec2 vertex; // attrib:0
+attribute highp vec2 uv; // attrib:4
 
-out highp vec2 uv_interp;
+varying highp vec2 uv_interp;
 
 void main() {
 
@@ -15,174 +21,47 @@ void main() {
 
 [fragment]
 
+#extension GL_ARB_shader_texture_lod : require
 
-precision highp float;
-precision highp int;
+#ifdef USE_GLES_OVER_GL
+#define mediump
+#define highp
+#else
+precision mediump float;
+precision mediump int;
+#endif
 
 #ifdef USE_SOURCE_PANORAMA
 uniform sampler2D source_panorama; //texunit:0
-#endif
-
-#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
-uniform sampler2DArray source_dual_paraboloid_array; //texunit:0
-uniform int source_array_index;
-#endif
-
-#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA)
+#else
 uniform samplerCube source_cube; //texunit:0
 #endif
 
 uniform int face_id;
 uniform float roughness;
-in highp vec2 uv_interp;
-
-
-layout(location = 0) out vec4 frag_color;
+varying highp vec2 uv_interp;
 
+uniform sampler2D radical_inverse_vdc_cache; // texunit:1
 
 #define M_PI 3.14159265359
 
-
-vec3 texelCoordToVec(vec2 uv, int faceID)
-{
-    mat3 faceUvVectors[6];
-/*
-    // -x
-    faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0);  // u -> +z
-    faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
-
-    // +x
-    faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
-    faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0);  // +x face
-
-    // -y
-    faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
-    faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
-    faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
-
-    // +y
-    faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
-    faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0);  // v -> +z
-    faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0);  // +y face
-
-    // -z
-    faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
-    faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
-
-    // +z
-    faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
-    faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0);  // +z face
-*/
-
-    // -x
-    faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0);  // u -> +z
-    faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face
-
-    // +x
-    faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z
-    faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0);  // +x face
-
-    // -y
-    faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
-    faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z
-    faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face
-
-    // +y
-    faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
-    faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0);  // v -> +z
-    faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0);  // +y face
-
-    // -z
-    faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
-    faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face
-
-    // +z
-    faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
-    faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
-    faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0);  // +z face
-
-    // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
-    vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
-    return normalize(result);
-}
-
-vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
-{
-	float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph]
-
-	// Compute distribution direction
-	float Phi = 2.0 * M_PI * Xi.x;
-	float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
-	float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
-
-	// Convert to spherical direction
-	vec3 H;
-	H.x = SinTheta * cos(Phi);
-	H.y = SinTheta * sin(Phi);
-	H.z = CosTheta;
-
-	vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
-	vec3 TangentX = normalize(cross(UpVector, N));
-	vec3 TangentY = cross(N, TangentX);
-
-	// Tangent to world space
-	return TangentX * H.x + TangentY * H.y + N * H.z;
-}
-
-// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
-float GGX(float NdotV, float a)
-{
-	float k = a / 2.0;
-	return NdotV / (NdotV * (1.0 - k) + k);
-}
-
-// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
-float G_Smith(float a, float nDotV, float nDotL)
-{
-	return GGX(nDotL, a * a) * GGX(nDotV, a * a);
-}
-
-float radicalInverse_VdC(uint bits) {
-    bits = (bits << 16u) | (bits >> 16u);
-    bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
-    bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
-    bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
-    bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
-    return float(bits) * 2.3283064365386963e-10; // / 0x100000000
-}
-
-vec2 Hammersley(uint i, uint N) {
-     return vec2(float(i)/float(N), radicalInverse_VdC(i));
-}
-
-
-
 #ifdef LOW_QUALITY
 
-#define SAMPLE_COUNT 64u
+#define SAMPLE_COUNT 64
 
 #else
 
-#define SAMPLE_COUNT 512u
+#define SAMPLE_COUNT 512
 
 #endif
 
-uniform bool z_flip;
-
 #ifdef USE_SOURCE_PANORAMA
 
-vec4 texturePanorama(vec3 normal,sampler2D pano ) {
+vec4 texturePanorama(sampler2D pano, vec3 normal) {
 
 	vec2 st = vec2(
-		atan(normal.x, normal.z),
-		acos(normal.y)
+	        atan(normal.x, normal.z),
+	        acos(normal.y)
 	);
 
 	if(st.x < 0.0)
@@ -190,105 +69,119 @@ vec4 texturePanorama(vec3 normal,sampler2D pano ) {
 
 	st/=vec2(M_PI*2.0,M_PI);
 
-	return textureLod(pano,st,0.0);
+	return texture2DLod(pano,st,0.0);
 
 }
 
 #endif
 
-#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
-
-
-vec4 textureDualParaboloidArray(vec3 normal) {
-
-	vec3 norm = normalize(normal);
-	norm.xy/=1.0+abs(norm.z);
-	norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25);
-	if (norm.z<0.0) {
-		norm.y=0.5-norm.y+0.5;
-	}
-	return textureLod(source_dual_paraboloid_array, vec3(norm.xy, float(source_array_index) ), 0.0);
-
+vec3 texelCoordToVec(vec2 uv, int faceID) {
+	mat3 faceUvVectors[6];
+
+	// -x
+	faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0);  // u -> +z
+	faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
+	faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face
+
+	// +x
+	faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z
+	faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
+	faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0);  // +x face
+
+	// -y
+	faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
+	faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z
+	faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face
+
+	// +y
+	faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
+	faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0);  // v -> +z
+	faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0);  // +y face
+
+	// -z
+	faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
+	faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
+	faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face
+
+	// +z
+	faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0);  // u -> +x
+	faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
+	faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0);  // +z face
+
+	// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
+	vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
+	return normalize(result);
 }
 
-#endif
-
-void main() {
-
-#ifdef USE_DUAL_PARABOLOID
-
-	vec3 N = vec3( uv_interp * 2.0 - 1.0, 0.0 );
-	N.z = 0.5 - 0.5*((N.x * N.x) + (N.y * N.y));
-	N = normalize(N);
-
-	if (z_flip) {
-		N.y=-N.y; //y is flipped to improve blending between both sides
-		N.z=-N.z;
-	}
-
+vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) {
+	float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph]
 
-#else
-	vec2 uv         = (uv_interp * 2.0) - 1.0;
-	vec3 N          = texelCoordToVec(uv, face_id);
-#endif
-	//vec4 color = color_interp;
+	// Compute distribution direction
+	float Phi = 2.0 * M_PI * Xi.x;
+	float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
+	float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
 
-#ifdef USE_DIRECT_WRITE
+	// Convert to spherical direction
+	vec3 H;
+	H.x = SinTheta * cos(Phi);
+	H.y = SinTheta * sin(Phi);
+	H.z = CosTheta;
 
-#ifdef USE_SOURCE_PANORAMA
+	vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+	vec3 TangentX = normalize(cross(UpVector, N));
+	vec3 TangentY = cross(N, TangentX);
 
-	frag_color=vec4(texturePanorama(N,source_panorama).rgb,1.0);
-#endif
+	// Tangent to world space
+	return TangentX * H.x + TangentY * H.y + N * H.z;
+}
 
-#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
+float radical_inverse_VdC(int i) {
+	return texture2D(radical_inverse_vdc_cache, vec2(float(i) / 512.0, 0.0)).x;
+}
 
-	frag_color=vec4(textureDualParaboloidArray(N).rgb,1.0);
-#endif
+vec2 Hammersley(int i, int N) {
+	return vec2(float(i) / float(N), radical_inverse_VdC(i));
+}
 
-#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA)
+uniform bool z_flip;
 
-	N.y=-N.y;
-	frag_color=vec4(texture(N,source_cube).rgb,1.0);
-#endif
+void main() {
 
+	vec3 color = vec3(0.0);
 
+	vec2 uv = (uv_interp * 2.0) - 1.0;
+	vec3 N = texelCoordToVec(uv, face_id);
 
+	vec4 sum = vec4(0.0);
 
-#else
+	for (int sample_num = 0; sample_num < SAMPLE_COUNT; sample_num++) {
 
-	vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
+		vec2 xi = Hammersley(sample_num, SAMPLE_COUNT);
 
-	for(uint sampleNum = 0u; sampleNum < SAMPLE_COUNT; sampleNum++) {
-		vec2 xi = Hammersley(sampleNum, SAMPLE_COUNT);
+		vec3 H = ImportanceSampleGGX(xi, roughness, N);
+		vec3 V = N;
+		vec3 L = normalize(2.0 * dot(V, H) * H - V);
 
-		vec3 H  = ImportanceSampleGGX( xi, roughness, N );
-		vec3 V  = N;
-		vec3 L  = normalize(2.0 * dot( V, H ) * H - V);
+		float NdotL = clamp(dot(N, L), 0.0, 1.0);
 
-		float ndotl = clamp(dot(N, L),0.0,1.0);
+		if (NdotL > 0.0) {
 
-		if (ndotl>0.0) {
 #ifdef USE_SOURCE_PANORAMA
-			sum.rgb += texturePanorama(H,source_panorama).rgb *ndotl;
+			sum.rgb += texturePanorama(source_panorama, H).rgb * NdotL;
+#else
+			H.y = -H.y;
+			sum.rgb += textureCubeLod(source_cube, H, 0.0).rgb * NdotL;
 #endif
 
-#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
-
-			sum.rgb += textureDualParaboloidArray(H).rgb *ndotl;
-#endif
+			sum.a += NdotL;
 
-#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA)
-			H.y=-H.y;
-			sum.rgb += textureLod(source_cube, H, 0.0).rgb *ndotl;
-#endif
-			sum.a += ndotl;
 		}
+
 	}
-	sum /= sum.a;
 
-	frag_color = vec4(sum.rgb, 1.0);
+	sum /= sum.a;
 
-#endif
+	gl_FragColor = vec4(sum.rgb, 1.0);
 
 }
 

+ 613 - 1842
drivers/gles2/shaders/scene.glsl

@@ -1,2113 +1,884 @@
 [vertex]
 
-#define M_PI 3.14159265359
+#ifdef USE_GLES_OVER_GL
+#define mediump
+#define highp
+#else
+precision mediump float;
+precision mediump int;
+#endif
 
-/*
-from VisualServer:
+#include "stdlib.glsl"
 
-ARRAY_VERTEX=0,
-ARRAY_NORMAL=1,
-ARRAY_TANGENT=2,
-ARRAY_COLOR=3,
-ARRAY_TEX_UV=4,
-ARRAY_TEX_UV2=5,
-ARRAY_BONES=6,
-ARRAY_WEIGHTS=7,
-ARRAY_INDEX=8,
-*/
 
-//hack to use uv if no uv present so it works with lightmap
 
+//
+// attributes
+//
 
-/* INPUT ATTRIBS */
+attribute highp vec4 vertex_attrib; // attrib:0
+attribute vec3 normal_attrib; // attrib:1
 
-layout(location=0) in highp vec4 vertex_attrib;
-layout(location=1) in vec3 normal_attrib;
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-layout(location=2) in vec4 tangent_attrib;
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+attribute vec4 tangent_attrib; // attrib:2
 #endif
 
-#if defined(ENABLE_COLOR_INTERP)
-layout(location=3) in vec4 color_attrib;
+#ifdef ENABLE_COLOR_INTERP
+attribute vec4 color_attrib; // attrib:3
 #endif
 
-#if defined(ENABLE_UV_INTERP)
-layout(location=4) in vec2 uv_attrib;
+#ifdef ENABLE_UV_INTERP
+attribute vec2 uv_attrib; // attrib:4
 #endif
 
-#if defined(ENABLE_UV2_INTERP)
-layout(location=5) in vec2 uv2_attrib;
+#ifdef ENABLE_UV2_INTERP
+attribute vec2 uv2_attrib; // attrib:5
 #endif
 
-uniform float normal_mult;
-
 #ifdef USE_SKELETON
-layout(location=6) in ivec4 bone_indices; // attrib:6
-layout(location=7) in vec4 bone_weights; // attrib:7
-#endif
-
-#ifdef USE_INSTANCING
-
-layout(location=8) in highp vec4 instance_xform0;
-layout(location=9) in highp vec4 instance_xform1;
-layout(location=10) in highp vec4 instance_xform2;
-layout(location=11) in lowp vec4 instance_color;
-
-#if defined(ENABLE_INSTANCE_CUSTOM)
-layout(location=12) in highp vec4 instance_custom_data;
-#endif
-
-#endif
-
-layout(std140) uniform SceneData { //ubo:0
 
-	highp mat4 projection_matrix;
-	highp mat4 inv_projection_matrix;
-	highp mat4 camera_inverse_matrix;
-	highp mat4 camera_matrix;
+#ifdef USE_SKELETON_SOFTWARE
 
-	mediump vec4 ambient_light_color;
-	mediump vec4 bg_color;
+attribute highp vec4 bone_transform_row_0; // attrib:9
+attribute highp vec4 bone_transform_row_1; // attrib:10
+attribute highp vec4 bone_transform_row_2; // attrib:11
 
-	mediump vec4 fog_color_enabled;
-	mediump vec4 fog_sun_color_amount;
-
-	mediump float ambient_energy;
-	mediump float bg_energy;
-
-	mediump float z_offset;
-	mediump float z_slope_scale;
-	highp float shadow_dual_paraboloid_render_zfar;
-	highp float shadow_dual_paraboloid_render_side;
-
-	highp vec2 viewport_size;
-	highp vec2 screen_pixel_size;
-	highp vec2 shadow_atlas_pixel_size;
-	highp vec2 directional_shadow_pixel_size;
-
-	highp float time;
-	highp float z_far;
-	mediump float reflection_multiplier;
-	mediump float subsurface_scatter_width;
-	mediump float ambient_occlusion_affect_light;
-
-	bool fog_depth_enabled;
-	highp float fog_depth_begin;
-	highp float fog_depth_curve;
-	bool fog_transmit_enabled;
-	highp float fog_transmit_curve;
-	bool fog_height_enabled;
-	highp float fog_height_min;
-	highp float fog_height_max;
-	highp float fog_height_curve;
-
-};
-
-uniform highp mat4 world_transform;
-
-
-#ifdef USE_LIGHT_DIRECTIONAL
+#else
 
-layout(std140) uniform DirectionalLightData { //ubo:3
+attribute vec4 bone_ids; // attrib:6
+attribute highp vec4 bone_weights; // attrib:7
 
-	highp vec4 light_pos_inv_radius;
-	mediump vec4 light_direction_attenuation;
-	mediump vec4 light_color_energy;
-	mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled,
-	mediump vec4 light_clamp;
-	mediump vec4 shadow_color_contact;
-	highp mat4 shadow_matrix1;
-	highp mat4 shadow_matrix2;
-	highp mat4 shadow_matrix3;
-	highp mat4 shadow_matrix4;
-	mediump vec4 shadow_split_offsets;
-};
+uniform highp sampler2D bone_transforms; // texunit:4
+uniform ivec2 skeleton_texture_size;
 
 #endif
 
-#ifdef USE_VERTEX_LIGHTING
-//omni and spot
-
-struct LightData {
-
-	highp vec4 light_pos_inv_radius;
-	mediump vec4 light_direction_attenuation;
-	mediump vec4 light_color_energy;
-	mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled,
-	mediump vec4 light_clamp;
-	mediump vec4 shadow_color_contact;
-	highp mat4 shadow_matrix;
-
-};
-
-
-layout(std140) uniform OmniLightData { //ubo:4
-
-	LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
-};
-
-layout(std140) uniform SpotLightData { //ubo:5
-
-	LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
-};
-
-#ifdef USE_FORWARD_LIGHTING
-
-
-uniform int omni_light_indices[MAX_FORWARD_LIGHTS];
-uniform int omni_light_count;
-
-uniform int spot_light_indices[MAX_FORWARD_LIGHTS];
-uniform int spot_light_count;
-
 #endif
 
-out vec4 diffuse_light_interp;
-out vec4 specular_light_interp;
-
-void light_compute(vec3 N, vec3 L,vec3 V, vec3 light_color, float roughness, inout vec3 diffuse, inout vec3 specular) {
-
-	float dotNL = max(dot(N,L), 0.0 );
-	diffuse += dotNL * light_color / M_PI;
-
-	if (roughness > 0.0) {
-
-		vec3 H = normalize(V + L);
-		float dotNH = max(dot(N,H), 0.0 );
-		float intensity = pow( dotNH, (1.0-roughness) * 256.0);
-		specular += light_color * intensity;
-
-	}
-}
-
-void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal, float roughness,inout vec3 diffuse, inout vec3 specular) {
-
-	vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz-vertex;
-	float light_length = length( light_rel_vec );
-	float normalized_distance = light_length*omni_lights[idx].light_pos_inv_radius.w;
-	vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w ));
+#ifdef USE_INSTANCING
 
-	light_compute(normal,normalize(light_rel_vec),eye_vec,omni_lights[idx].light_color_energy.rgb * light_attenuation,roughness,diffuse,specular);
+attribute highp vec4 instance_xform_row_0; // attrib:12
+attribute highp vec4 instance_xform_row_1; // attrib:13
+attribute highp vec4 instance_xform_row_2; // attrib:14
 
-}
+attribute highp vec4 instance_color; // attrib:15
+attribute highp vec4 instance_custom_data; // attrib:8
 
-void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness, inout vec3 diffuse, inout vec3 specular) {
+#endif
 
-	vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz-vertex;
-	float light_length = length( light_rel_vec );
-	float normalized_distance = light_length*spot_lights[idx].light_pos_inv_radius.w;
-	vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.001), spot_lights[idx].light_direction_attenuation.w ));
-	vec3 spot_dir = spot_lights[idx].light_direction_attenuation.xyz;
-	float spot_cutoff=spot_lights[idx].light_params.y;
-	float scos = max(dot(-normalize(light_rel_vec), spot_dir),spot_cutoff);
-	float spot_rim = (1.0 - scos) / (1.0 - spot_cutoff);
-	light_attenuation *= 1.0 - pow( max(spot_rim,0.001), spot_lights[idx].light_params.x);
 
 
-	light_compute(normal,normalize(light_rel_vec),eye_vec,spot_lights[idx].light_color_energy.rgb*light_attenuation,roughness,diffuse,specular);
-}
+//
+// uniforms
+//
 
+uniform mat4 camera_matrix;
+uniform mat4 camera_inverse_matrix;
+uniform mat4 projection_matrix;
+uniform mat4 projection_inverse_matrix;
 
-#endif
+uniform mat4 world_transform;
 
-/* Varyings */
+uniform highp float time;
 
-out highp vec3 vertex_interp;
-out vec3 normal_interp;
+uniform float normal_mult;
 
-#if defined(ENABLE_COLOR_INTERP)
-out vec4 color_interp;
+#ifdef RENDER_DEPTH
+uniform float light_bias;
+uniform float light_normal_bias;
 #endif
 
-#if defined(ENABLE_UV_INTERP)
-out vec2 uv_interp;
-#endif
 
-#if defined(ENABLE_UV2_INTERP)
-out vec2 uv2_interp;
-#endif
+//
+// varyings
+//
 
+varying highp vec3 vertex_interp;
+varying vec3 normal_interp;
 
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-out vec3 tangent_interp;
-out vec3 binormal_interp;
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+varying vec3 tangent_interp;
+varying vec3 binormal_interp;
 #endif
 
-
-
-
-
-#if defined(USE_MATERIAL)
-
-layout(std140) uniform UniformData { //ubo:1
-
-MATERIAL_UNIFORMS
-
-};
-
+#ifdef ENABLE_COLOR_INTERP
+varying vec4 color_interp;
 #endif
 
-VERTEX_SHADER_GLOBALS
-
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
-
-out highp float dp_clip;
-
+#ifdef ENABLE_UV_INTERP
+varying vec2 uv_interp;
 #endif
 
-#define SKELETON_TEXTURE_WIDTH 256
-
-#ifdef USE_SKELETON
-uniform highp sampler2D skeleton_texture; //texunit:-1
+#ifdef ENABLE_UV2_INTERP
+varying vec2 uv2_interp;
 #endif
 
-out highp vec4 position_interp;
 
-// FIXME: This triggers a Mesa bug that breaks rendering, so disabled for now.
-// See GH-13450 and https://bugs.freedesktop.org/show_bug.cgi?id=100316
-//invariant gl_Position;
+VERTEX_SHADER_GLOBALS
 
 void main() {
 
-	highp vec4 vertex = vertex_attrib; // vec4(vertex_attrib.xyz * data_attrib.x,1.0);
+	highp vec4 vertex = vertex_attrib;
 
 	mat4 world_matrix = world_transform;
 
-
 #ifdef USE_INSTANCING
-
 	{
-		highp mat4 m=mat4(instance_xform0,instance_xform1,instance_xform2,vec4(0.0,0.0,0.0,1.0));
+		highp mat4 m = mat4(instance_xform_row_0,
+		                    instance_xform_row_1,
+		                    instance_xform_row_2,
+		                    vec4(0.0, 0.0, 0.0, 1.0));
 		world_matrix = world_matrix * transpose(m);
 	}
 #endif
 
 	vec3 normal = normal_attrib * normal_mult;
 
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
 	vec3 tangent = tangent_attrib.xyz;
-	tangent*=normal_mult;
+	tangent *= normal_mult;
 	float binormalf = tangent_attrib.a;
+	vec3 binormal = normalize(cross(normal, tangent) * binormalf);
 #endif
 
-#if defined(ENABLE_COLOR_INTERP)
+#ifdef ENABLE_COLOR_INTERP
 	color_interp = color_attrib;
-#if defined(USE_INSTANCING)
+#ifdef USE_INSTANCING
 	color_interp *= instance_color;
 #endif
-
 #endif
 
-#ifdef USE_SKELETON
-	{
-		//skeleton transform
-		ivec2 tex_ofs = ivec2( bone_indices.x%256, (bone_indices.x/256)*3 );
-		highp mat3x4 m = mat3x4(
-			texelFetch(skeleton_texture,tex_ofs,0),
-			texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0),
-			texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0)
-		) * bone_weights.x;
+#ifdef ENABLE_UV_INTERP
+	uv_interp = uv_attrib;
+#endif
 
-		tex_ofs = ivec2( bone_indices.y%256, (bone_indices.y/256)*3 );
+#ifdef ENABLE_UV2_INTERP
+	uv2_interp = uv2_attrib;
+#endif
 
-		m+= mat3x4(
-					texelFetch(skeleton_texture,tex_ofs,0),
-					texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0),
-					texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0)
-				) * bone_weights.y;
+#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
+	vertex = world_matrix * vertex;
+	normal = normalize((world_matrix * vec4(normal, 0.0)).xyz);
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
 
-		tex_ofs = ivec2( bone_indices.z%256, (bone_indices.z/256)*3 );
+	tangent = normalize((world_matrix * vec4(tangent, 0.0)),xyz);
+	binormal = normalize((world_matrix * vec4(binormal, 0.0)).xyz);
+#endif
+#endif
 
-		m+= mat3x4(
-					texelFetch(skeleton_texture,tex_ofs,0),
-					texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0),
-					texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0)
-				) * bone_weights.z;
+#ifdef USE_SKELETON
 
+	highp mat4 bone_transform = mat4(1.0);
 
-		tex_ofs = ivec2( bone_indices.w%256, (bone_indices.w/256)*3 );
+#ifdef USE_SKELETON_SOFTWARE
+	// passing the transform as attributes
 
-		m+= mat3x4(
-					texelFetch(skeleton_texture,tex_ofs,0),
-					texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0),
-					texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0)
-				) * bone_weights.w;
+	bone_transform[0] = vec4(bone_transform_row_0.x, bone_transform_row_1.x, bone_transform_row_2.x, 0.0);
+	bone_transform[1] = vec4(bone_transform_row_0.y, bone_transform_row_1.y, bone_transform_row_2.y, 0.0);
+	bone_transform[2] = vec4(bone_transform_row_0.z, bone_transform_row_1.z, bone_transform_row_2.z, 0.0);
+	bone_transform[3] = vec4(bone_transform_row_0.w, bone_transform_row_1.w, bone_transform_row_2.w, 1.0);
 
+#else
+	// look up transform from the "pose texture"
+	{
+		
+		for (int i = 0; i < 4; i++) {
+			ivec2 tex_ofs = ivec2(int(bone_ids[i]) * 3, 0);
 
-		vertex.xyz = vertex * m;
+			highp mat4 b = mat4(texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(0, 0)),
+			              texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(1, 0)),
+			              texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(2, 0)),
+			              vec4(0.0, 0.0, 0.0, 1.0));
 
-		normal = vec4(normal,0.0) * m;
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-		tangent.xyz = vec4(tangent.xyz,0.0) * m;
-#endif
+			bone_transform += transpose(b) * bone_weights[i];
+		}
 	}
-#endif
-
 
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-
-	vec3 binormal = normalize( cross(normal,tangent) * binormalf );
 #endif
 
-#if defined(ENABLE_UV_INTERP)
-	uv_interp = uv_attrib;
+	world_matrix = bone_transform * world_matrix;
 #endif
 
-#if defined(ENABLE_UV2_INTERP)
-	uv2_interp = uv2_attrib;
-#endif
 
-#if defined(USE_INSTANCING) && defined(ENABLE_INSTANCE_CUSTOM)
+#ifdef USE_INSTANCING
 	vec4 instance_custom = instance_custom_data;
 #else
 	vec4 instance_custom = vec4(0.0);
-#endif
 
-	highp mat4 modelview = camera_inverse_matrix * world_matrix;
-	highp mat4 local_projection = projection_matrix;
-
-//using world coordinates
-#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
-
-	vertex = world_matrix * vertex;
-	normal = normalize((world_matrix * vec4(normal,0.0)).xyz);
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-
-	tangent = normalize((world_matrix * vec4(tangent,0.0)).xyz);
-	binormal = normalize((world_matrix * vec4(binormal,0.0)).xyz);
-#endif
 #endif
 
-	float roughness=0.0;
 
-//defines that make writing custom shaders easier
-#define projection_matrix local_projection
+	mat4 modelview = camera_matrix * world_matrix;
+
 #define world_transform world_matrix
+
 {
 
 VERTEX_SHADER_CODE
 
 }
 
+	vec4 outvec = vertex;
 
-
-//using local coordinates (default)
+	// use local coordinates
 #if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED)
-
 	vertex = modelview * vertex;
-	normal = normalize((modelview * vec4(normal,0.0)).xyz);
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+	normal = normalize((modelview * vec4(normal, 0.0)).xyz);
 
-	tangent = normalize((modelview * vec4(tangent,0.0)).xyz);
-	binormal = normalize((modelview * vec4(binormal,0.0)).xyz);
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+	tangent = normalize((modelview * vec4(tangent, 0.0)).xyz);
+	binormal = normalize((modelview * vec4(binormal, 0.0)).xyz);
 #endif
 #endif
 
-//using world coordinates
 #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
-
-	vertex = camera_inverse_matrix * vertex;
-	normal = normalize((camera_inverse_matrix * vec4(normal,0.0)).xyz);
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-
-	tangent = normalize((camera_inverse_matrix * vec4(tangent,0.0)).xyz);
-	binormal = normalize((camera_inverse_matrix * vec4(binormal,0.0)).xyz);
+	vertex = camera_matrix * vertex;
+	normal = normalize((camera_matrix * vec4(normal, 0.0)).xyz);
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+	tangent = normalize((camera_matrix * vec4(tangent, 0.0)).xyz);
+	binormal = normalize((camera_matrix * vec4(binormal, 0.0)).xyz);
 #endif
 #endif
 
 	vertex_interp = vertex.xyz;
 	normal_interp = normal;
 
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
 	tangent_interp = tangent;
 	binormal_interp = binormal;
 #endif
 
-
 #ifdef RENDER_DEPTH
 
+	float z_ofs = light_bias;
+	z_ofs += (1.0 - abs(normal_interp.z)) * light_normal_bias;
+	
+	vertex_interp.z -= z_ofs;
 
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
+#endif
 
-	vertex_interp.z*= shadow_dual_paraboloid_render_side;
-	normal_interp.z*= shadow_dual_paraboloid_render_side;
+	gl_Position = projection_matrix * vec4(vertex_interp, 1.0);
 
-	dp_clip=vertex_interp.z; //this attempts to avoid noise caused by objects sent to the other parabolloid side due to bias
+}
 
-	//for dual paraboloid shadow mapping, this is the fastest but least correct way, as it curves straight edges
+[fragment]
+#extension GL_ARB_shader_texture_lod : require
 
-	highp vec3 vtx = vertex_interp+normalize(vertex_interp)*z_offset;
-	highp float distance = length(vtx);
-	vtx = normalize(vtx);
-	vtx.xy/=1.0-vtx.z;
-	vtx.z=(distance/shadow_dual_paraboloid_render_zfar);
-	vtx.z=vtx.z * 2.0 - 1.0;
+#ifdef USE_GLES_OVER_GL
+#define mediump
+#define highp
+#else
+precision mediump float;
+precision mediump int;
+#endif
 
-	vertex.xyz=vtx;
-	vertex.w=1.0;
+#include "stdlib.glsl"
 
+#define M_PI 3.14159265359
 
-#else
+//
+// uniforms
+//
 
-	float z_ofs = z_offset;
-	z_ofs += (1.0-abs(normal_interp.z))*z_slope_scale;
-	vertex_interp.z-=z_ofs;
+uniform mat4 camera_matrix;
+uniform mat4 camera_inverse_matrix;
+uniform mat4 projection_matrix;
+uniform mat4 projection_inverse_matrix;
 
-#endif //RENDER_DEPTH_DUAL_PARABOLOID
+uniform mat4 world_transform;
 
-#endif //RENDER_DEPTH
+uniform highp float time;
 
-	gl_Position = projection_matrix * vec4(vertex_interp,1.0);
 
-	position_interp=gl_Position;
+#ifdef SCREEN_UV_USED
+uniform vec2 screen_pixel_size;
+#endif
 
-#ifdef USE_VERTEX_LIGHTING
+uniform highp sampler2D depth_buffer; //texunit:1
 
-	diffuse_light_interp=vec4(0.0);
-	specular_light_interp=vec4(0.0);
+#if defined(SCREEN_TEXTURE_USED)
+uniform highp sampler2D screen_texture; //texunit:2
+#endif
 
-#ifdef USE_FORWARD_LIGHTING
+#ifdef USE_RADIANCE_MAP
 
-	for(int i=0;i<omni_light_count;i++) {
-		light_process_omni(omni_light_indices[i],vertex_interp,-normalize( vertex_interp ),normal_interp,roughness,diffuse_light_interp.rgb,specular_light_interp.rgb);
-	}
+#define RADIANCE_MAX_LOD 6.0
+
+uniform samplerCube radiance_map; // texunit:0
+
+uniform mat4 radiance_inverse_xform;
 
-	for(int i=0;i<spot_light_count;i++) {
-		light_process_spot(spot_light_indices[i],vertex_interp,-normalize( vertex_interp ),normal_interp,roughness,diffuse_light_interp.rgb,specular_light_interp.rgb);
-	}
 #endif
 
-#ifdef USE_LIGHT_DIRECTIONAL
+uniform float bg_energy;
 
-	vec3 directional_diffuse = vec3(0.0);
-	vec3 directional_specular = vec3(0.0);
-	light_compute(normal_interp,-light_direction_attenuation.xyz,-normalize( vertex_interp ),light_color_energy.rgb,roughness,directional_diffuse,directional_specular);
+uniform float ambient_sky_contribution;
+uniform vec4 ambient_color;
+uniform float ambient_energy;
 
-	float diff_avg = dot(diffuse_light_interp.rgb,vec3(0.33333));
-	float diff_dir_avg = dot(directional_diffuse,vec3(0.33333));
-	if (diff_avg>0.0) {
-		diffuse_light_interp.a=diff_dir_avg/(diff_avg+diff_dir_avg);
-	} else {
-		diffuse_light_interp.a=1.0;
-	}
+#ifdef LIGHT_PASS
 
-	diffuse_light_interp.rgb+=directional_diffuse;
+#define LIGHT_TYPE_DIRECTIONAL 0
+#define LIGHT_TYPE_OMNI 1
+#define LIGHT_TYPE_SPOT 2
 
-	float spec_avg = dot(specular_light_interp.rgb,vec3(0.33333));
-	float spec_dir_avg = dot(directional_specular,vec3(0.33333));
-	if (spec_avg>0.0) {
-		specular_light_interp.a=spec_dir_avg/(spec_avg+spec_dir_avg);
-	} else {
-		specular_light_interp.a=1.0;
-	}
+// general for all lights
+uniform int light_type;
 
-	specular_light_interp.rgb+=directional_specular;
+uniform float light_energy;
+uniform vec4 light_color;
+uniform float light_specular;
 
-#endif //USE_LIGHT_DIRECTIONAL
+// directional
+uniform vec3 light_direction;
 
+// omni
+uniform vec3 light_position;
 
-#endif // USE_VERTEX_LIGHTING
+uniform float light_range;
+uniform vec4 light_attenuation;
 
-}
+// spot
+uniform float light_spot_attenuation;
+uniform float light_spot_range;
+uniform float light_spot_angle;
 
 
-[fragment]
+// shadows
+uniform highp sampler2D light_shadow_atlas; //texunit:3
+uniform float light_has_shadow;
 
-/* texture unit usage, N is max_texture_unity-N
+uniform mat4 light_shadow_matrix;
+uniform vec4 light_clamp;
 
-1-skeleton
-2-radiance
-3-reflection_atlas
-4-directional_shadow
-5-shadow_atlas
-6-decal_atlas
-7-screen
-8-depth
-9-probe1
-10-probe2
+// directional shadow
 
-*/
+uniform highp sampler2D light_directional_shadow; // texunit:3
+uniform vec4 light_split_offsets;
 
-uniform highp mat4 world_transform;
+uniform mat4 light_shadow_matrix1;
+uniform mat4 light_shadow_matrix2;
+uniform mat4 light_shadow_matrix3;
+uniform mat4 light_shadow_matrix4;
+#endif
 
-#define M_PI 3.14159265359
 
-/* Varyings */
+//
+// varyings
+//
 
-#if defined(ENABLE_COLOR_INTERP)
-in vec4 color_interp;
-#endif
+varying highp vec3 vertex_interp;
+varying vec3 normal_interp;
 
-#if defined(ENABLE_UV_INTERP)
-in vec2 uv_interp;
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+varying vec3 tangent_interp;
+varying vec3 binormal_interp;
 #endif
 
-#if defined(ENABLE_UV2_INTERP)
-in vec2 uv2_interp;
+#ifdef ENABLE_COLOR_INTERP
+varying vec4 color_interp;
 #endif
 
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-in vec3 tangent_interp;
-in vec3 binormal_interp;
+#ifdef ENABLE_UV_INTERP
+varying vec2 uv_interp;
 #endif
 
-in highp vec3 vertex_interp;
-in vec3 normal_interp;
-
+#ifdef ENABLE_UV2_INTERP
+varying vec2 uv2_interp;
+#endif
 
-/* PBR CHANNELS */
+varying vec3 view_interp;
 
-//used on forward mainly
-uniform bool no_ambient_light;
+vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) {
+	float dielectric = (0.034 * 2.0) * specular;
+	// energy conservation
+	return mix(vec3(dielectric), albedo, metallic); // TODO: reference?
+}
 
+FRAGMENT_SHADER_GLOBALS
 
 
-#ifdef USE_RADIANCE_MAP
+#ifdef LIGHT_PASS
+void light_compute(vec3 N,
+                   vec3 L,
+                   vec3 V,
+                   vec3 B,
+                   vec3 T,
+                   vec3 light_color,
+                   vec3 attenuation,
+                   vec3 diffuse_color,
+                   vec3 transmission,
+                   float specular_blob_intensity,
+                   float roughness,
+                   float metallic,
+                   float rim,
+                   float rim_tint,
+                   float clearcoat,
+                   float clearcoat_gloss,
+                   float anisotropy,
+                   inout vec3 diffuse_light,
+                   inout vec3 specular_light) {
+
+	float NdotL = dot(N, L);
+	float cNdotL = max(NdotL, 0.0);
+	float NdotV = dot(N, V);
+	float cNdotV = max(NdotV, 0.0);
 
+	{
+		// calculate diffuse reflection
 
+		// TODO hardcode Oren Nayar for now
+		float diffuse_brdf_NL;
 
-layout(std140) uniform Radiance { //ubo:2
+		diffuse_brdf_NL = max(0.0,(NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness)));
+		// diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
 
-	mat4 radiance_inverse_xform;
-	float radiance_ambient_contribution;
+		diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation;
+	}
 
-};
+	{
+		// calculate specular reflection
 
-#define RADIANCE_MAX_LOD 5.0
+		 vec3 R = normalize(-reflect(L,N));
+		 float cRdotV = max(dot(R, V), 0.0);
+		 float blob_intensity = pow(cRdotV, (1.0 - roughness) * 256.0);
+		 specular_light += light_color * attenuation * blob_intensity * specular_blob_intensity;
 
-#ifdef USE_RADIANCE_MAP_ARRAY
 
-uniform sampler2DArray radiance_map; //texunit:-2
+	}
+}
 
-vec3 textureDualParaboloid(sampler2DArray p_tex, vec3 p_vec,float p_roughness) {
 
-	vec3 norm = normalize(p_vec);
-	norm.xy/=1.0+abs(norm.z);
-	norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25);
 
-	// we need to lie the derivatives (normg) and assume that DP side is always the same
-	// to get proper texture filtering
-	vec2 normg=norm.xy;
-	if (norm.z>0.0) {
-		norm.y=0.5-norm.y+0.5;
-	}
 
-	// thanks to OpenGL spec using floor(layer + 0.5) for texture arrays,
-	// it's easy to have precision errors using fract() to interpolate layers
-	// as such, using fixed point to ensure it works.
+// shadows
 
-	float index = p_roughness * RADIANCE_MAX_LOD;
-	int indexi = int(index * 256.0);
-	vec3 base = textureGrad(p_tex, vec3(norm.xy, float(indexi/256)),dFdx(normg),dFdy(normg)).xyz;
-	vec3 next = textureGrad(p_tex, vec3(norm.xy, float(indexi/256+1)),dFdx(normg),dFdy(normg)).xyz;
-	return mix(base,next,float(indexi%256)/256.0);
+float sample_shadow(highp sampler2D shadow,
+                    vec2 shadow_pixel_size,
+                    vec2 pos,
+                    float depth,
+                    vec4 clamp_rect)
+{
+	// vec4 depth_value = texture2D(shadow, pos);
+	
+	// return depth_value.z;
+	return texture2DProj(shadow, vec4(pos, depth, 1.0)).r;
+	// return (depth_value.x + depth_value.y + depth_value.z + depth_value.w) / 4.0;
 }
 
-#else
 
-uniform sampler2D radiance_map; //texunit:-2
+#endif
 
-vec3 textureDualParaboloid(sampler2D p_tex, vec3 p_vec,float p_roughness) {
+void main() 
+{
 
-	vec3 norm = normalize(p_vec);
-	norm.xy/=1.0+abs(norm.z);
-	norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25);
-	if (norm.z>0.0) {
-		norm.y=0.5-norm.y+0.5;
-	}
-	return textureLod(p_tex, norm.xy, p_roughness * RADIANCE_MAX_LOD).xyz;
-}
+	highp vec3 vertex = vertex_interp;
+	vec3 albedo = vec3(0.8, 0.8, 0.8);
+	vec3 transmission = vec3(0.0);
+	float metallic = 0.0;
+	float specular = 0.5;
+	vec3 emission = vec3(0.0, 0.0, 0.0);
+	float roughness = 1.0;
+	float rim = 0.0;
+	float rim_tint = 0.0;
+	float clearcoat = 0.0;
+	float clearcoat_gloss = 0.0;
+	float anisotropy = 1.0;
+	vec2 anisotropy_flow = vec2(1.0,0.0);
 
-#endif
+	float alpha = 1.0;
+	float side = 1.0;
 
+#if defined(ENABLE_AO)
+	float ao = 1.0;
+	float ao_light_affect = 0.0;
 #endif
 
-/* Material Uniforms */
-
 
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+	vec3 binormal = normalize(binormal_interp) * side;
+	vec3 tangent = normalize(tangent_interp) * side;
+#else
+	vec3 binormal = vec3(0.0);
+	vec3 tangent = vec3(0.0);
+#endif
+	vec3 normal = normalize(normal_interp) * side;
 
-#if defined(USE_MATERIAL)
-
-layout(std140) uniform UniformData {
+#if defined(ENABLE_NORMALMAP)
+	vec3 normalmap = vec3(0.5);
+#endif
+	float normaldepth = 1.0;
 
-MATERIAL_UNIFORMS
 
-};
+#ifdef ALPHA_SCISSOR_USED
+	float alpha_scissor = 0.5;
+#endif
 
+#ifdef SCREEN_UV_USED
+	vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
 #endif
 
-FRAGMENT_SHADER_GLOBALS
+{
 
-layout(std140) uniform SceneData {
+FRAGMENT_SHADER_CODE
 
-	highp mat4 projection_matrix;
-	highp mat4 inv_projection_matrix;
-	highp mat4 camera_inverse_matrix;
-	highp mat4 camera_matrix;
 
-	mediump vec4 ambient_light_color;
-	mediump vec4 bg_color;
+}
 
-	mediump vec4 fog_color_enabled;
-	mediump vec4 fog_sun_color_amount;
+#if defined(ENABLE_NORMALMAP)
+	normalmap.xy = normalmap.xy * 2.0 - 1.0;
+	normalmap.z = sqrt(1.0 - dot(normalmap.xy, normalmap.xy));
 
-	mediump float ambient_energy;
-	mediump float bg_energy;
+	// normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side;
+	normal = normalmap;
+#endif
 
-	mediump float z_offset;
-	mediump float z_slope_scale;
-	highp float shadow_dual_paraboloid_render_zfar;
-	highp float shadow_dual_paraboloid_render_side;
+	normal = normalize(normal);
 
-	highp vec2 viewport_size;
-	highp vec2 screen_pixel_size;
-	highp vec2 shadow_atlas_pixel_size;
-	highp vec2 directional_shadow_pixel_size;
+	vec3 N = normal;
+	
+	vec3 specular_light = vec3(0.0, 0.0, 0.0);
+	vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
 
-	highp float time;
-	highp float z_far;
-	mediump float reflection_multiplier;
-	mediump float subsurface_scatter_width;
-	mediump float ambient_occlusion_affect_light;
+	vec3 ambient_light = vec3(0.0, 0.0, 0.0);
 
-	bool fog_depth_enabled;
-	highp float fog_depth_begin;
-	highp float fog_depth_curve;
-	bool fog_transmit_enabled;
-	highp float fog_transmit_curve;
-	bool fog_height_enabled;
-	highp float fog_height_min;
-	highp float fog_height_max;
-	highp float fog_height_curve;
-};
+	vec3 env_reflection_light = vec3(0.0, 0.0, 0.0);
 
-//directional light data
+	vec3 eye_position = -normalize(vertex_interp);
 
-#ifdef USE_LIGHT_DIRECTIONAL
+#ifdef ALPHA_SCISSOR_USED
+	if (alpha < alpha_scissor) {
+		discard;
+	}
+#endif
+	
+//
+// Lighting
+//
+#ifdef LIGHT_PASS
 
-layout(std140) uniform DirectionalLightData {
+	if (light_type == LIGHT_TYPE_OMNI) {
+		vec3 light_vec = light_position - vertex;
+		float light_length = length(light_vec);
 
-	highp vec4 light_pos_inv_radius;
-	mediump vec4 light_direction_attenuation;
-	mediump vec4 light_color_energy;
-	mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled,
-	mediump vec4 light_clamp;
-	mediump vec4 shadow_color_contact;
-	highp mat4 shadow_matrix1;
-	highp mat4 shadow_matrix2;
-	highp mat4 shadow_matrix3;
-	highp mat4 shadow_matrix4;
-	mediump vec4 shadow_split_offsets;
-};
+		float normalized_distance = light_length / light_range;
 
+		float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation.w);
 
-uniform highp sampler2DShadow directional_shadow; //texunit:-4
+		vec3 attenuation = vec3(omni_attenuation);
 
-#endif
+		if (light_has_shadow > 0.5) {
+			highp vec3 splane =  (light_shadow_matrix * vec4(vertex, 1.0)).xyz;
+			float shadow_len = length(splane);
 
-#ifdef USE_VERTEX_LIGHTING
-in vec4 diffuse_light_interp;
-in vec4 specular_light_interp;
-#endif
-//omni and spot
+			splane = normalize(splane);
 
-struct LightData {
+			vec4 clamp_rect = light_clamp;
 
-	highp vec4 light_pos_inv_radius;
-	mediump vec4 light_direction_attenuation;
-	mediump vec4 light_color_energy;
-	mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled,
-	mediump vec4 light_clamp;
-	mediump vec4 shadow_color_contact;
-	highp mat4 shadow_matrix;
+			if (splane.z >= 0.0) {
+				splane.z += 1.0;
 
-};
+				clamp_rect.y += clamp_rect.w;
+			} else {
+				splane.z = 1.0 - splane.z;
+			}
 
+			splane.xy /= splane.z;
+			splane.xy = splane.xy * 0.5 + 0.5;
+			splane.z = shadow_len / light_range;
 
-layout(std140) uniform OmniLightData { //ubo:4
+			splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw;
 
-	LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
-};
+			float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, clamp_rect);
 
-layout(std140) uniform SpotLightData { //ubo:5
+			if (shadow > splane.z) {
+			} else {
+				attenuation = vec3(0.0);
+			}
+		}
 
-	LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
-};
+		light_compute(normal,
+		              normalize(light_vec),
+		              eye_position,
+		              binormal,
+		              tangent,
+		              light_color.xyz * light_energy,
+		              attenuation,
+		              albedo,
+		              transmission,
+		              specular * light_specular,
+		              roughness,
+		              metallic,
+		              rim,
+		              rim_tint,
+		              clearcoat,
+		              clearcoat_gloss,
+		              anisotropy,
+		              diffuse_light,
+		              specular_light);
+
+	} else if (light_type == LIGHT_TYPE_DIRECTIONAL) {
+
+		vec3 light_vec = -light_direction;
+		vec3 attenuation = vec3(1.0, 1.0, 1.0);
+		
+		float depth_z = -vertex.z;
+		
+		if (light_has_shadow > 0.5) {
+		
+#ifdef LIGHT_USE_PSSM4
+			if (depth_z < light_split_offsets.w) {
+#elif defined(LIGHT_USE_PSSM2)
+			if (depth_z < light_split_offsets.y) {
+#else
+			if (depth_z < light_split_offsets.x) {
+#endif
+		
+			vec3 pssm_coord;
+			float pssm_fade = 0.0;
+			
+#ifdef LIGHT_USE_PSSM_BLEND
+			float pssm_blend;
+			vec3 pssm_coord2;
+			bool use_blend = true;
+#endif
+			
+#ifdef LIGHT_USE_PSSM4
+			if (depth_z < light_split_offsets.y) {
+				if (depth_z < light_split_offsets.x) {
+					highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0));
+					pssm_coord = splane.xyz / splane.w;
+					
+#ifdef LIGHT_USE_PSSM_BLEND
+					splane = (light_shadow_matrix2 * vec4(vertex, 1.0));
+					pssm_coord2 = splane.xyz / splane.w;
+					
+					pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
+#endif
+				} else {
+					highp vec4 splane = (light_shadow_matrix2 * vec4(vertex, 1.0));
+					pssm_coord = splane.xyz / splane.w;
+					
+#ifdef LIGHT_USE_PSSM_BLEND
+					splane = (light_shadow_matrix3 * vec4(vertex, 1.0));
+					pssm_coord2 = splane.xyz / splane.w;
+					
+					pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
+#endif
+				}
+			} else {
+				if (depth_z < light_split_offsets.z) {
 
+					highp vec4 splane = (light_shadow_matrix3 * vec4(vertex, 1.0));
+					pssm_coord = splane.xyz / splane.w;
 
-uniform highp sampler2DShadow shadow_atlas; //texunit:-5
+#if defined(LIGHT_USE_PSSM_BLEND)
+					splane = (light_shadow_matrix4 * vec4(vertex, 1.0));
+					pssm_coord2 = splane.xyz / splane.w;
+					pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z);
+#endif
 
+				} else {
 
-struct ReflectionData {
+					highp vec4 splane = (light_shadow_matrix4 * vec4(vertex, 1.0));
+					pssm_coord = splane.xyz / splane.w;
+					pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z);
 
-	mediump vec4 box_extents;
-	mediump vec4 box_offset;
-	mediump vec4 params; // intensity, 0, interior , boxproject
-	mediump vec4 ambient; //ambient color, energy
-	mediump vec4 atlas_clamp;
-	highp mat4 local_matrix; //up to here for spot and omni, rest is for directional
-	//notes: for ambientblend, use distance to edge to blend between already existing global environment
-};
+#if defined(LIGHT_USE_PSSM_BLEND)
+					use_blend = false;
+#endif
+				}
+			}
+			
+#endif // LIGHT_USE_PSSM4
+			
+#ifdef LIGHT_USE_PSSM2
+			if (depth_z < light_split_offsets.x) {
+				
+				highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0));
+				pssm_coord = splane.xyz / splane.w;
+				
+#ifdef LIGHT_USE_PSSM_BLEND
+				splane = (light_shadow_matrix2 * vec4(vertex, 1.0));
+				pssm_coord2 = splane.xyz / splane.w;
+				pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
+#endif
+			} else {
+				highp vec4 splane = (light_shadow_matrix2 * vec4(vertex, 1.0));
+				pssm_coord = splane.xyz / splane.w;
+				pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
+#ifdef LIGHT_USE_PSSM_BLEND
+				use_blend = false;
+#endif
+			}
+			
+#endif // LIGHT_USE_PSSM2
+			
+#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2)
+			{
+				highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0));
+				pssm_coord = splane.xyz / splane.w;
+			}
+#endif
+			
+			float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), pssm_coord.xy, pssm_coord.z, light_clamp);
+			
+#ifdef LIGHT_USE_PSSM_BLEND
+			if (use_blend) {
+				shadow = mix(shadow, sample_shadow(light_shadow_atlas, vec2(0.0), pssm_coord2.xy, pssm_coord2.z, light_clamp), pssm_blend);
+			}
+#endif
+			
+			attenuation *= shadow;
+			
+			
+		}
+			
+		}
 
-layout(std140) uniform ReflectionProbeData { //ubo:6
+		light_compute(normal,
+		              normalize(light_vec),
+		              eye_position,
+		              binormal,
+		              tangent,
+		              light_color.xyz * light_energy,
+		              attenuation,
+		              albedo,
+		              transmission,
+		              specular * light_specular,
+		              roughness,
+		              metallic,
+		              rim,
+		              rim_tint,
+		              clearcoat,
+		              clearcoat_gloss,
+		              anisotropy,
+		              diffuse_light,
+		              specular_light);
+	} else if (light_type == LIGHT_TYPE_SPOT) {
+
+		vec3 light_att = vec3(1.0);
+		
+		if (light_has_shadow > 0.5) {
+			highp vec4 splane =  (light_shadow_matrix * vec4(vertex, 1.0));
+			splane.xyz /= splane.w;
+			
+			float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, light_clamp);
+			
+			if (shadow > splane.z) {
+			} else {
+				light_att = vec3(0.0);
+			}
+			
+			
+		}
 
-	ReflectionData reflections[MAX_REFLECTION_DATA_STRUCTS];
-};
-uniform mediump sampler2D reflection_atlas; //texunit:-3
+		vec3 light_rel_vec = light_position - vertex;
+		float light_length = length(light_rel_vec);
+		float normalized_distance = light_length / light_range;
+
+		float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation.w);
+		vec3 spot_dir = light_direction;
+
+		float spot_cutoff = light_spot_angle;
+
+		float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_cutoff);
+		float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff));
+
+		spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation);
+
+		light_att *= vec3(spot_attenuation);
+		
+		light_compute(normal,
+		              normalize(light_rel_vec),
+		              eye_position,
+		              binormal,
+		              tangent,
+		              light_color.xyz * light_energy,
+		              light_att,
+		              albedo,
+		              transmission,
+		              specular * light_specular,
+		              roughness,
+		              metallic,
+		              rim,
+		              rim_tint,
+		              clearcoat,
+		              clearcoat_gloss,
+		              anisotropy,
+		              diffuse_light,
+		              specular_light);
+
+	}
+
+	gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha);
+#else
 
+#ifdef RENDER_DEPTH
 
-#ifdef USE_FORWARD_LIGHTING
+#else
 
-uniform int omni_light_indices[MAX_FORWARD_LIGHTS];
-uniform int omni_light_count;
+#ifdef USE_RADIANCE_MAP
 
-uniform int spot_light_indices[MAX_FORWARD_LIGHTS];
-uniform int spot_light_count;
 
-uniform int reflection_indices[MAX_FORWARD_LIGHTS];
-uniform int reflection_count;
+	vec3 ref_vec = reflect(-eye_position, N);
+	ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz);
 
-#endif
+	ref_vec.z *= -1.0;
 
+	env_reflection_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy;
 
-#if defined(SCREEN_TEXTURE_USED)
+	{
+		vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz);
+		vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).xyz * bg_energy;
 
-uniform highp sampler2D screen_texture; //texunit:-7
+		ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution);
 
-#endif
+	}
 
-#ifdef USE_MULTIPLE_RENDER_TARGETS
-
-layout(location=0) out vec4 diffuse_buffer;
-layout(location=1) out vec4 specular_buffer;
-layout(location=2) out vec4 normal_mr_buffer;
-#if defined(ENABLE_SSS)
-layout(location=3) out float sss_buffer;
-#endif
-
-#else
-
-layout(location=0) out vec4 frag_color;
-
-#endif
-
-in highp vec4 position_interp;
-uniform highp sampler2D depth_buffer; //texunit:-8
-
-#ifdef USE_CONTACT_SHADOWS
-
-float contact_shadow_compute(vec3 pos, vec3 dir, float max_distance) {
-
-	if (abs(dir.z)>0.99)
-		return 1.0;
-
-	vec3 endpoint = pos+dir*max_distance;
-	vec4 source = position_interp;
-	vec4 dest = projection_matrix * vec4(endpoint, 1.0);
-
-	vec2 from_screen = (source.xy / source.w) * 0.5 + 0.5;
-	vec2 to_screen = (dest.xy / dest.w) * 0.5 + 0.5;
-
-	vec2 screen_rel = to_screen - from_screen;
-
-	if (length(screen_rel)<0.00001)
-		return 1.0; //too small, don't do anything
-
-	/*float pixel_size; //approximate pixel size
-
-	if (screen_rel.x > screen_rel.y) {
-
-		pixel_size = abs((pos.x-endpoint.x)/(screen_rel.x/screen_pixel_size.x));
-	} else {
-		pixel_size = abs((pos.y-endpoint.y)/(screen_rel.y/screen_pixel_size.y));
-
-	}*/
-	vec4 bias = projection_matrix * vec4(pos+vec3(0.0,0.0,0.04), 1.0); //todo un-harcode the 0.04
-
-
-
-	vec2 pixel_incr = normalize(screen_rel)*screen_pixel_size;
-
-
-	float steps = length(screen_rel) / length(pixel_incr);
-	steps = min(2000.0,steps); //put a limit to avoid freezing in some strange situation
-	//steps=10.0;
-
-	vec4 incr = (dest - source)/steps;
-	float ratio=0.0;
-	float ratio_incr = 1.0/steps;
-
-	while(steps>0.0) {
-		source += incr*2.0;
-		bias+=incr*2.0;
-
-		vec3 uv_depth = (source.xyz / source.w) * 0.5 + 0.5;
-		float depth = texture(depth_buffer,uv_depth.xy).r;
-
-		if (depth < uv_depth.z) {
-			if (depth > (bias.z/bias.w) * 0.5 + 0.5) {
-				return min(pow(ratio,4.0),1.0);
-			} else {
-				return 1.0;
-			}
-		}
-
-
-		ratio+=ratio_incr;
-		steps-=1.0;
-	}
-
-	return 1.0;
-}
-
-#endif
-
-
-// This returns the G_GGX function divided by 2 cos_theta_m, where in practice cos_theta_m is either N.L or N.V.
-// We're dividing this factor off because the overall term we'll end up looks like
-// (see, for example, the first unnumbered equation in B. Burley, "Physically Based Shading at Disney", SIGGRAPH 2012):
-//
-//   F(L.V) D(N.H) G(N.L) G(N.V) / (4 N.L N.V)
-//
-// We're basically regouping this as
-//
-//   F(L.V) D(N.H) [G(N.L)/(2 N.L)] [G(N.V) / (2 N.V)]
-//
-// and thus, this function implements the [G(N.m)/(2 N.m)] part with m = L or V.
-//
-// The contents of the D and G (G1) functions (GGX) are taken from
-// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014).
-// Eqns 71-72 and 85-86 (see also Eqns 43 and 80).
-
-float G_GGX_2cos(float cos_theta_m, float alpha) {
-	// Schlick's approximation
-	// C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994)
-	// Eq. (19), although see Heitz (2014) the about the problems with his derivation.
-	// It nevertheless approximates GGX well with k = alpha/2.
-	float k = 0.5*alpha;
-	return 0.5 / (cos_theta_m * (1.0 - k) + k);
-
-	// float cos2 = cos_theta_m*cos_theta_m;
-	// float sin2 = (1.0-cos2);
-	// return 1.0 /( cos_theta_m + sqrt(cos2 + alpha*alpha*sin2) );
-}
-
-float D_GGX(float cos_theta_m, float alpha) {
-	float alpha2 = alpha*alpha;
-	float d = 1.0 + (alpha2-1.0)*cos_theta_m*cos_theta_m;
-	return alpha2/(M_PI * d * d);
-}
-
-float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) {
-	float cos2 = cos_theta_m * cos_theta_m;
-	float sin2 = (1.0-cos2);
-	float s_x = alpha_x * cos_phi;
-	float s_y = alpha_y * sin_phi;
-	return 1.0  / (cos_theta_m + sqrt(cos2 + (s_x*s_x + s_y*s_y)*sin2 ));
-}
-
-float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) {
-	float cos2 = cos_theta_m * cos_theta_m;
-	float sin2 = (1.0-cos2);
-	float r_x = cos_phi/alpha_x;
-	float r_y = sin_phi/alpha_y;
-	float d = cos2 + sin2*(r_x * r_x + r_y * r_y);
-	return 1.0 / (M_PI * alpha_x * alpha_y * d * d );
-}
-
-
-float SchlickFresnel(float u)
-{
-    float m = 1.0-u;
-    float m2 = m*m;
-    return m2*m2*m; // pow(m,5)
-}
-
-float GTR1(float NdotH, float a)
-{
-    if (a >= 1.0) return 1.0/M_PI;
-    float a2 = a*a;
-    float t = 1.0 + (a2-1.0)*NdotH*NdotH;
-    return (a2-1.0) / (M_PI*log(a2)*t);
-}
-
-vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) {
-	float dielectric = (0.034 * 2.0) * specular;
-	// energy conservation
-	return mix(vec3(dielectric), albedo, metallic); // TODO: reference?
-}
-
-void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec3 attenuation, vec3 diffuse_color, vec3 transmission, float specular_blob_intensity, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, inout vec3 diffuse_light, inout vec3 specular_light) {
-
-#if defined(USE_LIGHT_SHADER_CODE)
-//light is written by the light shader
-
-	vec3 normal = N;
-	vec3 albedo = diffuse_color;
-	vec3 light = L;
-	vec3 view = V;
-
-LIGHT_SHADER_CODE
-
-
-#else
-	float NdotL = dot(N,L);
-	float cNdotL = max(NdotL, 0.0); // clamped NdotL
-	float NdotV = dot(N, V);
-	float cNdotV = max(NdotV, 0.0);
-
-	if (metallic < 1.0) {
-#if defined(DIFFUSE_OREN_NAYAR)
-		vec3 diffuse_brdf_NL;
-#else
-		float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance
-#endif
-
-
-#if defined(DIFFUSE_LAMBERT_WRAP)
-		//energy conserving lambert wrap shader
-		diffuse_brdf_NL = max(0.0,(NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness)));
-
-#elif defined(DIFFUSE_OREN_NAYAR)
-
-		{
-			// see http://mimosa-pudica.net/improved-oren-nayar.html
-			float LdotV = dot(L, V);
-
-
-			float s = LdotV - NdotL * NdotV;
-			float t = mix(1.0, max(NdotL, NdotV), step(0.0, s));
-
-			float sigma2 = roughness * roughness; // TODO: this needs checking
-			vec3 A = 1.0 + sigma2 * (- 0.5 / (sigma2 + 0.33) + 0.17*diffuse_color / (sigma2 + 0.13) );
-			float B = 0.45 * sigma2 / (sigma2 + 0.09);
-
-			diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI);
-		}
-
-#elif defined(DIFFUSE_TOON)
-
-		diffuse_brdf_NL = smoothstep(-roughness,max(roughness,0.01),NdotL);
-
-#elif defined(DIFFUSE_BURLEY)
-
-		{
-
-
-			vec3 H = normalize(V + L);
-			float cLdotH = max(0.0,dot(L, H));
-
-			float FD90 = 0.5 + 2.0 * cLdotH * cLdotH * roughness;
-			float FdV = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotV);
-			float FdL = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotL);
-			diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL;
-	/*
-			float energyBias = mix(roughness, 0.0, 0.5);
-			float energyFactor = mix(roughness, 1.0, 1.0 / 1.51);
-			float fd90 = energyBias + 2.0 * VoH * VoH * roughness;
-			float f0 = 1.0;
-			float lightScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotL, 5.0);
-			float viewScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotV, 5.0);
-
-			diffuse_brdf_NL = lightScatter * viewScatter * energyFactor;*/
-		}
-#else
-		//lambert
-		diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
-#endif
-
-#if defined(TRANSMISSION_USED)
-		diffuse_light += light_color * diffuse_color * mix(vec3(diffuse_brdf_NL), vec3(M_PI), transmission) * attenuation;
-#else
-		diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation;
-#endif
-
-
-
-#if defined(LIGHT_USE_RIM)
-		float rim_light = pow(1.0-cNdotV, (1.0-roughness)*16.0);
-		diffuse_light += rim_light * rim * mix(vec3(1.0),diffuse_color,rim_tint) * light_color;
-#endif
-	}
-
-
-	if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely
-
-
-		// D
-
-#if defined(SPECULAR_BLINN)
-
-		vec3 H = normalize(V + L);
-		float cNdotH = max(dot(N,H), 0.0 );
-		float intensity = pow( cNdotH, (1.0-roughness) * 256.0);
-		specular_light += light_color * intensity * specular_blob_intensity * attenuation;
-
-#elif defined(SPECULAR_PHONG)
-
-		 vec3 R = normalize(-reflect(L,N));
-		 float cRdotV = max(0.0,dot(R,V));
-		 float intensity = pow( cRdotV, (1.0-roughness) * 256.0);
-		 specular_light += light_color * intensity * specular_blob_intensity * attenuation;
-
-#elif defined(SPECULAR_TOON)
-
-		vec3 R = normalize(-reflect(L,N));
-		float RdotV = dot(R,V);
-		float mid = 1.0-roughness;
-		mid*=mid;
-		float intensity = smoothstep(mid-roughness*0.5, mid+roughness*0.5, RdotV) * mid;
-		diffuse_light += light_color * intensity * specular_blob_intensity * attenuation; // write to diffuse_light, as in toon shading you generally want no reflection
-
-#elif defined(SPECULAR_DISABLED)
-		//none..
-
-#elif defined(SPECULAR_SCHLICK_GGX)
-		// shlick+ggx as default
-
-		vec3 H = normalize(V + L);
-
-		float cNdotH = max(dot(N,H), 0.0);
-		float cLdotH = max(dot(L,H), 0.0);
-
-# if defined(LIGHT_USE_ANISOTROPY)
-
-		float aspect = sqrt(1.0-anisotropy*0.9);
-		float rx = roughness/aspect;
-		float ry = roughness*aspect;
-		float ax = rx*rx;
-		float ay = ry*ry;
-		float XdotH = dot( T, H );
-		float YdotH = dot( B, H );
-		float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH);
-		float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH);
-
-# else
-		float alpha = roughness * roughness;
-		float D = D_GGX(cNdotH, alpha);
-		float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha);
-# endif
-		// F
-		float F0 = 1.0; // FIXME
-		float cLdotH5 = SchlickFresnel(cLdotH);
-		float F = mix(cLdotH5, 1.0, F0);
-
-		float specular_brdf_NL = cNdotL * D * F * G;
-
-		specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
-#endif
-
-#if defined(LIGHT_USE_CLEARCOAT)
-		if (clearcoat_gloss > 0.0) {
-# if !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_BLINN)
-			vec3 H = normalize(V + L);
-# endif
-# if !defined(SPECULAR_SCHLICK_GGX)
-			float cNdotH = max(dot(N,H), 0.0);
-			float cLdotH = max(dot(L,H), 0.0);
-			float cLdotH5 = SchlickFresnel(cLdotH);
-#endif
-			float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss));
-			float Fr = mix(.04, 1.0, cLdotH5);
-			float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
-
-
-			float specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
-
-			specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
-		}
-#endif
-	}
-
-
-#endif //defined(USE_LIGHT_SHADER_CODE)
-}
-
-
-float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 pos, float depth, vec4 clamp_rect) {
-
-#ifdef SHADOW_MODE_PCF_13
-
-	float avg=textureProj(shadow,vec4(pos,depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,0.0),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,0.0),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,shadow_pixel_size.y),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,shadow_pixel_size.y),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,-shadow_pixel_size.y),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,-shadow_pixel_size.y),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x*2.0,0.0),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x*2.0,0.0),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y*2.0),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y*2.0),depth,1.0));
-	return avg*(1.0/13.0);
-
-#elif defined(SHADOW_MODE_PCF_5)
-
-	float avg=textureProj(shadow,vec4(pos,depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,0.0),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,0.0),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y),depth,1.0));
-	avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y),depth,1.0));
-	return avg*(1.0/5.0);
-
-#else
-
-	return textureProj(shadow,vec4(pos,depth,1.0));
-
-#endif
-
-}
-
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
-
-in highp float dp_clip;
-
-#endif
-
-
-
-#if 0
-//need to save texture depth for this
-
-vec3 light_transmittance(float translucency,vec3 light_vec, vec3 normal, vec3 pos, float distance) {
-
-	float scale = 8.25 * (1.0 - translucency) / subsurface_scatter_width;
-	float d = scale * distance;
-
-    /**
-     * Armed with the thickness, we can now calculate the color by means of the
-     * precalculated transmittance profile.
-     * (It can be precomputed into a texture, for maximum performance):
-     */
-	float dd = -d * d;
-	vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) +
-		     vec3(0.1,   0.336, 0.344) * exp(dd / 0.0484) +
-		     vec3(0.118, 0.198, 0.0)   * exp(dd / 0.187)  +
-		     vec3(0.113, 0.007, 0.007) * exp(dd / 0.567)  +
-		     vec3(0.358, 0.004, 0.0)   * exp(dd / 1.99)   +
-		     vec3(0.078, 0.0,   0.0)   * exp(dd / 7.41);
-
-    /**
-     * Using the profile, we finally approximate the transmitted lighting from
-     * the back of the object:
-     */
-    return profile * clamp(0.3 + dot(light_vec, normal),0.0,1.0);
-}
-#endif
-
-void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal,vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
-
-	vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz-vertex;
-	float light_length = length( light_rel_vec );
-	float normalized_distance = light_length*omni_lights[idx].light_pos_inv_radius.w;
-	vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w ));
-
-	if (omni_lights[idx].light_params.w>0.5) {
-		//there is a shadowmap
-
-		highp vec3 splane=(omni_lights[idx].shadow_matrix * vec4(vertex,1.0)).xyz;
-		float shadow_len=length(splane);
-		splane=normalize(splane);
-		vec4 clamp_rect=omni_lights[idx].light_clamp;
-
-		if (splane.z>=0.0) {
-
-			splane.z+=1.0;
-
-			clamp_rect.y+=clamp_rect.w;
-
-		} else {
-
-			splane.z=1.0 - splane.z;
-
-			/*
-			if (clamp_rect.z<clamp_rect.w) {
-				clamp_rect.x+=clamp_rect.z;
-			} else {
-				clamp_rect.y+=clamp_rect.w;
-			}
-			*/
-
-		}
-
-		splane.xy/=splane.z;
-		splane.xy=splane.xy * 0.5 + 0.5;
-		splane.z = shadow_len * omni_lights[idx].light_pos_inv_radius.w;
-
-		splane.xy = clamp_rect.xy+splane.xy*clamp_rect.zw;
-		float shadow = sample_shadow(shadow_atlas,shadow_atlas_pixel_size,splane.xy,splane.z,clamp_rect);
-
-#ifdef USE_CONTACT_SHADOWS
-
-		if (shadow>0.01 && omni_lights[idx].shadow_color_contact.a>0.0) {
-
-			float contact_shadow = contact_shadow_compute(vertex,normalize(light_rel_vec),min(light_length,omni_lights[idx].shadow_color_contact.a));
-			shadow=min(shadow,contact_shadow);
-
-		}
-#endif
-		light_attenuation*=mix(omni_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow);
-	}
-
-	light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,omni_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,omni_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light);
-
-}
-
-void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent,vec3 albedo, vec3 transmission,float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss,float anisotropy,float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
-
-	vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz-vertex;
-	float light_length = length( light_rel_vec );
-	float normalized_distance = light_length*spot_lights[idx].light_pos_inv_radius.w;
-	vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.001), spot_lights[idx].light_direction_attenuation.w ));
-	vec3 spot_dir = spot_lights[idx].light_direction_attenuation.xyz;
-	float spot_cutoff=spot_lights[idx].light_params.y;
-	float scos = max(dot(-normalize(light_rel_vec), spot_dir),spot_cutoff);
-	float spot_rim = (1.0 - scos) / (1.0 - spot_cutoff);
-	light_attenuation *= 1.0 - pow( max(spot_rim,0.001), spot_lights[idx].light_params.x);
-
-	if (spot_lights[idx].light_params.w>0.5) {
-		//there is a shadowmap
-		highp vec4 splane=(spot_lights[idx].shadow_matrix * vec4(vertex,1.0));
-		splane.xyz/=splane.w;
-
-		float shadow = sample_shadow(shadow_atlas,shadow_atlas_pixel_size,splane.xy,splane.z,spot_lights[idx].light_clamp);
-
-#ifdef USE_CONTACT_SHADOWS
-		if (shadow>0.01 && spot_lights[idx].shadow_color_contact.a>0.0) {
-
-			float contact_shadow = contact_shadow_compute(vertex,normalize(light_rel_vec),min(light_length,spot_lights[idx].shadow_color_contact.a));
-			shadow=min(shadow,contact_shadow);
-
-		}
-#endif
-		light_attenuation*=mix(spot_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow);
-	}
-
-	light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,spot_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,spot_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light);
-
-}
-
-void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 tangent,float roughness,float anisotropy,vec3 ambient,vec3 skybox, inout highp vec4 reflection_accum,inout highp vec4 ambient_accum) {
-
-	vec3 ref_vec = normalize(reflect(vertex,normal));
-	vec3 local_pos = (reflections[idx].local_matrix * vec4(vertex,1.0)).xyz;
-	vec3 box_extents = reflections[idx].box_extents.xyz;
-
-	if (any(greaterThan(abs(local_pos),box_extents))) { //out of the reflection box
-		return;
-	}
-
-	vec3 inner_pos = abs(local_pos / box_extents);
-	float blend = max(inner_pos.x,max(inner_pos.y,inner_pos.z));
-	//make blend more rounded
-	blend=mix(length(inner_pos),blend,blend);
-	blend*=blend;
-	blend=1.001-blend;
-
-	if (reflections[idx].params.x>0.0){// compute reflection
-
-		vec3 local_ref_vec = (reflections[idx].local_matrix * vec4(ref_vec,0.0)).xyz;
-
-		if (reflections[idx].params.w > 0.5) { //box project
-
-			vec3 nrdir = normalize(local_ref_vec);
-			vec3 rbmax = (box_extents - local_pos)/nrdir;
-			vec3 rbmin = (-box_extents - local_pos)/nrdir;
-
-
-			vec3 rbminmax = mix(rbmin,rbmax,greaterThan(nrdir,vec3(0.0,0.0,0.0)));
-
-			float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
-			vec3 posonbox = local_pos + nrdir * fa;
-			local_ref_vec = posonbox - reflections[idx].box_offset.xyz;
-		}
-
-
-		vec4 clamp_rect=reflections[idx].atlas_clamp;
-		vec3 norm = normalize(local_ref_vec);
-		norm.xy/=1.0+abs(norm.z);
-		norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25);
-		if (norm.z>0.0) {
-			norm.y=0.5-norm.y+0.5;
-		}
-
-		vec2 atlas_uv =  norm.xy * clamp_rect.zw + clamp_rect.xy;
-		atlas_uv = clamp(atlas_uv,clamp_rect.xy,clamp_rect.xy+clamp_rect.zw);
-
-		highp vec4 reflection;
-		reflection.rgb = textureLod(reflection_atlas,atlas_uv,roughness*5.0).rgb;
-
-		if (reflections[idx].params.z < 0.5) {
-			reflection.rgb = mix(skybox,reflection.rgb,blend);
-		}
-		reflection.rgb*=reflections[idx].params.x;
-		reflection.a = blend;
-		reflection.rgb*=reflection.a;
-
-		reflection_accum+=reflection;
-	}
-
-	if (reflections[idx].ambient.a>0.0) { //compute ambient using skybox
-
-
-		vec3 local_amb_vec = (reflections[idx].local_matrix * vec4(normal,0.0)).xyz;
-
-		vec3 splane=normalize(local_amb_vec);
-		vec4 clamp_rect=reflections[idx].atlas_clamp;
-
-		splane.z*=-1.0;
-		if (splane.z>=0.0) {
-			splane.z+=1.0;
-			clamp_rect.y+=clamp_rect.w;
-		} else {
-			splane.z=1.0 - splane.z;
-			splane.y=-splane.y;
-		}
-
-		splane.xy/=splane.z;
-		splane.xy=splane.xy * 0.5 + 0.5;
-
-		splane.xy = splane.xy * clamp_rect.zw + clamp_rect.xy;
-		splane.xy = clamp(splane.xy,clamp_rect.xy,clamp_rect.xy+clamp_rect.zw);
-
-		highp vec4 ambient_out;
-		ambient_out.a=blend;
-		ambient_out.rgb = textureLod(reflection_atlas,splane.xy,5.0).rgb;
-		ambient_out.rgb=mix(reflections[idx].ambient.rgb,ambient_out.rgb,reflections[idx].ambient.a);
-		if (reflections[idx].params.z < 0.5) {
-			ambient_out.rgb = mix(ambient,ambient_out.rgb,blend);
-		}
-
-		ambient_out.rgb *= ambient_out.a;
-		ambient_accum+=ambient_out;
-	} else {
-
-		highp vec4 ambient_out;
-		ambient_out.a=blend;
-		ambient_out.rgb=reflections[idx].ambient.rgb;
-		if (reflections[idx].params.z < 0.5) {
-			ambient_out.rgb = mix(ambient,ambient_out.rgb,blend);
-		}
-		ambient_out.rgb *= ambient_out.a;
-		ambient_accum+=ambient_out;
-
-	}
-}
-
-#ifdef USE_GI_PROBES
-
-uniform mediump sampler3D gi_probe1; //texunit:-9
-uniform highp mat4 gi_probe_xform1;
-uniform highp vec3 gi_probe_bounds1;
-uniform highp vec3 gi_probe_cell_size1;
-uniform highp float gi_probe_multiplier1;
-uniform highp float gi_probe_bias1;
-uniform highp float gi_probe_normal_bias1;
-uniform bool gi_probe_blend_ambient1;
-
-uniform mediump sampler3D gi_probe2; //texunit:-10
-uniform highp mat4 gi_probe_xform2;
-uniform highp vec3 gi_probe_bounds2;
-uniform highp vec3 gi_probe_cell_size2;
-uniform highp float gi_probe_multiplier2;
-uniform highp float gi_probe_bias2;
-uniform highp float gi_probe_normal_bias2;
-uniform bool gi_probe2_enabled;
-uniform bool gi_probe_blend_ambient2;
-
-vec3 voxel_cone_trace(mediump sampler3D probe, vec3 cell_size, vec3 pos, vec3 ambient, bool blend_ambient, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
-
-	float dist = p_bias;//1.0; //dot(direction,mix(vec3(-1.0),vec3(1.0),greaterThan(direction,vec3(0.0))))*2.0;
-	float alpha=0.0;
-	vec3 color = vec3(0.0);
-
-	while(dist < max_distance && alpha < 0.95) {
-		float diameter = max(1.0, 2.0 * tan_half_angle * dist);
-		vec4 scolor = textureLod(probe, (pos + dist * direction) * cell_size, log2(diameter) );
-		float a = (1.0 - alpha);
-		color += scolor.rgb * a;
-		alpha += a * scolor.a;
-		dist += diameter * 0.5;
-	}
-
-	if (blend_ambient) {
-		color.rgb = mix(ambient,color.rgb,min(1.0,alpha/0.95));
-	}
-
-	return color;
-}
-
-void gi_probe_compute(mediump sampler3D probe, mat4 probe_xform, vec3 bounds,vec3 cell_size,vec3 pos, vec3 ambient, vec3 environment, bool blend_ambient,float multiplier, mat3 normal_mtx,vec3 ref_vec, float roughness,float p_bias,float p_normal_bias, inout vec4 out_spec, inout vec4 out_diff) {
-
-
-
-	vec3 probe_pos = (probe_xform * vec4(pos,1.0)).xyz;
-	vec3 ref_pos = (probe_xform * vec4(pos+ref_vec,1.0)).xyz;
-	ref_vec = normalize(ref_pos - probe_pos);
-
-	probe_pos+=(probe_xform * vec4(normal_mtx[2],0.0)).xyz*p_normal_bias;
-
-/*	out_diff.rgb = voxel_cone_trace(probe,cell_size,probe_pos,normalize((probe_xform * vec4(ref_vec,0.0)).xyz),0.0 ,100.0);
-	out_diff.a = 1.0;
-	return;*/
-	//out_diff = vec4(textureLod(probe,probe_pos*cell_size,3.0).rgb,1.0);
-	//return;
-
-	//this causes corrupted pixels, i have no idea why..
-	if (any(bvec2(any(lessThan(probe_pos,vec3(0.0))),any(greaterThan(probe_pos,bounds))))) {
-		return;
-	}
-
-	//vec3 blendv = probe_pos/bounds * 2.0 - 1.0;
-	//float blend = 1.001-max(blendv.x,max(blendv.y,blendv.z));
-	float blend=1.0;
-
-	float max_distance = length(bounds);
-
-	//radiance
-#ifdef VCT_QUALITY_HIGH
-
-#define MAX_CONE_DIRS 6
-	vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] (
-		vec3(0, 0, 1),
-		vec3(0.866025, 0, 0.5),
-		vec3(0.267617, 0.823639, 0.5),
-		vec3(-0.700629, 0.509037, 0.5),
-		vec3(-0.700629, -0.509037, 0.5),
-		vec3(0.267617, -0.823639, 0.5)
-	);
-
-	float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15);
-	float cone_angle_tan = 0.577;
-	float min_ref_tan = 0.0;
-#else
-
-#define MAX_CONE_DIRS 4
-
-	vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] (
-			vec3(0.707107, 0, 0.707107),
-			vec3(0, 0.707107, 0.707107),
-			vec3(-0.707107, 0, 0.707107),
-			vec3(0, -0.707107, 0.707107)
-	);
-
-	float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25);
-	float cone_angle_tan = 0.98269;
-	max_distance*=0.5;
-	float min_ref_tan = 0.2;
-
-#endif
-	vec3 light=vec3(0.0);
-	for(int i=0;i<MAX_CONE_DIRS;i++) {
-
-		vec3 dir = normalize( (probe_xform * vec4(pos + normal_mtx * cone_dirs[i],1.0)).xyz - probe_pos);
-		light+=cone_weights[i] * voxel_cone_trace(probe,cell_size,probe_pos,ambient,blend_ambient,dir,cone_angle_tan,max_distance,p_bias);
-
-	}
-
-	light*=multiplier;
-
-	out_diff += vec4(light*blend,blend);
-
-	//irradiance
-
-	vec3 irr_light =  voxel_cone_trace(probe,cell_size,probe_pos,environment,blend_ambient,ref_vec,max(min_ref_tan,tan(roughness * 0.5 * M_PI)) ,max_distance,p_bias);
-
-	irr_light *= multiplier;
-	//irr_light=vec3(0.0);
-
-	out_spec += vec4(irr_light*blend,blend);
-
-}
-
-
-void gi_probes_compute(vec3 pos, vec3 normal, float roughness, inout vec3 out_specular, inout vec3 out_ambient) {
-
-	roughness = roughness * roughness;
-
-	vec3 ref_vec = normalize(reflect(normalize(pos),normal));
-
-	//find arbitrary tangent and bitangent, then build a matrix
-	vec3 v0 = abs(normal.z) < 0.999 ? vec3(0, 0, 1) : vec3(0, 1, 0);
-	vec3 tangent = normalize(cross(v0, normal));
-	vec3 bitangent = normalize(cross(tangent, normal));
-	mat3 normal_mat = mat3(tangent,bitangent,normal);
-
-	vec4 diff_accum = vec4(0.0);
-	vec4 spec_accum = vec4(0.0);
-
-	vec3 ambient = out_ambient;
-	out_ambient = vec3(0.0);
-
-	vec3 environment = out_specular;
-
-	out_specular = vec3(0.0);
-
-	gi_probe_compute(gi_probe1,gi_probe_xform1,gi_probe_bounds1,gi_probe_cell_size1,pos,ambient,environment,gi_probe_blend_ambient1,gi_probe_multiplier1,normal_mat,ref_vec,roughness,gi_probe_bias1,gi_probe_normal_bias1,spec_accum,diff_accum);
-
-	if (gi_probe2_enabled) {
-
-		gi_probe_compute(gi_probe2,gi_probe_xform2,gi_probe_bounds2,gi_probe_cell_size2,pos,ambient,environment,gi_probe_blend_ambient2,gi_probe_multiplier2,normal_mat,ref_vec,roughness,gi_probe_bias2,gi_probe_normal_bias2,spec_accum,diff_accum);
-	}
-
-	if (diff_accum.a>0.0) {
-		diff_accum.rgb/=diff_accum.a;
-	}
-
-	if (spec_accum.a>0.0) {
-		spec_accum.rgb/=spec_accum.a;
-	}
-
-	out_specular+=spec_accum.rgb;
-	out_ambient+=diff_accum.rgb;
-
-}
-
-#endif
-
-
-
-void main() {
-
-#ifdef RENDER_DEPTH_DUAL_PARABOLOID
-
-	if (dp_clip>0.0)
-		discard;
-#endif
-
-	//lay out everything, whathever is unused is optimized away anyway
-	highp vec3 vertex = vertex_interp;
-	vec3 albedo = vec3(0.8,0.8,0.8);
-	vec3 transmission = vec3(0.0);
-	float metallic = 0.0;
-	float specular = 0.5;
-	vec3 emission = vec3(0.0,0.0,0.0);
-	float roughness = 1.0;
-	float rim = 0.0;
-	float rim_tint = 0.0;
-	float clearcoat=0.0;
-	float clearcoat_gloss=0.0;
-	float anisotropy = 1.0;
-	vec2 anisotropy_flow = vec2(1.0,0.0);
-
-#if defined(ENABLE_AO)
-	float ao=1.0;
-	float ao_light_affect=0.0;
-#endif
-
-	float alpha = 1.0;
-
-#ifdef METERIAL_DOUBLESIDED
-	float side=float(gl_FrontFacing)*2.0-1.0;
-#else
-	float side=1.0;
-#endif
-
-
-#if defined(ALPHA_SCISSOR_USED)
-	float alpha_scissor = 0.5;
-#endif
-
-#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-	vec3 binormal = normalize(binormal_interp)*side;
-	vec3 tangent = normalize(tangent_interp)*side;
-#else
-	vec3 binormal = vec3(0.0);
-	vec3 tangent = vec3(0.0);
-#endif
-	vec3 normal = normalize(normal_interp)*side;
-
-#if defined(ENABLE_UV_INTERP)
-	vec2 uv = uv_interp;
-#endif
-
-#if defined(ENABLE_UV2_INTERP)
-	vec2 uv2 = uv2_interp;
-#endif
-
-#if defined(ENABLE_COLOR_INTERP)
-	vec4 color = color_interp;
-#endif
-
-#if defined(ENABLE_NORMALMAP)
-
-	vec3 normalmap = vec3(0.0);
-#endif
-
-	float normaldepth=1.0;
-
-#if defined(SCREEN_UV_USED)
-	vec2 screen_uv = gl_FragCoord.xy*screen_pixel_size;
-#endif
-
-#if defined (ENABLE_SSS)
-	float sss_strength=0.0;
-#endif
-
-{
-
-
-FRAGMENT_SHADER_CODE
-
-}
-
-
-#if defined(ALPHA_SCISSOR_USED)
-	if (alpha<alpha_scissor) {
-		discard;
-	}
-#endif
-
-#ifdef USE_OPAQUE_PREPASS
-
-	if (alpha<0.99) {
-		discard;
-	}
-#endif
-
-#if defined(ENABLE_NORMALMAP)
-
-	normalmap.xy=normalmap.xy*2.0-1.0;
-	normalmap.z=sqrt(1.0-dot(normalmap.xy,normalmap.xy)); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc.
-
-	normal = normalize( mix(normal_interp,tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z,normaldepth) ) * side;
-
-#endif
-
-#if defined(LIGHT_USE_ANISOTROPY)
-
-	if (anisotropy>0.01) {
-		//rotation matrix
-		mat3 rot = mat3( tangent, binormal, normal );
-		//make local to space
-		tangent = normalize(rot * vec3(anisotropy_flow.x,anisotropy_flow.y,0.0));
-		binormal = normalize(rot * vec3(-anisotropy_flow.y,anisotropy_flow.x,0.0));
-	}
-
-#endif
-
-#ifdef ENABLE_CLIP_ALPHA
-	if (albedo.a<0.99) {
-		//used for doublepass and shadowmapping
-		discard;
-	}
-#endif
-
-/////////////////////// LIGHTING //////////////////////////////
-
-	//apply energy conservation
-
-#ifdef USE_VERTEX_LIGHTING
-
-	vec3 specular_light = specular_light_interp.rgb;
-	vec3 diffuse_light = diffuse_light_interp.rgb;
-#else
-
-	vec3 specular_light = vec3(0.0,0.0,0.0);
-	vec3 diffuse_light = vec3(0.0,0.0,0.0);
-
-#endif
-
-	vec3 ambient_light;
-	vec3 env_reflection_light = vec3(0.0,0.0,0.0);
-
-	vec3 eye_vec = -normalize( vertex_interp );
-
-
-
-#ifdef USE_RADIANCE_MAP
-
-	if (no_ambient_light) {
-		ambient_light=vec3(0.0,0.0,0.0);
-	} else {
-		{
-
-			{ //read radiance from dual paraboloid
-
-				vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n);
-				ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz);
-				vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy;
-				env_reflection_light = radiance;
-
-			}
-			//no longer a cubemap
-			//vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y);
-
-		}
-
-		{
-
-			vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz);
-			vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy;
-
-			ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution);
-			//ambient_light=vec3(0.0,0.0,0.0);
-		}
-	}
-
-#else
-
-	if (no_ambient_light){
-		ambient_light=vec3(0.0,0.0,0.0);
-	} else {
-		ambient_light=ambient_light_color.rgb;
-	}
-#endif
-
-	ambient_light*=ambient_energy;
-
-	float specular_blob_intensity=1.0;
-#if defined(SPECULAR_TOON)
-	specular_blob_intensity*=specular * 2.0;
-#endif
-
-#if defined(USE_LIGHT_DIRECTIONAL)
-
-	vec3 light_attenuation=vec3(1.0);
-
-	float depth_z = -vertex.z;
-#ifdef LIGHT_DIRECTIONAL_SHADOW
-
-#ifdef LIGHT_USE_PSSM4
-	if (depth_z < shadow_split_offsets.w) {
-#elif defined(LIGHT_USE_PSSM2)
-	if (depth_z < shadow_split_offsets.y) {
-#else
-	if (depth_z < shadow_split_offsets.x) {
-#endif //LIGHT_USE_PSSM4
-
-	vec3 pssm_coord;
-	float pssm_fade=0.0;
-
-#ifdef LIGHT_USE_PSSM_BLEND
-	float pssm_blend;
-	vec3 pssm_coord2;
-	bool use_blend=true;
-#endif
-
-
-#ifdef LIGHT_USE_PSSM4
-
-
-	if (depth_z < shadow_split_offsets.y) {
-
-		if (depth_z < shadow_split_offsets.x) {
-
-			highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0));
-			pssm_coord=splane.xyz/splane.w;
-
-
-#if defined(LIGHT_USE_PSSM_BLEND)
-
-			splane=(shadow_matrix2 * vec4(vertex,1.0));
-			pssm_coord2=splane.xyz/splane.w;
-			pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z);
-#endif
-
-		} else {
-
-			highp vec4 splane=(shadow_matrix2 * vec4(vertex,1.0));
-			pssm_coord=splane.xyz/splane.w;
-
-#if defined(LIGHT_USE_PSSM_BLEND)
-			splane=(shadow_matrix3 * vec4(vertex,1.0));
-			pssm_coord2=splane.xyz/splane.w;
-			pssm_blend=smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z);
-#endif
-
-		}
-	} else {
-
-
-		if (depth_z < shadow_split_offsets.z) {
-
-			highp vec4 splane=(shadow_matrix3 * vec4(vertex,1.0));
-			pssm_coord=splane.xyz/splane.w;
-
-#if defined(LIGHT_USE_PSSM_BLEND)
-			splane=(shadow_matrix4 * vec4(vertex,1.0));
-			pssm_coord2=splane.xyz/splane.w;
-			pssm_blend=smoothstep(shadow_split_offsets.y,shadow_split_offsets.z,depth_z);
-#endif
-
-		} else {
-
-			highp vec4 splane=(shadow_matrix4 * vec4(vertex,1.0));
-			pssm_coord=splane.xyz/splane.w;
-			pssm_fade = smoothstep(shadow_split_offsets.z,shadow_split_offsets.w,depth_z);
-
-#if defined(LIGHT_USE_PSSM_BLEND)
-			use_blend=false;
-
-#endif
-
-		}
-	}
-
-
-
-#endif //LIGHT_USE_PSSM4
-
-#ifdef LIGHT_USE_PSSM2
-
-	if (depth_z < shadow_split_offsets.x) {
-
-		highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0));
-		pssm_coord=splane.xyz/splane.w;
-
-
-#if defined(LIGHT_USE_PSSM_BLEND)
-
-		splane=(shadow_matrix2 * vec4(vertex,1.0));
-		pssm_coord2=splane.xyz/splane.w;
-		pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z);
-#endif
-
-	} else {
-		highp vec4 splane=(shadow_matrix2 * vec4(vertex,1.0));
-		pssm_coord=splane.xyz/splane.w;
-		pssm_fade = smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z);
-#if defined(LIGHT_USE_PSSM_BLEND)
-		use_blend=false;
-
-#endif
-
-	}
-
-#endif //LIGHT_USE_PSSM2
-
-#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2)
-	{ //regular orthogonal
-		highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0));
-		pssm_coord=splane.xyz/splane.w;
-	}
-#endif
-
-
-	//one one sample
-
-	float shadow = sample_shadow(directional_shadow,directional_shadow_pixel_size,pssm_coord.xy,pssm_coord.z,light_clamp);
-
-#if defined(LIGHT_USE_PSSM_BLEND)
-
-	if (use_blend) {
-		shadow=mix(shadow, sample_shadow(directional_shadow,directional_shadow_pixel_size,pssm_coord2.xy,pssm_coord2.z,light_clamp),pssm_blend);
-	}
-#endif
-
-#ifdef USE_CONTACT_SHADOWS
-	if (shadow>0.01 && shadow_color_contact.a>0.0) {
-
-		float contact_shadow = contact_shadow_compute(vertex,-light_direction_attenuation.xyz,shadow_color_contact.a);
-		shadow=min(shadow,contact_shadow);
-
-	}
-#endif
-	light_attenuation=mix(mix(shadow_color_contact.rgb,vec3(1.0),shadow),vec3(1.0),pssm_fade);
-
-
-	}
-
-
-#endif //LIGHT_DIRECTIONAL_SHADOW
-
-#ifdef USE_VERTEX_LIGHTING
-	diffuse_light*=mix(vec3(1.0),light_attenuation,diffuse_light_interp.a);
-	specular_light*=mix(vec3(1.0),light_attenuation,specular_light_interp.a);
-
-#else
-	light_compute(normal,-light_direction_attenuation.xyz,eye_vec,binormal,tangent,light_color_energy.rgb,light_attenuation,albedo,transmission,light_params.z*specular_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light);
-#endif
-
-
-#endif //#USE_LIGHT_DIRECTIONAL
-
-#ifdef USE_GI_PROBES
-	gi_probes_compute(vertex,normal,roughness,env_reflection_light,ambient_light);
-
-#endif
-
-#ifdef USE_FORWARD_LIGHTING
-
-
-	highp vec4 reflection_accum = vec4(0.0,0.0,0.0,0.0);
-	highp vec4 ambient_accum = vec4(0.0,0.0,0.0,0.0);
-	for(int i=0;i<reflection_count;i++) {
-		reflection_process(reflection_indices[i],vertex,normal,binormal,tangent,roughness,anisotropy,ambient_light,env_reflection_light,reflection_accum,ambient_accum);
-	}
-
-	if (reflection_accum.a>0.0) {
-		specular_light+=reflection_accum.rgb/reflection_accum.a;
-	} else {
-		specular_light+=env_reflection_light;
-	}
-
-	if (ambient_accum.a>0.0) {
-		ambient_light+=ambient_accum.rgb/ambient_accum.a;
-	}
-
-
-
-#ifdef USE_VERTEX_LIGHTING
-
-	diffuse_light*=albedo;
-#else
-
-	for(int i=0;i<omni_light_count;i++) {
-		light_process_omni(omni_light_indices[i],vertex,eye_vec,normal,binormal,tangent,albedo,transmission,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,specular_blob_intensity,diffuse_light,specular_light);
-	}
-
-	for(int i=0;i<spot_light_count;i++) {
-		light_process_spot(spot_light_indices[i],vertex,eye_vec,normal,binormal,tangent,albedo,transmission,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,specular_blob_intensity,diffuse_light,specular_light);
-	}
-
-#endif //USE_VERTEX_LIGHTING
-
-#endif
-
-
-
-
-#ifdef RENDER_DEPTH
-//nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
-#else
-
-	specular_light*=reflection_multiplier;
-	ambient_light*=albedo; //ambient must be multiplied by albedo at the end
+	ambient_light *= ambient_energy;
+	
+	specular_light += env_reflection_light;
+	
+	ambient_light *= albedo;
 
 #if defined(ENABLE_AO)
-	ambient_light*=ao;
-	ao_light_affect = mix(1.0,ao,ao_light_affect);
-	specular_light*=ao_light_affect;
-	diffuse_light*=ao_light_affect;
-#endif
-
-
-
-	//energy conservation
-	diffuse_light *= 1.0-metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point
-	ambient_light *= 1.0-metallic;
-
-
+	ambient_light *= ao;
+	ao_light_affect = mix(1.0, ao, ao_light_affect);
+	specular_light *= ao_light_affect;
+	diffuse_light *= ao_light_affect;
+#endif
+	
+	diffuse_light *= 1.0 - metallic;
+	ambient_light *= 1.0 - metallic;
+	
+	// environment BRDF approximation
+	
+	// TODO shadeless
 	{
-
-#if defined(DIFFUSE_TOON)
-		//simplify for toon, as
-		specular_light *= specular * metallic * albedo * 2.0;
-#else
-		// Environment brdf approximation (Lazarov 2013)
-		// see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
 		const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
 		const vec4 c1 = vec4( 1.0, 0.0425, 1.04, -0.04);
 		vec4 r = roughness * c0 + c1;
-		float ndotv = clamp(dot(normal,eye_vec),0.0,1.0);
+		float ndotv = clamp(dot(normal,eye_position),0.0,1.0);
 		float a004 = min( r.x * r.x, exp2( -9.28 * ndotv ) ) * r.x + r.y;
 		vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
 
 		vec3 specular_color = metallic_to_specular_color(metallic, specular, albedo);
 		specular_light *= AB.x * specular_color + AB.y;
-#endif
-
-	}
-
-	if (fog_color_enabled.a > 0.5) {
-
-		float fog_amount=0.0;
-
-
-
-#ifdef USE_LIGHT_DIRECTIONAL
-
-		vec3 fog_color = mix( fog_color_enabled.rgb, fog_sun_color_amount.rgb,fog_sun_color_amount.a * pow(max( dot(normalize(vertex),-light_direction_attenuation.xyz), 0.0),8.0) );
-#else
-
-		vec3 fog_color = fog_color_enabled.rgb;
-#endif
-
-		//apply fog
-
-		if (fog_depth_enabled) {
-
-			float fog_z = smoothstep(fog_depth_begin,z_far,length(vertex));
-
-			fog_amount = pow(fog_z,fog_depth_curve);
-			if (fog_transmit_enabled) {
-				vec3 total_light = emission + ambient_light + specular_light + diffuse_light;
-				float transmit = pow(fog_z,fog_transmit_curve);
-				fog_color = mix(max(total_light,fog_color),fog_color,transmit);
-			}
-		}
-
-		if (fog_height_enabled) {
-			float y = (camera_matrix * vec4(vertex,1.0)).y;
-			fog_amount = max(fog_amount,pow(smoothstep(fog_height_min,fog_height_max,y),fog_height_curve));
-		}
-
-		float rev_amount = 1.0 - fog_amount;
-
-
-		emission = emission * rev_amount + fog_color * fog_amount;
-		ambient_light*=rev_amount;
-		specular_light*rev_amount;
-		diffuse_light*=rev_amount;
-
 	}
 
-#ifdef USE_MULTIPLE_RENDER_TARGETS
 
+	gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha);
+	// gl_FragColor = vec4(normal, 1.0);
 
-#ifdef SHADELESS
-	diffuse_buffer=vec4(albedo.rgb,0.0);
-	specular_buffer=vec4(0.0);
 
 #else
-
-#if defined(ENABLE_AO)
-
-	float ambient_scale=0.0; // AO is supplied by material
-#else
-	//approximate ambient scale for SSAO, since we will lack full ambient
-	float max_emission=max(emission.r,max(emission.g,emission.b));
-	float max_ambient=max(ambient_light.r,max(ambient_light.g,ambient_light.b));
-	float max_diffuse=max(diffuse_light.r,max(diffuse_light.g,diffuse_light.b));
-	float total_ambient = max_ambient+max_diffuse+max_emission;
-	float ambient_scale = (total_ambient>0.0) ? (max_ambient+ambient_occlusion_affect_light*max_diffuse)/total_ambient : 0.0;
-#endif //ENABLE_AO
-
-	diffuse_buffer=vec4(emission+diffuse_light+ambient_light,ambient_scale);
-	specular_buffer=vec4(specular_light,metallic);
-
-#endif //SHADELESS
-
-	normal_mr_buffer=vec4(normalize(normal)*0.5+0.5,roughness);
-
-#if defined (ENABLE_SSS)
-	sss_buffer = sss_strength;
+	gl_FragColor = vec4(albedo, alpha);
 #endif
+#endif // RENDER_DEPTH
 
 
-#else //USE_MULTIPLE_RENDER_TARGETS
-
-
-#ifdef SHADELESS
-	frag_color=vec4(albedo,alpha);
-#else
-	frag_color=vec4(emission+ambient_light+diffuse_light+specular_light,alpha);
-#endif //SHADELESS
-
-
-#endif //USE_MULTIPLE_RENDER_TARGETS
-
-
-
-#endif //RENDER_DEPTH
+#endif // lighting
 
 
 }

+ 45 - 0
drivers/gles2/shaders/stdlib.glsl

@@ -0,0 +1,45 @@
+
+vec2 select2(vec2 a, vec2 b, bvec2 c)
+{
+	vec2 ret;
+
+	ret.x = c.x ? b.x : a.x;
+	ret.y = c.y ? b.y : a.y;
+
+	return ret;
+}
+
+vec3 select3(vec3 a, vec3 b, bvec3 c)
+{
+	vec3 ret;
+
+	ret.x = c.x ? b.x : a.x;
+	ret.y = c.y ? b.y : a.y;
+	ret.z = c.z ? b.z : a.z;
+
+	return ret;
+}
+
+vec4 select4(vec4 a, vec4 b, bvec4 c)
+{
+	vec4 ret;
+
+	ret.x = c.x ? b.x : a.x;
+	ret.y = c.y ? b.y : a.y;
+	ret.z = c.z ? b.z : a.z;
+	ret.w = c.w ? b.w : a.w;
+
+	return ret;
+}
+
+
+highp vec4 texel2DFetch(highp sampler2D tex, ivec2 size, ivec2 coord)
+{
+	float x_coord = float(2 * coord.x + 1) / float(size.x * 2);
+	float y_coord = float(2 * coord.y + 1) / float(size.y * 2);
+
+	x_coord = float(coord.x) / float(size.x);
+	y_coord = float(coord.y) / float(size.y);
+
+	return texture2DLod(tex, vec2(x_coord, y_coord), 0.0);
+}

+ 1 - 0
methods.py

@@ -259,6 +259,7 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2
     fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int32_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n")
     fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Color& p_color) { _FU GLfloat col[4]={p_color.r,p_color.g,p_color.b,p_color.a}; glUniform4fv(get_uniform(p_uniform),1,col); }\n\n")
     fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector2& p_vec2) { _FU GLfloat vec2[2]={p_vec2.x,p_vec2.y}; glUniform2fv(get_uniform(p_uniform),1,vec2); }\n\n")
+    fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Size2i& p_vec2) { _FU GLint vec2[2]={p_vec2.x,p_vec2.y}; glUniform2iv(get_uniform(p_uniform),1,vec2); }\n\n")
     fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector3& p_vec3) { _FU GLfloat vec3[3]={p_vec3.x,p_vec3.y,p_vec3.z}; glUniform3fv(get_uniform(p_uniform),1,vec3); }\n\n")
     fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b) { _FU glUniform2f(get_uniform(p_uniform),p_a,p_b); }\n\n")
     fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b, float p_c) { _FU glUniform3f(get_uniform(p_uniform),p_a,p_b,p_c); }\n\n")

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно