diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /drivers/gles2 | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'drivers/gles2')
-rw-r--r-- | drivers/gles2/SCsub | 5 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_gles2.cpp | 7918 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_gles2.h | 1441 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_instance_gles2.cpp | 40 | ||||
-rw-r--r-- | drivers/gles2/rasterizer_instance_gles2.h | 41 | ||||
-rw-r--r-- | drivers/gles2/shader_compiler_gles2.cpp | 648 | ||||
-rw-r--r-- | drivers/gles2/shader_compiler_gles2.h | 102 | ||||
-rw-r--r-- | drivers/gles2/shader_gles2.cpp | 746 | ||||
-rw-r--r-- | drivers/gles2/shader_gles2.h | 359 | ||||
-rw-r--r-- | drivers/gles2/shaders/SCsub | 9 | ||||
-rw-r--r-- | drivers/gles2/shaders/blur.glsl | 51 | ||||
-rw-r--r-- | drivers/gles2/shaders/canvas.glsl | 69 | ||||
-rw-r--r-- | drivers/gles2/shaders/copy.glsl | 253 | ||||
-rw-r--r-- | drivers/gles2/shaders/material.glsl | 875 |
14 files changed, 12557 insertions, 0 deletions
diff --git a/drivers/gles2/SCsub b/drivers/gles2/SCsub new file mode 100644 index 0000000000..a17335b41b --- /dev/null +++ b/drivers/gles2/SCsub @@ -0,0 +1,5 @@ +Import('env') + +env.add_source_files(env.drivers_sources,"*.cpp") + +SConscript("shaders/SCsub") diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp new file mode 100644 index 0000000000..c9601b26d5 --- /dev/null +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -0,0 +1,7918 @@ +/*************************************************************************/ +/* rasterizer_gles2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifdef GLES2_ENABLED + +#include "rasterizer_gles2.h" +#include "os/os.h" +#include "globals.h" +#include <stdio.h> +#include "servers/visual/shader_language.h" +#include "servers/visual/particle_system_sw.h" +#include "gl_context/context_gl.h" +#include <string.h> + +#ifdef GLEW_ENABLED +#define _GL_HALF_FLOAT_OES 0x140B +#else +#define _GL_HALF_FLOAT_OES 0x8D61 +#endif + + +#define _DEPTH_COMPONENT24_OES 0x81A6 + +#ifdef GLEW_ENABLED +#define _glClearDepth glClearDepth +#else +#define _glClearDepth glClearDepthf +#endif + +//#define DEBUG_OPENGL + +#ifdef DEBUG_OPENGL + +#define DEBUG_TEST_ERROR(m_section)\ +{\ + print_line("AT: "+String(m_section)); glFlush();\ + uint32_t err = glGetError();\ + if (err) {\ + print_line("OpenGL Error #"+itos(err)+" at: "+m_section);\ + }\ +} + +#else + +#define DEBUG_TEST_ERROR(m_section) + +#endif + +static const GLenum prim_type[]={GL_POINTS,GL_LINES,GL_TRIANGLES,GL_TRIANGLE_FAN}; + +_FORCE_INLINE_ static void _set_color_attrib(const Color& p_color) { + + GLfloat c[4]= { p_color.r, p_color.g, p_color.b, p_color.a }; + glVertexAttrib4fv( VS::ARRAY_COLOR, c ); +} + + + +static _FORCE_INLINE_ uint16_t make_half_float(float f) { + + union { + float fv; + uint32_t ui; + } ci; + ci.fv=f; + + unsigned int x = ci.ui; + unsigned int sign = (unsigned short)(x >> 31); + unsigned int mantissa; + unsigned int exp; + uint16_t hf; + + // get mantissa + mantissa = x & ((1 << 23) - 1); + // get exponent bits + exp = x & (0xFF << 23); + if (exp >= 0x47800000) + { + // check if the original single precision float number is a NaN + if (mantissa && (exp == (0xFF << 23))) + { + // we have a single precision NaN + mantissa = (1 << 23) - 1; + } + else + { + // 16-bit half-float representation stores number as Inf + mantissa = 0; + } + hf = (((uint16_t)sign) << 15) | (uint16_t)((0x1F << 10)) | + (uint16_t)(mantissa >> 13); + } + // check if exponent is <= -15 + else if (exp <= 0x38000000) + { + + // store a denorm half-float value or zero + exp = (0x38000000 - exp) >> 23; + mantissa >>= (14 + exp); + + hf = (((uint16_t)sign) << 15) | (uint16_t)(mantissa); + } + else + { + hf = (((uint16_t)sign) << 15) | + (uint16_t)((exp - 0x38000000) >> 13) | + (uint16_t)(mantissa >> 13); + } + + return hf; +} + +void RasterizerGLES2::_draw_primitive(int p_points, const Vector3 *p_vertices, const Vector3 *p_normals, const Color* p_colors, const Vector3 *p_uvs,const Plane *p_tangents,int p_instanced) { + + ERR_FAIL_COND(!p_vertices); + ERR_FAIL_COND(p_points <1 || p_points>4); + + bool quad=false; + + GLenum type; + switch(p_points) { + + case 1: type=GL_POINTS; break; + case 2: type=GL_LINES; break; + case 4: quad=true; p_points=3; + case 3: type=GL_TRIANGLES; break; + }; + + + glBindBuffer(GL_ARRAY_BUFFER,0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + GLfloat vert_array[18]; + GLfloat normal_array[18]; + GLfloat color_array[24]; + GLfloat tangent_array[24]; + GLfloat uv_array[18]; + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glVertexAttribPointer( VS::ARRAY_VERTEX, 3 ,GL_FLOAT, false, 0, vert_array ); + + for (int i=0;i<p_points;i++) { + + vert_array[i*3+0]=p_vertices[i].x; + vert_array[i*3+1]=p_vertices[i].y; + vert_array[i*3+2]=p_vertices[i].z; + if (quad) { + int idx=2+i; + if (idx==4) + idx=0; + vert_array[9+i*3+0]=p_vertices[idx].x; + vert_array[9+i*3+1]=p_vertices[idx].y; + vert_array[9+i*3+2]=p_vertices[idx].z; + + } + } + + if (p_normals) { + glEnableVertexAttribArray(VS::ARRAY_NORMAL); + glVertexAttribPointer( VS::ARRAY_NORMAL, 3 ,GL_FLOAT, false, 0, normal_array ); + for (int i=0;i<p_points;i++) { + + normal_array[i*3+0]=p_normals[i].x; + normal_array[i*3+1]=p_normals[i].y; + normal_array[i*3+2]=p_normals[i].z; + if (quad) { + int idx=2+i; + if (idx==4) + idx=0; + normal_array[9+i*3+0]=p_normals[idx].x; + normal_array[9+i*3+1]=p_normals[idx].y; + normal_array[9+i*3+2]=p_normals[idx].z; + + } + } + } else { + glDisableVertexAttribArray(VS::ARRAY_NORMAL); + } + + if (p_colors) { + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttribPointer( VS::ARRAY_COLOR, 4 ,GL_FLOAT, false, 0, color_array ); + for (int i=0;i<p_points;i++) { + + color_array[i*4+0]=p_colors[i].r; + color_array[i*4+1]=p_colors[i].g; + color_array[i*4+2]=p_colors[i].b; + color_array[i*4+3]=p_colors[i].a; + if (quad) { + int idx=2+i; + if (idx==4) + idx=0; + color_array[12+i*4+0]=p_colors[idx].r; + color_array[12+i*4+1]=p_colors[idx].g; + color_array[12+i*4+2]=p_colors[idx].b; + color_array[12+i*4+3]=p_colors[idx].a; + + } + } + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + } + + if (p_tangents) { + glEnableVertexAttribArray(VS::ARRAY_TANGENT); + glVertexAttribPointer( VS::ARRAY_TANGENT, 4 ,GL_FLOAT, false, 0, tangent_array ); + for (int i=0;i<p_points;i++) { + + tangent_array[i*4+0]=p_tangents[i].normal.x; + tangent_array[i*4+1]=p_tangents[i].normal.y; + tangent_array[i*4+2]=p_tangents[i].normal.z; + tangent_array[i*4+3]=p_tangents[i].d; + if (quad) { + int idx=2+i; + if (idx==4) + idx=0; + tangent_array[12+i*4+0]=p_tangents[idx].normal.x; + tangent_array[12+i*4+1]=p_tangents[idx].normal.y; + tangent_array[12+i*4+2]=p_tangents[idx].normal.z; + tangent_array[12+i*4+3]=p_tangents[idx].d; + + } + } + } else { + glDisableVertexAttribArray(VS::ARRAY_TANGENT); + } + + if (p_uvs) { + + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glVertexAttribPointer( VS::ARRAY_TEX_UV, 3 ,GL_FLOAT, false, 0, uv_array ); + for (int i=0;i<p_points;i++) { + + uv_array[i*3+0]=p_uvs[i].x; + uv_array[i*3+1]=p_uvs[i].y; + uv_array[i*3+2]=p_uvs[i].z; + if (quad) { + int idx=2+i; + if (idx==4) + idx=0; + uv_array[9+i*3+0]=p_uvs[idx].x; + uv_array[9+i*3+1]=p_uvs[idx].y; + uv_array[9+i*3+2]=p_uvs[idx].z; + + } + } + + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } + + /* + if (p_instanced>1) + glDrawArraysInstanced(type,0,p_points,p_instanced); + else + */ + + glDrawArrays(type,0,quad?6:p_points); + +}; + + +/* TEXTURE API */ +#define _EXT_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define _EXT_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define _EXT_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define _EXT_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#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_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define _EXT_COMPRESSED_RED_RGTC1 0x8DBB +#define _EXT_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define _EXT_COMPRESSED_RG_RGTC2 0x8DBD +#define _EXT_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define _EXT_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define _EXT_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#define _EXT_ETC1_RGB8_OES 0x8D64 + +/* TEXTURE API */ + +Image RasterizerGLES2::_get_gl_image_and_format(const Image& p_image, Image::Format p_format, uint32_t p_flags,GLenum& r_gl_format,int &r_gl_components,bool &r_has_alpha_cache,bool &r_compressed) { + + r_has_alpha_cache=false; + r_compressed=false; + Image image=p_image; + + switch(p_format) { + + case Image::FORMAT_GRAYSCALE: { + r_gl_components=1; + r_gl_format=GL_LUMINANCE; + + } break; + case Image::FORMAT_INTENSITY: { + + if (!image.empty()) + image.convert(Image::FORMAT_RGBA); + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + } break; + case Image::FORMAT_GRAYSCALE_ALPHA: { + + //image.convert(Image::FORMAT_RGBA); + r_gl_components=2; + r_gl_format=GL_LUMINANCE_ALPHA; + r_has_alpha_cache=true; + } break; + + case Image::FORMAT_INDEXED: { + + if (!image.empty()) + image.convert(Image::FORMAT_RGB); + r_gl_components=3; + r_gl_format=GL_RGB; + + } break; + + case Image::FORMAT_INDEXED_ALPHA: { + + if (!image.empty()) + image.convert(Image::FORMAT_RGBA); + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + + } break; + case Image::FORMAT_RGB: { + + r_gl_components=3; + r_gl_format=GL_RGB; + } break; + case Image::FORMAT_RGBA: { + + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + } break; + case Image::FORMAT_BC1: { + + r_gl_components=1; //doesn't matter much + r_gl_format=_EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT; + r_compressed=true; + + } break; + case Image::FORMAT_BC2: { + r_gl_components=1; //doesn't matter much + r_gl_format=_EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT; + r_has_alpha_cache=true; + r_compressed=true; + + } break; + case Image::FORMAT_BC3: { + + r_gl_components=1; //doesn't matter much + r_gl_format=_EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; + r_has_alpha_cache=true; + r_compressed=true; + + } break; + case Image::FORMAT_BC4: { + + r_gl_format=_EXT_COMPRESSED_RED_RGTC1; + r_gl_components=1; //doesn't matter much + r_compressed=true; + + } break; + case Image::FORMAT_BC5: { + + r_gl_format=_EXT_COMPRESSED_RG_RGTC2; + r_gl_components=1; //doesn't matter much + r_compressed=true; + } break; + case Image::FORMAT_PVRTC2: { + + if (!pvr_supported) { + + if (!image.empty()) { + image.decompress(); + } + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + + + } else { + + r_gl_format=_EXT_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + r_gl_components=1; //doesn't matter much + r_compressed=true; + + } + + } break; + case Image::FORMAT_PVRTC2_ALPHA: { + + if (!pvr_supported) { + + if (!image.empty()) + image.decompress(); + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + + + } else { + + r_gl_format=_EXT_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + r_gl_components=1; //doesn't matter much + r_compressed=true; + + } + + } break; + case Image::FORMAT_PVRTC4: { + + if (!pvr_supported) { + + if (!image.empty()) + image.decompress(); + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + } else { + + r_gl_format=_EXT_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + r_gl_components=1; //doesn't matter much + r_compressed=true; + } + + } break; + case Image::FORMAT_PVRTC4_ALPHA: { + + if (!pvr_supported) { + + if (!image.empty()) + image.decompress(); + r_gl_components=4; + r_gl_format=GL_RGBA; + r_has_alpha_cache=true; + + } else { + r_gl_format=_EXT_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + r_gl_components=1; //doesn't matter much + r_compressed=true; + } + + } break; + case Image::FORMAT_ETC: { + + if (!etc_supported) { + + if (!image.empty()) { + image.decompress(); + } + r_gl_components=3; + r_gl_format=GL_RGB; + + + } else { + + r_gl_format=_EXT_ETC1_RGB8_OES; + r_gl_components=1; //doesn't matter much + r_compressed=true; + } + + } break; + case Image::FORMAT_YUV_422: + case Image::FORMAT_YUV_444: { + + if (!image.empty()) + image.convert(Image::FORMAT_RGB); + r_gl_format=GL_RGB; + r_gl_components=3; + + } break; + + default: { + + ERR_FAIL_V(Image()); + } + } + + return image; +} + +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, + +}; + +RID RasterizerGLES2::texture_create() { + + Texture *texture = memnew(Texture); + ERR_FAIL_COND_V(!texture,RID()); + glGenTextures(1, &texture->tex_id); + texture->active=false; + texture->total_data_size=0; + + return texture_owner.make_rid( texture ); + +} + +void RasterizerGLES2::texture_allocate(RID p_texture,int p_width, int p_height,Image::Format p_format,uint32_t p_flags) { + + bool has_alpha_cache; + int components; + GLenum format; + bool compressed; + + int po2_width = nearest_power_of_2(p_width); + int po2_height = nearest_power_of_2(p_height); + + if (p_flags&VS::TEXTURE_FLAG_VIDEO_SURFACE) { + p_flags&=~VS::TEXTURE_FLAG_MIPMAPS; // no mipies for video + } + + + Texture *texture = texture_owner.get( p_texture ); + ERR_FAIL_COND(!texture); + texture->width=p_width; + texture->height=p_height; + texture->format=p_format; + texture->flags=p_flags; + texture->target = (p_flags & VS::TEXTURE_FLAG_CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + + bool scale_textures = !(p_flags&VS::TEXTURE_FLAG_VIDEO_SURFACE) && (!npo2_textures_available || p_flags&VS::TEXTURE_FLAG_MIPMAPS); + + + if (scale_textures) { + texture->alloc_width = po2_width; + texture->alloc_height = po2_height; + // print_line("scale because npo2: "+itos(npo2_textures_available)+" mm: "+itos(p_format&VS::TEXTURE_FLAG_MIPMAPS)+" "+itos(p_mipmap_count) ); + } else { + + texture->alloc_width = texture->width; + texture->alloc_height = texture->height; + }; + + _get_gl_image_and_format(Image(),texture->format,texture->flags,format,components,has_alpha_cache,compressed); + + texture->gl_components_cache=components; + texture->gl_format_cache=format; + texture->format_has_alpha=has_alpha_cache; + texture->compressed=compressed; + texture->has_alpha=false; //by default it doesn't have alpha unless something with alpha is blitteds + texture->data_size=0; + + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + + if (texture->flags&VS::TEXTURE_FLAG_MIPMAPS) + glTexParameteri(texture->target,GL_TEXTURE_MIN_FILTER,use_fast_texture_filter?GL_LINEAR_MIPMAP_NEAREST:GL_LINEAR_MIPMAP_LINEAR); + else + glTexParameteri(texture->target,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + + if (texture->flags&VS::TEXTURE_FLAG_FILTER) { + + glTexParameteri(texture->target,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering + + } else { + + glTexParameteri(texture->target,GL_TEXTURE_MAG_FILTER,GL_NEAREST); // raw Filtering + } + + bool force_clamp_to_edge = !(p_flags&VS::TEXTURE_FLAG_MIPMAPS) && (nearest_power_of_2(texture->alloc_height)!=texture->alloc_height || nearest_power_of_2(texture->alloc_width)!=texture->alloc_width); + + if (!force_clamp_to_edge && texture->flags&VS::TEXTURE_FLAG_REPEAT && texture->target != GL_TEXTURE_CUBE_MAP) { + + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + } else { + + //glTexParameterf( texture->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } + + if (p_flags&VS::TEXTURE_FLAG_VIDEO_SURFACE) { + //prealloc if video + glTexImage2D(texture->target, 0, format, p_width, p_height, 0, format, GL_UNSIGNED_BYTE,NULL); + } + + texture->active=true; +} + +void RasterizerGLES2::texture_set_data(RID p_texture,const Image& p_image,VS::CubeMapSide p_cube_side) { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(!texture->active); + ERR_FAIL_COND(texture->render_target); + ERR_FAIL_COND(texture->format != p_image.get_format() ); + ERR_FAIL_COND( p_image.empty() ); + + int components; + GLenum format; + bool alpha; + bool compressed; + + if (keep_copies && !(texture->flags&VS::TEXTURE_FLAG_VIDEO_SURFACE) && !(use_reload_hooks && texture->reloader)) { + texture->image[p_cube_side]=p_image; + } + + Image img = _get_gl_image_and_format(p_image, p_image.get_format(),texture->flags,format,components,alpha,compressed); + + if (texture->alloc_width != img.get_width() || texture->alloc_height != img.get_height()) { + + if (img.get_format() <= Image::FORMAT_INDEXED_ALPHA) + img.resize(texture->alloc_width, texture->alloc_height, Image::INTERPOLATE_BILINEAR); + }; + + + + if (img.detect_alpha()==Image::ALPHA_BLEND) { + texture->has_alpha=true; + } + + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP)?_cube_side_enum[p_cube_side]:GL_TEXTURE_2D; + + texture->data_size=img.get_data().size(); + DVector<uint8_t>::Read read = img.get_data().read(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + int mipmaps= (texture->flags&VS::TEXTURE_FLAG_MIPMAPS && img.get_mipmaps()>0) ? img.get_mipmaps() +1 : 1; + + + int w=img.get_width(); + int h=img.get_height(); + + int tsize=0; + for(int i=0;i<mipmaps;i++) { + + int size,ofs; + img.get_mipmap_offset_and_size(i,ofs,size); + + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glCompressedTexImage2D( blit_target, i, format,w,h,0,size,&read[ofs] ); + + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture->flags&VS::TEXTURE_FLAG_VIDEO_SURFACE) { + glTexSubImage2D( blit_target, i, 0,0,w,h,format,GL_UNSIGNED_BYTE,&read[ofs] ); + } else { + glTexImage2D(blit_target, i, format, w, h, 0, format, GL_UNSIGNED_BYTE,&read[ofs]); + } + + } + tsize+=size; + + w = MAX(1,w>>1); + h = MAX(1,h>>1); + + } + + _rinfo.texture_mem-=texture->total_data_size; + texture->total_data_size=tsize; + _rinfo.texture_mem+=texture->total_data_size; + + //printf("texture: %i x %i - size: %i - total: %i\n",texture->width,texture->height,tsize,_rinfo.texture_mem); + + + if (texture->flags&VS::TEXTURE_FLAG_MIPMAPS && mipmaps==1) { + //generate mipmaps if they were requested and the image does not contain them + glGenerateMipmap(texture->target); + } + + + + + if (mipmaps>1) { + + //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmaps-1 ); - assumed to have all, always + } + + //texture_set_flags(p_texture,texture->flags); + + +} + +Image RasterizerGLES2::texture_get_data(RID p_texture,VS::CubeMapSide p_cube_side) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,Image()); + ERR_FAIL_COND_V(!texture->active,Image()); + ERR_FAIL_COND_V(texture->data_size==0,Image()); + ERR_FAIL_COND_V(texture->render_target,Image()); + + return texture->image[p_cube_side]; + +#if 0 + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,Image()); + ERR_FAIL_COND_V(!texture->active,Image()); + ERR_FAIL_COND_V(texture->data_size==0,Image()); + + DVector<uint8_t> data; + GLenum format,type=GL_UNSIGNED_BYTE; + Image::Format fmt; + int pixelsize=0; + int pixelshift=0; + int minw=1,minh=1; + bool compressed=false; + + fmt=texture->format; + + switch(texture->format) { + + case Image::FORMAT_GRAYSCALE: { + + format=GL_LUMINANCE; + type=GL_UNSIGNED_BYTE; + data.resize(texture->alloc_width*texture->alloc_height); + pixelsize=1; + + } break; + case Image::FORMAT_INTENSITY: { + return Image(); + } break; + case Image::FORMAT_GRAYSCALE_ALPHA: { + + format=GL_LUMINANCE_ALPHA; + type=GL_UNSIGNED_BYTE; + pixelsize=2; + + } break; + case Image::FORMAT_RGB: { + format=GL_RGB; + type=GL_UNSIGNED_BYTE; + pixelsize=3; + } break; + case Image::FORMAT_RGBA: { + + format=GL_RGBA; + type=GL_UNSIGNED_BYTE; + pixelsize=4; + } break; + case Image::FORMAT_INDEXED: { + + format=GL_RGB; + type=GL_UNSIGNED_BYTE; + fmt=Image::FORMAT_RGB; + pixelsize=3; + } break; + case Image::FORMAT_INDEXED_ALPHA: { + + format=GL_RGBA; + type=GL_UNSIGNED_BYTE; + fmt=Image::FORMAT_RGBA; + pixelsize=4; + + } break; + case Image::FORMAT_BC1: { + + pixelsize=1; //doesn't matter much + format=GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + compressed=true; + pixelshift=1; + minw=minh=4; + + } break; + case Image::FORMAT_BC2: { + pixelsize=1; //doesn't matter much + format=GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + compressed=true; + minw=minh=4; + + } break; + case Image::FORMAT_BC3: { + + pixelsize=1; //doesn't matter much + format=GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + compressed=true; + minw=minh=4; + + } break; + case Image::FORMAT_BC4: { + + format=GL_COMPRESSED_RED_RGTC1; + pixelsize=1; //doesn't matter much + compressed=true; + pixelshift=1; + minw=minh=4; + + } break; + case Image::FORMAT_BC5: { + + format=GL_COMPRESSED_RG_RGTC2; + pixelsize=1; //doesn't matter much + compressed=true; + minw=minh=4; + + } break; + + default:{} + } + + data.resize(texture->data_size); + DVector<uint8_t>::Write wb = data.write(); + + glActiveTexture(GL_TEXTURE0); + int ofs=0; + glBindTexture(texture->target,texture->tex_id); + + int w=texture->alloc_width; + int h=texture->alloc_height; + for(int i=0;i<texture->mipmaps+1;i++) { + + if (compressed) { + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetCompressedTexImage(texture->target,i,&wb[ofs]); + + } else { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glGetTexImage(texture->target,i,format,type,&wb[ofs]); + } + + int size = (w*h*pixelsize)>>pixelshift; + ofs+=size; + + w=MAX(minw,w>>1); + h=MAX(minh,h>>1); + + } + + + wb=DVector<uint8_t>::Write(); + + Image img(texture->alloc_width,texture->alloc_height,texture->mipmaps,fmt,data); + + if (texture->format<Image::FORMAT_INDEXED && (texture->alloc_width!=texture->width || texture->alloc_height!=texture->height)) + img.resize(texture->width,texture->height); + + return img; +#endif +} + +void RasterizerGLES2::texture_set_flags(RID p_texture,uint32_t p_flags) { + + Texture *texture = texture_owner.get( p_texture ); + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(texture->render_target); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + uint32_t cube = texture->flags & VS::TEXTURE_FLAG_CUBEMAP; + texture->flags=p_flags|cube; // can't remove a cube from being a cube + + bool force_clamp_to_edge = !(p_flags&VS::TEXTURE_FLAG_MIPMAPS) && (nearest_power_of_2(texture->alloc_height)!=texture->alloc_height || nearest_power_of_2(texture->alloc_width)!=texture->alloc_width); + + if (!force_clamp_to_edge && texture->flags&VS::TEXTURE_FLAG_REPEAT && texture->target != GL_TEXTURE_CUBE_MAP) { + + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + } else { + //glTexParameterf( texture->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + + } + + + if (texture->flags&VS::TEXTURE_FLAG_FILTER) { + + glTexParameteri(texture->target,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering + if (texture->flags&VS::TEXTURE_FLAG_MIPMAPS) + glTexParameteri(texture->target,GL_TEXTURE_MIN_FILTER,use_fast_texture_filter?GL_LINEAR_MIPMAP_NEAREST:GL_LINEAR_MIPMAP_LINEAR); + else + glTexParameteri(texture->target,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering + + } else { + + glTexParameteri(texture->target,GL_TEXTURE_MAG_FILTER,GL_NEAREST); // nearest + } +} +uint32_t RasterizerGLES2::texture_get_flags(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->flags; + +} +Image::Format RasterizerGLES2::texture_get_format(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,Image::FORMAT_GRAYSCALE); + + return texture->format; +} +uint32_t RasterizerGLES2::texture_get_width(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->width; +} +uint32_t RasterizerGLES2::texture_get_height(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->height; +} + +bool RasterizerGLES2::texture_has_alpha(RID p_texture) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture,0); + + return texture->has_alpha; + +} + +void RasterizerGLES2::texture_set_size_override(RID p_texture,int p_width, int p_height) { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(texture->render_target); + + ERR_FAIL_COND(p_width<=0 || p_width>4096); + ERR_FAIL_COND(p_height<=0 || p_height>4096); + //real texture size is in alloc width and height + texture->width=p_width; + texture->height=p_height; + +} + +void RasterizerGLES2::texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const { + + Texture * texture = texture_owner.get(p_texture); + + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(texture->render_target); + + texture->reloader=p_owner; + texture->reloader_func=p_function; + if (use_reload_hooks && p_owner && keep_copies) { + + for(int i=0;i<6;i++) + texture->image[i]=Image(); + } +} + + +/* SHADER API */ + +RID RasterizerGLES2::shader_create(VS::ShaderMode p_mode) { + + Shader *shader = memnew( Shader ); + shader->mode=p_mode; + RID rid = shader_owner.make_rid(shader); + shader_set_mode(rid,p_mode); + _shader_make_dirty(shader); + + return rid; + +} + + + +void RasterizerGLES2::shader_set_mode(RID p_shader,VS::ShaderMode p_mode) { + + ERR_FAIL_INDEX(p_mode,3); + Shader *shader=shader_owner.get(p_shader); + ERR_FAIL_COND(!shader); + if (shader->custom_code_id && p_mode==shader->mode) + return; + + + if (shader->custom_code_id) { + + switch(shader->mode) { + case VS::SHADER_MATERIAL: { + material_shader.free_custom_shader(shader->custom_code_id); + } break; + } + + shader->custom_code_id=0; + } + + shader->mode=p_mode; + + switch(shader->mode) { + case VS::SHADER_MATERIAL: { + shader->custom_code_id=material_shader.create_custom_shader(); + } break; + } + _shader_make_dirty(shader); + +} +VS::ShaderMode RasterizerGLES2::shader_get_mode(RID p_shader) const { + + Shader *shader=shader_owner.get(p_shader); + ERR_FAIL_COND_V(!shader,VS::SHADER_MATERIAL); + return shader->mode; +} + + + +void RasterizerGLES2::shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs,int p_fragment_ofs) { + + Shader *shader=shader_owner.get(p_shader); + ERR_FAIL_COND(!shader); + +#ifdef DEBUG_ENABLED + if (shader->vertex_code==p_vertex && shader->fragment_code==p_fragment) + return; +#endif + shader->fragment_code=p_fragment; + shader->vertex_code=p_vertex; + shader->fragment_line=p_fragment_ofs; + shader->vertex_line=p_vertex_ofs; + _shader_make_dirty(shader); + +} + +String RasterizerGLES2::shader_get_vertex_code(RID p_shader) const { + + Shader *shader=shader_owner.get(p_shader); + ERR_FAIL_COND_V(!shader,String()); + return shader->vertex_code; + +} + +String RasterizerGLES2::shader_get_fragment_code(RID p_shader) const { + + Shader *shader=shader_owner.get(p_shader); + ERR_FAIL_COND_V(!shader,String()); + return shader->fragment_code; + +} + +void RasterizerGLES2::_shader_make_dirty(Shader* p_shader) { + + if (p_shader->dirty_list.in_list()) + return; + + _shader_dirty_list.add(&p_shader->dirty_list); +} + +void RasterizerGLES2::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const { + + Shader *shader=shader_owner.get(p_shader); + ERR_FAIL_COND(!shader); + + + if (shader->dirty_list.in_list()) + _update_shader(shader); // ok should be not anymore dirty + + + Map<int,StringName> order; + + + for(Map<StringName,ShaderLanguage::Uniform>::Element *E=shader->uniforms.front();E;E=E->next()) { + + + order[E->get().order]=E->key(); + } + + + for(Map<int,StringName>::Element *E=order.front();E;E=E->next()) { + + PropertyInfo pi; + ShaderLanguage::Uniform &u=shader->uniforms[E->get()]; + pi.name=E->get(); + switch(u.type) { + + case ShaderLanguage::TYPE_VOID: + case ShaderLanguage::TYPE_BOOL: + case ShaderLanguage::TYPE_FLOAT: + case ShaderLanguage::TYPE_VEC2: + case ShaderLanguage::TYPE_VEC3: + case ShaderLanguage::TYPE_MAT3: + case ShaderLanguage::TYPE_MAT4: + case ShaderLanguage::TYPE_VEC4: + pi.type=u.default_value.get_type(); + break; + case ShaderLanguage::TYPE_TEXTURE: + pi.type=Variant::_RID; + pi.hint=PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string="Texture"; + break; + case ShaderLanguage::TYPE_CUBEMAP: + pi.type=Variant::_RID; + pi.hint=PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string="CubeMap"; + break; + }; + + p_param_list->push_back(pi); + + } + +} + + +/* COMMON MATERIAL API */ + + +RID RasterizerGLES2::material_create() { + + return material_owner.make_rid( memnew( Material ) ); +} + +void RasterizerGLES2::material_set_shader(RID p_material, RID p_shader) { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + if (material->shader==p_shader) + return; + material->shader=p_shader; + material->shader_version=0; + +} + +RID RasterizerGLES2::material_get_shader(RID p_material) const { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material,RID()); + return material->shader; +} + + +void RasterizerGLES2::material_set_param(RID p_material, const StringName& p_param, const Variant& p_value) { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + + Map<StringName,Material::UniformData>::Element *E=material->shader_params.find(p_param); + if (E) { + + if (p_value.get_type()==Variant::NIL) { + + material->shader_params.erase(E); + material->shader_version=0; //get default! + } else { + E->get().value=p_value; + } + } else { + + Material::UniformData ud; + ud.index=-1; + ud.value=p_value; + ud.istexture=p_value.get_type()==Variant::_RID; /// cache it being texture + material->shader_params[p_param]=ud; //may be got at some point, or erased + + } +} +Variant RasterizerGLES2::material_get_param(RID p_material, const StringName& p_param) const { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material,Variant()); + + + if (material->shader.is_valid()) { + //update shader params if necesary + //make sure the shader is compiled and everything + //so the actual parameters can be properly retrieved! + material->shader_cache=shader_owner.get( material->shader ); + if (!material->shader_cache) { + //invalidate + material->shader=RID(); + material->shader_cache=NULL; + } else { + + if (material->shader_cache->dirty_list.in_list()) + _update_shader(material->shader_cache); + if (material->shader_cache->valid && material->shader_cache->version!=material->shader_version) { + //validate + _update_material_shader_params(material); + } + } + } + + + if (material->shader_params.has(p_param)) + return material->shader_params[p_param].value; + else + return Variant(); +} + + +void RasterizerGLES2::material_set_flag(RID p_material, VS::MaterialFlag p_flag,bool p_enabled) { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + ERR_FAIL_INDEX(p_flag,VS::MATERIAL_FLAG_MAX); + material->flags[p_flag]=p_enabled; + +} +bool RasterizerGLES2::material_get_flag(RID p_material,VS::MaterialFlag p_flag) const { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material,false); + ERR_FAIL_INDEX_V(p_flag,VS::MATERIAL_FLAG_MAX,false); + return material->flags[p_flag]; + + +} + +void RasterizerGLES2::material_set_hint(RID p_material, VS::MaterialHint p_hint,bool p_enabled) { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + ERR_FAIL_INDEX(p_hint,VS::MATERIAL_HINT_MAX); + material->hints[p_hint]=p_enabled; + +} + +bool RasterizerGLES2::material_get_hint(RID p_material,VS::MaterialHint p_hint) const { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material,false); + ERR_FAIL_INDEX_V(p_hint,VS::MATERIAL_HINT_MAX,false); + return material->hints[p_hint]; + +} + +void RasterizerGLES2::material_set_shade_model(RID p_material, VS::MaterialShadeModel p_model) { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + material->shade_model=p_model; + +}; + +VS::MaterialShadeModel RasterizerGLES2::material_get_shade_model(RID p_material) const { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material,VS::MATERIAL_SHADE_MODEL_LAMBERT); + return material->shade_model; +}; + + +void RasterizerGLES2::material_set_blend_mode(RID p_material,VS::MaterialBlendMode p_mode) { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + material->blend_mode=p_mode; + +} +VS::MaterialBlendMode RasterizerGLES2::material_get_blend_mode(RID p_material) const { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material,VS::MATERIAL_BLEND_MODE_ADD); + return material->blend_mode; +} + +void RasterizerGLES2::material_set_line_width(RID p_material,float p_line_width) { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + material->line_width=p_line_width; + +} +float RasterizerGLES2::material_get_line_width(RID p_material) const { + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material,0); + + return material->line_width; +} + + + +/* MESH API */ + +RID RasterizerGLES2::mesh_create() { + + + return mesh_owner.make_rid( memnew( Mesh ) ); +} + + + +void RasterizerGLES2::mesh_add_surface(RID p_mesh,VS::PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes,bool p_alpha_sort) { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_INDEX( p_primitive, VS::PRIMITIVE_MAX ); + ERR_FAIL_COND(p_arrays.size()!=VS::ARRAY_MAX); + + uint32_t format=0; + + // validation + int index_array_len=0; + int array_len=0; + + for(int i=0;i<p_arrays.size();i++) { + + if (p_arrays[i].get_type()==Variant::NIL) + continue; + + format|=(1<<i); + + if (i==VS::ARRAY_VERTEX) { + + array_len=Vector3Array(p_arrays[i]).size(); + ERR_FAIL_COND(array_len==0); + } else if (i==VS::ARRAY_INDEX) { + + index_array_len=IntArray(p_arrays[i]).size(); + } + } + + ERR_FAIL_COND((format&VS::ARRAY_FORMAT_VERTEX)==0); // mandatory + + + Surface *surface = memnew( Surface ); + ERR_FAIL_COND( !surface ); + + bool use_VBO=true; //glGenBuffersARB!=NULL; // TODO detect if it's in there + if ((!use_hw_skeleton_xform && format&VS::ARRAY_FORMAT_WEIGHTS) || mesh->morph_target_count>0) { + + use_VBO=false; + } + +// surface->packed=pack_arrays && use_VBO; + + int total_elem_size=0; + + for (int i=0;i<VS::ARRAY_MAX;i++) { + + + Surface::ArrayData&ad=surface->array[i]; + ad.size=0; + ad.ofs=0; + int elem_size=0; + int elem_count=0; + bool valid_local=true; + GLenum datatype; + bool normalize=false; + bool bind=false; + + if (!(format&(1<<i))) // no array + continue; + + + switch(i) { + + case VS::ARRAY_VERTEX: { + + if (use_VBO && use_half_float) { + elem_size=3*sizeof(int16_t); // vertex + datatype=_GL_HALF_FLOAT_OES; + } else { + + elem_size=3*sizeof(GLfloat); // vertex + datatype=GL_FLOAT; + } + bind=true; + elem_count=3; + + } break; + case VS::ARRAY_NORMAL: { + + if (use_VBO) { + elem_size=4*sizeof(int8_t); // vertex + datatype=GL_BYTE; + normalize=true; + } else { + elem_size=3*sizeof(GLfloat); // vertex + datatype=GL_FLOAT; + } + bind=true; + elem_count=3; + } break; + case VS::ARRAY_TANGENT: { + if (use_VBO) { + elem_size=4*sizeof(int8_t); // vertex + datatype=GL_BYTE; + normalize=true; + } else { + elem_size=4*sizeof(GLfloat); // vertex + datatype=GL_FLOAT; + } + bind=true; + elem_count=4; + + } break; + case VS::ARRAY_COLOR: { + + elem_size=4*sizeof(uint8_t); /* RGBA */ + datatype=GL_UNSIGNED_BYTE; + elem_count=4; + bind=true; + normalize=true; + } break; + case VS::ARRAY_TEX_UV: + case VS::ARRAY_TEX_UV2: { + if (use_VBO && use_half_float) { + elem_size=2*sizeof(int16_t); // vertex + datatype=_GL_HALF_FLOAT_OES; + } else { + elem_size=2*sizeof(GLfloat); // vertex + datatype=GL_FLOAT; + } + bind=true; + elem_count=2; + + } break; + case VS::ARRAY_WEIGHTS: { + + if (use_VBO) { + + elem_size=VS::ARRAY_WEIGHTS_SIZE*sizeof(GLushort); + elem_count=VS::ARRAY_WEIGHTS_SIZE; + valid_local=false; + bind=true; + normalize=true; + datatype=GL_UNSIGNED_SHORT; + elem_count=4; + + } else { + elem_size=VS::ARRAY_WEIGHTS_SIZE*sizeof(GLfloat); + elem_count=VS::ARRAY_WEIGHTS_SIZE; + valid_local=false; + bind=false; + datatype=GL_FLOAT; + elem_count=4; + } + + } break; + case VS::ARRAY_BONES: { + + if (use_VBO) { + elem_size=VS::ARRAY_WEIGHTS_SIZE*sizeof(GLubyte); + elem_count=VS::ARRAY_WEIGHTS_SIZE; + valid_local=false; + bind=true; + datatype=GL_UNSIGNED_BYTE; + elem_count=4; + } else { + + elem_size=VS::ARRAY_WEIGHTS_SIZE*sizeof(GLushort); + elem_count=VS::ARRAY_WEIGHTS_SIZE; + valid_local=false; + bind=false; + datatype=GL_UNSIGNED_SHORT; + elem_count=4; + + } + + + } break; + case VS::ARRAY_INDEX: { + + if (index_array_len<=0) { + ERR_PRINT("index_array_len==NO_INDEX_ARRAY"); + break; + } + /* determine wether using 16 or 32 bits indices */ + if (array_len>(1<<16)) { + + elem_size=4; + datatype=GL_UNSIGNED_INT; + } else { + elem_size=2; + datatype=GL_UNSIGNED_SHORT; + } + +/* + if (use_VBO) { + + glGenBuffers(1,&surface->index_id); + ERR_FAIL_COND(surface->index_id==0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,surface->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER,index_array_len*elem_size,NULL,GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); //unbind + } else { + surface->index_array_local = (uint8_t*)memalloc(index_array_len*elem_size); + }; +*/ + surface->index_array_len=index_array_len; // only way it can exist + ad.ofs=0; + ad.size=elem_size; + + + continue; + } break; + default: { + ERR_FAIL( ); + } + } + + ad.ofs=total_elem_size; + ad.size=elem_size; + ad.datatype=datatype; + ad.normalize=normalize; + ad.bind=bind; + ad.count=elem_count; + total_elem_size+=elem_size; + if (valid_local) { + surface->local_stride+=elem_size; + surface->morph_format|=(1<<i); + } + + + } + + surface->stride=total_elem_size; + surface->array_len=array_len; + surface->format=format; + surface->primitive=p_primitive; + surface->configured_format=0; + if (keep_copies) { + surface->data=p_arrays; + surface->morph_data=p_blend_shapes; + } + + uint8_t *array_ptr=NULL; + uint8_t *index_array_ptr=NULL; + DVector<uint8_t> array_pre_vbo; + DVector<uint8_t>::Write vaw; + DVector<uint8_t> index_array_pre_vbo; + DVector<uint8_t>::Write iaw; + + /* create pointers */ + if (use_VBO) { + + array_pre_vbo.resize(surface->array_len*surface->stride); + vaw = array_pre_vbo.write(); + array_ptr=vaw.ptr(); + + if (surface->index_array_len) { + + index_array_pre_vbo.resize(surface->index_array_len*surface->array[VS::ARRAY_INDEX].size); + iaw = index_array_pre_vbo.write(); + index_array_ptr=iaw.ptr(); + } + } else { + + surface->array_local = (uint8_t*)memalloc(surface->array_len*surface->stride); + array_ptr=(uint8_t*)surface->array_local; + if (surface->index_array_len) { + surface->index_array_local = (uint8_t*)memalloc(index_array_len*surface->array[VS::ARRAY_INDEX].size); + index_array_ptr=(uint8_t*)surface->index_array_local; + } + } + + + + _surface_set_arrays(surface,array_ptr,index_array_ptr,p_arrays,true); + + + /* create buffers!! */ + if (use_VBO) { + glGenBuffers(1,&surface->vertex_id); + ERR_FAIL_COND(surface->vertex_id==0); + glBindBuffer(GL_ARRAY_BUFFER,surface->vertex_id); + glBufferData(GL_ARRAY_BUFFER,surface->array_len*surface->stride,array_ptr,GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER,0); //unbind + if (surface->index_array_len) { + + glGenBuffers(1,&surface->index_id); + ERR_FAIL_COND(surface->index_id==0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,surface->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER,index_array_len*surface->array[VS::ARRAY_INDEX].size,index_array_ptr,GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); //unbind + + } + } + + mesh->surfaces.push_back(surface); + +} + +Error RasterizerGLES2::_surface_set_arrays(Surface *p_surface, uint8_t *p_mem,uint8_t *p_index_mem,const Array& p_arrays,bool p_main) { + + uint32_t stride = p_main ? p_surface->stride : p_surface->local_stride; + + for(int ai=0;ai<VS::ARRAY_MAX;ai++) { + if (ai>=p_arrays.size()) + break; + if (p_arrays[ai].get_type()==Variant::NIL) + continue; + Surface::ArrayData &a=p_surface->array[ai]; + + switch(ai) { + + + case VS::ARRAY_VERTEX: { + + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::VECTOR3_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<Vector3> array = p_arrays[ai]; + ERR_FAIL_COND_V( array.size() != p_surface->array_len, ERR_INVALID_PARAMETER ); + + + DVector<Vector3>::Read read = array.read(); + const Vector3* src=read.ptr(); + + // setting vertices means regenerating the AABB + AABB aabb; + + float scale=1; + float max=0; + + + + if (p_surface->array[VS::ARRAY_VERTEX].datatype==_GL_HALF_FLOAT_OES) { + + for (int i=0;i<p_surface->array_len;i++) { + + + uint16_t vector[3]={ make_half_float(src[i].x), make_half_float(src[i].y), make_half_float(src[i].z) }; + + copymem(&p_mem[a.ofs+i*stride], vector, a.size); + + if (i==0) { + + aabb=AABB(src[i],Vector3()); + } else { + + aabb.expand_to( src[i] ); + } + } + + + } else { + for (int i=0;i<p_surface->array_len;i++) { + + + GLfloat vector[3]={ src[i].x, src[i].y, src[i].z }; + + copymem(&p_mem[a.ofs+i*stride], vector, a.size); + + if (i==0) { + + aabb=AABB(src[i],Vector3()); + } else { + + aabb.expand_to( src[i] ); + } + } + } + + if (p_main) { + p_surface->aabb=aabb; + p_surface->vertex_scale=scale; + } + + + } break; + case VS::ARRAY_NORMAL: { + + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::VECTOR3_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<Vector3> array = p_arrays[ai]; + ERR_FAIL_COND_V( array.size() != p_surface->array_len, ERR_INVALID_PARAMETER ); + + + DVector<Vector3>::Read read = array.read(); + const Vector3* src=read.ptr(); + + // setting vertices means regenerating the AABB + + if (p_surface->array[VS::ARRAY_NORMAL].datatype==GL_BYTE) { + + for (int i=0;i<p_surface->array_len;i++) { + + GLbyte vector[4]={ + CLAMP(src[i].x*127,-128,127), + CLAMP(src[i].y*127,-128,127), + CLAMP(src[i].z*127,-128,127), + 0, + }; + + copymem(&p_mem[a.ofs+i*stride], vector, a.size); + + } + + } else { + for (int i=0;i<p_surface->array_len;i++) { + + + GLfloat vector[3]={ src[i].x, src[i].y, src[i].z }; + copymem(&p_mem[a.ofs+i*stride], vector, a.size); + + } + } + + + } break; + case VS::ARRAY_TANGENT: { + + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::REAL_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<real_t> array = p_arrays[ai]; + + ERR_FAIL_COND_V( array.size() != p_surface->array_len*4, ERR_INVALID_PARAMETER ); + + + DVector<real_t>::Read read = array.read(); + const real_t* src = read.ptr(); + + if (p_surface->array[VS::ARRAY_TANGENT].datatype==GL_BYTE) { + + for (int i=0;i<p_surface->array_len;i++) { + + GLbyte xyzw[4]={ + CLAMP(src[i*4+0]*127,-128,127), + CLAMP(src[i*4+1]*127,-128,127), + CLAMP(src[i*4+2]*127,-128,127), + CLAMP(src[i*4+3]*127,-128,127) + }; + + copymem(&p_mem[a.ofs+i*stride], xyzw, a.size); + + } + + + } else { + for (int i=0;i<p_surface->array_len;i++) { + + GLfloat xyzw[4]={ + src[i*4+0], + src[i*4+1], + src[i*4+2], + src[i*4+3] + }; + + copymem(&p_mem[a.ofs+i*stride], xyzw, a.size); + + } + } + + } break; + case VS::ARRAY_COLOR: { + + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::COLOR_ARRAY, ERR_INVALID_PARAMETER ); + + + DVector<Color> array = p_arrays[ai]; + + ERR_FAIL_COND_V( array.size() != p_surface->array_len, ERR_INVALID_PARAMETER ); + + + DVector<Color>::Read read = array.read(); + const Color* src = read.ptr(); + bool alpha=false; + + for (int i=0;i<p_surface->array_len;i++) { + + if (src[i].a<0.98) // tolerate alpha a bit, for crappy exporters + alpha=true; + + uint8_t colors[4]; + + for(int j=0;j<4;j++) { + + colors[j]=CLAMP( int((src[i][j])*255.0), 0,255 ); + } + + copymem(&p_mem[a.ofs+i*stride], colors, a.size); + + } + + if (p_main) + p_surface->has_alpha=alpha; + + } break; + case VS::ARRAY_TEX_UV: + case VS::ARRAY_TEX_UV2: { + + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::VECTOR3_ARRAY && p_arrays[ai].get_type() != Variant::VECTOR2_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<Vector2> array = p_arrays[ai]; + + ERR_FAIL_COND_V( array.size() != p_surface->array_len , ERR_INVALID_PARAMETER); + + DVector<Vector2>::Read read = array.read(); + + const Vector2 * src=read.ptr(); + float scale=1.0; + + + if (p_surface->array[ai].datatype==_GL_HALF_FLOAT_OES) { + + for (int i=0;i<p_surface->array_len;i++) { + + uint16_t uv[2]={ make_half_float(src[i].x) , make_half_float(src[i].y) }; + copymem(&p_mem[a.ofs+i*stride], uv, a.size); + } + + } else { + for (int i=0;i<p_surface->array_len;i++) { + + GLfloat uv[2]={ src[i].x , src[i].y }; + + copymem(&p_mem[a.ofs+i*stride], uv, a.size); + + } + } + + if (p_main) { + + if (ai==VS::ARRAY_TEX_UV) { + + p_surface->uv_scale=scale; + } + if (ai==VS::ARRAY_TEX_UV2) { + + p_surface->uv2_scale=scale; + } + } + + } break; + case VS::ARRAY_WEIGHTS: { + + + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::REAL_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<real_t> array = p_arrays[ai]; + + ERR_FAIL_COND_V( array.size() != p_surface->array_len*VS::ARRAY_WEIGHTS_SIZE, ERR_INVALID_PARAMETER ); + + + DVector<real_t>::Read read = array.read(); + + const real_t * src = read.ptr(); + + if (p_surface->array[VS::ARRAY_WEIGHTS].datatype==GL_UNSIGNED_SHORT) { + + for (int i=0;i<p_surface->array_len;i++) { + + GLushort data[VS::ARRAY_WEIGHTS_SIZE]; + for (int j=0;j<VS::ARRAY_WEIGHTS_SIZE;j++) { + data[j]=CLAMP(src[i*VS::ARRAY_WEIGHTS_SIZE+j]*65535,0,65535); + } + + copymem(&p_mem[a.ofs+i*stride], data, a.size); + } + } else { + + for (int i=0;i<p_surface->array_len;i++) { + + GLfloat data[VS::ARRAY_WEIGHTS_SIZE]; + for (int j=0;j<VS::ARRAY_WEIGHTS_SIZE;j++) { + data[j]=src[i*VS::ARRAY_WEIGHTS_SIZE+j]; + } + + copymem(&p_mem[a.ofs+i*stride], data, a.size); + + + } + + + } + + } break; + case VS::ARRAY_BONES: { + + + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::REAL_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<int> array = p_arrays[ai]; + + ERR_FAIL_COND_V( array.size() != p_surface->array_len*VS::ARRAY_WEIGHTS_SIZE, ERR_INVALID_PARAMETER ); + + + DVector<int>::Read read = array.read(); + + const int * src = read.ptr(); + + p_surface->max_bone=0; + + + if (p_surface->array[VS::ARRAY_BONES].datatype==GL_UNSIGNED_BYTE) { + + for (int i=0;i<p_surface->array_len;i++) { + + GLubyte data[VS::ARRAY_WEIGHTS_SIZE]; + for (int j=0;j<VS::ARRAY_WEIGHTS_SIZE;j++) { + data[j]=CLAMP(src[i*VS::ARRAY_WEIGHTS_SIZE+j],0,255); + p_surface->max_bone=MAX(data[j],p_surface->max_bone); + + } + + copymem(&p_mem[a.ofs+i*stride], data, a.size); + + + } + + } else { + for (int i=0;i<p_surface->array_len;i++) { + + GLushort data[VS::ARRAY_WEIGHTS_SIZE]; + for (int j=0;j<VS::ARRAY_WEIGHTS_SIZE;j++) { + data[j]=src[i*VS::ARRAY_WEIGHTS_SIZE+j]; + p_surface->max_bone=MAX(data[j],p_surface->max_bone); + + } + + copymem(&p_mem[a.ofs+i*stride], data, a.size); + + + } + } + + + } break; + case VS::ARRAY_INDEX: { + + ERR_FAIL_COND_V( p_surface->index_array_len<=0, ERR_INVALID_DATA ); + ERR_FAIL_COND_V( p_arrays[ai].get_type() != Variant::INT_ARRAY, ERR_INVALID_PARAMETER ); + + DVector<int> indices = p_arrays[ai]; + ERR_FAIL_COND_V( indices.size() == 0, ERR_INVALID_PARAMETER ); + ERR_FAIL_COND_V( indices.size() != p_surface->index_array_len, ERR_INVALID_PARAMETER ); + + /* determine wether using 16 or 32 bits indices */ + + DVector<int>::Read read = indices.read(); + const int *src=read.ptr(); + + for (int i=0;i<p_surface->index_array_len;i++) { + + + if (a.size==2) { + uint16_t v=src[i]; + + copymem(&p_index_mem[i*a.size], &v, a.size); + } else { + uint32_t v=src[i]; + + copymem(&p_index_mem[i*a.size], &v, a.size); + } + } + + + } break; + + + default: { ERR_FAIL_V(ERR_INVALID_PARAMETER);} + } + + p_surface->configured_format|=(1<<ai); + } + + return OK; +} + + + +void RasterizerGLES2::mesh_add_custom_surface(RID p_mesh,const Variant& p_dat) { + + ERR_EXPLAIN("OpenGL Rasterizer does not support custom surfaces. Running on wrong platform?"); + ERR_FAIL_V(); +} + +Array RasterizerGLES2::mesh_get_surface_arrays(RID p_mesh,int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,Array()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Array() ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, Array() ); + + return surface->data; + + +} +Array RasterizerGLES2::mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const{ + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,Array()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Array() ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, Array() ); + + return surface->morph_data; + +} + + +void RasterizerGLES2::mesh_set_morph_target_count(RID p_mesh,int p_amount) { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + ERR_FAIL_COND( mesh->surfaces.size()!=0 ); + + mesh->morph_target_count=p_amount; + +} + +int RasterizerGLES2::mesh_get_morph_target_count(RID p_mesh) const{ + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,-1); + + return mesh->morph_target_count; + +} + +void RasterizerGLES2::mesh_set_morph_target_mode(RID p_mesh,VS::MorphTargetMode p_mode) { + + ERR_FAIL_INDEX(p_mode,2); + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + + mesh->morph_target_mode=p_mode; + +} + +VS::MorphTargetMode RasterizerGLES2::mesh_get_morph_target_mode(RID p_mesh) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,VS::MORPH_MODE_NORMALIZED); + + return mesh->morph_target_mode; + +} + + + +void RasterizerGLES2::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned) { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size() ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND( !surface); + + if (surface->material_owned && surface->material.is_valid()) + free(surface->material); + + surface->material_owned=p_owned; + + surface->material=p_material; +} + +RID RasterizerGLES2::mesh_surface_get_material(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,RID()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), RID() ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, RID() ); + + return surface->material; +} + +int RasterizerGLES2::mesh_surface_get_array_len(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,-1); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), -1 ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, -1 ); + + return surface->array_len; +} +int RasterizerGLES2::mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,-1); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), -1 ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, -1 ); + + return surface->index_array_len; +} +uint32_t RasterizerGLES2::mesh_surface_get_format(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0 ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, 0 ); + + return surface->format; +} +VS::PrimitiveType RasterizerGLES2::mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,VS::PRIMITIVE_POINTS); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), VS::PRIMITIVE_POINTS ); + Surface *surface = mesh->surfaces[p_surface]; + ERR_FAIL_COND_V( !surface, VS::PRIMITIVE_POINTS ); + + return surface->primitive; +} + +void RasterizerGLES2::mesh_remove_surface(RID p_mesh,int p_index) { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_index, mesh->surfaces.size() ); + Surface *surface = mesh->surfaces[p_index]; + ERR_FAIL_COND( !surface); + + if (surface->vertex_id) + glDeleteBuffers(1,&surface->vertex_id); + if (surface->index_id) + glDeleteBuffers(1,&surface->index_id); + + + if (mesh->morph_target_count) { + for(int i=0;i<mesh->morph_target_count;i++) + memfree(surface->morph_targets_local[i].array); + memfree( surface->morph_targets_local ); + } + + memdelete( mesh->surfaces[p_index] ); + mesh->surfaces.remove(p_index); + +} +int RasterizerGLES2::mesh_get_surface_count(RID p_mesh) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,-1); + + return mesh->surfaces.size(); +} + +AABB RasterizerGLES2::mesh_get_aabb(RID p_mesh) const { + + Mesh *mesh = mesh_owner.get( p_mesh ); + ERR_FAIL_COND_V(!mesh,AABB()); + + AABB aabb; + + for (int i=0;i<mesh->surfaces.size();i++) { + + if (i==0) + aabb=mesh->surfaces[i]->aabb; + else + aabb.merge_with(mesh->surfaces[i]->aabb); + } + + return aabb; +} +/* MULTIMESH API */ + +RID RasterizerGLES2::multimesh_create() { + + return multimesh_owner.make_rid( memnew( MultiMesh )); +} + +void RasterizerGLES2::multimesh_set_instance_count(RID p_multimesh,int p_count) { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND(!multimesh); + + //multimesh->elements.clear(); // make sure to delete everything, so it "fails" in all implementations + + if (use_texture_instancing) { + + if (nearest_power_of_2(p_count)!=nearest_power_of_2(multimesh->elements.size())) { + if (multimesh->tex_id) { + glDeleteTextures(1,&multimesh->tex_id); + multimesh->tex_id=0; + } + + if (p_count) { + + uint32_t po2 = nearest_power_of_2(p_count); + if (po2&0xAAAAAAAA) { + //half width + + multimesh->tw=Math::sqrt(po2*2); + multimesh->th=multimesh->tw/2; + } else { + + multimesh->tw=Math::sqrt(po2); + multimesh->th=multimesh->tw; + + } + multimesh->tw*=4; + if (multimesh->th==0) + multimesh->th=1; + + + + glGenTextures(1, &multimesh->tex_id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,multimesh->tex_id); + +#ifdef GLEW_ENABLED + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, multimesh->tw, multimesh->th, 0, GL_RGBA, GL_FLOAT,NULL); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, multimesh->tw, multimesh->th, 0, GL_RGBA, GL_FLOAT,NULL); +#endif + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + //multimesh->pixel_size=1.0/ps; + + glBindTexture(GL_TEXTURE_2D,0); + } + + } + + if (!multimesh->dirty_list.in_list()) { + _multimesh_dirty_list.add(&multimesh->dirty_list); + } + + } + + multimesh->elements.resize(p_count); + +} +int RasterizerGLES2::multimesh_get_instance_count(RID p_multimesh) const { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND_V(!multimesh,-1); + + return multimesh->elements.size(); +} + +void RasterizerGLES2::multimesh_set_mesh(RID p_multimesh,RID p_mesh) { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND(!multimesh); + + multimesh->mesh=p_mesh; + +} +void RasterizerGLES2::multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb) { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND(!multimesh); + multimesh->aabb=p_aabb; +} +void RasterizerGLES2::multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform) { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index,multimesh->elements.size()); + MultiMesh::Element &e=multimesh->elements[p_index]; + + e.matrix[0]=p_transform.basis.elements[0][0]; + e.matrix[1]=p_transform.basis.elements[1][0]; + e.matrix[2]=p_transform.basis.elements[2][0]; + e.matrix[3]=0; + e.matrix[4]=p_transform.basis.elements[0][1]; + e.matrix[5]=p_transform.basis.elements[1][1]; + e.matrix[6]=p_transform.basis.elements[2][1]; + e.matrix[7]=0; + e.matrix[8]=p_transform.basis.elements[0][2]; + e.matrix[9]=p_transform.basis.elements[1][2]; + e.matrix[10]=p_transform.basis.elements[2][2]; + e.matrix[11]=0; + e.matrix[12]=p_transform.origin.x; + e.matrix[13]=p_transform.origin.y; + e.matrix[14]=p_transform.origin.z; + e.matrix[15]=1; + + if (!multimesh->dirty_list.in_list()) { + _multimesh_dirty_list.add(&multimesh->dirty_list); + } + +} +void RasterizerGLES2::multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color) { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND(!multimesh) + ERR_FAIL_INDEX(p_index,multimesh->elements.size()); + MultiMesh::Element &e=multimesh->elements[p_index]; + e.color[0]=CLAMP(p_color.r*255,0,255); + e.color[1]=CLAMP(p_color.g*255,0,255); + e.color[2]=CLAMP(p_color.b*255,0,255); + e.color[3]=CLAMP(p_color.a*255,0,255); + + if (!multimesh->dirty_list.in_list()) { + _multimesh_dirty_list.add(&multimesh->dirty_list); + } + +} + +RID RasterizerGLES2::multimesh_get_mesh(RID p_multimesh) const { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND_V(!multimesh,RID()); + + return multimesh->mesh; +} +AABB RasterizerGLES2::multimesh_get_aabb(RID p_multimesh) const { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND_V(!multimesh,AABB()); + + return multimesh->aabb; +} + +Transform RasterizerGLES2::multimesh_instance_get_transform(RID p_multimesh,int p_index) const { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND_V(!multimesh,Transform()); + + ERR_FAIL_INDEX_V(p_index,multimesh->elements.size(),Transform()); + MultiMesh::Element &e=multimesh->elements[p_index]; + + Transform tr; + + tr.basis.elements[0][0]=e.matrix[0]; + tr.basis.elements[1][0]=e.matrix[1]; + tr.basis.elements[2][0]=e.matrix[2]; + tr.basis.elements[0][1]=e.matrix[4]; + tr.basis.elements[1][1]=e.matrix[5]; + tr.basis.elements[2][1]=e.matrix[6]; + tr.basis.elements[0][2]=e.matrix[8]; + tr.basis.elements[1][2]=e.matrix[9]; + tr.basis.elements[2][2]=e.matrix[10]; + tr.origin.x=e.matrix[12]; + tr.origin.y=e.matrix[13]; + tr.origin.z=e.matrix[14]; + + return tr; +} +Color RasterizerGLES2::multimesh_instance_get_color(RID p_multimesh,int p_index) const { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND_V(!multimesh,Color()); + ERR_FAIL_INDEX_V(p_index,multimesh->elements.size(),Color()); + MultiMesh::Element &e=multimesh->elements[p_index]; + Color c; + c.r=e.color[0]/255.0; + c.g=e.color[1]/255.0; + c.b=e.color[2]/255.0; + c.a=e.color[3]/255.0; + + return c; + +} + +void RasterizerGLES2::multimesh_set_visible_instances(RID p_multimesh,int p_visible) { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND(!multimesh); + multimesh->visible=p_visible; + +} + +int RasterizerGLES2::multimesh_get_visible_instances(RID p_multimesh) const { + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND_V(!multimesh,-1); + return multimesh->visible; + +} + + +/* PARTICLES API */ + +RID RasterizerGLES2::particles_create() { + + Particles *particles = memnew( Particles ); + ERR_FAIL_COND_V(!particles,RID()); + return particles_owner.make_rid(particles); +} + +void RasterizerGLES2::particles_set_amount(RID p_particles, int p_amount) { + + ERR_FAIL_COND(p_amount<1); + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.amount=p_amount; + +} + +int RasterizerGLES2::particles_get_amount(RID p_particles) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,-1); + return particles->data.amount; + +} + +void RasterizerGLES2::particles_set_emitting(RID p_particles, bool p_emitting) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.emitting=p_emitting;; + +} +bool RasterizerGLES2::particles_is_emitting(RID p_particles) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,false); + return particles->data.emitting; + +} + +void RasterizerGLES2::particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.visibility_aabb=p_visibility; + +} + +void RasterizerGLES2::particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + + particles->data.emission_half_extents=p_half_extents; +} +Vector3 RasterizerGLES2::particles_get_emission_half_extents(RID p_particles) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,Vector3()); + + return particles->data.emission_half_extents; +} + +void RasterizerGLES2::particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + + particles->data.emission_base_velocity=p_base_velocity; +} + +Vector3 RasterizerGLES2::particles_get_emission_base_velocity(RID p_particles) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,Vector3()); + + return particles->data.emission_base_velocity; +} + + +void RasterizerGLES2::particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + + particles->data.emission_points=p_points; +} + +DVector<Vector3> RasterizerGLES2::particles_get_emission_points(RID p_particles) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,DVector<Vector3>()); + + return particles->data.emission_points; + +} + +void RasterizerGLES2::particles_set_gravity_normal(RID p_particles, const Vector3& p_normal) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + + particles->data.gravity_normal=p_normal; + +} +Vector3 RasterizerGLES2::particles_get_gravity_normal(RID p_particles) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,Vector3()); + + return particles->data.gravity_normal; +} + + +AABB RasterizerGLES2::particles_get_visibility_aabb(RID p_particles) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,AABB()); + return particles->data.visibility_aabb; + +} + +void RasterizerGLES2::particles_set_variable(RID p_particles, VS::ParticleVariable p_variable,float p_value) { + + ERR_FAIL_INDEX(p_variable,VS::PARTICLE_VAR_MAX); + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.particle_vars[p_variable]=p_value; + +} +float RasterizerGLES2::particles_get_variable(RID p_particles, VS::ParticleVariable p_variable) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,-1); + return particles->data.particle_vars[p_variable]; +} + +void RasterizerGLES2::particles_set_randomness(RID p_particles, VS::ParticleVariable p_variable,float p_randomness) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.particle_randomness[p_variable]=p_randomness; + +} +float RasterizerGLES2::particles_get_randomness(RID p_particles, VS::ParticleVariable p_variable) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,-1); + return particles->data.particle_randomness[p_variable]; + +} + +void RasterizerGLES2::particles_set_color_phases(RID p_particles, int p_phases) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + ERR_FAIL_COND( p_phases<0 || p_phases>VS::MAX_PARTICLE_COLOR_PHASES ); + particles->data.color_phase_count=p_phases; + +} +int RasterizerGLES2::particles_get_color_phases(RID p_particles) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,-1); + return particles->data.color_phase_count; +} + + +void RasterizerGLES2::particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos) { + + ERR_FAIL_INDEX(p_phase, VS::MAX_PARTICLE_COLOR_PHASES); + if (p_pos<0.0) + p_pos=0.0; + if (p_pos>1.0) + p_pos=1.0; + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.color_phases[p_phase].pos=p_pos; + +} +float RasterizerGLES2::particles_get_color_phase_pos(RID p_particles, int p_phase) const { + + ERR_FAIL_INDEX_V(p_phase, VS::MAX_PARTICLE_COLOR_PHASES, -1.0); + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,-1); + return particles->data.color_phases[p_phase].pos; + +} + +void RasterizerGLES2::particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color) { + + ERR_FAIL_INDEX(p_phase, VS::MAX_PARTICLE_COLOR_PHASES); + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.color_phases[p_phase].color=p_color; + + //update alpha + particles->has_alpha=false; + for(int i=0;i<VS::MAX_PARTICLE_COLOR_PHASES;i++) { + if (particles->data.color_phases[i].color.a<0.99) + particles->has_alpha=true; + } + +} + +Color RasterizerGLES2::particles_get_color_phase_color(RID p_particles, int p_phase) const { + + ERR_FAIL_INDEX_V(p_phase, VS::MAX_PARTICLE_COLOR_PHASES, Color()); + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,Color()); + return particles->data.color_phases[p_phase].color; + +} + +void RasterizerGLES2::particles_set_attractors(RID p_particles, int p_attractors) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + ERR_FAIL_COND( p_attractors<0 || p_attractors>VisualServer::MAX_PARTICLE_ATTRACTORS ); + particles->data.attractor_count=p_attractors; + +} +int RasterizerGLES2::particles_get_attractors(RID p_particles) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,-1); + return particles->data.attractor_count; +} + +void RasterizerGLES2::particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + ERR_FAIL_INDEX(p_attractor,particles->data.attractor_count); + particles->data.attractors[p_attractor].pos=p_pos;; +} +Vector3 RasterizerGLES2::particles_get_attractor_pos(RID p_particles,int p_attractor) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,Vector3()); + ERR_FAIL_INDEX_V(p_attractor,particles->data.attractor_count,Vector3()); + return particles->data.attractors[p_attractor].pos; +} + +void RasterizerGLES2::particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + ERR_FAIL_INDEX(p_attractor,particles->data.attractor_count); + particles->data.attractors[p_attractor].force=p_force; +} + +float RasterizerGLES2::particles_get_attractor_strength(RID p_particles,int p_attractor) const { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,0); + ERR_FAIL_INDEX_V(p_attractor,particles->data.attractor_count,0); + return particles->data.attractors[p_attractor].force; +} + +void RasterizerGLES2::particles_set_material(RID p_particles, RID p_material,bool p_owned) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + if (particles->material_owned && particles->material.is_valid()) + free(particles->material); + + particles->material_owned=p_owned; + + particles->material=p_material; + +} +RID RasterizerGLES2::particles_get_material(RID p_particles) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,RID()); + return particles->material; + +} + +void RasterizerGLES2::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.local_coordinates=p_enable; + +} + +bool RasterizerGLES2::particles_is_using_local_coordinates(RID p_particles) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,false); + return particles->data.local_coordinates; +} +bool RasterizerGLES2::particles_has_height_from_velocity(RID p_particles) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,false); + return particles->data.height_from_velocity; +} + +void RasterizerGLES2::particles_set_height_from_velocity(RID p_particles, bool p_enable) { + + Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND(!particles); + particles->data.height_from_velocity=p_enable; + +} + +AABB RasterizerGLES2::particles_get_aabb(RID p_particles) const { + + const Particles* particles = particles_owner.get( p_particles ); + ERR_FAIL_COND_V(!particles,AABB()); + return particles->data.visibility_aabb; +} + +/* SKELETON API */ + +RID RasterizerGLES2::skeleton_create() { + + Skeleton *skeleton = memnew( Skeleton ); + ERR_FAIL_COND_V(!skeleton,RID()); + return skeleton_owner.make_rid( skeleton ); +} +void RasterizerGLES2::skeleton_resize(RID p_skeleton,int p_bones) { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND(!skeleton); + if (p_bones == skeleton->bones.size()) { + return; + }; + if (use_hw_skeleton_xform) { + + if (nearest_power_of_2(p_bones)!=nearest_power_of_2(skeleton->bones.size())) { + if (skeleton->tex_id) { + glDeleteTextures(1,&skeleton->tex_id); + skeleton->tex_id=0; + } + + if (p_bones) { + + glGenTextures(1, &skeleton->tex_id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,skeleton->tex_id); + int ps = nearest_power_of_2(p_bones*3); +#ifdef GLEW_ENABLED + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, ps, 1, 0, GL_RGBA, GL_FLOAT,skel_default.ptr()); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ps, 1, 0, GL_RGBA, GL_FLOAT,skel_default.ptr()); +#endif + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + skeleton->pixel_size=1.0/ps; + + glBindTexture(GL_TEXTURE_2D,0); + } + + } + + if (!skeleton->dirty_list.in_list()) { + _skeleton_dirty_list.add(&skeleton->dirty_list); + } + + } + skeleton->bones.resize(p_bones); + +} +int RasterizerGLES2::skeleton_get_bone_count(RID p_skeleton) const { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND_V(!skeleton, -1); + return skeleton->bones.size(); +} +void RasterizerGLES2::skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform) { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_INDEX( p_bone, skeleton->bones.size() ); + + Skeleton::Bone &b = skeleton->bones[p_bone]; + + b.mtx[0][0]=p_transform.basis[0][0]; + b.mtx[0][1]=p_transform.basis[1][0]; + b.mtx[0][2]=p_transform.basis[2][0]; + b.mtx[1][0]=p_transform.basis[0][1]; + b.mtx[1][1]=p_transform.basis[1][1]; + b.mtx[1][2]=p_transform.basis[2][1]; + b.mtx[2][0]=p_transform.basis[0][2]; + b.mtx[2][1]=p_transform.basis[1][2]; + b.mtx[2][2]=p_transform.basis[2][2]; + b.mtx[3][0]=p_transform.origin[0]; + b.mtx[3][1]=p_transform.origin[1]; + b.mtx[3][2]=p_transform.origin[2]; + + if (skeleton->tex_id) { + if (!skeleton->dirty_list.in_list()) { + _skeleton_dirty_list.add(&skeleton->dirty_list); + } + } + +} + +Transform RasterizerGLES2::skeleton_bone_get_transform(RID p_skeleton,int p_bone) { + + Skeleton *skeleton = skeleton_owner.get( p_skeleton ); + ERR_FAIL_COND_V(!skeleton, Transform()); + ERR_FAIL_INDEX_V( p_bone, skeleton->bones.size(), Transform() ); + + const Skeleton::Bone &b = skeleton->bones[p_bone]; + + Transform t; + t.basis[0][0]=b.mtx[0][0]; + t.basis[1][0]=b.mtx[0][1]; + t.basis[2][0]=b.mtx[0][2]; + t.basis[0][1]=b.mtx[1][0]; + t.basis[1][1]=b.mtx[1][1]; + t.basis[2][1]=b.mtx[1][2]; + t.basis[0][2]=b.mtx[2][0]; + t.basis[1][2]=b.mtx[2][1]; + t.basis[2][2]=b.mtx[2][2]; + t.origin[0]=b.mtx[3][0]; + t.origin[1]=b.mtx[3][1]; + t.origin[2]=b.mtx[3][2]; + + return t; + +} + + +/* LIGHT API */ + +RID RasterizerGLES2::light_create(VS::LightType p_type) { + + Light *light = memnew( Light ); + light->type=p_type; + return light_owner.make_rid(light); +} + +VS::LightType RasterizerGLES2::light_get_type(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,VS::LIGHT_OMNI); + return light->type; +} + +void RasterizerGLES2::light_set_color(RID p_light,VS::LightColor p_type, const Color& p_color) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX( p_type, 3 ); + light->colors[p_type]=p_color; +} +Color RasterizerGLES2::light_get_color(RID p_light,VS::LightColor p_type) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light, Color()); + ERR_FAIL_INDEX_V( p_type, 3, Color() ); + return light->colors[p_type]; +} + +void RasterizerGLES2::light_set_shadow(RID p_light,bool p_enabled) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + light->shadow_enabled=p_enabled; +} + +bool RasterizerGLES2::light_has_shadow(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,false); + return light->shadow_enabled; +} + +void RasterizerGLES2::light_set_volumetric(RID p_light,bool p_enabled) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + light->volumetric_enabled=p_enabled; + +} +bool RasterizerGLES2::light_is_volumetric(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,false); + return light->volumetric_enabled; +} + +void RasterizerGLES2::light_set_projector(RID p_light,RID p_texture) { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND(!light); + light->projector=p_texture; +} +RID RasterizerGLES2::light_get_projector(RID p_light) const { + + Light *light = light_owner.get(p_light); + ERR_FAIL_COND_V(!light,RID()); + return light->projector; +} + +void RasterizerGLES2::light_set_var(RID p_light, VS::LightParam p_var, float p_value) { + + Light * light = light_owner.get( p_light ); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX( p_var, VS::LIGHT_PARAM_MAX ); + + light->vars[p_var]=p_value; +} +float RasterizerGLES2::light_get_var(RID p_light, VS::LightParam p_var) const { + + Light * light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light,0); + + ERR_FAIL_INDEX_V( p_var, VS::LIGHT_PARAM_MAX,0 ); + + return light->vars[p_var]; +} + +void RasterizerGLES2::light_set_operator(RID p_light,VS::LightOp p_op) { + +}; + +VS::LightOp RasterizerGLES2::light_get_operator(RID p_light) const { + + return VS::LightOp(); +}; + +void RasterizerGLES2::light_omni_set_shadow_mode(RID p_light,VS::LightOmniShadowMode p_mode) { + + Light * light = light_owner.get( p_light ); + ERR_FAIL_COND(!light); + + light->omni_shadow_mode=p_mode; +} +VS::LightOmniShadowMode RasterizerGLES2::light_omni_get_shadow_mode(RID p_light) const { + + const Light * light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light,VS::LIGHT_OMNI_SHADOW_DEFAULT); + + return light->omni_shadow_mode; +} + +void RasterizerGLES2::light_directional_set_shadow_mode(RID p_light,VS::LightDirectionalShadowMode p_mode) { + + Light * light = light_owner.get( p_light ); + ERR_FAIL_COND(!light); + + light->directional_shadow_mode=p_mode; +} + + +VS::LightDirectionalShadowMode RasterizerGLES2::light_directional_get_shadow_mode(RID p_light) const { + + const Light * light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light,VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); + + return light->directional_shadow_mode; +} + +void RasterizerGLES2::light_directional_set_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param, float p_value) { + + Light * light = light_owner.get( p_light ); + ERR_FAIL_COND(!light); + + light->directional_shadow_param[p_param]=p_value; +} + +float RasterizerGLES2::light_directional_get_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param) const { + + const Light * light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light,0); + return light->directional_shadow_param[p_param]; +} + + +AABB RasterizerGLES2::light_get_aabb(RID p_light) const { + + Light *light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light,AABB()); + + switch( light->type ) { + + case VS::LIGHT_SPOT: { + + float len=light->vars[VS::LIGHT_PARAM_RADIUS]; + float size=Math::tan(Math::deg2rad(light->vars[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->vars[VS::LIGHT_PARAM_RADIUS]; + return AABB( -Vector3(r,r,r), Vector3(r,r,r)*2 ); + } break; + case VS::LIGHT_DIRECTIONAL: { + + return AABB(); + } break; + default: {} + } + + ERR_FAIL_V( AABB() ); +} + + +RID RasterizerGLES2::light_instance_create(RID p_light) { + + Light *light = light_owner.get( p_light ); + ERR_FAIL_COND_V(!light, RID()); + + LightInstance *light_instance = memnew( LightInstance ); + + light_instance->light=p_light; + light_instance->base=light; + light_instance->last_pass=0; + + return light_instance_owner.make_rid( light_instance ); +} +void RasterizerGLES2::light_instance_set_transform(RID p_light_instance,const Transform& p_transform) { + + LightInstance *lighti = light_instance_owner.get( p_light_instance ); + ERR_FAIL_COND(!lighti); + lighti->transform=p_transform; + +} + + +Rasterizer::ShadowType RasterizerGLES2::light_instance_get_shadow_type(RID p_light_instance, bool p_far) const { + + LightInstance *lighti = light_instance_owner.get( p_light_instance ); + ERR_FAIL_COND_V(!lighti,Rasterizer::SHADOW_NONE); + + switch(lighti->base->type) { + + case VS::LIGHT_DIRECTIONAL: { + switch(lighti->base->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { + return SHADOW_ORTHOGONAL; + } break; + case VS::LIGHT_DIRECTIONAL_SHADOW_PERSPECTIVE:{ + return SHADOW_PSM; + } break; + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_SPLIT:{ + return SHADOW_PSSM; + } break; + } + + }break; + case VS::LIGHT_OMNI: return SHADOW_DUAL_PARABOLOID; break; + case VS::LIGHT_SPOT: return SHADOW_SIMPLE; break; + } + + return Rasterizer::SHADOW_NONE; +} + +int RasterizerGLES2::light_instance_get_shadow_passes(RID p_light_instance) const { + + LightInstance *lighti = light_instance_owner.get( p_light_instance ); + ERR_FAIL_COND_V(!lighti,0); + if (lighti->base->type==VS::LIGHT_OMNI || (lighti->base->type==VS::LIGHT_DIRECTIONAL && lighti->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_SPLIT)) + return 2; // dp + else + return 1; +} + +void RasterizerGLES2::light_instance_set_shadow_transform(RID p_light_instance, int p_index, const CameraMatrix& p_camera, const Transform& p_transform, float p_split_near,float p_split_far) { + + LightInstance *lighti = light_instance_owner.get( p_light_instance ); + ERR_FAIL_COND(!lighti); + + ERR_FAIL_COND(lighti->base->type!=VS::LIGHT_DIRECTIONAL); +// ERR_FAIL_INDEX(p_index,1); + + if (p_index==0) { + lighti->custom_projection=p_camera; + lighti->custom_transform=p_transform; + //Plane p(0,0,-p_split_far,1); + //p=camera_projection.xform4(p); + //lighti->shadow_split=p.normal.z/p.d; + lighti->shadow_split=1.0/p_split_far; + + //lighti->shadow_split=-p_split_far; + } else { + + lighti->custom_projection2=p_camera; + lighti->custom_transform2=p_transform; + lighti->shadow_split2=p_split_far; + + } + +} + +int RasterizerGLES2::light_instance_get_shadow_size(RID p_light_instance, int p_index) const{ + + LightInstance *lighti = light_instance_owner.get( p_light_instance ); + ERR_FAIL_COND_V(!lighti,1); + ERR_FAIL_COND_V(!lighti->near_shadow_buffer,256); + return lighti->near_shadow_buffer->size/2; +} + +void RasterizerGLES2::shadow_clear_near() { + + + for(int i=0;i<near_shadow_buffers.size();i++) { + + if (near_shadow_buffers[i].owner) + near_shadow_buffers[i].owner->clear_near_shadow_buffers(); + } + +} + +bool RasterizerGLES2::shadow_allocate_near(RID p_light) { + + if (!use_shadow_mapping || !use_framebuffers) + return false; + + LightInstance *li = light_instance_owner.get(p_light); + ERR_FAIL_COND_V(!li,false); + ERR_FAIL_COND_V( li->near_shadow_buffer, false); + + int skip=0; + if (framebuffer.active) { + + int sc = framebuffer.scale; + while(sc>1) { + sc/=2; + skip++; + } + } + + for(int i=0;i<near_shadow_buffers.size();i++) { + + + if (skip>0) { + skip--; + continue; + } + + if (near_shadow_buffers[i].owner!=NULL) + continue; + + near_shadow_buffers[i].owner=li; + li->near_shadow_buffer=&near_shadow_buffers[i]; + return true; + } + + return false; +} + +bool RasterizerGLES2::shadow_allocate_far(RID p_light) { + + return false; +} + + +/* PARTICLES INSTANCE */ + +RID RasterizerGLES2::particles_instance_create(RID p_particles) { + + ERR_FAIL_COND_V(!particles_owner.owns(p_particles),RID()); + ParticlesInstance *particles_instance = memnew( ParticlesInstance ); + ERR_FAIL_COND_V(!particles_instance, RID() ); + particles_instance->particles=p_particles; + return particles_instance_owner.make_rid(particles_instance); +} + +void RasterizerGLES2::particles_instance_set_transform(RID p_particles_instance,const Transform& p_transform) { + + ParticlesInstance *particles_instance=particles_instance_owner.get(p_particles_instance); + ERR_FAIL_COND(!particles_instance); + particles_instance->transform=p_transform; +} + + + +RID RasterizerGLES2::viewport_data_create() { + + ViewportData *vd = memnew( ViewportData ); + + glActiveTexture(GL_TEXTURE0); + glGenFramebuffers(1, &vd->lum_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, vd->lum_fbo); + + glGenTextures(1, &vd->lum_color); + glBindTexture(GL_TEXTURE_2D, vd->lum_color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, vd->lum_color, 0); + + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, base_framebuffer); + DEBUG_TEST_ERROR("Viewport Data Init"); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Can't create framebuffer for vd"); + } + + return viewport_data_owner.make_rid(vd); +} + +RID RasterizerGLES2::render_target_create(){ + + RenderTarget *rt = memnew( RenderTarget ); + rt->fbo=0; + rt->width=0; + rt->height=0; + rt->last_pass=0; + + Texture *texture = memnew(Texture); + texture->active=false; + texture->total_data_size=0; + texture->render_target=rt; + rt->texture_ptr=texture; + rt->texture=texture_owner.make_rid( texture ); + rt->texture_ptr->active=false; + return render_target_owner.make_rid(rt); + +} +void RasterizerGLES2::render_target_set_size(RID p_render_target,int p_width,int p_height){ + + RenderTarget *rt = render_target_owner.get(p_render_target); + + if (p_width==rt->width && p_height==rt->height) + return; + + if (rt->width!=0 && rt->height!=0) { + + glDeleteFramebuffers(1,&rt->fbo); + glDeleteRenderbuffers(1,&rt->depth); + glDeleteTextures(1,&rt->color); + + rt->fbo=0; + rt->width=0; + rt->height=0; + rt->texture_ptr->tex_id=0; + rt->texture_ptr->active=false; + + } + + if (p_width==0 || p_height==0) + return; + + rt->width=p_width; + rt->height=p_height; + + //fbo + glGenFramebuffers(1, &rt->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + + //depth + glGenRenderbuffers(1, &rt->depth); + glBindRenderbuffer(GL_RENDERBUFFER, rt->depth ); + + glRenderbufferStorage(GL_RENDERBUFFER, use_depth24?_DEPTH_COMPONENT24_OES:GL_DEPTH_COMPONENT16, rt->width,rt->height); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); + + //color + glGenTextures(1, &rt->color); + glBindTexture(GL_TEXTURE_2D, rt->color); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + + rt->texture_ptr->tex_id=rt->color; + rt->texture_ptr->active=true; + rt->texture_ptr->width=p_width; + rt->texture_ptr->height=p_height; + +# + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + + glDeleteRenderbuffers(1,&rt->fbo); + glDeleteTextures(1,&rt->depth); + glDeleteTextures(1,&rt->color); + rt->fbo=0; + rt->width=0; + rt->height=0; + rt->color=0; + rt->depth=0; + rt->texture_ptr->tex_id=0; + rt->texture_ptr->active=false; + WARN_PRINT("Could not create framebuffer!!"); + } + + glBindFramebuffer(GL_FRAMEBUFFER, base_framebuffer); + +} + +RID RasterizerGLES2::render_target_get_texture(RID p_render_target) const{ + + const RenderTarget *rt = render_target_owner.get(p_render_target); + ERR_FAIL_COND_V(!rt,RID()); + return rt->texture; +} +bool RasterizerGLES2::render_target_renedered_in_frame(RID p_render_target){ + + RenderTarget *rt = render_target_owner.get(p_render_target); + ERR_FAIL_COND_V(!rt,false); + return rt->last_pass==frame; +} + + +/* RENDER API */ +/* all calls (inside begin/end shadow) are always warranted to be in the following order: */ + + + + + +void RasterizerGLES2::begin_frame() { + + + + + _update_framebuffer(); + + glDepthFunc(GL_LEQUAL); + glFrontFace(GL_CW); + + //fragment_lighting=Globals::get_singleton()->get("rasterizer/use_fragment_lighting"); + canvas_shader.set_conditional(CanvasShaderGLES2::USE_PIXEL_SNAP,GLOBAL_DEF("rasterizer/use_pixel_snap",false)); + + + window_size = Size2( OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height ); + + double time = (OS::get_singleton()->get_ticks_usec()/1000); // get msec + time/=1000.0; // make secs + time_delta=time-last_time; + last_time=time; + frame++; + clear_viewport(Color(1,0,0.5)); + + _rinfo.vertex_count=0; + _rinfo.object_count=0; + _rinfo.mat_change_count=0; + _rinfo.shader_change_count=0; + _rinfo.ci_draw_commands=0; + _rinfo.surface_count=0; + _rinfo.draw_calls=0; + + + _update_fixed_materials(); + while(_shader_dirty_list.first()) { + + _update_shader(_shader_dirty_list.first()->self()); + } + + while(_skeleton_dirty_list.first()) { + + + Skeleton *s=_skeleton_dirty_list.first()->self(); + + float *sk_float = (float*)skinned_buffer; + for(int i=0;i<s->bones.size();i++) { + + float *m = &sk_float[i*12]; + const Skeleton::Bone &b=s->bones[i]; + m[0]=b.mtx[0][0]; + m[1]=b.mtx[1][0]; + m[2]=b.mtx[2][0]; + m[3]=b.mtx[3][0]; + + m[4]=b.mtx[0][1]; + m[5]=b.mtx[1][1]; + m[6]=b.mtx[2][1]; + m[7]=b.mtx[3][1]; + + m[8]=b.mtx[0][2]; + m[9]=b.mtx[1][2]; + m[10]=b.mtx[2][2]; + m[11]=b.mtx[3][2]; + + + } + + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,s->tex_id); + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,nearest_power_of_2(s->bones.size()*3),1,GL_RGBA,GL_FLOAT,sk_float); + _skeleton_dirty_list.remove( _skeleton_dirty_list.first() ); + } + + while(_multimesh_dirty_list.first()) { + + + MultiMesh *s=_multimesh_dirty_list.first()->self(); + + float *sk_float = (float*)skinned_buffer; + for(int i=0;i<s->elements.size();i++) { + + float *m = &sk_float[i*16]; + const float *im=s->elements[i].matrix; + for(int j=0;j<16;j++) { + m[j]=im[j]; + } + + } + + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,s->tex_id); + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,s->tw,s->th,GL_RGBA,GL_FLOAT,sk_float); + _multimesh_dirty_list.remove( _multimesh_dirty_list.first() ); + } + + + draw_next_frame=false; +// material_shader.set_uniform_default(MaterialShaderGLES2::SCREENZ_SCALE, Math::fmod(time, 3600.0)); + /* nehe ?*/ + +// glClearColor(0,0,1,1); +// glClear(GL_COLOR_BUFFER_BIT); //should not clear if anything else cleared.. +} + +void RasterizerGLES2::capture_viewport(Image* r_capture) { +#if 0 + DVector<uint8_t> pixels; + pixels.resize(viewport.width*viewport.height*3); + DVector<uint8_t>::Write w = pixels.write(); +#ifdef GLEW_ENABLED + glReadBuffer(GL_COLOR_ATTACHMENT0); +#endif + glPixelStorei(GL_PACK_ALIGNMENT, 1); + if (current_rt) + glReadPixels( 0, 0, viewport.width, viewport.height,GL_RGB,GL_UNSIGNED_BYTE,w.ptr() ); + else + glReadPixels( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height,GL_RGB,GL_UNSIGNED_BYTE,w.ptr()); + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + w=DVector<uint8_t>::Write(); + + r_capture->create(viewport.width,viewport.height,0,Image::FORMAT_RGB,pixels); +#else + + + DVector<uint8_t> pixels; + pixels.resize(viewport.width*viewport.height*4); + DVector<uint8_t>::Write w = pixels.write(); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + if (current_rt) { +#ifdef GLEW_ENABLED + glReadBuffer(GL_COLOR_ATTACHMENT0); +#endif + glReadPixels( 0, 0, viewport.width, viewport.height,GL_RGBA,GL_UNSIGNED_BYTE,w.ptr() ); + } else { + // back? + glReadPixels( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height,GL_RGBA,GL_UNSIGNED_BYTE,w.ptr()); + } + w=DVector<uint8_t>::Write(); + + r_capture->create(viewport.width,viewport.height,0,Image::FORMAT_RGBA,pixels); + r_capture->flip_y(); + + +#endif + +} + + +void RasterizerGLES2::clear_viewport(const Color& p_color) { + + if (current_rt) { + + glScissor( 0, 0, viewport.width, viewport.height ); + } else { + glScissor( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height ); + } + + glEnable(GL_SCISSOR_TEST); + glClearColor(p_color.r,p_color.g,p_color.b,1.0); + glClear(GL_COLOR_BUFFER_BIT); //should not clear if anything else cleared.. + glDisable(GL_SCISSOR_TEST); +}; + + +void RasterizerGLES2::set_render_target(RID p_render_target) { + + if (!p_render_target.is_valid()) { + glBindFramebuffer(GL_FRAMEBUFFER,base_framebuffer); + current_rt=NULL; + + } else { + RenderTarget *rt = render_target_owner.get(p_render_target); + ERR_FAIL_COND(!rt); + ERR_FAIL_COND(rt->fbo==0); + glBindFramebuffer(GL_FRAMEBUFFER,rt->fbo); + current_rt=rt; + } + +} + +void RasterizerGLES2::set_viewport(const VS::ViewportRect& p_viewport) { + + viewport=p_viewport; + //viewport.width/=2; + //viewport.height/=2; + //print_line("viewport: "+itos(p_viewport.x)+","+itos(p_viewport.y)+","+itos(p_viewport.width)+","+itos(p_viewport.height)); + + if (current_rt) { + + glViewport( 0, 0,viewport.width, viewport.height ); + } else { + glViewport( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height ); + } +} + +void RasterizerGLES2::begin_scene(RID p_viewport_data,RID p_env,VS::ScenarioDebugMode p_debug) { + + + current_debug=p_debug; + opaque_render_list.clear(); + alpha_render_list.clear(); + light_instance_count=0; + current_env = p_env.is_valid() ? environment_owner.get(p_env) : NULL; + scene_pass++; + last_light_id=0; + directional_light_count=0; + lights_use_shadow=false; + texscreen_used=false; + current_vd=viewport_data_owner.get(p_viewport_data); + if (current_debug==VS::SCENARIO_DEBUG_WIREFRAME) { +#ifdef GLEW_ENABLED + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); +#endif + } + + //set state + + glCullFace(GL_FRONT); + cull_front=true; +}; + +void RasterizerGLES2::begin_shadow_map( RID p_light_instance, int p_shadow_pass ) { + + ERR_FAIL_COND(shadow); + shadow = light_instance_owner.get(p_light_instance); + shadow_pass=p_shadow_pass; + ERR_FAIL_COND(!shadow); + + opaque_render_list.clear(); + alpha_render_list.clear(); +// pre_zpass_render_list.clear(); + light_instance_count=0; + + glCullFace(GL_FRONT); + cull_front=true; + +} + +void RasterizerGLES2::set_camera(const Transform& p_world,const CameraMatrix& p_projection) { + + camera_transform=p_world; + camera_transform_inverse=camera_transform.inverse(); + camera_projection=p_projection; + camera_plane = Plane( camera_transform.origin, camera_transform.basis.get_axis(2) ); + camera_z_near=camera_projection.get_z_near(); + camera_z_far=camera_projection.get_z_far(); + camera_projection.get_viewport_size(camera_vp_size.x,camera_vp_size.y); +} + +void RasterizerGLES2::add_light( RID p_light_instance ) { + +#define LIGHT_FADE_TRESHOLD 0.05 + + ERR_FAIL_COND( light_instance_count >= MAX_SCENE_LIGHTS ); + + LightInstance *li = light_instance_owner.get(p_light_instance); + ERR_FAIL_COND(!li); + + + switch(li->base->type) { + + case VS::LIGHT_DIRECTIONAL: { + + ERR_FAIL_COND( directional_light_count >= RenderList::MAX_LIGHTS); + directional_lights[directional_light_count++]=li; + + if (li->base->shadow_enabled) { + CameraMatrix bias; + bias.set_light_bias(); + Transform modelview=Transform(camera_transform_inverse * li->custom_transform).inverse(); + li->shadow_projection = bias * li->custom_projection * modelview; + + Transform modelview2=Transform(camera_transform_inverse * li->custom_transform2).inverse(); + li->shadow_projection2 = bias * li->custom_projection2 * modelview2; + lights_use_shadow=true; + } + } break; + case VS::LIGHT_OMNI: { + + if (li->base->shadow_enabled) { + li->shadow_projection = Transform(camera_transform_inverse * li->transform).inverse(); + lights_use_shadow=true; + } + } break; + case VS::LIGHT_SPOT: { + + if (li->base->shadow_enabled) { + CameraMatrix bias; + bias.set_light_bias(); + Transform modelview=Transform(camera_transform_inverse * li->transform).inverse(); + li->shadow_projection = bias * li->projection * modelview; + lights_use_shadow=true; + } + } break; + + } + + + /* make light hash */ + + // actually, not really a hash, but helps to sort the lights + // and avoid recompiling redudant shader versions + + + li->last_pass=scene_pass; + li->sort_key=light_instance_count; + + light_instances[light_instance_count++]=li; + +} + +void RasterizerGLES2::_update_shader( Shader* p_shader) const { + + _shader_dirty_list.remove( &p_shader->dirty_list ); + + p_shader->valid=false; + + + p_shader->uniforms.clear(); + Vector<StringName> uniform_names; + + String vertex_code; + String vertex_globals; + ShaderCompilerGLES2::Flags flags; + + if (p_shader->mode==VS::SHADER_MATERIAL) { + Error err = shader_precompiler.compile(p_shader->vertex_code,ShaderLanguage::SHADER_MATERIAL_VERTEX,vertex_code,vertex_globals,flags,&p_shader->uniforms); + if (err) { + return; //invalid + } + } + + //print_line("compiled vertex: "+vertex_code); + //print_line("compiled vertex globals: "+vertex_globals); + + //print_line("UCV: "+itos(p_shader->uniforms.size())); + String fragment_code; + String fragment_globals; + + Error err = shader_precompiler.compile(p_shader->fragment_code,(p_shader->mode==VS::SHADER_MATERIAL?ShaderLanguage::SHADER_MATERIAL_FRAGMENT:ShaderLanguage::SHADER_POST_PROCESS),fragment_code,fragment_globals,flags,&p_shader->uniforms); + if (err) { + return; //invalid + } + + //print_line("compiled fragment: "+fragment_code); + //print_line("compiled fragment globals: "+fragment_globals); + + //print_line("UCF: "+itos(p_shader->uniforms.size())); + + for(Map<StringName,ShaderLanguage::Uniform>::Element *E=p_shader->uniforms.front();E;E=E->next()) { + + uniform_names.push_back("_"+String(E->key())); + } + + if (p_shader->mode==VS::SHADER_MATERIAL) { + //print_line("setting code to id.. "+itos(p_shader->custom_code_id)); + Vector<const char*> enablers; + if (flags.use_color_interp) + enablers.push_back("#define ENABLE_COLOR_INTERP\n"); + if (flags.use_uv_interp) + enablers.push_back("#define ENABLE_UV_INTERP\n"); + if (flags.use_uv2_interp) + enablers.push_back("#define ENABLE_UV2_INTERP\n"); + if (flags.use_tangent_interp) + enablers.push_back("#define ENABLE_TANGENT_INTERP\n"); + if (flags.use_var1_interp) + enablers.push_back("#define ENABLE_VAR1_INTERP\n"); + if (flags.use_var2_interp) + enablers.push_back("#define ENABLE_VAR2_INTERP\n"); + if (flags.uses_texscreen) { + enablers.push_back("#define ENABLE_TEXSCREEN\n"); + } + if (flags.uses_screen_uv) { + enablers.push_back("#define ENABLE_SCREEN_UV\n"); + } + + material_shader.set_custom_shader_code(p_shader->custom_code_id,vertex_code, vertex_globals,fragment_code, fragment_globals,uniform_names,enablers); + } else { + //postprocess_shader.set_custom_shader_code(p_shader->custom_code_id,vertex_code, vertex_globals,fragment_code, fragment_globals,uniform_names); + } + + p_shader->valid=true; + p_shader->has_alpha=flags.uses_alpha || flags.uses_texscreen; + p_shader->has_texscreen=flags.uses_texscreen; + p_shader->has_screen_uv=flags.uses_screen_uv; + p_shader->can_zpass=!flags.uses_discard && !flags.vertex_code_writes_vertex; + p_shader->version++; + +} + + +void RasterizerGLES2::_add_geometry( const Geometry* p_geometry, const InstanceData *p_instance, const Geometry *p_geometry_cmp, const GeometryOwner *p_owner) { + + Material *m=NULL; + RID m_src=p_instance->material_override.is_valid() ? p_instance->material_override : p_geometry->material; + +#ifdef DEBUG_ENABLED + if (current_debug==VS::SCENARIO_DEBUG_OVERDRAW) { + m_src=overdraw_material; + } + +#endif + + if (m_src) + m=material_owner.get( m_src ); + + if (!m) { + m=material_owner.get( default_material ); + } + + ERR_FAIL_COND(!m); + + if (m->last_pass!=frame) { + + if (m->shader.is_valid()) { + + m->shader_cache=shader_owner.get(m->shader); + if (m->shader_cache) { + + + + if (!m->shader_cache->valid) { + m->shader_cache=NULL; + } else { + if (m->shader_cache->has_texscreen) + texscreen_used=true; + } + } else { + m->shader=RID(); + } + + } else { + m->shader_cache=NULL; + } + + m->last_pass=frame; + } + + + LightInstance *lights[RenderList::MAX_LIGHTS]; + + RenderList *render_list=NULL; + + bool has_alpha = m->blend_mode!=VS::MATERIAL_BLEND_MODE_MIX || (m->shader_cache && m->shader_cache->has_alpha) || m->flags[VS::MATERIAL_FLAG_ONTOP]; + + + if (shadow) { + + if (has_alpha) + return; //bye + + if (true) { + m = shadow_mat_ptr; //for now do this always + if (m->last_pass!=frame) { + + if (m->shader.is_valid()) { + + m->shader_cache=shader_owner.get(m->shader); + if (m->shader_cache) { + + + if (!m->shader_cache->valid) + m->shader_cache=NULL; + } else { + m->shader=RID(); + } + + } else { + m->shader_cache=NULL; + } + + m->last_pass=frame; + } + } + + render_list = &opaque_render_list; + /* notyet + if (!m->shader_cache || m->shader_cache->can_zpass) + render_list = &alpha_render_list; + } else { + render_list = &opaque_render_list; + }*/ + + } else { + if (has_alpha) { + render_list = &alpha_render_list; + } else { + render_list = &opaque_render_list; + + } + } + + + RenderList::Element *e = render_list->add_element(); + + e->geometry=p_geometry; + e->geometry_cmp=p_geometry_cmp; + e->material=m; + e->instance=p_instance; + //e->depth=camera_plane.distance_to(p_world->origin); + e->depth=camera_transform.origin.distance_to(p_instance->transform.origin); + e->owner=p_owner; + e->light_type=0; + e->additive=false; + e->additive_ptr=&e->additive; + e->sort_flags=0; + + + if (p_instance->skeleton.is_valid()) { + e->skeleton=skeleton_owner.get(p_instance->skeleton); + if (!e->skeleton) + const_cast<InstanceData*>(p_instance)->skeleton=RID(); + else + e->sort_flags|=RenderList::SORT_FLAG_SKELETON; + } else { + e->skeleton=NULL; + + } + + if (e->geometry->type==Geometry::GEOMETRY_MULTISURFACE) + e->sort_flags|=RenderList::SORT_FLAG_INSTANCING; + + + e->mirror=p_instance->mirror; + if (m->flags[VS::MATERIAL_FLAG_INVERT_FACES]) + e->mirror=!e->mirror; + + e->light_type=0xFF; // no lights! + e->light=0xFFFF; + + if (shadow || m->flags[VS::MATERIAL_FLAG_UNSHADED]) { + + e->light_type=0x7F; //unshaded is zero + } else { + + //setup lights + uint16_t light_count=0; + uint16_t sort_key[4]; + uint8_t light_types[4]; + + int dlc = MIN(directional_light_count,RenderList::MAX_LIGHTS);; + light_count=dlc; + + for(int i=0;i<dlc;i++) { + sort_key[i]=directional_lights[i]->sort_key; + light_types[i]=VS::LIGHT_DIRECTIONAL; + if (directional_lights[i]->base->shadow_enabled) { + light_types[i]|=0x8; + if (directional_lights[i]->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_SPLIT) + light_types[i]|=0x10; + + } + + } + + + const RID *liptr = p_instance->light_instances.ptr(); + int ilc=p_instance->light_instances.size(); + + + + for(int i=0;i<ilc;i++) { + + if (light_count>=RenderList::MAX_LIGHTS) + break; + + LightInstance *li=light_instance_owner.get( liptr[i] ); + if (!li || li->last_pass!=scene_pass) //lit by light not in visible scene + continue; + light_types[light_count]=li->base->type; + if (li->base->shadow_enabled) + light_types[light_count]|=0x8; + sort_key[light_count++]=li->sort_key; + + + } + + for(int i=0;i<light_count;i++) { + + RenderList::Element *ec; + if (i>0) { + + ec = render_list->add_element(); + memcpy(ec,e,sizeof(RenderList::Element)); + } else { + + ec=e; + } + + ec->light_type=light_types[i]; + ec->light=sort_key[i]; + ec->additive_ptr=&e->additive; + } + + } + + DEBUG_TEST_ERROR("Add Geometry"); + +} + +void RasterizerGLES2::add_mesh( const RID& p_mesh, const InstanceData *p_data) { + + Mesh *mesh = mesh_owner.get(p_mesh); + ERR_FAIL_COND(!mesh); + + int ssize = mesh->surfaces.size(); + + for (int i=0;i<ssize;i++) { + + Surface *s = mesh->surfaces[i]; + _add_geometry(s,p_data,s,NULL); + } + + mesh->last_pass=frame; + +} + +void RasterizerGLES2::add_multimesh( const RID& p_multimesh, const InstanceData *p_data){ + + MultiMesh *multimesh = multimesh_owner.get(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (!multimesh->mesh.is_valid()) + return; + if (multimesh->elements.empty()) + return; + + Mesh *mesh = mesh_owner.get(multimesh->mesh); + ERR_FAIL_COND(!mesh); + + int surf_count = mesh->surfaces.size(); + if (multimesh->last_pass!=scene_pass) { + + multimesh->cache_surfaces.resize(surf_count); + for(int i=0;i<surf_count;i++) { + + multimesh->cache_surfaces[i].material=mesh->surfaces[i]->material; + multimesh->cache_surfaces[i].has_alpha=mesh->surfaces[i]->has_alpha; + multimesh->cache_surfaces[i].surface=mesh->surfaces[i]; + } + + multimesh->last_pass=scene_pass; + } + + for(int i=0;i<surf_count;i++) { + + _add_geometry(&multimesh->cache_surfaces[i],p_data,multimesh->cache_surfaces[i].surface,multimesh); + } + + +} + +void RasterizerGLES2::add_particles( const RID& p_particle_instance, const InstanceData *p_data){ + + //print_line("adding particles"); + ParticlesInstance *particles_instance = particles_instance_owner.get(p_particle_instance); + ERR_FAIL_COND(!particles_instance); + Particles *p=particles_owner.get( particles_instance->particles ); + ERR_FAIL_COND(!p); + + _add_geometry(p,p_data,p,particles_instance); + draw_next_frame=true; + +} + + +void RasterizerGLES2::_set_cull(bool p_front,bool p_reverse_cull) { + + bool front = p_front; + if (p_reverse_cull) + front=!front; + + if (front!=cull_front) { + + glCullFace(front?GL_FRONT:GL_BACK); + cull_front=front; + } +} + + +_FORCE_INLINE_ void RasterizerGLES2::_update_material_shader_params(Material *p_material) const { + + + Map<StringName,Material::UniformData> old_mparams=p_material->shader_params; + Map<StringName,Material::UniformData> &mparams=p_material->shader_params; + mparams.clear(); + int idx=0; + for(Map<StringName,ShaderLanguage::Uniform>::Element *E=p_material->shader_cache->uniforms.front();E;E=E->next()) { + + Material::UniformData ud; + + bool keep=true; + + if (!old_mparams.has(E->key())) + keep=false; + else if (old_mparams[E->key()].value.get_type()!=E->value().default_value.get_type()) { + + if (old_mparams[E->key()].value.get_type()==Variant::OBJECT) { + if (E->value().default_value.get_type()!=Variant::_RID) //hackfor textures + keep=false; + } else if (!old_mparams[E->key()].value.is_num() || !E->value().default_value.get_type()) + keep=false; + } + + if (keep) { + ud.value=old_mparams[E->key()].value; + //print_line("KEEP: "+String(E->key())); + } else { + ud.value=E->value().default_value; + //print_line("NEW: "+String(E->key())+" because: hasold-"+itos(old_mparams.has(E->key()))); + //if (old_mparams.has(E->key())) + // print_line(" told "+Variant::get_type_name(old_mparams[E->key()].value.get_type())+" tnew "+Variant::get_type_name(E->value().default_value.get_type())); + } + + ud.istexture=(E->get().type==ShaderLanguage::TYPE_TEXTURE || E->get().type==ShaderLanguage::TYPE_CUBEMAP); + ud.index=idx++; + mparams[E->key()]=ud; + } + + p_material->shader_version=p_material->shader_cache->version; + +} + +bool RasterizerGLES2::_setup_material(const Geometry *p_geometry,const Material *p_material,bool p_no_const_light) { + + if (p_material->flags[VS::MATERIAL_FLAG_DOUBLE_SIDED]) { + glDisable(GL_CULL_FACE); + } else { + glEnable(GL_CULL_FACE); + } + + //glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); + + /* + if (p_material->flags[VS::MATERIAL_FLAG_WIREFRAME]) + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); + else + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + */ + + if (p_material->line_width) + glLineWidth(p_material->line_width); + + + //all goes to false by default + material_shader.set_conditional(MaterialShaderGLES2::USE_SHADOW_PASS,shadow!=NULL); + material_shader.set_conditional(MaterialShaderGLES2::USE_SHADOW_PCF,use_shadow_pcf); + //material_shader.set_conditional(MaterialShaderGLES2::USE_SHADOW_ESM,true); + + + if (!shadow) { + + bool depth_test=!p_material->flags[VS::MATERIAL_FLAG_ONTOP]; + bool depth_write=!p_material->hints[VS::MATERIAL_HINT_NO_DEPTH_DRAW]; + + if (current_depth_mask!=depth_write) { + current_depth_mask=depth_write; + glDepthMask( depth_write ); + + } + + + if (current_depth_test!=depth_test) { + + + current_depth_test=depth_test; + if(depth_test) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + } + + + material_shader.set_conditional(MaterialShaderGLES2::USE_FOG,current_env && current_env->fx_enabled[VS::ENV_FX_FOG]); + //glDepthMask( true ); + + } + + + DEBUG_TEST_ERROR("Pre Shader Bind"); + + bool rebind=false; + + if (p_material->shader_cache && p_material->shader_cache->valid) { + + // // reduce amount of conditional compilations + // for(int i=0;i<_tex_version_count;i++) + // material_shader.set_conditional((MaterialShaderGLES2::Conditionals)_tex_version[i],false); + + + // material_shader.set_custom_shader(p_material->shader_cache->custom_code_id); + + if (p_material->shader_version!=p_material->shader_cache->version) { + //shader changed somehow, must update uniforms + + _update_material_shader_params((Material*)p_material); + + } + material_shader.set_custom_shader(p_material->shader_cache->custom_code_id); + rebind = material_shader.bind(); + + DEBUG_TEST_ERROR("Shader Bind"); + + //set uniforms! + int texcoord=0; + for (Map<StringName,Material::UniformData>::Element *E=p_material->shader_params.front();E;E=E->next()) { + + if (E->get().index<0) + continue; + if (E->get().istexture) { + //clearly a texture.. + RID rid = E->get().value; + int loc = material_shader.get_custom_uniform_location(E->get().index); //should be automatic.. + + + Texture *t=NULL; + if (rid.is_valid()) { + + t=texture_owner.get(rid); + if (!t) + E->get().value=RID(); //nullify, invalid texture + } + + + glActiveTexture(GL_TEXTURE0+texcoord); + glUniform1i(loc,texcoord); //TODO - this could happen automatically on compile... + if (t) { + if (t->render_target) + t->render_target->last_pass=frame; + glBindTexture(t->target,t->tex_id); + } else + glBindTexture(GL_TEXTURE_2D,white_tex); //no texture + texcoord++; + + } else { + material_shader.set_custom_uniform(E->get().index,E->get().value); + } + + } + + if (p_material->shader_cache->has_texscreen && framebuffer.active) { + material_shader.set_uniform(MaterialShaderGLES2::TEXSCREEN_SCREEN_MULT,Vector2(float(viewport.width)/framebuffer.width,float(viewport.height)/framebuffer.height)); + material_shader.set_uniform(MaterialShaderGLES2::TEXSCREEN_TEX,texcoord); + glActiveTexture(GL_TEXTURE0+texcoord); + glBindTexture(GL_TEXTURE_2D,framebuffer.sample_color); + + } + if (p_material->shader_cache->has_screen_uv) { + material_shader.set_uniform(MaterialShaderGLES2::SCREEN_UV_MULT,Vector2(1.0/viewport.width,1.0/viewport.height)); + } + DEBUG_TEST_ERROR("Material arameters"); + + } else { + + material_shader.set_custom_shader(0); + rebind = material_shader.bind(); + + DEBUG_TEST_ERROR("Shader bind2"); + } + + + + if (shadow) { + + float zofs = shadow->base->vars[VS::LIGHT_PARAM_SHADOW_Z_OFFSET]; + float zslope = shadow->base->vars[VS::LIGHT_PARAM_SHADOW_Z_SLOPE_SCALE]; + if (shadow_pass>=1 && shadow->base->type==VS::LIGHT_DIRECTIONAL) { + float m = Math::pow(shadow->base->directional_shadow_param[VS::LIGHT_DIRECTIONAL_SHADOW_PARAM_PSSM_ZOFFSET_SCALE],shadow_pass); + zofs*=m; + zslope*=m; + } + material_shader.set_uniform(MaterialShaderGLES2::SHADOW_Z_OFFSET,zofs); + material_shader.set_uniform(MaterialShaderGLES2::SHADOW_Z_SLOPE_SCALE,zslope); + if (shadow->base->type==VS::LIGHT_OMNI) + material_shader.set_uniform(MaterialShaderGLES2::DUAL_PARABOLOID,shadow->dp); + DEBUG_TEST_ERROR("Shadow uniforms"); + + } + + + if (current_env && current_env->fx_enabled[VS::ENV_FX_FOG]) { + + Color col_begin = current_env->fx_param[VS::ENV_FX_PARAM_FOG_BEGIN_COLOR]; + Color col_end = current_env->fx_param[VS::ENV_FX_PARAM_FOG_END_COLOR]; + float from = current_env->fx_param[VS::ENV_FX_PARAM_FOG_BEGIN]; + float zf = camera_z_far; + float curve = current_env->fx_param[VS::ENV_FX_PARAM_FOG_ATTENUATION]; + material_shader.set_uniform(MaterialShaderGLES2::FOG_PARAMS,Vector3(from,zf,curve)); + material_shader.set_uniform(MaterialShaderGLES2::FOG_COLOR_BEGIN,Vector3(col_begin.r,col_begin.g,col_begin.b)); + material_shader.set_uniform(MaterialShaderGLES2::FOG_COLOR_END,Vector3(col_end.r,col_end.g,col_end.b)); + } + + material_shader.set_uniform(MaterialShaderGLES2::CONST_LIGHT_MULT,p_no_const_light?0.0:1.0); + //material_shader.set_uniform(MaterialShaderGLES2::TIME,Math::fmod(last_time,300.0)); + //if uses TIME - draw_next_frame=true + + return rebind; + +} + + + +void RasterizerGLES2::_setup_light(uint16_t p_light) { + + if (shadow) + return; + + if (p_light==0xFFFF) + return; + + enum { + VL_LIGHT_POS, + VL_LIGHT_DIR, + VL_LIGHT_ATTENUATION, + VL_LIGHT_SPOT_ATTENUATION, + VL_LIGHT_AMBIENT, + VL_LIGHT_DIFFUSE, + VL_LIGHT_SPECULAR, + VL_LIGHT_MAX + }; + + static const MaterialShaderGLES2::Uniforms light_uniforms[VL_LIGHT_MAX]={ + MaterialShaderGLES2::LIGHT_POS, + MaterialShaderGLES2::LIGHT_DIRECTION, + MaterialShaderGLES2::LIGHT_ATTENUATION, + MaterialShaderGLES2::LIGHT_SPOT_ATTENUATION, + MaterialShaderGLES2::LIGHT_AMBIENT, + MaterialShaderGLES2::LIGHT_DIFFUSE, + MaterialShaderGLES2::LIGHT_SPECULAR, + }; + + + GLfloat light_data[VL_LIGHT_MAX][3]; + memset(light_data,0,(VL_LIGHT_MAX)*3*sizeof(GLfloat)); + + LightInstance *li=light_instances[p_light]; + Light *l=li->base; + + + for(int j=0;j<3;j++) { + light_data[VL_LIGHT_AMBIENT][j]=l->colors[VS::LIGHT_COLOR_AMBIENT][j]; + light_data[VL_LIGHT_DIFFUSE][j]=l->colors[VS::LIGHT_COLOR_DIFFUSE][j]; + light_data[VL_LIGHT_SPECULAR][j]=l->colors[VS::LIGHT_COLOR_SPECULAR][j]; + } + + if (l->type!=VS::LIGHT_OMNI) { + + Vector3 dir = -li->transform.get_basis().get_axis(2); + dir = camera_transform_inverse.basis.xform(dir).normalized(); + for(int j=0;j<3;j++) + light_data[VL_LIGHT_DIR][j]=dir[j]; + } + + + if (l->type!=VS::LIGHT_DIRECTIONAL) { + + Vector3 pos = li->transform.get_origin(); + pos = camera_transform_inverse.xform(pos); + for(int j=0;j<3;j++) + light_data[VL_LIGHT_POS][j]=pos[j]; + } + + if (li->near_shadow_buffer) { + + glActiveTexture(GL_TEXTURE7); + //if (read_depth_supported) { + + glBindTexture(GL_TEXTURE_2D,li->near_shadow_buffer->depth); + //} else { + + + //} + + material_shader.set_uniform(MaterialShaderGLES2::SHADOW_MATRIX,li->shadow_projection); + material_shader.set_uniform(MaterialShaderGLES2::SHADOW_TEXEL_SIZE,Vector2(1.0,1.0)/li->near_shadow_buffer->size); + material_shader.set_uniform(MaterialShaderGLES2::SHADOW_TEXTURE,7); + + if (li->base->type==VS::LIGHT_DIRECTIONAL && li->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_SPLIT) { + + material_shader.set_uniform(MaterialShaderGLES2::SHADOW_MATRIX2,li->shadow_projection2); + material_shader.set_uniform(MaterialShaderGLES2::LIGHT_PSSM_SPLIT,li->shadow_split); + //print_line("shadow split: "+rtos(li->shadow_split)); + } + material_shader.set_uniform(MaterialShaderGLES2::SHADOW_DARKENING,li->base->vars[VS::LIGHT_PARAM_SHADOW_DARKENING]); + //matrix + + } + + + light_data[VL_LIGHT_ATTENUATION][0]=l->vars[VS::LIGHT_PARAM_ENERGY]; + light_data[VL_LIGHT_ATTENUATION][1]=l->vars[VS::LIGHT_PARAM_RADIUS]; + light_data[VL_LIGHT_ATTENUATION][2]=l->vars[VS::LIGHT_PARAM_ATTENUATION]; + + light_data[VL_LIGHT_SPOT_ATTENUATION][0]=Math::cos(Math::deg2rad(l->vars[VS::LIGHT_PARAM_SPOT_ANGLE])); + light_data[VL_LIGHT_SPOT_ATTENUATION][1]=l->vars[VS::LIGHT_PARAM_SPOT_ATTENUATION]; + + + //int uf = material_shader.get_uniform(MaterialShaderGLES2::LIGHT_PARAMS); + for(int i=0;i<VL_LIGHT_MAX;i++) { + glUniform3f( material_shader.get_uniform(light_uniforms[i]),light_data[i][0],light_data[i][1],light_data[i][2]); + } + +} + + +template<bool USE_NORMAL, bool USE_TANGENT> +void RasterizerGLES2::_skeleton_xform(const uint8_t * p_src_array, int p_src_stride, uint8_t * p_dst_array, int p_dst_stride, int p_elements,const uint8_t *p_src_bones, const uint8_t *p_src_weights, const Skeleton::Bone *p_bone_xforms) { + + uint32_t basesize = 3; + if (USE_NORMAL) + basesize+=3; + if (USE_TANGENT) + basesize+=4; + + uint32_t extra=(p_dst_stride-basesize*4); + + for(int i=0;i<p_elements;i++) { + + uint32_t ss = p_src_stride*i; + uint32_t ds = p_dst_stride*i; + const uint16_t *bi = (const uint16_t*)&p_src_bones[ss]; + const float *bw = (const float *)&p_src_weights[ss]; + const float *src_vec=(const float *)&p_src_array[ss]; + float *dst_vec=(float*)&p_dst_array[ds]; + + dst_vec[0]=0.0; + dst_vec[1]=0.0; + dst_vec[2]=0.0; + //conditionals simply removed by optimizer + if (USE_NORMAL) { + + dst_vec[3]=0.0; + dst_vec[4]=0.0; + dst_vec[5]=0.0; + + if (USE_TANGENT) { + + dst_vec[6]=0.0; + dst_vec[7]=0.0; + dst_vec[8]=0.0; + dst_vec[9]=src_vec[9]; + } + } else { + + if (USE_TANGENT) { + + dst_vec[3]=0.0; + dst_vec[4]=0.0; + dst_vec[5]=0.0; + dst_vec[6]=src_vec[6]; + } + } + + +#define _XFORM_BONE(m_idx)\ + if (bw[m_idx]==0)\ + goto end;\ + p_bone_xforms[bi[m_idx]].transform_add_mul3(&src_vec[0],&dst_vec[0],bw[m_idx]);\ + if (USE_NORMAL) {\ + p_bone_xforms[bi[m_idx]].transform3_add_mul3(&src_vec[3],&dst_vec[3],bw[m_idx]);\ + if (USE_TANGENT) {\ + p_bone_xforms[bi[m_idx]].transform3_add_mul3(&src_vec[6],&dst_vec[6],bw[m_idx]);\ + }\ + } else {\ + if (USE_TANGENT) {\ + p_bone_xforms[bi[m_idx]].transform3_add_mul3(&src_vec[3],&dst_vec[3],bw[m_idx]);\ + }\ + } + + _XFORM_BONE(0); + _XFORM_BONE(1); + _XFORM_BONE(2); + _XFORM_BONE(3); + + end: + + //copy extra stuff + const uint8_t *esp =(const uint8_t*) &src_vec[basesize]; + uint8_t *edp =(uint8_t*) &dst_vec[basesize]; + + + for(uint32_t j=0;j<extra;j++) { + + edp[j]=esp[j]; + } + + } +} + + +Error RasterizerGLES2::_setup_geometry(const Geometry *p_geometry, const Material* p_material, const Skeleton *p_skeleton,const float *p_morphs) { + + + switch(p_geometry->type) { + + case Geometry::GEOMETRY_MULTISURFACE: + case Geometry::GEOMETRY_SURFACE: { + + const Surface *surf=NULL; + if (p_geometry->type==Geometry::GEOMETRY_SURFACE) + surf=static_cast<const Surface*>(p_geometry); + else if (p_geometry->type==Geometry::GEOMETRY_MULTISURFACE) + surf=static_cast<const MultiMeshSurface*>(p_geometry)->surface; + + + if (surf->format != surf->configured_format) { + if (OS::get_singleton()->is_stdout_verbose()) { + + print_line("has format: "+itos(surf->format)); + print_line("configured format: "+itos(surf->configured_format)); + } + ERR_EXPLAIN("Missing arrays (not set) in surface"); + } + ERR_FAIL_COND_V( surf->format != surf->configured_format, ERR_UNCONFIGURED ); + uint8_t *base=0; + int stride=surf->stride; + bool use_VBO = (surf->array_local==0); + _setup_geometry_vinfo=surf->array_len; + + bool skeleton_valid = p_skeleton && (surf->format&VS::ARRAY_FORMAT_BONES) && (surf->format&VS::ARRAY_FORMAT_WEIGHTS) && !p_skeleton->bones.empty() && p_skeleton->bones.size() > surf->max_bone; + /* + if (surf->packed) { + float scales[4]={surf->vertex_scale,surf->uv_scale,surf->uv2_scale,0.0}; + glVertexAttrib4fv( 7, scales ); + } else { + glVertexAttrib4f( 7, 1,1,1,1 ); + + }*/ + + if (!use_VBO) { + + DEBUG_TEST_ERROR("Draw NO VBO"); + + base = surf->array_local; + glBindBuffer(GL_ARRAY_BUFFER, 0); + bool can_copy_to_local=surf->local_stride * surf->array_len <= skinned_buffer_size; + if (!can_copy_to_local) + skeleton_valid=false; + + + /* compute morphs */ + + if (p_morphs && surf->morph_target_count && can_copy_to_local) { + + base = skinned_buffer; + stride=surf->local_stride; + + //copy all first + float coef=1.0; + + for(int i=0;i<surf->morph_target_count;i++) { + if (surf->mesh->morph_target_mode==VS::MORPH_MODE_NORMALIZED) + coef-=p_morphs[i]; + ERR_FAIL_COND_V( surf->morph_format != surf->morph_targets_local[i].configured_format, ERR_INVALID_DATA ); + + } + + + for(int i=0;i<VS::ARRAY_MAX-1;i++) { + + const Surface::ArrayData& ad=surf->array[i]; + if (ad.size==0) + continue; + + int ofs = ad.ofs; + int src_stride=surf->stride; + int dst_stride=surf->local_stride; + int count = surf->array_len; + + switch(i) { + + case VS::ARRAY_VERTEX: + case VS::ARRAY_NORMAL: + case VS::ARRAY_TANGENT: + { + + for(int k=0;k<count;k++) { + + const float *src = (const float*)&surf->array_local[ofs+k*src_stride]; + float *dst = (float*)&base[ofs+k*dst_stride]; + + dst[0]= src[0]*coef; + dst[1]= src[1]*coef; + dst[2]= src[2]*coef; + } break; + + } break; + case VS::ARRAY_TEX_UV: + case VS::ARRAY_TEX_UV2: { + + for(int k=0;k<count;k++) { + + const float *src = (const float*)&surf->array_local[ofs+k*src_stride]; + float *dst = (float*)&base[ofs+k*dst_stride]; + + dst[0]= src[0]*coef; + dst[1]= src[1]*coef; + } break; + + } break; + } + } + + + for(int j=0;j<surf->morph_target_count;j++) { + + for(int i=0;i<VS::ARRAY_MAX-1;i++) { + + const Surface::ArrayData& ad=surf->array[i]; + if (ad.size==0) + continue; + + + int ofs = ad.ofs; + int dst_stride=surf->local_stride; + int count = surf->array_len; + const uint8_t *morph=surf->morph_targets_local[j].array; + float w = p_morphs[j]; + + switch(i) { + + case VS::ARRAY_VERTEX: + case VS::ARRAY_NORMAL: + case VS::ARRAY_TANGENT: + { + + for(int k=0;k<count;k++) { + + const float *src_morph = (const float*)&morph[ofs+k*dst_stride]; + float *dst = (float*)&base[ofs+k*dst_stride]; + + dst[0]+= src_morph[0]*w; + dst[1]+= src_morph[1]*w; + dst[2]+= src_morph[2]*w; + } break; + + } break; + case VS::ARRAY_TEX_UV: + case VS::ARRAY_TEX_UV2: { + + for(int k=0;k<count;k++) { + + const float *src_morph = (const float*)&morph[ofs+k*dst_stride]; + float *dst = (float*)&base[ofs+k*dst_stride]; + + dst[0]+= src_morph[0]*w; + dst[1]+= src_morph[1]*w; + } break; + + } break; + } + } + } + +#if 0 + { + //in-place skeleton tansformation, only used for morphs, slow. + //should uptimize some day.... + + const uint8_t *src_weights=&surf->array_local[surf->array[VS::ARRAY_WEIGHTS].ofs]; + const uint8_t *src_bones=&surf->array_local[surf->array[VS::ARRAY_BONES].ofs]; + int src_stride = surf->stride; + int count = surf->array_len; + const Transform *skeleton = &p_skeleton->bones[0]; + + for(int i=0;i<VS::ARRAY_MAX-1;i++) { + + const Surface::ArrayData& ad=surf->array[i]; + if (ad.size==0) + continue; + + int ofs = ad.ofs; + + + switch(i) { + + case VS::ARRAY_VERTEX: { + for(int k=0;k<count;k++) { + + float *ptr= (float*)&base[ofs+k*stride]; + const GLfloat* weights = reinterpret_cast<const GLfloat*>(&src_weights[k*src_stride]); + const GLfloat *bones = reinterpret_cast<const GLfloat*>(&src_bones[k*src_stride]); + + Vector3 src( ptr[0], ptr[1], ptr[2] ); + Vector3 dst; + for(int j=0;j<VS::ARRAY_WEIGHTS_SIZE;j++) { + + float w = weights[j]; + if (w==0) + break; + + //print_line("accum "+itos(i)+" += "+rtos(Math::ftoi(bones[j]))+" * "+skeleton[ Math::ftoi(bones[j]) ]+" * "+rtos(w)); + int bidx = Math::fast_ftoi(bones[j]); + dst+=skeleton[ bidx ].xform(src) * w; + } + + ptr[0]=dst.x; + ptr[1]=dst.y; + ptr[2]=dst.z; + + } break; + + } break; + case VS::ARRAY_NORMAL: + case VS::ARRAY_TANGENT: { + for(int k=0;k<count;k++) { + + float *ptr= (float*)&base[ofs+k*stride]; + const GLfloat* weights = reinterpret_cast<const GLfloat*>(&src_weights[k*src_stride]); + const GLfloat *bones = reinterpret_cast<const GLfloat*>(&src_bones[k*src_stride]); + + Vector3 src( ptr[0], ptr[1], ptr[2] ); + Vector3 dst; + for(int j=0;j<VS::ARRAY_WEIGHTS_SIZE;j++) { + + float w = weights[j]; + if (w==0) + break; + + //print_line("accum "+itos(i)+" += "+rtos(Math::ftoi(bones[j]))+" * "+skeleton[ Math::ftoi(bones[j]) ]+" * "+rtos(w)); + int bidx=Math::fast_ftoi(bones[j]); + dst+=skeleton[ bidx ].basis.xform(src) * w; + } + + ptr[0]=dst.x; + ptr[1]=dst.y; + ptr[2]=dst.z; + + } break; + + } break; + } + } + } +#endif + + } else if (skeleton_valid) { + + base = skinned_buffer; + //copy stuff and get it ready for the skeleton + + int src_stride = surf->stride; + int dst_stride = surf->stride - ( surf->array[VS::ARRAY_BONES].size + surf->array[VS::ARRAY_WEIGHTS].size ); + const uint8_t *src_weights=&surf->array_local[surf->array[VS::ARRAY_WEIGHTS].ofs]; + const uint8_t *src_bones=&surf->array_local[surf->array[VS::ARRAY_BONES].ofs]; + const Skeleton::Bone *skeleton = &p_skeleton->bones[0]; + + if (surf->format&VS::ARRAY_FORMAT_NORMAL && surf->format&VS::ARRAY_FORMAT_TANGENT) + _skeleton_xform<true,true>(surf->array_local,surf->stride,base,dst_stride,surf->array_len,src_bones,src_weights,skeleton); + else if (surf->format&(VS::ARRAY_FORMAT_NORMAL)) + _skeleton_xform<true,false>(surf->array_local,surf->stride,base,dst_stride,surf->array_len,src_bones,src_weights,skeleton); + else if (surf->format&(VS::ARRAY_FORMAT_TANGENT)) + _skeleton_xform<false,true>(surf->array_local,surf->stride,base,dst_stride,surf->array_len,src_bones,src_weights,skeleton); + else + _skeleton_xform<false,false>(surf->array_local,surf->stride,base,dst_stride,surf->array_len,src_bones,src_weights,skeleton); + + + stride=dst_stride; + } + + + + + } else { + + glBindBuffer(GL_ARRAY_BUFFER, surf->vertex_id); + }; + + for (int i=0;i<(VS::ARRAY_MAX-1);i++) { + + const Surface::ArrayData& ad=surf->array[i]; + +// if (!gl_texcoord_shader[i]) +// continue; + + if (ad.size==0 || ! ad.bind) { + glDisableVertexAttribArray(i); + if (i == VS::ARRAY_COLOR) { + _set_color_attrib(Color(1, 1, 1,1)); + }; + //print_line("disable: "+itos(i)); + continue; // this one is disabled. + } + + glEnableVertexAttribArray(i); +// print_line("set: "+itos(i)+" - count: "+itos(ad.count)+" datatype: "+itos(ad.datatype)+" ofs: "+itos(ad.ofs)+" stride: "+itos(stride)+" total len: "+itos(surf->array_len)); + glVertexAttribPointer(i, ad.count, ad.datatype, ad.normalize, stride, &base[ad.ofs]); + + } +#ifdef GLEW_ENABLED +//"desktop" opengl needs this. + if (surf->primitive==VS::PRIMITIVE_POINTS) { + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + + } else { + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + } +#endif + } break; + + default: break; + + }; + + return OK; +}; + +static const GLenum gl_primitive[]={ + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_LINE_LOOP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN +}; + + + +void RasterizerGLES2::_render(const Geometry *p_geometry,const Material *p_material, const Skeleton* p_skeleton, const GeometryOwner *p_owner,const Transform& p_xform) { + + + _rinfo.object_count++; + + switch(p_geometry->type) { + + case Geometry::GEOMETRY_SURFACE: { + + Surface *s = (Surface*)p_geometry; + + _rinfo.vertex_count+=s->array_len; + + if (s->index_array_len>0) { + + if (s->index_array_local) { + + //print_line("LOCAL F: "+itos(s->format)+" C: "+itos(s->index_array_len)+" VC: "+itos(s->array_len)); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len>(1<<16))?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT, s->index_array_local); + + } else { + // print_line("indices: "+itos(s->index_array_local) ); + + + //print_line("VBO F: "+itos(s->format)+" C: "+itos(s->index_array_len)+" VC: "+itos(s->array_len)); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,s->index_id); + 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); + + }; + + _rinfo.draw_calls++; + } break; + + case Geometry::GEOMETRY_MULTISURFACE: { + + material_shader.bind_uniforms(); + Surface *s = static_cast<const MultiMeshSurface*>(p_geometry)->surface; + const MultiMesh *mm = static_cast<const MultiMesh*>(p_owner); + int element_count=mm->elements.size(); + + if (element_count==0) + return; + + const MultiMesh::Element *elements=&mm->elements[0]; + + _rinfo.vertex_count+=s->array_len*element_count; + + _rinfo.draw_calls+=element_count; + + + if (use_texture_instancing) { + //this is probably the fastest all around way if vertex texture fetch is supported + + float twd=(1.0/mm->tw)*4.0; + float thd=1.0/mm->th; + float parm[3]={0.0,01.0,(1.0f/mm->tw)}; + glActiveTexture(GL_TEXTURE6); + glDisableVertexAttribArray(6); + glBindTexture(GL_TEXTURE_2D,mm->tex_id); + + if (s->index_array_len>0) { + + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,s->index_id); + for(int i=0;i<element_count;i++) { + parm[0]=(i%(mm->tw>>2))*twd; + parm[1]=(i/(mm->tw>>2))*thd; + glVertexAttrib3fv(6,parm); + glDrawElements(gl_primitive[s->primitive],s->index_array_len, (s->array_len>(1<<16))?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT,0); + + } + + + } else { + + for(int i=0;i<element_count;i++) { + //parm[0]=(i%(mm->tw>>2))*twd; + //parm[1]=(i/(mm->tw>>2))*thd; + glVertexAttrib3fv(6,parm); + glDrawArrays(gl_primitive[s->primitive],0,s->array_len); + } + }; + + } else if (use_attribute_instancing) { + //if not, using atributes instead of uniforms can be really fast in forward rendering architectures + if (s->index_array_len>0) { + + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,s->index_id); + for(int i=0;i<element_count;i++) { + glVertexAttrib4fv(8,&elements[i].matrix[0]); + glVertexAttrib4fv(9,&elements[i].matrix[4]); + glVertexAttrib4fv(10,&elements[i].matrix[8]); + glVertexAttrib4fv(11,&elements[i].matrix[12]); + glDrawElements(gl_primitive[s->primitive],s->index_array_len, (s->array_len>(1<<16))?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT,0); + } + + + } else { + + for(int i=0;i<element_count;i++) { + glVertexAttrib4fv(8,&elements[i].matrix[0]); + glVertexAttrib4fv(9,&elements[i].matrix[4]); + glVertexAttrib4fv(10,&elements[i].matrix[8]); + glVertexAttrib4fv(11,&elements[i].matrix[12]); + glDrawArrays(gl_primitive[s->primitive],0,s->array_len); + } + }; + + + } else { + + //nothing to do, slow path (hope no hardware has to use it... but you never know) + + if (s->index_array_len>0) { + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,s->index_id); + for(int i=0;i<element_count;i++) { + + glUniformMatrix4fv(material_shader.get_uniform_location(MaterialShaderGLES2::INSTANCE_TRANSFORM), 1, false, elements[i].matrix); + glDrawElements(gl_primitive[s->primitive],s->index_array_len, (s->array_len>(1<<16))?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT,0); + } + + + } else { + + for(int i=0;i<element_count;i++) { + glUniformMatrix4fv(material_shader.get_uniform_location(MaterialShaderGLES2::INSTANCE_TRANSFORM), 1, false, elements[i].matrix); + glDrawArrays(gl_primitive[s->primitive],0,s->array_len); + } + }; + } + } break; + case Geometry::GEOMETRY_PARTICLES: { + + + //print_line("particulinas"); + const Particles *particles = static_cast<const Particles*>( p_geometry ); + ERR_FAIL_COND(!p_owner); + ParticlesInstance *particles_instance = (ParticlesInstance*)p_owner; + + ParticleSystemProcessSW &pp = particles_instance->particles_process; + float td = time_delta; //MIN(time_delta,1.0/10.0); + pp.process(&particles->data,particles_instance->transform,td); + ERR_EXPLAIN("A parameter in the particle system is not correct."); + ERR_FAIL_COND(!pp.valid); + + + Transform camera; + if (shadow) + camera=shadow->transform; + else + camera=camera_transform; + + particle_draw_info.prepare(&particles->data,&pp,particles_instance->transform,camera); + _rinfo.draw_calls+=particles->data.amount; + + + _rinfo.vertex_count+=4*particles->data.amount; + + { + static const Vector3 points[4]={ + Vector3(-1.0,1.0,0), + Vector3(1.0,1.0,0), + Vector3(1.0,-1.0,0), + Vector3(-1.0,-1.0,0) + }; + static const Vector3 uvs[4]={ + Vector3(0.0,0.0,0.0), + Vector3(1.0,0.0,0.0), + Vector3(1.0,1.0,0.0), + Vector3(0,1.0,0.0) + }; + static const Vector3 normals[4]={ + Vector3(0,0,1), + Vector3(0,0,1), + Vector3(0,0,1), + Vector3(0,0,1) + }; + + static const Plane tangents[4]={ + Plane(Vector3(1,0,0),0), + Plane(Vector3(1,0,0),0), + Plane(Vector3(1,0,0),0), + Plane(Vector3(1,0,0),0) + }; + + for(int i=0;i<particles->data.amount;i++) { + + ParticleSystemDrawInfoSW::ParticleDrawInfo &pinfo=*particle_draw_info.draw_info_order[i]; + if (!pinfo.data->active) + continue; + + material_shader.set_uniform(MaterialShaderGLES2::WORLD_TRANSFORM, pinfo.transform); + _set_color_attrib(pinfo.color); + _draw_primitive(4,points,normals,NULL,uvs,tangents); + + } + + } + + } break; + default: break; + }; +}; + +void RasterizerGLES2::_setup_shader_params(const Material *p_material) { + + int idx=0; + int tex_idx=0; +#if 0 + for(Map<StringName,Variant>::Element *E=p_material->shader_cache->params.front();E;E=E->next(),idx++) { + + Variant v; // + v = E->get(); + const Map<StringName,Variant>::Element *F=p_material->shader_params.find(E->key()); + if (F) + v=F->get(); + + switch(v.get_type() ) { + case Variant::OBJECT: + case Variant::_RID: { + + RID tex=v; + if (!tex.is_valid()) + break; + + Texture *texture = texture_owner.get(tex); + if (!texture) + break; + glUniform1i( material_shader.get_custom_uniform_location(idx), tex_idx); + glActiveTexture(tex_idx); + glBindTexture(texture->target,texture->tex_id); + + } break; + case Variant::COLOR: { + + Color c=v; + material_shader.set_custom_uniform(idx,Vector3(c.r,c.g,c.b)); + } break; + default: { + + material_shader.set_custom_uniform(idx,v); + } break; + } + + } +#endif + +} + +void RasterizerGLES2::_setup_skeleton(const Skeleton *p_skeleton) { + + material_shader.set_conditional(MaterialShaderGLES2::USE_SKELETON,p_skeleton!=NULL); + if (p_skeleton && p_skeleton->tex_id) { + + glActiveTexture(GL_TEXTURE6); + glBindTexture(GL_TEXTURE_2D,p_skeleton->tex_id); + } + + +} + + +void RasterizerGLES2::_render_list_forward(RenderList *p_render_list,const Transform& p_view_transform, const Transform& p_view_transform_inverse,const CameraMatrix& p_projection,bool p_reverse_cull,bool p_fragment_light,bool p_alpha_pass) { + + const Material *prev_material=NULL; + uint16_t prev_light=0x777E; + const Geometry *prev_geometry_cmp=NULL; + uint8_t prev_light_type=0xEF; + const ParamOverrideMap* prev_overrides=NULL; // make it diferent than NULL + const Skeleton *prev_skeleton =NULL; + uint8_t prev_sort_flags=0xFF; + + Geometry::Type prev_geometry_type=Geometry::GEOMETRY_INVALID; + + material_shader.set_conditional(MaterialShaderGLES2::USE_VERTEX_LIGHTING,!shadow && !p_fragment_light); + material_shader.set_conditional(MaterialShaderGLES2::USE_FRAGMENT_LIGHTING,!shadow && p_fragment_light); + material_shader.set_conditional(MaterialShaderGLES2::USE_SKELETON,false); + + if (shadow) { + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_DIRECTIONAL,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_OMNI,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_SPOT,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_USE_SHADOW,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_USE_PSSM,false); + material_shader.set_conditional(MaterialShaderGLES2::SHADELESS,false); + } + + + + + bool prev_blend=false; + glDisable(GL_BLEND); + for (int i=0;i<p_render_list->element_count;i++) { + + RenderList::Element *e = p_render_list->elements[i]; + const Material *material = e->material; + uint16_t light = e->light; + uint8_t light_type = e->light_type; + uint8_t sort_flags= e->sort_flags; + const Skeleton *skeleton = e->skeleton; + const Geometry *geometry_cmp = e->geometry_cmp; + + bool rebind=false; + bool additive=false; + + + if (!shadow) { + + if (texscreen_used && !texscreen_copied && material->shader_cache && material->shader_cache->valid && material->shader_cache->has_texscreen) { + + texscreen_copied=true; + _copy_to_texscreen(); + + //force reset state + prev_material=NULL; + prev_light=0x777E; + prev_geometry_cmp=NULL; + prev_light_type=0xEF; + prev_overrides=NULL; // make it diferent than NULL + prev_skeleton =NULL; + prev_sort_flags=0xFF; + prev_geometry_type=Geometry::GEOMETRY_INVALID; + glEnable(GL_BLEND); + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + + } + + if (light_type!=prev_light_type) { + + if (material->flags[VS::MATERIAL_FLAG_UNSHADED]) { + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_DIRECTIONAL,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_OMNI,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_SPOT,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_USE_SHADOW,false); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_USE_PSSM,false); + material_shader.set_conditional(MaterialShaderGLES2::SHADELESS,true); + } else { + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_DIRECTIONAL,(light_type&0x3)==VS::LIGHT_DIRECTIONAL); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_OMNI,(light_type&0x3)==VS::LIGHT_OMNI); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_TYPE_SPOT,(light_type&0x3)==VS::LIGHT_SPOT); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_USE_SHADOW,(light_type&0x8)); + material_shader.set_conditional(MaterialShaderGLES2::LIGHT_USE_PSSM,(light_type&0x10)); + material_shader.set_conditional(MaterialShaderGLES2::SHADELESS,false); + } + + rebind=true; + } + + + if (!*e->additive_ptr) { + + additive=false; + *e->additive_ptr=true; + } else { + additive=true; + } + + bool desired_blend=false; + VS::MaterialBlendMode desired_blend_mode=VS::MATERIAL_BLEND_MODE_MIX; + + if (additive) { + desired_blend=true; + desired_blend_mode=VS::MATERIAL_BLEND_MODE_ADD; + } else { + desired_blend=p_alpha_pass; + desired_blend_mode=material->blend_mode; + } + + if (prev_blend!=desired_blend) { + + if (desired_blend) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + + prev_blend=desired_blend; + } + + if (desired_blend && desired_blend_mode!=current_blend_mode) { + + switch(desired_blend_mode) { + + + case VS::MATERIAL_BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + } break; + case VS::MATERIAL_BLEND_MODE_ADD: { + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + + } break; + case VS::MATERIAL_BLEND_MODE_SUB: { + + glBlendEquation(GL_FUNC_SUBTRACT); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + } break; + case VS::MATERIAL_BLEND_MODE_MUL: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + } break; + + } + + current_blend_mode=desired_blend_mode; + } + + } + + if (sort_flags!=prev_sort_flags) { + + if (sort_flags&RenderList::SORT_FLAG_INSTANCING) { + material_shader.set_conditional(MaterialShaderGLES2::USE_UNIFORM_INSTANCING,!use_texture_instancing && !use_attribute_instancing); + material_shader.set_conditional(MaterialShaderGLES2::USE_ATTRIBUTE_INSTANCING,use_attribute_instancing); + material_shader.set_conditional(MaterialShaderGLES2::USE_TEXTURE_INSTANCING,use_texture_instancing); + } else { + material_shader.set_conditional(MaterialShaderGLES2::USE_UNIFORM_INSTANCING,false); + material_shader.set_conditional(MaterialShaderGLES2::USE_ATTRIBUTE_INSTANCING,false); + material_shader.set_conditional(MaterialShaderGLES2::USE_TEXTURE_INSTANCING,false); + } + rebind=true; + } + if (use_hw_skeleton_xform && skeleton!=prev_skeleton) { + if (!prev_skeleton || !skeleton) + rebind=true; //went from skeleton <-> no skeleton, needs rebind + _setup_skeleton(skeleton); + } + + if (material!=prev_material || rebind) { + + rebind = _setup_material(e->geometry,material,additive); + + DEBUG_TEST_ERROR("Setup material"); + _rinfo.mat_change_count++; + //_setup_material_overrides(e->material,NULL,material_overrides); + //_setup_material_skeleton(material,skeleton); + } else { + + if (prev_skeleton!=skeleton) { + //_setup_material_skeleton(material,skeleton); + }; + } + + + if (geometry_cmp!=prev_geometry_cmp || prev_skeleton!=skeleton) { + + _setup_geometry(e->geometry, material,e->skeleton,e->instance->morph_values.ptr()); + _rinfo.surface_count++; + DEBUG_TEST_ERROR("Setup geometry"); + }; + + if (i==0 || light!=prev_light || rebind) { + if (e->light!=0xFFFF) { + _setup_light(e->light&0x3); + } + } + + _set_cull(e->mirror,p_reverse_cull); + + + if (i==0 || rebind) { + material_shader.set_uniform(MaterialShaderGLES2::CAMERA_INVERSE_TRANSFORM, p_view_transform_inverse); + material_shader.set_uniform(MaterialShaderGLES2::PROJECTION_TRANSFORM, p_projection); + if (skeleton && use_hw_skeleton_xform) { + //material_shader.set_uniform(MaterialShaderGLES2::SKELETON_MATRICES,6); + material_shader.set_uniform(MaterialShaderGLES2::SKELTEX_PIXEL_SIZE,skeleton->pixel_size); + } + _rinfo.shader_change_count++; + } + + if (e->instance->billboard || e->instance->depth_scale) { + + Transform xf=e->instance->transform; + if (e->instance->depth_scale) { + + if (p_projection.matrix[3][3]) { + //orthogonal matrix, try to do about the same + //with viewport size + //real_t w = Math::abs( 1.0/(2.0*(p_projection.matrix[0][0])) ); + real_t h = Math::abs( 1.0/(2.0*p_projection.matrix[1][1]) ); + float sc = (h*2.0); //consistent with Y-fov + xf.basis.scale( Vector3(sc,sc,sc)); + } else { + //just scale by depth + real_t sc = -camera_plane.distance_to(xf.origin); + xf.basis.scale( Vector3(sc,sc,sc)); + } + } + + if (e->instance->billboard) { + + Vector3 scale = xf.basis.get_scale(); + xf.set_look_at(xf.origin,xf.origin+p_view_transform.get_basis().get_axis(2),p_view_transform.get_basis().get_axis(1)); + xf.basis.scale(scale); + } + material_shader.set_uniform(MaterialShaderGLES2::WORLD_TRANSFORM, xf); + + } else { + material_shader.set_uniform(MaterialShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + } + + + _render(e->geometry, material, skeleton,e->owner,e->instance->transform); + DEBUG_TEST_ERROR("Rendering"); + + prev_material=material; + prev_skeleton=skeleton; + prev_geometry_cmp=geometry_cmp; + prev_light=e->light; + prev_light_type=e->light_type; + prev_sort_flags=sort_flags; +// prev_geometry_type=geometry->type; + } + + //print_line("shaderchanges: "+itos(p_alpha_pass)+": "+itos(_rinfo.shader_change_count)); + + + +}; + + +void RasterizerGLES2::_copy_to_texscreen() { + + //what am i missing? + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); +#ifdef GLEW_ENABLED + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif + glDisable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + glBindBuffer(GL_ARRAY_BUFFER,0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + + for(int i=0;i<VS::ARRAY_MAX;i++) { + glDisableVertexAttribArray(i); + } + + glActiveTexture(GL_TEXTURE0); + + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.sample_fbo); + glActiveTexture(GL_TEXTURE0); + glBindTexture( GL_TEXTURE_2D, framebuffer.color ); + copy_shader.bind(); + _copy_screen_quad(); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.fbo); + + +} + +void RasterizerGLES2::_copy_screen_quad() { + + + Vector2 dst_pos[4]={ + Vector2(-1, 1), + Vector2( 1, 1), + Vector2( 1,-1), + Vector2(-1,-1) + }; + + Size2 uvscale( + (viewport.width / float(framebuffer.scale)) / framebuffer.width, + (viewport.height / float(framebuffer.scale)) / framebuffer.height + ); + + Vector2 src_uv[4]={ + Vector2( 0, 1)*uvscale, + Vector2( 1, 1)*uvscale, + Vector2( 1, 0)*uvscale, + Vector2( 0, 0)*uvscale + }; + + Vector2 full_uv[4]={ + Vector2( 0, 1), + Vector2( 1, 1), + Vector2( 1, 0), + Vector2( 0, 0) + }; + + _draw_gui_primitive2(4,dst_pos,NULL,src_uv,full_uv); + +} + +void RasterizerGLES2::_process_glow_bloom() { + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.blur[0].fbo); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, framebuffer.color ); + copy_shader.set_conditional(CopyShaderGLES2::USE_GLOW_COPY,true); + if (current_vd && current_env->fx_enabled[VS::ENV_FX_HDR]) { + + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR,true); + + } + + copy_shader.bind(); + copy_shader.set_uniform(CopyShaderGLES2::BLOOM,float(current_env->fx_param[VS::ENV_FX_PARAM_GLOW_BLOOM])); + copy_shader.set_uniform(CopyShaderGLES2::BLOOM_TRESHOLD,float(current_env->fx_param[VS::ENV_FX_PARAM_GLOW_BLOOM_TRESHOLD])); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE),0); + + if (current_vd && current_env->fx_enabled[VS::ENV_FX_HDR]) { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, current_vd->lum_color ); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::HDR_SOURCE),2); + copy_shader.set_uniform(CopyShaderGLES2::TONEMAP_EXPOSURE,float(current_env->fx_param[VS::ENV_FX_PARAM_HDR_EXPOSURE])); + copy_shader.set_uniform(CopyShaderGLES2::HDR_GLOW_TRESHOLD,float(current_env->fx_param[VS::ENV_FX_PARAM_HDR_GLOW_TRESHOLD])); + copy_shader.set_uniform(CopyShaderGLES2::HDR_GLOW_SCALE,float(current_env->fx_param[VS::ENV_FX_PARAM_HDR_GLOW_SCALE])); + + glActiveTexture(GL_TEXTURE0); + } + + glViewport( 0, 0, framebuffer.blur_size, framebuffer.blur_size ); + _copy_screen_quad(); + + copy_shader.set_conditional(CopyShaderGLES2::USE_GLOW_COPY,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR,false); + int passes = current_env->fx_param[VS::ENV_FX_PARAM_GLOW_BLUR_PASSES]; + Vector2 psize(1.0/framebuffer.blur_size,1.0/framebuffer.blur_size); + + for(int i=0;i<passes;i++) { + + static const Vector2 src_uv[4]={ + Vector2( 0, 1), + Vector2( 1, 1), + Vector2( 1, 0), + Vector2( 0, 0) + }; + static const Vector2 dst_pos[4]={ + Vector2(-1, 1), + Vector2( 1, 1), + Vector2( 1,-1), + Vector2(-1,-1) + }; + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.blur[1].fbo); + glBindTexture(GL_TEXTURE_2D, framebuffer.blur[0].color ); + copy_shader.set_conditional(CopyShaderGLES2::BLUR_V_PASS,true); + copy_shader.set_conditional(CopyShaderGLES2::BLUR_H_PASS,false); + copy_shader.bind(); + copy_shader.set_uniform(CopyShaderGLES2::PIXEL_SIZE,psize); + + _draw_gui_primitive(4,dst_pos,NULL,src_uv); + + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.blur[0].fbo); + glBindTexture(GL_TEXTURE_2D, framebuffer.blur[1].color ); + copy_shader.set_conditional(CopyShaderGLES2::BLUR_V_PASS,false); + copy_shader.set_conditional(CopyShaderGLES2::BLUR_H_PASS,true); + copy_shader.bind(); + copy_shader.set_uniform(CopyShaderGLES2::PIXEL_SIZE,psize); + + _draw_gui_primitive(4,dst_pos,NULL,src_uv); + + + } + + copy_shader.set_conditional(CopyShaderGLES2::BLUR_V_PASS,false); + copy_shader.set_conditional(CopyShaderGLES2::BLUR_H_PASS,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR,false); + + //blur it + + +} + +void RasterizerGLES2::_process_hdr() { + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.luminance[0].fbo); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, framebuffer.color ); + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR_COPY,true); + copy_shader.bind(); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE),0); + glViewport( 0, 0, framebuffer.luminance[0].size, framebuffer.luminance[0].size ); + _copy_screen_quad(); + + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR_COPY,false); + int passes = current_env->fx_param[VS::ENV_FX_PARAM_GLOW_BLUR_PASSES]; + + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR_REDUCE,true); + copy_shader.bind(); + + for(int i=1;i<framebuffer.luminance.size();i++) { + + + static const Vector2 src_uv[4]={ + Vector2( 0, 1), + Vector2( 1, 1), + Vector2( 1, 0), + Vector2( 0, 0) + }; + static const Vector2 dst_pos[4]={ + Vector2(-1, 1), + Vector2( 1, 1), + Vector2( 1,-1), + Vector2(-1,-1) + }; + + + Vector2 psize(1.0/framebuffer.luminance[i-1].size,1.0/framebuffer.luminance[i-1].size); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.luminance[i].fbo); + glBindTexture(GL_TEXTURE_2D, framebuffer.luminance[i-1].color ); + glViewport( 0, 0, framebuffer.luminance[i].size, framebuffer.luminance[i].size ); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE),0); + + if (framebuffer.luminance[i].size==1) { + //last step + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR_STORE,true); + copy_shader.bind(); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, current_vd->lum_color ); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE_VD_LUM),1); + copy_shader.set_uniform(CopyShaderGLES2::HDR_TIME_DELTA,time_delta); + copy_shader.set_uniform(CopyShaderGLES2::HDR_EXP_ADJ_SPEED,float(current_env->fx_param[VS::ENV_FX_PARAM_HDR_EXPOSURE_ADJUST_SPEED])); + copy_shader.set_uniform(CopyShaderGLES2::MIN_LUMINANCE,float(current_env->fx_param[VS::ENV_FX_PARAM_HDR_MIN_LUMINANCE])); + copy_shader.set_uniform(CopyShaderGLES2::MAX_LUMINANCE,float(current_env->fx_param[VS::ENV_FX_PARAM_HDR_MAX_LUMINANCE])); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE),0); + + //swap them + SWAP( current_vd->lum_color, framebuffer.luminance[i].color); + SWAP( current_vd->lum_fbo, framebuffer.luminance[i].fbo); + + } + + copy_shader.set_uniform(CopyShaderGLES2::PIXEL_SIZE,psize); + + _draw_gui_primitive(4,dst_pos,NULL,src_uv); + + } + + + + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR_REDUCE,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR_STORE,false); + + draw_next_frame=true; + +} + + +void RasterizerGLES2::_draw_tex_bg() { + + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + RID texture; + + if (current_env->bg_mode==VS::ENV_BG_TEXTURE || current_env->bg_mode==VS::ENV_BG_TEXTURE_RGBE) { + texture=current_env->bg_param[VS::ENV_BG_PARAM_TEXTURE]; + } else { + texture=current_env->bg_param[VS::ENV_BG_PARAM_CUBEMAP]; + } + + if (!texture_owner.owns(texture)) { + return; + } + + Texture *t = texture_owner.get(texture); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(t->target, t->tex_id); + + copy_shader.set_conditional(CopyShaderGLES2::USE_ENERGY,true); + + if (current_env->bg_mode==VS::ENV_BG_TEXTURE || current_env->bg_mode==VS::ENV_BG_TEXTURE_RGBE) { + copy_shader.set_conditional(CopyShaderGLES2::USE_CUBEMAP,false); + + } else { + copy_shader.set_conditional(CopyShaderGLES2::USE_CUBEMAP,true); + } + + if (current_env->bg_mode==VS::ENV_BG_CUBEMAP_RGBE || current_env->bg_mode==VS::ENV_BG_TEXTURE_RGBE) { + copy_shader.set_conditional(CopyShaderGLES2::USE_RGBE,true); + } else { + copy_shader.set_conditional(CopyShaderGLES2::USE_RGBE,false); + } + + copy_shader.bind(); + + if (current_env->bg_mode==VS::ENV_BG_TEXTURE || current_env->bg_mode==VS::ENV_BG_TEXTURE_RGBE) { + glUniform1i( copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE),0); + } else { + glUniform1i( copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE_CUBE),0); + } + + float nrg =float(current_env->bg_param[VS::ENV_BG_PARAM_ENERGY]); + if (current_env->fx_enabled[VS::ENV_FX_HDR]) + nrg*=0.25; //go down a quarter for hdr + copy_shader.set_uniform(CopyShaderGLES2::ENERGY,nrg); + + Vector3 vertices[4]={ + Vector3(-1,-1,1), + Vector3( 1,-1,1), + Vector3( 1, 1,1), + Vector3(-1, 1,1) + }; + + + Vector3 src_uv[4]={ + Vector3( 0, 1, 0), + Vector3( 1, 1, 0), + Vector3( 1, 0, 0), + Vector3( 0, 0, 0) + }; + + if (current_env->bg_mode==VS::ENV_BG_TEXTURE || current_env->bg_mode==VS::ENV_BG_TEXTURE_RGBE) { + + //regular texture + //adjust aspect + + float aspect_t = t->width / float(t->height); + float aspect_v = viewport.width / float(viewport.height); + + if (aspect_v > aspect_t) { + //wider than texture + for(int i=0;i<4;i++) { + src_uv[i].y=(src_uv[i].y-0.5)*(aspect_t/aspect_v)+0.5; + } + + } else { + //narrower than texture + for(int i=0;i<4;i++) { + src_uv[i].x=(src_uv[i].x-0.5)*(aspect_v/aspect_t)+0.5; + } + } + + float scale=current_env->bg_param[VS::ENV_BG_PARAM_SCALE]; + for(int i=0;i<4;i++) { + + src_uv[i].x*=scale; + src_uv[i].y*=scale; + } + } else { + + //skybox uv vectors + float vw,vh,zn; + camera_projection.get_viewport_size(vw,vh); + zn=camera_projection.get_z_near(); + + float scale=current_env->bg_param[VS::ENV_BG_PARAM_SCALE]; + + for(int i=0;i<4;i++) { + + Vector3 uv=src_uv[i]; + uv.x=(uv.x*2.0-1.0)*vw*scale; + uv.y=-(uv.y*2.0-1.0)*vh*scale; + uv.z=-zn; + src_uv[i] = camera_transform.basis.xform(uv).normalized(); + src_uv[i].z = -src_uv[i].z; + + } + } + + _draw_primitive(4,vertices,NULL,NULL,src_uv); + + copy_shader.set_conditional(CopyShaderGLES2::USE_ENERGY,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_RGBE,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_CUBEMAP,false); +} + +void RasterizerGLES2::end_scene() { + + + + glEnable(GL_BLEND); + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + + bool use_fb=false; + + if (framebuffer.active) { + + //detect when to use the framebuffer object + if (texscreen_used || framebuffer.scale!=1) { + use_fb=true; + } else if (current_env) { + use_fb=false; + for(int i=0;i<VS::ENV_FX_MAX;i++) { + + if (i==VS::ENV_FX_FOG) //does not need fb + continue; + + if (current_env->fx_enabled[i]) { + use_fb=true; + break; + } + } + } + } + + + if (use_fb) { + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.fbo); + glViewport( 0,0,viewport.width / framebuffer.scale, viewport.height / framebuffer.scale ); + glScissor( 0,0,viewport.width / framebuffer.scale, viewport.height / framebuffer.scale ); + + material_shader.set_conditional(MaterialShaderGLES2::USE_HDR,current_env && current_env->fx_enabled[VS::ENV_FX_HDR]); + + } else { + if (current_rt) { + glScissor( 0,0,viewport.width,viewport.height); + } else { + glScissor( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height ); + } + } + + glEnable(GL_SCISSOR_TEST); + _glClearDepth(1.0); + + bool draw_tex_background=false; + + if (current_debug==VS::SCENARIO_DEBUG_OVERDRAW) { + + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + } else if (current_env) { + + switch(current_env->bg_mode) { + + case VS::ENV_BG_KEEP: { + //copy from framebuffer if framebuffer + glClear(GL_DEPTH_BUFFER_BIT); + } break; + case VS::ENV_BG_DEFAULT_COLOR: + case VS::ENV_BG_COLOR: { + + Color bgcolor; + if (current_env->bg_mode==VS::ENV_BG_COLOR) + bgcolor = current_env->bg_param[VS::ENV_BG_PARAM_COLOR]; + else + bgcolor = Globals::get_singleton()->get("render/default_clear_color"); + float a = use_fb ? 0.0 : 1.0; + glClearColor(bgcolor.r,bgcolor.g,bgcolor.b,a); + _glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + } break; + case VS::ENV_BG_TEXTURE: + case VS::ENV_BG_CUBEMAP: + case VS::ENV_BG_TEXTURE_RGBE: + case VS::ENV_BG_CUBEMAP_RGBE: { + + + glClear(GL_DEPTH_BUFFER_BIT); + draw_tex_background=true; + } break; + + } + } else { + + glClearColor(0.3,0.3,0.3,1.0); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + } + + glDisable(GL_SCISSOR_TEST); + + + //material_shader.set_uniform_camera(MaterialShaderGLES2::PROJECTION_MATRIX, camera_projection); + + /* + printf("setting projection to "); + for (int i=0; i<16; i++) { + printf("%f, ", ((float*)camera_projection.matrix)[i]); + }; + printf("\n"); + + print_line(String("setting camera to ")+camera_transform_inverse); + */ +// material_shader.set_uniform_default(MaterialShaderGLES2::CAMERA_INVERSE, camera_transform_inverse); + + + current_depth_test=true; + current_depth_mask=true; + texscreen_copied=false; + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + current_blend_mode=VS::MATERIAL_BLEND_MODE_MIX; + + material_shader.set_conditional(MaterialShaderGLES2::USE_GLOW,current_env && current_env->fx_enabled[VS::ENV_FX_GLOW]); + opaque_render_list.sort_mat_light_type_flags(); + _render_list_forward(&opaque_render_list,camera_transform,camera_transform_inverse,camera_projection,false,fragment_lighting); + + if (draw_tex_background) { + //most 3D vendors recommend drawing a texture bg or skybox here, + //after opaque geometry has been drawn + //so the zbuffer can get rid of most pixels + _draw_tex_bg(); + } + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + current_blend_mode=VS::MATERIAL_BLEND_MODE_MIX; + material_shader.set_conditional(MaterialShaderGLES2::USE_GLOW,false); + if (current_env && current_env->fx_enabled[VS::ENV_FX_GLOW]) { + glColorMask(1,1,1,0); //don't touch alpha + } + + alpha_render_list.sort_z(); + _render_list_forward(&alpha_render_list,camera_transform,camera_transform_inverse,camera_projection,false,false,true); + glColorMask(1,1,1,1); + +// material_shader.set_conditional( MaterialShaderGLES2::USE_FOG,false); + + DEBUG_TEST_ERROR("Drawing Scene"); + +#ifdef GLEW_ENABLED + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); +#endif + + if (use_fb) { + + + + for(int i=0;i<VS::ARRAY_MAX;i++) { + glDisableVertexAttribArray(i); + } + glBindBuffer(GL_ARRAY_BUFFER,0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_SCISSOR_TEST); + glDepthMask(false); + + if (current_env && current_env->fx_enabled[VS::ENV_FX_HDR]) { + _process_hdr(); + } + if (current_env && current_env->fx_enabled[VS::ENV_FX_GLOW]) { + _process_glow_bloom(); + } + + glBindFramebuffer(GL_FRAMEBUFFER, current_rt?current_rt->fbo:base_framebuffer); + + if (current_rt) { + glBindFramebuffer(GL_FRAMEBUFFER, current_rt->fbo); + glViewport( 0,0,viewport.width,viewport.height); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, base_framebuffer); + glViewport( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height ); + } + + //time to copy!!! + copy_shader.set_conditional(CopyShaderGLES2::USE_BCS,current_env && current_env->fx_enabled[VS::ENV_FX_BCS]); + copy_shader.set_conditional(CopyShaderGLES2::USE_GAMMA,current_env && current_env->fx_enabled[VS::ENV_FX_GAMMA]); + copy_shader.set_conditional(CopyShaderGLES2::USE_GLOW,current_env && current_env->fx_enabled[VS::ENV_FX_GLOW]); + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR,current_env && current_env->fx_enabled[VS::ENV_FX_HDR]); + + copy_shader.bind(); + //copy_shader.set_uniform(CopyShaderGLES2::SOURCE,0); + + if (current_env && current_env->fx_enabled[VS::ENV_FX_GLOW]) { + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, framebuffer.blur[0].color ); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::GLOW_SOURCE),1); + + } + + if (current_env && current_env->fx_enabled[VS::ENV_FX_HDR]) { + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, current_vd->lum_color ); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::HDR_SOURCE),2); + copy_shader.set_uniform(CopyShaderGLES2::TONEMAP_EXPOSURE,float(current_env->fx_param[VS::ENV_FX_PARAM_HDR_EXPOSURE])); + + } + + + if (current_env && current_env->fx_enabled[VS::ENV_FX_BCS]) { + + Vector3 bcs; + bcs.x=current_env->fx_param[VS::ENV_FX_PARAM_BCS_BRIGHTNESS]; + bcs.y=current_env->fx_param[VS::ENV_FX_PARAM_BCS_CONTRAST]; + bcs.z=current_env->fx_param[VS::ENV_FX_PARAM_BCS_SATURATION]; + copy_shader.set_uniform(CopyShaderGLES2::BCS,bcs); + } + if (current_env && current_env->fx_enabled[VS::ENV_FX_GAMMA]) { + copy_shader.set_uniform(CopyShaderGLES2::GAMMA,float(current_env->fx_param[VS::ENV_FX_PARAM_GAMMA])); + } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, framebuffer.color ); + glUniform1i(copy_shader.get_uniform_location(CopyShaderGLES2::SOURCE),0); + + _copy_screen_quad(); + + copy_shader.set_conditional(CopyShaderGLES2::USE_BCS,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_GAMMA,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_GLOW,false); + copy_shader.set_conditional(CopyShaderGLES2::USE_HDR,false); + material_shader.set_conditional(MaterialShaderGLES2::USE_HDR,false); + + + if (current_env && current_env->fx_enabled[VS::ENV_FX_HDR] && GLOBAL_DEF("rasterizer/debug_hdr",false)) { + _debug_luminances(); + } + } + + current_env=NULL; + current_debug=VS::SCENARIO_DEBUG_DISABLED; + if (GLOBAL_DEF("rasterizer/debug_shadow_maps",false)) { + _debug_shadows(); + } + + +} +void RasterizerGLES2::end_shadow_map() { + + + + ERR_FAIL_COND(!shadow); + + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DITHER); + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + + + ShadowBuffer *sb = shadow->near_shadow_buffer; + + ERR_FAIL_COND(!sb); + + glBindFramebuffer(GL_FRAMEBUFFER, sb->fbo); + + if (!use_rgba_shadowmaps) + glColorMask(0, 0, 0, 0); + + //glEnable(GL_POLYGON_OFFSET_FILL); + //glPolygonOffset( 8.0f, 16.0f); + + CameraMatrix cm; + float z_near,z_far; + Transform light_transform; + + float dp_direction=0.0; + bool flip_facing=false; + + switch(shadow->base->type) { + + case VS::LIGHT_DIRECTIONAL: { + + if (shadow->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_SPLIT) { + + if (shadow_pass==0) { + + cm = shadow->custom_projection; + light_transform=shadow->custom_transform; + glViewport(0, sb->size*0.5, sb->size, sb->size*0.5); + glScissor(0, sb->size*0.5, sb->size, sb->size*0.5); + } else { + + cm = shadow->custom_projection2; + light_transform=shadow->custom_transform2; + glViewport(0, 0, sb->size, sb->size*0.5); + glScissor(0, 0, sb->size, sb->size*0.5); + + } + + glEnable(GL_SCISSOR_TEST); + + } else { + cm = shadow->custom_projection; + light_transform=shadow->custom_transform; + glViewport(0, 0, sb->size, sb->size); + } + + z_near=cm.get_z_near(); + z_far=cm.get_z_far(); + + _glClearDepth(1.0f); + glClearColor(1,1,1,1); + + if (use_rgba_shadowmaps) + glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); + else + glClear(GL_DEPTH_BUFFER_BIT); + + + glDisable(GL_SCISSOR_TEST); + + } break; + case VS::LIGHT_OMNI: { + + material_shader.set_conditional(MaterialShaderGLES2::USE_DUAL_PARABOLOID,true); + dp_direction = shadow_pass?1.0:-1.0; + flip_facing = (shadow_pass == 1); + light_transform=shadow->transform; + z_near=0; + z_far=shadow->base->vars[ VS::LIGHT_PARAM_RADIUS ]; + shadow->dp.x=1.0/z_far; + shadow->dp.y=dp_direction; + + if (shadow_pass==0) { + glViewport(0, sb->size*0.5, sb->size, sb->size*0.5); + glScissor(0, sb->size*0.5, sb->size, sb->size*0.5); + } else { + glViewport(0, 0, sb->size, sb->size*0.5); + glScissor(0, 0, sb->size, sb->size*0.5); + } + glEnable(GL_SCISSOR_TEST); + shadow->projection=cm; + + + glClearColor(1,1,1,1); + _glClearDepth(1.0f); + if (use_rgba_shadowmaps) + glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); + else + glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + + + } break; + case VS::LIGHT_SPOT: { + + float far = shadow->base->vars[ VS::LIGHT_PARAM_RADIUS ]; + ERR_FAIL_COND( far<=0 ); + float near= far/200.0; + if (near<0.05) + near=0.05; + + float angle = shadow->base->vars[ VS::LIGHT_PARAM_SPOT_ANGLE ]; + + cm.set_perspective( angle*2.0, 1.0, near, far ); + + shadow->projection=cm; // cache + light_transform=shadow->transform; + z_near=cm.get_z_near(); + z_far=cm.get_z_far(); + + glViewport(0, 0, sb->size, sb->size); + _glClearDepth(1.0f); + glClearColor(1,1,1,1); + if (use_rgba_shadowmaps) + glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); + else + glClear(GL_DEPTH_BUFFER_BIT); + + + } break; + } + + Transform light_transform_inverse = light_transform.affine_inverse(); + + opaque_render_list.sort_mat_geom(); + _render_list_forward(&opaque_render_list,light_transform,light_transform_inverse,cm,flip_facing,false); + + material_shader.set_conditional(MaterialShaderGLES2::USE_DUAL_PARABOLOID,false); + + glBindFramebuffer(GL_FRAMEBUFFER, current_rt?current_rt->fbo:base_framebuffer); + //glDisable(GL_POLYGON_OFFSET_FILL); + + + if (!use_rgba_shadowmaps) + glColorMask(1, 1, 1, 1); + + DEBUG_TEST_ERROR("Drawing Shadow"); + shadow=NULL; + + +} + +void RasterizerGLES2::_debug_draw_shadow(GLuint tex, const Rect2& p_rect) { + + + + + Matrix32 modelview; + modelview.translate(p_rect.pos.x, p_rect.pos.y); + canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, modelview); + glBindTexture(GL_TEXTURE_2D,tex); + + Vector3 coords[4]= { + Vector3(p_rect.pos.x, p_rect.pos.y, 0 ), + Vector3(p_rect.pos.x+p_rect.size.width, + p_rect.pos.y, 0 ), + Vector3(p_rect.pos.x+p_rect.size.width, + p_rect.pos.y+p_rect.size.height, 0 ), + Vector3(p_rect.pos.x, + p_rect.pos.y+p_rect.size.height, 0 ) + }; + + Vector3 texcoords[4]={ + Vector3( 0.0f,0.0f, 0), + Vector3( 1.0f,0.0f, 0), + Vector3( 1.0f, 1.0f, 0), + Vector3( 0.0f, 1.0f, 0), + }; + + _draw_primitive(4,coords,0,0,texcoords); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + +} + +void RasterizerGLES2::_debug_draw_shadows_type(Vector<ShadowBuffer>& p_shadows,Point2& ofs) { + + + Size2 debug_size(128,128); +// Size2 debug_size(512,512); + + + for (int i=0;i<p_shadows.size();i++) { + + ShadowBuffer *sb=&p_shadows[i]; + + if (!sb->owner) + continue; + + + if (sb->owner->base->type==VS::LIGHT_DIRECTIONAL) { + + //if (sb->owner->shadow_pass!=scene_pass-1) + // continue; + } else { + + //if (sb->owner->shadow_pass!=frame) + // continue; + } + _debug_draw_shadow(sb->depth, Rect2( ofs, debug_size )); + ofs.x+=debug_size.x; + if ( (ofs.x+debug_size.x) > viewport.width ) { + + ofs.x=0; + ofs.y+=debug_size.y; + } + } + +} + + +void RasterizerGLES2::_debug_luminances() { + + canvas_shader.set_conditional(CanvasShaderGLES2::DEBUG_ENCODED_32,true); + canvas_begin(); + glDisable(GL_BLEND); + canvas_shader.bind(); + + Size2 debug_size(128,128); + Size2 ofs; + + for (int i=0;i<framebuffer.luminance.size();i++) { + + _debug_draw_shadow(framebuffer.luminance[i].color, Rect2( ofs, debug_size )); + ofs.x+=debug_size.x; + if ( (ofs.x+debug_size.x) > viewport.width ) { + + ofs.x=0; + ofs.y+=debug_size.y; + } + } + + canvas_shader.set_conditional(CanvasShaderGLES2::DEBUG_ENCODED_32,false); + +} + + +void RasterizerGLES2::_debug_shadows() { + + canvas_begin(); + glDisable(GL_BLEND); + Size2 ofs; + + /* + for(int i=0;i<16;i++) { + glActiveTexture(GL_TEXTURE0+i); + //glDisable(GL_TEXTURE_2D); + } + glActiveTexture(GL_TEXTURE0); + //glEnable(GL_TEXTURE_2D); + */ + + + _debug_draw_shadows_type(near_shadow_buffers,ofs); +// _debug_draw_shadows_type(far_shadow_buffers,ofs); + +} + +void RasterizerGLES2::end_frame() { + + + //print_line("VTX: "+itos(_rinfo.vertex_count)+" OBJ: "+itos(_rinfo.object_count)+" MAT: "+itos(_rinfo.mat_change_count)+" SHD: "+itos(_rinfo.shader_change_count)+" CI: "+itos(_rinfo.ci_draw_commands)); + + //print_line("TOTAL VTX: "+itos(_rinfo.vertex_count)); + OS::get_singleton()->swap_buffers(); +} + +void RasterizerGLES2::flush_frame() { + + glFlush(); +} + +/* CANVAS API */ + +void RasterizerGLES2::canvas_begin() { + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); +#ifdef GLEW_ENABLED + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + glLineWidth(1.0); + glBindBuffer(GL_ARRAY_BUFFER,0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); + for(int i=0;i<VS::ARRAY_MAX;i++) { + glDisableVertexAttribArray(i); + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture( GL_TEXTURE_2D, white_tex ); + canvas_tex=RID(); + //material_shader.unbind(); + canvas_shader.unbind(); + canvas_shader.bind(); + canvas_shader.set_uniform(CanvasShaderGLES2::TEXTURE, 0); + _set_color_attrib(Color(1,1,1)); + Transform canvas_transform; + canvas_transform.translate(-(viewport.width / 2.0f), -(viewport.height / 2.0f), 0.0f); + canvas_transform.scale( Vector3( 2.0f / viewport.width, -2.0f / viewport.height, 1.0f ) ); + canvas_shader.set_uniform(CanvasShaderGLES2::PROJECTION_MATRIX,canvas_transform); + canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,Matrix32()); + canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Matrix32()); + + canvas_opacity=1.0; + + canvas_blend_mode=VS::MATERIAL_BLEND_MODE_MIX; + + +} +void RasterizerGLES2::canvas_set_opacity(float p_opacity) { + + canvas_opacity = p_opacity; +} + +void RasterizerGLES2::canvas_set_blend_mode(VS::MaterialBlendMode p_mode) { + + if (p_mode==canvas_blend_mode) + return; + switch(p_mode) { + + case VS::MATERIAL_BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + } break; + case VS::MATERIAL_BLEND_MODE_ADD: { + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + + } break; + case VS::MATERIAL_BLEND_MODE_SUB: { + + glBlendEquation(GL_FUNC_SUBTRACT); + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + } break; + case VS::MATERIAL_BLEND_MODE_MUL: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + } break; + + } + + canvas_blend_mode=p_mode; + +} + + +void RasterizerGLES2::canvas_begin_rect(const Matrix32& p_transform) { + + + canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,p_transform); + canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Matrix32()); + +} + +void RasterizerGLES2::canvas_set_clip(bool p_clip, const Rect2& p_rect) { + + if (p_clip) { + + glEnable(GL_SCISSOR_TEST); + glScissor(viewport.x+p_rect.pos.x,viewport.y+ (viewport.height-(p_rect.pos.y+p_rect.size.height)), + p_rect.size.width,p_rect.size.height); + } else { + + glDisable(GL_SCISSOR_TEST); + } + + +} + +void RasterizerGLES2::canvas_end_rect() { + + //glPopMatrix(); +} + + +RasterizerGLES2::Texture* RasterizerGLES2::_bind_canvas_texture(const RID& p_texture) { + + if (p_texture==canvas_tex) { + if (canvas_tex.is_valid()) { + Texture*texture=texture_owner.get(p_texture); + return texture; + } + return NULL; + } + + + + if (p_texture.is_valid()) { + + Texture*texture=texture_owner.get(p_texture); + if (!texture) { + canvas_tex=RID(); + glBindTexture(GL_TEXTURE_2D,white_tex); + return NULL; + } + + if (texture->render_target) + texture->render_target->last_pass=frame; + + glBindTexture(GL_TEXTURE_2D,texture->tex_id); + canvas_tex=p_texture; + return texture; + + + } else { + + + glBindTexture(GL_TEXTURE_2D,white_tex); + canvas_tex=p_texture; + } + + + return NULL; +} + +void RasterizerGLES2::canvas_draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width) { + + _bind_canvas_texture(RID()); + Color c=p_color; + c.a*=canvas_opacity; + _set_color_attrib(c); + + Vector3 verts[2]={ + Vector3(p_from.x,p_from.y,0), + Vector3(p_to.x,p_to.y,0) + }; + + glLineWidth(p_width); + _draw_primitive(2,verts,0,0,0); + _rinfo.ci_draw_commands++; +} + +void RasterizerGLES2::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color* p_colors, const Vector2 *p_uvs) { + + + + static const GLenum prim[5]={GL_POINTS,GL_POINTS,GL_LINES,GL_TRIANGLES,GL_TRIANGLE_FAN}; + + +//#define GLES_USE_PRIMITIVE_BUFFER + +#ifndef GLES_NO_CLIENT_ARRAYS + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glVertexAttribPointer( VS::ARRAY_VERTEX, 2 ,GL_FLOAT, false, sizeof(Vector2), p_vertices ); + + if (p_colors) { + + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttribPointer( VS::ARRAY_COLOR, 4 ,GL_FLOAT, false, sizeof(Color), p_colors ); + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + } + + if (p_uvs) { + + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glVertexAttribPointer( VS::ARRAY_TEX_UV, 2 ,GL_FLOAT, false, sizeof(Vector2), p_uvs ); + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } + + glDrawArrays(prim[p_points],0,p_points); + +#else + + glBindBuffer(GL_ARRAY_BUFFER,gui_quad_buffer); + float b[32]; + int ofs=0; + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glVertexAttribPointer( VS::ARRAY_VERTEX, 2 ,GL_FLOAT, false, sizeof(float)*2, ((float*)0)+ofs ); + for(int i=0;i<p_points;i++) { + b[ofs++]=p_vertices[i].x; + b[ofs++]=p_vertices[i].y; + } + + if (p_colors) { + + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttribPointer( VS::ARRAY_COLOR, 4 ,GL_FLOAT, false, sizeof(float)*4, ((float*)0)+ofs ); + for(int i=0;i<p_points;i++) { + b[ofs++]=p_colors[i].r; + b[ofs++]=p_colors[i].g; + b[ofs++]=p_colors[i].b; + b[ofs++]=p_colors[i].a; + } + + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + } + + + if (p_uvs) { + + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glVertexAttribPointer( VS::ARRAY_TEX_UV, 2 ,GL_FLOAT, false, sizeof(float)*2, ((float*)0)+ofs ); + for(int i=0;i<p_points;i++) { + b[ofs++]=p_uvs[i].x; + b[ofs++]=p_uvs[i].y; + } + + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } + + glBufferSubData(GL_ARRAY_BUFFER,0,ofs*4,&b[0]); + glDrawArrays(prim[p_points],0,p_points); + glBindBuffer(GL_ARRAY_BUFFER,0); + + +#endif + _rinfo.ci_draw_commands++; +} + +void RasterizerGLES2::_draw_gui_primitive2(int p_points, const Vector2 *p_vertices, const Color* p_colors, const Vector2 *p_uvs, const Vector2 *p_uvs2) { + + + static const GLenum prim[5]={GL_POINTS,GL_POINTS,GL_LINES,GL_TRIANGLES,GL_TRIANGLE_FAN}; + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glVertexAttribPointer( VS::ARRAY_VERTEX, 2 ,GL_FLOAT, false, sizeof(Vector2), p_vertices ); + if (p_colors) { + + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttribPointer( VS::ARRAY_COLOR, 4 ,GL_FLOAT, false, sizeof(Color), p_colors ); + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + } + + if (p_uvs) { + + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glVertexAttribPointer( VS::ARRAY_TEX_UV, 2 ,GL_FLOAT, false, sizeof(Vector2), p_uvs ); + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } + + if (p_uvs2) { + + glEnableVertexAttribArray(VS::ARRAY_TEX_UV2); + glVertexAttribPointer( VS::ARRAY_TEX_UV2, 2 ,GL_FLOAT, false, sizeof(Vector2), p_uvs2 ); + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV2); + } + + glDrawArrays(prim[p_points],0,p_points); + _rinfo.ci_draw_commands++; +} + +void RasterizerGLES2::_draw_textured_quad(const Rect2& p_rect, const Rect2& p_src_region, const Size2& p_tex_size,bool p_h_flip, bool p_v_flip ) { + + Vector2 texcoords[4]= { + Vector2( p_src_region.pos.x/p_tex_size.width, + p_src_region.pos.y/p_tex_size.height), + + Vector2((p_src_region.pos.x+p_src_region.size.width)/p_tex_size.width, + p_src_region.pos.y/p_tex_size.height), + + Vector2( (p_src_region.pos.x+p_src_region.size.width)/p_tex_size.width, + (p_src_region.pos.y+p_src_region.size.height)/p_tex_size.height), + + Vector2( p_src_region.pos.x/p_tex_size.width, + (p_src_region.pos.y+p_src_region.size.height)/p_tex_size.height) + }; + + if (p_h_flip) { + SWAP( texcoords[0], texcoords[1] ); + SWAP( texcoords[2], texcoords[3] ); + } + if (p_v_flip) { + SWAP( texcoords[1], texcoords[2] ); + SWAP( texcoords[0], texcoords[3] ); + } + + Vector2 coords[4]= { + Vector2( p_rect.pos.x, p_rect.pos.y ), + Vector2( p_rect.pos.x+p_rect.size.width, p_rect.pos.y ), + Vector2( p_rect.pos.x+p_rect.size.width, p_rect.pos.y+p_rect.size.height ), + Vector2( p_rect.pos.x,p_rect.pos.y+p_rect.size.height ) + }; + + _draw_gui_primitive(4,coords,0,texcoords); + _rinfo.ci_draw_commands++; +} + +void RasterizerGLES2::_draw_quad(const Rect2& p_rect) { + + Vector2 coords[4]= { + Vector2( p_rect.pos.x,p_rect.pos.y ), + Vector2( p_rect.pos.x+p_rect.size.width,p_rect.pos.y ), + Vector2( p_rect.pos.x+p_rect.size.width,p_rect.pos.y+p_rect.size.height ), + Vector2( p_rect.pos.x,p_rect.pos.y+p_rect.size.height ) + }; + + _draw_gui_primitive(4,coords,0,0); + _rinfo.ci_draw_commands++; + +} + +void RasterizerGLES2::canvas_draw_rect(const Rect2& p_rect, int p_flags, const Rect2& p_source,RID p_texture,const Color& p_modulate) { + + Color m = p_modulate; + m.a*=canvas_opacity; + _set_color_attrib(m); + Texture *texture = _bind_canvas_texture(p_texture); + + + if ( texture ) { + + if (!(p_flags&CANVAS_RECT_REGION)) { + + Rect2 region = Rect2(0,0,texture->width,texture->height); + _draw_textured_quad(p_rect,region,region.size,p_flags&CANVAS_RECT_FLIP_H,p_flags&CANVAS_RECT_FLIP_V); + + } else { + + _draw_textured_quad(p_rect, p_source, Size2(texture->width,texture->height),p_flags&CANVAS_RECT_FLIP_H,p_flags&CANVAS_RECT_FLIP_V ); + + } + } else { + + //glDisable(GL_TEXTURE_2D); + _draw_quad( p_rect ); + //print_line("rect: "+p_rect); + + } + + _rinfo.ci_draw_commands++; + +} + +void RasterizerGLES2::canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margin, bool p_draw_center,const Color& p_modulate) { + + Color m = p_modulate; + m.a*=canvas_opacity; + _set_color_attrib(m); + + Texture* texture=_bind_canvas_texture(p_texture); + ERR_FAIL_COND(!texture); + /* CORNERS */ + + _draw_textured_quad( // top left + Rect2( p_rect.pos, Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_TOP])), + Rect2( Point2(), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_TOP])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // top right + Rect2( Point2( p_rect.pos.x + p_rect.size.width - p_margin[MARGIN_RIGHT], p_rect.pos.y), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_TOP])), + Rect2( Point2(texture->width-p_margin[MARGIN_RIGHT],0), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_TOP])), + Size2( texture->width, texture->height ) ); + + + _draw_textured_quad( // bottom left + Rect2( Point2(p_rect.pos.x,p_rect.pos.y + p_rect.size.height - p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_BOTTOM])), + Rect2( Point2(0,texture->height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_BOTTOM])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // bottom right + Rect2( Point2( p_rect.pos.x + p_rect.size.width - p_margin[MARGIN_RIGHT], p_rect.pos.y + p_rect.size.height - p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_BOTTOM])), + Rect2( Point2(texture->width-p_margin[MARGIN_RIGHT],texture->height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_BOTTOM])), + Size2( texture->width, texture->height ) ); + + Rect2 rect_center( p_rect.pos+Point2( p_margin[MARGIN_LEFT], p_margin[MARGIN_TOP]), Size2( p_rect.size.width - p_margin[MARGIN_LEFT] - p_margin[MARGIN_RIGHT], p_rect.size.height - p_margin[MARGIN_TOP] - p_margin[MARGIN_BOTTOM] )); + + Rect2 src_center( Point2( p_margin[MARGIN_LEFT], p_margin[MARGIN_TOP]), Size2( texture->width - p_margin[MARGIN_LEFT] - p_margin[MARGIN_RIGHT], texture->height - p_margin[MARGIN_TOP] - p_margin[MARGIN_BOTTOM] )); + + + _draw_textured_quad( // top + Rect2( Point2(rect_center.pos.x,p_rect.pos.y),Size2(rect_center.size.width,p_margin[MARGIN_TOP])), + Rect2( Point2(p_margin[MARGIN_LEFT],0), Size2(src_center.size.width,p_margin[MARGIN_TOP])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // bottom + Rect2( Point2(rect_center.pos.x,rect_center.pos.y+rect_center.size.height),Size2(rect_center.size.width,p_margin[MARGIN_BOTTOM])), + Rect2( Point2(p_margin[MARGIN_LEFT],src_center.pos.y+src_center.size.height), Size2(src_center.size.width,p_margin[MARGIN_BOTTOM])), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // left + Rect2( Point2(p_rect.pos.x,rect_center.pos.y),Size2(p_margin[MARGIN_LEFT],rect_center.size.height)), + Rect2( Point2(0,p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_LEFT],src_center.size.height)), + Size2( texture->width, texture->height ) ); + + _draw_textured_quad( // right + Rect2( Point2(rect_center.pos.x+rect_center.size.width,rect_center.pos.y),Size2(p_margin[MARGIN_RIGHT],rect_center.size.height)), + Rect2( Point2(src_center.pos.x+src_center.size.width,p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_RIGHT],src_center.size.height)), + Size2( texture->width, texture->height ) ); + + if (p_draw_center) { + + _draw_textured_quad( + rect_center, + src_center, + Size2( texture->width, texture->height )); + } + + + _rinfo.ci_draw_commands++; +} + +void RasterizerGLES2::canvas_draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width) { + + ERR_FAIL_COND(p_points.size()<1); + _set_color_attrib(Color(1,1,1,canvas_opacity)); + _bind_canvas_texture(p_texture); + _draw_gui_primitive(p_points.size(),p_points.ptr(),p_colors.ptr(),p_uvs.ptr()); + + _rinfo.ci_draw_commands++; +} + +void RasterizerGLES2::canvas_draw_polygon(int p_vertex_count, const int* p_indices, const Vector2* p_vertices, const Vector2* p_uvs, const Color* p_colors,const RID& p_texture,bool p_singlecolor) { + + bool do_colors=false; + + if (p_singlecolor) { + Color m = *p_colors; + m.a*=canvas_opacity; + _set_color_attrib(m); + } else if (!p_colors) { + _set_color_attrib( Color(1,1,1,canvas_opacity)); + } else + do_colors=true; + + Texture *texture = _bind_canvas_texture(p_texture); + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glVertexAttribPointer( VS::ARRAY_VERTEX, 2 ,GL_FLOAT, false, sizeof(Vector2), p_vertices ); + if (do_colors) { + + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttribPointer( VS::ARRAY_COLOR, 4 ,GL_FLOAT, false, sizeof(Color), p_colors ); + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + } + + if (texture && p_uvs) { + + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glVertexAttribPointer( VS::ARRAY_TEX_UV, 2 ,GL_FLOAT, false, sizeof(Vector2), p_uvs ); + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } + + if (p_indices) { + + glDrawElements(GL_TRIANGLES, p_vertex_count, GL_UNSIGNED_INT, p_indices ); + } else { + glDrawArrays(GL_TRIANGLES,0,p_vertex_count); + } + + _rinfo.ci_draw_commands++; + +}; + + +void RasterizerGLES2::canvas_set_transform(const Matrix32& p_transform) { + + canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,p_transform); + + //canvas_transform = Variant(p_transform); +} + +/* ENVIRONMENT */ + +RID RasterizerGLES2::environment_create() { + + Environment * env = memnew( Environment ); + return environment_owner.make_rid(env); +} + +void RasterizerGLES2::environment_set_background(RID p_env,VS::EnvironmentBG p_bg) { + + ERR_FAIL_INDEX(p_bg,VS::ENV_BG_MAX); + Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND(!env); + env->bg_mode=p_bg; +} + +VS::EnvironmentBG RasterizerGLES2::environment_get_background(RID p_env) const{ + + const Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND_V(!env,VS::ENV_BG_MAX); + return env->bg_mode; +} + +void RasterizerGLES2::environment_set_background_param(RID p_env,VS::EnvironmentBGParam p_param, const Variant& p_value){ + + ERR_FAIL_INDEX(p_param,VS::ENV_BG_PARAM_MAX); + Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND(!env); + env->bg_param[p_param]=p_value; + +} +Variant RasterizerGLES2::environment_get_background_param(RID p_env,VS::EnvironmentBGParam p_param) const{ + + ERR_FAIL_INDEX_V(p_param,VS::ENV_BG_PARAM_MAX,Variant()); + const Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND_V(!env,Variant()); + return env->bg_param[p_param]; + +} + +void RasterizerGLES2::environment_set_enable_fx(RID p_env,VS::EnvironmentFx p_effect,bool p_enabled){ + + ERR_FAIL_INDEX(p_effect,VS::ENV_FX_MAX); + Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND(!env); + env->fx_enabled[p_effect]=p_enabled; +} +bool RasterizerGLES2::environment_is_fx_enabled(RID p_env,VS::EnvironmentFx p_effect) const{ + + ERR_FAIL_INDEX_V(p_effect,VS::ENV_FX_MAX,false); + const Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND_V(!env,false); + return env->fx_enabled[p_effect]; + +} + +void RasterizerGLES2::environment_fx_set_param(RID p_env,VS::EnvironmentFxParam p_param,const Variant& p_value){ + + ERR_FAIL_INDEX(p_param,VS::ENV_FX_PARAM_MAX); + Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND(!env); + env->fx_param[p_param]=p_value; +} +Variant RasterizerGLES2::environment_fx_get_param(RID p_env,VS::EnvironmentFxParam p_param) const{ + + ERR_FAIL_INDEX_V(p_param,VS::ENV_FX_PARAM_MAX,Variant()); + const Environment * env = environment_owner.get(p_env); + ERR_FAIL_COND_V(!env,Variant()); + return env->fx_param[p_param]; + +} + +/*MISC*/ + +bool RasterizerGLES2::is_texture(const RID& p_rid) const { + + return texture_owner.owns(p_rid); +} +bool RasterizerGLES2::is_material(const RID& p_rid) const { + + return material_owner.owns(p_rid); +} +bool RasterizerGLES2::is_mesh(const RID& p_rid) const { + + return mesh_owner.owns(p_rid); +} +bool RasterizerGLES2::is_multimesh(const RID& p_rid) const { + + return multimesh_owner.owns(p_rid); +} +bool RasterizerGLES2::is_particles(const RID &p_beam) const { + + return particles_owner.owns(p_beam); +} + +bool RasterizerGLES2::is_light(const RID& p_rid) const { + + return light_owner.owns(p_rid); +} +bool RasterizerGLES2::is_light_instance(const RID& p_rid) const { + + return light_instance_owner.owns(p_rid); +} +bool RasterizerGLES2::is_particles_instance(const RID& p_rid) const { + + return particles_instance_owner.owns(p_rid); +} +bool RasterizerGLES2::is_skeleton(const RID& p_rid) const { + + return skeleton_owner.owns(p_rid); +} +bool RasterizerGLES2::is_environment(const RID& p_rid) const { + + return environment_owner.owns(p_rid); +} +bool RasterizerGLES2::is_shader(const RID& p_rid) const { + + return false; +} + +void RasterizerGLES2::free(const RID& p_rid) { + if (texture_owner.owns(p_rid)) { + + // delete the texture + Texture *texture = texture_owner.get(p_rid); + +// glDeleteTextures( 1,&texture->tex_id ); + _rinfo.texture_mem-=texture->total_data_size; + texture_owner.free(p_rid); + memdelete(texture); + + } else if (shader_owner.owns(p_rid)) { + + // delete the texture + Shader *shader = shader_owner.get(p_rid); + + switch(shader->mode) { + case VS::SHADER_MATERIAL: { + material_shader.free_custom_shader(shader->custom_code_id); + } break; + case VS::SHADER_POST_PROCESS: { + //postprocess_shader.free_custom_shader(shader->custom_code_id); + } break; + } + + if (shader->dirty_list.in_list()) + _shader_dirty_list.remove(&shader->dirty_list); + + //material_shader.free_custom_shader(shader->custom_code_id); + shader_owner.free(p_rid); + memdelete(shader); + + } else if (material_owner.owns(p_rid)) { + + Material *material = material_owner.get( p_rid ); + ERR_FAIL_COND(!material); + + _free_fixed_material(p_rid); //just in case + material_owner.free(p_rid); + memdelete(material); + + } else if (mesh_owner.owns(p_rid)) { + + Mesh *mesh = mesh_owner.get(p_rid); + ERR_FAIL_COND(!mesh); + for (int i=0;i<mesh->surfaces.size();i++) { + + Surface *surface = mesh->surfaces[i]; + if (surface->array_local != 0) { + memfree(surface->array_local); + }; + if (surface->index_array_local != 0) { + memfree(surface->index_array_local); + }; + + if (mesh->morph_target_count>0) { + + for(int i=0;i<mesh->morph_target_count;i++) { + + memfree(surface->morph_targets_local[i].array); + } + memfree(surface->morph_targets_local); + surface->morph_targets_local=NULL; + } + + if (surface->vertex_id) + glDeleteBuffers(1,&surface->vertex_id); + if (surface->index_id) + glDeleteBuffers(1,&surface->index_id); + + memdelete( surface ); + }; + + mesh->surfaces.clear(); + + mesh_owner.free(p_rid); + memdelete(mesh); + + } else if (multimesh_owner.owns(p_rid)) { + + MultiMesh *multimesh = multimesh_owner.get(p_rid); + ERR_FAIL_COND(!multimesh); + + if (multimesh->tex_id) { + glDeleteTextures(1,&multimesh->tex_id); + } + + multimesh_owner.free(p_rid); + memdelete(multimesh); + + } else if (particles_owner.owns(p_rid)) { + + Particles *particles = particles_owner.get(p_rid); + ERR_FAIL_COND(!particles); + + particles_owner.free(p_rid); + memdelete(particles); + } else if (particles_instance_owner.owns(p_rid)) { + + ParticlesInstance *particles_isntance = particles_instance_owner.get(p_rid); + ERR_FAIL_COND(!particles_isntance); + + particles_instance_owner.free(p_rid); + memdelete(particles_isntance); + + } else if (skeleton_owner.owns(p_rid)) { + + Skeleton *skeleton = skeleton_owner.get( p_rid ); + ERR_FAIL_COND(!skeleton); + + if (skeleton->dirty_list.in_list()) + _skeleton_dirty_list.remove(&skeleton->dirty_list); + if (skeleton->tex_id) { + glDeleteTextures(1,&skeleton->tex_id); + } + skeleton_owner.free(p_rid); + memdelete(skeleton); + + } else if (light_owner.owns(p_rid)) { + + Light *light = light_owner.get( p_rid ); + ERR_FAIL_COND(!light) + + light_owner.free(p_rid); + memdelete(light); + + } else if (light_instance_owner.owns(p_rid)) { + + LightInstance *light_instance = light_instance_owner.get( p_rid ); + ERR_FAIL_COND(!light_instance); + light_instance->clear_shadow_buffers(); + light_instance_owner.free(p_rid); + memdelete( light_instance ); + + } else if (environment_owner.owns(p_rid)) { + + Environment *env = environment_owner.get( p_rid ); + ERR_FAIL_COND(!env); + + environment_owner.free(p_rid); + memdelete( env ); + + } else if (viewport_data_owner.owns(p_rid)) { + + ViewportData *viewport_data = viewport_data_owner.get( p_rid ); + ERR_FAIL_COND(!viewport_data); + glDeleteFramebuffers(1,&viewport_data->lum_fbo); + glDeleteTextures(1,&viewport_data->lum_color); + viewport_data_owner.free(p_rid); + memdelete( viewport_data ); + + } else if (render_target_owner.owns(p_rid)) { + + RenderTarget *render_target = render_target_owner.get( p_rid ); + ERR_FAIL_COND(!render_target); + render_target_set_size(p_rid,0,0); //clears framebuffer + texture_owner.free(render_target->texture); + memdelete(render_target->texture_ptr); + render_target_owner.free(p_rid); + memdelete( render_target ); + + }; +} + + + +bool RasterizerGLES2::ShadowBuffer::init(int p_size,bool p_use_depth) { + + size=p_size; + // Create a framebuffer object + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + // Create a render buffer + glGenRenderbuffers(1, &rbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + + // Create a texture for storing the depth + glGenTextures(1, &depth); + glBindTexture(GL_TEXTURE_2D, depth); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Remove artifact on the edges of the shadowmap + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //print_line("ERROR? "+itos(glGetError())); + if ( p_use_depth ) { + + // We'll use a depth texture to store the depths in the shadow map + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size, size, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + // Attach the depth texture to FBO depth attachment point + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, depth, 0); + +#ifdef GLEW_ENABLED + glDrawBuffer(GL_NONE); +#endif + } else { + // We'll use a RGBA texture into which we pack the depth info + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + // Attach the RGBA texture to FBO color attachment point + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, depth, 0); + + // Allocate 16-bit depth buffer + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size,size); + + // Attach the render buffer as depth buffer - will be ignored + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, rbo); + + + } + +#if 0 + + if (!p_use_depth) { + + + print_line("try no depth!"); + + glGenTextures(1, &rgba); + glBindTexture(GL_TEXTURE_2D, rgba); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rgba, 0); +/* + glGenRenderbuffers(1, &depth); + glBindRenderbuffer(GL_RENDERBUFFER, depth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, p_size, p_size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); +*/ + glGenTextures(1, &depth); + glBindTexture(GL_TEXTURE_2D, depth); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, size, size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0); + + } else { + +// glGenRenderbuffers(1, &rbo); +// glBindRenderbuffer(GL_RENDERBUFFER, rbo); + + glGenTextures(1, &depth); + glBindTexture(GL_TEXTURE_2D, depth); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size, size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0); + + } + +#endif + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + //printf("errnum: %x\n",status); +#ifdef GLEW_ENABLED + if (p_use_depth) { + glDrawBuffer(GL_BACK); + } +#endif + glBindFramebuffer(GL_FRAMEBUFFER, 0); + DEBUG_TEST_ERROR("Shadow Buffer Init"); + ERR_FAIL_COND_V( status != GL_FRAMEBUFFER_COMPLETE,false ); + +#ifdef GLEW_ENABLED + if (p_use_depth) { + glDrawBuffer(GL_BACK); + } +#endif + +#if 0 + glGenFramebuffers(1, &fbo_blur); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_blur); + + glGenRenderbuffers(1, &rbo_blur); + glBindRenderbuffer(GL_RENDERBUFFER, rbo_blur); + + glGenTextures(1, &blur); + glBindTexture(GL_TEXTURE_2D, blur); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); +// glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, size, size, 0, +// GL_DEPTH_COMPONENT16, GL_UNSIGNED_SHORT, NULL); + + // Attach the RGBA texture to FBO color attachment point + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, blur, 0); + + // Allocate 16-bit depth buffer + /* glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size); + + // Attach the render buffer as depth buffer - will be ignored + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, rbo_blur); +*/ + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + OS::get_singleton()->print("Status: %x\n",status); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + DEBUG_TEST_ERROR("Shadow Blur Buffer Init"); + ERR_FAIL_COND_V( status != GL_FRAMEBUFFER_COMPLETE,false ); +#endif + + return true; + +} + + + +void RasterizerGLES2::_update_framebuffer() { + + if (!use_framebuffers) + return; + + int scale = GLOBAL_DEF("rasterizer/framebuffer_shrink",1); + + int dwidth = OS::get_singleton()->get_video_mode().width/scale; + int dheight = OS::get_singleton()->get_video_mode().height/scale; + + if (framebuffer.fbo && dwidth==framebuffer.width && dheight==framebuffer.height) + return; + + + bool use_fbo=true; + + + if (framebuffer.fbo!=0) { + + glDeleteFramebuffers(1,&framebuffer.fbo); +#if 0 + glDeleteTextures(1,&framebuffer.depth); +#else + glDeleteRenderbuffers(1,&framebuffer.depth); + +#endif + glDeleteTextures(1,&framebuffer.color); + + for(int i=0;i<framebuffer.luminance.size();i++) { + + glDeleteTextures(1,&framebuffer.luminance[i].color); + glDeleteFramebuffers(1,&framebuffer.luminance[i].fbo); + + } + for(int i=0;i<3;i++) { + + glDeleteTextures(1,&framebuffer.blur[i].color); + glDeleteFramebuffers(1,&framebuffer.blur[i].fbo); + } + + glDeleteTextures(1,&framebuffer.sample_color); + glDeleteFramebuffers(1,&framebuffer.sample_fbo); + + framebuffer.fbo=0; + } + + framebuffer.active=use_fbo; + framebuffer.width=dwidth; + framebuffer.height=dheight; + framebuffer.scale=scale; + + if (!framebuffer.active) + return; + + + glGenFramebuffers(1, &framebuffer.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.fbo); + + //print_line("generating fbo, id: "+itos(framebuffer.fbo)); + //depth + + // Create a render buffer + +#if 0 + glGenTextures(1, &framebuffer.depth); + glBindTexture(GL_TEXTURE_2D, framebuffer.depth); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, framebuffer.width, framebuffer.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE ); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, framebuffer.depth, 0); + +#else + + glGenRenderbuffers(1, &framebuffer.depth); + glBindRenderbuffer(GL_RENDERBUFFER, framebuffer.depth ); + + glRenderbufferStorage(GL_RENDERBUFFER, use_depth24?_DEPTH_COMPONENT24_OES:GL_DEPTH_COMPONENT16, framebuffer.width,framebuffer.height); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, framebuffer.depth); + +#endif + //color + glGenTextures(1, &framebuffer.color); + glBindTexture(GL_TEXTURE_2D, framebuffer.color); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, framebuffer.width, framebuffer.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer.color, 0); +# + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + + glDeleteFramebuffers(1,&framebuffer.fbo); +#if 0 + glDeleteTextures(1,&framebuffer.depth); +#else + glDeleteRenderbuffers(1,&framebuffer.depth); + +#endif + glDeleteTextures(1,&framebuffer.color); + framebuffer.fbo=0; + framebuffer.active=false; + //print_line("**************** NO FAMEBUFFEEEERRRR????"); + WARN_PRINT("Could not create framebuffer!!"); + } + + //sample + + glGenFramebuffers(1, &framebuffer.sample_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.sample_fbo); + glGenTextures(1, &framebuffer.sample_color); + glBindTexture(GL_TEXTURE_2D, framebuffer.sample_color); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, framebuffer.width, framebuffer.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer.sample_color, 0); +# + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + + glDeleteFramebuffers(1,&framebuffer.fbo); +#if 0 + glDeleteTextures(1,&framebuffer.depth); +#else + glDeleteRenderbuffers(1,&framebuffer.depth); + +#endif + glDeleteTextures(1,&framebuffer.color); + glDeleteTextures(1,&framebuffer.sample_color); + glDeleteFramebuffers(1,&framebuffer.sample_fbo); + framebuffer.fbo=0; + framebuffer.active=false; + //print_line("**************** NO FAMEBUFFEEEERRRR????"); + WARN_PRINT("Could not create framebuffer!!"); + } + //blur + + int size = GLOBAL_DEF("rasterizer/blur_buffer_size",256); + + if (size!=framebuffer.blur_size) { + + + for(int i=0;i<3;i++) { + + if (framebuffer.blur[i].fbo) { + glDeleteFramebuffers(1,&framebuffer.blur[i].fbo); + glDeleteTextures(1,&framebuffer.blur[i].color); + framebuffer.blur[i].fbo=0; + framebuffer.blur[i].color=0; + } + } + + framebuffer.blur_size=size; + + for(int i=0;i<3;i++) { + + glGenFramebuffers(1, &framebuffer.blur[i].fbo); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.blur[i].fbo); + + glGenTextures(1, &framebuffer.blur[i].color); + glBindTexture(GL_TEXTURE_2D, framebuffer.blur[i].color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, framebuffer.blur[i].color, 0); + + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + DEBUG_TEST_ERROR("Shadow Buffer Init"); + ERR_CONTINUE( status != GL_FRAMEBUFFER_COMPLETE ); + + + } + + } + + // luminance + + int base_size = GLOBAL_DEF("rasterizer/luminance_buffer_size",81); + + if (framebuffer.luminance.empty() || framebuffer.luminance[0].size!=base_size) { + + + for(int i=0;i<framebuffer.luminance.size();i++) { + + glDeleteFramebuffers(1,&framebuffer.luminance[i].fbo); + glDeleteTextures(1,&framebuffer.luminance[i].color); + } + + framebuffer.luminance.clear(); + + + + while(base_size>0) { + + FrameBuffer::Luminance lb; + lb.size=base_size; + + + glGenFramebuffers(1, &lb.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, lb.fbo); + + glGenTextures(1, &lb.color); + glBindTexture(GL_TEXTURE_2D, lb.color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lb.size, lb.size, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, lb.color, 0); + + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + DEBUG_TEST_ERROR("Shadow Buffer Init"); + ERR_CONTINUE( status != GL_FRAMEBUFFER_COMPLETE ); + + base_size/=3; + framebuffer.luminance.push_back(lb); + + } + } + + + +} + +void RasterizerGLES2::set_base_framebuffer(GLuint p_id) { + + base_framebuffer=p_id; +} + +#if 0 +void RasterizerGLES2::_update_blur_buffer() { + + int size = GLOBAL_DEF("rasterizer/blur_buffer_size",256); + if (size!=framebuffer.blur_size) { + + for(int i=0;i<3;i++) { + + if (framebuffer.blur[i].fbo) { + glDeleteFramebuffers(1,&framebuffer.blur[i].fbo); + glDeleteTextures(1,&framebuffer.blur[i].color); + framebuffer.blur[i].fbo=0; + framebuffer.blur[i].color=0; + } + } + + framebuffer.blur_size=size; + + for(int i=0;i<3;i++) { + + glGenFramebuffers(1, &framebuffer.blur[i].fbo); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.blur[i].fbo); + + glGenTextures(1, &framebuffer.blur[i].color); + glBindTexture(GL_TEXTURE_2D, framebuffer.blur[i].color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, framebuffer.blur[i].color, 0); + + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + DEBUG_TEST_ERROR("Shadow Buffer Init"); + ERR_CONTINUE( status != GL_FRAMEBUFFER_COMPLETE ); + + + } + + } + + + + + +} +#endif +void RasterizerGLES2::init() { + +#ifdef GLEW_ENABLED + GLuint res = glewInit(); + ERR_FAIL_COND(res!=GLEW_OK); +#endif + + + + + scene_pass=1; + + if (extensions.size()==0) { + + set_extensions( (const char*)glGetString( GL_EXTENSIONS )); + } + + + GLint tmp = 0; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &tmp); + //print_line("GL_MAX_VERTEX_ATTRIBS "+itos(tmp)); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glFrontFace(GL_CW); + //glEnable(GL_TEXTURE_2D); + + default_material=create_default_material(); + + material_shader.init(); + canvas_shader.init(); + copy_shader.init(); + +#ifdef GLEW_ENABLED + material_shader.set_conditional(MaterialShaderGLES2::USE_GLES_OVER_GL,true); + canvas_shader.set_conditional(CanvasShaderGLES2::USE_GLES_OVER_GL,true); + copy_shader.set_conditional(CopyShaderGLES2::USE_GLES_OVER_GL,true); +#endif + + + shadow=NULL; + shadow_pass=0; + + framebuffer.fbo=0; + framebuffer.width=0; + framebuffer.height=0; +// framebuffer.buff16=false; +// framebuffer.blur[0].fbo=false; +// framebuffer.blur[1].fbo=false; + framebuffer.active=false; + + //do a single initial clear + glClearColor(0,0,0,1); + //glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + skinned_buffer_size = GLOBAL_DEF("rasterizer/skinned_buffer_size",DEFAULT_SKINNED_BUFFER_SIZE); + skinned_buffer = memnew_arr( uint8_t, skinned_buffer_size ); + + glGenTextures(1, &white_tex); + unsigned char whitetexdata[8*8*3]; + for(int i=0;i<8*8*3;i++) { + whitetexdata[i]=255; + } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,white_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE,whitetexdata); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D,0); + +#ifdef GLEW_ENABLED + + read_depth_supported=true; + pvr_supported=false; + etc_supported=false; + use_depth24 =true; + s3tc_supported = true; + use_hw_skeleton_xform = false; +// use_texture_instancing=false; +// use_attribute_instancing=true; + use_texture_instancing=false; + use_attribute_instancing=true; +#ifdef OSX_ENABLED + use_rgba_shadowmaps=true; +#else + use_rgba_shadowmaps=false; +#endif + use_half_float=true; + +#else + read_depth_supported=extensions.has("GL_OES_depth_texture"); + use_rgba_shadowmaps=!read_depth_supported; + pvr_supported=extensions.has("GL_IMG_texture_compression_pvrtc"); + etc_supported=extensions.has("GL_OES_compressed_ETC1_RGB8_texture"); + use_depth24 = extensions.has("GL_OES_depth24"); + s3tc_supported = extensions.has("GL_EXT_texture_compression_dxt1") || extensions.has("GL_EXT_texture_compression_s3tc"); + use_half_float = extensions.has("GL_OES_vertex_half_float"); + + + GLint vtf; + glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,&vtf); + use_hw_skeleton_xform=vtf>0 && extensions.has("GL_OES_texture_float"); + //if (extensions.has("GL_QCOM_tiled_rendering")) + // use_hw_skeleton_xform=false; + GLint mva; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS,&mva); + if (vtf==0 && mva>8) { + //tegra 3, mali 400 + use_attribute_instancing=true; + use_texture_instancing=false; + } else if (vtf>0 && extensions.has("GL_OES_texture_float")){ + //use_texture_instancing=true; + use_texture_instancing=false; // i don't get it, uniforms are faster. + use_attribute_instancing=false; + + + + } else { + + use_texture_instancing=false; + use_attribute_instancing=false; + } + + + //etc_supported=false; + use_hw_skeleton_xform=false; + +#endif + + //use_rgba_shadowmaps=true; + + + + + //read_depth_supported=false; + + { + //shadowmaps + OS::VideoMode vm=OS::get_singleton()->get_video_mode(); + + //don't use a shadowbuffer too big in GLES, this should be the maximum + int max_shadow_size = GLOBAL_DEF("rasterizer/max_shadow_buffer_size",1024);//nearest_power_of_2(MIN(vm.width,vm.height))/2; + while(max_shadow_size>=16) { + + ShadowBuffer sb; + bool s = sb.init(max_shadow_size,!use_rgba_shadowmaps); + if (s) + near_shadow_buffers.push_back(sb); + max_shadow_size/=2; + } + + //material_shader + material_shader.set_conditional(MaterialShaderGLES2::USE_DEPTH_SHADOWS,!use_rgba_shadowmaps); + + } + + + shadow_material = material_create(); //empty with nothing + shadow_mat_ptr = material_owner.get(shadow_material); + overdraw_material = create_overdraw_debug_material(); + + npo2_textures_available=true; + //fragment_lighting=false; + _rinfo.texture_mem=0; + current_env=NULL; + current_rt=NULL; + current_vd=NULL; + current_debug=VS::SCENARIO_DEBUG_DISABLED; + + glGenBuffers(1,&gui_quad_buffer); + glBindBuffer(GL_ARRAY_BUFFER,gui_quad_buffer); + glBufferData(GL_ARRAY_BUFFER,128,NULL,GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER,0); //unbind + + + + _update_framebuffer(); + DEBUG_TEST_ERROR("Initializing"); +} + +void RasterizerGLES2::finish() { + + + memdelete_arr(skinned_buffer); +} + +int RasterizerGLES2::get_render_info(VS::RenderInfo p_info) { + + switch(p_info) { + + case VS::INFO_OBJECTS_IN_FRAME: { + + return _rinfo.object_count; + } break; + case VS::INFO_VERTICES_IN_FRAME: { + + return _rinfo.vertex_count; + } break; + case VS::INFO_MATERIAL_CHANGES_IN_FRAME: { + + return _rinfo.mat_change_count; + } break; + case VS::INFO_SHADER_CHANGES_IN_FRAME: { + + return _rinfo.shader_change_count; + } break; + case VS::INFO_DRAW_CALLS_IN_FRAME: { + + return _rinfo.draw_calls; + } break; + case VS::INFO_SURFACE_CHANGES_IN_FRAME: { + + return _rinfo.surface_count; + } break; + case VS::INFO_USAGE_VIDEO_MEM_TOTAL: { + + return 0; + } break; + case VS::INFO_VIDEO_MEM_USED: { + + return get_render_info(VS::INFO_TEXTURE_MEM_USED)+get_render_info(VS::INFO_VERTEX_MEM_USED); + } break; + case VS::INFO_TEXTURE_MEM_USED: { + + return _rinfo.texture_mem; + } break; + case VS::INFO_VERTEX_MEM_USED: { + + return 0; + } break; + } + + return 0; +} + +void RasterizerGLES2::set_extensions(const char *p_strings) { + + Vector<String> strings = String(p_strings).split(" ",false); + for(int i=0;i<strings.size();i++) { + + extensions.insert(strings[i]); +// print_line(strings[i]); + } +} + +bool RasterizerGLES2::needs_to_draw_next_frame() const { + + return draw_next_frame; +} + +bool RasterizerGLES2::has_feature(VS::Features p_feature) const { + + switch( p_feature) { + case VS::FEATURE_SHADERS: return true; + case VS::FEATURE_NEEDS_RELOAD_HOOK: return use_reload_hooks; + default: return false; + } +} + + +void RasterizerGLES2::reload_vram() { + + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glFrontFace(GL_CW); + + + + //do a single initial clear + glClearColor(0,0,0,1); + //glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + + glGenTextures(1, &white_tex); + unsigned char whitetexdata[8*8*3]; + for(int i=0;i<8*8*3;i++) { + whitetexdata[i]=255; + } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,white_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE,whitetexdata); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D,0); + + + + List<RID> textures; + texture_owner.get_owned_list(&textures); + keep_copies=false; + for(List<RID>::Element *E=textures.front();E;E=E->next()) { + + RID tid = E->get(); + Texture *t=texture_owner.get(tid); + ERR_CONTINUE(!t); + t->tex_id=0; + t->data_size=0; + glGenTextures(1, &t->tex_id); + t->active=false; + if (t->render_target) + continue; + texture_allocate(tid,t->width,t->height,t->format,t->flags); + bool had_image=false; + for(int i=0;i<6;i++) { + if (!t->image[i].empty()) { + texture_set_data(tid,t->image[i],VS::CubeMapSide(i)); + had_image=true; + } + } + + if (!had_image && t->reloader) { + Object *rl = ObjectDB::get_instance(t->reloader); + if (rl) + rl->call(t->reloader_func,tid); + } + } + keep_copies=true; + + List<RID> render_targets; + render_target_owner.get_owned_list(&render_targets); + for(List<RID>::Element *E=render_targets.front();E;E=E->next()) { + RenderTarget *rt = render_target_owner.get(E->get()); + + int w = rt->width; + int h = rt->height; + rt->width=0; + rt->height=0; + render_target_set_size(E->get(),w,h); + } + + + List<RID> meshes; + mesh_owner.get_owned_list(&meshes); + for(List<RID>::Element *E=meshes.front();E;E=E->next()) { + + Mesh *mesh = mesh_owner.get(E->get()); + Vector<Surface*> surfaces =mesh->surfaces; + mesh->surfaces.clear(); + for(int i=0;i<surfaces.size();i++) { + mesh_add_surface(E->get(),surfaces[i]->primitive,surfaces[i]->data,surfaces[i]->morph_data,surfaces[i]->alpha_sort); + mesh_surface_set_material(E->get(),i,surfaces[i]->material); + + if (surfaces[i]->array_local != 0) { + memfree(surfaces[i]->array_local); + }; + if (surfaces[i]->index_array_local != 0) { + memfree(surfaces[i]->index_array_local); + }; + + memdelete( surfaces[i] ); + } + + } + + List<RID> skeletons; + skeleton_owner.get_owned_list(&skeletons); + for(List<RID>::Element *E=skeletons.front();E;E=E->next()) { + + Skeleton *sk = skeleton_owner.get(E->get()); + if (!sk->tex_id) + continue; //does not use hw transform, leave alone + + Vector<Skeleton::Bone> bones = sk->bones; + sk->bones.clear(); + sk->tex_id=0; + sk->pixel_size=1.0; + skeleton_resize(E->get(),bones.size()); + sk->bones=bones; + } + + List<RID> multimeshes; + multimesh_owner.get_owned_list(&multimeshes); + for(List<RID>::Element *E=multimeshes.front();E;E=E->next()) { + + MultiMesh *mm = multimesh_owner.get(E->get()); + if (!mm->tex_id) + continue; //does not use hw transform, leave alone + + Vector<MultiMesh::Element> elements = mm->elements; + mm->elements.clear(); + + mm->tw=1; + mm->th=1; + mm->tex_id=0; + mm->last_pass=0; + mm->visible = -1; + + multimesh_set_instance_count(E->get(),elements.size()); + mm->elements=elements; + + } + + + + + if (framebuffer.fbo!=0) { + + framebuffer.fbo=0; + framebuffer.depth=0; + framebuffer.color=0; + + for(int i=0;i<3;i++) { + framebuffer.blur[i].fbo=0; + framebuffer.blur[i].color=0; + } + + framebuffer.luminance.clear(); + + } + + for(int i=0;i<near_shadow_buffers.size();i++) { + near_shadow_buffers[i].init(near_shadow_buffers[i].size,!use_rgba_shadowmaps); + } + + + + canvas_shader.clear_caches(); + material_shader.clear_caches(); + blur_shader.clear_caches(); + copy_shader.clear_caches(); + + + List<RID> shaders; + shader_owner.get_owned_list(&shaders); + for(List<RID>::Element *E=shaders.front();E;E=E->next()) { + + Shader *s = shader_owner.get(E->get()); + s->custom_code_id=0; + s->version=1; + s->valid=false; + shader_set_mode(E->get(),s->mode); + + } + + List<RID> materials; + material_owner.get_owned_list(&materials); + for(List<RID>::Element *E=materials.front();E;E=E->next()) { + + + Material *m = material_owner.get(E->get()); + RID shader = m->shader; + m->shader_version=0; + material_set_shader(E->get(),shader); + + } + + + +} + +void RasterizerGLES2::set_use_framebuffers(bool p_use) { + + use_framebuffers=p_use; +} + +RasterizerGLES2::RasterizerGLES2(bool p_compress_arrays,bool p_keep_ram_copy,bool p_default_fragment_lighting,bool p_use_reload_hooks) { + + keep_copies=p_keep_ram_copy; + use_reload_hooks=p_use_reload_hooks; + pack_arrays=p_compress_arrays; + p_default_fragment_lighting=false; + fragment_lighting=GLOBAL_DEF("rasterizer/use_fragment_lighting",true); + read_depth_supported=true; //todo check for extension + use_shadow_pcf=GLOBAL_DEF("rasterizer/use_shadow_pcf",true); + use_shadow_mapping=true; + use_fast_texture_filter=GLOBAL_DEF("rasterizer/trilinear_mipmap_filter",true); + skel_default.resize(1024*4); + for(int i=0;i<1024/3;i++) { + float * ptr = skel_default.ptr(); + ptr+=i*4*4; + ptr[0]=1.0; + ptr[1]=0.0; + ptr[2]=0.0; + ptr[3]=0.0; + + ptr[4]=0.0; + ptr[5]=1.0; + ptr[6]=0.0; + ptr[7]=0.0; + + ptr[8]=0.0; + ptr[9]=0.0; + ptr[10]=1.0; + ptr[12]=0.0; + + } + + base_framebuffer=0; + frame = 0; + draw_next_frame=false; + use_framebuffers=true; + framebuffer.active=false; +}; + +RasterizerGLES2::~RasterizerGLES2() { + +}; + + +#endif diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h new file mode 100644 index 0000000000..bab560dddb --- /dev/null +++ b/drivers/gles2/rasterizer_gles2.h @@ -0,0 +1,1441 @@ +/*************************************************************************/ +/* rasterizer_gles2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef RASTERIZER_GLES2_H +#define RASTERIZER_GLES2_H + +#include "servers/visual/rasterizer.h" + +#ifdef GLES2_ENABLED + +#include "image.h" +#include "rid.h" +#include "servers/visual_server.h" +#include "list.h" +#include "map.h" +#include "camera_matrix.h" +#include "sort.h" +#include "self_list.h" + +#include "platform_config.h" +#ifndef GLES2_INCLUDE_H +#include <GLES2/gl2.h> +#else +#include GLES2_INCLUDE_H +#endif + +#include "drivers/gles2/shaders/material.glsl.h" +#include "drivers/gles2/shaders/canvas.glsl.h" +#include "drivers/gles2/shaders/blur.glsl.h" +#include "drivers/gles2/shaders/copy.glsl.h" +#include "drivers/gles2/shader_compiler_gles2.h" +#include "servers/visual/particle_system_sw.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class RasterizerGLES2 : public Rasterizer { + + enum { + + MAX_SCENE_LIGHTS=2048, + LIGHT_SPOT_BIT=0x80, + DEFAULT_SKINNED_BUFFER_SIZE = 2048 * 1024, // 10k vertices + MAX_HW_LIGHTS = 1, + }; + + + uint8_t *skinned_buffer; + int skinned_buffer_size; + bool pvr_supported; + bool s3tc_supported; + bool etc_supported; + bool npo2_textures_available; + bool read_depth_supported; + bool use_framebuffers; + bool use_shadow_mapping; + bool use_shadow_esm; + bool use_shadow_pcf; + bool use_hw_skeleton_xform; + bool use_depth24; + bool use_texture_instancing; + bool use_attribute_instancing; + bool use_rgba_shadowmaps; + bool use_half_float; + + + Vector<float> skel_default; + + Image _get_gl_image_and_format(const Image& p_image, Image::Format p_format, uint32_t p_flags,GLenum& r_gl_format,int &r_gl_components,bool &r_has_alpha_cache,bool &r_compressed); + + class RenderTarget; + + struct Texture { + + uint32_t flags; + int width,height; + int alloc_width, alloc_height; + Image::Format format; + + GLenum target; + GLenum gl_format_cache; + int gl_components_cache; + int data_size; //original data size, useful for retrieving back + bool has_alpha; + bool format_has_alpha; + bool compressed; + bool disallow_mipmaps; + int total_data_size; + + ObjectID reloader; + StringName reloader_func; + Image image[6]; + + bool active; + GLuint tex_id; + + RenderTarget *render_target; + + Texture() { + + render_target=NULL; + flags=width=height=0; + tex_id=0; + data_size=0; + format=Image::FORMAT_GRAYSCALE; + gl_components_cache=0; + format_has_alpha=false; + has_alpha=false; + active=false; + disallow_mipmaps=false; + compressed=false; + total_data_size=0; + target=GL_TEXTURE_2D; + + reloader=0; + } + + ~Texture() { + + if (tex_id!=0) { + + glDeleteTextures(1,&tex_id); + } + } + }; + + mutable RID_Owner<Texture> texture_owner; + + struct Shader { + + String vertex_code; + String fragment_code; + int vertex_line; + int fragment_line; + VS::ShaderMode mode; + + uint32_t custom_code_id; + uint32_t version; + + + bool valid; + bool has_alpha; + bool can_zpass; + bool has_texscreen; + bool has_screen_uv; + + Map<StringName,ShaderLanguage::Uniform> uniforms; + + + SelfList<Shader> dirty_list; + + Shader() : dirty_list(this) { + + valid=false; + custom_code_id=0; + has_alpha=false; + version=1; + vertex_line=0; + fragment_line=0; + can_zpass=true; + has_texscreen=false; + has_screen_uv=false; + } + + + }; + + mutable RID_Owner<Shader> shader_owner; + mutable SelfList<Shader>::List _shader_dirty_list; + _FORCE_INLINE_ void _shader_make_dirty(Shader* p_shader); + void _update_shader( Shader* p_shader) const; + + struct Material { + + bool flags[VS::MATERIAL_FLAG_MAX]; + bool hints[VS::MATERIAL_HINT_MAX]; + + VS::MaterialShadeModel shade_model; + VS::MaterialBlendMode blend_mode; + + float line_width; + bool has_alpha; + + mutable uint32_t shader_version; + + RID shader; // shader material + Shader *shader_cache; + + struct UniformData { + + bool istexture; + Variant value; + int index; + }; + + mutable Map<StringName,UniformData> shader_params; + + uint64_t last_pass; + + + Material() { + + for(int i=0;i<VS::MATERIAL_FLAG_MAX;i++) + flags[i]=false; + flags[VS::MATERIAL_FLAG_VISIBLE]=true; + for(int i=0;i<VS::MATERIAL_HINT_MAX;i++) + hints[i]=false; + + line_width=1; + has_alpha=false; + blend_mode=VS::MATERIAL_BLEND_MODE_MIX; + last_pass = 0; + shader_version=0; + shader_cache=NULL; + + } + }; + + _FORCE_INLINE_ void _update_material_shader_params(Material *p_material) const; + mutable RID_Owner<Material> material_owner; + + + + struct Geometry { + + enum Type { + GEOMETRY_INVALID, + GEOMETRY_SURFACE, + GEOMETRY_POLY, + GEOMETRY_PARTICLES, + GEOMETRY_MULTISURFACE, + }; + + Type type; + RID material; + bool has_alpha; + bool material_owned; + + Geometry() { has_alpha=false; material_owned = false; } + virtual ~Geometry() {}; + }; + + struct GeometryOwner { + + virtual ~GeometryOwner() {} + }; + + class Mesh; + + struct Surface : public Geometry { + + struct ArrayData { + + uint32_t ofs,size,datatype,count; + bool normalize; + bool bind; + + ArrayData() { ofs=0; size=0; count=0; datatype=0; normalize=0; bind=false;} + }; + + Mesh *mesh; + + Array data; + Array morph_data; + ArrayData array[VS::ARRAY_MAX]; + // support for vertex array objects + GLuint array_object_id; + // support for vertex buffer object + GLuint vertex_id; // 0 means, unconfigured + GLuint index_id; // 0 means, unconfigured + // no support for the above, array in localmem. + uint8_t *array_local; + uint8_t *index_array_local; + + //bool packed; + + struct MorphTarget { + uint32_t configured_format; + uint8_t *array; + }; + + MorphTarget* morph_targets_local; + int morph_target_count; + AABB aabb; + + int array_len; + int index_array_len; + int max_bone; + + float vertex_scale; + float uv_scale; + float uv2_scale; + + bool alpha_sort; + + VS::PrimitiveType primitive; + + uint32_t format; + uint32_t configured_format; + + int stride; + int local_stride; + uint32_t morph_format; + + bool active; + + Point2 uv_min; + Point2 uv_max; + + Surface() { + + + array_len=0; + local_stride=0; + morph_format=0; + type=GEOMETRY_SURFACE; + primitive=VS::PRIMITIVE_POINTS; + index_array_len=0; + vertex_scale=1.0; + uv_scale=1.0; + uv2_scale=1.0; + + alpha_sort=false; + + format=0; + stride=0; + morph_targets_local=0; + morph_target_count=0; + + array_local = index_array_local = 0; + vertex_id = index_id = 0; + + active=false; + //packed=false; + } + + ~Surface() { + + } + }; + + + struct Mesh { + + bool active; + Vector<Surface*> surfaces; + int morph_target_count; + VS::MorphTargetMode morph_target_mode; + + mutable uint64_t last_pass; + Mesh() { + morph_target_mode=VS::MORPH_MODE_NORMALIZED; + morph_target_count=0; + last_pass=0; + active=false; + } + }; + mutable RID_Owner<Mesh> mesh_owner; + + Error _surface_set_arrays(Surface *p_surface, uint8_t *p_mem,uint8_t *p_index_mem,const Array& p_arrays,bool p_main); + + + struct MultiMesh; + + struct MultiMeshSurface : public Geometry { + + Surface *surface; + MultiMeshSurface() { type=GEOMETRY_MULTISURFACE; } + }; + + struct MultiMesh : public GeometryOwner { + + struct Element { + + float matrix[16]; + uint8_t color[4]; + Element() { + matrix[0]=1; + matrix[1]=0; + matrix[2]=0; + matrix[3]=0; + + matrix[4]=0; + matrix[5]=1; + matrix[6]=0; + matrix[7]=0; + + matrix[8]=0; + matrix[9]=0; + matrix[10]=1; + matrix[11]=0; + + matrix[12]=0; + matrix[13]=0; + matrix[14]=0; + matrix[15]=1; + }; + + + }; + + AABB aabb; + RID mesh; + int visible; + + //IDirect3DVertexBuffer9* instance_buffer; + Vector<Element> elements; + Vector<MultiMeshSurface> cache_surfaces; + mutable uint64_t last_pass; + GLuint tex_id; + int tw; + int th; + + SelfList<MultiMesh> dirty_list; + + MultiMesh() : dirty_list(this) { + + tw=1; + th=1; + tex_id=0; + last_pass=0; + visible = -1; + } + }; + + mutable RID_Owner<MultiMesh> multimesh_owner; + mutable SelfList<MultiMesh>::List _multimesh_dirty_list; + + struct Particles : public Geometry { + + ParticleSystemSW data; // software particle system + + Particles() { + type=GEOMETRY_PARTICLES; + + } + }; + + mutable RID_Owner<Particles> particles_owner; + + struct ParticlesInstance : public GeometryOwner { + + RID particles; + + ParticleSystemProcessSW particles_process; + Transform transform; + + ParticlesInstance() { } + }; + + mutable RID_Owner<ParticlesInstance> particles_instance_owner; + ParticleSystemDrawInfoSW particle_draw_info; + + struct Skeleton { + + struct Bone { + + float mtx[4][4]; //used + + Bone() { + for(int i=0;i<4;i++) { + for(int j=0;j<4;j++) { + + mtx[i][j]=(i==j)?1:0; + + } + } + + } + + _ALWAYS_INLINE_ void transform_add_mul3(const float * p_src, float* r_dst, float p_weight) const { + + r_dst[0]+=((mtx[0][0]*p_src[0] ) + ( mtx[1][0]*p_src[1] ) + ( mtx[2][0]*p_src[2] ) + mtx[3][0])*p_weight; + r_dst[1]+=((mtx[0][1]*p_src[0] ) + ( mtx[1][1]*p_src[1] ) + ( mtx[2][1]*p_src[2] ) + mtx[3][1])*p_weight; + r_dst[2]+=((mtx[0][2]*p_src[0] ) + ( mtx[1][2]*p_src[1] ) + ( mtx[2][2]*p_src[2] ) + mtx[3][2])*p_weight; + } + _ALWAYS_INLINE_ void transform3_add_mul3(const float * p_src, float* r_dst, float p_weight) const { + + r_dst[0]+=((mtx[0][0]*p_src[0] ) + ( mtx[1][0]*p_src[1] ) + ( mtx[2][0]*p_src[2] ) )*p_weight; + r_dst[1]+=((mtx[0][1]*p_src[0] ) + ( mtx[1][1]*p_src[1] ) + ( mtx[2][1]*p_src[2] ) )*p_weight; + r_dst[2]+=((mtx[0][2]*p_src[0] ) + ( mtx[1][2]*p_src[1] ) + ( mtx[2][2]*p_src[2] ) )*p_weight; + } + }; + + GLuint tex_id; + float pixel_size; //for texture + Vector<Bone> bones; + + SelfList<Skeleton> dirty_list; + + Skeleton() : dirty_list(this) { tex_id=0; pixel_size=1.0; } + + }; + + mutable RID_Owner<Skeleton> skeleton_owner; + mutable SelfList<Skeleton>::List _skeleton_dirty_list; + + + template<bool USE_NORMAL, bool USE_TANGENT> + void _skeleton_xform(const uint8_t * p_src_array, int p_src_stride, uint8_t * p_dst_array, int p_dst_stride, int p_elements,const uint8_t *p_src_bones, const uint8_t *p_src_weights, const Skeleton::Bone *p_bone_xforms); + + struct Light { + + VS::LightType type; + float vars[VS::LIGHT_PARAM_MAX]; + Color colors[3]; + bool shadow_enabled; + RID projector; + bool volumetric_enabled; + Color volumetric_color; + VS::LightOmniShadowMode omni_shadow_mode; + VS::LightDirectionalShadowMode directional_shadow_mode; + float directional_shadow_param[3]; + + + Light() { + + vars[VS::LIGHT_PARAM_SPOT_ATTENUATION]=1; + vars[VS::LIGHT_PARAM_SPOT_ANGLE]=45; + vars[VS::LIGHT_PARAM_ATTENUATION]=1.0; + vars[VS::LIGHT_PARAM_ENERGY]=1.0; + vars[VS::LIGHT_PARAM_RADIUS]=1.0; + vars[VS::LIGHT_PARAM_SHADOW_DARKENING]=0.0; + vars[VS::LIGHT_PARAM_SHADOW_Z_OFFSET]=0.2; + vars[VS::LIGHT_PARAM_SHADOW_Z_SLOPE_SCALE]=1.4; + colors[VS::LIGHT_COLOR_AMBIENT]=Color(0,0,0); + colors[VS::LIGHT_COLOR_DIFFUSE]=Color(1,1,1); + colors[VS::LIGHT_COLOR_SPECULAR]=Color(1,1,1); + shadow_enabled=false; + volumetric_enabled=false; + + directional_shadow_param[VS::LIGHT_DIRECTIONAL_SHADOW_PARAM_PSSM_SPLIT_WEIGHT]=0.5; + directional_shadow_param[VS::LIGHT_DIRECTIONAL_SHADOW_PARAM_MAX_DISTANCE]=0; + directional_shadow_param[VS::LIGHT_DIRECTIONAL_SHADOW_PARAM_PSSM_ZOFFSET_SCALE]=2.0; + omni_shadow_mode=VS::LIGHT_OMNI_SHADOW_DEFAULT; + directional_shadow_mode=VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + } + }; + + + struct Environment { + + + VS::EnvironmentBG bg_mode; + Variant bg_param[VS::ENV_BG_PARAM_MAX]; + bool fx_enabled[VS::ENV_FX_MAX]; + Variant fx_param[VS::ENV_FX_PARAM_MAX]; + + Environment() { + + bg_mode=VS::ENV_BG_DEFAULT_COLOR; + bg_param[VS::ENV_BG_PARAM_COLOR]=Color(0,0,0); + bg_param[VS::ENV_BG_PARAM_TEXTURE]=RID(); + bg_param[VS::ENV_BG_PARAM_CUBEMAP]=RID(); + bg_param[VS::ENV_BG_PARAM_ENERGY]=1.0; + bg_param[VS::ENV_BG_PARAM_SCALE]=1.0; + + for(int i=0;i<VS::ENV_FX_MAX;i++) + fx_enabled[i]=false; + + fx_param[VS::ENV_FX_PARAM_GLOW_BLUR_PASSES]=1; + fx_param[VS::ENV_FX_PARAM_GLOW_BLOOM]=0.0; + fx_param[VS::ENV_FX_PARAM_GLOW_BLOOM_TRESHOLD]=0.5; + fx_param[VS::ENV_FX_PARAM_DOF_BLUR_PASSES]=1; + fx_param[VS::ENV_FX_PARAM_DOF_BLUR_BEGIN]=100.0; + fx_param[VS::ENV_FX_PARAM_DOF_BLUR_RANGE]=10.0; + fx_param[VS::ENV_FX_PARAM_HDR_EXPOSURE]=0.4; + fx_param[VS::ENV_FX_PARAM_HDR_SCALAR]=1.0; + fx_param[VS::ENV_FX_PARAM_HDR_GLOW_TRESHOLD]=0.95; + fx_param[VS::ENV_FX_PARAM_HDR_GLOW_SCALE]=0.2; + fx_param[VS::ENV_FX_PARAM_HDR_MIN_LUMINANCE]=0.4; + fx_param[VS::ENV_FX_PARAM_HDR_MAX_LUMINANCE]=8.0; + fx_param[VS::ENV_FX_PARAM_HDR_EXPOSURE_ADJUST_SPEED]=0.5; + fx_param[VS::ENV_FX_PARAM_FOG_BEGIN]=100.0; + fx_param[VS::ENV_FX_PARAM_FOG_ATTENUATION]=1.0; + fx_param[VS::ENV_FX_PARAM_FOG_BEGIN_COLOR]=Color(0,0,0); + fx_param[VS::ENV_FX_PARAM_FOG_END_COLOR]=Color(0,0,0); + fx_param[VS::ENV_FX_PARAM_FOG_BG]=true; + fx_param[VS::ENV_FX_PARAM_BCS_BRIGHTNESS]=1.0; + fx_param[VS::ENV_FX_PARAM_BCS_CONTRAST]=1.0; + fx_param[VS::ENV_FX_PARAM_BCS_SATURATION]=1.0; + fx_param[VS::ENV_FX_PARAM_GAMMA]=1.0; + + } + + }; + + mutable RID_Owner<Environment> environment_owner; + + struct ViewportData { + + //1x1 fbo+texture for storing previous HDR value + GLuint lum_fbo; + GLuint lum_color; + + ViewportData() { lum_fbo=0; lum_color=0; } + }; + + mutable RID_Owner<ViewportData> viewport_data_owner; + + struct RenderTarget { + + Texture *texture_ptr; + RID texture; + GLuint fbo; + GLuint color; + GLuint depth; + int width,height; + uint64_t last_pass; + + }; + + mutable RID_Owner<RenderTarget> render_target_owner; + + + + struct ShadowBuffer; + + struct LightInstance { + + struct SplitInfo { + + CameraMatrix camera; + Transform transform; + float near; + float far; + }; + + RID light; + Light *base; + Transform transform; + CameraMatrix projection; + + Transform custom_transform; + CameraMatrix custom_projection; + + Transform custom_transform2; + CameraMatrix custom_projection2; + + Vector3 light_vector; + Vector3 spot_vector; + float linear_att; + + uint64_t shadow_pass; + uint64_t last_pass; + uint16_t sort_key; + + Vector2 dp; + + CameraMatrix shadow_projection; + CameraMatrix shadow_projection2; + + float shadow_split; + float shadow_split2; + + + ShadowBuffer* near_shadow_buffer; + + void clear_shadow_buffers() { + + clear_near_shadow_buffers(); + } + + void clear_near_shadow_buffers() { + + if (near_shadow_buffer) { + near_shadow_buffer->owner=NULL; + near_shadow_buffer=NULL; + } + } + + LightInstance() { shadow_pass=0; last_pass=0; sort_key=0; near_shadow_buffer=NULL;} + + }; + mutable RID_Owner<Light> light_owner; + mutable RID_Owner<LightInstance> light_instance_owner; + + LightInstance *light_instances[MAX_SCENE_LIGHTS]; + LightInstance *directional_lights[4]; + int light_instance_count; + int directional_light_count; + int last_light_id; + bool current_depth_test; + bool current_depth_mask; + VS::MaterialBlendMode current_blend_mode; + bool use_fast_texture_filter; + + bool fragment_lighting; + RID shadow_material; + Material *shadow_mat_ptr; + + GLuint base_framebuffer; + + GLuint gui_quad_buffer; + + + struct RenderList { + + enum { + MAX_ELEMENTS=4096, + MAX_LIGHTS=4, + SORT_FLAG_SKELETON=1, + SORT_FLAG_INSTANCING=2, + }; + + struct Element { + + + float depth; + const InstanceData *instance; + const Skeleton *skeleton; + const Geometry *geometry; + const Geometry *geometry_cmp; + const Material *material; + const GeometryOwner *owner; + bool *additive_ptr; + bool additive; + bool mirror; + uint16_t light; + uint8_t light_type; + uint8_t sort_flags; + }; + + + Element _elements[MAX_ELEMENTS]; + Element *elements[MAX_ELEMENTS]; + int element_count; + + void clear() { + + element_count=0; + } + + struct SortZ { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + return A->depth > B->depth; + } + }; + + void sort_z() { + + SortArray<Element*,SortZ> sorter; + sorter.sort(elements,element_count); + } + + + struct SortMatGeom { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + // TODO move to a single uint64 (one comparison) + if (A->material->shader_cache == B->material->shader_cache) { + if (A->material == B->material) { + + return A->geometry_cmp < B->geometry_cmp; + } else { + + return (A->material < B->material); + } + } else { + + return B->material->shader_cache < B->material->shader_cache; + } + } + }; + + void sort_mat_geom() { + + SortArray<Element*,SortMatGeom> sorter; + sorter.sort(elements,element_count); + } + + struct SortMatLight { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + if (A->geometry_cmp == B->geometry_cmp) { + + if (A->material == B->material) { + + return A->light<B->light; + } else { + + return (A->material < B->material); + } + } else { + + return (A->geometry_cmp < B->geometry_cmp); + } + } + }; + + void sort_mat_light() { + + SortArray<Element*,SortMatLight> sorter; + sorter.sort(elements,element_count); + } + + struct SortMatLightType { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + if (A->light_type == B->light_type) { + if (A->material->shader_cache == B->material->shader_cache) { + if (A->material == B->material) { + + return (A->geometry_cmp < B->geometry_cmp); + } else { + + return (A->material < B->material); + } + } else { + + return (A->material->shader_cache < B->material->shader_cache); + } + } else { + + return A->light_type < B->light_type; + } + } + }; + + void sort_mat_light_type() { + + SortArray<Element*,SortMatLightType> sorter; + sorter.sort(elements,element_count); + } + + struct SortMatLightTypeFlags { + + _FORCE_INLINE_ bool operator()(const Element* A, const Element* B ) const { + + if (A->sort_flags == B->sort_flags) { + if (A->light_type == B->light_type) { + if (A->material->shader_cache == B->material->shader_cache) { + if (A->material == B->material) { + + return (A->geometry_cmp < B->geometry_cmp); + } else { + + return (A->material < B->material); + } + } else { + + return (A->material->shader_cache < B->material->shader_cache); + } + } else { + + return A->light_type < B->light_type; + } + } else { + + return A->sort_flags < B->sort_flags; //one is null and one is not + } + } + }; + + void sort_mat_light_type_flags() { + + SortArray<Element*,SortMatLightTypeFlags> sorter; + sorter.sort(elements,element_count); + } + _FORCE_INLINE_ Element* add_element() { + + if (element_count>MAX_ELEMENTS) + return NULL; + elements[element_count]=&_elements[element_count]; + return elements[element_count++]; + } + + RenderList() { + + element_count = 0; + for (int i=0;i<MAX_ELEMENTS;i++) + elements[i]=&_elements[i]; // assign elements + } + }; + + + + RenderList opaque_render_list; + RenderList alpha_render_list; + + RID default_material; + + + CameraMatrix camera_projection; + Transform camera_transform; + Transform camera_transform_inverse; + float camera_z_near; + float camera_z_far; + Size2 camera_vp_size; + Set<String> extensions; + bool texscreen_copied; + bool texscreen_used; + + + + Plane camera_plane; + + void _add_geometry( const Geometry* p_geometry, const InstanceData *p_instance, const Geometry *p_geometry_cmp, const GeometryOwner *p_owner); + void _render_list_forward(RenderList *p_render_list,const Transform& p_view_transform,const Transform& p_view_transform_inverse, const CameraMatrix& p_projection,bool p_reverse_cull=false,bool p_fragment_light=false,bool p_alpha_pass=false); + + //void _setup_light(LightInstance* p_instance, int p_idx); + void _setup_light(uint16_t p_light); + + _FORCE_INLINE_ void _setup_shader_params(const Material *p_material); + bool _setup_material(const Geometry *p_geometry,const Material *p_material,bool p_no_const_light); + void _setup_skeleton(const Skeleton *p_skeleton); + + + Error _setup_geometry(const Geometry *p_geometry, const Material* p_material,const Skeleton *p_skeleton, const float *p_morphs); + void _render(const Geometry *p_geometry,const Material *p_material, const Skeleton* p_skeleton, const GeometryOwner *p_owner,const Transform& p_xform); + + + /***********/ + /* SHADOWS */ + /***********/ + + struct ShadowBuffer { + + int size; + GLuint fbo; + GLuint rbo; + GLuint depth; + GLuint rgba; //for older devices +#if 0 + GLuint fbo_blur; + GLuint rbo_blur; + GLuint blur; +#endif + + LightInstance *owner; + bool init(int p_size,bool p_use_depth); + ShadowBuffer() { size=0; depth=0; owner=NULL; } + }; + + Vector<ShadowBuffer> near_shadow_buffers; + Vector<ShadowBuffer> far_shadow_buffers; + + LightInstance *shadow; + int shadow_pass; + + + float shadow_near_far_split_size_ratio; + bool _allocate_shadow_buffers(LightInstance *p_instance, Vector<ShadowBuffer>& p_buffers); + void _debug_draw_shadow(GLuint tex, const Rect2& p_rect); + void _debug_draw_shadows_type(Vector<ShadowBuffer>& p_shadows,Point2& ofs); + void _debug_shadows(); + void _debug_luminances(); + + + /***********/ + /* FBOs */ + /***********/ + + + struct FrameBuffer { + + GLuint fbo; + GLuint color; + GLuint depth; + + + int width,height; + int scale; + bool active; + + int blur_size; + struct Blur { + + GLuint fbo; + GLuint color; + + Blur() { fbo=0; color=0; } + } blur[3]; + + struct Luminance { + + int size; + GLuint fbo; + GLuint color; + + Luminance() { fbo=0; color=0; size=0;} + }; + + Vector<Luminance> luminance; + + GLuint sample_fbo; + GLuint sample_color; + + FrameBuffer() { + blur_size=0; + } + + } framebuffer; + + void _update_framebuffer(); + void _process_glow_and_bloom(); + //void _update_blur_buffer(); + + /*********/ + /* FRAME */ + /*********/ + + struct _Rinfo { + + int texture_mem; + int vertex_count; + int object_count; + int mat_change_count; + int surface_count; + int shader_change_count; + int ci_draw_commands; + int draw_calls; + + } _rinfo; + + + RenderTarget *current_rt; + ViewportData *current_vd; + + + GLuint white_tex; + RID canvas_tex; + float canvas_opacity; + _FORCE_INLINE_ Texture* _bind_canvas_texture(const RID& p_texture); + VS::MaterialBlendMode canvas_blend_mode; + + + int _setup_geometry_vinfo; + + bool pack_arrays; + bool keep_copies; + bool use_reload_hooks; + bool cull_front; + bool lights_use_shadow; + _FORCE_INLINE_ void _set_cull(bool p_front,bool p_reverse_cull=false); + + void _process_glow_bloom(); + void _process_hdr(); + void _draw_tex_bg(); + + Size2 window_size; + VS::ViewportRect viewport; + double last_time; + double time_delta; + uint64_t frame; + uint64_t scene_pass; + bool draw_next_frame; + Environment *current_env; + VS::ScenarioDebugMode current_debug; + RID overdraw_material; + + mutable MaterialShaderGLES2 material_shader; + CanvasShaderGLES2 canvas_shader; + BlurShaderGLES2 blur_shader; + CopyShaderGLES2 copy_shader; + + mutable ShaderCompilerGLES2 shader_precompiler; + + void _draw_primitive(int p_points, const Vector3 *p_vertices, const Vector3 *p_normals, const Color* p_colors, const Vector3 *p_uvs,const Plane *p_tangents=NULL,int p_instanced=1); + _FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color* p_colors, const Vector2 *p_uvs); + _FORCE_INLINE_ void _draw_gui_primitive2(int p_points, const Vector2 *p_vertices, const Color* p_colors, const Vector2 *p_uvs, const Vector2 *p_uvs2); + void _draw_textured_quad(const Rect2& p_rect, const Rect2& p_src_region, const Size2& p_tex_size,bool p_h_flip=false, bool p_v_flip=false ); + void _draw_quad(const Rect2& p_rect); + void _copy_screen_quad(); + void _copy_to_texscreen(); + +public: + + /* TEXTURE API */ + + 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); + virtual void texture_set_data(RID p_texture,const Image& p_image,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT); + virtual Image texture_get_data(RID p_texture,VS::CubeMapSide p_cube_side=VS::CUBEMAP_LEFT) const; + virtual void texture_set_flags(RID p_texture,uint32_t p_flags); + virtual uint32_t texture_get_flags(RID p_texture) const; + virtual Image::Format texture_get_format(RID p_texture) const; + virtual uint32_t texture_get_width(RID p_texture) const; + virtual uint32_t texture_get_height(RID p_texture) const; + virtual bool texture_has_alpha(RID p_texture) const; + virtual void texture_set_size_override(RID p_texture,int p_width, int p_height); + virtual void texture_set_reload_hook(RID p_texture,ObjectID p_owner,const StringName& p_function) const; + + /* SHADER API */ + + virtual RID shader_create(VS::ShaderMode p_mode=VS::SHADER_MATERIAL); + + virtual void shader_set_mode(RID p_shader,VS::ShaderMode p_mode); + virtual VS::ShaderMode shader_get_mode(RID p_shader) const; + + virtual void shader_set_code(RID p_shader, const String& p_vertex, const String& p_fragment,int p_vertex_ofs=0,int p_fragment_ofs=0); + virtual String shader_get_fragment_code(RID p_shader) const; + virtual String shader_get_vertex_code(RID p_shader) const; + + virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const; + + + /* COMMON MATERIAL API */ + + virtual RID material_create(); + + virtual void material_set_shader(RID p_shader_material, RID p_shader); + virtual RID material_get_shader(RID p_shader_material) const; + + virtual void material_set_param(RID p_material, const StringName& p_param, const Variant& p_value); + virtual Variant material_get_param(RID p_material, const StringName& p_param) const; + + virtual void material_set_flag(RID p_material, VS::MaterialFlag p_flag,bool p_enabled); + virtual bool material_get_flag(RID p_material,VS::MaterialFlag p_flag) const; + + virtual void material_set_hint(RID p_material, VS::MaterialHint p_hint,bool p_enabled); + virtual bool material_get_hint(RID p_material,VS::MaterialHint p_hint) const; + + virtual void material_set_shade_model(RID p_material, VS::MaterialShadeModel p_model); + virtual VS::MaterialShadeModel material_get_shade_model(RID p_material) const; + + virtual void material_set_blend_mode(RID p_material,VS::MaterialBlendMode p_mode); + virtual VS::MaterialBlendMode material_get_blend_mode(RID p_material) const; + + virtual void material_set_line_width(RID p_material,float p_line_width); + virtual float material_get_line_width(RID p_material) const; + + + /* MESH API */ + + virtual RID mesh_create(); + + virtual void mesh_add_surface(RID p_mesh,VS::PrimitiveType p_primitive,const Array& p_arrays,const Array& p_blend_shapes=Array(),bool p_alpha_sort=false); + virtual Array mesh_get_surface_arrays(RID p_mesh,int p_surface) const; + virtual Array mesh_get_surface_morph_arrays(RID p_mesh,int p_surface) const; + virtual void mesh_add_custom_surface(RID p_mesh,const Variant& p_dat); + + virtual void mesh_set_morph_target_count(RID p_mesh,int p_amount); + virtual int mesh_get_morph_target_count(RID p_mesh) const; + + virtual void mesh_set_morph_target_mode(RID p_mesh,VS::MorphTargetMode p_mode); + virtual VS::MorphTargetMode mesh_get_morph_target_mode(RID p_mesh) const; + + virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material,bool p_owned=false); + virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const; + + virtual int mesh_surface_get_array_len(RID p_mesh, int p_surface) const; + virtual int mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const; + virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const; + virtual VS::PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const; + + virtual void mesh_remove_surface(RID p_mesh,int p_index); + virtual int mesh_get_surface_count(RID p_mesh) const; + + virtual AABB mesh_get_aabb(RID p_mesh) const; + + /* MULTIMESH API */ + + virtual RID multimesh_create(); + + virtual void multimesh_set_instance_count(RID p_multimesh,int p_count); + virtual int multimesh_get_instance_count(RID p_multimesh) const; + + virtual void multimesh_set_mesh(RID p_multimesh,RID p_mesh); + virtual void multimesh_set_aabb(RID p_multimesh,const AABB& p_aabb); + virtual void multimesh_instance_set_transform(RID p_multimesh,int p_index,const Transform& p_transform); + virtual void multimesh_instance_set_color(RID p_multimesh,int p_index,const Color& p_color); + + virtual RID multimesh_get_mesh(RID p_multimesh) const; + virtual AABB multimesh_get_aabb(RID p_multimesh) const;; + + virtual Transform multimesh_instance_get_transform(RID p_multimesh,int p_index) const; + virtual Color multimesh_instance_get_color(RID p_multimesh,int p_index) const; + + virtual void multimesh_set_visible_instances(RID p_multimesh,int p_visible); + virtual int multimesh_get_visible_instances(RID p_multimesh) const; + + /* PARTICLES API */ + + virtual RID particles_create(); + + virtual void particles_set_amount(RID p_particles, int p_amount); + virtual int particles_get_amount(RID p_particles) const; + + virtual void particles_set_emitting(RID p_particles, bool p_emitting); + virtual bool particles_is_emitting(RID p_particles) const; + + virtual void particles_set_visibility_aabb(RID p_particles, const AABB& p_visibility); + virtual AABB particles_get_visibility_aabb(RID p_particles) const; + + virtual void particles_set_emission_half_extents(RID p_particles, const Vector3& p_half_extents); + virtual Vector3 particles_get_emission_half_extents(RID p_particles) const; + + virtual void particles_set_emission_base_velocity(RID p_particles, const Vector3& p_base_velocity); + virtual Vector3 particles_get_emission_base_velocity(RID p_particles) const; + + virtual void particles_set_emission_points(RID p_particles, const DVector<Vector3>& p_points); + virtual DVector<Vector3> particles_get_emission_points(RID p_particles) const; + + virtual void particles_set_gravity_normal(RID p_particles, const Vector3& p_normal); + virtual Vector3 particles_get_gravity_normal(RID p_particles) const; + + virtual void particles_set_variable(RID p_particles, VS::ParticleVariable p_variable,float p_value); + virtual float particles_get_variable(RID p_particles, VS::ParticleVariable p_variable) const; + + virtual void particles_set_randomness(RID p_particles, VS::ParticleVariable p_variable,float p_randomness); + virtual float particles_get_randomness(RID p_particles, VS::ParticleVariable p_variable) const; + + virtual void particles_set_color_phase_pos(RID p_particles, int p_phase, float p_pos); + virtual float particles_get_color_phase_pos(RID p_particles, int p_phase) const; + + virtual void particles_set_color_phases(RID p_particles, int p_phases); + virtual int particles_get_color_phases(RID p_particles) const; + + virtual void particles_set_color_phase_color(RID p_particles, int p_phase, const Color& p_color); + virtual Color particles_get_color_phase_color(RID p_particles, int p_phase) const; + + virtual void particles_set_attractors(RID p_particles, int p_attractors); + virtual int particles_get_attractors(RID p_particles) const; + + virtual void particles_set_attractor_pos(RID p_particles, int p_attractor, const Vector3& p_pos); + virtual Vector3 particles_get_attractor_pos(RID p_particles,int p_attractor) const; + + virtual void particles_set_attractor_strength(RID p_particles, int p_attractor, float p_force); + virtual float particles_get_attractor_strength(RID p_particles,int p_attractor) const; + + virtual void particles_set_material(RID p_particles, RID p_material,bool p_owned=false); + virtual RID particles_get_material(RID p_particles) const; + + virtual AABB particles_get_aabb(RID p_particles) const; + + virtual void particles_set_height_from_velocity(RID p_particles, bool p_enable); + virtual bool particles_has_height_from_velocity(RID p_particles) const; + + virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable); + virtual bool particles_is_using_local_coordinates(RID p_particles) const; + + /* SKELETON API */ + + virtual RID skeleton_create(); + virtual void skeleton_resize(RID p_skeleton,int p_bones); + virtual int skeleton_get_bone_count(RID p_skeleton) const; + virtual void skeleton_bone_set_transform(RID p_skeleton,int p_bone, const Transform& p_transform); + virtual Transform skeleton_bone_get_transform(RID p_skeleton,int p_bone); + + + /* LIGHT API */ + + virtual RID light_create(VS::LightType p_type); + virtual VS::LightType light_get_type(RID p_light) const; + + virtual void light_set_color(RID p_light,VS::LightColor p_type, const Color& p_color); + virtual Color light_get_color(RID p_light,VS::LightColor p_type) const; + + virtual void light_set_shadow(RID p_light,bool p_enabled); + virtual bool light_has_shadow(RID p_light) const; + + virtual void light_set_volumetric(RID p_light,bool p_enabled); + virtual bool light_is_volumetric(RID p_light) const; + + virtual void light_set_projector(RID p_light,RID p_texture); + virtual RID light_get_projector(RID p_light) const; + + virtual void light_set_var(RID p_light, VS::LightParam p_var, float p_value); + virtual float light_get_var(RID p_light, VS::LightParam p_var) const; + + virtual void light_set_operator(RID p_light,VS::LightOp p_op); + virtual VS::LightOp light_get_operator(RID p_light) const; + + virtual void light_omni_set_shadow_mode(RID p_light,VS::LightOmniShadowMode p_mode); + virtual VS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) const; + + virtual void light_directional_set_shadow_mode(RID p_light,VS::LightDirectionalShadowMode p_mode); + virtual VS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) const; + virtual void light_directional_set_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param, float p_value); + virtual float light_directional_get_shadow_param(RID p_light,VS::LightDirectionalShadowParam p_param) const; + + + + virtual AABB light_get_aabb(RID p_poly) const; + + + virtual RID light_instance_create(RID p_light); + virtual void light_instance_set_transform(RID p_light_instance,const Transform& p_transform); + + virtual ShadowType light_instance_get_shadow_type(RID p_light_instance,bool p_far=false) const; + virtual int light_instance_get_shadow_passes(RID p_light_instance) const; + virtual void light_instance_set_shadow_transform(RID p_light_instance, int p_index, const CameraMatrix& p_camera, const Transform& p_transform, float p_split_near=0,float p_split_far=0); + virtual int light_instance_get_shadow_size(RID p_light_instance, int p_index=0) const; + + + virtual void shadow_clear_near(); + virtual bool shadow_allocate_near(RID p_light); + virtual bool shadow_allocate_far(RID p_light); + + + /* SHADOW */ + + virtual RID particles_instance_create(RID p_particles); + virtual void particles_instance_set_transform(RID p_particles_instance,const Transform& p_transform); + + + /* VIEWPORT */ + + virtual RID viewport_data_create(); + + virtual RID render_target_create(); + virtual void render_target_set_size(RID p_render_target, int p_width, int p_height); + virtual RID render_target_get_texture(RID p_render_target) const; + virtual bool render_target_renedered_in_frame(RID p_render_target); + + + /* RENDER API */ + /* all calls (inside begin/end shadow) are always warranted to be in the following order: */ + + virtual void begin_frame(); + + + virtual void set_viewport(const VS::ViewportRect& p_viewport); + virtual void set_render_target(RID p_render_target); + virtual void clear_viewport(const Color& p_color); + virtual void capture_viewport(Image* r_capture); + + + virtual void begin_scene(RID p_viewport_data,RID p_env,VS::ScenarioDebugMode p_debug); + + virtual void begin_shadow_map( RID p_light_instance, int p_shadow_pass ); + + virtual void set_camera(const Transform& p_world,const CameraMatrix& p_projection); + + virtual void add_light( RID p_light_instance ); ///< all "add_light" calls happen before add_geometry calls + + typedef Map<StringName,Variant> ParamOverrideMap; + + virtual void add_mesh( const RID& p_mesh, const InstanceData *p_data); + virtual void add_multimesh( const RID& p_multimesh, const InstanceData *p_data); + virtual void add_particles( const RID& p_particle_instance, const InstanceData *p_data); + + virtual void end_scene(); + virtual void end_shadow_map(); + + virtual void end_frame(); + + /* CANVAS API */ + + virtual void canvas_begin(); + virtual void canvas_set_opacity(float p_opacity); + virtual void canvas_set_blend_mode(VS::MaterialBlendMode p_mode); + virtual void canvas_begin_rect(const Matrix32& p_transform); + virtual void canvas_set_clip(bool p_clip, const Rect2& p_rect); + virtual void canvas_end_rect(); + virtual void canvas_draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width); + virtual void canvas_draw_rect(const Rect2& p_rect, int p_flags, const Rect2& p_source,RID p_texture,const Color& p_modulate); + virtual void canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margins, bool p_draw_center=true,const Color& p_modulate=Color(1,1,1)); + virtual void canvas_draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, RID p_texture,float p_width); + virtual void canvas_draw_polygon(int p_vertex_count, const int* p_indices, const Vector2* p_vertices, const Vector2* p_uvs, const Color* p_colors,const RID& p_texture,bool p_singlecolor); + virtual void canvas_set_transform(const Matrix32& p_transform); + + /* ENVIRONMENT */ + + + virtual RID environment_create(); + + virtual void environment_set_background(RID p_env,VS::EnvironmentBG p_bg); + virtual VS::EnvironmentBG environment_get_background(RID p_env) const; + + virtual void environment_set_background_param(RID p_env,VS::EnvironmentBGParam p_param, const Variant& p_value); + virtual Variant environment_get_background_param(RID p_env,VS::EnvironmentBGParam p_param) const; + + virtual void environment_set_enable_fx(RID p_env,VS::EnvironmentFx p_effect,bool p_enabled); + virtual bool environment_is_fx_enabled(RID p_env,VS::EnvironmentFx p_effect) const; + + virtual void environment_fx_set_param(RID p_env,VS::EnvironmentFxParam p_param,const Variant& p_value); + virtual Variant environment_fx_get_param(RID p_env,VS::EnvironmentFxParam p_param) const; + + + /*MISC*/ + + virtual bool is_texture(const RID& p_rid) const; + virtual bool is_material(const RID& p_rid) const; + virtual bool is_mesh(const RID& p_rid) const; + virtual bool is_multimesh(const RID& p_rid) const; + virtual bool is_particles(const RID &p_beam) const; + + virtual bool is_light(const RID& p_rid) const; + virtual bool is_light_instance(const RID& p_rid) const; + virtual bool is_particles_instance(const RID& p_rid) const; + virtual bool is_skeleton(const RID& p_rid) const; + virtual bool is_environment(const RID& p_rid) const; + virtual bool is_shader(const RID& p_rid) const; + + virtual void free(const RID& p_rid); + + virtual void init(); + virtual void finish(); + + virtual int get_render_info(VS::RenderInfo p_info); + + void set_base_framebuffer(GLuint p_id); + + virtual void flush_frame(); //not necesary in most cases + void set_extensions(const char *p_strings); + + virtual bool needs_to_draw_next_frame() const; + + void set_use_framebuffers(bool p_use); + void reload_vram(); + + virtual bool has_feature(VS::Features p_feature) const; + + + RasterizerGLES2(bool p_compress_arrays=false,bool p_keep_ram_copy=true,bool p_default_fragment_lighting=true,bool p_use_reload_hooks=false); + virtual ~RasterizerGLES2(); +}; + +#endif +#endif diff --git a/drivers/gles2/rasterizer_instance_gles2.cpp b/drivers/gles2/rasterizer_instance_gles2.cpp new file mode 100644 index 0000000000..eec9431f36 --- /dev/null +++ b/drivers/gles2/rasterizer_instance_gles2.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* rasterizer_instance_gles2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "rasterizer_instance_gles2.h" +#include "rasterizer_gles2.h" + +#ifdef GLES2_ENABLED + +Rasterizer *instance_RasterizerGLES2() { + + return memnew( RasterizerGLES2 ); +} + + +#endif diff --git a/drivers/gles2/rasterizer_instance_gles2.h b/drivers/gles2/rasterizer_instance_gles2.h new file mode 100644 index 0000000000..97dcb7bc73 --- /dev/null +++ b/drivers/gles2/rasterizer_instance_gles2.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* rasterizer_instance_gles2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef RASTERIZER_INSTANCE_GLES2_H +#define RASTERIZER_INSTANCE_GLES2_H + +#include "servers/visual/rasterizer.h" + +#ifdef GLES2_ENABLED + + +Rasterizer *instance_RasterizerGLES2(); + +#endif + +#endif // RASTERIZER_INSTANCE_gles2_H diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp new file mode 100644 index 0000000000..6dc6259fef --- /dev/null +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -0,0 +1,648 @@ +/*************************************************************************/ +/* shader_compiler_gles2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "shader_compiler_gles2.h" +#include "print_string.h" + +#include "stdio.h" + +//#define DEBUG_SHADER_ENABLED + +typedef ShaderLanguage SL; + +struct CodeGLSL2 { + + String code; + +}; + +static String _mktab(int p_level) { + + String tb; + for(int i=0;i<p_level;i++) { + tb+="\t"; + } + + return tb; +} + +static String _typestr(SL::DataType p_type) { + + switch(p_type) { + + case SL::TYPE_VOID: return "void"; + case SL::TYPE_BOOL: return "bool"; + case SL::TYPE_FLOAT: return "float"; + case SL::TYPE_VEC2: return "vec2"; + case SL::TYPE_VEC3: return "vec3"; + case SL::TYPE_VEC4: return "vec4"; + case SL::TYPE_MAT3: return "mat3"; + case SL::TYPE_MAT4: return "mat4"; + case SL::TYPE_TEXTURE: return "sampler2D"; + case SL::TYPE_CUBEMAP: return "samplerCube"; + } + + return ""; +} + +static String _mknum(float p_num) { + return String::num_real(p_num); +} + +static String _opstr(SL::Operator p_op) { + + switch(p_op) { + case SL::OP_ASSIGN: return "="; + case SL::OP_ADD: return "+"; + case SL::OP_SUB: return "-"; + case SL::OP_MUL: return "*"; + case SL::OP_DIV: return "/"; + case SL::OP_ASSIGN_ADD: return "+="; + case SL::OP_ASSIGN_SUB: return "-="; + case SL::OP_ASSIGN_MUL: return "*="; + case SL::OP_ASSIGN_DIV: return "/="; + case SL::OP_NEG: return "-"; + case SL::OP_NOT: return "!"; + case SL::OP_CMP_EQ: return "=="; + case SL::OP_CMP_NEQ: return "!="; + case SL::OP_CMP_LEQ: return "<="; + case SL::OP_CMP_GEQ: return ">="; + case SL::OP_CMP_LESS: return "<"; + case SL::OP_CMP_GREATER: return ">"; + case SL::OP_CMP_OR: return "||"; + case SL::OP_CMP_AND: return "&&"; + default: return ""; + } + + return ""; +} + + +//#ifdef DEBUG_SHADER_ENABLED +#if 1 +#define ENDL "\n" +#else +#define ENDL "" +#endif + + + +String ShaderCompilerGLES2::dump_node_code(SL::Node *p_node,int p_level,bool p_assign_left) { + + String code; + + switch(p_node->type) { + + case SL::Node::TYPE_PROGRAM: { + + SL::ProgramNode *pnode=(SL::ProgramNode*)p_node; + + code+=dump_node_code(pnode->body,p_level); + } break; + case SL::Node::TYPE_FUNCTION: { + + } break; + case SL::Node::TYPE_BLOCK: { + SL::BlockNode *bnode=(SL::BlockNode*)p_node; + + //variables + for(Map<StringName,SL::DataType>::Element *E=bnode->variables.front();E;E=E->next()) { + + code+=_mktab(p_level)+_typestr(E->value())+" "+replace_string(E->key())+";"ENDL; + } + + for(int i=0;i<bnode->statements.size();i++) { + + code+=_mktab(p_level)+dump_node_code(bnode->statements[i],p_level)+";"ENDL; + } + + + } break; + case SL::Node::TYPE_VARIABLE: { + SL::VariableNode *vnode=(SL::VariableNode*)p_node; + if (type==ShaderLanguage::SHADER_MATERIAL_FRAGMENT) { + + if (vnode->name==vname_discard) { + uses_discard=true; + } + if (vnode->name==vname_screen_uv) { + uses_screen_uv=true; + } + if (vnode->name==vname_diffuse_alpha && p_assign_left) { + uses_alpha=true; + } + if (vnode->name==vname_color_interp) { + flags->use_color_interp=true; + } + if (vnode->name==vname_uv_interp) { + flags->use_uv_interp=true; + } + if (vnode->name==vname_uv2_interp) { + flags->use_uv2_interp=true; + } + if (vnode->name==vname_var1_interp) { + flags->use_var1_interp=true; + } + if (vnode->name==vname_var2_interp) { + flags->use_var2_interp=true; + } + if (vnode->name==vname_tangent_interp || vnode->name==vname_binormal_interp) { + flags->use_tangent_interp=true; + } + + } + + code=replace_string(vnode->name); + + } break; + case SL::Node::TYPE_CONSTANT: { + SL::ConstantNode *cnode=(SL::ConstantNode*)p_node; + switch(cnode->datatype) { + + + case SL::TYPE_BOOL: code=cnode->value.operator bool()?"true":"false"; break; + case SL::TYPE_FLOAT: code=_mknum(cnode->value); break; //force zeros, so GLSL doesn't confuse with integer. + case SL::TYPE_VEC2: { Vector2 v = cnode->value; code="vec2("+_mknum(v.x)+", "+_mknum(v.y)+")"; } break; + case SL::TYPE_VEC3: { Vector3 v = cnode->value; code="vec3("+_mknum(v.x)+", "+_mknum(v.y)+", "+_mknum(v.z)+")"; } break; + case SL::TYPE_VEC4: { Plane v = cnode->value; code="vec4("+_mknum(v.normal.x)+", "+_mknum(v.normal.y)+", "+_mknum(v.normal.z)+", "+_mknum(v.d)+")"; } break; + case SL::TYPE_MAT3: { Matrix3 x = cnode->value; code="mat3( vec3("+_mknum(x.get_axis(0).x)+", "+_mknum(x.get_axis(0).y)+", "+_mknum(x.get_axis(0).z)+"), vec3("+_mknum(x.get_axis(1).x)+", "+_mknum(x.get_axis(1).y)+", "+_mknum(x.get_axis(1).z)+"), vec3("+_mknum(x.get_axis(2).x)+", "+_mknum(x.get_axis(2).y)+", "+_mknum(x.get_axis(2).z)+"))"; } break; + case SL::TYPE_MAT4: { Transform x = cnode->value; code="mat4( vec4("+_mknum(x.basis.get_axis(0).x)+", "+_mknum(x.basis.get_axis(0).y)+", "+_mknum(x.basis.get_axis(0).z)+",0.0), vec4("+_mknum(x.basis.get_axis(1).x)+", "+_mknum(x.basis.get_axis(1).y)+", "+_mknum(x.basis.get_axis(1).z)+",0.0), vec4("+_mknum(x.basis.get_axis(2).x)+", "+_mknum(x.basis.get_axis(2).y)+", "+_mknum(x.basis.get_axis(2).z)+",0.0), vec4("+_mknum(x.origin.x)+", "+_mknum(x.origin.y)+", "+_mknum(x.origin.z)+",1.0))"; } break; + default: code="<error: "+Variant::get_type_name(cnode->value.get_type())+" ("+itos(cnode->datatype)+">"; + } + + } break; + case SL::Node::TYPE_OPERATOR: { + SL::OperatorNode *onode=(SL::OperatorNode*)p_node; + + + switch(onode->op) { + + case SL::OP_ASSIGN_MUL: { + + + if (onode->arguments[0]->get_datatype()==SL::TYPE_VEC3 && onode->arguments[1]->get_datatype()==SL::TYPE_MAT4) { + + String mul_l=dump_node_code(onode->arguments[0],p_level,true); + String mul_r=dump_node_code(onode->arguments[1],p_level); + code=mul_l+"=(vec4("+mul_l+",1.0)*("+mul_r+")).xyz"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_MAT4 && onode->arguments[1]->get_datatype()==SL::TYPE_VEC3) { + + String mul_l=dump_node_code(onode->arguments[0],p_level,true); + String mul_r=dump_node_code(onode->arguments[1],p_level); + code=mul_l+"=(("+mul_l+")*vec4("+mul_r+",1.0)).xyz"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_VEC2 && onode->arguments[1]->get_datatype()==SL::TYPE_MAT4) { + + String mul_l=dump_node_code(onode->arguments[0],p_level,true); + String mul_r=dump_node_code(onode->arguments[1],p_level); + code=mul_l+"=(vec4("+mul_l+",1.0,1.0)*("+mul_r+")).xy"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_MAT4 && onode->arguments[1]->get_datatype()==SL::TYPE_VEC2) { + + String mul_l=dump_node_code(onode->arguments[0],p_level,true); + String mul_r=dump_node_code(onode->arguments[1],p_level); + code=mul_l+"=(("+mul_l+")*vec4("+mul_r+",1.0,1.0)).xy"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_VEC2 && onode->arguments[1]->get_datatype()==SL::TYPE_MAT3) { + String mul_l=dump_node_code(onode->arguments[0],p_level,true); + String mul_r=dump_node_code(onode->arguments[1],p_level); + code=mul_l+"=(("+mul_l+")*vec3("+mul_r+",1.0)).xy"; + break; + } + + + }; + case SL::OP_ASSIGN: + case SL::OP_ASSIGN_ADD: + case SL::OP_ASSIGN_SUB: + case SL::OP_ASSIGN_DIV: + code="("+dump_node_code(onode->arguments[0],p_level,true)+_opstr(onode->op)+dump_node_code(onode->arguments[1],p_level)+")"; + break; + + case SL::OP_MUL: + + if (onode->arguments[0]->get_datatype()==SL::TYPE_MAT4 && onode->arguments[1]->get_datatype()==SL::TYPE_VEC3) { + + code="("+dump_node_code(onode->arguments[0],p_level)+"*vec4("+dump_node_code(onode->arguments[1],p_level)+",1.0)).xyz"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_VEC3 && onode->arguments[1]->get_datatype()==SL::TYPE_MAT4) { + + code="(vec4("+dump_node_code(onode->arguments[0],p_level)+",1.0)*"+dump_node_code(onode->arguments[1],p_level)+").xyz"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_MAT4 && onode->arguments[1]->get_datatype()==SL::TYPE_VEC2) { + + code="("+dump_node_code(onode->arguments[0],p_level)+"*vec4("+dump_node_code(onode->arguments[1],p_level)+",1.0,1.0)).xyz"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_VEC2 && onode->arguments[1]->get_datatype()==SL::TYPE_MAT4) { + + code="(vec4("+dump_node_code(onode->arguments[0],p_level)+",1.0,1.0)*"+dump_node_code(onode->arguments[1],p_level)+").xyz"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_MAT3 && onode->arguments[1]->get_datatype()==SL::TYPE_VEC2) { + + code="("+dump_node_code(onode->arguments[0],p_level)+"*vec3("+dump_node_code(onode->arguments[1],p_level)+",1.0)).xy"; + break; + } else if (onode->arguments[0]->get_datatype()==SL::TYPE_VEC2 && onode->arguments[1]->get_datatype()==SL::TYPE_MAT3) { + + code="(vec3("+dump_node_code(onode->arguments[0],p_level)+",1.0)*"+dump_node_code(onode->arguments[1],p_level)+").xy"; + break; + } + + case SL::OP_ADD: + case SL::OP_SUB: + case SL::OP_DIV: + case SL::OP_CMP_EQ: + case SL::OP_CMP_NEQ: + case SL::OP_CMP_LEQ: + case SL::OP_CMP_GEQ: + case SL::OP_CMP_LESS: + case SL::OP_CMP_GREATER: + case SL::OP_CMP_OR: + case SL::OP_CMP_AND: + //handle binary + code="("+dump_node_code(onode->arguments[0],p_level)+_opstr(onode->op)+dump_node_code(onode->arguments[1],p_level)+")"; + break; + case SL::OP_NEG: + case SL::OP_NOT: + //handle unary + code=_opstr(onode->op)+dump_node_code(onode->arguments[0],p_level); + break; + case SL::OP_CONSTRUCT: + case SL::OP_CALL: { + String callfunc=dump_node_code(onode->arguments[0],p_level); + + + code=callfunc+"("; + /*if (callfunc=="mat4") { + //fix constructor for mat4 + for(int i=1;i<onode->arguments.size();i++) { + if (i>1) + code+=", "; + //transform + code+="vec4( "+dump_node_code(onode->arguments[i],p_level)+(i==4?",1.0)":",0.0)"); + + } + } else*/ if (callfunc=="tex") { + + code="texture2D( "+dump_node_code(onode->arguments[1],p_level)+","+dump_node_code(onode->arguments[2],p_level)+")"; + break; + } else if (callfunc=="texcube") { + + code="(textureCube( "+dump_node_code(onode->arguments[1],p_level)+",("+dump_node_code(onode->arguments[2],p_level)+")).xyz"; + break; + } else if (callfunc=="texscreen") { + //create the call to sample the screen, and clamp it + uses_texscreen=true; + code="(texture2D( texscreen_tex, min(("+dump_node_code(onode->arguments[1],p_level)+").xy*texscreen_screen_mult,texscreen_screen_mult))).rgb"; + //code="(texture2D( screen_texture, ("+dump_node_code(onode->arguments[1],p_level)+").xy).rgb"; + break; + } else if (callfunc=="texpos") { + //create the call to sample the screen, and clamp it + uses_texpos=true; + code="get_texpos("+dump_node_code(onode->arguments[1],p_level)+""; +// code="get_texpos(gl_ProjectionMatrixInverse * texture2D( depth_texture, clamp(("+dump_node_code(onode->arguments[1],p_level)+").xy,vec2(0.0),vec2(1.0))*gl_LightSource[5].specular.zw+gl_LightSource[5].specular.xy)"; + //code="(texture2D( screen_texture, ("+dump_node_code(onode->arguments[1],p_level)+").xy).rgb"; + break; + + } else { + + for(int i=1;i<onode->arguments.size();i++) { + if (i>1) + code+=", "; + //transform + code+=dump_node_code(onode->arguments[i],p_level); + + } + } + code+=")"; + break; + } break; + default: {} + } + + } break; + case SL::Node::TYPE_CONTROL_FLOW: { + SL::ControlFlowNode *cfnode=(SL::ControlFlowNode*)p_node; + if (cfnode->flow_op==SL::FLOW_OP_IF) { + + code+="if ("+dump_node_code(cfnode->statements[0],p_level)+") {"ENDL; + code+=dump_node_code(cfnode->statements[1],p_level+1); + if (cfnode->statements.size()==3) { + + code+="} else {"ENDL; + code+=dump_node_code(cfnode->statements[2],p_level+1); + } + + code+="}"ENDL; + + } else if (cfnode->flow_op==SL::FLOW_OP_RETURN) { + + if (cfnode->statements.size()) { + code="return "+dump_node_code(cfnode->statements[0],p_level); + } else { + code="return"; + } + } + + } break; + case SL::Node::TYPE_MEMBER: { + SL::MemberNode *mnode=(SL::MemberNode*)p_node; + String m; + if (mnode->basetype==SL::TYPE_MAT4) { + if (mnode->name=="x") + m="[0]"; + else if (mnode->name=="y") + m="[1]"; + else if (mnode->name=="z") + m="[2]"; + else if (mnode->name=="w") + m="[3]"; + + } else if (mnode->basetype==SL::TYPE_MAT3) { + if (mnode->name=="x") + m="[0]"; + else if (mnode->name=="y") + m="[1]"; + else if (mnode->name=="z") + m="[2]"; + + } else { + m="."+mnode->name; + } + code=dump_node_code(mnode->owner,p_level)+m; + + } break; + } + + return code; + +} + + +void ShaderCompilerGLES2::compile_node(SL::ProgramNode *p_program) { + + // feed the local replace table and global code + global_code=""; + + // uniforms first! + + int ubase=0; + if (uniforms) + ubase=uniforms->size(); + for(Map<StringName,SL::Uniform>::Element *E=p_program->uniforms.front();E;E=E->next()) { + + String uline="uniform "+_typestr(E->get().type)+" _"+E->key().operator String()+";"ENDL; + global_code+=uline; + if (uniforms) { + SL::Uniform u = E->get(); + u.order+=ubase; + uniforms->insert(E->key(),u); + } + } + + for(int i=0;i<p_program->functions.size();i++) { + + + SL::FunctionNode *fnode=p_program->functions[i].function; + + StringName funcname=fnode->name; + String newfuncname=replace_string(funcname); + + String header; + header=_typestr(fnode->return_type)+" "+newfuncname+"("; + for(int i=0;i<fnode->arguments.size();i++) { + + if (i>0) + header+=", "; + header+=_typestr(fnode->arguments[i].type)+" "+replace_string(fnode->arguments[i].name); + } + + header+=") {"ENDL; + String fcode=header; + fcode+=dump_node_code(fnode->body,1); + fcode+="}"ENDL; + global_code+=fcode; + + } + +/* for(Map<StringName,SL::DataType>::Element *E=p_program->preexisting_variables.front();E;E=E->next()) { + + StringName varname=E->key(); + String newvarname=replace_string(varname); + global_code+="uniform "+_typestr(E->get())+" "+newvarname+";"ENDL; + }*/ + + code=dump_node_code(p_program,0); + +#ifdef DEBUG_SHADER_ENABLED + + print_line("GLOBAL CODE:\n\n"); + print_line(global_code); + global_code=global_code.replace("\n",""); + print_line("CODE:\n\n"); + print_line(code); + code=code.replace("\n",""); +#endif +} + +void ShaderCompilerGLES2::create_glsl_120_code(void *p_str,SL::ProgramNode *p_program) { + + ShaderCompilerGLES2 *compiler=(ShaderCompilerGLES2*)p_str; + compiler->compile_node(p_program); +} + + +String ShaderCompilerGLES2::replace_string(const StringName& p_string) { + + Map<StringName,StringName>::Element *E=NULL; + E=replace_table.find(p_string); + if (E) + return E->get(); + + E=mode_replace_table[type].find(p_string); + if (E) + return E->get(); + + + return "_"+p_string.operator String(); +} + +Error ShaderCompilerGLES2::compile(const String& p_code, ShaderLanguage::ShaderType p_type, String& r_code_line, String& r_globals_line, Flags& r_flags, Map<StringName,ShaderLanguage::Uniform> *r_uniforms) { + + uses_texscreen=false; + uses_texpos=false; + uses_alpha=false; + uses_discard=false; + uses_screen_uv=false; + vertex_code_writes_vertex=false; + uniforms=r_uniforms; + flags=&r_flags; + r_flags.use_color_interp=false; + r_flags.use_uv_interp=false; + r_flags.use_uv2_interp=false; + r_flags.use_tangent_interp=false; + r_flags.use_var1_interp=false; + r_flags.use_var2_interp=false; + + String error; + int errline,errcol; + + type=p_type; + Error err = SL::compile(p_code,p_type,create_glsl_120_code,this,&error,&errline,&errcol); + + if (err) { + print_line("***Error precompiling shader: "+error); + print_line("error "+itos(errline)+":"+itos(errcol)); + return err; + } + + r_flags.uses_alpha=uses_alpha; + r_flags.uses_texscreen=uses_texscreen; + r_flags.uses_texpos=uses_texpos; + r_flags.vertex_code_writes_vertex=vertex_code_writes_vertex; + r_flags.uses_discard=uses_discard; + r_flags.uses_screen_uv=uses_screen_uv; + r_code_line=code; + r_globals_line=global_code; + return OK; +} + +ShaderCompilerGLES2::ShaderCompilerGLES2() { + + replace_table["bool"]= "bool"; + replace_table["float" ]= "float"; + replace_table["vec2" ]= "vec2"; + replace_table["vec3" ]= "vec3"; + replace_table["vec4" ]= "vec4"; + replace_table["mat3" ]= "mat3"; + replace_table["mat4" ]= "mat4"; + replace_table["texture" ]= "sampler2D"; + replace_table["cubemap" ]= "samplerCube"; + + replace_table["sin"]= "sin"; + replace_table["cos" ]= "cos"; + replace_table["tan" ]= "tan"; + replace_table["asin" ]= "asin"; + replace_table["acos" ]= "acos"; + replace_table["atan" ]= "atan"; + replace_table["atan2"]= "atan"; + replace_table["sinh" ]= "sinh"; + replace_table["cosh" ]= "cosh"; + replace_table["tanh" ]= "tanh"; + replace_table["pow" ]= "pow"; + replace_table["exp" ]= "exp"; + replace_table["log" ]= "log"; + replace_table["sqrt"]= "sqrt"; + replace_table["abs" ]= "abs"; + replace_table["sign"]= "sign"; + replace_table["floor"]= "floor"; + replace_table["trunc"]= "trunc"; + replace_table["round"]= "round"; + replace_table["ceil" ]= "ceil"; + replace_table["fract"]= "fract"; + replace_table["mod" ]= "mod"; + replace_table["min" ]= "min"; + replace_table["max"]= "max"; + replace_table["clamp"]= "clamp"; + replace_table["mix" ]= "mix"; + replace_table["step" ]= "step"; + replace_table["length"]= "length"; + replace_table["distance"]= "distance"; + replace_table["dot" ]= "dot"; + replace_table["cross" ]="cross"; + replace_table["normalize"]= "normalize"; + replace_table["reflect"]= "reflect"; + replace_table["tex"]= "tex"; + replace_table["texa"]= "texa"; + replace_table["tex2"]= "tex2"; + replace_table["texcube"]= "textureCube"; + replace_table["texscreen"]= "texscreen"; + replace_table["texpos"]= "texpos"; + + mode_replace_table[0]["VERTEX"]="vertex_interp"; + mode_replace_table[0]["NORMAL"]="normal_interp"; + mode_replace_table[0]["TANGENT"]="tangent_interp"; + mode_replace_table[0]["BINORMAL"]="binormal_interp"; + mode_replace_table[0]["UV"]="uv_interp.xy"; + mode_replace_table[0]["UV2"]="uv_interp.zw"; + mode_replace_table[0]["COLOR"]="color_interp"; + //@TODO convert to glsl stuff + mode_replace_table[0]["SPEC_EXP"]="vertex_specular_exp"; + mode_replace_table[0]["WORLD_MATRIX"]="world_transform"; + mode_replace_table[0]["INV_CAMERA_MATRIX"]="camera_inverse_transform"; + mode_replace_table[0]["PROJECTION_MATRIX"]="projection_transform"; + mode_replace_table[0]["POINT_SIZE"]="gl_PointSize"; + mode_replace_table[0]["VAR1"]="var1_interp"; + mode_replace_table[0]["VAR2"]="var2_interp"; + +// mode_replace_table[0]["SCREEN_POS"]="SCREEN_POS"; +// mode_replace_table[0]["SCREEN_SIZE"]="SCREEN_SIZE"; + mode_replace_table[0]["INSTANCE_ID"]="instance_id"; + mode_replace_table[0]["TIME"]="time"; + + mode_replace_table[1]["VERTEX"]="vertex"; + //mode_replace_table[1]["POSITION"]="IN_POSITION"; + mode_replace_table[1]["NORMAL"]="normal"; + mode_replace_table[1]["TANGENT"]="tangent"; + mode_replace_table[1]["BINORMAL"]="binormal"; + mode_replace_table[1]["VAR1"]="var1_interp"; + mode_replace_table[1]["VAR2"]="var2_interp"; + mode_replace_table[1]["UV"]="uv"; + mode_replace_table[1]["UV2"]="uv2"; + mode_replace_table[1]["SCREEN_UV"]="screen_uv"; + mode_replace_table[1]["VAR1"]="var1_interp"; + mode_replace_table[1]["VAR2"]="var2_interp"; + mode_replace_table[1]["COLOR"]="color"; + mode_replace_table[1]["DIFFUSE"]="diffuse.rgb"; + mode_replace_table[1]["DIFFUSE_ALPHA"]="diffuse"; + mode_replace_table[1]["SPECULAR"]="specular"; + mode_replace_table[1]["EMISSION"]="emission"; + mode_replace_table[1]["SPEC_EXP"]="specular_exp"; + mode_replace_table[1]["GLOW"]="glow"; + mode_replace_table[1]["DISCARD"]="discard_"; + mode_replace_table[1]["POINT_COORD"]="gl_PointCoord"; + mode_replace_table[1]["INV_CAMERA_MATRIX"]="camera_inverse_transform"; + + //mode_replace_table[1]["SCREEN_POS"]="SCREEN_POS"; + //mode_replace_table[1]["SCREEN_TEXEL_SIZE"]="SCREEN_TEXEL_SIZE"; + mode_replace_table[1]["TIME"]="time"; + + out_vertex_name="VERTEX"; + + vname_discard="DISCARD"; + vname_screen_uv="SCREEN_UV"; + vname_diffuse_alpha="DIFFUSE_ALPHA"; + vname_color_interp="COLOR"; + vname_uv_interp="UV"; + vname_uv2_interp="UV2"; + vname_tangent_interp="TANGENT"; + vname_binormal_interp="BINORMAL"; + vname_var1_interp="VAR1"; + vname_var2_interp="VAR2"; + +} diff --git a/drivers/gles2/shader_compiler_gles2.h b/drivers/gles2/shader_compiler_gles2.h new file mode 100644 index 0000000000..2f4aa7647e --- /dev/null +++ b/drivers/gles2/shader_compiler_gles2.h @@ -0,0 +1,102 @@ +/*************************************************************************/ +/* shader_compiler_gles2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SHADER_COMPILER_GLES2_H +#define SHADER_COMPILER_GLES2_H + +#include "servers/visual/shader_language.h" +class ShaderCompilerGLES2 { + + class Uniform; +public: + class Flags; +private: + + ShaderLanguage::ProgramNode *program_node; + String dump_node_code(ShaderLanguage::Node *p_node,int p_level,bool p_assign_left=false); + void compile_node(ShaderLanguage::ProgramNode *p_program); + static void create_glsl_120_code(void *p_str,ShaderLanguage::ProgramNode *p_program); + + + bool uses_texscreen; + bool uses_texpos; + bool uses_alpha; + bool uses_discard; + bool uses_screen_uv; + bool vertex_code_writes_vertex; + Flags *flags; + + StringName vname_discard; + StringName vname_screen_uv; + StringName vname_diffuse_alpha; + StringName vname_color_interp; + StringName vname_uv_interp; + StringName vname_uv2_interp; + StringName vname_tangent_interp; + StringName vname_binormal_interp; + StringName vname_var1_interp; + StringName vname_var2_interp; + + Map<StringName,ShaderLanguage::Uniform> *uniforms; + + StringName out_vertex_name; + + String global_code; + String code; + ShaderLanguage::ShaderType type; + + String replace_string(const StringName& p_string); + + Map<StringName,StringName> mode_replace_table[2]; + Map<StringName,StringName> replace_table; + +public: + + struct Flags { + + bool uses_alpha; + bool uses_texscreen; + bool uses_texpos; + bool vertex_code_writes_vertex; + bool uses_discard; + bool uses_screen_uv; + bool use_color_interp; + bool use_uv_interp; + bool use_uv2_interp; + bool use_tangent_interp; + bool use_var1_interp; + bool use_var2_interp; + }; + + Error compile(const String& p_code, ShaderLanguage::ShaderType p_type, String& r_code_line, String& r_globals_line, Flags& r_flags, Map<StringName,ShaderLanguage::Uniform> *r_uniforms=NULL); + + ShaderCompilerGLES2(); + +}; + +#endif // SHADER_COMPILERL_GL_H diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp new file mode 100644 index 0000000000..d665fddd2c --- /dev/null +++ b/drivers/gles2/shader_gles2.cpp @@ -0,0 +1,746 @@ +/*************************************************************************/ +/* shader_gles2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "shader_gles2.h" + +#ifdef GLES2_ENABLED +#include "print_string.h" + +//#define DEBUG_OPENGL + +#ifdef DEBUG_OPENGL + +#define DEBUG_TEST_ERROR(m_section)\ +{\ + uint32_t err = glGetError();\ + if (err) {\ + print_line("OpenGL Error #"+itos(err)+" at: "+m_section);\ + }\ +} +#else + +#define DEBUG_TEST_ERROR(m_section) + +#endif + +ShaderGLES2 *ShaderGLES2::active=NULL; + + + +//#define DEBUG_SHADER + +#ifdef DEBUG_SHADER + +#define DEBUG_PRINT(m_text) print_line(m_text); + +#else + +#define DEBUG_PRINT(m_text) + +#endif + + +void ShaderGLES2::bind_uniforms() { + + if (!uniforms_dirty) { + return; + }; + + // upload default uniforms + const Map<uint32_t,Variant>::Element *E =uniform_defaults.front(); + + while(E) { + int idx=E->key(); + int location=version->uniform_location[idx]; + + if (location<0) { + E=E->next(); + continue; + + } + + const Variant &v=E->value(); + _set_uniform_variant(location, v); + //print_line("uniform "+itos(location)+" value "+v+ " type "+Variant::get_type_name(v.get_type())); + E=E->next(); + }; + + const Map<uint32_t,CameraMatrix>::Element* C = uniform_cameras.front(); + while (C) { + + int location = version->uniform_location[C->key()]; + if (location<0) { + C=C->next(); + continue; + } + + glUniformMatrix4fv(location,1,false,&(C->get().matrix[0][0])); + C = C->next(); + }; + + uniforms_dirty = false; +}; + +GLint ShaderGLES2::get_uniform_location(int p_idx) const { + + ERR_FAIL_COND_V(!version, -1); + + return version->uniform_location[p_idx]; +}; + +bool ShaderGLES2::bind() { + + if (active!=this || !version || new_conditional_version.key!=conditional_version.key) { + conditional_version=new_conditional_version; + version = get_current_version(); + } else { + + return false; + } + + ERR_FAIL_COND_V(!version,false); + + glUseProgram( version->id ); + + DEBUG_TEST_ERROR("Use Program"); + + active=this; + uniforms_dirty = true; +/* + * why on earth is this code here? + for (int i=0;i<texunit_pair_count;i++) { + + glUniform1i(texunit_pairs[i].location, texunit_pairs[i].index); + DEBUG_TEST_ERROR("Uniform 1 i"); + } + +*/ + return true; +} + +void ShaderGLES2::unbind() { + + version=NULL; + glUseProgram(0); + uniforms_dirty = true; + active=NULL; +} + + +static String _fix_error_code_line(const String& p_error,int p_code_start,int p_offset) { + + int last_find_pos=-1; + // NVIDIA + String error=p_error; + while((last_find_pos=p_error.find("(",last_find_pos+1))!=-1) { + + int end_pos=last_find_pos+1; + + while(true) { + + if (p_error[end_pos]>='0' && p_error[end_pos]<='9') { + + end_pos++; + continue; + } else if (p_error[end_pos]==')') { + break; + } else { + + end_pos=-1; + break; + } + + } + + if (end_pos==-1) + continue; + + String numstr = error.substr(last_find_pos+1,(end_pos-last_find_pos)-1); + String begin = error.substr(0,last_find_pos+1); + String end = error.substr(end_pos,error.length()); + int num = numstr.to_int()+p_code_start-p_offset; + error = begin+itos(num)+end; + } + + // ATI + last_find_pos=-1; + while((last_find_pos=p_error.find("ERROR: ",last_find_pos+1))!=-1) { + + last_find_pos+=6; + int end_pos=last_find_pos+1; + + while(true) { + + if (p_error[end_pos]>='0' && p_error[end_pos]<='9') { + + end_pos++; + continue; + } else if (p_error[end_pos]==':') { + break; + } else { + + end_pos=-1; + break; + } + + } + continue; + if (end_pos==-1) + continue; + + String numstr = error.substr(last_find_pos+1,(end_pos-last_find_pos)-1); + print_line("numstr: "+numstr); + String begin = error.substr(0,last_find_pos+1); + String end = error.substr(end_pos,error.length()); + int num = numstr.to_int()+p_code_start-p_offset; + error = begin+itos(num)+end; + } + return error; +} + +ShaderGLES2::Version* ShaderGLES2::get_current_version() { + + Version *_v=version_map.getptr(conditional_version); + + if (_v) { + + if (conditional_version.code_version!=0) { + CustomCode *cc=custom_code_map.getptr(conditional_version.code_version); + ERR_FAIL_COND_V(!cc,_v); + if (cc->version==_v->code_version) + return _v; + } else { + return _v; + } + + } + + + + if (!_v) + version_map[conditional_version]=Version(); + + + Version &v = version_map[conditional_version]; + + if (!_v) { + + v.uniform_location = memnew_arr( GLint, uniform_count ); + + } else { + if (v.ok) { + //bye bye shaders + glDeleteShader( v.vert_id ); + glDeleteShader( v.frag_id ); + glDeleteProgram( v.id ); + v.id=0; + } + + } + + v.ok=false; + /* SETUP CONDITIONALS */ + + Vector<const char*> strings; + //strings.push_back("#version 120\n"); //ATI requieres this before anything + int define_line_ofs=1; + + for(int j=0;j<conditional_count;j++) { + + bool enable=((1<<j)&conditional_version.version); + strings.push_back(enable?conditional_defines[j]:""); + if (enable) + define_line_ofs++; + + if (enable) { + DEBUG_PRINT(conditional_defines[j]); + } + + } + + //keep them around during the function + CharString code_string; + CharString code_globals; + + + //print_line("code version? "+itos(conditional_version.code_version)); + + CustomCode *cc=NULL; + + if ( conditional_version.code_version>0 ) { + //do custom code related stuff + + ERR_FAIL_COND_V( !custom_code_map.has( conditional_version.code_version ), NULL ); + cc=&custom_code_map[conditional_version.code_version]; + v.code_version=cc->version; + define_line_ofs+=2; + } + + + /* CREATE PROGRAM */ + + v.id = glCreateProgram(); + + ERR_FAIL_COND_V(v.id==0, NULL); + + /* VERTEX SHADER */ + + + if (cc) { + for(int i=0;i<cc->custom_defines.size();i++) { + + strings.push_back(cc->custom_defines[i]); + } + } + + int strings_base_size=strings.size(); +#if 0 + if (cc) { + + String _code_string = "#define VERTEX_SHADER_CODE "+cc->vertex+"\n"; + String _code_globals = "#define VERTEX_SHADER_GLOBALS "+cc->vertex_globals+"\n"; + + code_string=_code_string.ascii(); + code_globals=_code_globals.ascii(); + DEBUG_PRINT( code_globals.get_data() ); + DEBUG_PRINT( code_string.get_data() ); + strings.push_back(code_globals); + strings.push_back(code_string); + } +#endif + + + strings.push_back(vertex_code0.get_data()); + if (cc) { + code_globals=cc->vertex_globals.ascii(); + strings.push_back(code_globals.get_data()); + } + strings.push_back(vertex_code1.get_data()); + + if (cc) { + code_string=cc->vertex.ascii(); + strings.push_back(code_string.get_data()); + } + + strings.push_back(vertex_code2.get_data()); +#ifdef DEBUG_SHADER + for(int i=0;i<strings.size();i++) { + + print_line("vert strings "+itos(i)+":"+String(strings[i])); + } +#endif + + + v.vert_id = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(v.vert_id,strings.size(),&strings[0],NULL); + glCompileShader(v.vert_id); + + GLint status; + + glGetShaderiv(v.vert_id,GL_COMPILE_STATUS,&status); + if (status==GL_FALSE) { + // error compiling + GLsizei iloglen; + glGetShaderiv(v.vert_id,GL_INFO_LOG_LENGTH,&iloglen); + + if (iloglen<0) { + + glDeleteShader(v.vert_id); + glDeleteProgram( v.id ); + v.id=0; + + ERR_PRINT("NO LOG, WTF"); + } else { + + if (iloglen==0) { + + iloglen = 4096; //buggy driver (Adreno 220+....) + } + + + char *ilogmem = (char*)Memory::alloc_static(iloglen+1); + ilogmem[iloglen]=0; + glGetShaderInfoLog(v.vert_id, iloglen, &iloglen, ilogmem); + + String err_string=get_shader_name()+": Vertex Program Compilation Failed:\n"; + + err_string+=ilogmem; + err_string=_fix_error_code_line(err_string,vertex_code_start,define_line_ofs); + ERR_PRINT(err_string.ascii().get_data()); + Memory::free_static(ilogmem); + glDeleteShader(v.vert_id); + glDeleteProgram( v.id ); + v.id=0; + + } + + ERR_FAIL_V(NULL); + } + + /* FRAGMENT SHADER */ + + strings.resize(strings_base_size); +#if 0 + if (cc) { + + String _code_string = "#define FRAGMENT_SHADER_CODE "+cc->fragment+"\n"; + String _code_globals = "#define FRAGMENT_SHADER_GLOBALS "+cc->fragment_globals+"\n"; + + code_string=_code_string.ascii(); + code_globals=_code_globals.ascii(); + DEBUG_PRINT( code_globals.get_data() ); + DEBUG_PRINT( code_string.get_data() ); + strings.push_back(code_globals); + strings.push_back(code_string); + } +#endif + + + strings.push_back(fragment_code0.get_data()); + if (cc) { + code_globals=cc->fragment_globals.ascii(); + strings.push_back(code_globals.get_data()); + } + strings.push_back(fragment_code1.get_data()); + + if (cc) { + code_string=cc->fragment.ascii(); + strings.push_back(code_string.get_data()); + } + + strings.push_back(fragment_code2.get_data()); +#ifdef DEBUG_SHADER + for(int i=0;i<strings.size();i++) { + + print_line("frag strings "+itos(i)+":"+String(strings[i])); + } +#endif + + v.frag_id = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(v.frag_id,strings.size(),&strings[0],NULL); + glCompileShader(v.frag_id); + + glGetShaderiv(v.frag_id,GL_COMPILE_STATUS,&status); + if (status==GL_FALSE) { + // error compiling + GLsizei iloglen; + glGetShaderiv(v.frag_id,GL_INFO_LOG_LENGTH,&iloglen); + + if (iloglen<0) { + + glDeleteShader(v.frag_id); + glDeleteShader(v.vert_id); + glDeleteProgram( v.id ); + v.id=0; + ERR_PRINT("NO LOG, WTF"); + } else { + + if (iloglen==0) { + + iloglen = 4096; //buggy driver (Adreno 220+....) + } + + char *ilogmem = (char*)Memory::alloc_static(iloglen+1); + ilogmem[iloglen]=0; + glGetShaderInfoLog(v.frag_id, iloglen, &iloglen, ilogmem); + + String err_string=get_shader_name()+": Fragment Program Compilation Failed:\n"; + + err_string+=ilogmem; + err_string=_fix_error_code_line(err_string,fragment_code_start,define_line_ofs); + ERR_PRINT(err_string.ascii().get_data()); + Memory::free_static(ilogmem); + glDeleteShader(v.frag_id); + glDeleteShader(v.vert_id); + glDeleteProgram( v.id ); + v.id=0; + + } + + ERR_FAIL_V( NULL ); + } + + glAttachShader(v.id,v.frag_id); + glAttachShader(v.id,v.vert_id); + + // bind attributes before linking + for (int i=0;i<attribute_pair_count;i++) { + + glBindAttribLocation(v.id, attribute_pairs[i].index, attribute_pairs[i].name ); + } + + glLinkProgram(v.id); + + glGetProgramiv(v.id, GL_LINK_STATUS, &status); + + if (status==GL_FALSE) { + // error linking + GLsizei iloglen; + glGetProgramiv(v.id,GL_INFO_LOG_LENGTH,&iloglen); + + if (iloglen<0) { + + glDeleteShader(v.frag_id); + glDeleteShader(v.vert_id); + glDeleteProgram( v.id ); + v.id=0; + ERR_FAIL_COND_V(iloglen<=0, NULL); + } + + if (iloglen==0) { + + iloglen = 4096; //buggy driver (Adreno 220+....) + } + + + char *ilogmem = (char*)Memory::alloc_static(iloglen+1); + ilogmem[iloglen]=0; + glGetProgramInfoLog(v.id, iloglen, &iloglen, ilogmem); + + String err_string=get_shader_name()+": Program LINK FAILED:\n"; + + err_string+=ilogmem; + err_string=_fix_error_code_line(err_string,fragment_code_start,define_line_ofs); + ERR_PRINT(err_string.ascii().get_data()); + Memory::free_static(ilogmem); + glDeleteShader(v.frag_id); + glDeleteShader(v.vert_id); + glDeleteProgram( v.id ); + v.id=0; + + ERR_FAIL_V(NULL); + } + + /* UNIFORMS */ + + glUseProgram(v.id); + + + //print_line("uniforms: "); + for(int j=0;j<uniform_count;j++) { + + + v.uniform_location[j]=glGetUniformLocation(v.id,uniform_names[j]); + // print_line("uniform "+String(uniform_names[j])+" location "+itos(v.uniform_location[j])); + } + + // set texture uniforms + for (int i=0;i<texunit_pair_count;i++) { + + GLint loc = glGetUniformLocation(v.id,texunit_pairs[i].name); + if (loc>=0) + glUniform1i(loc,texunit_pairs[i].index); + } + + if ( cc ) { + + v.custom_uniform_locations.resize(cc->custom_uniforms.size()); + for(int i=0;i<cc->custom_uniforms.size();i++) { + + v.custom_uniform_locations[i]=glGetUniformLocation(v.id,String(cc->custom_uniforms[i]).ascii().get_data()); + } + } + + glUseProgram(0); + + + v.ok=true; + + return &v; +} + +GLint ShaderGLES2::get_uniform_location(const String& p_name) const { + + ERR_FAIL_COND_V(!version,-1); + return glGetUniformLocation(version->id,p_name.ascii().get_data()); +} + + +void ShaderGLES2::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) { + + ERR_FAIL_COND(version); + conditional_version.key=0; + new_conditional_version.key=0; + uniform_count=p_uniform_count; + conditional_count=p_conditional_count; + conditional_defines=p_conditional_defines; + uniform_names=p_uniform_names; + vertex_code=p_vertex_code; + fragment_code=p_fragment_code; + texunit_pairs=p_texunit_pairs; + texunit_pair_count=p_texunit_pair_count; + vertex_code_start=p_vertex_code_start; + fragment_code_start=p_fragment_code_start; + attribute_pairs=p_attribute_pairs; + attribute_pair_count=p_attribute_count; + + //split vertex and shader code (thank you, retarded shader compiler programmers from you know what company). + { + String globals_tag="\nVERTEX_SHADER_GLOBALS"; + String code_tag="\nVERTEX_SHADER_CODE"; + String code = vertex_code; + int cpos = code.find(globals_tag); + if (cpos==-1) { + vertex_code0=code.ascii(); + } else { + vertex_code0=code.substr(0,cpos).ascii(); + code = code.substr(cpos+globals_tag.length(),code.length()); + + cpos = code.find(code_tag); + + if (cpos==-1) { + vertex_code1=code.ascii(); + } else { + + vertex_code1=code.substr(0,cpos).ascii(); + vertex_code2=code.substr(cpos+code_tag.length(),code.length()).ascii(); + } + } + } + + { + String globals_tag="\nFRAGMENT_SHADER_GLOBALS"; + String code_tag="\nFRAGMENT_SHADER_CODE"; + String code = fragment_code; + int cpos = code.find(globals_tag); + if (cpos==-1) { + fragment_code0=code.ascii(); + } else { + fragment_code0=code.substr(0,cpos).ascii(); + code = code.substr(cpos+globals_tag.length(),code.length()); + + cpos = code.find(code_tag); + + if (cpos==-1) { + fragment_code1=code.ascii(); + } else { + + fragment_code1=code.substr(0,cpos).ascii(); + fragment_code2=code.substr(cpos+code_tag.length(),code.length()).ascii(); + } + } + } + +} + +void ShaderGLES2::finish() { + + const VersionKey *V=NULL; + while((V=version_map.next(V))) { + + Version &v=version_map[*V]; + glDeleteShader( v.vert_id ); + glDeleteShader( v.frag_id ); + glDeleteProgram( v.id ); + memdelete_arr( v.uniform_location ); + + } + +} + + +void ShaderGLES2::clear_caches() { + + const VersionKey *V=NULL; + while((V=version_map.next(V))) { + + Version &v=version_map[*V]; + glDeleteShader( v.vert_id ); + glDeleteShader( v.frag_id ); + glDeleteProgram( v.id ); + memdelete_arr( v.uniform_location ); + } + + version_map.clear(); + + custom_code_map.clear(); + version=NULL; + last_custom_code=1; + uniforms_dirty = true; + +} + +uint32_t ShaderGLES2::create_custom_shader() { + + custom_code_map[last_custom_code]=CustomCode(); + custom_code_map[last_custom_code].version=1; + return last_custom_code++; +} + +void ShaderGLES2::set_custom_shader_code(uint32_t p_code_id, const String& p_vertex, const String& p_vertex_globals,const String& p_fragment, const String& p_fragment_globals,const Vector<StringName>& p_uniforms,const Vector<const char*> &p_custom_defines) { + + ERR_FAIL_COND(!custom_code_map.has(p_code_id)); + CustomCode *cc=&custom_code_map[p_code_id]; + + cc->vertex=p_vertex; + cc->vertex_globals=p_vertex_globals; + cc->fragment=p_fragment; + cc->fragment_globals=p_fragment_globals; + cc->custom_uniforms=p_uniforms; + cc->custom_defines=p_custom_defines; + cc->version++; +} + +void ShaderGLES2::set_custom_shader(uint32_t p_code_id) { + + new_conditional_version.code_version=p_code_id; +} + +void ShaderGLES2::free_custom_shader(uint32_t p_code_id) { + + if (! custom_code_map.has( p_code_id )) { + print_line("no code id "+itos(p_code_id)); + } else { + print_line("freed code id "+itos(p_code_id)); + + } + + ERR_FAIL_COND(! custom_code_map.has( p_code_id )); + if (conditional_version.code_version==p_code_id) + conditional_version.code_version=0; //bye + + custom_code_map.erase(p_code_id); + +} + + + +ShaderGLES2::ShaderGLES2() { + version=NULL; + last_custom_code=1; + uniforms_dirty = true; +} + + +ShaderGLES2::~ShaderGLES2() { + + finish(); +} + + +#endif diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h new file mode 100644 index 0000000000..bb99862b9e --- /dev/null +++ b/drivers/gles2/shader_gles2.h @@ -0,0 +1,359 @@ +/*************************************************************************/ +/* shader_gles2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SHADER_GLES2_H +#define SHADER_GLES2_H + +#ifdef GLES2_ENABLED + +#include "platform_config.h" +#ifndef GLES2_INCLUDE_H +#include <GLES2/gl2.h> +#else +#include GLES2_INCLUDE_H +#endif + +#include "hash_map.h" +#include "map.h" +#include "variant.h" +#include "camera_matrix.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + +class ShaderGLES2 { +protected: + + struct Enum { + + uint64_t mask; + uint64_t shift; + const char *defines[16]; + }; + + struct EnumValue { + + uint64_t set_mask; + uint64_t clear_mask; + }; + + struct AttributePair { + + const char *name; + int index; + }; + + struct UniformPair { + const char* name; + Variant::Type type_hint; + }; + + struct TexUnitPair { + + const char *name; + int index; + }; + + bool uniforms_dirty; +private: + + //@TODO Optimize to a fixed set of shader pools and use a LRU + int uniform_count; + int texunit_pair_count; + int conditional_count; + int vertex_code_start; + int fragment_code_start; + int attribute_pair_count; + + struct CustomCode { + + String vertex; + String vertex_globals; + String fragment; + String fragment_globals; + uint32_t version; + Vector<StringName> custom_uniforms; + Vector<const char*> custom_defines; + + }; + + + struct Version { + + GLuint id; + GLuint vert_id; + GLuint frag_id; + GLint *uniform_location; + Vector<GLint> custom_uniform_locations; + uint32_t code_version; + bool ok; + Version() { code_version=0; ok=false; uniform_location=NULL; } + }; + + Version *version; + + union VersionKey { + + struct { + uint32_t version; + uint32_t code_version; + }; + uint64_t key; + bool operator==(const VersionKey& p_key) const { return key==p_key.key; } + bool operator<(const VersionKey& p_key) const { return key<p_key.key; } + + }; + + struct VersionKeyHash { + + static _FORCE_INLINE_ uint32_t hash( const VersionKey& p_key) { return HashMapHahserDefault::hash(p_key.key); }; + }; + + //this should use a way more cachefriendly version.. + HashMap<VersionKey,Version,VersionKeyHash> version_map; + + HashMap<uint32_t,CustomCode> custom_code_map; + uint32_t last_custom_code; + + + VersionKey conditional_version; + VersionKey new_conditional_version; + + virtual String get_shader_name() const=0; + + const char** conditional_defines; + const char** uniform_names; + const AttributePair *attribute_pairs; + const TexUnitPair *texunit_pairs; + const char* vertex_code; + const char* fragment_code; + CharString fragment_code0; + CharString fragment_code1; + CharString fragment_code2; + + CharString vertex_code0; + CharString vertex_code1; + CharString vertex_code2; + + Version * get_current_version(); + + static ShaderGLES2 *active; + + _FORCE_INLINE_ void _set_uniform_variant(GLint p_uniform,const Variant& p_value) { + + if (p_uniform<0) + return; // do none + switch(p_value.get_type()) { + + case Variant::BOOL: + case Variant::INT:/* { + + int val=p_value; + glUniform1i( p_uniform, val ); + } break; */ + case Variant::REAL: { + + real_t val=p_value; + glUniform1f( p_uniform, val ); + } break; + case Variant::COLOR: { + + Color val=p_value; + glUniform4f( p_uniform, val.r, val.g,val.b,val.a ); + } break; + case Variant::VECTOR2: { + + Vector2 val=p_value; + glUniform2f( p_uniform, val.x,val.y ); + } break; + case Variant::VECTOR3: { + + Vector3 val=p_value; + glUniform3f( p_uniform, val.x,val.y,val.z ); + } break; + case Variant::PLANE: { + + Plane val=p_value; + glUniform4f( p_uniform, val.normal.x,val.normal.y,val.normal.z,val.d ); + } break; + + case Variant::MATRIX32: { + + Matrix32 tr=p_value; + GLfloat matrix[16]={ /* build a 16x16 matrix */ + tr.elements[0][0], + tr.elements[0][1], + 0, + 0, + tr.elements[1][0], + tr.elements[1][1], + 0, + 0, + 0, + 0, + 1, + 0, + tr.elements[2][0], + tr.elements[2][1], + 0, + 1 + }; + + glUniformMatrix4fv(p_uniform,1,false,matrix); + + } break; + case Variant::MATRIX3: + case Variant::TRANSFORM: { + + Transform tr=p_value; + GLfloat matrix[16]={ /* build a 16x16 matrix */ + tr.basis.elements[0][0], + tr.basis.elements[1][0], + tr.basis.elements[2][0], + 0, + tr.basis.elements[0][1], + tr.basis.elements[1][1], + tr.basis.elements[2][1], + 0, + tr.basis.elements[0][2], + tr.basis.elements[1][2], + tr.basis.elements[2][2], + 0, + tr.origin.x, + tr.origin.y, + tr.origin.z, + 1 + }; + + + glUniformMatrix4fv(p_uniform,1,false,matrix); + } 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_uniform) 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_id,const String& p_vertex, const String& p_vertex_globals,const String& p_fragment, const String& p_fragment_globals,const Vector<StringName>& p_uniforms,const Vector<const char*> &p_custom_defines); + void set_custom_shader(uint32_t p_id); + void free_custom_shader(uint32_t p_id); + + void set_uniform_default(int p_idx, const Variant& p_value) { + + if (p_value.get_type()==Variant::NIL) { + + uniform_defaults.erase(p_idx); + } else { + + uniform_defaults[p_idx]=p_value; + } + uniforms_dirty = true; + }; + + void set_uniform_camera(int p_idx, const CameraMatrix& p_mat) { + + uniform_cameras[p_idx] = p_mat; + uniforms_dirty = true; + }; + + _FORCE_INLINE_ void set_custom_uniform(int p_idx, const Variant& p_value) { + + ERR_FAIL_COND(!version); + ERR_FAIL_INDEX(p_idx,version->custom_uniform_locations.size()); + _set_uniform_variant( version->custom_uniform_locations[p_idx], p_value ); + } + + _FORCE_INLINE_ GLint get_custom_uniform_location(int p_idx) { + + ERR_FAIL_COND_V(!version,-1); + ERR_FAIL_INDEX_V(p_idx,version->custom_uniform_locations.size(),-1); + return version->custom_uniform_locations[p_idx]; + } + + virtual void init()=0; + void finish(); + + virtual ~ShaderGLES2(); + +}; + + +// called a lot, made inline + + +int ShaderGLES2::_get_uniform(int p_which) const { + + ERR_FAIL_INDEX_V( p_which, uniform_count,-1 ); + ERR_FAIL_COND_V( !version, -1 ); + return version->uniform_location[p_which]; +} + +void ShaderGLES2::_set_conditional(int p_which, bool p_value) { + + ERR_FAIL_INDEX(p_which,conditional_count); + if (p_value) + new_conditional_version.version|=(1<<p_which); + else + new_conditional_version.version&=~(1<<p_which); +} + +#endif +#endif diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub new file mode 100644 index 0000000000..c665cf9036 --- /dev/null +++ b/drivers/gles2/shaders/SCsub @@ -0,0 +1,9 @@ +Import('env') + +if env['BUILDERS'].has_key('GLSL120GLES'): + env.GLSL120GLES('material.glsl'); + env.GLSL120GLES('canvas.glsl'); + env.GLSL120GLES('blur.glsl'); + env.GLSL120GLES('copy.glsl'); + + diff --git a/drivers/gles2/shaders/blur.glsl b/drivers/gles2/shaders/blur.glsl new file mode 100644 index 0000000000..b68a55aee7 --- /dev/null +++ b/drivers/gles2/shaders/blur.glsl @@ -0,0 +1,51 @@ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif + +attribute highp vec4 vertex_attrib; // attrib:0 +attribute vec2 uv_in; // attrib:4 + +varying vec2 uv_out; + + + +void main() { + + color_interp = color_attrib; + uv_interp = uv_attrib; + vec4 outvec = vec4(vertex, 1.0); + outvec = extra_matrix * outvec; + outvec = modelview_matrix * outvec; + gl_Position = projection_matrix * outvec; +} + +[fragment] + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif + + // texunit:0 +uniform sampler2D texture; +varying vec2 uv_out; + + +void main() { + + vec4 color = color_interp; + + color *= texture2D( texture, uv_interp ); + + gl_FragColor = color; +} + diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl new file mode 100644 index 0000000000..66bdf15db4 --- /dev/null +++ b/drivers/gles2/shaders/canvas.glsl @@ -0,0 +1,69 @@ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif + +uniform highp mat4 projection_matrix; +uniform highp mat4 modelview_matrix; +uniform highp mat4 extra_matrix; +attribute highp vec3 vertex; // attrib:0 +attribute vec4 color_attrib; // attrib:3 +attribute highp vec2 uv_attrib; // attrib:4 + +varying vec2 uv_interp; +varying vec4 color_interp; + +//uniform bool snap_pixels; + +void main() { + + color_interp = color_attrib; + uv_interp = uv_attrib; + highp vec4 outvec = vec4(vertex, 1.0); + outvec = extra_matrix * outvec; + outvec = modelview_matrix * outvec; +#ifdef USE_PIXEL_SNAP + + outvec.xy=floor(outvec.xy+0.5); +#endif + gl_Position = projection_matrix * outvec; +} + +[fragment] + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif + + // texunit:0 +uniform sampler2D texture; + +varying vec2 uv_interp; +varying vec4 color_interp; + +#ifdef MOMO + +#endif + +void main() { + + vec4 color = color_interp; + + color *= texture2D( texture, uv_interp ); + +#ifdef DEBUG_ENCODED_32 + highp float enc32 = dot( color,highp vec4(1.0 / (256.0 * 256.0 * 256.0),1.0 / (256.0 * 256.0),1.0 / 256.0,1) ); + color = vec4(vec3(enc32),1.0); +#endif + gl_FragColor = color; +} + diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl new file mode 100644 index 0000000000..2ed19cbd65 --- /dev/null +++ b/drivers/gles2/shaders/copy.glsl @@ -0,0 +1,253 @@ +[vertex] + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif + +attribute highp vec4 vertex_attrib; // attrib:0 +#ifdef USE_CUBEMAP +attribute vec3 cube_in; // attrib:4 +#else +attribute vec2 uv_in; // attrib:4 +#endif +attribute vec2 uv2_in; // attrib:5 + +#ifdef USE_CUBEMAP +varying vec3 cube_interp; +#else +varying vec2 uv_interp; +#endif + +varying vec2 uv2_interp; + +void main() { + +#ifdef USE_CUBEMAP + cube_interp = cube_in; +#else + uv_interp = uv_in; +#endif + uv2_interp = uv2_in; + gl_Position = vertex_attrib; +} + +[fragment] + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif + + +#define LUM_RANGE 4.0 + + +#ifdef USE_CUBEMAP +varying vec3 cube_interp; +uniform samplerCube source_cube; +#else +varying vec2 uv_interp; +uniform sampler2D source; +#endif +varying vec2 uv2_interp; + +#ifdef USE_GLOW + +uniform sampler2D glow_source; + +#endif + + +#if defined(USE_HDR) && defined(USE_GLOW_COPY) +uniform highp float hdr_glow_treshold; +uniform highp float hdr_glow_scale; +#endif + +#ifdef USE_HDR + +uniform sampler2D hdr_source; +uniform highp float tonemap_exposure; + +#endif + +#ifdef USE_BCS + +uniform vec3 bcs; + +#endif + +#ifdef USE_GAMMA + +uniform float gamma; + +#endif + +#ifdef USE_GLOW_COPY + +uniform float bloom; +uniform float bloom_treshold; + +#endif + +#if defined(BLUR_V_PASS) || defined(BLUR_H_PASS) || defined(USE_HDR_REDUCE) + +uniform vec2 pixel_size; + +#ifdef USE_HDR_STORE + +uniform highp float hdr_time_delta; +uniform highp float hdr_exp_adj_speed; +uniform highp float min_luminance; +uniform highp float max_luminance; +uniform sampler2D source_vd_lum; + +#endif + +#endif + +#ifdef USE_ENERGY + +uniform highp float energy; + +#endif + + +void main() { + + //vec4 color = color_interp; +#ifdef USE_CUBEMAP + vec4 color = textureCube( source_cube, normalize(cube_interp) ); + +#else + vec4 color = texture2D( source, uv_interp ); +#endif + + //color.rg=uv_interp; + +#ifdef USE_BCS + + color.rgb = mix(vec3(0.0),color.rgb,bcs.x); + color.rgb = mix(vec3(0.5),color.rgb,bcs.y); + color.rgb = mix(vec3(dot(vec3(1.0),color.rgb)*0.33333),color.rgb,bcs.z); + +#endif + +#ifdef BLUR_V_PASS + + color+=texture2D(source,uv_interp+vec2(0.0,pixel_size.y*-3.0)); + color+=texture2D(source,uv_interp+vec2(0.0,pixel_size.y*-2.0)); + color+=texture2D(source,uv_interp+vec2(0.0,pixel_size.y*-1.0)); + color+=texture2D(source,uv_interp+vec2(0.0,pixel_size.y*1.0)); + color+=texture2D(source,uv_interp+vec2(0.0,pixel_size.y*2.0)); + color+=texture2D(source,uv_interp+vec2(0.0,pixel_size.y*3.0)); + + color*=(1.0/7.0); + +#endif + +#ifdef BLUR_H_PASS + + + color+=texture2D(source,uv_interp+vec2(pixel_size.x*-3.0,0.0)); + color+=texture2D(source,uv_interp+vec2(pixel_size.x*-2.0,0.0)); + color+=texture2D(source,uv_interp+vec2(pixel_size.x*-1.0,0.0)); + color+=texture2D(source,uv_interp+vec2(pixel_size.x*1.0,0.0)); + color+=texture2D(source,uv_interp+vec2(pixel_size.x*2.0,0.0)); + color+=texture2D(source,uv_interp+vec2(pixel_size.x*3.0,0.0)); + + color*=(1.0/7.0); + +#endif + +#ifdef USE_HDR + + highp vec4 _mult = vec4(1.0 / (256.0 * 256.0 * 256.0),1.0 / (256.0 * 256.0),1.0 / 256.0,1); + highp float hdr_lum = dot(texture2D( hdr_source, vec2(0.0) ), _mult ); + color.rgb*=LUM_RANGE; + hdr_lum*=LUM_RANGE; //restore to full range + highp float tone_scale = tonemap_exposure / hdr_lum; //only linear supported + color.rgb*=tone_scale; + +#endif + +#ifdef USE_GLOW_COPY + + highp vec3 glowcol = color.rgb*color.a+step(bloom_treshold,dot(vec3(0.3333,0.3333,0.3333),color.rgb))*bloom*color.rgb; + +#ifdef USE_HDR + highp float collum = max(color.r,max(color.g,color.b)); + glowcol+=color.rgb*max(collum-hdr_glow_treshold,0.0)*hdr_glow_scale; +#endif + color.rgb=glowcol; + color.a=0.0; + +#endif + + +#ifdef USE_GLOW + + vec4 glow = texture2D( glow_source, uv2_interp ); + + color.rgb+=glow.rgb; + +#endif + +#ifdef USE_GAMMA + + color.rgb = pow(color.rgb,gamma); + +#endif + + +#ifdef USE_HDR_COPY + + //highp float lum = dot(color.rgb,highp vec3(1.0/3.0,1.0/3.0,1.0/3.0)); + highp float lum = max(color.r,max(color.g,color.b)); + highp vec4 comp = fract(lum * vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0)); + comp -= comp.xxyz * vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0); + color=comp; +#endif + +#ifdef USE_HDR_REDUCE + + highp vec4 _multcv = vec4(1.0 / (256.0 * 256.0 * 256.0),1.0 / (256.0 * 256.0),1.0 / 256.0, 1.0); + highp float lum_accum = dot(color,_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(-pixel_size.x,-pixel_size.y) ),_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(0.0,-pixel_size.y) ),_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(pixel_size.x,-pixel_size.y) ),_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(-pixel_size.x,0.0) ),_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(pixel_size.x,0.0) ),_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(-pixel_size.x,pixel_size.y) ),_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(0.0,pixel_size.y) ),_multcv ); + lum_accum += dot(texture2D( source, uv_interp+vec2(pixel_size.x,pixel_size.y) ),_multcv ); + lum_accum/=9.0; + +#ifdef USE_HDR_STORE + + highp float vd_lum = dot(texture2D( source_vd_lum, vec2(0.0) ), _multcv ); + lum_accum = clamp( vd_lum + (lum_accum-vd_lum)*hdr_time_delta*hdr_exp_adj_speed,min_luminance*(1.0/LUM_RANGE),max_luminance*(1.0/LUM_RANGE)); +#endif + + highp vec4 comp = fract(lum_accum * vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0)); + comp -= comp.xxyz * vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0); + color=comp; +#endif + +#ifdef USE_RGBE + + color.rgb = pow(color.rgb,color.a*255.0-(8.0+128.0)); +#endif + +#ifdef USE_ENERGY + color.rgb*=energy; +#endif + gl_FragColor = color; +} + diff --git a/drivers/gles2/shaders/material.glsl b/drivers/gles2/shaders/material.glsl new file mode 100644 index 0000000000..9c2fbaee6c --- /dev/null +++ b/drivers/gles2/shaders/material.glsl @@ -0,0 +1,875 @@ +[vertex] + + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif +/* +from VisualServer: + +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, +*/ + +/* INPUT ATTRIBS */ + +attribute highp vec4 vertex_attrib; // attrib:0 +attribute vec3 normal_attrib; // attrib:1 +attribute vec4 tangent_attrib; // attrib:2 +attribute vec4 color_attrib; // attrib:3 +attribute vec2 uv_attrib; // attrib:4 +attribute vec2 uv2_attrib; // attrib:5 + +#ifdef USE_SKELETON +attribute vec4 bone_indices; // attrib:6 +attribute vec4 bone_weights; // attrib:7 +uniform highp sampler2D skeleton_matrices; // texunit:6 +uniform highp float skeltex_pixel_size; +#endif + +#ifdef USE_ATTRIBUTE_INSTANCING + +attribute highp vec4 instance_row0; // attrib:8 +attribute highp vec4 instance_row1; // attrib:9 +attribute highp vec4 instance_row2; // attrib:10 +attribute highp vec4 instance_row3; // attrib:11 + +#endif + +#ifdef USE_TEXTURE_INSTANCING + +attribute highp vec3 instance_uv; // attrib:6 +uniform highp sampler2D instance_matrices; // texunit:6 + +#endif + +uniform highp mat4 world_transform; +uniform highp mat4 camera_inverse_transform; +uniform highp mat4 projection_transform; + +#ifdef USE_UNIFORM_INSTANCING +//shittiest form of instancing (but most compatible) +uniform highp mat4 instance_transform; +#endif + +/* Varyings */ + +varying vec3 vertex_interp; +varying vec3 normal_interp; + +#if defined(ENABLE_COLOR_INTERP) +varying vec4 color_interp; +#endif + +#if defined(ENABLE_UV_INTERP) +varying vec2 uv_interp; +#endif + +#if defined(ENABLE_UV2_INTERP) +varying vec2 uv2_interp; +#endif + +#if defined(ENABLE_VAR1_INTERP) +varying vec4 var1_interp; +#endif + +#if defined(ENABLE_VAR2_INTERP) +varying vec4 var2_interp; +#endif + +#if defined(ENABLE_TANGENT_INTERP) +varying vec3 tangent_interp; +varying vec3 binormal_interp; +#endif + +#ifdef USE_FOG + +varying vec4 fog_interp; +uniform highp vec3 fog_params; +uniform vec3 fog_color_begin; +uniform vec3 fog_color_end; + +#endif + +#ifdef USE_VERTEX_LIGHTING + +uniform vec3 light_pos; +uniform vec3 light_direction; +uniform vec3 light_attenuation; +uniform vec3 light_spot_attenuation; +uniform vec3 light_ambient; +uniform vec3 light_diffuse; +uniform vec3 light_specular; + + + +#endif + +varying vec4 diffuse_interp; +varying vec3 specular_interp; +//intended for static branching +//pretty much all meaningful platforms support +//static branching + +uniform float time; +uniform float instance_id; + + +#if !defined(USE_DEPTH_SHADOWS) && defined(USE_SHADOW_PASS) + +varying vec4 position_interp; + +#endif + +#ifdef LIGHT_USE_SHADOW + +uniform highp mat4 shadow_matrix; +varying highp vec4 shadow_coord; +#ifdef LIGHT_USE_PSSM +uniform highp mat4 shadow_matrix2; +varying highp vec4 shadow_coord2; +#endif + + +#endif + +#ifdef USE_SHADOW_PASS + +uniform highp float shadow_z_offset; +uniform highp float shadow_z_slope_scale; + +#endif + +#ifdef USE_DUAL_PARABOLOID +uniform highp vec2 dual_paraboloid; +varying float dp_clip; +#endif + + + +VERTEX_SHADER_GLOBALS + + + + +void main() { +#ifdef USE_UNIFORM_INSTANCING + + highp mat4 modelview = (camera_inverse_transform * (world_transform * instance_transform)); +#else + +#ifdef USE_ATTRIBUTE_INSTANCING + + highp mat4 minst=mat4(instance_row0,instance_row1,instance_row2,instance_row3); + highp mat4 modelview = (camera_inverse_transform * (world_transform * minst)); + +#else + +#ifdef USE_TEXTURE_INSTANCING + + highp vec2 ins_ofs=vec2(instance_uv.z,0.0); + + highp mat4 minst=mat4( + texture2D(instance_matrices,instance_uv.xy), + texture2D(instance_matrices,instance_uv.xy+ins_ofs), + texture2D(instance_matrices,instance_uv.xy+ins_ofs*2.0), + texture2D(instance_matrices,instance_uv.xy+ins_ofs*3.0) + ); + + /*highp mat4 minst=mat4( + vec4(1.0,0.0,0.0,0.0), + vec4(0.0,1.0,0.0,0.0), + vec4(0.0,0.0,1.0,0.0), + vec4(0.0,0.0,0.0,1.0) + );*/ + + highp mat4 modelview = (camera_inverse_transform * (world_transform * minst)); + +#else + highp mat4 modelview = (camera_inverse_transform * world_transform); +#endif + +#endif + +#endif + highp vec4 vertex_in = vertex_attrib; // vec4(vertex_attrib.xyz * data_attrib.x,1.0); + vec3 normal_in = normal_attrib; +#if defined(ENABLE_TANGENT_INTERP) + vec3 tangent_in = tangent_attrib.xyz; +#endif + +#ifdef USE_SKELETON + + { + //skeleton transform + highp mat4 m=mat4(texture2D(skeleton_matrices,vec2((bone_indices.x*3.0+0.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.x*3.0+1.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.x*3.0+2.0)*skeltex_pixel_size,0.0)),vec4(0.0,0.0,0.0,1.0))*bone_weights.x; + m+=mat4(texture2D(skeleton_matrices,vec2((bone_indices.y*3.0+0.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.y*3.0+1.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.y*3.0+2.0)*skeltex_pixel_size,0.0)),vec4(0.0,0.0,0.0,1.0))*bone_weights.y; + m+=mat4(texture2D(skeleton_matrices,vec2((bone_indices.z*3.0+0.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.z*3.0+1.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.z*3.0+2.0)*skeltex_pixel_size,0.0)),vec4(0.0,0.0,0.0,1.0))*bone_weights.z; + m+=mat4(texture2D(skeleton_matrices,vec2((bone_indices.w*3.0+0.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.w*3.0+1.0)*skeltex_pixel_size,0.0)),texture2D(skeleton_matrices,vec2((bone_indices.w*3.0+2.0)*skeltex_pixel_size,0.0)),vec4(0.0,0.0,0.0,1.0))*bone_weights.w; + + vertex_in = vertex_in * m; + normal_in = (vec4(normal_in,0.0) * m).xyz; +#if defined(ENABLE_TANGENT_INTERP) + tangent_in = (vec4(tangent_in,0.0) * m).xyz; +#endif + } + +#endif + + vertex_interp = (modelview * vertex_in).xyz; + normal_interp = normalize((modelview * vec4(normal_in,0.0)).xyz); + +#if defined(ENABLE_COLOR_INTERP) + color_interp = color_attrib; +#endif + +#if defined(ENABLE_TANGENT_INTERP) + tangent_interp=normalize(tangent_in); + binormal_interp = normalize( cross(normal_interp,tangent_interp) * tangent_attrib.a ); +#endif + +#if defined(ENABLE_UV_INTERP) + uv_interp = uv_attrib; +#endif +#if defined(ENABLE_UV2_INTERP) + uv2_interp = uv2_attrib; +#endif + + float vertex_specular_exp = 40.0; //material_specular.a; + + + +VERTEX_SHADER_CODE + + +#ifdef USE_DUAL_PARABOLOID +//for dual paraboloid shadow mapping + highp vec3 vtx = vertex_interp; + vtx.z*=dual_paraboloid.y; //side to affect + vtx.z+=0.01; + dp_clip=vtx.z; + highp float len=length( vtx ); + vtx=normalize(vtx); + vtx.xy/=1.0+vtx.z; + vtx.z = len*dual_paraboloid.x; // it's a reciprocal(len - z_near) / (z_far - z_near); + vtx+=normalize(vtx)*0.025; + vtx.z = vtx.z * 2.0 - 1.0; // fit to clipspace + vertex_interp=vtx; + + //vertex_interp.w = z_clip; + +#endif + +#ifdef USE_SHADOW_PASS + + float z_ofs = shadow_z_offset; + z_ofs += (1.0-abs(normal_interp.z))*shadow_z_slope_scale; + vertex_interp.z-=z_ofs; +#endif + +#ifdef LIGHT_USE_SHADOW + + shadow_coord = shadow_matrix * vec4(vertex_interp,1.0); + shadow_coord.xyz/=shadow_coord.w; + +#ifdef LIGHT_USE_PSSM + shadow_coord.y*=0.5; + shadow_coord.y+=0.5; + shadow_coord2 = shadow_matrix2 * vec4(vertex_interp,1.0); + shadow_coord2.xyz/=shadow_coord2.w; + shadow_coord2.y*=0.5; +#endif +#endif + +#ifdef USE_FOG + + fog_interp.a = pow( clamp( (-vertex_interp.z-fog_params.x)/(fog_params.y-fog_params.x), 0.0, 1.0 ), fog_params.z ); + fog_interp.rgb = mix( fog_color_begin, fog_color_end, fog_interp.a ); +#endif + +#ifndef VERTEX_SHADER_WRITE_POSITION +//vertex shader might write a position + gl_Position = projection_transform * vec4(vertex_interp,1.0); +#endif + + + +#if !defined(USE_DEPTH_SHADOWS) && defined(USE_SHADOW_PASS) + + position_interp=gl_Position; + +#endif + + +#ifdef USE_VERTEX_LIGHTING + + vec3 eye_vec = -normalize(vertex_interp); + +#ifdef LIGHT_TYPE_DIRECTIONAL + + vec3 light_dir = -light_direction; + float attenuation = light_attenuation.r; + + +#endif + +#ifdef LIGHT_TYPE_OMNI + vec3 light_dir = light_pos-vertex_interp; + float radius = light_attenuation.g; + float dist = min(length(light_dir),radius); + light_dir=normalize(light_dir); + float attenuation = pow( max(1.0 - dist/radius, 0.0), light_attenuation.b ) * light_attenuation.r; + +#endif + +#ifdef LIGHT_TYPE_SPOT + + vec3 light_dir = light_pos-vertex_interp; + float radius = light_attenuation.g; + float dist = min(length(light_dir),radius); + light_dir=normalize(light_dir); + float attenuation = pow( max(1.0 - dist/radius, 0.0), light_attenuation.b ) * light_attenuation.r; + vec3 spot_dir = light_direction; + float spot_cutoff=light_spot_attenuation.r; + float scos = max(dot(-light_dir, spot_dir),spot_cutoff); + float rim = (1.0 - scos) / (1.0 - spot_cutoff); + attenuation *= 1.0 - pow( rim, light_spot_attenuation.g); + + +#endif + +#if defined(LIGHT_TYPE_DIRECTIONAL) || defined(LIGHT_TYPE_OMNI) || defined(LIGHT_TYPE_SPOT) + + //process_shade(normal_interp,light_dir,eye_vec,vertex_specular_exp,attenuation,diffuse_interp,specular_interp); + { + float NdotL = max(0.0,dot( normal_interp, light_dir )); + vec3 half_vec = normalize(light_dir + eye_vec); + float eye_light = max(dot(normal_interp, half_vec),0.0); + diffuse_interp.rgb=light_diffuse * NdotL * attenuation;// + light_ambient; + diffuse_interp.a=attenuation; + if (NdotL > 0.0) { + specular_interp=light_specular * pow( eye_light, vertex_specular_exp ) * attenuation; + } else { + specular_interp=vec3(0.0); + } + } +#else + +#ifdef SHADELESS + + diffuse_interp=vec4(vec3(1.0),0.0); + specular_interp=vec3(0.0); +# else + + diffuse_interp=vec4(0.0); + specular_interp=vec3(0.0); +# endif + +#endif + + + + +#endif + + +} + + +[fragment] + + +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else + +precision mediump float; +precision mediump int; + +#endif + +/* Varyings */ + +#if defined(ENABLE_COLOR_INTERP) +varying vec4 color_interp; +#endif + +#if defined(ENABLE_UV_INTERP) +varying vec2 uv_interp; +#endif + +#if defined(ENABLE_UV2_INTERP) +varying vec2 uv2_interp; +#endif + +#if defined(ENABLE_TANGENT_INTERP) +varying vec3 tangent_interp; +varying vec3 binormal_interp; +#endif + +#if defined(ENABLE_VAR1_INTERP) +varying vec4 var1_interp; +#endif + +#if defined(ENABLE_VAR2_INTERP) +varying vec4 var2_interp; +#endif + +#ifdef LIGHT_USE_PSSM +uniform float light_pssm_split; +#endif + +varying vec3 vertex_interp; +varying vec3 normal_interp; + +#ifdef USE_FOG + +varying vec4 fog_interp; + +#endif + +/* Material Uniforms */ + +#ifdef USE_VERTEX_LIGHTING + +varying vec4 diffuse_interp; +varying vec3 specular_interp; + +#endif + +#if !defined(USE_DEPTH_SHADOWS) && defined(USE_SHADOW_PASS) + +varying vec4 position_interp; + +#endif + + + +uniform vec3 light_pos; +uniform vec3 light_direction; +uniform vec3 light_attenuation; +uniform vec3 light_spot_attenuation; +uniform vec3 light_ambient; +uniform vec3 light_diffuse; +uniform vec3 light_specular; + + +#ifdef USE_FRAGMENT_LIGHTING + + + +vec3 process_shade(in vec3 normal, in vec3 light_dir, in vec3 eye_vec, in vec3 diffuse, in vec3 specular, in float specular_exp, in float attenuation) { + + float NdotL = max(0.0,dot( normal, light_dir )); + vec3 half_vec = normalize(light_dir + eye_vec); + float eye_light = max(dot(normal, half_vec),0.0); + + vec3 ret = light_ambient *diffuse + light_diffuse * diffuse * NdotL * attenuation; + if (NdotL > 0.0) { + ret+=light_specular * specular * pow( eye_light, specular_exp ) * attenuation; + } + return ret; +} + +# ifdef USE_DEPTH_SHADOWS +# else +# endif + +#endif + +uniform float const_light_mult; +uniform float time; + + + +FRAGMENT_SHADER_GLOBALS + + + +#ifdef LIGHT_USE_SHADOW + +varying highp vec4 shadow_coord; +#ifdef LIGHT_USE_PSSM +varying highp vec4 shadow_coord2; +#endif +uniform highp sampler2D shadow_texture; +uniform highp vec2 shadow_texel_size; + +uniform float shadow_darkening; + +#ifdef USE_DEPTH_SHADOWS + +#define SHADOW_DEPTH(m_tex,m_uv) (texture2D((m_tex),(m_uv)).z) + +#else + +//#define SHADOW_DEPTH(m_tex,m_uv) dot(texture2D((m_tex),(m_uv)),highp vec4(1.0 / (256.0 * 256.0 * 256.0),1.0 / (256.0 * 256.0),1.0 / 256.0,1) ) +#define SHADOW_DEPTH(m_tex,m_uv) dot(texture2D((m_tex),(m_uv)),vec4(1.0 / (256.0 * 256.0 * 256.0),1.0 / (256.0 * 256.0),1.0 / 256.0,1) ) + +#endif + +#ifdef USE_SHADOW_PCF + + +float SAMPLE_SHADOW_TEX( highp vec2 coord, highp float refdepth) { + + float avg=(SHADOW_DEPTH(shadow_texture,coord) < refdepth ? 0.0 : 1.0); + avg+=(SHADOW_DEPTH(shadow_texture,coord+vec2(shadow_texel_size.x,0.0)) < refdepth ? 0.0 : 1.0); + avg+=(SHADOW_DEPTH(shadow_texture,coord+vec2(-shadow_texel_size.x,0.0)) < refdepth ? 0.0 : 1.0); + avg+=(SHADOW_DEPTH(shadow_texture,coord+vec2(0.0,shadow_texel_size.y)) < refdepth ? 0.0 : 1.0); + avg+=(SHADOW_DEPTH(shadow_texture,coord+vec2(0.0,-shadow_texel_size.y)) < refdepth ? 0.0 : 1.0); + return avg*0.2; +} + + +/* + 16x averaging +float SAMPLE_SHADOW_TEX( highp vec2 coord, highp float refdepth) { + + vec2 offset = vec2( + lessThan(vec2(0.25),fract(gl_FragCoord.xy * 0.5)) + ); + offset.y += offset.x; // y ^= x in floating point + + if (offset.y > 1.1) + offset.y = 0.0; + float avg = step( refdepth, SHADOW_DEPTH(shadow_texture, coord+ (offset + vec2(-1.5, 0.5))*shadow_texel_size) ); + avg+=step(refdepth, SHADOW_DEPTH(shadow_texture, coord+ (offset + vec2(0.5, 0.5))*shadow_texel_size) ); + avg+=step(refdepth, SHADOW_DEPTH(shadow_texture, coord+ (offset + vec2(-1.5, -1.5))*shadow_texel_size) ); + avg+=step(refdepth, SHADOW_DEPTH(shadow_texture, coord+ (offset + vec2(0.5, -1.5))*shadow_texel_size) ); + return avg * 0.25; +} +*/ + +/* +float SAMPLE_SHADOW_TEX( highp vec2 coord, highp float refdepth) { + + vec2 offset = vec2( + lessThan(vec2(0.25),fract(gl_FragCoord.xy * 0.5)) + ); + offset.y += offset.x; // y ^= x in floating point + + if (offset.y > 1.1) + offset.y = 0.0; + return step( refdepth, SHADOW_DEPTH(shadow_texture, coord+ offset*shadow_texel_size) ); + +} + +*/ +/* simple pcf4 */ +//#define SAMPLE_SHADOW_TEX(m_coord,m_depth) ((step(m_depth,SHADOW_DEPTH(shadow_texture,m_coord))+step(m_depth,SHADOW_DEPTH(shadow_texture,m_coord+vec2(0.0,shadow_texel_size.y)))+step(m_depth,SHADOW_DEPTH(shadow_texture,m_coord+vec2(shadow_texel_size.x,0.0)))+step(m_depth,SHADOW_DEPTH(shadow_texture,m_coord+shadow_texel_size)))/4.0) + +#endif + +#ifdef USE_SHADOW_ESM + + +float SAMPLE_SHADOW_TEX(vec2 p_uv,float p_depth) { + + vec2 unnormalized = p_uv/shadow_texel_size; + vec2 fractional = fract(unnormalized); + unnormalized = floor(unnormalized); + + vec4 exponent; + exponent.x = SHADOW_DEPTH(shadow_texture, (unnormalized + vec2( -0.5, 0.5 )) * shadow_texel_size ); + exponent.y = SHADOW_DEPTH(shadow_texture, (unnormalized + vec2( 0.5, 0.5 )) * shadow_texel_size ); + exponent.z = SHADOW_DEPTH(shadow_texture, (unnormalized + vec2( 0.5, -0.5 )) * shadow_texel_size ); + exponent.w = SHADOW_DEPTH(shadow_texture, (unnormalized + vec2( -0.5, -0.5 )) * shadow_texel_size ); + + highp float occluder = (exponent.w + (exponent.x - exponent.w) * fractional.y); + occluder = occluder + ((exponent.z + (exponent.y - exponent.z) * fractional.y) - occluder)*fractional.x; + return clamp(exp(28.0 * ( occluder - p_depth )),0.0,1.0); + +} + + +#endif + +#if !defined(USE_SHADOW_PCF) && !defined(USE_SHADOW_ESM) + +#define SAMPLE_SHADOW_TEX(m_coord,m_depth) (SHADOW_DEPTH(shadow_texture,m_coord) < m_depth ? 0.0 : 1.0) + +#endif + + +#endif + +#ifdef USE_DUAL_PARABOLOID + +varying float dp_clip; + +#endif + +uniform highp mat4 camera_inverse_transform; + +#if defined(ENABLE_TEXSCREEN) + +uniform vec2 texscreen_screen_mult; +uniform sampler2D texscreen_tex; + +#endif + +#if defined(ENABLE_SCREEN_UV) + +uniform vec2 screen_uv_mult; + +#endif + +void main() { + +#ifdef USE_DUAL_PARABOLOID + if (dp_clip<0.0) + discard; +#endif + + //lay out everything, whathever is unused is optimized away anyway + vec3 vertex = vertex_interp; + vec4 diffuse = vec4(0.9,0.9,0.9,1.0); + vec3 specular = vec3(0.0,0.0,0.0); + vec3 emission = vec3(0.0,0.0,0.0); + float specular_exp=1.0; + float glow=0.0; + float shade_param=0.0; + float side=float(gl_FrontFacing)*2.0-1.0; +#if defined(ENABLE_TANGENT_INTERP) + vec3 binormal = normalize(binormal_interp)*side; + vec3 tangent = normalize(tangent_interp)*side; +#endif +// vec3 normal = abs(normalize(normal_interp))*side; + vec3 normal = normalize(normal_interp)*side; +#if defined(ENABLE_SCREEN_UV) + vec2 screen_uv = gl_FragCoord.xy*screen_uv_mult; +#endif + +#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 + + + + +#ifdef FRAGMENT_SHADER_CODE_USE_DISCARD + float discard_=0.0; +#endif + + +FRAGMENT_SHADER_CODE + + +#ifdef FRAGMENT_SHADER_CODE_USE_DISCARD + if (discard_>0.0) { + //easy to eliminate dead code + discard; + } +#endif + + + float shadow_attenuation = 1.0; + + +#ifdef LIGHT_USE_SHADOW +#ifdef LIGHT_TYPE_DIRECTIONAL + +#ifdef LIGHT_USE_PSSM + + +// if (vertex_interp.z > light_pssm_split) { +#if 0 + highp vec3 splane = vec3(0.0,0.0,0.0); + + if (gl_FragCoord.w > light_pssm_split) { + + splane = shadow_coord.xyz; + splane.y+=1.0; + } else { + splane = shadow_coord2.xyz; + } + splane.y*=0.5; + shadow_attenuation=SAMPLE_SHADOW_TEX(splane.xy,splane.z); + +#else +/* + float sa_a = SAMPLE_SHADOW_TEX(shadow_coord.xy,shadow_coord.z); + float sa_b = SAMPLE_SHADOW_TEX(shadow_coord2.xy,shadow_coord2.z); + if (gl_FragCoord.w > light_pssm_split) { + shadow_attenuation=sa_a; + } else { + shadow_attenuation=sa_b; + } +*/ + + if (gl_FragCoord.w > light_pssm_split) { + shadow_attenuation=SAMPLE_SHADOW_TEX(shadow_coord.xy,shadow_coord.z); + } else { + shadow_attenuation=SAMPLE_SHADOW_TEX(shadow_coord2.xy,shadow_coord2.z); + } + + +#endif + +#else + + shadow_attenuation=SAMPLE_SHADOW_TEX(shadow_coord.xy,shadow_coord.z); +#endif +#endif + +#ifdef LIGHT_TYPE_OMNI + + vec3 splane=shadow_coord.xyz;///shadow_coord.w; + float shadow_len=length(splane); + splane=normalize(splane); + float vofs=0.0; + + if (splane.z>=0.0) { + + splane.z+=1.0; + } else { + + splane.z=1.0 - splane.z; + vofs=0.5; + } + splane.xy/=splane.z; + splane.xy=splane.xy * 0.5 + 0.5; + float lradius = light_attenuation.g; + splane.z = shadow_len / lradius; + splane.y=clamp(splane.y,0.0,1.0)*0.5+vofs; + + shadow_attenuation=SAMPLE_SHADOW_TEX(splane.xy,splane.z); +#endif + +#ifdef LIGHT_TYPE_SPOT + + shadow_attenuation=SAMPLE_SHADOW_TEX(shadow_coord.xy,shadow_coord.z); +#endif + + shadow_attenuation=mix(shadow_attenuation,1.0,shadow_darkening); +#endif + + +#ifdef USE_FRAGMENT_LIGHTING + + vec3 eye_vec = -normalize(vertex); + +#ifdef LIGHT_TYPE_DIRECTIONAL + + vec3 light_dir = -light_direction; + float light_attenuation = light_attenuation.r; + + diffuse.rgb=process_shade(normal,light_dir,eye_vec,diffuse.rgb,specular,specular_exp,shadow_attenuation)*light_attenuation; + +#endif + +#ifdef LIGHT_TYPE_OMNI + + vec3 light_dir = light_pos-vertex; + float radius = light_attenuation.g; + float dist = min(length(light_dir),radius); + light_dir=normalize(light_dir); + float attenuation = pow( max(1.0 - dist/radius, 0.0), light_attenuation.b ) * light_attenuation.r; + + diffuse.rgb=process_shade(normal,light_dir,eye_vec,diffuse.rgb,specular,specular_exp,shadow_attenuation)*attenuation; +#endif + + +#ifdef LIGHT_TYPE_SPOT + + vec3 light_dir = light_pos-vertex; + float radius = light_attenuation.g; + float dist = min(length(light_dir),radius); + light_dir=normalize(light_dir); + float attenuation = pow( max(1.0 - dist/radius, 0.0), light_attenuation.b ) * light_attenuation.r; + vec3 spot_dir = light_direction; + float spot_cutoff=light_spot_attenuation.r; + float scos = max(dot(-light_dir, spot_dir),spot_cutoff); + float rim = (1.0 - scos) / (1.0 - spot_cutoff); + attenuation *= 1.0 - pow( rim, light_spot_attenuation.g); + + diffuse.rgb=process_shade(normal,light_dir,eye_vec,diffuse.rgb,specular,specular_exp,shadow_attenuation)*attenuation; + +#endif + + +# if !defined(LIGHT_TYPE_DIRECTIONAL) && !defined(LIGHT_TYPE_OMNI) && !defined (LIGHT_TYPE_SPOT) +//none + diffuse.rgb=vec3(0.0,0.0,0.0); +# endif + + diffuse.rgb+=const_light_mult*emission; + +#endif + + +#ifdef USE_VERTEX_LIGHTING + + vec3 ambient = light_ambient*diffuse.rgb; +# if defined(LIGHT_TYPE_OMNI) || defined (LIGHT_TYPE_SPOT) + ambient*=diffuse_interp.a; //attenuation affects ambient too +# endif + +// diffuse.rgb=(diffuse.rgb * diffuse_interp.rgb + specular * specular_interp)*shadow_attenuation + ambient; +// diffuse.rgb+=emission * const_light_mult; + diffuse.rgb=(diffuse.rgb * diffuse_interp.rgb + specular * specular_interp)*shadow_attenuation + ambient; + diffuse.rgb+=emission * const_light_mult; + + +#endif + + + + + +#ifdef USE_SHADOW_PASS + +#ifdef USE_DEPTH_SHADOWS + + //do nothing, depth is just written +#else + // pack depth to rgba + //highp float bias = 0.0005; + highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0;//bias; + highp vec4 comp = fract(depth * vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0)); + comp -= comp.xxyz * vec4(0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0); + gl_FragColor = comp; + +#endif + +#else + +#ifdef USE_FOG + + diffuse.rgb = mix(diffuse.rgb,fog_interp.rgb,fog_interp.a); +#endif + +#ifdef USE_GLOW + + diffuse.a=glow; +#endif + +#ifdef USE_HDR + diffuse.rgb*=0.25; +#endif + gl_FragColor = diffuse; +#endif +} + + |