virtualx-engine/drivers/gles2/rasterizer_gles2.cpp
Juan Linietsky b324ff7ea5 A bit of everything:
-IMA-ADPCM support for samples, this means that sound effects can be compressed and use 4 timess less RAM.
-New 3D import workflow based on Wavefront OBJ. Import single objects as mesh resources instead of full scenes. Many people prefers to work this way. Just like the rest of the imported resources, these are updated in realtime if modified externally.
-Mesh resources now support naming surfaces. This helps reimporting to identify which user-created materials must be kept.
-Several fixes and improvements to SurfaceTool.
-Anti Aliasing added to WorldEnvironment effects (using FXAA)
-2D Physics bodies (RigidBody, KinematicBody, etc), Raycasts, Tilemap, etc support collision layers. This makes easy to group which objects collide against which.
-2D Trigger shapes can now also trigger collision reporting in other 2D bodies (it used to be in Area2D before)
-Viewport render target textures can now be filtered.
-Few fixes in GDscript make it easier to work with static functions and class members.
-Several and many bugfixes.
2014-05-14 01:22:15 -03:00

8128 lines
215 KiB
C++

/*************************************************************************/
/* 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
#define _EXT_ATC_RGB_AMD 0x8C92
#define _EXT_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93
#define _EXT_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
/* 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_ATC: {
if (!atitc_supported) {
if (!image.empty()) {
image.decompress();
}
r_gl_components=3;
r_gl_format=GL_RGB;
} else {
r_gl_format=_EXT_ATC_RGB_AMD;
r_gl_components=1; //doesn't matter much
r_compressed=true;
}
} break;
case Image::FORMAT_ATC_ALPHA_EXPLICIT: {
if (!atitc_supported) {
if (!image.empty()) {
image.decompress();
}
r_gl_components=4;
r_gl_format=GL_RGBA;
} else {
r_gl_format=_EXT_ATC_RGBA_EXPLICIT_ALPHA_AMD;
r_gl_components=1; //doesn't matter much
r_compressed=true;
}
} break;
case Image::FORMAT_ATC_ALPHA_INTERPOLATED: {
if (!atitc_supported) {
if (!image.empty()) {
image.decompress();
}
r_gl_components=4;
r_gl_format=GL_RGBA;
} else {
r_gl_format=_EXT_ATC_RGBA_INTERPOLATED_ALPHA_AMD;
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;
_get_gl_image_and_format(Image(),texture->format,texture->flags,format,components,has_alpha_cache,compressed);
bool scale_textures = !compressed && !(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;
};
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 (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);
texture->ignore_mipmaps = compressed && img.get_mipmaps()==0;
if (texture->flags&VS::TEXTURE_FLAG_MIPMAPS && !texture->ignore_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 = !(texture->flags&VS::TEXTURE_FLAG_MIPMAPS && !texture->ignore_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 );
}
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 && !texture->ignore_mipmaps) {
//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);
if (texture->render_target) {
p_flags&=VS::TEXTURE_FLAG_FILTER;//can change only filter
}
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 && !texture->ignore_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_MIPMAPS && !texture->ignore_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
}
}
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());
if (mesh->custom_aabb!=AABB())
return mesh->custom_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;
}
void RasterizerGLES2::mesh_set_custom_aabb(RID p_mesh,const AABB& p_aabb) {
Mesh *mesh = mesh_owner.get( p_mesh );
ERR_FAIL_COND(!mesh);
mesh->custom_aabb=p_aabb;
}
AABB RasterizerGLES2::mesh_get_custom_aabb(RID p_mesh) const {
const Mesh *mesh = mesh_owner.get( p_mesh );
ERR_FAIL_COND_V(!mesh,AABB());
return mesh->custom_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_2_SPLITS:
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS:{
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_DIRECTIONAL && lighti->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
return 4; // dp4
} else if (lighti->base->type==VS::LIGHT_OMNI || (lighti->base->type==VS::LIGHT_DIRECTIONAL && lighti->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS)) {
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);
lighti->custom_projection[p_index]=p_camera;
lighti->custom_transform[p_index]=p_transform;
lighti->shadow_split[p_index]=1.0/p_split_far;
#if 0
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;
}
#endif
}
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;
texture->ignore_mipmaps=true;
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));
shadow_filter=ShadowFilterTechnique(int(Globals::get_singleton()->get("rasterizer/shadow_filter")));
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, bool p_transparent_bg, bool p_vflip) {
if (!p_render_target.is_valid()) {
glBindFramebuffer(GL_FRAMEBUFFER,base_framebuffer);
current_rt=NULL;
current_rt_vflip=false;
} 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;
current_rt_transparent=p_transparent_bg;
current_rt_vflip=!p_vflip;
}
}
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;
if (current_rt && current_rt_vflip) {
camera_transform.basis.set_axis(1,-camera_transform.basis.get_axis(1));
}
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();
int passes=light_instance_get_shadow_passes(p_light_instance);
for(int i=0;i<passes;i++) {
Transform modelview=Transform(camera_transform_inverse * li->custom_transform[i]).inverse();
li->shadow_projection[i] = bias * li->custom_projection[i] * modelview;
}
lights_use_shadow=true;
}
} break;
case VS::LIGHT_OMNI: {
if (li->base->shadow_enabled) {
li->shadow_projection[0] = 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[0] = 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);
// ("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_2_SPLITS)
light_types[i]|=0x10;
else if (directional_lights[i]->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS)
light_types[i]|=0x30;
}
}
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,shadow_filter!=SHADOW_FILTER_NONE);
material_shader.set_conditional(MaterialShaderGLES2::USE_SHADOW_PCF_HQ,shadow_filter>SHADOW_FILTER_PCF5);
//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[0]);
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) {
if (li->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
material_shader.set_uniform(MaterialShaderGLES2::SHADOW_MATRIX2,li->shadow_projection[1]);
material_shader.set_uniform(MaterialShaderGLES2::LIGHT_PSSM_SPLIT,Vector3(li->shadow_split[0],li->shadow_split[1],li->shadow_split[2]));
} else if (li->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
material_shader.set_uniform(MaterialShaderGLES2::SHADOW_MATRIX2,li->shadow_projection[1]);
material_shader.set_uniform(MaterialShaderGLES2::SHADOW_MATRIX3,li->shadow_projection[2]);
material_shader.set_uniform(MaterialShaderGLES2::SHADOW_MATRIX4,li->shadow_projection[3]);
material_shader.set_uniform(MaterialShaderGLES2::LIGHT_PSSM_SPLIT,Vector3(li->shadow_split[0],li->shadow_split[1],li->shadow_split[2]));
}
//print_line("shadow split: "+rtos(li->shadow_split));
} else
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) {
if (current_rt && current_rt_vflip) {
p_reverse_cull=!p_reverse_cull;
glFrontFace(GL_CCW);
}
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::LIGHT_USE_PSSM4,false);
material_shader.set_conditional(MaterialShaderGLES2::SHADELESS,false);
}
bool stores_glow = !shadow && (current_env && current_env->fx_enabled[VS::ENV_FX_GLOW]) && !p_alpha_pass;
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::LIGHT_USE_PSSM4,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::LIGHT_USE_PSSM4,(light_type&0x20));
material_shader.set_conditional(MaterialShaderGLES2::SHADELESS,false);
}
rebind=true;
}
if (!*e->additive_ptr) {
additive=false;
*e->additive_ptr=true;
} else {
additive=true;
}
if (stores_glow)
material_shader.set_conditional(MaterialShaderGLES2::USE_GLOW,!additive);
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));
if (current_rt && current_rt_vflip) {
glFrontFace(GL_CW);
}
};
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_rt && current_rt_transparent) {
glClearColor(0,0,0,0);
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);
Size2 size;
if (current_rt) {
glBindFramebuffer(GL_FRAMEBUFFER, current_rt->fbo);
glViewport( 0,0,viewport.width,viewport.height);
size=Size2(viewport.width,viewport.height);
} else {
glBindFramebuffer(GL_FRAMEBUFFER, base_framebuffer);
glViewport( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height );
size=Size2(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.set_conditional(CopyShaderGLES2::USE_NO_ALPHA,true);
copy_shader.set_conditional(CopyShaderGLES2::USE_FXAA,current_env && current_env->fx_enabled[VS::ENV_FX_FXAA]);
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_FXAA])
copy_shader.set_uniform(CopyShaderGLES2::PIXEL_SIZE,Size2(1.0/size.x,1.0/size.y));
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);
copy_shader.set_conditional(CopyShaderGLES2::USE_NO_ALPHA,false);
copy_shader.set_conditional(CopyShaderGLES2::USE_FXAA,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_4_SPLITS) {
cm = shadow->custom_projection[shadow_pass];
light_transform=shadow->custom_transform[shadow_pass];
if (shadow_pass==0) {
glViewport(0, sb->size*0.5, sb->size*0.5, sb->size*0.5);
glScissor(0, sb->size*0.5, sb->size*0.5, sb->size*0.5);
} else if (shadow_pass==1) {
glViewport(0, 0, sb->size*0.5, sb->size*0.5);
glScissor(0, 0, sb->size*0.5, sb->size*0.5);
} else if (shadow_pass==2) {
glViewport(sb->size*0.5, sb->size*0.5, sb->size*0.5, sb->size*0.5);
glScissor(sb->size*0.5, sb->size*0.5, sb->size*0.5, sb->size*0.5);
} else if (shadow_pass==3) {
glViewport(sb->size*0.5, 0, sb->size*0.5, sb->size*0.5);
glScissor(sb->size*0.5, 0, sb->size*0.5, sb->size*0.5);
}
glEnable(GL_SCISSOR_TEST);
} else if (shadow->base->directional_shadow_mode==VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
if (shadow_pass==0) {
cm = shadow->custom_projection[0];
light_transform=shadow->custom_transform[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 {
cm = shadow->custom_projection[1];
light_transform=shadow->custom_transform[1];
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[0];
light_transform=shadow->custom_transform[0];
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);
float csy = 1.0;
if (current_rt && current_rt_vflip)
csy = -1.0;
canvas_transform.scale( Vector3( 2.0f / viewport.width, csy * -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_disable_blending() {
glDisable(GL_BLEND);
}
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.luminance.clear();
framebuffer.blur_size=0;
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;
atitc_supported = false;
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
for (Set<String>::Element *E=extensions.front();E;E=E->next()) {
print_line(E->get());
}
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") || extensions.has("WEBGL_compressed_texture_s3tc");
use_half_float = extensions.has("GL_OES_vertex_half_float");
atitc_supported=extensions.has("GL_AMD_compressed_ATC_texture");
print_line("S3TC: "+itos(s3tc_supported)+" ATITC: "+itos(atitc_supported));
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
shadow_filter=ShadowFilterTechnique((int)(GLOBAL_DEF("rasterizer/shadow_filter",SHADOW_FILTER_PCF5)));
Globals::get_singleton()->set_custom_property_info("rasterizer/shadow_filter",PropertyInfo(Variant::INT,"rasterizer/shadow_filter",PROPERTY_HINT_ENUM,"None,PCF5,PCF13,ESM,VSM"));
use_shadow_mapping=true;
use_fast_texture_filter=!bool(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