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/rasterizer_gles2.cpp | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'drivers/gles2/rasterizer_gles2.cpp')
-rw-r--r-- | drivers/gles2/rasterizer_gles2.cpp | 7918 |
1 files changed, 7918 insertions, 0 deletions
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 |