2013-12-18 10:12:15 +08:00
/****************************************************************************
2014-01-07 11:25:07 +08:00
Copyright ( c ) 2013 - 2014 Chukong Technologies Inc .
2013-12-18 10:12:15 +08:00
http : //www.cocos2d-x.org
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the " Software " ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-11-05 01:14:22 +08:00
2014-04-30 08:37:36 +08:00
# include "renderer/CCRenderer.h"
2014-04-15 07:46:19 +08:00
# include <algorithm>
2014-09-01 11:49:04 +08:00
# include "renderer/CCTrianglesCommand.h"
2014-04-30 08:37:36 +08:00
# include "renderer/CCQuadCommand.h"
# include "renderer/CCBatchCommand.h"
# include "renderer/CCCustomCommand.h"
# include "renderer/CCGroupCommand.h"
2014-08-05 15:08:01 +08:00
# include "renderer/CCPrimitiveCommand.h"
2015-05-06 04:07:32 +08:00
# include "renderer/CCMeshCommand.h"
2014-05-10 09:39:25 +08:00
# include "renderer/CCGLProgramCache.h"
2015-05-06 04:07:32 +08:00
# include "renderer/CCMaterial.h"
# include "renderer/CCTechnique.h"
# include "renderer/CCPass.h"
# include "renderer/CCRenderState.h"
2014-05-09 09:01:48 +08:00
# include "renderer/ccGLStateCache.h"
2015-05-06 04:07:32 +08:00
2014-05-01 10:09:13 +08:00
# include "base/CCConfiguration.h"
2014-04-30 08:37:36 +08:00
# include "base/CCDirector.h"
# include "base/CCEventDispatcher.h"
# include "base/CCEventListenerCustom.h"
# include "base/CCEventType.h"
2014-10-20 16:25:24 +08:00
# include "2d/CCCamera.h"
2014-08-27 12:09:23 +08:00
# include "2d/CCScene.h"
2014-04-15 07:46:19 +08:00
2013-11-05 01:14:22 +08:00
NS_CC_BEGIN
2014-04-15 07:46:19 +08:00
// helper
2014-04-17 10:16:37 +08:00
static bool compareRenderCommand ( RenderCommand * a , RenderCommand * b )
2014-01-18 15:10:04 +08:00
{
2014-01-19 03:35:27 +08:00
return a - > getGlobalOrder ( ) < b - > getGlobalOrder ( ) ;
2014-01-18 15:10:04 +08:00
}
2015-01-12 14:20:41 +08:00
static bool compare3DCommand ( RenderCommand * a , RenderCommand * b )
{
2015-01-28 07:41:58 +08:00
return a - > getDepth ( ) > b - > getDepth ( ) ;
2015-01-12 14:20:41 +08:00
}
2014-04-15 07:46:19 +08:00
// queue
2015-04-06 21:45:19 +08:00
RenderQueue : : RenderQueue ( )
{
}
2014-04-15 07:46:19 +08:00
2014-01-18 15:10:04 +08:00
void RenderQueue : : push_back ( RenderCommand * command )
{
2014-01-19 03:35:27 +08:00
float z = command - > getGlobalOrder ( ) ;
2015-01-27 14:29:09 +08:00
if ( z < 0 )
2015-01-12 14:20:41 +08:00
{
2015-01-27 14:29:09 +08:00
_commands [ QUEUE_GROUP : : GLOBALZ_NEG ] . push_back ( command ) ;
}
else if ( z > 0 )
{
_commands [ QUEUE_GROUP : : GLOBALZ_POS ] . push_back ( command ) ;
}
else
{
if ( command - > is3D ( ) )
2015-01-12 14:20:41 +08:00
{
2015-01-27 14:29:09 +08:00
if ( command - > isTransparent ( ) )
{
_commands [ QUEUE_GROUP : : TRANSPARENT_3D ] . push_back ( command ) ;
}
else
{
_commands [ QUEUE_GROUP : : OPAQUE_3D ] . push_back ( command ) ;
}
2015-01-12 14:20:41 +08:00
}
else
{
2015-01-27 14:29:09 +08:00
_commands [ QUEUE_GROUP : : GLOBALZ_ZERO ] . push_back ( command ) ;
2015-01-12 14:20:41 +08:00
}
}
2014-01-18 15:10:04 +08:00
}
ssize_t RenderQueue : : size ( ) const
{
2015-01-27 14:29:09 +08:00
ssize_t result ( 0 ) ;
2015-01-28 10:32:22 +08:00
for ( int index = 0 ; index < QUEUE_GROUP : : QUEUE_COUNT ; + + index )
2015-01-27 14:29:09 +08:00
{
result + = _commands [ index ] . size ( ) ;
}
return result ;
2014-01-18 15:10:04 +08:00
}
void RenderQueue : : sort ( )
{
// Don't sort _queue0, it already comes sorted
2015-01-27 14:29:09 +08:00
std : : sort ( std : : begin ( _commands [ QUEUE_GROUP : : TRANSPARENT_3D ] ) , std : : end ( _commands [ QUEUE_GROUP : : TRANSPARENT_3D ] ) , compare3DCommand ) ;
std : : sort ( std : : begin ( _commands [ QUEUE_GROUP : : GLOBALZ_NEG ] ) , std : : end ( _commands [ QUEUE_GROUP : : GLOBALZ_NEG ] ) , compareRenderCommand ) ;
std : : sort ( std : : begin ( _commands [ QUEUE_GROUP : : GLOBALZ_POS ] ) , std : : end ( _commands [ QUEUE_GROUP : : GLOBALZ_POS ] ) , compareRenderCommand ) ;
2014-01-18 15:10:04 +08:00
}
2014-01-19 03:35:27 +08:00
RenderCommand * RenderQueue : : operator [ ] ( ssize_t index ) const
2014-01-18 15:10:04 +08:00
{
2015-01-28 10:32:22 +08:00
for ( int queIndex = 0 ; queIndex < QUEUE_GROUP : : QUEUE_COUNT ; + + queIndex )
2015-01-27 14:29:09 +08:00
{
if ( index < static_cast < ssize_t > ( _commands [ queIndex ] . size ( ) ) )
return _commands [ queIndex ] [ index ] ;
else
{
index - = _commands [ queIndex ] . size ( ) ;
}
}
2015-01-12 14:20:41 +08:00
2014-01-18 15:10:04 +08:00
CCASSERT ( false , " invalid index " ) ;
return nullptr ;
2015-01-27 14:29:09 +08:00
2014-01-18 15:10:04 +08:00
}
void RenderQueue : : clear ( )
{
2015-04-06 21:45:19 +08:00
for ( int i = 0 ; i < QUEUE_COUNT ; + + i )
{
_commands [ i ] . clear ( ) ;
}
}
void RenderQueue : : realloc ( size_t reserveSize )
{
for ( int i = 0 ; i < QUEUE_COUNT ; + + i )
2015-01-27 14:29:09 +08:00
{
2015-04-06 21:45:19 +08:00
_commands [ i ] = std : : vector < RenderCommand * > ( ) ;
_commands [ i ] . reserve ( reserveSize ) ;
2015-01-27 14:29:09 +08:00
}
2014-01-18 15:10:04 +08:00
}
2013-11-05 01:14:22 +08:00
2015-01-27 08:37:11 +08:00
void RenderQueue : : saveRenderState ( )
{
2015-01-29 17:40:00 +08:00
_isDepthEnabled = glIsEnabled ( GL_DEPTH_TEST ) ! = GL_FALSE ;
_isCullEnabled = glIsEnabled ( GL_CULL_FACE ) ! = GL_FALSE ;
2015-01-27 08:37:11 +08:00
glGetBooleanv ( GL_DEPTH_WRITEMASK , & _isDepthWrite ) ;
CHECK_GL_ERROR_DEBUG ( ) ;
}
void RenderQueue : : restoreRenderState ( )
{
if ( _isCullEnabled )
{
glEnable ( GL_CULL_FACE ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setCullFace ( true ) ;
2015-01-27 08:37:11 +08:00
}
else
{
glDisable ( GL_CULL_FACE ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setCullFace ( false ) ;
2015-01-27 08:37:11 +08:00
}
if ( _isDepthEnabled )
{
glEnable ( GL_DEPTH_TEST ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( true ) ;
2015-01-27 08:37:11 +08:00
}
else
{
glDisable ( GL_DEPTH_TEST ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( false ) ;
2015-01-27 08:37:11 +08:00
}
glDepthMask ( _isDepthWrite ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( _isDepthEnabled ) ;
2015-01-27 08:37:11 +08:00
CHECK_GL_ERROR_DEBUG ( ) ;
}
2014-01-18 15:10:04 +08:00
//
//
//
2014-04-15 07:46:19 +08:00
static const int DEFAULT_RENDER_QUEUE = 0 ;
2013-11-05 01:14:22 +08:00
2014-04-15 07:46:19 +08:00
//
// constructors, destructors, init
//
2013-11-05 01:14:22 +08:00
Renderer : : Renderer ( )
2013-11-08 02:09:53 +08:00
: _lastMaterialID ( 0 )
2014-06-23 23:58:45 +08:00
, _lastBatchedMeshCommand ( nullptr )
2014-08-27 14:54:35 +08:00
, _filledVertex ( 0 )
, _filledIndex ( 0 )
2014-10-16 15:33:31 +08:00
, _numberQuads ( 0 )
2013-11-22 08:36:19 +08:00
, _glViewAssigned ( false )
2014-04-11 16:05:22 +08:00
, _isRendering ( false )
2015-01-16 08:41:07 +08:00
, _isDepthTestFor2D ( false )
2013-12-31 11:02:01 +08:00
# if CC_ENABLE_CACHE_TEXTURE_DATA
2013-12-31 10:54:37 +08:00
, _cacheTextureListener ( nullptr )
2013-12-31 11:02:01 +08:00
# endif
2013-11-08 07:48:37 +08:00
{
2014-08-28 07:31:57 +08:00
_groupCommandManager = new ( std : : nothrow ) GroupCommandManager ( ) ;
2014-04-12 13:01:42 +08:00
2013-11-21 03:05:01 +08:00
_commandGroupStack . push ( DEFAULT_RENDER_QUEUE ) ;
2013-11-14 09:31:12 +08:00
RenderQueue defaultRenderQueue ;
_renderGroups . push_back ( defaultRenderQueue ) ;
2014-08-28 09:58:39 +08:00
_batchedCommands . reserve ( BATCH_QUADCOMMAND_RESEVER_SIZE ) ;
2015-01-10 06:06:21 +08:00
// default clear color
_clearColor = Color4F : : BLACK ;
2013-11-08 07:48:37 +08:00
}
Renderer : : ~ Renderer ( )
2013-11-05 01:14:22 +08:00
{
2013-11-14 09:31:12 +08:00
_renderGroups . clear ( ) ;
2014-04-12 13:01:42 +08:00
_groupCommandManager - > release ( ) ;
2013-11-22 08:36:19 +08:00
glDeleteBuffers ( 2 , _buffersVBO ) ;
2014-10-16 14:15:29 +08:00
glDeleteBuffers ( 2 , _quadbuffersVBO ) ;
2013-11-22 08:36:19 +08:00
2013-12-05 09:02:02 +08:00
if ( Configuration : : getInstance ( ) - > supportsShareableVAO ( ) )
{
2014-10-16 14:15:29 +08:00
glDeleteVertexArrays ( 1 , & _buffersVAO ) ;
2013-11-23 02:24:52 +08:00
glDeleteVertexArrays ( 1 , & _quadVAO ) ;
2013-11-22 08:36:19 +08:00
GL : : bindVAO ( 0 ) ;
2013-12-05 09:02:02 +08:00
}
2013-12-27 11:56:56 +08:00
# if CC_ENABLE_CACHE_TEXTURE_DATA
2013-12-31 10:54:37 +08:00
Director : : getInstance ( ) - > getEventDispatcher ( ) - > removeEventListener ( _cacheTextureListener ) ;
2013-12-27 11:56:56 +08:00
# endif
2013-11-08 08:50:53 +08:00
}
2013-11-22 08:36:19 +08:00
void Renderer : : initGLView ( )
{
2013-12-27 11:56:56 +08:00
# if CC_ENABLE_CACHE_TEXTURE_DATA
2014-07-09 23:03:04 +08:00
_cacheTextureListener = EventListenerCustom : : create ( EVENT_RENDERER_RECREATED , [ this ] ( EventCustom * event ) {
/** listen the event that renderer was recreated on Android/WP8 */
2013-12-31 10:54:37 +08:00
this - > setupBuffer ( ) ;
} ) ;
Director : : getInstance ( ) - > getEventDispatcher ( ) - > addEventListenerWithFixedPriority ( _cacheTextureListener , - 1 ) ;
2013-12-05 09:02:02 +08:00
# endif
2013-11-22 08:36:19 +08:00
2014-10-16 14:15:29 +08:00
//setup index data for quads
for ( int i = 0 ; i < VBO_SIZE / 4 ; i + + )
{
_quadIndices [ i * 6 + 0 ] = ( GLushort ) ( i * 4 + 0 ) ;
_quadIndices [ i * 6 + 1 ] = ( GLushort ) ( i * 4 + 1 ) ;
_quadIndices [ i * 6 + 2 ] = ( GLushort ) ( i * 4 + 2 ) ;
_quadIndices [ i * 6 + 3 ] = ( GLushort ) ( i * 4 + 3 ) ;
_quadIndices [ i * 6 + 4 ] = ( GLushort ) ( i * 4 + 2 ) ;
_quadIndices [ i * 6 + 5 ] = ( GLushort ) ( i * 4 + 1 ) ;
}
2013-12-05 09:02:02 +08:00
setupBuffer ( ) ;
2013-11-22 08:36:19 +08:00
_glViewAssigned = true ;
2013-11-08 08:50:53 +08:00
}
2013-12-05 09:02:02 +08:00
void Renderer : : setupBuffer ( )
{
if ( Configuration : : getInstance ( ) - > supportsShareableVAO ( ) )
{
setupVBOAndVAO ( ) ;
}
else
{
setupVBO ( ) ;
}
}
2013-11-08 08:50:53 +08:00
void Renderer : : setupVBOAndVAO ( )
{
2014-10-16 14:15:29 +08:00
//generate vbo and vao for trianglesCommand
glGenVertexArrays ( 1 , & _buffersVAO ) ;
GL : : bindVAO ( _buffersVAO ) ;
2013-11-08 08:50:53 +08:00
glGenBuffers ( 2 , & _buffersVBO [ 0 ] ) ;
glBindBuffer ( GL_ARRAY_BUFFER , _buffersVBO [ 0 ] ) ;
2014-08-27 14:54:35 +08:00
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _verts [ 0 ] ) * VBO_SIZE , _verts , GL_DYNAMIC_DRAW ) ;
2013-11-08 08:50:53 +08:00
// vertices
glEnableVertexAttribArray ( GLProgram : : VERTEX_ATTRIB_POSITION ) ;
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_POSITION , 3 , GL_FLOAT , GL_FALSE , sizeof ( V3F_C4B_T2F ) , ( GLvoid * ) offsetof ( V3F_C4B_T2F , vertices ) ) ;
// colors
glEnableVertexAttribArray ( GLProgram : : VERTEX_ATTRIB_COLOR ) ;
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_COLOR , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( V3F_C4B_T2F ) , ( GLvoid * ) offsetof ( V3F_C4B_T2F , colors ) ) ;
// tex coords
2014-05-09 03:34:26 +08:00
glEnableVertexAttribArray ( GLProgram : : VERTEX_ATTRIB_TEX_COORD ) ;
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_TEX_COORD , 2 , GL_FLOAT , GL_FALSE , sizeof ( V3F_C4B_T2F ) , ( GLvoid * ) offsetof ( V3F_C4B_T2F , texCoords ) ) ;
2013-11-08 08:50:53 +08:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _buffersVBO [ 1 ] ) ;
2014-08-27 14:54:35 +08:00
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( _indices [ 0 ] ) * INDEX_VBO_SIZE , _indices , GL_STATIC_DRAW ) ;
2013-11-08 08:50:53 +08:00
// Must unbind the VAO before changing the element buffer.
GL : : bindVAO ( 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
2014-10-16 14:15:29 +08:00
//generate vbo and vao for quadCommand
glGenVertexArrays ( 1 , & _quadVAO ) ;
GL : : bindVAO ( _quadVAO ) ;
glGenBuffers ( 2 , & _quadbuffersVBO [ 0 ] ) ;
glBindBuffer ( GL_ARRAY_BUFFER , _quadbuffersVBO [ 0 ] ) ;
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _quadVerts [ 0 ] ) * VBO_SIZE , _quadVerts , GL_DYNAMIC_DRAW ) ;
// vertices
glEnableVertexAttribArray ( GLProgram : : VERTEX_ATTRIB_POSITION ) ;
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_POSITION , 3 , GL_FLOAT , GL_FALSE , sizeof ( V3F_C4B_T2F ) , ( GLvoid * ) offsetof ( V3F_C4B_T2F , vertices ) ) ;
// colors
glEnableVertexAttribArray ( GLProgram : : VERTEX_ATTRIB_COLOR ) ;
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_COLOR , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( V3F_C4B_T2F ) , ( GLvoid * ) offsetof ( V3F_C4B_T2F , colors ) ) ;
// tex coords
glEnableVertexAttribArray ( GLProgram : : VERTEX_ATTRIB_TEX_COORD ) ;
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_TEX_COORD , 2 , GL_FLOAT , GL_FALSE , sizeof ( V3F_C4B_T2F ) , ( GLvoid * ) offsetof ( V3F_C4B_T2F , texCoords ) ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _quadbuffersVBO [ 1 ] ) ;
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( _quadIndices [ 0 ] ) * INDEX_VBO_SIZE , _quadIndices , GL_STATIC_DRAW ) ;
// Must unbind the VAO before changing the element buffer.
GL : : bindVAO ( 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
2013-11-08 08:50:53 +08:00
CHECK_GL_ERROR_DEBUG ( ) ;
2013-11-05 01:14:22 +08:00
}
2013-12-05 09:02:02 +08:00
void Renderer : : setupVBO ( )
{
glGenBuffers ( 2 , & _buffersVBO [ 0 ] ) ;
2014-10-16 15:33:31 +08:00
glGenBuffers ( 2 , & _quadbuffersVBO [ 0 ] ) ;
2013-12-05 09:02:02 +08:00
mapBuffers ( ) ;
}
void Renderer : : mapBuffers ( )
{
// Avoid changing the element buffer for whatever VAO might be bound.
GL : : bindVAO ( 0 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , _buffersVBO [ 0 ] ) ;
2014-08-27 14:54:35 +08:00
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _verts [ 0 ] ) * VBO_SIZE , _verts , GL_DYNAMIC_DRAW ) ;
2014-10-16 14:15:29 +08:00
glBindBuffer ( GL_ARRAY_BUFFER , _quadbuffersVBO [ 0 ] ) ;
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _quadVerts [ 0 ] ) * VBO_SIZE , _quadVerts , GL_DYNAMIC_DRAW ) ;
2013-12-05 09:02:02 +08:00
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _buffersVBO [ 1 ] ) ;
2014-08-27 14:54:35 +08:00
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( _indices [ 0 ] ) * INDEX_VBO_SIZE , _indices , GL_STATIC_DRAW ) ;
2014-10-16 14:15:29 +08:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _quadbuffersVBO [ 1 ] ) ;
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( _quadIndices [ 0 ] ) * INDEX_VBO_SIZE , _quadIndices , GL_STATIC_DRAW ) ;
2013-12-05 09:02:02 +08:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
CHECK_GL_ERROR_DEBUG ( ) ;
}
2013-11-16 09:32:29 +08:00
void Renderer : : addCommand ( RenderCommand * command )
{
2014-01-17 14:29:25 +08:00
int renderQueue = _commandGroupStack . top ( ) ;
addCommand ( command , renderQueue ) ;
2013-11-16 09:32:29 +08:00
}
void Renderer : : addCommand ( RenderCommand * command , int renderQueue )
2013-11-05 01:14:22 +08:00
{
2014-04-11 16:05:22 +08:00
CCASSERT ( ! _isRendering , " Cannot add command while rendering " ) ;
2014-01-17 14:29:25 +08:00
CCASSERT ( renderQueue > = 0 , " Invalid render queue " ) ;
CCASSERT ( command - > getType ( ) ! = RenderCommand : : Type : : UNKNOWN_COMMAND , " Invalid Command Type " ) ;
2015-02-13 09:48:27 +08:00
_renderGroups [ renderQueue ] . push_back ( command ) ;
2014-08-27 17:53:46 +08:00
}
2013-11-21 03:05:01 +08:00
void Renderer : : pushGroup ( int renderQueueID )
{
2014-04-11 16:07:55 +08:00
CCASSERT ( ! _isRendering , " Cannot change render queue while rendering " ) ;
2013-11-21 03:05:01 +08:00
_commandGroupStack . push ( renderQueueID ) ;
}
void Renderer : : popGroup ( )
{
2014-04-11 16:07:55 +08:00
CCASSERT ( ! _isRendering , " Cannot change render queue while rendering " ) ;
2013-11-21 03:05:01 +08:00
_commandGroupStack . pop ( ) ;
}
2013-11-16 03:29:11 +08:00
int Renderer : : createRenderQueue ( )
{
RenderQueue newRenderQueue ;
_renderGroups . push_back ( newRenderQueue ) ;
2013-11-21 03:05:01 +08:00
return ( int ) _renderGroups . size ( ) - 1 ;
2013-11-16 03:29:11 +08:00
}
2015-01-12 14:20:41 +08:00
void Renderer : : processRenderCommand ( RenderCommand * command )
2014-04-07 22:51:32 +08:00
{
2015-01-12 14:20:41 +08:00
auto commandType = command - > getType ( ) ;
if ( RenderCommand : : Type : : TRIANGLES_COMMAND = = commandType )
2014-04-07 22:51:32 +08:00
{
2015-01-12 14:20:41 +08:00
//Draw if we have batched other commands which are not triangle command
flush3D ( ) ;
flushQuads ( ) ;
//Process triangle command
auto cmd = static_cast < TrianglesCommand * > ( command ) ;
//Draw batched Triangles if necessary
if ( cmd - > isSkipBatching ( ) | | _filledVertex + cmd - > getVertexCount ( ) > VBO_SIZE | | _filledIndex + cmd - > getIndexCount ( ) > INDEX_VBO_SIZE )
2014-04-07 22:51:32 +08:00
{
2015-01-12 14:20:41 +08:00
CCASSERT ( cmd - > getVertexCount ( ) > = 0 & & cmd - > getVertexCount ( ) < VBO_SIZE , " VBO for vertex is not big enough, please break the data down or use customized render command " ) ;
CCASSERT ( cmd - > getIndexCount ( ) > = 0 & & cmd - > getIndexCount ( ) < INDEX_VBO_SIZE , " VBO for index is not big enough, please break the data down or use customized render command " ) ;
//Draw batched Triangles if VBO is full
drawBatchedTriangles ( ) ;
}
//Batch Triangles
_batchedCommands . push_back ( cmd ) ;
fillVerticesAndIndices ( cmd ) ;
if ( cmd - > isSkipBatching ( ) )
{
drawBatchedTriangles ( ) ;
}
}
else if ( RenderCommand : : Type : : QUAD_COMMAND = = commandType )
{
//Draw if we have batched other commands which are not quad command
flush3D ( ) ;
flushTriangles ( ) ;
//Process quad command
auto cmd = static_cast < QuadCommand * > ( command ) ;
//Draw batched quads if necessary
if ( cmd - > isSkipBatching ( ) | | ( _numberQuads + cmd - > getQuadCount ( ) ) * 4 > VBO_SIZE )
{
CCASSERT ( cmd - > getQuadCount ( ) > = 0 & & cmd - > getQuadCount ( ) * 4 < VBO_SIZE , " VBO for vertex is not big enough, please break the data down or use customized render command " ) ;
//Draw batched quads if VBO is full
drawBatchedQuads ( ) ;
2014-10-16 14:15:29 +08:00
}
2015-01-12 14:20:41 +08:00
//Batch Quads
_batchQuadCommands . push_back ( cmd ) ;
fillQuads ( cmd ) ;
if ( cmd - > isSkipBatching ( ) )
{
drawBatchedQuads ( ) ;
}
}
else if ( RenderCommand : : Type : : MESH_COMMAND = = commandType )
{
flush2D ( ) ;
auto cmd = static_cast < MeshCommand * > ( command ) ;
if ( cmd - > isSkipBatching ( ) | | _lastBatchedMeshCommand = = nullptr | | _lastBatchedMeshCommand - > getMaterialID ( ) ! = cmd - > getMaterialID ( ) )
2014-10-16 14:15:29 +08:00
{
flush3D ( ) ;
2015-01-10 06:06:21 +08:00
if ( cmd - > isSkipBatching ( ) )
{
2015-06-16 05:12:44 +08:00
// XXX: execute() will call bind() and unbind()
// but unbind() shouldn't be call if the next command is a MESH_COMMAND with Material.
// Once most of cocos2d-x moves to Pass/StateBlock, only bind() should be used.
2015-01-12 14:20:41 +08:00
cmd - > execute ( ) ;
2015-01-10 06:06:21 +08:00
}
else
{
2015-01-12 14:20:41 +08:00
cmd - > preBatchDraw ( ) ;
2015-01-10 06:06:21 +08:00
cmd - > batchDraw ( ) ;
2015-01-12 14:20:41 +08:00
_lastBatchedMeshCommand = cmd ;
2015-01-10 06:06:21 +08:00
}
2014-04-07 22:51:32 +08:00
}
else
{
2015-01-12 14:20:41 +08:00
cmd - > batchDraw ( ) ;
2014-04-07 22:51:32 +08:00
}
}
2015-01-12 14:20:41 +08:00
else if ( RenderCommand : : Type : : GROUP_COMMAND = = commandType )
{
flush ( ) ;
int renderQueueID = ( ( GroupCommand * ) command ) - > getRenderQueueID ( ) ;
visitRenderQueue ( _renderGroups [ renderQueueID ] ) ;
}
else if ( RenderCommand : : Type : : CUSTOM_COMMAND = = commandType )
{
flush ( ) ;
auto cmd = static_cast < CustomCommand * > ( command ) ;
cmd - > execute ( ) ;
}
else if ( RenderCommand : : Type : : BATCH_COMMAND = = commandType )
{
flush ( ) ;
auto cmd = static_cast < BatchCommand * > ( command ) ;
cmd - > execute ( ) ;
}
else if ( RenderCommand : : Type : : PRIMITIVE_COMMAND = = commandType )
{
flush ( ) ;
auto cmd = static_cast < PrimitiveCommand * > ( command ) ;
cmd - > execute ( ) ;
}
else
{
CCLOGERROR ( " Unknown commands in renderQueue " ) ;
}
}
2015-01-27 08:37:11 +08:00
void Renderer : : visitRenderQueue ( RenderQueue & queue )
2015-01-12 14:20:41 +08:00
{
2015-01-27 08:37:11 +08:00
queue . saveRenderState ( ) ;
2015-01-28 06:41:16 +08:00
//
//Process Global-Z < 0 Objects
//
2015-01-28 10:59:29 +08:00
const auto & zNegQueue = queue . getSubQueue ( RenderQueue : : QUEUE_GROUP : : GLOBALZ_NEG ) ;
2015-01-28 06:41:16 +08:00
if ( zNegQueue . size ( ) > 0 )
2015-01-28 03:23:48 +08:00
{
2015-01-28 06:41:16 +08:00
if ( _isDepthTestFor2D )
{
glEnable ( GL_DEPTH_TEST ) ;
glDepthMask ( true ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( true ) ;
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( true ) ;
2015-01-28 06:41:16 +08:00
}
else
{
glDisable ( GL_DEPTH_TEST ) ;
glDepthMask ( false ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( false ) ;
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( false ) ;
2015-01-28 06:41:16 +08:00
}
for ( auto it = zNegQueue . cbegin ( ) ; it ! = zNegQueue . cend ( ) ; + + it )
{
processRenderCommand ( * it ) ;
}
flush ( ) ;
2015-01-27 14:29:09 +08:00
}
2015-01-28 03:23:48 +08:00
2015-01-28 06:41:16 +08:00
//
2015-01-12 14:20:41 +08:00
//Process Opaque Object
2015-01-28 06:41:16 +08:00
//
2015-01-28 10:59:29 +08:00
const auto & opaqueQueue = queue . getSubQueue ( RenderQueue : : QUEUE_GROUP : : OPAQUE_3D ) ;
2015-01-12 14:20:41 +08:00
if ( opaqueQueue . size ( ) > 0 )
{
2015-01-28 07:41:58 +08:00
//Clear depth to achieve layered rendering
2015-01-12 14:20:41 +08:00
glEnable ( GL_DEPTH_TEST ) ;
2015-05-06 04:07:32 +08:00
glDepthMask ( true ) ;
RenderState : : StateBlock : : _defaultState - > setDepthTest ( true ) ;
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( true ) ;
2015-01-28 06:41:16 +08:00
for ( auto it = opaqueQueue . cbegin ( ) ; it ! = opaqueQueue . cend ( ) ; + + it )
{
2015-01-12 14:20:41 +08:00
processRenderCommand ( * it ) ;
}
2015-01-28 06:41:16 +08:00
flush ( ) ;
2015-01-12 14:20:41 +08:00
}
2015-01-28 06:41:16 +08:00
//
//Process 3D Transparent object
//
2015-01-28 10:59:29 +08:00
const auto & transQueue = queue . getSubQueue ( RenderQueue : : QUEUE_GROUP : : TRANSPARENT_3D ) ;
2015-01-28 06:41:16 +08:00
if ( transQueue . size ( ) > 0 )
2015-01-12 14:20:41 +08:00
{
glEnable ( GL_DEPTH_TEST ) ;
2015-01-28 06:41:16 +08:00
glDepthMask ( false ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( true ) ;
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( false ) ;
2015-01-28 06:41:16 +08:00
for ( auto it = transQueue . cbegin ( ) ; it ! = transQueue . cend ( ) ; + + it )
{
processRenderCommand ( * it ) ;
}
flush ( ) ;
2015-01-12 14:20:41 +08:00
}
2015-01-28 06:41:16 +08:00
//
//Process Global-Z = 0 Queue
//
2015-01-28 10:59:29 +08:00
const auto & zZeroQueue = queue . getSubQueue ( RenderQueue : : QUEUE_GROUP : : GLOBALZ_ZERO ) ;
2015-01-28 06:41:16 +08:00
if ( zZeroQueue . size ( ) > 0 )
2015-01-16 08:41:07 +08:00
{
2015-01-28 06:41:16 +08:00
if ( _isDepthTestFor2D )
{
glEnable ( GL_DEPTH_TEST ) ;
glDepthMask ( true ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( true ) ;
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( true ) ;
2015-01-28 06:41:16 +08:00
}
else
{
glDisable ( GL_DEPTH_TEST ) ;
glDepthMask ( false ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( false ) ;
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( false ) ;
2015-01-28 06:41:16 +08:00
}
for ( auto it = zZeroQueue . cbegin ( ) ; it ! = zZeroQueue . cend ( ) ; + + it )
{
processRenderCommand ( * it ) ;
}
flush ( ) ;
2015-01-16 08:41:07 +08:00
}
2015-01-28 06:41:16 +08:00
//
//Process Global-Z > 0 Queue
//
2015-01-28 10:59:29 +08:00
const auto & zPosQueue = queue . getSubQueue ( RenderQueue : : QUEUE_GROUP : : GLOBALZ_POS ) ;
2015-01-28 06:41:16 +08:00
if ( zPosQueue . size ( ) > 0 )
2015-01-12 14:20:41 +08:00
{
2015-01-28 06:41:16 +08:00
for ( auto it = zPosQueue . cbegin ( ) ; it ! = zPosQueue . cend ( ) ; + + it )
{
processRenderCommand ( * it ) ;
}
flush ( ) ;
2015-01-12 14:20:41 +08:00
}
2015-01-27 08:37:11 +08:00
queue . restoreRenderState ( ) ;
2014-04-07 22:51:32 +08:00
}
2013-11-05 01:14:22 +08:00
void Renderer : : render ( )
{
2013-11-07 06:57:42 +08:00
//Uncomment this once everything is rendered by new renderer
2013-11-05 01:14:22 +08:00
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2014-08-30 03:54:24 +08:00
//TODO: setup camera or MVP
2014-04-11 16:05:22 +08:00
_isRendering = true ;
2013-11-22 08:36:19 +08:00
if ( _glViewAssigned )
2013-11-14 09:31:12 +08:00
{
2013-11-22 08:36:19 +08:00
//Process render commands
//1. Sort render commands based on ID
2014-01-18 15:10:04 +08:00
for ( auto & renderqueue : _renderGroups )
2013-11-14 09:31:12 +08:00
{
2014-01-18 15:10:04 +08:00
renderqueue . sort ( ) ;
2013-11-22 08:36:19 +08:00
}
2014-04-07 22:51:32 +08:00
visitRenderQueue ( _renderGroups [ 0 ] ) ;
2013-11-14 09:31:12 +08:00
}
2014-04-04 12:48:16 +08:00
clean ( ) ;
2014-04-11 16:05:22 +08:00
_isRendering = false ;
2014-04-04 12:48:16 +08:00
}
void Renderer : : clean ( )
{
// Clear render group
2013-11-15 02:35:28 +08:00
for ( size_t j = 0 ; j < _renderGroups . size ( ) ; j + + )
2013-11-05 01:14:22 +08:00
{
2013-12-27 14:02:17 +08:00
//commands are owned by nodes
// for (const auto &cmd : _renderGroups[j])
// {
// cmd->releaseToCommandPool();
// }
2013-11-15 02:35:28 +08:00
_renderGroups [ j ] . clear ( ) ;
2013-11-11 16:14:29 +08:00
}
2014-04-04 12:48:16 +08:00
2014-10-16 14:56:17 +08:00
// Clear batch commands
2014-08-28 09:58:39 +08:00
_batchedCommands . clear ( ) ;
2014-10-16 14:15:29 +08:00
_batchQuadCommands . clear ( ) ;
2014-08-27 14:54:35 +08:00
_filledVertex = 0 ;
_filledIndex = 0 ;
2014-10-16 14:15:29 +08:00
_numberQuads = 0 ;
2013-11-11 16:14:29 +08:00
_lastMaterialID = 0 ;
2014-06-23 23:58:45 +08:00
_lastBatchedMeshCommand = nullptr ;
2013-11-11 16:14:29 +08:00
}
2013-11-09 04:06:39 +08:00
2015-01-10 06:06:21 +08:00
void Renderer : : clear ( )
{
2015-01-12 14:20:41 +08:00
//Enable Depth mask to make sure glClear clear the depth buffer correctly
2015-01-10 06:06:21 +08:00
glDepthMask ( true ) ;
2015-05-13 17:40:10 +08:00
glClearColor ( _clearColor . r , _clearColor . g , _clearColor . b , _clearColor . a ) ;
2015-01-10 06:06:21 +08:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
glDepthMask ( false ) ;
2015-05-06 06:59:41 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthWrite ( false ) ;
2015-01-10 06:06:21 +08:00
}
2015-01-16 08:54:27 +08:00
void Renderer : : setDepthTest ( bool enable )
{
if ( enable )
{
glClearDepth ( 1.0f ) ;
glEnable ( GL_DEPTH_TEST ) ;
glDepthFunc ( GL_LEQUAL ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( true ) ;
RenderState : : StateBlock : : _defaultState - > setDepthFunction ( RenderState : : DEPTH_LEQUAL ) ;
2015-01-16 08:54:27 +08:00
// glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}
else
{
glDisable ( GL_DEPTH_TEST ) ;
2015-05-06 04:07:32 +08:00
RenderState : : StateBlock : : _defaultState - > setDepthTest ( false ) ;
2015-01-16 08:54:27 +08:00
}
2015-05-06 04:07:32 +08:00
2015-01-16 08:54:27 +08:00
_isDepthTestFor2D = enable ;
CHECK_GL_ERROR_DEBUG ( ) ;
}
2015-01-12 14:20:41 +08:00
void Renderer : : fillVerticesAndIndices ( const TrianglesCommand * cmd )
2014-01-16 06:35:26 +08:00
{
2014-08-28 09:58:39 +08:00
memcpy ( _verts + _filledVertex , cmd - > getVertices ( ) , sizeof ( V3F_C4B_T2F ) * cmd - > getVertexCount ( ) ) ;
2014-08-27 16:43:14 +08:00
const Mat4 & modelView = cmd - > getModelView ( ) ;
2014-08-27 18:39:49 +08:00
for ( ssize_t i = 0 ; i < cmd - > getVertexCount ( ) ; + + i )
2014-04-09 14:21:41 +08:00
{
2014-08-27 16:43:14 +08:00
V3F_C4B_T2F * q = & _verts [ i + _filledVertex ] ;
2014-08-27 14:54:35 +08:00
Vec3 * vec1 = ( Vec3 * ) & q - > vertices ;
2014-04-10 22:38:57 +08:00
modelView . transformPoint ( vec1 ) ;
2014-01-16 06:35:26 +08:00
}
2014-08-27 16:43:14 +08:00
2014-08-27 18:39:49 +08:00
const unsigned short * indices = cmd - > getIndices ( ) ;
2014-08-27 16:43:14 +08:00
//fill index
2014-08-27 18:39:49 +08:00
for ( ssize_t i = 0 ; i < cmd - > getIndexCount ( ) ; + + i )
2014-08-27 16:43:14 +08:00
{
2014-08-27 18:39:49 +08:00
_indices [ _filledIndex + i ] = _filledVertex + indices [ i ] ;
2014-08-27 16:43:14 +08:00
}
2014-08-27 18:39:49 +08:00
_filledVertex + = cmd - > getVertexCount ( ) ;
_filledIndex + = cmd - > getIndexCount ( ) ;
2014-01-16 06:35:26 +08:00
}
2014-10-16 14:15:29 +08:00
void Renderer : : fillQuads ( const QuadCommand * cmd )
{
const Mat4 & modelView = cmd - > getModelView ( ) ;
2014-12-30 15:07:50 +08:00
const V3F_C4B_T2F * quads = ( V3F_C4B_T2F * ) cmd - > getQuads ( ) ;
2014-10-16 14:15:29 +08:00
for ( ssize_t i = 0 ; i < cmd - > getQuadCount ( ) * 4 ; + + i )
{
2014-12-30 15:07:50 +08:00
_quadVerts [ i + _numberQuads * 4 ] = quads [ i ] ;
modelView . transformPoint ( quads [ i ] . vertices , & ( _quadVerts [ i + _numberQuads * 4 ] . vertices ) ) ;
2014-10-16 14:15:29 +08:00
}
_numberQuads + = cmd - > getQuadCount ( ) ;
}
void Renderer : : drawBatchedTriangles ( )
2013-11-11 16:14:29 +08:00
{
2014-08-30 03:54:24 +08:00
//TODO: we can improve the draw performance by insert material switching command before hand.
2013-11-14 02:26:26 +08:00
2014-08-27 16:43:14 +08:00
int indexToDraw = 0 ;
int startIndex = 0 ;
2013-11-05 01:14:22 +08:00
2013-11-11 16:14:29 +08:00
//Upload buffer to VBO
2014-08-28 09:58:39 +08:00
if ( _filledVertex < = 0 | | _filledIndex < = 0 | | _batchedCommands . empty ( ) )
2013-11-19 07:52:47 +08:00
{
2013-11-11 16:14:29 +08:00
return ;
2013-11-19 07:52:47 +08:00
}
2013-11-11 16:14:29 +08:00
2013-12-05 09:02:02 +08:00
if ( Configuration : : getInstance ( ) - > supportsShareableVAO ( ) )
{
2014-08-27 14:54:35 +08:00
//Bind VAO
2014-10-16 14:15:29 +08:00
GL : : bindVAO ( _buffersVAO ) ;
2013-12-05 09:02:02 +08:00
//Set VBO data
glBindBuffer ( GL_ARRAY_BUFFER , _buffersVBO [ 0 ] ) ;
2013-11-11 16:14:29 +08:00
2014-01-17 13:35:58 +08:00
// option 1: subdata
// glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] );
// option 2: data
// glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * (n-start), &quads_[start], GL_DYNAMIC_DRAW);
// option 3: orphaning + glMapBuffer
2014-08-27 14:54:35 +08:00
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _verts [ 0 ] ) * _filledVertex , nullptr , GL_DYNAMIC_DRAW ) ;
2013-12-05 09:02:02 +08:00
void * buf = glMapBuffer ( GL_ARRAY_BUFFER , GL_WRITE_ONLY ) ;
2014-08-27 14:54:35 +08:00
memcpy ( buf , _verts , sizeof ( _verts [ 0 ] ) * _filledVertex ) ;
2013-12-05 09:02:02 +08:00
glUnmapBuffer ( GL_ARRAY_BUFFER ) ;
2013-11-11 16:14:29 +08:00
2013-12-05 09:02:02 +08:00
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
2014-08-27 14:54:35 +08:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _buffersVBO [ 1 ] ) ;
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( _indices [ 0 ] ) * _filledIndex , _indices , GL_STATIC_DRAW ) ;
2013-12-05 09:02:02 +08:00
}
else
{
2014-08-27 14:54:35 +08:00
# define kQuadSize sizeof(_verts[0])
2013-12-05 09:02:02 +08:00
glBindBuffer ( GL_ARRAY_BUFFER , _buffersVBO [ 0 ] ) ;
2014-08-27 14:54:35 +08:00
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _verts [ 0 ] ) * _filledVertex , _verts , GL_DYNAMIC_DRAW ) ;
2013-12-05 09:02:02 +08:00
GL : : enableVertexAttribs ( GL : : VERTEX_ATTRIB_FLAG_POS_COLOR_TEX ) ;
// vertices
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_POSITION , 3 , GL_FLOAT , GL_FALSE , kQuadSize , ( GLvoid * ) offsetof ( V3F_C4B_T2F , vertices ) ) ;
// colors
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_COLOR , 4 , GL_UNSIGNED_BYTE , GL_TRUE , kQuadSize , ( GLvoid * ) offsetof ( V3F_C4B_T2F , colors ) ) ;
// tex coords
2014-05-09 03:34:26 +08:00
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_TEX_COORD , 2 , GL_FLOAT , GL_FALSE , kQuadSize , ( GLvoid * ) offsetof ( V3F_C4B_T2F , texCoords ) ) ;
2013-12-05 09:02:02 +08:00
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _buffersVBO [ 1 ] ) ;
2014-08-27 14:54:35 +08:00
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , sizeof ( _indices [ 0 ] ) * _filledIndex , _indices , GL_STATIC_DRAW ) ;
2013-12-05 09:02:02 +08:00
}
2013-11-11 16:14:29 +08:00
2013-11-12 03:54:08 +08:00
//Start drawing verties in batch
2014-08-28 09:58:39 +08:00
for ( const auto & cmd : _batchedCommands )
2013-11-11 16:14:29 +08:00
{
2014-05-13 10:12:56 +08:00
auto newMaterialID = cmd - > getMaterialID ( ) ;
2014-10-16 14:15:29 +08:00
if ( _lastMaterialID ! = newMaterialID | | newMaterialID = = MATERIAL_ID_DO_NOT_BATCH )
2013-11-11 16:14:29 +08:00
{
2014-01-22 15:52:00 +08:00
//Draw quads
2014-08-27 16:43:14 +08:00
if ( indexToDraw > 0 )
2013-11-09 04:06:39 +08:00
{
2014-08-27 16:43:14 +08:00
glDrawElements ( GL_TRIANGLES , ( GLsizei ) indexToDraw , GL_UNSIGNED_SHORT , ( GLvoid * ) ( startIndex * sizeof ( _indices [ 0 ] ) ) ) ;
2014-02-08 11:37:44 +08:00
_drawnBatches + + ;
2014-08-27 16:43:14 +08:00
_drawnVertices + = indexToDraw ;
2013-11-09 04:06:39 +08:00
2014-08-27 16:43:14 +08:00
startIndex + = indexToDraw ;
indexToDraw = 0 ;
2013-11-09 04:06:39 +08:00
}
2013-11-05 01:14:22 +08:00
2014-01-22 15:52:00 +08:00
//Use new material
cmd - > useMaterial ( ) ;
2014-05-13 10:12:56 +08:00
_lastMaterialID = newMaterialID ;
2013-11-11 16:14:29 +08:00
}
2014-01-22 15:52:00 +08:00
2014-08-27 18:39:49 +08:00
indexToDraw + = cmd - > getIndexCount ( ) ;
2013-11-05 01:14:22 +08:00
}
2014-10-16 14:56:17 +08:00
//Draw any remaining triangles
2014-08-27 16:43:14 +08:00
if ( indexToDraw > 0 )
2013-11-12 03:54:08 +08:00
{
2014-08-27 16:43:14 +08:00
glDrawElements ( GL_TRIANGLES , ( GLsizei ) indexToDraw , GL_UNSIGNED_SHORT , ( GLvoid * ) ( startIndex * sizeof ( _indices [ 0 ] ) ) ) ;
2014-02-08 11:37:44 +08:00
_drawnBatches + + ;
2014-08-27 16:43:14 +08:00
_drawnVertices + = indexToDraw ;
2013-11-12 03:54:08 +08:00
}
2013-11-08 07:48:37 +08:00
2013-12-05 09:02:02 +08:00
if ( Configuration : : getInstance ( ) - > supportsShareableVAO ( ) )
{
//Unbind VAO
GL : : bindVAO ( 0 ) ;
}
else
{
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
}
2014-08-28 09:58:39 +08:00
_batchedCommands . clear ( ) ;
2014-08-27 14:54:35 +08:00
_filledVertex = 0 ;
_filledIndex = 0 ;
2013-11-08 07:48:37 +08:00
}
2014-10-16 14:15:29 +08:00
void Renderer : : drawBatchedQuads ( )
{
//TODO: we can improve the draw performance by insert material switching command before hand.
2015-05-06 04:07:32 +08:00
ssize_t indexToDraw = 0 ;
2014-10-16 14:15:29 +08:00
int startIndex = 0 ;
//Upload buffer to VBO
if ( _numberQuads < = 0 | | _batchQuadCommands . empty ( ) )
{
return ;
}
if ( Configuration : : getInstance ( ) - > supportsShareableVAO ( ) )
{
//Bind VAO
GL : : bindVAO ( _quadVAO ) ;
//Set VBO data
glBindBuffer ( GL_ARRAY_BUFFER , _quadbuffersVBO [ 0 ] ) ;
// option 1: subdata
2015-05-06 06:59:41 +08:00
// glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] );
2014-10-16 14:15:29 +08:00
// option 2: data
2015-05-06 06:59:41 +08:00
// glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * (n-start), &quads_[start], GL_DYNAMIC_DRAW);
2014-10-16 14:15:29 +08:00
// option 3: orphaning + glMapBuffer
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _quadVerts [ 0 ] ) * _numberQuads * 4 , nullptr , GL_DYNAMIC_DRAW ) ;
void * buf = glMapBuffer ( GL_ARRAY_BUFFER , GL_WRITE_ONLY ) ;
memcpy ( buf , _quadVerts , sizeof ( _quadVerts [ 0 ] ) * _numberQuads * 4 ) ;
glUnmapBuffer ( GL_ARRAY_BUFFER ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _quadbuffersVBO [ 1 ] ) ;
}
else
{
# define kQuadSize sizeof(_verts[0])
glBindBuffer ( GL_ARRAY_BUFFER , _quadbuffersVBO [ 0 ] ) ;
glBufferData ( GL_ARRAY_BUFFER , sizeof ( _quadVerts [ 0 ] ) * _numberQuads * 4 , _quadVerts , GL_DYNAMIC_DRAW ) ;
GL : : enableVertexAttribs ( GL : : VERTEX_ATTRIB_FLAG_POS_COLOR_TEX ) ;
// vertices
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_POSITION , 3 , GL_FLOAT , GL_FALSE , kQuadSize , ( GLvoid * ) offsetof ( V3F_C4B_T2F , vertices ) ) ;
// colors
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_COLOR , 4 , GL_UNSIGNED_BYTE , GL_TRUE , kQuadSize , ( GLvoid * ) offsetof ( V3F_C4B_T2F , colors ) ) ;
// tex coords
glVertexAttribPointer ( GLProgram : : VERTEX_ATTRIB_TEX_COORD , 2 , GL_FLOAT , GL_FALSE , kQuadSize , ( GLvoid * ) offsetof ( V3F_C4B_T2F , texCoords ) ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , _quadbuffersVBO [ 1 ] ) ;
}
2015-05-06 04:07:32 +08:00
// FIXME: The logic of this code is confusing, and error prone
// Needs refactoring
//Start drawing vertices in batch
2014-10-16 14:15:29 +08:00
for ( const auto & cmd : _batchQuadCommands )
{
2015-05-06 04:07:32 +08:00
bool commandQueued = true ;
2014-10-16 14:15:29 +08:00
auto newMaterialID = cmd - > getMaterialID ( ) ;
if ( _lastMaterialID ! = newMaterialID | | newMaterialID = = MATERIAL_ID_DO_NOT_BATCH )
{
2015-05-06 04:07:32 +08:00
// flush buffer
2014-10-16 14:15:29 +08:00
if ( indexToDraw > 0 )
{
glDrawElements ( GL_TRIANGLES , ( GLsizei ) indexToDraw , GL_UNSIGNED_SHORT , ( GLvoid * ) ( startIndex * sizeof ( _indices [ 0 ] ) ) ) ;
_drawnBatches + + ;
_drawnVertices + = indexToDraw ;
startIndex + = indexToDraw ;
indexToDraw = 0 ;
}
//Use new material
_lastMaterialID = newMaterialID ;
2015-05-06 04:07:32 +08:00
2015-05-06 06:59:41 +08:00
cmd - > useMaterial ( ) ;
2015-05-06 04:07:32 +08:00
}
if ( commandQueued )
{
indexToDraw + = cmd - > getQuadCount ( ) * 6 ;
2014-10-16 14:15:29 +08:00
}
}
//Draw any remaining quad
if ( indexToDraw > 0 )
{
glDrawElements ( GL_TRIANGLES , ( GLsizei ) indexToDraw , GL_UNSIGNED_SHORT , ( GLvoid * ) ( startIndex * sizeof ( _indices [ 0 ] ) ) ) ;
_drawnBatches + + ;
_drawnVertices + = indexToDraw ;
}
if ( Configuration : : getInstance ( ) - > supportsShareableVAO ( ) )
{
//Unbind VAO
GL : : bindVAO ( 0 ) ;
}
else
{
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
}
_batchQuadCommands . clear ( ) ;
_numberQuads = 0 ;
}
2013-11-09 04:06:39 +08:00
void Renderer : : flush ( )
2014-06-23 23:58:45 +08:00
{
flush2D ( ) ;
flush3D ( ) ;
}
void Renderer : : flush2D ( )
2013-11-09 04:06:39 +08:00
{
2015-01-12 14:20:41 +08:00
flushQuads ( ) ;
flushTriangles ( ) ;
2013-11-09 04:06:39 +08:00
}
2014-06-23 23:58:45 +08:00
void Renderer : : flush3D ( )
{
if ( _lastBatchedMeshCommand )
{
2014-06-24 19:45:51 +08:00
_lastBatchedMeshCommand - > postBatchDraw ( ) ;
2014-06-23 23:58:45 +08:00
_lastBatchedMeshCommand = nullptr ;
}
}
2015-01-10 06:06:21 +08:00
void Renderer : : flushQuads ( )
{
if ( _numberQuads > 0 )
{
drawBatchedQuads ( ) ;
_lastMaterialID = 0 ;
}
}
void Renderer : : flushTriangles ( )
{
if ( _filledIndex > 0 )
{
drawBatchedTriangles ( ) ;
_lastMaterialID = 0 ;
}
}
2014-04-15 07:46:19 +08:00
2015-01-10 06:06:21 +08:00
// helpers
2014-05-15 01:07:09 +08:00
bool Renderer : : checkVisibility ( const Mat4 & transform , const Size & size )
2014-04-15 07:46:19 +08:00
{
2014-08-27 12:09:23 +08:00
auto scene = Director : : getInstance ( ) - > getRunningScene ( ) ;
// only cull the default camera. The culling algorithm is valid for default camera.
2014-09-01 09:56:05 +08:00
if ( scene & & scene - > _defaultCamera ! = Camera : : getVisitingCamera ( ) )
2014-08-27 12:09:23 +08:00
return true ;
2014-04-15 07:46:19 +08:00
// half size of the screen
Size screen_half = Director : : getInstance ( ) - > getWinSize ( ) ;
screen_half . width / = 2 ;
screen_half . height / = 2 ;
float hSizeX = size . width / 2 ;
float hSizeY = size . height / 2 ;
2014-05-15 01:07:09 +08:00
Vec4 v4world , v4local ;
2014-04-15 22:35:35 +08:00
v4local . set ( hSizeX , hSizeY , 0 , 1 ) ;
transform . transformVector ( v4local , & v4world ) ;
2014-04-15 07:46:19 +08:00
// center of screen is (0,0)
v4world . x - = screen_half . width ;
v4world . y - = screen_half . height ;
// convert content size to world coordinates
2014-04-15 22:35:35 +08:00
float wshw = std : : max ( fabsf ( hSizeX * transform . m [ 0 ] + hSizeY * transform . m [ 4 ] ) , fabsf ( hSizeX * transform . m [ 0 ] - hSizeY * transform . m [ 4 ] ) ) ;
float wshh = std : : max ( fabsf ( hSizeX * transform . m [ 1 ] + hSizeY * transform . m [ 5 ] ) , fabsf ( hSizeX * transform . m [ 1 ] - hSizeY * transform . m [ 5 ] ) ) ;
2014-04-15 07:46:19 +08:00
// compare if it in the positive quadrant of the screen
float tmpx = ( fabsf ( v4world . x ) - wshw ) ;
float tmpy = ( fabsf ( v4world . y ) - wshh ) ;
bool ret = ( tmpx < screen_half . width & & tmpy < screen_half . height ) ;
return ret ;
}
2015-01-10 06:06:21 +08:00
void Renderer : : setClearColor ( const Color4F & clearColor )
{
_clearColor = clearColor ;
}
2014-01-17 13:49:14 +08:00
NS_CC_END