irrlicht/source/Irrlicht/COGLES2Driver.cpp
hybrid 2ba228a486 New OpenGL-ES 2.x driver. This is a fully shader based render pipeline, which is compatible with, e.g., latest iPhone and other smartphone systems, and embedded devices such as the Pandora game console and the Beagle board.
The whole implementation of the new driver was provided by the folks from Amundis (http://amundis.fr/index.php?lang=en). There are more projects to come from them. Many thanks to Thibault and his colleagues for providing the code for inclusion in the engine.
Please note that you have to remove not only the OpenGL driver (automatically done in IrrCompileConfig once the ogl-es driver is enabled there), but also the linking against the opengl32.lib in the project files. Otherwise no rendering will happen.
Also note that the driver currently does not support all features of the engine. Some materials are not fully working, the shadow system seems to be missing, and the mipmapping is broken. These things will be fixed in future bug fixes.

git-svn-id: http://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@3370 dfc29bdd-3216-0410-991c-e03cc46cb475
2010-08-08 17:46:48 +00:00

2710 lines
110 KiB
C++

// Copyright (C) 2009-2010 Amundis
// Heavily based on the OpenGL driver implemented by Nikolaus Gebhardt
// and OpenGL ES driver implemented by Christian Stehno
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in Irrlicht.h
#include "COGLES2Driver.h"
// needed here also because of the create methods' parameters
#include "CNullDriver.h"
#ifdef _IRR_COMPILE_WITH_OGLES2_
#include "COGLES2Texture.h"
#include "COGLES2MaterialRenderer.h"
#include "COGLES2NormalMapRenderer.h"
#include "COGLES2ParallaxMapRenderer.h"
#include "COGLES2Renderer2D.h"
#include "CImage.h"
#include "os.h"
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#ifndef GL_BGRA
// we need to do this for the IMG_BGRA8888 extension
int GL_BGRA = GL_RGBA;
#endif
namespace irr
{
namespace video
{
//! constructor and init code
COGLES2Driver::COGLES2Driver( const SIrrlichtCreationParameters& params,
const SExposedVideoData& data, io::IFileSystem* io
#if defined(_IRR_COMPILE_WITH_IPHONE_DEVICE_)
, const MIrrIPhoneDevice& device
#endif
)
: CNullDriver( io, params.WindowSize ), COGLES2ExtensionHandler(),
CurrentRenderMode( ERM_NONE ), ResetRenderStates( true ),
Transformation3DChanged( true ), AntiAlias( params.AntiAlias ),
RenderTargetTexture( 0 ), CurrentRendertargetSize( 0, 0 ), ColorFormat( ECF_R8G8B8 ),
EglDisplay( EGL_NO_DISPLAY )
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
, HDc( 0 )
#elif defined(_IRR_COMPILE_WITH_IPHONE_DEVICE_)
, ViewFramebuffer( 0 )
, ViewRenderbuffer( 0 )
, ViewDepthRenderbuffer( 0 )
#endif
, NoHighLevelShader( true )
, BlendEnabled( false )
, SourceFactor( EBF_ZERO )
, DestFactor( EBF_ZERO )
{
#ifdef _DEBUG
setDebugName( "COGLES2Driver" );
#endif
ExposedData = data;
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
EglWindow = ( NativeWindowType )data.OpenGLWin32.HWnd;
HDc = GetDC(( HWND )EglWindow );
EglDisplay = eglGetDisplay(( NativeDisplayType )HDc );
#elif defined(_IRR_COMPILE_WITH_X11_DEVICE_)
EglWindow = ( NativeWindowType )ExposedData.OpenGLLinux.X11Window;
EglDisplay = eglGetDisplay(( NativeDisplayType )ExposedData.OpenGLLinux.X11Display );
#elif defined(_IRR_COMPILE_WITH_IPHONE_DEVICE_)
Device = device;
#endif
if ( EglDisplay == EGL_NO_DISPLAY )
{
os::Printer::log( "Getting OpenGL-ES2 display." );
EglDisplay = eglGetDisplay(( NativeDisplayType ) EGL_DEFAULT_DISPLAY );
}
if ( EglDisplay == EGL_NO_DISPLAY )
{
os::Printer::log( "Could not get OpenGL-ES2 display." );
}
EGLint majorVersion, minorVersion;
if ( !eglInitialize( EglDisplay, &majorVersion, &minorVersion ) )
{
os::Printer::log( "Could not initialize OpenGL-ES2 display." );
}
else
{
char text[64];
sprintf( text, "EglDisplay initialized. Egl version %d.%d\n", majorVersion, minorVersion );
os::Printer::log( text );
}
EGLint attribs[] =
{
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 5,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, params.WithAlphaChannel ? 1 : 0,
EGL_BUFFER_SIZE, 16,//params.Bits,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
//EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
EGL_DEPTH_SIZE, params.ZBufferBits,
EGL_STENCIL_SIZE, params.Stencilbuffer,
EGL_SAMPLE_BUFFERS, params.AntiAlias ? 1 : 0,
EGL_SAMPLES, params.AntiAlias,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE, 0
};
/*EGLint attribs[] =
{
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE, 0
};*/
EGLint contextAttrib[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE, 0
};
EGLConfig config;
EGLint num_configs;
if ( !eglChooseConfig( EglDisplay, attribs, &config, 1, &num_configs ) )
{
os::Printer::log( "Could not get config for OpenGL-ES2 display." );
}
else
{
char log[64];
snprintf( log, 64, "Got %d configs.\n", num_configs );
os::Printer::log( log );
}
os::Printer::log( " Creating EglSurface with nativeWindow..." );
EglSurface = eglCreateWindowSurface( EglDisplay, config, EglWindow, NULL );
if ( EGL_NO_SURFACE == EglSurface )
{
os::Printer::log( "FAILED\n" );
EglSurface = eglCreateWindowSurface( EglDisplay, config, NULL, NULL );
os::Printer::log( "Creating EglSurface without nativeWindows..." );
}
else
os::Printer::log( "SUCCESS\n" );
if ( EGL_NO_SURFACE == EglSurface )
{
os::Printer::log( "FAILED\n" );
os::Printer::log( "Could not create surface for OpenGL-ES2 display." );
}
else
os::Printer::log( "SUCCESS\n" );
#ifdef EGL_VERSION_1_2
eglBindAPI( EGL_OPENGL_ES_API );
#endif
os::Printer::log( "Creating EglContext..." );
EglContext = eglCreateContext( EglDisplay, config, EGL_NO_CONTEXT, contextAttrib );
if ( testEGLError() )
{
os::Printer::log( "FAILED\n" );
os::Printer::log( "Could not create Context for OpenGL-ES2 display." );
}
eglMakeCurrent( EglDisplay, EglSurface, EglSurface, EglContext );
if ( testEGLError() )
{
os::Printer::log( "Could not make Context current for OpenGL-ES2 display." );
}
genericDriverInit( params.WindowSize, params.Stencilbuffer );
// set vsync
if ( params.Vsync )
eglSwapInterval( EglDisplay, 1 );
}
//! destructor
COGLES2Driver::~COGLES2Driver()
{
deleteMaterialRenders();
deleteAllTextures();
// HACK : the following is commented because destroying the context crashes under Linux ( Thibault 04-feb-10 )
/*eglMakeCurrent( EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
eglDestroyContext( EglDisplay, EglContext );
eglDestroySurface( EglDisplay, EglSurface );*/
eglTerminate( EglDisplay );
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
if ( HDc )
ReleaseDC(( HWND )EglWindow, HDc );
#endif
delete TwoDRenderer;
delete FixedPipeline;
}
// -----------------------------------------------------------------------
// METHODS
// -----------------------------------------------------------------------
bool COGLES2Driver::genericDriverInit( const core::dimension2d<u32>& screenSize, bool stencilBuffer )
{
Name = glGetString( GL_VERSION );
printVersion();
os::Printer::log( eglQueryString( EglDisplay, EGL_CLIENT_APIS ) );
// print renderer information
vendorName = glGetString( GL_VENDOR );
os::Printer::log( vendorName.c_str(), ELL_INFORMATION );
u32 i;
for ( i = 0; i < MATERIAL_MAX_TEXTURES; ++i )
CurrentTexture[i] = 0;
// load extensions
initExtensions( this,
EglDisplay,
stencilBuffer );
StencilBuffer = stencilBuffer;
FixedPipeline = new COGLES2FixedPipelineShader( this, FileSystem );
FixedPipeline->useProgram(); //For setting the default uniforms (Alpha)
TwoDRenderer = new COGLES2Renderer2d( this, FileSystem );
glPixelStorei( GL_PACK_ALIGNMENT, 2 );
// Reset The Current Viewport
glViewport( 0, 0, screenSize.Width, screenSize.Height );
setAmbientLight( SColorf( 0.0f, 0.0f, 0.0f, 0.0f ) );
#ifdef GL_separate_specular_color
if ( FeatureAvailable[IRR_separate_specular_color] )
glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR );
#endif
glClearDepthf( 1.0f );
//TODO : OpenGL ES 2.0 Port : GL_PERSPECTIVE_CORRECTION_HINT
//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glHint( GL_GENERATE_MIPMAP_HINT, GL_FASTEST );
glDepthFunc( GL_LEQUAL );
glFrontFace( GL_CW );
UserClipPlane.reallocate( 0 );
// create material renderers
createMaterialRenderers();
// set the renderstates
setRenderStates3DMode();
// set fog mode
setFog( FogColor, FogType, FogStart, FogEnd, FogDensity, PixelFog, RangeFog );
// create matrix for flipping textures
TextureFlipMatrix.buildTextureTransform( 0.0f, core::vector2df( 0, 0 ), core::vector2df( 0, 1.0f ), core::vector2df( 1.0f, -1.0f ) );
// We need to reset once more at the beginning of the first rendering.
// This fixes problems with intermediate changes to the material during texture load.
ResetRenderStates = true;
glUseProgram( 0 );
testGLError();
return true;
}
void COGLES2Driver::createMaterialRenderers()
{
// create OGLES1 material renderers
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_SOLID( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_SOLID_2_LAYER( this ) );
// add the same renderer for all lightmap types
COGLES2MaterialRenderer_LIGHTMAP* lmr = new COGLES2MaterialRenderer_LIGHTMAP( this );
addMaterialRenderer( lmr ); // for EMT_LIGHTMAP:
addMaterialRenderer( lmr ); // for EMT_LIGHTMAP_ADD:
addMaterialRenderer( lmr ); // for EMT_LIGHTMAP_M2:
addMaterialRenderer( lmr ); // for EMT_LIGHTMAP_M4:
addMaterialRenderer( lmr ); // for EMT_LIGHTMAP_LIGHTING:
addMaterialRenderer( lmr ); // for EMT_LIGHTMAP_LIGHTING_M2:
addMaterialRenderer( lmr ); // for EMT_LIGHTMAP_LIGHTING_M4:
lmr->drop();
// add remaining material renderer
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_DETAIL_MAP( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_SPHERE_MAP( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_REFLECTION_2_LAYER( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_TRANSPARENT_ADD_COLOR( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_TRANSPARENT_VERTEX_ALPHA( this ) );
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER( this ) );
// add normal map renderers
s32 tmp = 0;
video::IMaterialRenderer* renderer = 0;
renderer = new COGLES2NormalMapRenderer( this, FileSystem, tmp, MaterialRenderers[EMT_SOLID].Renderer );
renderer->drop();
renderer = new COGLES2NormalMapRenderer( this, FileSystem, tmp, MaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].Renderer );
renderer->drop();
renderer = new COGLES2NormalMapRenderer( this, FileSystem, tmp, MaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].Renderer );
renderer->drop();
// add parallax map renderers
renderer = new COGLES2ParallaxMapRenderer( this, FileSystem, tmp, MaterialRenderers[EMT_SOLID].Renderer );
renderer->drop();
renderer = new COGLES2ParallaxMapRenderer( this, FileSystem, tmp, MaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].Renderer );
renderer->drop();
renderer = new COGLES2ParallaxMapRenderer( this, FileSystem, tmp, MaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].Renderer );
renderer->drop();
// add basic 1 texture blending
addAndDropMaterialRenderer( new COGLES2MaterialRenderer_ONETEXTURE_BLEND( this ) );
}
//! presents the rendered scene on the screen, returns false if failed
bool COGLES2Driver::endScene()
{
CNullDriver::endScene();
eglSwapBuffers( EglDisplay, EglSurface );
EGLint g = eglGetError();
if ( EGL_SUCCESS != g )
{
if ( EGL_CONTEXT_LOST == g )
{
// o-oh, ogl-es has lost contexts...
os::Printer::log( "Context lost, please restart your app." );
}
else
os::Printer::log( "Could not swap buffers for OpenGL-ES2 driver." );
return false;
}
return true;
}
//! clears the zbuffer
bool COGLES2Driver::beginScene( bool backBuffer, bool zBuffer, SColor color,
const SExposedVideoData& videoData, core::rect<s32>* sourceRect )
{
CNullDriver::beginScene( backBuffer, zBuffer, color );
GLbitfield mask = 0;
if ( backBuffer )
{
const f32 inv = 1.0f / 255.0f;
glClearColor( color.getRed() * inv, color.getGreen() * inv,
color.getBlue() * inv, color.getAlpha() * inv );
mask |= GL_COLOR_BUFFER_BIT;
}
if ( zBuffer )
{
glDepthMask( GL_TRUE );
LastMaterial.ZWriteEnable = true;
mask |= GL_DEPTH_BUFFER_BIT;
}
glClear( mask );
testGLError();
return true;
}
//! Returns the transformation set by setTransform
const core::matrix4& COGLES2Driver::getTransform( E_TRANSFORMATION_STATE state ) const
{
return Matrices[state];
}
//! sets transformation
void COGLES2Driver::setTransform( E_TRANSFORMATION_STATE state, const core::matrix4& mat )
{
Matrices[state] = mat;
Transformation3DChanged = true;
}
bool COGLES2Driver::updateVertexHardwareBuffer( SHWBufferLink_opengl *HWBuffer )
{
if ( !HWBuffer )
return false;
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const void* vertices = mb->getVertices();
const u32 vertexCount = mb->getVertexCount();
const E_VERTEX_TYPE vType = mb->getVertexType();
const u32 vertexSize = getVertexPitchFromType( vType );
//buffer vertex data, and convert colours...
core::array<c8> buffer( vertexSize * vertexCount );
memcpy( buffer.pointer(), vertices, vertexSize * vertexCount );
//get or create buffer
bool newBuffer = false;
if ( !HWBuffer->vbo_verticesID )
{
glGenBuffers( 1, &HWBuffer->vbo_verticesID );
if ( !HWBuffer->vbo_verticesID ) return false;
newBuffer = true;
}
else if ( HWBuffer->vbo_verticesSize < vertexCount*vertexSize )
{
newBuffer = true;
}
glBindBuffer( GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID );
//copy data to graphics card
glGetError(); // clear error storage
if ( !newBuffer )
glBufferSubData( GL_ARRAY_BUFFER, 0, vertexCount * vertexSize, buffer.const_pointer() );
else
{
HWBuffer->vbo_verticesSize = vertexCount * vertexSize;
if ( HWBuffer->Mapped_Vertex == scene::EHM_STATIC )
glBufferData( GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_STATIC_DRAW );
else
glBufferData( GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_DYNAMIC_DRAW );
}
glBindBuffer( GL_ARRAY_BUFFER, 0 );
return ( glGetError() == GL_NO_ERROR );
}
bool COGLES2Driver::updateIndexHardwareBuffer( SHWBufferLink_opengl *HWBuffer )
{
if ( !HWBuffer )
return false;
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const void* indices = mb->getIndices();
u32 indexCount = mb->getIndexCount();
GLenum indexSize;
switch ( mb->getIndexType() )
{
case( EIT_16BIT ):
{
indexSize = sizeof( u16 );
break;
}
case( EIT_32BIT ):
{
indexSize = sizeof( u32 );
break;
}
default:
{
return false;
}
}
//get or create buffer
bool newBuffer = false;
if ( !HWBuffer->vbo_indicesID )
{
glGenBuffers( 1, &HWBuffer->vbo_indicesID );
if ( !HWBuffer->vbo_indicesID ) return false;
newBuffer = true;
}
else if ( HWBuffer->vbo_indicesSize < indexCount*indexSize )
{
newBuffer = true;
}
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID );
//copy data to graphics card
glGetError(); // clear error storage
if ( !newBuffer )
glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * indexSize, indices );
else
{
HWBuffer->vbo_indicesSize = indexCount * indexSize;
if ( HWBuffer->Mapped_Index == scene::EHM_STATIC )
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STATIC_DRAW );
else
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_DYNAMIC_DRAW );
}
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
return ( glGetError() == GL_NO_ERROR );
}
//! updates hardware buffer if needed
bool COGLES2Driver::updateHardwareBuffer( SHWBufferLink *HWBuffer )
{
if ( !HWBuffer )
return false;
if ( HWBuffer->Mapped_Vertex != scene::EHM_NEVER )
{
if ( HWBuffer->ChangedID_Vertex != HWBuffer->MeshBuffer->getChangedID_Vertex()
|| !(( SHWBufferLink_opengl* )HWBuffer )->vbo_verticesID )
{
HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex();
if ( !updateVertexHardwareBuffer(( SHWBufferLink_opengl* )HWBuffer ) )
return false;
}
}
if ( HWBuffer->Mapped_Index != scene::EHM_NEVER )
{
if ( HWBuffer->ChangedID_Index != HWBuffer->MeshBuffer->getChangedID_Index()
|| !(( SHWBufferLink_opengl* )HWBuffer )->vbo_indicesID )
{
HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index();
if ( !updateIndexHardwareBuffer(( SHWBufferLink_opengl* )HWBuffer ) )
return false;
}
}
return true;
}
//! Create hardware buffer from meshbuffer
COGLES2Driver::SHWBufferLink *COGLES2Driver::createHardwareBuffer( const scene::IMeshBuffer* mb )
{
if ( !mb || ( mb->getHardwareMappingHint_Index() == scene::EHM_NEVER && mb->getHardwareMappingHint_Vertex() == scene::EHM_NEVER ) )
return 0;
SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl( mb );
//add to map
HWBufferMap.insert( HWBuffer->MeshBuffer, HWBuffer );
HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex();
HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index();
HWBuffer->Mapped_Vertex = mb->getHardwareMappingHint_Vertex();
HWBuffer->Mapped_Index = mb->getHardwareMappingHint_Index();
HWBuffer->LastUsed = 0;
HWBuffer->vbo_verticesID = 0;
HWBuffer->vbo_indicesID = 0;
HWBuffer->vbo_verticesSize = 0;
HWBuffer->vbo_indicesSize = 0;
if ( !updateHardwareBuffer( HWBuffer ) )
{
deleteHardwareBuffer( HWBuffer );
return 0;
}
return HWBuffer;
}
void COGLES2Driver::deleteHardwareBuffer( SHWBufferLink *_HWBuffer )
{
if ( !_HWBuffer )
return;
SHWBufferLink_opengl *HWBuffer = ( SHWBufferLink_opengl* )_HWBuffer;
if ( HWBuffer->vbo_verticesID )
{
glDeleteBuffers( 1, &HWBuffer->vbo_verticesID );
HWBuffer->vbo_verticesID = 0;
}
if ( HWBuffer->vbo_indicesID )
{
glDeleteBuffers( 1, &HWBuffer->vbo_indicesID );
HWBuffer->vbo_indicesID = 0;
}
CNullDriver::deleteHardwareBuffer( _HWBuffer );
}
//! Draw hardware buffer
void COGLES2Driver::drawHardwareBuffer( SHWBufferLink *_HWBuffer )
{
if ( !_HWBuffer )
return;
SHWBufferLink_opengl *HWBuffer = ( SHWBufferLink_opengl* )_HWBuffer;
updateHardwareBuffer( HWBuffer ); //check if update is needed
HWBuffer->LastUsed = 0;//reset count
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const void *vertices = mb->getVertices();
const void *indexList = mb->getIndices();
if ( HWBuffer->Mapped_Vertex != scene::EHM_NEVER )
{
glBindBuffer( GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID );
vertices = 0;
}
if ( HWBuffer->Mapped_Index != scene::EHM_NEVER )
{
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID );
indexList = 0;
}
drawVertexPrimitiveList( vertices, mb->getVertexCount(), indexList,
mb->getIndexCount() / 3, mb->getVertexType(),
scene::EPT_TRIANGLES, mb->getIndexType() );
if ( HWBuffer->Mapped_Vertex != scene::EHM_NEVER )
glBindBuffer( GL_ARRAY_BUFFER, 0 );
if ( HWBuffer->Mapped_Index != scene::EHM_NEVER )
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
}
// small helper function to create vertex buffer object adress offsets
static inline u8* buffer_offset( const long offset )
{
return (( u8* )0 + offset );
}
//! draws a vertex primitive list
void COGLES2Driver::drawVertexPrimitiveList( const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType )
{
testGLError();
if ( !checkPrimitiveCount( primitiveCount ) )
return;
setRenderStates3DMode();
drawVertexPrimitiveList2d3d( vertices, vertexCount, ( const u16* )indexList, primitiveCount, vType, pType, iType );
if ( static_cast<u32>( Material.MaterialType ) < MaterialRenderers.size() )
MaterialRenderers[Material.MaterialType].Renderer->PostRender( this, video::EVT_STANDARD );
}
void COGLES2Driver::drawVertexPrimitiveList2d3d( const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType, bool threed )
{
if ( !primitiveCount || !vertexCount )
return;
if ( !threed && !checkPrimitiveCount( primitiveCount ) )
return;
CNullDriver::drawVertexPrimitiveList( vertices, vertexCount, indexList, primitiveCount, vType, pType, iType );
//TODO: treat #ifdef GL_OES_point_size_array outside this if
if ( NoHighLevelShader )
{
glEnableVertexAttribArray( EVA_COLOR );
glEnableVertexAttribArray( EVA_POSITION );
if (( pType != scene::EPT_POINTS ) && ( pType != scene::EPT_POINT_SPRITES ) )
{
glEnableVertexAttribArray( EVA_TCOORD0 );
}
#ifdef GL_OES_point_size_array
else if ( FeatureAvailable[IRR_OES_point_size_array] && ( Material.Thickness == 0.0f ) )
glEnableClientState( GL_POINT_SIZE_ARRAY_OES );
#endif
if ( threed && ( pType != scene::EPT_POINTS ) && ( pType != scene::EPT_POINT_SPRITES ) )
{
glEnableVertexAttribArray( EVA_NORMAL );
}
switch ( vType )
{
case EVT_STANDARD:
if ( vertices )
{
#ifdef GL_OES_point_size_array
if (( pType == scene::EPT_POINTS ) || ( pType == scene::EPT_POINT_SPRITES ) )
{
if ( FeatureAvailable[IRR_OES_point_size_array] && ( Material.Thickness == 0.0f ) )
glPointSizePointerOES( GL_FLOAT, sizeof( S3DVertex ), &( static_cast<const S3DVertex*>( vertices ) )[0].Normal.X );
}
else
#endif
glVertexAttribPointer( EVA_POSITION, ( threed ? 3 : 2 ), GL_FLOAT, false, sizeof( S3DVertex ), &( static_cast<const S3DVertex*>( vertices ) )[0].Pos );
if ( threed )
glVertexAttribPointer( EVA_NORMAL, 3, GL_FLOAT, false, sizeof( S3DVertex ), &( static_cast<const S3DVertex*>( vertices ) )[0].Normal );
glVertexAttribPointer( EVA_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof( S3DVertex ), &( static_cast<const S3DVertex*>( vertices ) )[0].Color );
glVertexAttribPointer( EVA_TCOORD0, 2, GL_FLOAT, false, sizeof( S3DVertex ), &( static_cast<const S3DVertex*>( vertices ) )[0].TCoords );
}
else
{
glVertexAttribPointer( EVA_POSITION, 3, GL_FLOAT, false, sizeof( S3DVertex ), 0 );
glVertexAttribPointer( EVA_NORMAL, 3, GL_FLOAT, false, sizeof( S3DVertex ), buffer_offset( 12 ) );
glVertexAttribPointer( EVA_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof( S3DVertex ), buffer_offset( 24 ) );
glVertexAttribPointer( EVA_TCOORD0, 2, GL_FLOAT, false, sizeof( S3DVertex ), buffer_offset( 28 ) );
}
if ( CurrentTexture[1] )
{
// There must be some optimisation here as it uses the same texture coord !
glEnableVertexAttribArray( EVA_TCOORD1 );
if ( vertices )
glVertexAttribPointer( EVA_TCOORD1, 2, GL_FLOAT, false, sizeof( S3DVertex ), &( static_cast<const S3DVertex*>( vertices ) )[0].TCoords );
else
glVertexAttribPointer( EVA_TCOORD1, 2, GL_FLOAT, false, sizeof( S3DVertex ), buffer_offset( 28 ) );
}
break;
case EVT_2TCOORDS:
glEnableVertexAttribArray( EVA_TCOORD1 );
if ( vertices )
{
glVertexAttribPointer( EVA_POSITION, ( threed ? 3 : 2 ), GL_FLOAT, false, sizeof( S3DVertex2TCoords ), &( static_cast<const S3DVertex2TCoords*>( vertices ) )[0].Pos );
if ( threed )
glVertexAttribPointer( EVA_NORMAL, 3, GL_FLOAT, false, sizeof( S3DVertex2TCoords ), &( static_cast<const S3DVertex2TCoords*>( vertices ) )[0].Normal );
glVertexAttribPointer( EVA_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof( S3DVertex2TCoords ), &( static_cast<const S3DVertex2TCoords*>( vertices ) )[0].Color );
glVertexAttribPointer( EVA_TCOORD0, 2, GL_FLOAT, false, sizeof( S3DVertex2TCoords ), &( static_cast<const S3DVertex2TCoords*>( vertices ) )[0].TCoords );
glVertexAttribPointer( EVA_TCOORD1, 2, GL_FLOAT, false, sizeof( S3DVertex2TCoords ), &( static_cast<const S3DVertex2TCoords*>( vertices ) )[0].TCoords2 );
}
else
{
glVertexAttribPointer( EVA_POSITION, 3, GL_FLOAT, false, sizeof( S3DVertex2TCoords ), buffer_offset( 0 ) );
glVertexAttribPointer( EVA_NORMAL, 3, GL_FLOAT, false, sizeof( S3DVertex2TCoords ), buffer_offset( 12 ) );
glVertexAttribPointer( EVA_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof( S3DVertex2TCoords ), buffer_offset( 24 ) );
glVertexAttribPointer( EVA_TCOORD0, 2, GL_FLOAT, false, sizeof( S3DVertex2TCoords ), buffer_offset( 28 ) );
glVertexAttribPointer( EVA_TCOORD1, 2, GL_FLOAT, false, sizeof( S3DVertex2TCoords ), buffer_offset( 36 ) );
}
break;
case EVT_TANGENTS:
glEnableVertexAttribArray( EVA_TANGENT );
glEnableVertexAttribArray( EVA_BINORMAL );
if ( vertices )
{
glVertexAttribPointer( EVA_POSITION, ( threed ? 3 : 2 ), GL_FLOAT, false, sizeof( S3DVertexTangents ), &( static_cast<const S3DVertexTangents*>( vertices ) )[0].Pos );
if ( threed )
glVertexAttribPointer( EVA_NORMAL, 3, GL_FLOAT, false, sizeof( S3DVertexTangents ), &( static_cast<const S3DVertexTangents*>( vertices ) )[0].Normal );
glVertexAttribPointer( EVA_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof( S3DVertexTangents ), &( static_cast<const S3DVertexTangents*>( vertices ) )[0].Color );
glVertexAttribPointer( EVA_TCOORD0, 2, GL_FLOAT, false, sizeof( S3DVertexTangents ), &( static_cast<const S3DVertexTangents*>( vertices ) )[0].TCoords );
glVertexAttribPointer( EVA_TANGENT, 3, GL_FLOAT, false, sizeof( S3DVertexTangents ), &( static_cast<const S3DVertexTangents*>( vertices ) )[0].Tangent );
glVertexAttribPointer( EVA_BINORMAL, 3, GL_FLOAT, false, sizeof( S3DVertexTangents ), &( static_cast<const S3DVertexTangents*>( vertices ) )[0].Binormal );
}
else
{
glVertexAttribPointer( EVA_POSITION, 3, GL_FLOAT, false, sizeof( S3DVertexTangents ), buffer_offset( 0 ) );
glVertexAttribPointer( EVA_NORMAL, 3, GL_FLOAT, false, sizeof( S3DVertexTangents ), buffer_offset( 12 ) );
glVertexAttribPointer( EVA_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof( S3DVertexTangents ), buffer_offset( 24 ) );
glVertexAttribPointer( EVA_TCOORD0, 2, GL_FLOAT, false, sizeof( S3DVertexTangents ), buffer_offset( 28 ) );
glVertexAttribPointer( EVA_TANGENT, 3, GL_FLOAT, false, sizeof( S3DVertexTangents ), buffer_offset( 36 ) );
glVertexAttribPointer( EVA_BINORMAL, 3, GL_FLOAT, false, sizeof( S3DVertexTangents ), buffer_offset( 48 ) );
}
break;
}
}
// draw everything
GLenum indexSize = 0;
switch ( iType )
{
case( EIT_16BIT ):
{
indexSize = GL_UNSIGNED_SHORT;
break;
}
case( EIT_32BIT ):
{
#ifdef GL_OES_element_index_uint
#ifndef GL_UNSIGNED_INT
#define GL_UNSIGNED_INT 0x1405
#endif
if ( FeatureAvailable[IRR_OES_element_index_uint] )
indexSize = GL_UNSIGNED_INT;
else
#endif
indexSize = GL_UNSIGNED_SHORT;
break;
}
}
switch ( pType )
{
case scene::EPT_POINTS:
case scene::EPT_POINT_SPRITES:
{
#ifdef GL_OES_point_sprite
if ( pType == scene::EPT_POINT_SPRITES && FeatureAvailable[IRR_OES_point_sprite] )
glEnable( GL_POINT_SPRITE_OES );
#endif
// if ==0 we use the point size array
if ( Material.Thickness != 0.f )
{
// float quadratic[] = {0.0f, 0.0f, 10.01f};
//TODO : OpenGL ES 2.0 Port GL_POINT_DISTANCE_ATTENUATION
//glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, quadratic);
// float maxParticleSize = 1.0f;
//TODO : OpenGL ES 2.0 Port GL_POINT_SIZE_MAX
//glGetFloatv(GL_POINT_SIZE_MAX, &maxParticleSize);
// maxParticleSize=maxParticleSize<Material.Thickness?maxParticleSize:Material.Thickness;
// glPointParameterf(GL_POINT_SIZE_MAX,maxParticleSize);
// glPointParameterf(GL_POINT_SIZE_MIN,Material.Thickness);
//TODO : OpenGL ES 2.0 Port GL_POINT_FADE_THRESHOLD_SIZE
//glPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE, 60.0f);
//glPointSize(Material.Thickness);
}
#ifdef GL_OES_point_sprite
if ( pType == scene::EPT_POINT_SPRITES && FeatureAvailable[IRR_OES_point_sprite] )
glTexEnvf( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE );
#endif
glDrawArrays( GL_POINTS, 0, primitiveCount );
#ifdef GL_OES_point_sprite
if ( pType == scene::EPT_POINT_SPRITES && FeatureAvailable[IRR_OES_point_sprite] )
{
glDisable( GL_POINT_SPRITE_OES );
glTexEnvf( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_FALSE );
}
#endif
}
break;
case scene::EPT_LINE_STRIP:
glDrawElements( GL_LINE_STRIP, primitiveCount + 1, indexSize, indexList );
break;
case scene::EPT_LINE_LOOP:
glDrawElements( GL_LINE_LOOP, primitiveCount, indexSize, indexList );
break;
case scene::EPT_LINES:
glDrawElements( GL_LINES, primitiveCount*2, indexSize, indexList );
break;
case scene::EPT_TRIANGLE_STRIP:
glDrawElements( GL_TRIANGLE_STRIP, primitiveCount + 2, indexSize, indexList );
break;
case scene::EPT_TRIANGLE_FAN:
glDrawElements( GL_TRIANGLE_FAN, primitiveCount + 2, indexSize, indexList );
break;
case scene::EPT_TRIANGLES:
glDrawElements(( LastMaterial.Wireframe ) ? GL_LINES : ( LastMaterial.PointCloud ) ? GL_POINTS : GL_TRIANGLES, primitiveCount*3, indexSize, indexList );
break;
case scene::EPT_QUAD_STRIP:
// TODO ogl-es
// glDrawElements(GL_QUAD_STRIP, primitiveCount*2+2, indexSize, indexList);
break;
case scene::EPT_QUADS:
// TODO ogl-es
// glDrawElements(GL_QUADS, primitiveCount*4, indexSize, indexList);
break;
case scene::EPT_POLYGON:
// TODO ogl-es
// glDrawElements(GL_POLYGON, primitiveCount, indexSize, indexList);
break;
}
if ( NoHighLevelShader )
{
if ( vType == EVT_TANGENTS )
{
glDisableVertexAttribArray( EVA_TANGENT );
glDisableVertexAttribArray( EVA_BINORMAL );
}
if (( vType != EVT_STANDARD ) || CurrentTexture[1] )
{
glDisableVertexAttribArray( EVA_TCOORD1 );
}
#ifdef GL_OES_point_size_array
if ( FeatureAvailable[IRR_OES_point_size_array] && ( Material.Thickness == 0.0f ) )
glDisableClientState( GL_POINT_SIZE_ARRAY_OES );
#endif
glDisableVertexAttribArray( EVA_POSITION );
glDisableVertexAttribArray( EVA_NORMAL );
glDisableVertexAttribArray( EVA_COLOR );
glDisableVertexAttribArray( EVA_TCOORD0 );
}
testGLError();
}
//! draws a 2d image, using a color and the alpha channel of the texture
void COGLES2Driver::draw2DImage( const video::ITexture* texture,
const core::position2d<s32>& pos,
const core::rect<s32>& sourceRect,
const core::rect<s32>* clipRect, SColor color,
bool useAlphaChannelOfTexture )
{
if ( !texture )
return;
if ( !sourceRect.isValid() )
return;
core::position2d<s32> targetPos( pos );
core::position2d<s32> sourcePos( sourceRect.UpperLeftCorner );
core::dimension2d<s32> sourceSize( sourceRect.getSize() );
if ( clipRect )
{
if ( targetPos.X < clipRect->UpperLeftCorner.X )
{
sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X;
if ( sourceSize.Width <= 0 )
return;
sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X;
targetPos.X = clipRect->UpperLeftCorner.X;
}
if ( targetPos.X + sourceSize.Width > clipRect->LowerRightCorner.X )
{
sourceSize.Width -= ( targetPos.X + sourceSize.Width ) - clipRect->LowerRightCorner.X;
if ( sourceSize.Width <= 0 )
return;
}
if ( targetPos.Y < clipRect->UpperLeftCorner.Y )
{
sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y;
if ( sourceSize.Height <= 0 )
return;
sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y;
targetPos.Y = clipRect->UpperLeftCorner.Y;
}
if ( targetPos.Y + sourceSize.Height > clipRect->LowerRightCorner.Y )
{
sourceSize.Height -= ( targetPos.Y + sourceSize.Height ) - clipRect->LowerRightCorner.Y;
if ( sourceSize.Height <= 0 )
return;
}
}
// clip these coordinates
if ( targetPos.X < 0 )
{
sourceSize.Width += targetPos.X;
if ( sourceSize.Width <= 0 )
return;
sourcePos.X -= targetPos.X;
targetPos.X = 0;
}
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
if ( targetPos.X + sourceSize.Width > ( s32 )renderTargetSize.Width )
{
sourceSize.Width -= ( targetPos.X + sourceSize.Width ) - renderTargetSize.Width;
if ( sourceSize.Width <= 0 )
return;
}
if ( targetPos.Y < 0 )
{
sourceSize.Height += targetPos.Y;
if ( sourceSize.Height <= 0 )
return;
sourcePos.Y -= targetPos.Y;
targetPos.Y = 0;
}
if ( targetPos.Y + sourceSize.Height > ( s32 )renderTargetSize.Height )
{
sourceSize.Height -= ( targetPos.Y + sourceSize.Height ) - renderTargetSize.Height;
if ( sourceSize.Height <= 0 )
return;
}
// ok, we've clipped everything.
// now draw it.
// texcoords need to be flipped horizontally for RTTs
const bool isRTT = texture->isRenderTarget();
const core::dimension2d<u32>& ss = texture->getOriginalSize();
const f32 invW = 1.f / static_cast<f32>( ss.Width );
const f32 invH = 1.f / static_cast<f32>( ss.Height );
const core::rect<f32> tcoords(
sourcePos.X * invW,
( isRTT ? ( sourcePos.Y + sourceSize.Height ) : sourcePos.Y ) * invH,
( sourcePos.X + sourceSize.Width ) * invW,
( isRTT ? sourcePos.Y : ( sourcePos.Y + sourceSize.Height ) ) * invH );
const core::rect<s32> poss( targetPos, sourceSize );
disableTextures( 1 );
if ( !setTexture( 0, texture ) )
return;
setRenderStates2DMode( color.getAlpha() < 255, true, useAlphaChannelOfTexture );
u16 indices[] = {0, 1, 2, 3};
S3DVertex vertices[4];
vertices[0] = S3DVertex(( f32 )poss.UpperLeftCorner.X, ( f32 )poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y );
vertices[1] = S3DVertex(( f32 )poss.LowerRightCorner.X, ( f32 )poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y );
vertices[2] = S3DVertex(( f32 )poss.LowerRightCorner.X, ( f32 )poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y );
vertices[3] = S3DVertex(( f32 )poss.UpperLeftCorner.X, ( f32 )poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y );
drawVertexPrimitiveList2d3d( vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false );
}
void COGLES2Driver::draw2DImageBatch( const video::ITexture* texture,
const core::array<core::position2d<s32> >& positions,
const core::array<core::rect<s32> >& sourceRects,
const core::rect<s32>* clipRect,
SColor color,
bool useAlphaChannelOfTexture )
{
if ( !texture )
return;
if ( !setTexture( 0, const_cast<video::ITexture*>( texture ) ) )
return;
const irr::u32 drawCount = core::min_<u32>( positions.size(), sourceRects.size() );
core::array<S3DVertex> vtx( drawCount * 4 );
core::array<u16> indices( drawCount * 6 );
for ( u32 i = 0; i < drawCount; i++ )
{
core::position2d<s32> targetPos = positions[i];
core::position2d<s32> sourcePos = sourceRects[i].UpperLeftCorner;
// This needs to be signed as it may go negative.
core::dimension2d<s32> sourceSize( sourceRects[i].getSize() );
if ( clipRect )
{
if ( targetPos.X < clipRect->UpperLeftCorner.X )
{
sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X;
if ( sourceSize.Width <= 0 )
continue;
sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X;
targetPos.X = clipRect->UpperLeftCorner.X;
}
if ( targetPos.X + ( s32 )sourceSize.Width > clipRect->LowerRightCorner.X )
{
sourceSize.Width -= ( targetPos.X + sourceSize.Width ) - clipRect->LowerRightCorner.X;
if ( sourceSize.Width <= 0 )
continue;
}
if ( targetPos.Y < clipRect->UpperLeftCorner.Y )
{
sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y;
if ( sourceSize.Height <= 0 )
continue;
sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y;
targetPos.Y = clipRect->UpperLeftCorner.Y;
}
if ( targetPos.Y + ( s32 )sourceSize.Height > clipRect->LowerRightCorner.Y )
{
sourceSize.Height -= ( targetPos.Y + sourceSize.Height ) - clipRect->LowerRightCorner.Y;
if ( sourceSize.Height <= 0 )
continue;
}
}
// clip these coordinates
if ( targetPos.X < 0 )
{
sourceSize.Width += targetPos.X;
if ( sourceSize.Width <= 0 )
continue;
sourcePos.X -= targetPos.X;
targetPos.X = 0;
}
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
if ( targetPos.X + sourceSize.Width > ( s32 )renderTargetSize.Width )
{
sourceSize.Width -= ( targetPos.X + sourceSize.Width ) - renderTargetSize.Width;
if ( sourceSize.Width <= 0 )
continue;
}
if ( targetPos.Y < 0 )
{
sourceSize.Height += targetPos.Y;
if ( sourceSize.Height <= 0 )
continue;
sourcePos.Y -= targetPos.Y;
targetPos.Y = 0;
}
if ( targetPos.Y + sourceSize.Height > ( s32 )renderTargetSize.Height )
{
sourceSize.Height -= ( targetPos.Y + sourceSize.Height ) - renderTargetSize.Height;
if ( sourceSize.Height <= 0 )
continue;
}
// ok, we've clipped everything.
// now draw it.
core::rect<f32> tcoords;
tcoords.UpperLeftCorner.X = ((( f32 )sourcePos.X ) ) / texture->getOriginalSize().Width ;
tcoords.UpperLeftCorner.Y = ((( f32 )sourcePos.Y ) ) / texture->getOriginalSize().Height;
tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + (( f32 )( sourceSize.Width ) / texture->getOriginalSize().Width );
tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + (( f32 )( sourceSize.Height ) / texture->getOriginalSize().Height );
const core::rect<s32> poss( targetPos, sourceSize );
setRenderStates2DMode( color.getAlpha() < 255, true, useAlphaChannelOfTexture );
vtx.push_back( S3DVertex(( f32 )poss.UpperLeftCorner.X, ( f32 )poss.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y ) );
vtx.push_back( S3DVertex(( f32 )poss.LowerRightCorner.X, ( f32 )poss.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y ) );
vtx.push_back( S3DVertex(( f32 )poss.LowerRightCorner.X, ( f32 )poss.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y ) );
vtx.push_back( S3DVertex(( f32 )poss.UpperLeftCorner.X, ( f32 )poss.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y ) );
const u32 curPos = vtx.size() - 4;
indices.push_back( 0 + curPos );
indices.push_back( 1 + curPos );
indices.push_back( 2 + curPos );
indices.push_back( 0 + curPos );
indices.push_back( 2 + curPos );
indices.push_back( 3 + curPos );
}
if ( vtx.size() )
{
drawVertexPrimitiveList2d3d( vtx.pointer(),
vtx.size(), indices.pointer(),
indices.size() / 3,
EVT_STANDARD,
scene::EPT_TRIANGLES,
EIT_16BIT,
false );
}
}
//! The same, but with a four element array of colors, one for each vertex
void COGLES2Driver::draw2DImage( const video::ITexture* texture, const core::rect<s32>& destRect,
const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect,
const video::SColor* const colors, bool useAlphaChannelOfTexture )
{
if ( !texture )
return;
// texcoords need to be flipped horizontally for RTTs
const bool isRTT = texture->isRenderTarget();
const core::dimension2du& ss = texture->getOriginalSize();
const f32 invW = 1.f / static_cast<f32>( ss.Width );
const f32 invH = 1.f / static_cast<f32>( ss.Height );
const core::rect<f32> tcoords(
sourceRect.UpperLeftCorner.X * invW,
( isRTT ? sourceRect.LowerRightCorner.Y : sourceRect.UpperLeftCorner.Y ) * invH,
sourceRect.LowerRightCorner.X * invW,
( isRTT ? sourceRect.UpperLeftCorner.Y : sourceRect.LowerRightCorner.Y ) *invH );
const video::SColor temp[4] =
{
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF
};
const video::SColor* const useColor = colors ? colors : temp;
disableTextures( 1 );
setTexture( 0, texture );
setRenderStates2DMode( useColor[0].getAlpha() < 255 || useColor[1].getAlpha() < 255 ||
useColor[2].getAlpha() < 255 || useColor[3].getAlpha() < 255,
true, useAlphaChannelOfTexture );
if ( clipRect )
{
if ( !clipRect->isValid() )
return;
glEnable( GL_SCISSOR_TEST );
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
glScissor( clipRect->UpperLeftCorner.X, renderTargetSize.Height - clipRect->LowerRightCorner.Y,
clipRect->getWidth(), clipRect->getHeight() );
}
u16 indices[] = {0, 1, 2, 3};
S3DVertex vertices[4];
vertices[0] = S3DVertex(( f32 )destRect.UpperLeftCorner.X, ( f32 )destRect.UpperLeftCorner.Y, 0, 0, 0, 1, useColor[0], tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y );
vertices[1] = S3DVertex(( f32 )destRect.LowerRightCorner.X, ( f32 )destRect.UpperLeftCorner.Y, 0, 0, 0, 1, useColor[3], tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y );
vertices[2] = S3DVertex(( f32 )destRect.LowerRightCorner.X, ( f32 )destRect.LowerRightCorner.Y, 0, 0, 0, 1, useColor[2], tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y );
vertices[3] = S3DVertex(( f32 )destRect.UpperLeftCorner.X, ( f32 )destRect.LowerRightCorner.Y, 0, 0, 0, 1, useColor[1], tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y );
drawVertexPrimitiveList2d3d( vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false );
if ( clipRect )
glDisable( GL_SCISSOR_TEST );
testGLError();
}
//! draws a set of 2d images, using a color and the alpha channel
void COGLES2Driver::draw2DImageBatch( const video::ITexture* texture,
const core::position2d<s32>& pos,
const core::array<core::rect<s32> >& sourceRects,
const core::array<s32>& indices, s32 kerningWidth,
const core::rect<s32>* clipRect, SColor color,
bool useAlphaChannelOfTexture )
{
if ( !texture )
return;
disableTextures( 1 );
if ( !setTexture( 0, texture ) )
return;
setRenderStates2DMode( color.getAlpha() < 255, true, useAlphaChannelOfTexture );
if ( clipRect )
{
if ( !clipRect->isValid() )
return;
glEnable( GL_SCISSOR_TEST );
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
glScissor( clipRect->UpperLeftCorner.X, renderTargetSize.Height - clipRect->LowerRightCorner.Y,
clipRect->getWidth(), clipRect->getHeight() );
}
const core::dimension2du& ss = texture->getOriginalSize();
core::position2d<s32> targetPos( pos );
// texcoords need to be flipped horizontally for RTTs
const bool isRTT = texture->isRenderTarget();
const f32 invW = 1.f / static_cast<f32>( ss.Width );
const f32 invH = 1.f / static_cast<f32>( ss.Height );
core::array<S3DVertex> vertices;
core::array<u16> quadIndices;
vertices.reallocate( indices.size()*4 );
quadIndices.reallocate( indices.size()*3 );
for ( u32 i = 0; i < indices.size(); ++i )
{
const s32 currentIndex = indices[i];
if ( !sourceRects[currentIndex].isValid() )
break;
const core::rect<f32> tcoords(
sourceRects[currentIndex].UpperLeftCorner.X * invW,
( isRTT ? sourceRects[currentIndex].LowerRightCorner.Y : sourceRects[currentIndex].UpperLeftCorner.Y ) * invH,
sourceRects[currentIndex].LowerRightCorner.X * invW,
( isRTT ? sourceRects[currentIndex].UpperLeftCorner.Y : sourceRects[currentIndex].LowerRightCorner.Y ) * invH );
const core::rect<s32> poss( targetPos, sourceRects[currentIndex].getSize() );
vertices.push_back( S3DVertex(( f32 )poss.UpperLeftCorner.X, ( f32 )poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y ) );
vertices.push_back( S3DVertex(( f32 )poss.LowerRightCorner.X, ( f32 )poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y ) );
vertices.push_back( S3DVertex(( f32 )poss.LowerRightCorner.X, ( f32 )poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y ) );
vertices.push_back( S3DVertex(( f32 )poss.UpperLeftCorner.X, ( f32 )poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y ) );
targetPos.X += sourceRects[currentIndex].getWidth();
}
drawVertexPrimitiveList2d3d( vertices.pointer(), 4, quadIndices.pointer(), 2*indices.size(), video::EVT_STANDARD, scene::EPT_TRIANGLES, EIT_16BIT, false );
if ( clipRect )
glDisable( GL_SCISSOR_TEST );
testGLError();
}
//! draw a 2d rectangle
void COGLES2Driver::draw2DRectangle( SColor color, const core::rect<s32>& position,
const core::rect<s32>* clip )
{
disableTextures();
setRenderStates2DMode( color.getAlpha() < 255, false, false );
core::rect<s32> pos = position;
if ( clip )
pos.clipAgainst( *clip );
if ( !pos.isValid() )
return;
u16 indices[] = {0, 1, 2, 3};
S3DVertex vertices[4];
vertices[0] = S3DVertex(( f32 )pos.UpperLeftCorner.X, ( f32 )pos.UpperLeftCorner.Y, 0, 0, 0, 1, color, 0, 0 );
vertices[1] = S3DVertex(( f32 )pos.LowerRightCorner.X, ( f32 )pos.UpperLeftCorner.Y, 0, 0, 0, 1, color, 0, 0 );
vertices[2] = S3DVertex(( f32 )pos.LowerRightCorner.X, ( f32 )pos.LowerRightCorner.Y, 0, 0, 0, 1, color, 0, 0 );
vertices[3] = S3DVertex(( f32 )pos.UpperLeftCorner.X, ( f32 )pos.LowerRightCorner.Y, 0, 0, 0, 1, color, 0, 0 );
drawVertexPrimitiveList2d3d( vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false );
}
//! draw an 2d rectangle
void COGLES2Driver::draw2DRectangle( const core::rect<s32>& position,
SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown,
const core::rect<s32>* clip )
{
core::rect<s32> pos = position;
if ( clip )
pos.clipAgainst( *clip );
if ( !pos.isValid() )
return;
disableTextures();
setRenderStates2DMode( colorLeftUp.getAlpha() < 255 ||
colorRightUp.getAlpha() < 255 ||
colorLeftDown.getAlpha() < 255 ||
colorRightDown.getAlpha() < 255, false, false );
u16 indices[] = {0, 1, 2, 3};
S3DVertex vertices[4];
vertices[0] = S3DVertex(( f32 )pos.UpperLeftCorner.X, ( f32 )pos.UpperLeftCorner.Y, 0, 0, 0, 1, colorLeftUp, 0, 0 );
vertices[1] = S3DVertex(( f32 )pos.LowerRightCorner.X, ( f32 )pos.UpperLeftCorner.Y, 0, 0, 0, 1, colorRightUp, 0, 0 );
vertices[2] = S3DVertex(( f32 )pos.LowerRightCorner.X, ( f32 )pos.LowerRightCorner.Y, 0, 0, 0, 1, colorRightDown, 0, 0 );
vertices[3] = S3DVertex(( f32 )pos.UpperLeftCorner.X, ( f32 )pos.LowerRightCorner.Y, 0, 0, 0, 1, colorLeftDown, 0, 0 );
drawVertexPrimitiveList2d3d( vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false );
}
//! Draws a 2d line.
void COGLES2Driver::draw2DLine( const core::position2d<s32>& start,
const core::position2d<s32>& end,
SColor color )
{
disableTextures();
setRenderStates2DMode( color.getAlpha() < 255, false, false );
u16 indices[] = {0, 1};
S3DVertex vertices[2];
vertices[0] = S3DVertex(( f32 )start.X, ( f32 )start.Y, 0, 0, 0, 1, color, 0, 0 );
vertices[1] = S3DVertex(( f32 )end.X, ( f32 )end.Y, 0, 0, 0, 1, color, 1, 1 );
drawVertexPrimitiveList2d3d( vertices, 2, indices, 1, video::EVT_STANDARD, scene::EPT_LINES, EIT_16BIT, false );
}
bool COGLES2Driver::setTexture( u32 stage, const video::ITexture* texture )
{
if ( stage >= MaxTextureUnits )
return false;
if ( CurrentTexture[stage] == texture )
return true;
glActiveTexture( GL_TEXTURE0 + stage );
CurrentTexture[stage] = texture;
if ( !texture )
{
return true;
}
else
{
if ( texture->getDriverType() != EDT_OGLES2 )
{
os::Printer::log( "Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR );
return false;
}
glBindTexture( GL_TEXTURE_2D,
static_cast<const COGLES2Texture*>( texture )->getOGLES2TextureName() );
}
testGLError();
return true;
}
//! disables all textures beginning with the optional fromStage parameter.
bool COGLES2Driver::disableTextures( u32 fromStage )
{
bool result = true;
for ( u32 i = fromStage; i < MaxTextureUnits; ++i )
result &= setTexture( i, 0 );
return result;
}
//! creates a matrix in supplied GLfloat array to pass to OGLES1
inline void COGLES2Driver::createGLMatrix( float gl_matrix[16], const core::matrix4& m )
{
memcpy( gl_matrix, m.pointer(), 16 * sizeof( f32 ) );
}
//! creates a opengltexturematrix from a D3D style texture matrix
inline void COGLES2Driver::createGLTextureMatrix( float *o, const core::matrix4& m )
{
o[0] = m[0];
o[1] = m[1];
o[2] = 0.f;
o[3] = 0.f;
o[4] = m[4];
o[5] = m[5];
o[6] = 0.f;
o[7] = 0.f;
o[8] = 0.f;
o[9] = 0.f;
o[10] = 1.f;
o[11] = 0.f;
o[12] = m[8];
o[13] = m[9];
o[14] = 0.f;
o[15] = 1.f;
}
//! returns a device dependent texture from a software surface (IImage)
video::ITexture* COGLES2Driver::createDeviceDependentTexture(IImage* surface, const io::path& name, void* mipmapData)
{
return new COGLES2Texture( surface, name, this );
}
//! Sets a material.
void COGLES2Driver::setMaterial( const SMaterial& material )
{
Material = material;
OverrideMaterial.apply( Material );
for ( s32 i = MaxTextureUnits - 1; i >= 0; --i )
{
setTexture( i, Material.getTexture( i ) );
setTransform(( E_TRANSFORMATION_STATE )( ETS_TEXTURE_0 + i ),
Material.getTextureMatrix( i ) );
}
}
//! prints error if an error happened.
bool COGLES2Driver::testGLError()
{
#ifdef _DEBUG
GLenum g = glGetError();
switch ( g )
{
case GL_NO_ERROR:
return false;
case GL_INVALID_ENUM:
os::Printer::log( "GL_INVALID_ENUM", ELL_ERROR );
break;
case GL_INVALID_VALUE:
os::Printer::log( "GL_INVALID_VALUE", ELL_ERROR );
break;
case GL_INVALID_OPERATION:
os::Printer::log( "GL_INVALID_OPERATION", ELL_ERROR );
break;
case GL_OUT_OF_MEMORY:
os::Printer::log( "GL_OUT_OF_MEMORY", ELL_ERROR );
break;
};
return true;
#else
return false;
#endif
}
bool COGLES2Driver::testEGLError()
{
#if defined(EGL_VERSION_1_0) && defined(_DEBUG)
EGLint g = eglGetError();
switch ( g )
{
case EGL_SUCCESS:
return false;
case EGL_NOT_INITIALIZED :
os::Printer::log( "Not Initialized", ELL_ERROR );
break;
case EGL_BAD_ACCESS:
os::Printer::log( "Bad Access", ELL_ERROR );
break;
case EGL_BAD_ALLOC:
os::Printer::log( "Bad Alloc", ELL_ERROR );
break;
case EGL_BAD_ATTRIBUTE:
os::Printer::log( "Bad Attribute", ELL_ERROR );
break;
case EGL_BAD_CONTEXT:
os::Printer::log( "Bad Context", ELL_ERROR );
break;
case EGL_BAD_CONFIG:
os::Printer::log( "Bad Config", ELL_ERROR );
break;
case EGL_BAD_CURRENT_SURFACE:
os::Printer::log( "Bad Current Surface", ELL_ERROR );
break;
case EGL_BAD_DISPLAY:
os::Printer::log( "Bad Display", ELL_ERROR );
break;
case EGL_BAD_SURFACE:
os::Printer::log( "Bad Surface", ELL_ERROR );
break;
case EGL_BAD_MATCH:
os::Printer::log( "Bad Match", ELL_ERROR );
break;
case EGL_BAD_PARAMETER:
os::Printer::log( "Bad Parameter", ELL_ERROR );
break;
case EGL_BAD_NATIVE_PIXMAP:
os::Printer::log( "Bad Native Pixmap", ELL_ERROR );
break;
case EGL_BAD_NATIVE_WINDOW:
os::Printer::log( "Bad Native Window", ELL_ERROR );
break;
case EGL_CONTEXT_LOST:
os::Printer::log( "Context Lost", ELL_ERROR );
break;
};
return true;
#else
return false;
#endif
}
//! sets the needed renderstates
void COGLES2Driver::setRenderStates3DMode()
{
if ( CurrentRenderMode != ERM_3D )
{
// Reset Texture Stages
if ( BlendEnabled )
{
glDisable( GL_BLEND );
BlendEnabled = false;
}
ResetRenderStates = true;
}
if ( ResetRenderStates || LastMaterial != Material )
{
// unset old material
if ( LastMaterial.MaterialType != Material.MaterialType &&
static_cast<u32>( LastMaterial.MaterialType ) < MaterialRenderers.size() )
MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial();
// set new material.
if ( static_cast<u32>( Material.MaterialType ) < MaterialRenderers.size() )
MaterialRenderers[Material.MaterialType].Renderer->OnSetMaterial(
Material, LastMaterial, ResetRenderStates, this );
LastMaterial = Material;
ResetRenderStates = false;
}
if ( static_cast<u32>( Material.MaterialType ) < MaterialRenderers.size() )
MaterialRenderers[Material.MaterialType].Renderer->OnRender( this, video::EVT_STANDARD );
testGLError();
CurrentRenderMode = ERM_3D;
}
GLint COGLES2Driver::getTextureWrapMode(u8 clamp) const
{
switch (clamp)
{
case ETC_CLAMP:
// mode=GL_CLAMP; not supported in ogl-es
return GL_CLAMP_TO_EDGE;
case ETC_CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
case ETC_CLAMP_TO_BORDER:
// mode=GL_CLAMP_TO_BORDER; not supported in ogl-es
return GL_CLAMP_TO_EDGE;
case ETC_MIRROR:
#ifdef GL_OES_texture_mirrored_repeat
if ( FeatureAvailable[IRR_OES_texture_mirrored_repeat] )
return GL_MIRRORED_REPEAT_OES;
else
#endif
return GL_REPEAT;
default:
return GL_REPEAT;
}
}
void COGLES2Driver::setWrapMode( const SMaterial& material )
{
testGLError();
// texture address mode
// Has to be checked always because it depends on the textures
for ( u32 u = 0; u < MaxTextureUnits; ++u )
{
if (MultiTextureExtension)
glActiveTexture(GL_TEXTURE0 + u);
else if (u>0)
break; // stop loop
// the APPLE npot restricted extension needs some care as it only supports CLAMP_TO_EDGE
if (queryFeature(EVDF_TEXTURE_NPOT) && !FeatureAvailable[IRR_OES_texture_npot] &&
CurrentTexture[u] && (CurrentTexture[u]->getSize() != CurrentTexture[u]->getOriginalSize()))
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getTextureWrapMode(material.TextureLayer[u].TextureWrapU));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getTextureWrapMode(material.TextureLayer[u].TextureWrapV));
}
}
}
//! Can be called by an IMaterialRenderer to make its work easier.
void COGLES2Driver::setBasicRenderStates( const SMaterial& material, const SMaterial& lastmaterial,
bool resetAllRenderStates )
{
testGLError();
// Texture filter
// Has to be checked always because it depends on the textures
// Filtering has to be set for each texture layer
for ( u32 i = 0; i < MaxTextureUnits; ++i )
{
//Thibault : strange Blue artifact on textures in exemple 02
glActiveTexture( GL_TEXTURE0 + i );
if ( material.TextureLayer[i].BilinearFilter || material.TextureLayer[i].TrilinearFilter )
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
else
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
if ( material.getTexture( i ) && material.getTexture( i )->hasMipMaps() )
{
if ( material.TextureLayer[i].TrilinearFilter )
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
}
else if ( material.TextureLayer[i].BilinearFilter )
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
}
else
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
}
}
else if ( material.TextureLayer[i].BilinearFilter || material.TextureLayer[i].TrilinearFilter )
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
}
else
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
}
#ifdef GL_EXT_texture_filter_anisotropic
if ( FeatureAvailable[IRR_EXT_texture_filter_anisotropic] )
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
static_cast<GLfloat>( material.TextureLayer[i].AnisotropicFilter > 1 ? core::min_( MaxAnisotropy, material.TextureLayer[i].AnisotropicFilter ) : 1 ) );
#endif
}
testGLError();
// TODO ogl-es
// fillmode
// if (resetAllRenderStates || (lastmaterial.Wireframe != material.Wireframe) || (lastmaterial.PointCloud != material.PointCloud))
// glPolygonMode(GL_FRONT_AND_BACK, material.Wireframe ? GL_LINE : material.PointCloud? GL_POINT : GL_FILL);
// shademode
if ( resetAllRenderStates || ( lastmaterial.GouraudShading != material.GouraudShading ) )
{
//TODO : OpenGL ES 2.0 Port glShadeModel
//if (material.GouraudShading)
// glShadeModel(GL_SMOOTH);
//else
// glShadeModel(GL_FLAT);
}
testGLError();
// zbuffer
if ( resetAllRenderStates || lastmaterial.ZBuffer != material.ZBuffer )
{
switch ( material.ZBuffer )
{
case ECFN_NEVER:
glDisable( GL_DEPTH_TEST );
break;
case ECFN_LESSEQUAL:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LEQUAL );
break;
case ECFN_EQUAL:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_EQUAL );
break;
case ECFN_LESS:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );
break;
case ECFN_NOTEQUAL:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_NOTEQUAL );
break;
case ECFN_GREATEREQUAL:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_GEQUAL );
break;
case ECFN_GREATER:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_GREATER );
break;
case ECFN_ALWAYS:
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_ALWAYS );
break;
}
}
testGLError();
// zwrite
// if (resetAllRenderStates || lastmaterial.ZWriteEnable != material.ZWriteEnable)
{
if ( material.ZWriteEnable && ( AllowZWriteOnTransparent || !material.isTransparent() ) )
{
glDepthMask( GL_TRUE );
}
else
glDepthMask( GL_FALSE );
}
// back face culling
if ( resetAllRenderStates || ( lastmaterial.FrontfaceCulling != material.FrontfaceCulling ) || ( lastmaterial.BackfaceCulling != material.BackfaceCulling ) )
{
if (( material.FrontfaceCulling ) && ( material.BackfaceCulling ) )
{
glCullFace( GL_FRONT_AND_BACK );
glEnable( GL_CULL_FACE );
}
else if ( material.BackfaceCulling )
{
glCullFace( GL_BACK );
glEnable( GL_CULL_FACE );
}
else if ( material.FrontfaceCulling )
{
glCullFace( GL_FRONT );
glEnable( GL_CULL_FACE );
}
else
glDisable( GL_CULL_FACE );
}
testGLError();
// Color Mask
if ( resetAllRenderStates || lastmaterial.ColorMask != material.ColorMask )
{
glColorMask(
( material.ColorMask & ECP_RED ) ? GL_TRUE : GL_FALSE,
( material.ColorMask & ECP_GREEN ) ? GL_TRUE : GL_FALSE,
( material.ColorMask & ECP_BLUE ) ? GL_TRUE : GL_FALSE,
( material.ColorMask & ECP_ALPHA ) ? GL_TRUE : GL_FALSE );
}
testGLError();
// thickness
if ( resetAllRenderStates || lastmaterial.Thickness != material.Thickness )
{
//TODO : OpenGL ES 2.0 Port glPointSize
//glPointSize(material.Thickness);
glLineWidth( material.Thickness == 0 ? 1 : material.Thickness );
//glLineWidth with 0 generate GL_INVALID_VALUE on real hardware.
}
testGLError();
// Anti aliasing
if ( resetAllRenderStates || lastmaterial.AntiAliasing != material.AntiAliasing )
{
// if (FeatureAvailable[IRR_ARB_multisample])
{
if ( material.AntiAliasing & EAAM_ALPHA_TO_COVERAGE )
glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE );
else if ( lastmaterial.AntiAliasing & EAAM_ALPHA_TO_COVERAGE )
glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE );
//TODO : OpenGL ES 2.0 Port GL_MULTISAMPLE
//if ((AntiAlias >= 2) && (material.AntiAliasing & (EAAM_SIMPLE|EAAM_QUALITY)))
// glEnable(GL_MULTISAMPLE);
//else
// glDisable(GL_MULTISAMPLE);
}
if ( AntiAlias >= 2 )
{
//TODO : OpenGL ES 2.0 Port GL_LINE_SMOOTH
//if (material.AntiAliasing & EAAM_LINE_SMOOTH)
// glEnable(GL_LINE_SMOOTH);
//else if (lastmaterial.AntiAliasing & EAAM_LINE_SMOOTH)
// glDisable(GL_LINE_SMOOTH);
//if (material.AntiAliasing & EAAM_POINT_SMOOTH)
// // often in software, and thus very slow
// glEnable(GL_POINT_SMOOTH);
//else if (lastmaterial.AntiAliasing & EAAM_POINT_SMOOTH)
// glDisable(GL_POINT_SMOOTH);
}
}
testGLError();
setWrapMode( material );
//Thibault : Strange blue Artifact in exemple 01
glActiveTexture( GL_TEXTURE0 );
testGLError();
}
//! sets the needed renderstates
void COGLES2Driver::setRenderStates2DMode( bool alpha, bool texture, bool alphaChannel )
{
if ( CurrentRenderMode != ERM_2D || Transformation3DChanged )
{
// unset last 3d material
if ( CurrentRenderMode == ERM_3D )
{
if ( static_cast<u32>( LastMaterial.MaterialType ) < MaterialRenderers.size() )
MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial();
SMaterial mat;
mat.ZBuffer = ECFN_NEVER;
mat.Lighting = false;
mat.TextureLayer[0].BilinearFilter = false;
mat.ColorMaterial = 0;
setBasicRenderStates( mat, mat, true );
LastMaterial = mat;
}
TwoDRenderer->useProgram(); //Fixed Pipeline Shader needed to render 2D
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
core::matrix4 m;
m.buildProjectionMatrixOrthoLH( f32( renderTargetSize.Width ), f32( -( s32 )( renderTargetSize.Height ) ), -1.0, 1.0 );
m.setTranslation( core::vector3df( -1, 1, 0 ) );
TwoDRenderer->setOrthoMatrix( m );
Transformation3DChanged = false;
}
if ( alphaChannel || alpha )
{
if ( ! BlendEnabled )
{
glEnable( GL_BLEND );
BlendEnabled = true;
}
blendFunc( EBF_SRC_ALPHA, EBF_ONE_MINUS_SRC_ALPHA );
TwoDRenderer->useAlphaTest( true );
TwoDRenderer->setAlphaTestValue( 0.f );
}
else
{
if ( BlendEnabled )
{
glDisable( GL_BLEND );
BlendEnabled = false;
}
TwoDRenderer->useAlphaTest( false );
}
if ( texture )
{
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_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
TwoDRenderer->useTexture( true );
}
else
TwoDRenderer->useTexture( false );
CurrentRenderMode = ERM_2D;
testGLError();
}
//! \return Returns the name of the video driver.
const wchar_t* COGLES2Driver::getName() const
{
return Name.c_str();
}
//! deletes all dynamic lights there are
void COGLES2Driver::deleteAllDynamicLights()
{
RequestedLights.clear();
CNullDriver::deleteAllDynamicLights();
}
//! adds a dynamic light
s32 COGLES2Driver::addDynamicLight( const SLight& light )
{
CNullDriver::addDynamicLight( light );
RequestedLights.push_back( RequestedLight( light ) );
u32 newLightIndex = RequestedLights.size() - 1;
return ( s32 )newLightIndex;
}
//! Turns a dynamic light on or off
//! \param lightIndex: the index returned by addDynamicLight
//! \param turnOn: true to turn the light on, false to turn it off
void COGLES2Driver::turnLightOn( s32 lightIndex, bool turnOn )
{
if ( lightIndex < 0 || lightIndex >= ( s32 )RequestedLights.size() )
return;
RequestedLight & requestedLight = RequestedLights[lightIndex];
requestedLight.DesireToBeOn = turnOn;
}
//! returns the maximal amount of dynamic lights the device can handle
u32 COGLES2Driver::getMaximalDynamicLightAmount() const
{
return MaxLights;
}
//! Sets the dynamic ambient light color.
void COGLES2Driver::setAmbientLight( const SColorf& color )
{
AmbientLight = color;
}
//! returns the dynamic ambient light color.
const SColorf& COGLES2Driver::getAmbientLight() const
{
return AmbientLight;
}
// this code was sent in by Oliver Klems, thank you
void COGLES2Driver::setViewPort( const core::rect<s32>& area )
{
core::rect<s32> vp = area;
core::rect<s32> rendert( 0, 0, getCurrentRenderTargetSize().Width, getCurrentRenderTargetSize().Height );
vp.clipAgainst( rendert );
if ( vp.getHeight() > 0 && vp.getWidth() > 0 )
glViewport( vp.UpperLeftCorner.X,
getCurrentRenderTargetSize().Height - vp.UpperLeftCorner.Y - vp.getHeight(),
vp.getWidth(), vp.getHeight() );
ViewPort = vp;
testGLError();
}
//! Draws a shadow volume into the stencil buffer.
void COGLES2Driver::drawStencilShadowVolume( const core::vector3df* triangles, s32 count, bool zfail )
{
if ( !StencilBuffer || !count )
return;
// unset last 3d material
if ( CurrentRenderMode == ERM_3D &&
static_cast<u32>( Material.MaterialType ) < MaterialRenderers.size() )
{
MaterialRenderers[Material.MaterialType].Renderer->OnUnsetMaterial();
ResetRenderStates = true;
}
// store current OGLES state
const GLboolean cullFaceEnabled = glIsEnabled( GL_CULL_FACE );
GLint cullFaceMode;
glGetIntegerv( GL_CULL_FACE_MODE, &cullFaceMode );
GLint depthFunc;
glGetIntegerv( GL_DEPTH_FUNC, &depthFunc );
GLboolean depthMask;
glGetBooleanv( GL_DEPTH_WRITEMASK, &depthMask );
glDepthFunc( GL_LEQUAL );
glDepthMask( GL_FALSE ); // no depth buffer writing
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); // no color buffer drawing
glEnable( GL_STENCIL_TEST );
glEnable( GL_POLYGON_OFFSET_FILL );
glPolygonOffset( 0.0f, 1.0f );
glEnableVertexAttribArray( EVA_POSITION );
glVertexAttribPointer( EVA_POSITION, 3, GL_FLOAT, false, sizeof( core::vector3df ), &triangles[0] );
glStencilMask( ~0 );
glStencilFunc( GL_ALWAYS, 0, ~0 );
GLenum decr = GL_DECR;
GLenum incr = GL_INCR;
#if defined(GL_OES_stencil_wrap)
if ( FeatureAvailable[IRR_OES_stencil_wrap] )
{
decr = GL_DECR_WRAP_OES;
incr = GL_INCR_WRAP_OES;
}
#endif
glEnable( GL_CULL_FACE );
if ( !zfail )
{
// ZPASS Method
glCullFace( GL_BACK );
glStencilOp( GL_KEEP, GL_KEEP, incr );
glDrawArrays( GL_TRIANGLES, 0, count );
glCullFace( GL_FRONT );
glStencilOp( GL_KEEP, GL_KEEP, decr );
glDrawArrays( GL_TRIANGLES, 0, count );
}
else
{
// ZFAIL Method
glStencilOp( GL_KEEP, incr, GL_KEEP );
glCullFace( GL_FRONT );
glDrawArrays( GL_TRIANGLES, 0, count );
glStencilOp( GL_KEEP, decr, GL_KEEP );
glCullFace( GL_BACK );
glDrawArrays( GL_TRIANGLES, 0, count );
}
glDisableVertexAttribArray( EVA_POSITION );
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
glDisable( GL_STENCIL_TEST );
if ( cullFaceEnabled )
glEnable( GL_CULL_FACE );
else
glDisable( GL_CULL_FACE );
glCullFace( cullFaceMode );
glDepthFunc( depthFunc );
glDepthMask( depthMask );
testGLError();
}
void COGLES2Driver::drawStencilShadow( bool clearStencilBuffer, video::SColor leftUpEdge,
video::SColor rightUpEdge, video::SColor leftDownEdge, video::SColor rightDownEdge )
{
if ( !StencilBuffer )
return;
disableTextures();
// store attributes
GLboolean depthMask;
glGetBooleanv( GL_DEPTH_WRITEMASK, &depthMask );
// GLint shadeModel;
//TODO : OpenGL ES 2.0 Port glGetIntegerv
//glGetIntegerv(GL_SHADE_MODEL, &shadeModel);
// GLint blendSrc, blendDst;
//TODO : OpenGL ES 2.0 Port glGetIntegerv
//glGetIntegerv(GL_BLEND_SRC, &blendSrc);
//glGetIntegerv(GL_BLEND_DST, &blendDst);
glDepthMask( GL_FALSE );
//TODO : OpenGL ES 2.0 Port glShadeModel
//glShadeModel( GL_FLAT );
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
if ( ! BlendEnabled )
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_STENCIL_TEST );
glStencilFunc( GL_NOTEQUAL, 0, ~0 );
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
// draw a shadow rectangle covering the entire screen using stencil buffer
//Wrapper->glMatrixMode(GL_MODELVIEW);
//TODO : OpenGL ES 2.0 Port glPushMatrix
//glPushMatrix();
//Wrapper->glLoadIdentity();
//Wrapper->glMatrixMode(GL_PROJECTION);
//TODO : OpenGL ES 2.0 Port glPushMatrix
//glPushMatrix();
//Wrapper->glLoadIdentity();
u16 indices[] = {0, 1, 2, 3};
S3DVertex vertices[4];
vertices[0] = S3DVertex( -1.f, -1.f, 0.9f, 0, 0, 1, leftDownEdge, 0, 0 );
vertices[1] = S3DVertex( -1.f, 1.f, 0.9f, 0, 0, 1, leftUpEdge, 0, 0 );
vertices[2] = S3DVertex( 1.f, 1.f, 0.9f, 0, 0, 1, rightUpEdge, 0, 0 );
vertices[3] = S3DVertex( 1.f, -1.f, 0.9f, 0, 0, 1, rightDownEdge, 0, 0 );
drawVertexPrimitiveList2d3d( vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false );
if ( clearStencilBuffer )
glClear( GL_STENCIL_BUFFER_BIT );
// restore settings
//TODO : OpenGL ES 2.0 Port glPopMatrix
//glPopMatrix();
//Wrapper->glMatrixMode(GL_MODELVIEW);
//TODO : OpenGL ES 2.0 Port glPopMatrix
//glPopMatrix();
glDisable( GL_STENCIL_TEST );
glDepthMask( depthMask );
//TODO : OpenGL ES 2.0 Port glShadeModel
//glShadeModel(shadeModel);
if ( ! BlendEnabled )
glDisable( GL_BLEND );
//TODO :
//glBlendFunc(blendSrc, blendDst);
testGLError();
}
//! Draws a 3d line.
void COGLES2Driver::draw3DLine( const core::vector3df& start,
const core::vector3df& end, SColor color )
{
setRenderStates3DMode();
u16 indices[] = {0, 1};
S3DVertex vertices[2];
vertices[0] = S3DVertex( start.X, start.Y, start.Z, 0, 0, 1, color, 0, 0 );
vertices[1] = S3DVertex( end.X, end.Y, end.Z, 0, 0, 1, color, 0, 0 );
drawVertexPrimitiveList2d3d( vertices, 2, indices, 1, video::EVT_STANDARD, scene::EPT_LINES );
}
//! Only used by the internal engine. Used to notify the driver that
//! the window was resized.
void COGLES2Driver::OnResize( const core::dimension2d<u32>& size )
{
CNullDriver::OnResize( size );
glViewport( 0, 0, size.Width, size.Height );
testGLError();
}
//! Returns type of video driver
E_DRIVER_TYPE COGLES2Driver::getDriverType() const
{
return EDT_OGLES2;
}
//! returns color format
ECOLOR_FORMAT COGLES2Driver::getColorFormat() const
{
return ColorFormat;
}
//! Sets a vertex shader constant.
void COGLES2Driver::setVertexShaderConstant( const f32* data, s32 startRegister, s32 constantAmount )
{
#ifdef GL_vertex_program
for ( s32 i = 0; i < constantAmount; ++i )
glProgramLocalParameter4fv( GL_VERTEX_PROGRAM, startRegister + i, &data[i*4] );
#endif
}
//! Sets a pixel shader constant.
void COGLES2Driver::setPixelShaderConstant( const f32* data, s32 startRegister, s32 constantAmount )
{
#ifdef GL_fragment_program
for ( s32 i = 0; i < constantAmount; ++i )
glProgramLocalParameter4fv( GL_FRAGMENT_PROGRAM, startRegister + i, &data[i*4] );
#endif
}
//! Sets a constant for the vertex shader based on a name.
bool COGLES2Driver::setVertexShaderConstant( const c8* name, const f32* floats, int count )
{
//pass this along, as in GLSL the same routine is used for both vertex and fragment shaders
return setPixelShaderConstant( name, floats, count );
}
//! Sets a constant for the pixel shader based on a name.
bool COGLES2Driver::setPixelShaderConstant( const c8* name, const f32* floats, int count )
{
os::Printer::log( "Error: Please call services->setPixelShaderConstant(), not VideoDriver->setPixelShaderConstant()." );
return false;
}
//! Sets a vertex pointer the vertex shader based on a name.
bool COGLES2Driver::setVertexShaderPointer( const c8*, const void*, s32, bool, u16 )
{
os::Printer::log( "Error: Please call services->setVertexPointer(), not VideoDriver->setVertexPointer()." );
return false;
}
//! Adds a new material renderer to the VideoDriver, using pixel and/or
//! vertex shaders to render geometry.
s32 COGLES2Driver::addShaderMaterial( const c8* vertexShaderProgram,
const c8* pixelShaderProgram,
IShaderConstantSetCallBack* callback,
E_MATERIAL_TYPE baseMaterial, s32 userData )
{
os::Printer::log( "No shader support." );
return -1;
}
//! Adds a new material renderer to the VideoDriver, using GLSL to render geometry.
s32 COGLES2Driver::addHighLevelShaderMaterial(
const c8* vertexShaderProgram,
const c8* vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8* pixelShaderProgram,
const c8* pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8* geometryShaderProgram,
const c8* geometryShaderEntryPointName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType,
scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut,
IShaderConstantSetCallBack* callback,
E_MATERIAL_TYPE baseMaterial,
s32 userData)
{
s32 nr = -1;
COGLES2SLMaterialRenderer* r = new COGLES2SLMaterialRenderer(
this, nr, vertexShaderProgram,
pixelShaderProgram,
callback, getMaterialRenderer( baseMaterial ), userData );
r->drop();
return nr;
}
//! Returns a pointer to the IVideoDriver interface. (Implementation for
//! IMaterialRendererServices)
IVideoDriver* COGLES2Driver::getVideoDriver()
{
return this;
}
//! Returns pointer to the IGPUProgrammingServices interface.
IGPUProgrammingServices* COGLES2Driver::getGPUProgrammingServices()
{
return this;
}
ITexture* COGLES2Driver::addRenderTargetTexture( const core::dimension2d<u32>& size,
const io::path& name,
const ECOLOR_FORMAT format )
{
//disable mip-mapping
const bool generateMipLevels = getTextureCreationFlag( ETCF_CREATE_MIP_MAPS );
setTextureCreationFlag( ETCF_CREATE_MIP_MAPS, false );
video::ITexture* rtt = 0;
#if defined(GL_OES_framebuffer_object)
// if driver supports FrameBufferObjects, use them
if ( queryFeature( EVDF_FRAMEBUFFER_OBJECT ) )
{
rtt = new COGLES2FBOTexture( size, name, this, format );
if ( rtt )
{
addTexture( rtt );
ITexture* tex = createDepthTexture( rtt );
if ( tex )
{
static_cast<video::COGLES2FBODepthTexture*>( tex )->attach( rtt );
tex->drop();
}
rtt->drop();
}
}
else
#endif
{
// the simple texture is only possible for size <= screensize
// we try to find an optimal size with the original constraints
core::dimension2du destSize( core::min_( size.Width, ScreenSize.Width ), core::min_( size.Height, ScreenSize.Height ) );
destSize = destSize.getOptimalSize(( size == size.getOptimalSize() ), false, false );
rtt = addTexture( destSize, name, ECF_A8R8G8B8 );
if ( rtt )
static_cast<video::COGLES2Texture*>( rtt )->setIsRenderTarget( true );
}
//restore mip-mapping
setTextureCreationFlag( ETCF_CREATE_MIP_MAPS, generateMipLevels );
return rtt;
}
//! Returns the maximum amount of primitives
u32 COGLES2Driver::getMaximalPrimitiveCount() const
{
return 65535;
}
//! set or reset render target
bool COGLES2Driver::setRenderTarget( video::ITexture* texture, bool clearBackBuffer,
bool clearZBuffer, SColor color )
{
// check for right driver type
if ( texture && texture->getDriverType() != EDT_OGLES2 )
{
os::Printer::log( "Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR );
return false;
}
// check if we should set the previous RT back
setTexture( 0, 0 );
ResetRenderStates = true;
if ( RenderTargetTexture != 0 )
{
RenderTargetTexture->unbindRTT();
}
if ( texture )
{
// we want to set a new target. so do this.
RenderTargetTexture = static_cast<COGLES2Texture*>( texture );
RenderTargetTexture->bindRTT();
CurrentRendertargetSize = texture->getSize();
}
else
{
glViewport( 0, 0, ScreenSize.Width, ScreenSize.Height );
RenderTargetTexture = 0;
CurrentRendertargetSize = core::dimension2d<u32>( 0, 0 );
}
GLbitfield mask = 0;
if ( clearBackBuffer )
{
const f32 inv = 1.0f / 255.0f;
glClearColor( color.getRed() * inv, color.getGreen() * inv,
color.getBlue() * inv, color.getAlpha() * inv );
mask |= GL_COLOR_BUFFER_BIT;
}
if ( clearZBuffer )
{
glDepthMask( GL_TRUE );
LastMaterial.ZWriteEnable = true;
mask |= GL_DEPTH_BUFFER_BIT;
}
glClear( mask );
testGLError();
return true;
}
// returns the current size of the screen or rendertarget
const core::dimension2d<u32>& COGLES2Driver::getCurrentRenderTargetSize() const
{
if ( CurrentRendertargetSize.Width == 0 )
return ScreenSize;
else
return CurrentRendertargetSize;
}
//! Clears the ZBuffer.
void COGLES2Driver::clearZBuffer()
{
GLboolean enabled = GL_TRUE;
glGetBooleanv( GL_DEPTH_WRITEMASK, &enabled );
glDepthMask( GL_TRUE );
glClear( GL_DEPTH_BUFFER_BIT );
glDepthMask( enabled );
testGLError();
}
//! Returns an image created from the last rendered frame.
// We want to read the front buffer to get the latest render finished.
// This is not possible under ogl-es, though, so one has to call this method
// outside of the render loop only.
IImage* COGLES2Driver::createScreenShot()
{
int format = GL_RGBA;
int type = GL_UNSIGNED_BYTE;
if ( FeatureAvailable[IRR_IMG_read_format] || FeatureAvailable[IRR_OES_read_format] )
{
#ifdef GL_IMPLEMENTATION_COLOR_READ_TYPE_OES
glGetIntegerv( GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, &format );
glGetIntegerv( GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, &type );
#endif
// there's a format we don't support ATM
if ( GL_UNSIGNED_SHORT_4_4_4_4 == type )
type = GL_UNSIGNED_SHORT_5_5_5_1;
}
IImage* newImage = 0;
if ( GL_RGBA == format )
{
if ( GL_UNSIGNED_BYTE == type )
newImage = new CImage( ECF_A8R8G8B8, ScreenSize );
else
newImage = new CImage( ECF_A1R5G5B5, ScreenSize );
}
else
{
if ( GL_UNSIGNED_BYTE == type )
newImage = new CImage( ECF_R8G8B8, ScreenSize );
else
newImage = new CImage( ECF_R5G6B5, ScreenSize );
}
u8* pixels = static_cast<u8*>( newImage->lock() );
if ( !pixels )
{
newImage->drop();
return 0;
}
glReadPixels( 0, 0, ScreenSize.Width, ScreenSize.Height, format, type, pixels );
// opengl images are horizontally flipped, so we have to fix that here.
const s32 pitch = newImage->getPitch();
u8* p2 = pixels + ( ScreenSize.Height - 1 ) * pitch;
u8* tmpBuffer = new u8[pitch];
for ( u32 i = 0; i < ScreenSize.Height; i += 2 )
{
memcpy( tmpBuffer, pixels, pitch );
memcpy( pixels, p2, pitch );
memcpy( p2, tmpBuffer, pitch );
pixels += pitch;
p2 -= pitch;
}
delete [] tmpBuffer;
newImage->unlock();
if ( testGLError() )
{
newImage->drop();
return 0;
}
testGLError();
return newImage;
}
//! get depth texture for the given render target texture
ITexture* COGLES2Driver::createDepthTexture( ITexture* texture, bool shared )
{
if (( texture->getDriverType() != EDT_OGLES2 ) || ( !texture->isRenderTarget() ) )
return 0;
COGLES2Texture* tex = static_cast<COGLES2Texture*>( texture );
if ( !tex->isFrameBufferObject() )
return 0;
if ( shared )
{
for ( u32 i = 0; i < DepthTextures.size(); ++i )
{
if ( DepthTextures[i]->getSize() == texture->getSize() )
{
DepthTextures[i]->grab();
return DepthTextures[i];
}
}
DepthTextures.push_back( new COGLES2FBODepthTexture( texture->getSize(), "depth1", this ) );
return DepthTextures.getLast();
}
return ( new COGLES2FBODepthTexture( texture->getSize(), "depth1", this ) );
}
void COGLES2Driver::removeDepthTexture( ITexture* texture )
{
for ( u32 i = 0; i < DepthTextures.size(); ++i )
{
if ( texture == DepthTextures[i] )
{
DepthTextures.erase( i );
return;
}
}
}
void COGLES2Driver::reloadShaders()
{
FixedPipeline->reload();
}
void COGLES2Driver::deleteFramebuffers( s32 n, const u32 *framebuffers )
{
glDeleteFramebuffers( n, framebuffers );
}
void COGLES2Driver::deleteRenderbuffers( s32 n, const u32 *renderbuffers )
{
glDeleteRenderbuffers( n, renderbuffers );
}
void COGLES2Driver::enableBlend()
{
if ( ! BlendEnabled )
{
BlendEnabled = true;
glEnable( GL_BLEND );
}
}
void COGLES2Driver::disableBlend()
{
if ( BlendEnabled )
{
BlendEnabled = false;
glDisable( GL_BLEND );
}
}
u32 getGLBlend( E_BLEND_FACTOR factor )
{
u32 r = 0;
switch ( factor )
{
case EBF_ZERO:
r = GL_ZERO;
break;
case EBF_ONE:
r = GL_ONE;
break;
case EBF_DST_COLOR:
r = GL_DST_COLOR;
break;
case EBF_ONE_MINUS_DST_COLOR:
r = GL_ONE_MINUS_DST_COLOR;
break;
case EBF_SRC_COLOR:
r = GL_SRC_COLOR;
break;
case EBF_ONE_MINUS_SRC_COLOR:
r = GL_ONE_MINUS_SRC_COLOR;
break;
case EBF_SRC_ALPHA:
r = GL_SRC_ALPHA;
break;
case EBF_ONE_MINUS_SRC_ALPHA:
r = GL_ONE_MINUS_SRC_ALPHA;
break;
case EBF_DST_ALPHA:
r = GL_DST_ALPHA;
break;
case EBF_ONE_MINUS_DST_ALPHA:
r = GL_ONE_MINUS_DST_ALPHA;
break;
case EBF_SRC_ALPHA_SATURATE:
r = GL_SRC_ALPHA_SATURATE;
break;
}
return r;
}
void COGLES2Driver::blendFunc( E_BLEND_FACTOR sFactor, E_BLEND_FACTOR dFactor )
{
if ( sFactor != SourceFactor || dFactor != DestFactor )
{
SourceFactor = sFactor;
DestFactor = dFactor;
glBlendFunc( getGLBlend( sFactor ), getGLBlend( dFactor ) );
}
}
//! Set/unset a clipping plane.
bool COGLES2Driver::setClipPlane( u32 index, const core::plane3df& plane, bool enable )
{
if ( index >= UserClipPlane.size() )
UserClipPlane.push_back( SUserClipPlane() );
UserClipPlane[index].Plane = plane;
UserClipPlane[index].Enabled = enable;
return true;
}
//! Enable/disable a clipping plane.
void COGLES2Driver::enableClipPlane( u32 index, bool enable )
{
if ( index >= MaxUserClipPlanes )
return;
UserClipPlane[index].Enabled = enable;
}
//! Get the ClipPlane Count
u32 COGLES2Driver::getClipPlaneCount() const
{
return UserClipPlane.size();
}
const core::plane3df& COGLES2Driver::getClipPlane( irr::u32 index ) const
{
if ( index < UserClipPlane.size() )
return UserClipPlane[index].Plane;
else
return *(( core::plane3df* )0 );
}
core::dimension2du COGLES2Driver::getMaxTextureSize() const
{
return core::dimension2du(MaxTextureSize, MaxTextureSize);
}
} // end namespace
} // end namespace
#endif // _IRR_COMPILE_WITH_OGLES2_
namespace irr
{
namespace video
{
// -----------------------------------
// WINDOWS VERSION
// -----------------------------------
#if defined(_IRR_COMPILE_WITH_X11_DEVICE_) || defined(_IRR_COMPILE_WITH_SDL_DEVICE_) || defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) || defined(_IRR_COMPILE_WITH_CONSOLE_DEVICE_)
IVideoDriver* createOGLES2Driver( const SIrrlichtCreationParameters& params,
video::SExposedVideoData& data, io::IFileSystem* io )
{
#ifdef _IRR_COMPILE_WITH_OGLES2_
return new COGLES2Driver( params, data, io );
#else
return 0;
#endif // _IRR_COMPILE_WITH_OGLES2_
}
#endif
// -----------------------------------
// MACOSX VERSION
// -----------------------------------
#if defined(_IRR_COMPILE_WITH_OSX_DEVICE_)
IVideoDriver* createOGLES2Driver( const SIrrlichtCreationParameters& params,
io::IFileSystem* io, CIrrDeviceMacOSX *device )
{
#ifdef _IRR_COMPILE_WITH_OGLES2_
return new COGLES2Driver( params, io, device );
#else
return 0;
#endif // _IRR_COMPILE_WITH_OGLES2_
}
#endif // _IRR_COMPILE_WITH_OSX_DEVICE_
// -----------------------------------
// IPHONE VERSION
// -----------------------------------
#if defined(_IRR_COMPILE_WITH_IPHONE_DEVICE_)
IVideoDriver* createOGLES2Driver( const SIrrlichtCreationParameters& params,
video::SExposedVideoData& data, io::IFileSystem* io,
MIrrIPhoneDevice const & device )
{
#ifdef _IRR_COMPILE_WITH_OGLES2_
return new COGLES2Driver( params, data, io, device );
#else
return 0;
#endif // _IRR_COMPILE_WITH_OGLES2_
}
#endif // _IRR_COMPILE_WITH_IPHONE_DEVICE_
} // end namespace
} // end namespace