diff options
-rw-r--r-- | drivers/gles2/rasterizer_canvas_gles2.cpp | 172 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_canvas_gles2.h | 2 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_gles2.cpp | 4 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_scene_gles2.cpp | 2087 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_scene_gles2.h | 317 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_storage_gles2.cpp | 2149 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_storage_gles2.h | 326 | ||||
-rw-r--r-- | drivers/gles2/shader_compiler_gles2.cpp | 67 | ||||
-rw-r--r-- | drivers/gles2/shader_gles2.cpp | 389 | ||||
-rw-r--r-- | drivers/gles2/shader_gles2.h | 216 | ||||
-rw-r--r-- | drivers/gles2/shaders/SCsub | 4 | ||||
-rw-r--r-- | drivers/gles2/shaders/canvas.glsl | 8 | ||||
-rw-r--r-- | drivers/gles2/shaders/copy.glsl | 63 | ||||
-rw-r--r-- | drivers/gles2/shaders/cube_to_dp.glsl | 24 | ||||
-rw-r--r-- | drivers/gles2/shaders/cubemap_filter.glsl | 321 | ||||
-rw-r--r-- | drivers/gles2/shaders/scene.glsl | 2319 | ||||
-rw-r--r-- | drivers/gles2/shaders/stdlib.glsl | 45 | ||||
-rw-r--r-- | methods.py | 1 |
18 files changed, 6278 insertions, 2236 deletions
diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index fb150d6820..256d37186d 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/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. diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index 4eab8c6038..cda3ec79e7 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/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); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 5de1d5f1dc..335ad28670 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/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(); } diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index f7712be5d0..00a79db347 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/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,13 +661,1537 @@ void RasterizerSceneGLES2::gi_probe_instance_set_bounds(RID p_probe, const Vecto //////////////////////////// //////////////////////////// +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) { + + 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::_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; + } + } +} + +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::_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::_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) { @@ -223,6 +2202,100 @@ void RasterizerSceneGLES2::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_dra } 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() { diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index 110222f709..0ce7e9ae97 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/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); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index fc80436efb..468659ed80 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -32,14 +32,40 @@ #include "rasterizer_canvas_gles2.h" #include "rasterizer_scene_gles2.h" +#include "math/transform.h" + +#include "servers/visual/shader_language.h" + GLuint RasterizerStorageGLES2::system_fbo = 0; /* TEXTURE API */ -Ref<Image> RasterizerStorageGLES2::_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) { +#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + +#define _EXT_ETC1_RGB8_OES 0x8D64 + +#ifdef GLES_OVER_GL +#define _GL_HALF_FLOAT_OES 0x140B +#else +#define _GL_HALF_FLOAT_OES 0x8D61 +#endif + +void RasterizerStorageGLES2::bind_quad_array() const { + glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8); + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); +} + +Ref<Image> RasterizerStorageGLES2::_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) { r_gl_format = 0; Ref<Image> image = p_image; + r_compressed = false; bool need_decompress = false; @@ -98,9 +124,14 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_RF: { - ERR_EXPLAIN("R float texture not supported"); - ERR_FAIL_V(image); + if (!config.float_texture_supported) { + ERR_EXPLAIN("R float texture not supported"); + ERR_FAIL_V(image); + } + r_gl_internal_format = GL_ALPHA; + r_gl_format = GL_ALPHA; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RGF: { ERR_EXPLAIN("RG float texture not supported"); @@ -108,54 +139,87 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_RGBF: { + if (!config.float_texture_supported) { - ERR_EXPLAIN("RGB float texture not supported"); - ERR_FAIL_V(image); + ERR_EXPLAIN("RGB float texture not supported"); + ERR_FAIL_V(image); + } + + r_gl_internal_format = GL_RGB; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RGBAF: { + if (!config.float_texture_supported) { - ERR_EXPLAIN("RGBA float texture not supported"); - ERR_FAIL_V(image); + ERR_EXPLAIN("RGBA float texture not supported"); + ERR_FAIL_V(image); + } + + r_gl_internal_format = GL_RGBA; + r_gl_format = GL_RGBA; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RH: { - ERR_EXPLAIN("R half float texture not supported"); - ERR_FAIL_V(image); + need_decompress = true; } break; case Image::FORMAT_RGH: { - ERR_EXPLAIN("RG half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBH: { - ERR_EXPLAIN("RGB half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBAH: { - ERR_EXPLAIN("RGBA half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBE9995: { - ERR_EXPLAIN("RGBA float texture not supported"); - ERR_FAIL_V(image); + r_gl_internal_format = GL_RGB; + r_gl_format = GL_RGB; + r_gl_type = GL_UNSIGNED_BYTE; + + if (image.is_valid()) + + image = image->rgbe_to_srgb(); + + return image; } break; case Image::FORMAT_DXT1: { - need_decompress = true; + r_compressed = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + } else { + need_decompress = true; + } } break; case Image::FORMAT_DXT3: { - need_decompress = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_DXT5: { - need_decompress = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_RGTC_R: { @@ -198,7 +262,14 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_ETC: { - need_decompress = true; + if (config.etc1_supported) { + r_gl_internal_format = _EXT_ETC1_RGB8_OES; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_ETC2_R11: { @@ -280,6 +351,9 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ GLenum internal_format; GLenum type; + bool compressed = false; + bool srgb = false; + if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { p_flags &= ~VS::TEXTURE_FLAG_MIPMAPS; // no mipies for video } @@ -293,7 +367,7 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->stored_cube_sides = 0; texture->target = (p_flags & VS::TEXTURE_FLAG_CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; - _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, format, internal_format, type); + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, format, internal_format, type, compressed); texture->alloc_width = texture->width; texture->alloc_height = texture->height; @@ -304,6 +378,8 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->data_size = 0; texture->mipmaps = 1; + texture->compressed = compressed; + glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); @@ -328,13 +404,12 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p GLenum format; GLenum internal_format; bool compressed = false; - bool srgb; if (config.keep_original_textures && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { texture->images[p_cube_side] = p_image; } - Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type); + Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type, compressed); if (config.shrink_textures_x2 && (p_image->has_mipmaps() || !p_image->is_compressed()) && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { @@ -423,11 +498,21 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p int size, ofs; img->get_mipmap_offset_and_size(i, ofs, size); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { - glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int bw = w; + int bh = h; + + glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); } else { - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { + glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } } tsize += size; @@ -683,27 +768,171 @@ void RasterizerStorageGLES2::texture_set_force_redraw_if_visible(RID p_texture, } void RasterizerStorageGLES2::texture_set_detect_3d_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_3d = p_callback; + texture->detect_3d_ud = p_userdata; } void RasterizerStorageGLES2::texture_set_detect_srgb_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_srgb = p_callback; + texture->detect_srgb_ud = p_userdata; } void RasterizerStorageGLES2::texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_normal = p_callback; + texture->detect_normal_ud = p_userdata; } RID RasterizerStorageGLES2::texture_create_radiance_cubemap(RID p_source, int p_resolution) const { - // TODO + return RID(); } RID RasterizerStorageGLES2::sky_create() { - return RID(); + Sky *sky = memnew(Sky); + sky->radiance = 0; + return sky_owner.make_rid(sky); } void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size) { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND(!sky); + + if (sky->panorama.is_valid()) { + sky->panorama = RID(); + glDeleteTextures(1, &sky->radiance); + sky->radiance = 0; + } + + sky->panorama = p_panorama; + if (!sky->panorama.is_valid()) { + return; // the panorama was cleared + } + + Texture *texture = texture_owner.getornull(sky->panorama); + if (!texture) { + sky->panorama = RID(); + ERR_FAIL_COND(!texture); + } + + // glBindVertexArray(0) and more + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //need this for proper sampling + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // New cubemap that will hold the mipmaps with different roughness values + glActiveTexture(GL_TEXTURE2); + glGenTextures(1, &sky->radiance); + glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance); + + // Now we create a new framebuffer. The new cubemap images will be used as + // attachements for it, so we can fill them by issuing draw calls. + GLuint tmp_fb; + + glGenFramebuffers(1, &tmp_fb); + glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb); + + int size = p_radiance_size; + + int lod = 0; + + shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, texture->target == GL_TEXTURE_2D); + + shaders.cubemap_filter.bind(); + + int mipmaps = 6; + + int mm_level = mipmaps; + + GLenum internal_format = GL_RGBA; + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_BYTE; // This is suboptimal... TODO other format for FBO? + + // Set the initial (empty) mipmaps + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glTexImage2D(_cube_side_enum[i], lod, internal_format, size, size, 0, format, type, NULL); + } + + lod++; + + size >>= 1; + } + + lod = 0; + mm_level = mipmaps; + + size = p_radiance_size; + + // now render to the framebuffer, mipmap level for mipmap level + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], sky->radiance, lod); + + glViewport(0, 0, size, size); + + bind_quad_array(); + + shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i); + + float roughness = mm_level ? lod / (float)(mipmaps - 1) : 1; + shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + size >>= 1; + + mm_level--; + + lod++; + } + + // restore ranges + + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Framebuffer did its job. thank mr framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); + glDeleteFramebuffers(1, &tmp_fb); } /* SHADER API */ @@ -755,6 +984,8 @@ void RasterizerStorageGLES2::shader_set_code(RID p_shader, const String &p_code) if (mode == VS::SHADER_CANVAS_ITEM) { shader->shader = &canvas->state.canvas_shader; + } else if (mode == VS::SHADER_SPATIAL) { + shader->shader = &scene->state.scene_shader; } else { return; } @@ -815,6 +1046,62 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { actions->uniforms = &p_shader->uniforms; } break; + case VS::SHADER_SPATIAL: { + p_shader->spatial.blend_mode = Shader::Spatial::BLEND_MODE_MIX; + p_shader->spatial.depth_draw_mode = Shader::Spatial::DEPTH_DRAW_OPAQUE; + p_shader->spatial.cull_mode = Shader::Spatial::CULL_MODE_BACK; + p_shader->spatial.uses_alpha = false; + p_shader->spatial.uses_alpha_scissor = false; + p_shader->spatial.uses_discard = false; + p_shader->spatial.unshaded = false; + p_shader->spatial.no_depth_test = false; + p_shader->spatial.uses_sss = false; + p_shader->spatial.uses_time = false; + p_shader->spatial.uses_vertex_lighting = false; + p_shader->spatial.uses_screen_texture = false; + p_shader->spatial.uses_depth_texture = false; + p_shader->spatial.uses_vertex = false; + p_shader->spatial.writes_modelview_or_projection = false; + p_shader->spatial.uses_world_coordinates = false; + + shaders.actions_scene.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_ADD); + shaders.actions_scene.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MIX); + shaders.actions_scene.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_SUB); + shaders.actions_scene.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MUL); + + shaders.actions_scene.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_OPAQUE); + shaders.actions_scene.render_mode_values["depth_draw_always"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALWAYS); + shaders.actions_scene.render_mode_values["depth_draw_never"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_NEVER); + shaders.actions_scene.render_mode_values["depth_draw_alpha_prepass"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS); + + shaders.actions_scene.render_mode_values["cull_front"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_FRONT); + shaders.actions_scene.render_mode_values["cull_back"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_BACK); + shaders.actions_scene.render_mode_values["cull_disabled"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_DISABLED); + + shaders.actions_scene.render_mode_flags["unshaded"] = &p_shader->spatial.unshaded; + shaders.actions_scene.render_mode_flags["depth_test_disable"] = &p_shader->spatial.no_depth_test; + + shaders.actions_scene.render_mode_flags["vertex_lighting"] = &p_shader->spatial.uses_vertex_lighting; + + shaders.actions_scene.render_mode_flags["world_vertex_coords"] = &p_shader->spatial.uses_world_coordinates; + + shaders.actions_scene.usage_flag_pointers["ALPHA"] = &p_shader->spatial.uses_alpha; + shaders.actions_scene.usage_flag_pointers["ALPHA_SCISSOR"] = &p_shader->spatial.uses_alpha_scissor; + + shaders.actions_scene.usage_flag_pointers["SSS_STRENGTH"] = &p_shader->spatial.uses_sss; + shaders.actions_scene.usage_flag_pointers["DISCARD"] = &p_shader->spatial.uses_discard; + shaders.actions_scene.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->spatial.uses_screen_texture; + shaders.actions_scene.usage_flag_pointers["DEPTH_TEXTURE"] = &p_shader->spatial.uses_depth_texture; + shaders.actions_scene.usage_flag_pointers["TIME"] = &p_shader->spatial.uses_time; + + shaders.actions_scene.write_flag_pointers["MODELVIEW_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["PROJECTION_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["VERTEX"] = &p_shader->spatial.uses_vertex; + + actions = &shaders.actions_scene; + actions->uniforms = &p_shader->uniforms; + } break; + default: { return; } break; @@ -832,6 +1119,11 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time; + p_shader->shader->set_custom_shader(p_shader->custom_code_id); + p_shader->shader->bind(); + + // cache uniform locations + for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) { _material_make_dirty(E->self()); } @@ -919,6 +1211,10 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn pi.type = Variant::POOL_INT_ARRAY; } break; + case ShaderLanguage::TYPE_FLOAT: { + pi.type = Variant::REAL; + } break; + case ShaderLanguage::TYPE_VEC2: { pi.type = Variant::VECTOR2; } break; @@ -1071,183 +1367,1277 @@ Variant RasterizerStorageGLES2::material_get_param(RID p_material, const StringN } void RasterizerStorageGLES2::material_set_line_width(RID p_material, float p_width) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + material->line_width = p_width; } void RasterizerStorageGLES2::material_set_next_pass(RID p_material, RID p_next_material) { + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + + material->next_pass = p_next_material; } bool RasterizerStorageGLES2::material_is_animated(RID p_material) { - return false; + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool animated = material->is_animated_cache; + if (!animated && material->next_pass.is_valid()) { + animated = material_is_animated(material->next_pass); + } + return animated; } bool RasterizerStorageGLES2::material_casts_shadows(RID p_material) { - return false; + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool casts_shadows = material->can_cast_shadow_cache; + + if (!casts_shadows && material->next_pass.is_valid()) { + casts_shadows = material_casts_shadows(material->next_pass); + } + + return casts_shadows; } void RasterizerStorageGLES2::material_add_instance_owner(RID p_material, RasterizerScene::InstanceBase *p_instance) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<RasterizerScene::InstanceBase *, int>::Element *E = material->instance_owners.find(p_instance); + if (E) { + E->get()++; + } else { + material->instance_owners[p_instance] = 1; + } } void RasterizerStorageGLES2::material_remove_instance_owner(RID p_material, RasterizerScene::InstanceBase *p_instance) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<RasterizerScene::InstanceBase *, int>::Element *E = material->instance_owners.find(p_instance); + ERR_FAIL_COND(!E); + + E->get()--; + + if (E->get() == 0) { + material->instance_owners.erase(E); + } } void RasterizerStorageGLES2::material_set_render_priority(RID p_material, int priority) { + ERR_FAIL_COND(priority < VS::MATERIAL_RENDER_PRIORITY_MIN); + ERR_FAIL_COND(priority > VS::MATERIAL_RENDER_PRIORITY_MAX); + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + + material->render_priority = priority; +} + +void RasterizerStorageGLES2::_update_material(Material *p_material) { + if (p_material->dirty_list.in_list()) { + _material_dirty_list.remove(&p_material->dirty_list); + } + + if (p_material->shader && p_material->shader->dirty_list.in_list()) { + _update_shader(p_material->shader); + } + + if (p_material->shader && !p_material->shader->valid) { + return; + } + + { + bool can_cast_shadow = false; + bool is_animated = false; + + if (p_material->shader && p_material->shader->mode == VS::SHADER_SPATIAL) { + + if (p_material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX && + (!p_material->shader->spatial.uses_alpha || (p_material->shader->spatial.uses_alpha && p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS))) { + can_cast_shadow = true; + } + + if (p_material->shader->spatial.uses_discard && p_material->shader->uses_fragment_time) { + is_animated = true; + } + + if (p_material->shader->spatial.uses_vertex && p_material->shader->uses_vertex_time) { + is_animated = true; + } + + if (can_cast_shadow != p_material->can_cast_shadow_cache || is_animated != p_material->is_animated_cache) { + p_material->can_cast_shadow_cache = can_cast_shadow; + p_material->is_animated_cache = is_animated; + + for (Map<Geometry *, int>::Element *E = p_material->geometry_owners.front(); E; E = E->next()) { + E->key()->material_changed_notify(); + } + + for (Map<RasterizerScene::InstanceBase *, int>::Element *E = p_material->instance_owners.front(); E; E = E->next()) { + E->key()->base_material_changed(); + } + } + } + } + + // uniforms and other thigns will be set in the use_material method in ShaderGLES2 + + if (p_material->shader && p_material->shader->texture_count > 0) { + + p_material->textures.resize(p_material->shader->texture_count); + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_material->shader->uniforms.front(); E; E = E->next()) { + if (E->get().texture_order < 0) + continue; // not a texture, does not go here + + RID texture; + + Map<StringName, Variant>::Element *V = p_material->params.find(E->key()); + + if (V) { + texture = V->get(); + } + + if (!texture.is_valid()) { + Map<StringName, RID>::Element *W = p_material->shader->default_textures.find(E->key()); + + if (W) { + texture = W->get(); + } + } + + p_material->textures.write[E->get().texture_order] = Pair<StringName, RID>(E->key(), texture); + } + } else { + p_material->textures.clear(); + } +} + +void RasterizerStorageGLES2::_material_add_geometry(RID p_material, Geometry *p_geometry) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + + if (I) { + I->get()++; + } else { + material->geometry_owners[p_geometry] = 1; + } +} + +void RasterizerStorageGLES2::_material_remove_geometry(RID p_material, Geometry *p_geometry) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + ERR_FAIL_COND(!I); + + I->get()--; + + if (I->get() == 0) { + material->geometry_owners.erase(I); + } } void RasterizerStorageGLES2::update_dirty_materials() { + while (_material_dirty_list.first()) { + + Material *material = _material_dirty_list.first()->self(); + _update_material(material); + } } /* MESH API */ RID RasterizerStorageGLES2::mesh_create() { - return RID(); + + Mesh *mesh = memnew(Mesh); + + return mesh_owner.make_rid(mesh); } void RasterizerStorageGLES2::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, const Vector<AABB> &p_bone_aabbs) { + PoolVector<uint8_t> array = p_array; + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(!(p_format & VS::ARRAY_FORMAT_VERTEX)); + + //must have index and bones, both. + { + uint32_t bones_weight = VS::ARRAY_FORMAT_BONES | VS::ARRAY_FORMAT_WEIGHTS; + ERR_EXPLAIN("Array must have both bones and weights in format or none."); + ERR_FAIL_COND((p_format & bones_weight) && (p_format & bones_weight) != bones_weight); + } + + //bool has_morph = p_blend_shapes.size(); + + Surface::Attrib attribs[VS::ARRAY_MAX]; + + int stride = 0; + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + + attribs[i].index = i; + + if (!(p_format & (1 << i))) { + attribs[i].enabled = false; + attribs[i].integer = false; + continue; + } + + attribs[i].enabled = true; + attribs[i].offset = stride; + attribs[i].integer = false; + + switch (i) { + + case VS::ARRAY_VERTEX: { + + if (p_format & VS::ARRAY_FLAG_USE_2D_VERTICES) { + attribs[i].size = 2; + } else { + attribs[i].size = (p_format & VS::ARRAY_COMPRESS_VERTEX) ? 4 : 3; + } + + if (p_format & VS::ARRAY_COMPRESS_VERTEX) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += attribs[i].size * 2; + } else { + attribs[i].type = GL_FLOAT; + stride += attribs[i].size * 4; + } + + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_NORMAL: { + + attribs[i].size = 3; + + if (p_format & VS::ARRAY_COMPRESS_NORMAL) { + attribs[i].type = GL_BYTE; + stride += 4; //pad extra byte + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 12; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_TANGENT: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_TANGENT) { + attribs[i].type = GL_BYTE; + stride += 4; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_COLOR: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_COLOR) { + attribs[i].type = GL_UNSIGNED_BYTE; + stride += 4; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_TEX_UV: { + + attribs[i].size = 2; + + if (p_format & VS::ARRAY_COMPRESS_TEX_UV) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += 4; + } else { + attribs[i].type = GL_FLOAT; + stride += 8; + } + + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_TEX_UV2: { + + attribs[i].size = 2; + + if (p_format & VS::ARRAY_COMPRESS_TEX_UV2) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += 4; + } else { + attribs[i].type = GL_FLOAT; + stride += 8; + } + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_BONES: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_FLAG_USE_16_BIT_BONES) { + attribs[i].type = GL_UNSIGNED_SHORT; + stride += 8; + } else { + attribs[i].type = GL_UNSIGNED_BYTE; + stride += 4; + } + + attribs[i].normalized = GL_FALSE; + attribs[i].integer = true; + + } break; + case VS::ARRAY_WEIGHTS: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_WEIGHTS) { + + attribs[i].type = GL_UNSIGNED_SHORT; + stride += 8; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_INDEX: { + + attribs[i].size = 1; + + if (p_vertex_count >= (1 << 16)) { + attribs[i].type = GL_UNSIGNED_INT; + attribs[i].stride = 4; + } else { + attribs[i].type = GL_UNSIGNED_SHORT; + attribs[i].stride = 2; + } + + attribs[i].normalized = GL_FALSE; + + } break; + } + } + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + attribs[i].stride = stride; + } + + //validate sizes + + int array_size = stride * p_vertex_count; + int index_array_size = 0; + if (array.size() != array_size && array.size() + p_vertex_count * 2 == array_size) { + //old format, convert + array = PoolVector<uint8_t>(); + + array.resize(p_array.size() + p_vertex_count * 2); + + PoolVector<uint8_t>::Write w = array.write(); + PoolVector<uint8_t>::Read r = p_array.read(); + + uint16_t *w16 = (uint16_t *)w.ptr(); + const uint16_t *r16 = (uint16_t *)r.ptr(); + + uint16_t one = Math::make_half_float(1); + + for (int i = 0; i < p_vertex_count; i++) { + + *w16++ = *r16++; + *w16++ = *r16++; + *w16++ = *r16++; + *w16++ = one; + for (int j = 0; j < (stride / 2) - 4; j++) { + *w16++ = *r16++; + } + } + } + + ERR_FAIL_COND(array.size() != array_size); + + if (p_format & VS::ARRAY_FORMAT_INDEX) { + + index_array_size = attribs[VS::ARRAY_INDEX].stride * p_index_count; + } + + ERR_FAIL_COND(p_index_array.size() != index_array_size); + + ERR_FAIL_COND(p_blend_shapes.size() != mesh->blend_shape_count); + + for (int i = 0; i < p_blend_shapes.size(); i++) { + ERR_FAIL_COND(p_blend_shapes[i].size() != array_size); + } + + // all valid, create stuff + + Surface *surface = memnew(Surface); + + surface->active = true; + surface->array_len = p_vertex_count; + surface->index_array_len = p_index_count; + surface->array_byte_size = array.size(); + surface->index_array_byte_size = p_index_array.size(); + surface->primitive = p_primitive; + surface->mesh = mesh; + surface->format = p_format; + surface->skeleton_bone_aabb = p_bone_aabbs; + surface->skeleton_bone_used.resize(surface->skeleton_bone_aabb.size()); + + surface->aabb = p_aabb; + surface->max_bone = p_bone_aabbs.size(); + + surface->data = array; + surface->index_data = p_index_array; + + surface->total_data_size += surface->array_byte_size + surface->index_array_byte_size; + + for (int i = 0; i < surface->skeleton_bone_used.size(); i++) { + surface->skeleton_bone_used.write[i] = surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0; + } + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + surface->attribs[i] = attribs[i]; + } + + // Okay, now the OpenGL stuff, wheeeeey \o/ + { + PoolVector<uint8_t>::Read vr = array.read(); + + glGenBuffers(1, &surface->vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id); + glBufferData(GL_ARRAY_BUFFER, array_size, vr.ptr(), (p_format & VS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (p_format & VS::ARRAY_FORMAT_INDEX) { + PoolVector<uint8_t>::Read ir = p_index_array.read(); + + glGenBuffers(1, &surface->index_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_array_size, ir.ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // TODO generate wireframes + } + + { + // blend shapes + + for (int i = 0; i < p_blend_shapes.size(); i++) { + + Surface::BlendShape mt; + + PoolVector<uint8_t>::Read vr = p_blend_shapes[i].read(); + + surface->total_data_size += array_size; + + glGenBuffers(1, &mt.vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, mt.vertex_id); + glBufferData(GL_ARRAY_BUFFER, array_size, vr.ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + surface->blend_shapes.push_back(mt); + } + } + + mesh->surfaces.push_back(surface); + mesh->instance_change_notify(); + + info.vertex_mem += surface->total_data_size; } void RasterizerStorageGLES2::mesh_set_blend_shape_count(RID p_mesh, int p_amount) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(mesh->surfaces.size() != 0); + ERR_FAIL_COND(p_amount < 0); + + mesh->blend_shape_count = p_amount; } int RasterizerStorageGLES2::mesh_get_blend_shape_count(RID p_mesh) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + + return mesh->blend_shape_count; } void RasterizerStorageGLES2::mesh_set_blend_shape_mode(RID p_mesh, VS::BlendShapeMode p_mode) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + mesh->blend_shape_mode = p_mode; } VS::BlendShapeMode RasterizerStorageGLES2::mesh_get_blend_shape_mode(RID p_mesh) const { - return VS::BLEND_SHAPE_MODE_NORMALIZED; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, VS::BLEND_SHAPE_MODE_NORMALIZED); + + return mesh->blend_shape_mode; } void RasterizerStorageGLES2::mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const PoolVector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + int total_size = p_data.size(); + ERR_FAIL_COND(p_offset + total_size > mesh->surfaces[p_surface]->array_byte_size); + + PoolVector<uint8_t>::Read r = p_data.read(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_id); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, total_size, r.ptr()); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } void RasterizerStorageGLES2::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + if (mesh->surfaces[p_surface]->material == p_material) + return; + + if (mesh->surfaces[p_surface]->material.is_valid()) { + _material_remove_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]); + } + + mesh->surfaces[p_surface]->material = p_material; + + if (mesh->surfaces[p_surface]->material.is_valid()) { + _material_add_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]); + } + + mesh->instance_material_change_notify(); } RID RasterizerStorageGLES2::mesh_surface_get_material(RID p_mesh, int p_surface) const { - return RID(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, RID()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), RID()); + + return mesh->surfaces[p_surface]->material; } int RasterizerStorageGLES2::mesh_surface_get_array_len(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->array_len; } int RasterizerStorageGLES2::mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->index_array_len; } PoolVector<uint8_t> RasterizerStorageGLES2::mesh_surface_get_array(RID p_mesh, int p_surface) const { - return PoolVector<uint8_t>(); + + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, PoolVector<uint8_t>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), PoolVector<uint8_t>()); + + Surface *surface = mesh->surfaces[p_surface]; + + return surface->data; } PoolVector<uint8_t> RasterizerStorageGLES2::mesh_surface_get_index_array(RID p_mesh, int p_surface) const { - return PoolVector<uint8_t>(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, PoolVector<uint8_t>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), PoolVector<uint8_t>()); + + Surface *surface = mesh->surfaces[p_surface]; + + return surface->index_data; } uint32_t RasterizerStorageGLES2::mesh_surface_get_format(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->format; } VS::PrimitiveType RasterizerStorageGLES2::mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const { - return VS::PRIMITIVE_TRIANGLES; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, VS::PRIMITIVE_MAX); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), VS::PRIMITIVE_MAX); + + return mesh->surfaces[p_surface]->primitive; } AABB RasterizerStorageGLES2::mesh_surface_get_aabb(RID p_mesh, int p_surface) const { - return AABB(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), AABB()); + + return mesh->surfaces[p_surface]->aabb; } Vector<PoolVector<uint8_t> > RasterizerStorageGLES2::mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const { + WARN_PRINT("GLES2 mesh_surface_get_blend_shapes is not implemented"); return Vector<PoolVector<uint8_t> >(); } Vector<AABB> RasterizerStorageGLES2::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const { - return Vector<AABB>(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, Vector<AABB>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector<AABB>()); + + return mesh->surfaces[p_surface]->skeleton_bone_aabb; } void RasterizerStorageGLES2::mesh_remove_surface(RID p_mesh, int p_surface) { + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + Surface *surface = mesh->surfaces[p_surface]; + + if (surface->material.is_valid()) { + // TODO _material_remove_geometry(surface->material, mesh->surfaces[p_surface]); + } + + glDeleteBuffers(1, &surface->vertex_id); + if (surface->index_id) { + glDeleteBuffers(1, &surface->index_id); + } + + for (int i = 0; i < surface->blend_shapes.size(); i++) { + glDeleteBuffers(1, &surface->blend_shapes[i].vertex_id); + } + + info.vertex_mem -= surface->total_data_size; + + mesh->instance_material_change_notify(); + + memdelete(surface); + + mesh->surfaces.remove(p_surface); + + mesh->instance_change_notify(); } int RasterizerStorageGLES2::mesh_get_surface_count(RID p_mesh) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + return mesh->surfaces.size(); } void RasterizerStorageGLES2::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + mesh->custom_aabb = p_aabb; } AABB RasterizerStorageGLES2::mesh_get_custom_aabb(RID p_mesh) const { - return AABB(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + return mesh->custom_aabb; } AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const { - return AABB(); + Mesh *mesh = mesh_owner.get(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + if (mesh->custom_aabb != AABB()) + return mesh->custom_aabb; + + // TODO handle skeletons + + AABB aabb; + + if (mesh->surfaces.size() >= 1) { + aabb = mesh->surfaces[0]->aabb; + } + + for (int i = 0; i < mesh->surfaces.size(); i++) { + aabb.merge_with(mesh->surfaces[i]->aabb); + } + + return aabb; } void RasterizerStorageGLES2::mesh_clear(RID p_mesh) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + while (mesh->surfaces.size()) { + mesh_remove_surface(p_mesh, 0); + } } /* MULTIMESH API */ RID RasterizerStorageGLES2::multimesh_create() { - return RID(); + MultiMesh *multimesh = memnew(MultiMesh); + return multimesh_owner.make_rid(multimesh); } void RasterizerStorageGLES2::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->size == p_instances && multimesh->transform_format == p_transform_format && multimesh->color_format == p_color_format && multimesh->custom_data_format == p_data) { + return; + } + + multimesh->size = p_instances; + + multimesh->color_format = p_color_format; + multimesh->transform_format = p_transform_format; + multimesh->custom_data_format = p_data; + + if (multimesh->size) { + multimesh->data.resize(0); + } + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + multimesh->xform_floats = 8; + } else { + multimesh->xform_floats = 12; + } + + if (multimesh->color_format == VS::MULTIMESH_COLOR_NONE) { + multimesh->color_floats = 0; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + multimesh->color_floats = 1; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + multimesh->color_floats = 4; + } + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE) { + multimesh->custom_data_floats = 0; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + multimesh->custom_data_floats = 1; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + multimesh->custom_data_floats = 4; + } + + int format_floats = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + + multimesh->data.resize(format_floats * p_instances); + + for (int i = 0; i < p_instances * format_floats; i += format_floats) { + int color_from = 0; + int custom_data_from = 0; + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + color_from = 8; + custom_data_from = 8; + } else { + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + multimesh->data.write[i + 8] = 0.0; + multimesh->data.write[i + 9] = 0.0; + multimesh->data.write[i + 10] = 1.0; + multimesh->data.write[i + 11] = 0.0; + color_from = 12; + custom_data_from = 12; + } + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colu = 0xFFFFFFFF; + multimesh->data.write[i + color_from + 0] = cu.colf; + custom_data_from = color_from + 1; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + multimesh->data.write[i + color_from + 0] = 1.0; + multimesh->data.write[i + color_from + 1] = 1.0; + multimesh->data.write[i + color_from + 2] = 1.0; + multimesh->data.write[i + color_from + 3] = 1.0; + custom_data_from = color_from + 4; + } + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colu = 0; + multimesh->data.write[i + custom_data_from + 0] = cu.colf; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + multimesh->data.write[i + custom_data_from + 0] = 0.0; + multimesh->data.write[i + custom_data_from + 1] = 0.0; + multimesh->data.write[i + custom_data_from + 2] = 0.0; + multimesh->data.write[i + custom_data_from + 3] = 0.0; + } + } + + multimesh->dirty_aabb = true; + multimesh->dirty_data = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } int RasterizerStorageGLES2::multimesh_get_instance_count(RID p_multimesh) const { - return 0; + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, 0); + + return multimesh->size; } void RasterizerStorageGLES2::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.remove(&multimesh->mesh_list); + } + } + + multimesh->mesh = p_mesh; + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.add(&multimesh->mesh_list); + } + } + + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D); + + int stride = multimesh->color_floats + multimesh->custom_data_floats + multimesh->xform_floats; + + float *dataptr = &multimesh->data.write[stride * p_index]; + + dataptr[0] = p_transform.basis.elements[0][0]; + dataptr[1] = p_transform.basis.elements[0][1]; + dataptr[2] = p_transform.basis.elements[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.elements[1][0]; + dataptr[5] = p_transform.basis.elements[1][1]; + dataptr[6] = p_transform.basis.elements[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.elements[2][0]; + dataptr[9] = p_transform.basis.elements[2][1]; + dataptr[10] = p_transform.basis.elements[2][2]; + dataptr[11] = p_transform.origin.z; + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + dataptr[0] = p_transform.elements[0][0]; + dataptr[1] = p_transform.elements[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.elements[2][0]; + dataptr[4] = p_transform.elements[0][1]; + dataptr[5] = p_transform.elements[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.elements[2][1]; + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->color_format == VS::MULTIMESH_COLOR_NONE); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + + uint8_t *data8 = (uint8_t *)dataptr; + data8[0] = CLAMP(p_color.r * 255.0, 0, 255); + data8[1] = CLAMP(p_color.g * 255.0, 0, 255); + data8[2] = CLAMP(p_color.b * 255.0, 0, 255); + data8[3] = CLAMP(p_color.a * 255.0, 0, 255); + + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + dataptr[0] = p_color.r; + dataptr[1] = p_color.g; + dataptr[2] = p_color.b; + dataptr[3] = p_color.a; + } + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } -void RasterizerStorageGLES2::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +void RasterizerStorageGLES2::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_custom_data) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + + uint8_t *data8 = (uint8_t *)dataptr; + data8[0] = CLAMP(p_custom_data.r * 255.0, 0, 255); + data8[1] = CLAMP(p_custom_data.g * 255.0, 0, 255); + data8[2] = CLAMP(p_custom_data.b * 255.0, 0, 255); + data8[3] = CLAMP(p_custom_data.a * 255.0, 0, 255); + + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + dataptr[0] = p_custom_data.r; + dataptr[1] = p_custom_data.g; + dataptr[2] = p_custom_data.b; + dataptr[3] = p_custom_data.a; + } + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } RID RasterizerStorageGLES2::multimesh_get_mesh(RID p_multimesh) const { - return RID(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, RID()); + + return multimesh->mesh; } Transform RasterizerStorageGLES2::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { - return Transform(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform()); + ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D, Transform()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + Transform xform; + + xform.basis.elements[0][0] = dataptr[0]; + xform.basis.elements[0][1] = dataptr[1]; + xform.basis.elements[0][2] = dataptr[2]; + xform.origin.x = dataptr[3]; + xform.basis.elements[1][0] = dataptr[4]; + xform.basis.elements[1][1] = dataptr[5]; + xform.basis.elements[1][2] = dataptr[6]; + xform.origin.y = dataptr[7]; + xform.basis.elements[2][0] = dataptr[8]; + xform.basis.elements[2][1] = dataptr[9]; + xform.basis.elements[2][2] = dataptr[10]; + xform.origin.z = dataptr[11]; + + return xform; } Transform2D RasterizerStorageGLES2::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { - return Transform2D(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform2D()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform2D()); + ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D, Transform2D()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + Transform2D xform; + + xform.elements[0][0] = dataptr[0]; + xform.elements[1][0] = dataptr[1]; + xform.elements[2][0] = dataptr[3]; + xform.elements[0][1] = dataptr[4]; + xform.elements[1][1] = dataptr[5]; + xform.elements[2][1] = dataptr[7]; + + return xform; } Color RasterizerStorageGLES2::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Color()); + ERR_FAIL_COND_V(multimesh->color_format == VS::MULTIMESH_COLOR_NONE, Color()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colf = dataptr[0]; + + return Color::hex(BSWAP32(cu.colu)); + + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + Color c; + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + + return c; + } + return Color(); } Color RasterizerStorageGLES2::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Color()); + ERR_FAIL_COND_V(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE, Color()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colf = dataptr[0]; + + return Color::hex(BSWAP32(cu.colu)); + + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + Color c; + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + + return c; + } + return Color(); } void RasterizerStorageGLES2::multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + int dsize = multimesh->data.size(); + + ERR_FAIL_COND(dsize != p_array.size()); + + PoolVector<float>::Read r = p_array.read(); + copymem(multimesh->data.ptrw(), r.ptr(), dsize * sizeof(float)); + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + multimesh->visible_instances = p_visible; } int RasterizerStorageGLES2::multimesh_get_visible_instances(RID p_multimesh) const { - return 0; + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, -1); + + return multimesh->visible_instances; } AABB RasterizerStorageGLES2::multimesh_get_aabb(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, AABB()); - return AABB(); + const_cast<RasterizerStorageGLES2 *>(this)->update_dirty_multimeshes(); + + return multimesh->aabb; } void RasterizerStorageGLES2::update_dirty_multimeshes() { + + while (multimesh_update_list.first()) { + + MultiMesh *multimesh = multimesh_update_list.first()->self(); + + if (multimesh->size && multimesh->dirty_aabb) { + + AABB mesh_aabb; + + if (multimesh->mesh.is_valid()) { + mesh_aabb = mesh_get_aabb(multimesh->mesh, RID()); + } else { + mesh_aabb.size += Vector3(0.001, 0.001, 0.001); + } + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + int count = multimesh->data.size(); + float *data = multimesh->data.ptrw(); + + AABB aabb; + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + + for (int i = 0; i < count; i += stride) { + + float *dataptr = &data[i]; + + Transform xform; + xform.basis[0][0] = dataptr[0]; + xform.basis[0][1] = dataptr[1]; + xform.origin[0] = dataptr[3]; + xform.basis[1][0] = dataptr[4]; + xform.basis[1][1] = dataptr[5]; + xform.origin[1] = dataptr[7]; + + AABB laabb = xform.xform(mesh_aabb); + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + + } else { + + for (int i = 0; i < count; i += stride) { + + float *dataptr = &data[i]; + + Transform xform; + xform.basis.elements[0][0] = dataptr[0]; + xform.basis.elements[0][1] = dataptr[1]; + xform.basis.elements[0][2] = dataptr[2]; + xform.origin.x = dataptr[3]; + xform.basis.elements[1][0] = dataptr[4]; + xform.basis.elements[1][1] = dataptr[5]; + xform.basis.elements[1][2] = dataptr[6]; + xform.origin.y = dataptr[7]; + xform.basis.elements[2][0] = dataptr[8]; + xform.basis.elements[2][1] = dataptr[9]; + xform.basis.elements[2][2] = dataptr[10]; + xform.origin.z = dataptr[11]; + + AABB laabb = xform.xform(mesh_aabb); + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + } + + multimesh->aabb = aabb; + } + + multimesh->dirty_aabb = false; + multimesh->dirty_data = false; + + multimesh->instance_change_notify(); + + multimesh_update_list.remove(multimesh_update_list.first()); + } } /* IMMEDIATE API */ @@ -1297,119 +2687,453 @@ RID RasterizerStorageGLES2::immediate_get_material(RID p_immediate) const { /* SKELETON API */ RID RasterizerStorageGLES2::skeleton_create() { - return RID(); + + Skeleton *skeleton = memnew(Skeleton); + + return skeleton_owner.make_rid(skeleton); } void RasterizerStorageGLES2::skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_COND(p_bones < 0); + + if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) { + return; + } + + skeleton->size = p_bones; + skeleton->use_2d = p_2d_skeleton; + + // TODO use float texture for vertex shader + if (config.float_texture_supported) { + glGenTextures(1, &skeleton->tex_id); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, skeleton->tex_id); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, p_bones * 3, 1, 0, GL_RGB, GL_FLOAT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + } + if (skeleton->use_2d) { + skeleton->bone_data.resize(p_bones * 4 * 2); + } else { + skeleton->bone_data.resize(p_bones * 4 * 3); + } } int RasterizerStorageGLES2::skeleton_get_bone_count(RID p_skeleton) const { - return 0; + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + + return skeleton->size; } void RasterizerStorageGLES2::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) { + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(skeleton->use_2d); + + float *bone_data = skeleton->bone_data.ptrw(); + + int base_offset = p_bone * 4 * 3; + + bone_data[base_offset + 0] = p_transform.basis[0].x; + bone_data[base_offset + 1] = p_transform.basis[0].y; + bone_data[base_offset + 2] = p_transform.basis[0].z; + bone_data[base_offset + 3] = p_transform.origin.x; + + bone_data[base_offset + 4] = p_transform.basis[1].x; + bone_data[base_offset + 5] = p_transform.basis[1].y; + bone_data[base_offset + 6] = p_transform.basis[1].z; + bone_data[base_offset + 7] = p_transform.origin.y; + + bone_data[base_offset + 8] = p_transform.basis[2].x; + bone_data[base_offset + 9] = p_transform.basis[2].y; + bone_data[base_offset + 10] = p_transform.basis[2].z; + bone_data[base_offset + 11] = p_transform.origin.z; + + if (!skeleton->update_list.in_list()) { + skeleton_update_list.add(&skeleton->update_list); + } } Transform RasterizerStorageGLES2::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { - return Transform(); + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, Transform()); + + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform()); + ERR_FAIL_COND_V(skeleton->use_2d, Transform()); + + const float *bone_data = skeleton->bone_data.ptr(); + + Transform ret; + + int base_offset = p_bone * 4 * 3; + + ret.basis[0].x = bone_data[base_offset + 0]; + ret.basis[0].y = bone_data[base_offset + 1]; + ret.basis[0].z = bone_data[base_offset + 2]; + ret.origin.x = bone_data[base_offset + 3]; + + ret.basis[1].x = bone_data[base_offset + 4]; + ret.basis[1].y = bone_data[base_offset + 5]; + ret.basis[1].z = bone_data[base_offset + 6]; + ret.origin.y = bone_data[base_offset + 7]; + + ret.basis[2].x = bone_data[base_offset + 8]; + ret.basis[2].y = bone_data[base_offset + 9]; + ret.basis[2].z = bone_data[base_offset + 10]; + ret.origin.z = bone_data[base_offset + 11]; + + return ret; } void RasterizerStorageGLES2::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(!skeleton->use_2d); + + float *bone_data = skeleton->bone_data.ptrw(); + + int base_offset = p_bone * 4 * 2; + + bone_data[base_offset + 0] = p_transform[0][0]; + bone_data[base_offset + 1] = p_transform[1][0]; + bone_data[base_offset + 2] = 0; + bone_data[base_offset + 3] = p_transform[2][0]; + bone_data[base_offset + 4] = p_transform[0][1]; + bone_data[base_offset + 5] = p_transform[1][1]; + bone_data[base_offset + 6] = 0; + bone_data[base_offset + 7] = p_transform[2][1]; + + if (!skeleton->update_list.in_list()) { + skeleton_update_list.add(&skeleton->update_list); + } } Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { - return Transform2D(); + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, Transform2D()); + + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D()); + ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D()); + + const float *bone_data = skeleton->bone_data.ptr(); + + Transform2D ret; + + int base_offset = p_bone * 4 * 2; + + ret[0][0] = bone_data[base_offset + 0]; + ret[1][0] = bone_data[base_offset + 1]; + ret[2][0] = bone_data[base_offset + 3]; + ret[0][1] = bone_data[base_offset + 4]; + ret[1][1] = bone_data[base_offset + 5]; + ret[2][1] = bone_data[base_offset + 7]; + + return ret; } void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { } +void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size) { + + glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer); + + if (p_size > resources.skeleton_transform_buffer_size) { + // new requested buffer is bigger, so resizing the GPU buffer + + resources.skeleton_transform_buffer_size = p_size; + + glBufferData(GL_ARRAY_BUFFER, p_size * sizeof(float), p_data.read().ptr(), GL_DYNAMIC_DRAW); + } else { + glBufferSubData(GL_ARRAY_BUFFER, 0, p_size * sizeof(float), p_data.read().ptr()); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + void RasterizerStorageGLES2::update_dirty_skeletons() { + + if (!config.float_texture_supported) + return; + + glActiveTexture(GL_TEXTURE0); + + while (skeleton_update_list.first()) { + Skeleton *skeleton = skeleton_update_list.first()->self(); + + if (skeleton->size) { + glBindTexture(GL_TEXTURE_2D, skeleton->tex_id); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, skeleton->size * 3, 1, GL_RGBA, GL_FLOAT, skeleton->bone_data.ptr()); + } + + for (Set<RasterizerScene::InstanceBase *>::Element *E = skeleton->instances.front(); E; E = E->next()) { + E->get()->base_changed(); + } + + skeleton_update_list.remove(skeleton_update_list.first()); + } } /* Light API */ RID RasterizerStorageGLES2::light_create(VS::LightType p_type) { - return RID(); + + Light *light = memnew(Light); + + light->type = p_type; + + light->param[VS::LIGHT_PARAM_ENERGY] = 1.0; + light->param[VS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; + light->param[VS::LIGHT_PARAM_SPECULAR] = 0.5; + light->param[VS::LIGHT_PARAM_RANGE] = 1.0; + light->param[VS::LIGHT_PARAM_SPOT_ANGLE] = 45; + light->param[VS::LIGHT_PARAM_CONTACT_SHADOW_SIZE] = 45; + light->param[VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; + light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE] = 0.1; + + light->color = Color(1, 1, 1, 1); + light->shadow = false; + light->negative = false; + light->cull_mask = 0xFFFFFFFF; + light->directional_shadow_mode = VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + light->omni_shadow_mode = VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + light->omni_shadow_detail = VS::LIGHT_OMNI_SHADOW_DETAIL_VERTICAL; + light->directional_blend_splits = false; + light->directional_range_mode = VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; + light->reverse_cull = false; + light->version = 0; + + return light_owner.make_rid(light); } void RasterizerStorageGLES2::light_set_color(RID p_light, const Color &p_color) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->color = p_color; } void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param, float p_value) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX(p_param, VS::LIGHT_PARAM_MAX); + + switch (p_param) { + case VS::LIGHT_PARAM_RANGE: + case VS::LIGHT_PARAM_SPOT_ANGLE: + case VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE: + case VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET: + case VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: + case VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: + case VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: + case VS::LIGHT_PARAM_SHADOW_BIAS: { + light->version++; + light->instance_change_notify(); + } break; + } + + light->param[p_param] = p_value; } void RasterizerStorageGLES2::light_set_shadow(RID p_light, bool p_enabled) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->shadow = p_enabled; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_set_shadow_color(RID p_light, const Color &p_color) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->shadow_color = p_color; } void RasterizerStorageGLES2::light_set_projector(RID p_light, RID p_texture) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->projector = p_texture; } void RasterizerStorageGLES2::light_set_negative(RID p_light, bool p_enable) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->negative = p_enable; } void RasterizerStorageGLES2::light_set_cull_mask(RID p_light, uint32_t p_mask) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->cull_mask = p_mask; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->reverse_cull = p_enabled; } void RasterizerStorageGLES2::light_omni_set_shadow_mode(RID p_light, VS::LightOmniShadowMode p_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_mode = p_mode; + + light->version++; + light->instance_change_notify(); } VS::LightOmniShadowMode RasterizerStorageGLES2::light_omni_get_shadow_mode(RID p_light) { - return VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_OMNI_SHADOW_CUBE); + + return light->omni_shadow_mode; } void RasterizerStorageGLES2::light_omni_set_shadow_detail(RID p_light, VS::LightOmniShadowDetail p_detail) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_detail = p_detail; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_directional_set_shadow_mode(RID p_light, VS::LightDirectionalShadowMode p_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_shadow_mode = p_mode; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_directional_set_blend_splits(RID p_light, bool p_enable) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_blend_splits = p_enable; + + light->version++; + light->instance_change_notify(); } bool RasterizerStorageGLES2::light_directional_get_blend_splits(RID p_light) const { - return false; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); + return light->directional_blend_splits; } VS::LightDirectionalShadowMode RasterizerStorageGLES2::light_directional_get_shadow_mode(RID p_light) { - return VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); + return light->directional_shadow_mode; } void RasterizerStorageGLES2::light_directional_set_shadow_depth_range_mode(RID p_light, VS::LightDirectionalShadowDepthRangeMode p_range_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_range_mode = p_range_mode; } VS::LightDirectionalShadowDepthRangeMode RasterizerStorageGLES2::light_directional_get_shadow_depth_range_mode(RID p_light) const { - return VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE); + + return light->directional_range_mode; } VS::LightType RasterizerStorageGLES2::light_get_type(RID p_light) const { - return VS::LIGHT_DIRECTIONAL; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL); + + return light->type; } float RasterizerStorageGLES2::light_get_param(RID p_light, VS::LightParam p_param) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0.0); + ERR_FAIL_INDEX_V(p_param, VS::LIGHT_PARAM_MAX, 0.0); - return VS::LIGHT_DIRECTIONAL; + return light->param[p_param]; } Color RasterizerStorageGLES2::light_get_color(RID p_light) { - return Color(); + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, Color()); + + return light->color; } bool RasterizerStorageGLES2::light_has_shadow(RID p_light) const { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); - return VS::LIGHT_DIRECTIONAL; + return light->shadow; } uint64_t RasterizerStorageGLES2::light_get_version(RID p_light) const { - return 0; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->version; } AABB RasterizerStorageGLES2::light_get_aabb(RID p_light) const { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, AABB()); + + switch (light->type) { + + case VS::LIGHT_SPOT: { + float len = light->param[VS::LIGHT_PARAM_RANGE]; + float size = Math::tan(Math::deg2rad(light->param[VS::LIGHT_PARAM_SPOT_ANGLE])) * len; + return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); + } break; + + case VS::LIGHT_OMNI: { + float r = light->param[VS::LIGHT_PARAM_RANGE]; + return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2); + } break; + + case VS::LIGHT_DIRECTIONAL: { + return AABB(); + } break; + } + + ERR_FAIL_V(AABB()); return AABB(); } @@ -1794,6 +3518,8 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) { texture_set_flags(rt->texture, texture->flags); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // copy texscreen buffers { int w = rt->width; @@ -1867,6 +3593,7 @@ RID RasterizerStorageGLES2::render_target_create() { t->data_size = 0; t->total_data_size = 0; t->ignore_mipmaps = false; + t->compressed = false; t->mipmaps = 1; t->active = true; t->tex_id = 0; @@ -1963,14 +3690,197 @@ void RasterizerStorageGLES2::canvas_light_occluder_set_polylines(RID p_occluder, } VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const { + + if (mesh_owner.owns(p_rid)) { + return VS::INSTANCE_MESH; + } + if (light_owner.owns(p_rid)) { + return VS::INSTANCE_LIGHT; + } + if (multimesh_owner.owns(p_rid)) { + return VS::INSTANCE_MULTIMESH; + } + return VS::INSTANCE_NONE; } bool RasterizerStorageGLES2::free(RID p_rid) { - return false; + + if (render_target_owner.owns(p_rid)) { + + RenderTarget *rt = render_target_owner.getornull(p_rid); + _render_target_clear(rt); + + Texture *t = texture_owner.get(rt->texture); + texture_owner.free(rt->texture); + memdelete(t); + render_target_owner.free(p_rid); + memdelete(rt); + + return true; + } else if (texture_owner.owns(p_rid)) { + + Texture *t = texture_owner.get(p_rid); + // can't free a render target texture + ERR_FAIL_COND_V(t->render_target, true); + + info.texture_mem -= t->total_data_size; + texture_owner.free(p_rid); + memdelete(t); + + return true; + } else if (sky_owner.owns(p_rid)) { + + Sky *sky = sky_owner.get(p_rid); + sky_set_texture(p_rid, RID(), 256); + sky_owner.free(p_rid); + memdelete(sky); + + return true; + } else if (shader_owner.owns(p_rid)) { + + Shader *shader = shader_owner.get(p_rid); + + if (shader->shader) { + shader->shader->free_custom_shader(shader->custom_code_id); + } + + if (shader->dirty_list.in_list()) { + _shader_dirty_list.remove(&shader->dirty_list); + } + + while (shader->materials.first()) { + Material *m = shader->materials.first()->self(); + + m->shader = NULL; + _material_make_dirty(m); + + shader->materials.remove(shader->materials.first()); + } + + shader_owner.free(p_rid); + memdelete(shader); + + return true; + } else if (material_owner.owns(p_rid)) { + + Material *m = material_owner.get(p_rid); + + if (m->shader) { + m->shader->materials.remove(&m->list); + } + + for (Map<Geometry *, int>::Element *E = m->geometry_owners.front(); E; E = E->next()) { + Geometry *g = E->key(); + g->material = RID(); + } + + for (Map<RasterizerScene::InstanceBase *, int>::Element *E = m->instance_owners.front(); E; E = E->next()) { + + RasterizerScene::InstanceBase *ins = E->key(); + + if (ins->material_override == p_rid) { + ins->material_override = RID(); + } + + for (int i = 0; i < ins->materials.size(); i++) { + if (ins->materials[i] == p_rid) { + ins->materials.write[i] = RID(); + } + } + } + + material_owner.free(p_rid); + memdelete(m); + + return true; + } else if (skeleton_owner.owns(p_rid)) { + + Skeleton *s = skeleton_owner.get(p_rid); + + if (s->update_list.in_list()) { + skeleton_update_list.remove(&s->update_list); + } + + for (Set<RasterizerScene::InstanceBase *>::Element *E = s->instances.front(); E; E = E->next()) { + E->get()->skeleton = RID(); + } + + skeleton_allocate(p_rid, 0, false); + + if (s->tex_id) { + glDeleteTextures(1, &s->tex_id); + } + + skeleton_owner.free(p_rid); + memdelete(s); + + return true; + } else if (mesh_owner.owns(p_rid)) { + + Mesh *mesh = mesh_owner.get(p_rid); + + mesh->instance_remove_deps(); + mesh_clear(p_rid); + + while (mesh->multimeshes.first()) { + MultiMesh *multimesh = mesh->multimeshes.first()->self(); + multimesh->mesh = RID(); + multimesh->dirty_aabb = true; + + mesh->multimeshes.remove(mesh->multimeshes.first()); + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } + } + + mesh_owner.free(p_rid); + memdelete(mesh); + + return true; + } else if (multimesh_owner.owns(p_rid)) { + + MultiMesh *multimesh = multimesh_owner.get(p_rid); + multimesh->instance_remove_deps(); + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.remove(&multimesh->mesh_list); + } + } + + multimesh_allocate(p_rid, 0, VS::MULTIMESH_TRANSFORM_3D, VS::MULTIMESH_COLOR_NONE); + + update_dirty_multimeshes(); + + multimesh_owner.free(p_rid); + memdelete(multimesh); + + return true; + } else if (light_owner.owns(p_rid)) { + + Light *light = light_owner.get(p_rid); + light->instance_remove_deps(); + + light_owner.free(p_rid); + memdelete(light); + + return true; + } else { + return false; + } } bool RasterizerStorageGLES2::has_os_feature(const String &p_feature) const { + + if (p_feature == "s3tc") + return config.s3tc_supported; + + if (p_feature == "etc") + return config.etc1_supported; + return false; } @@ -1998,14 +3908,20 @@ void RasterizerStorageGLES2::initialize() { RasterizerStorageGLES2::system_fbo = 0; { - const char *gl_extensions = (const char *)glGetString(GL_EXTENSIONS); - Vector<String> strings = String(gl_extensions).split(" ", false); - for (int i = 0; i < strings.size(); i++) { - config.extensions.insert(strings[i]); + + const GLubyte *extension_string = glGetString(GL_EXTENSIONS); + + Vector<String> extensions = String((const char *)extension_string).split(" "); + + for (int i = 0; i < extensions.size(); i++) { + config.extensions.insert(extensions[i]); } } config.shrink_textures_x2 = false; + config.float_texture_supported = config.extensions.find("GL_ARB_texture_float") != NULL || config.extensions.find("GL_OES_texture_float") != NULL; + config.s3tc_supported = config.extensions.find("GL_EXT_texture_compression_s3tc") != NULL; + config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture") != NULL; frame.count = 0; frame.delta = 0; @@ -2017,6 +3933,38 @@ void RasterizerStorageGLES2::initialize() { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &config.max_texture_size); shaders.copy.init(); + shaders.cubemap_filter.init(); + + { + // quad for copying stuff + + glGenBuffers(1, &resources.quadie); + glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + { + const float qv[16] = { + -1, + -1, + 0, + 0, + -1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + 1, + 0, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } { //default textures @@ -2073,14 +4021,55 @@ void RasterizerStorageGLES2::initialize() { glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } + + // skeleton buffer + { + resources.skeleton_transform_buffer_size = 0; + glGenBuffers(1, &resources.skeleton_transform_buffer); + } + + // radical inverse vdc cache texture + // used for cubemap filtering + if (config.float_texture_supported) { + glGenTextures(1, &resources.radical_inverse_vdc_cache_tex); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); + + float radical_inverse[512]; + + for (uint32_t i = 0; i < 512; i++) { + uint32_t bits = i; + + bits = (bits << 16) | (bits >> 16); + bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); + bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); + bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); + bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); + + float value = float(bits) * 2.3283064365386963e-10; + + radical_inverse[i] = value; + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_FLOAT, radical_inverse); + + glBindTexture(GL_TEXTURE_2D, 0); + } } void RasterizerStorageGLES2::finalize() { } +void RasterizerStorageGLES2::_copy_screen() { + bind_quad_array(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + void RasterizerStorageGLES2::update_dirty_resources() { update_dirty_shaders(); update_dirty_materials(); + update_dirty_skeletons(); } RasterizerStorageGLES2::RasterizerStorageGLES2() { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index c2e1144128..fe5d4af952 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/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(); diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 16d4412802..6c7f767733 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/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"; diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp index baada9331e..146575973f 100644 --- a/drivers/gles2/shader_gles2.cpp +++ b/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) { } diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h index c3635bc201..0c53c4ba72 100644 --- a/drivers/gles2/shader_gles2.h +++ b/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) @@ -262,49 +430,13 @@ 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) { diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub index 5de3e1ac90..acb93fff8f 100644 --- a/drivers/gles2/shaders/SCsub +++ b/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'); diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 11c6ab9b76..29d81bb2c4 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/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 diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl index a21da68525..feaeb2152b 100644 --- a/drivers/gles2/shaders/copy.glsl +++ b/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; } diff --git a/drivers/gles2/shaders/cube_to_dp.glsl b/drivers/gles2/shaders/cube_to_dp.glsl index 5ffc78c0b9..0b3f53a870 100644 --- a/drivers/gles2/shaders/cube_to_dp.glsl +++ b/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); diff --git a/drivers/gles2/shaders/cubemap_filter.glsl b/drivers/gles2/shaders/cubemap_filter.glsl index 485fbb6ee0..62ecd9471b 100644 --- a/drivers/gles2/shaders/cubemap_filter.glsl +++ b/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); } diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 79b989be4a..e08e9d1117 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -1,940 +1,393 @@ [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; - - mediump vec4 ambient_light_color; - mediump vec4 bg_color; - - 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; +#ifdef USE_SKELETON_SOFTWARE - 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; +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 -}; - -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); + normal = normalize((modelview * vec4(normal, 0.0)).xyz); -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - 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 - - vertex_interp.z*= shadow_dual_paraboloid_render_side; - normal_interp.z*= shadow_dual_paraboloid_render_side; - - 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 - - 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; - - vertex.xyz=vtx; - vertex.w=1.0; - - -#else - - float z_ofs = z_offset; - z_ofs += (1.0-abs(normal_interp.z))*z_slope_scale; - vertex_interp.z-=z_ofs; - -#endif //RENDER_DEPTH_DUAL_PARABOLOID - -#endif //RENDER_DEPTH - - gl_Position = projection_matrix * vec4(vertex_interp,1.0); - - position_interp=gl_Position; - -#ifdef USE_VERTEX_LIGHTING - - diffuse_light_interp=vec4(0.0); - specular_light_interp=vec4(0.0); - -#ifdef USE_FORWARD_LIGHTING - - 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); - } - - 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 - - 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); - - 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; - } - - diffuse_light_interp.rgb+=directional_diffuse; - - 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; - } - - specular_light_interp.rgb+=directional_specular; - -#endif //USE_LIGHT_DIRECTIONAL - - -#endif // USE_VERTEX_LIGHTING + gl_Position = projection_matrix * vec4(vertex_interp, 1.0); } - [fragment] +#extension GL_ARB_shader_texture_lod : require -/* texture unit usage, N is max_texture_unity-N - -1-skeleton -2-radiance -3-reflection_atlas -4-directional_shadow -5-shadow_atlas -6-decal_atlas -7-screen -8-depth -9-probe1 -10-probe2 - -*/ - -uniform highp mat4 world_transform; - -#define M_PI 3.14159265359 - -/* Varyings */ - -#if defined(ENABLE_COLOR_INTERP) -in vec4 color_interp; -#endif - -#if defined(ENABLE_UV_INTERP) -in vec2 uv_interp; -#endif - -#if defined(ENABLE_UV2_INTERP) -in vec2 uv2_interp; -#endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) -in vec3 tangent_interp; -in vec3 binormal_interp; -#endif - -in highp vec3 vertex_interp; -in vec3 normal_interp; - - -/* PBR CHANNELS */ - -//used on forward mainly -uniform bool no_ambient_light; - - - -#ifdef USE_RADIANCE_MAP - - - -layout(std140) uniform Radiance { //ubo:2 - - mat4 radiance_inverse_xform; - float radiance_ambient_contribution; - -}; - -#define RADIANCE_MAX_LOD 5.0 - -#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. - - 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); -} - +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp #else - -uniform sampler2D radiance_map; //texunit:-2 - -vec3 textureDualParaboloid(sampler2D 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); - 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; -} - +precision mediump float; +precision mediump int; #endif -#endif - -/* Material Uniforms */ - - - -#if defined(USE_MATERIAL) - -layout(std140) uniform UniformData { - -MATERIAL_UNIFORMS - -}; - -#endif - -FRAGMENT_SHADER_GLOBALS - -layout(std140) uniform SceneData { - - 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; - - 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; -}; - -//directional light data +#include "stdlib.glsl" -#ifdef USE_LIGHT_DIRECTIONAL - -layout(std140) uniform DirectionalLightData { - - 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 sampler2DShadow directional_shadow; //texunit:-4 - -#endif - -#ifdef USE_VERTEX_LIGHTING -in vec4 diffuse_light_interp; -in vec4 specular_light_interp; -#endif -//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]; -}; - - -uniform highp sampler2DShadow shadow_atlas; //texunit:-5 - - -struct ReflectionData { - - 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 -}; - -layout(std140) uniform ReflectionProbeData { //ubo:6 - - ReflectionData reflections[MAX_REFLECTION_DATA_STRUCTS]; -}; -uniform mediump sampler2D reflection_atlas; //texunit:-3 +#define M_PI 3.14159265359 +// +// uniforms +// -#ifdef USE_FORWARD_LIGHTING +uniform mat4 camera_matrix; +uniform mat4 camera_inverse_matrix; +uniform mat4 projection_matrix; +uniform mat4 projection_inverse_matrix; -uniform int omni_light_indices[MAX_FORWARD_LIGHTS]; -uniform int omni_light_count; +uniform mat4 world_transform; -uniform int spot_light_indices[MAX_FORWARD_LIGHTS]; -uniform int spot_light_count; +uniform highp float time; -uniform int reflection_indices[MAX_FORWARD_LIGHTS]; -uniform int reflection_count; +#ifdef SCREEN_UV_USED +uniform vec2 screen_pixel_size; #endif +uniform highp sampler2D depth_buffer; //texunit:1 #if defined(SCREEN_TEXTURE_USED) - -uniform highp sampler2D screen_texture; //texunit:-7 - +uniform highp sampler2D screen_texture; //texunit:2 #endif -#ifdef USE_MULTIPLE_RENDER_TARGETS +#ifdef USE_RADIANCE_MAP -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 +#define RADIANCE_MAX_LOD 6.0 -#else +uniform samplerCube radiance_map; // texunit:0 -layout(location=0) out vec4 frag_color; +uniform mat4 radiance_inverse_xform; #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) { +uniform float bg_energy; - if (abs(dir.z)>0.99) - return 1.0; +uniform float ambient_sky_contribution; +uniform vec4 ambient_color; +uniform float ambient_energy; - vec3 endpoint = pos+dir*max_distance; - vec4 source = position_interp; - vec4 dest = projection_matrix * vec4(endpoint, 1.0); +#ifdef LIGHT_PASS - vec2 from_screen = (source.xy / source.w) * 0.5 + 0.5; - vec2 to_screen = (dest.xy / dest.w) * 0.5 + 0.5; +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_OMNI 1 +#define LIGHT_TYPE_SPOT 2 - vec2 screen_rel = to_screen - from_screen; +// general for all lights +uniform int light_type; - if (length(screen_rel)<0.00001) - return 1.0; //too small, don't do anything +uniform float light_energy; +uniform vec4 light_color; +uniform float light_specular; - /*float pixel_size; //approximate pixel size +// directional +uniform vec3 light_direction; - if (screen_rel.x > screen_rel.y) { +// omni +uniform vec3 light_position; - 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)); +uniform float light_range; +uniform vec4 light_attenuation; - }*/ - vec4 bias = projection_matrix * vec4(pos+vec3(0.0,0.0,0.04), 1.0); //todo un-harcode the 0.04 +// spot +uniform float light_spot_attenuation; +uniform float light_spot_range; +uniform float light_spot_angle; +// shadows +uniform highp sampler2D light_shadow_atlas; //texunit:3 +uniform float light_has_shadow; - 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; - } - } - +uniform mat4 light_shadow_matrix; +uniform vec4 light_clamp; - ratio+=ratio_incr; - steps-=1.0; - } +// directional shadow - return 1.0; -} +uniform highp sampler2D light_directional_shadow; // texunit:3 +uniform vec4 light_split_offsets; +uniform mat4 light_shadow_matrix1; +uniform mat4 light_shadow_matrix2; +uniform mat4 light_shadow_matrix3; +uniform mat4 light_shadow_matrix4; #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 +// varyings // -// 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); -} +varying highp vec3 vertex_interp; +varying vec3 normal_interp; -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 )); -} +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +varying vec3 tangent_interp; +varying vec3 binormal_interp; +#endif -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 ); -} +#ifdef ENABLE_COLOR_INTERP +varying vec4 color_interp; +#endif +#ifdef ENABLE_UV_INTERP +varying vec2 uv_interp; +#endif -float SchlickFresnel(float u) -{ - float m = 1.0-u; - float m2 = m*m; - return m2*m2*m; // pow(m,5) -} +#ifdef ENABLE_UV2_INTERP +varying vec2 uv2_interp; +#endif -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); -} +varying vec3 view_interp; vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { float dielectric = (0.034 * 2.0) * specular; @@ -942,1172 +395,490 @@ vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { 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 +FRAGMENT_SHADER_GLOBALS -#else - float NdotL = dot(N,L); - float cNdotL = max(NdotL, 0.0); // clamped NdotL +#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); - 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 + { + // calculate diffuse reflection + // TODO hardcode Oren Nayar for now + float diffuse_brdf_NL; -#if defined(DIFFUSE_LAMBERT_WRAP) - //energy conserving lambert wrap shader diffuse_brdf_NL = max(0.0,(NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + // diffuse_brdf_NL = cNdotL * (1.0 / M_PI); -#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) + { + // calculate specular reflection 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 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; - 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); +// shadows +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; } -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() +{ - -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 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); + 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 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; + float side = 1.0; -#ifdef METERIAL_DOUBLESIDED - float side=float(gl_FrontFacing)*2.0-1.0; -#else - float side=1.0; +#if defined(ENABLE_AO) + float ao = 1.0; + float ao_light_affect = 0.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; +#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(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 + vec3 normal = normalize(normal_interp) * side; #if defined(ENABLE_NORMALMAP) - - vec3 normalmap = vec3(0.0); + vec3 normalmap = vec3(0.5); #endif + float normaldepth = 1.0; - float normaldepth=1.0; -#if defined(SCREEN_UV_USED) - vec2 screen_uv = gl_FragCoord.xy*screen_pixel_size; +#ifdef ALPHA_SCISSOR_USED + float alpha_scissor = 0.5; #endif -#if defined (ENABLE_SSS) - float sss_strength=0.0; +#ifdef SCREEN_UV_USED + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; #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)); - 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; + normal = normalmap; +#endif - normal = normalize( mix(normal_interp,tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z,normaldepth) ) * side; + normal = normalize(normal); -#endif + vec3 N = normal; + + vec3 specular_light = vec3(0.0, 0.0, 0.0); + vec3 diffuse_light = vec3(0.0, 0.0, 0.0); -#if defined(LIGHT_USE_ANISOTROPY) + vec3 ambient_light = vec3(0.0, 0.0, 0.0); - 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)); - } + vec3 env_reflection_light = vec3(0.0, 0.0, 0.0); -#endif + vec3 eye_position = -normalize(vertex_interp); -#ifdef ENABLE_CLIP_ALPHA - if (albedo.a<0.99) { - //used for doublepass and shadowmapping +#ifdef ALPHA_SCISSOR_USED + if (alpha < alpha_scissor) { discard; } #endif + +// +// Lighting +// +#ifdef LIGHT_PASS -/////////////////////// 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 + if (light_type == LIGHT_TYPE_OMNI) { + vec3 light_vec = light_position - vertex; + float light_length = length(light_vec); - vec3 ambient_light; - vec3 env_reflection_light = vec3(0.0,0.0,0.0); + float normalized_distance = light_length / light_range; - vec3 eye_vec = -normalize( vertex_interp ); + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); + vec3 attenuation = vec3(omni_attenuation); + if (light_has_shadow > 0.5) { + highp vec3 splane = (light_shadow_matrix * vec4(vertex, 1.0)).xyz; + float shadow_len = length(splane); -#ifdef USE_RADIANCE_MAP + splane = normalize(splane); - if (no_ambient_light) { - ambient_light=vec3(0.0,0.0,0.0); - } else { - { + vec4 clamp_rect = light_clamp; - { //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; + if (splane.z >= 0.0) { + splane.z += 1.0; + clamp_rect.y += clamp_rect.w; + } else { + splane.z = 1.0 - splane.z; } - //no longer a cubemap - //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y); - } + splane.xy /= splane.z; + splane.xy = splane.xy * 0.5 + 0.5; + splane.z = shadow_len / light_range; - { + splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; - vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz); - vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy; + float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, clamp_rect); - ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution); - //ambient_light=vec3(0.0,0.0,0.0); + if (shadow > splane.z) { + } else { + attenuation = vec3(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 + 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 < shadow_split_offsets.w) { + if (depth_z < light_split_offsets.w) { #elif defined(LIGHT_USE_PSSM2) - if (depth_z < shadow_split_offsets.y) { + if (depth_z < light_split_offsets.y) { #else - if (depth_z < shadow_split_offsets.x) { -#endif //LIGHT_USE_PSSM4 - - vec3 pssm_coord; - float pssm_fade=0.0; - + 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; + 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); + 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) { - } - } else { - - - if (depth_z < shadow_split_offsets.z) { - - highp vec4 splane=(shadow_matrix3 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; + highp vec4 splane = (light_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); + 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 { + } 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); + 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); #if defined(LIGHT_USE_PSSM_BLEND) - use_blend=false; - + use_blend = false; #endif - - } - } - - - -#endif //LIGHT_USE_PSSM4 - + } + } + +#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); + 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=(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; - + } 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 - + } + +#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); - } + { + highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; + } #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); - - } + + 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 - light_attenuation=mix(mix(shadow_color_contact.rgb,vec3(1.0),shadow),vec3(1.0),pssm_fade); - - - } - - -#endif //LIGHT_DIRECTIONAL_SHADOW + + attenuation *= 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); + 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); + } + + + } + 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 - 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 +#ifdef RENDER_DEPTH - 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); - } +#ifdef USE_RADIANCE_MAP - 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 + 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; + { + 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; + ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); -#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; + gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); + // gl_FragColor = vec4(normal, 1.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; + gl_FragColor = vec4(albedo, alpha); #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 - - -#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; -#endif - - -#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 //RENDER_DEPTH +#endif // lighting } diff --git a/drivers/gles2/shaders/stdlib.glsl b/drivers/gles2/shaders/stdlib.glsl new file mode 100644 index 0000000000..ebbdb96311 --- /dev/null +++ b/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); +} diff --git a/methods.py b/methods.py index 33d503a41a..b2061f78bd 100644 --- a/methods.py +++ b/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") |