irrlicht-android/source/Irrlicht/COGLES2Driver.cpp

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