//----------------------------------------------------------------------------- // Copyright (c) 2013 GarageGames, LLC // // 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. //----------------------------------------------------------------------------- #ifndef _SPRITE_BATCH_H_ #include "SpriteBatch.h" #endif #ifndef _SPRITE_BATCH_QUERY_H_ #include "2d/core/SpriteBatchQuery.h" #endif #ifndef _SCENE_RENDER_OBJECT_H_ #include "2d/scene/SceneRenderObject.h" #endif //------------------------------------------------------------------------------ static StringTableEntry spritesNodeName = StringTable->insert( "Sprites" ); //------------------------------------------------------------------------------ SpriteBatch::SpriteBatch() : mMasterBatchId( 0 ), mSelectedSprite( NULL ), mBatchSortMode( SceneRenderQueue::RENDER_SORT_OFF ), mDefaultSpriteStride( 1.0f, 1.0f), mDefaultSpriteSize( 1.0f, 1.0f ), mDefaultSpriteAngle( 0.0f ), mpSpriteBatchQuery( NULL ), mBatchCulling( true ) { // Reset batch transform. mBatchTransform.SetIdentity(); mBatchTransformDirty = true; mBatchTransformId = 0; // Reset local extents. mLocalExtents.SetZero(); mLocalExtentsDirty = true; } //------------------------------------------------------------------------------ SpriteBatch::~SpriteBatch() { } //----------------------------------------------------------------------------- bool SpriteBatch::onAdd() { // Create the sprite batch query if required. if ( mBatchCulling ) createSpriteBatchQuery(); return true; } //----------------------------------------------------------------------------- void SpriteBatch::onRemove() { // Clear the sprites. clearSprites(); // Delete the sprite batch query. destroySpriteBatchQuery(); } //----------------------------------------------------------------------------- void SpriteBatch::prepareRender( SceneRenderObject* pSceneRenderObject, const SceneRenderState* pSceneRenderState, SceneRenderQueue* pSceneRenderQueue ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_PrepareRender); // Set the sort mode. pSceneRenderQueue->setSortMode( getBatchSortMode() ); // Calculate local AABB. const b2AABB localAABB = calculateLocalAABB( pSceneRenderState->mRenderAABB ); // Do we have a sprite batch query? if ( mpSpriteBatchQuery != NULL ) { // Yes, so debug Profiling. PROFILE_START(SpriteBatch_PrepareRenderQuery); // Yes, so fetch sprite batch query and clear results. SpriteBatchQuery* pSpriteBatchQuery = getSpriteBatchQuery( true ); // Perform query. pSpriteBatchQuery->queryArea( localAABB, false ); // Debug Profiling. PROFILE_END(); // SpriteBatch_PrepareRenderQuery // Fetch result count. const U32 resultCount = pSpriteBatchQuery->getQueryResultsCount(); // Finish if no results. if ( resultCount == 0 ) return; // Fetch results. typeSpriteBatchQueryResultVector& queryResults = pSpriteBatchQuery->getQueryResults(); // Add picked sprites. for ( U32 n = 0; n < resultCount; n++ ) { // Fetch sprite batch Item. SpriteBatchItem* pSpriteBatchItem = queryResults[n].mpSpriteBatchItem; // Skip if not visible. if ( !pSpriteBatchItem->getVisible() ) continue; // Create a render request. SceneRenderRequest* pSceneRenderRequest = pSceneRenderQueue->createRenderRequest(); // Prepare batch item. pSpriteBatchItem->prepareRender( pSceneRenderRequest, mBatchTransformId ); // Set identity. pSceneRenderRequest->mpSceneRenderObject = pSceneRenderObject; // Set custom data. pSceneRenderRequest->mpCustomData1 = pSpriteBatchItem; } // Clear sprite batch query. pSpriteBatchQuery->clearQuery(); } else { // No, so perform a render request for all the sprites. for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr ) { // Fetch sprite batch Item. SpriteBatchItem* pSpriteBatchItem = spriteItr->value; // Skip if not visible. if ( !pSpriteBatchItem->getVisible() ) continue; // Create a render request. SceneRenderRequest* pSceneRenderRequest = pSceneRenderQueue->createRenderRequest(); // Prepare batch item. pSpriteBatchItem->prepareRender( pSceneRenderRequest, mBatchTransformId ); // Set identity. pSceneRenderRequest->mpSceneRenderObject = pSceneRenderObject; // Set custom data. pSceneRenderRequest->mpCustomData1 = pSpriteBatchItem; } } } //------------------------------------------------------------------------------ void SpriteBatch::render( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer ) { // Fetch sprite batch Item. SpriteBatchItem* pSpriteBatchItem = (SpriteBatchItem*)pSceneRenderRequest->mpCustomData1; // Batch render. pSpriteBatchItem->render( pBatchRenderer, pSceneRenderRequest, mBatchTransformId ); } //------------------------------------------------------------------------------ void SpriteBatch::createQueryProxy( SpriteBatchItem* pSpriteBatchItem ) { // Sanity! AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch:createQueryProxy() - Cannot create query proxy on NULL sprite batch item." ); // Debug Profiling. PROFILE_SCOPE(SpriteBatch_CreateTreeProxy); // Finish if the batch query is not available. if ( mpSpriteBatchQuery == NULL ) return; // Create proxy. pSpriteBatchItem->mProxyId = mpSpriteBatchQuery->add( pSpriteBatchItem ); } //------------------------------------------------------------------------------ void SpriteBatch::destroyQueryProxy( SpriteBatchItem* pSpriteBatchItem ) { // Sanity! AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch:destroyQueryProxy() - Cannot destroy query proxy on NULL sprite batch item." ); // Debug Profiling. PROFILE_SCOPE(SpriteBatch_DestroyTreeProxy); // Finish if the batch query is not available. if ( mpSpriteBatchQuery == NULL ) return; // Destroy proxy. mpSpriteBatchQuery->remove( pSpriteBatchItem ); // Remove proxy reference. pSpriteBatchItem->mProxyId = INVALID_SPRITE_PROXY; } //------------------------------------------------------------------------------ void SpriteBatch::moveQueryProxy( SpriteBatchItem* pSpriteBatchItem, const b2AABB& localAABB ) { // Sanity! AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch:moveQueryProxy() - Cannot move query proxy on NULL sprite batch item." ); // Debug Profiling. PROFILE_SCOPE(SpriteBatch_MoveTreeProxy); // Finish if the batch query is not available. if ( mpSpriteBatchQuery == NULL || pSpriteBatchItem->getProxyId() == INVALID_SPRITE_PROXY ) return; // Move proxy. mpSpriteBatchQuery->update( pSpriteBatchItem, localAABB, b2Vec2(0.0f, 0.0f) ); } //------------------------------------------------------------------------------ SpriteBatchQuery* SpriteBatch::getSpriteBatchQuery( const bool clearQuery ) { if ( mpSpriteBatchQuery == NULL ) return NULL; // Clear the query if specified. if ( clearQuery ) mpSpriteBatchQuery->clearQuery(); return mpSpriteBatchQuery; } //------------------------------------------------------------------------------ void SpriteBatch::copyTo( SpriteBatch* pSpriteBatch ) const { // Clear any existing sprites. pSpriteBatch->clearSprites(); // Set master sprite Id. pSpriteBatch->mMasterBatchId = mMasterBatchId; // Set batch sort mode. pSpriteBatch->setBatchSortMode( getBatchSortMode() ); // Set batch culling. pSpriteBatch->setBatchCulling( getBatchCulling() ); // Set sprite default size and angle. pSpriteBatch->setDefaultSpriteStride( getDefaultSpriteStride() ); pSpriteBatch->setDefaultSpriteSize( getDefaultSpriteSize() ); pSpriteBatch->setDefaultSpriteAngle( getDefaultSpriteAngle() ); // Copy sprites. for( typeSpriteBatchHash::const_iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr ) { // Fetch sprite. SpriteBatchItem* pSpriteBatchItem = spriteItr->value; // Add a sprite. const U32 spriteBatchId = pSpriteBatch->addSprite( pSpriteBatchItem->getLogicalPosition() ); // Fetch new sprite. SpriteBatchItem* pNewSpriteBatchItem = pSpriteBatch->findSpriteId( spriteBatchId ); // Push a copy to it. pSpriteBatchItem->copyTo( pNewSpriteBatchItem ); } } //------------------------------------------------------------------------------ U32 SpriteBatch::addSprite( const SpriteBatchItem::LogicalPosition& logicalPosition ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_AddSprite); // Create sprite layout. mSelectedSprite = createSprite( logicalPosition ); // Finish if no sprite created. if ( mSelectedSprite == NULL ) return 0; // Insert logical position into sprite positions if it's valid. if ( logicalPosition.isValid() ) mSpritePositions.insert( logicalPosition, mSelectedSprite ); // Flag local extents as dirty. setLocalExtentsDirty(); return mSelectedSprite->getBatchId(); } //------------------------------------------------------------------------------ bool SpriteBatch::removeSprite( void ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_RemoveSprite); // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return false; // Remove the sprite logical position if it's valid. const SpriteBatchItem::LogicalPosition& logicalPosition = mSelectedSprite->getLogicalPosition(); if ( logicalPosition.isValid() ) mSpritePositions.erase( mSelectedSprite->getLogicalPosition() ); // Fetch and remove any sprite name. StringTableEntry spriteName = mSelectedSprite->getName(); if ( spriteName != StringTable->EmptyString ) mSpriteNames.erase( spriteName ); // Destroy the sprite. destroySprite( mSelectedSprite->getBatchId() ); // Reset the selected sprite. mSelectedSprite = NULL; // Flag local extents as dirty. setLocalExtentsDirty(); return true; } //------------------------------------------------------------------------------ void SpriteBatch::clearSprites( void ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_ClearSprites); // Deselect any sprite. deselectSprite(); // Clear sprite positions. mSpritePositions.clear(); // Clear sprite names. mSpriteNames.clear(); // Cache all sprites. for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr ) { SpriteBatchItemFactory.cacheObject( spriteItr->value ); } mSprites.clear(); mMasterBatchId = 0; // Flag local extents as dirty. setLocalExtentsDirty(); } //------------------------------------------------------------------------------ void SpriteBatch::setBatchCulling( const bool batchCulling ) { // Finish if no change. if ( mBatchCulling == batchCulling ) return; // Set batch culling. mBatchCulling = batchCulling; // Create/destroy sprite batch query appropriately. if ( mBatchCulling ) createSpriteBatchQuery(); else destroySpriteBatchQuery(); } //------------------------------------------------------------------------------ bool SpriteBatch::selectSprite( const SpriteBatchItem::LogicalPosition& logicalPosition ) { // Select sprite. mSelectedSprite = findSpritePosition( logicalPosition ); // Finish if we selected the sprite. if ( mSelectedSprite != NULL ) return true; // Not selected so warn. Con::warnf( "Cannot select sprite at logical position '%s' as one does not exist.", logicalPosition.getString() ); return false; } //------------------------------------------------------------------------------ bool SpriteBatch::selectSpriteId( const U32 batchId ) { // Select sprite. mSelectedSprite = findSpriteId( batchId ); // Finish if we selected the sprite. if ( mSelectedSprite != NULL ) return true; // Not selected so warn. Con::warnf( "Cannot select sprite Id '%d' as it does not exist.", batchId ); return false; } //------------------------------------------------------------------------------ bool SpriteBatch::selectSpriteName( const char* pName ) { // Fail if no name specified. if ( pName == NULL || pName == StringTable->EmptyString ) return false; // Select sprite. mSelectedSprite = findSpriteName( pName ); // Finish if we selected the sprite. if ( mSelectedSprite != NULL ) return true; // Not selected so warn. Con::warnf( "Cannot select sprite name '%s' as it does not exist.", pName ); return false; } //------------------------------------------------------------------------------ U32 SpriteBatch::getSpriteId( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return 0; // Get sprite id. return mSelectedSprite->getBatchId(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteImage( const char* pAssetId, const U32 imageFrame ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_SetSpriteImage); // Sanity! AssertFatal( pAssetId, "Cannot set sprite image using a NULL asset Id." ); // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set image and frame. mSelectedSprite->setImage( pAssetId, imageFrame ); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteImage( const char* pAssetId, const char* namedFrame ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_SetSpriteImage); // Sanity! AssertFatal( pAssetId, "Cannot set sprite image using a NULL asset Id." ); // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set image and frame. mSelectedSprite->setImage( pAssetId, namedFrame ); } //------------------------------------------------------------------------------ StringTableEntry SpriteBatch::getSpriteImage( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return StringTable->EmptyString; // Get sprite image. return mSelectedSprite->getImage(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteImageFrame( const U32 imageFrame ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set image frame. mSelectedSprite->setImageFrame( imageFrame ); } //------------------------------------------------------------------------------ U32 SpriteBatch::getSpriteImageFrame( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return NULL; // Get image frame. return mSelectedSprite->getImageFrame(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteNamedImageFrame( const char* namedFrame ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set image frame. mSelectedSprite->setNamedImageFrame( namedFrame ); } //------------------------------------------------------------------------------ StringTableEntry SpriteBatch::getSpriteNamedImageFrame( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return NULL; // Get image frame. return mSelectedSprite->getNamedImageFrame(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteAnimation( const char* pAssetId ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_SetSpriteAnimation); // Sanity! AssertFatal( pAssetId, "Cannot set sprite animation using a NULL asset Id." ); // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set animation. mSelectedSprite->setAnimation( pAssetId ); } //------------------------------------------------------------------------------ StringTableEntry SpriteBatch::getSpriteAnimation( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return StringTable->EmptyString; // Get animation. return mSelectedSprite->getAnimation(); } //------------------------------------------------------------------------------ void SpriteBatch::clearSpriteAsset( void ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Clear the asset. mSelectedSprite->clearAssets(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteVisible( const bool visible ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set visibility. mSelectedSprite->setVisible( visible ); } //------------------------------------------------------------------------------ bool SpriteBatch::getSpriteVisible( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return false; // Get visibility. return mSelectedSprite->getVisible(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteLocalPosition( const Vector2& localPosition ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set local position. mSelectedSprite->setLocalPosition( localPosition ); // Flag local extents as dirty. setLocalExtentsDirty(); } //------------------------------------------------------------------------------ Vector2 SpriteBatch::getSpriteLocalPosition( void ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return Vector2::getZero(); // Get local position. return mSelectedSprite->getLocalPosition(); } //------------------------------------------------------------------------------ const SpriteBatchItem::LogicalPosition SpriteBatch::getSpriteLogicalPosition( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return NULL; // Get logical position. return mSelectedSprite->getLogicalPosition(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteAngle( const F32 localAngle ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set local angle. mSelectedSprite->setLocalAngle( localAngle ); // Flag local extents as dirty. setLocalExtentsDirty(); } //------------------------------------------------------------------------------ F32 SpriteBatch::getSpriteAngle( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return 0.0f; // Get local angle. return mSelectedSprite->getLocalAngle(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteDepth( const F32 depth ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set depth. mSelectedSprite->setDepth( depth ); } //------------------------------------------------------------------------------ F32 SpriteBatch::getSpriteDepth( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return 0.0f; // Get depth. return mSelectedSprite->getDepth(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteSize( const Vector2& size ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set size. mSelectedSprite->setSize( size ); // Flag local extents as dirty. setLocalExtentsDirty(); } //------------------------------------------------------------------------------ Vector2 SpriteBatch::getSpriteSize( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return Vector2::getZero(); // Get size. return mSelectedSprite->getSize(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteFlipX( const bool flipX ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set flip X. mSelectedSprite->setFlipX( flipX ); } //------------------------------------------------------------------------------ bool SpriteBatch::getSpriteFlipX( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return false; // Get flip X. return mSelectedSprite->getFlipX(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteFlipY( const bool flipY ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set flip Y. mSelectedSprite->setFlipY( flipY ); } //------------------------------------------------------------------------------ bool SpriteBatch::getSpriteFlipY( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return false; // Get flip Y. return mSelectedSprite->getFlipY(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteSortPoint( const Vector2& sortPoint ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set sort point. mSelectedSprite->setSortPoint( sortPoint ); } //------------------------------------------------------------------------------ Vector2 SpriteBatch::getSpriteSortPoint( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return Vector2::getZero(); // Get sort point. return mSelectedSprite->getSortPoint(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteRenderGroup( const char* pRenderGroup ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set render group. mSelectedSprite->setRenderGroup( pRenderGroup ); } //------------------------------------------------------------------------------ StringTableEntry SpriteBatch::getSpriteRenderGroup( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return NULL; // Get render group. return mSelectedSprite->getRenderGroup(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteBlendMode( const bool blendMode ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set blend mode. mSelectedSprite->setBlendMode( blendMode ); } //------------------------------------------------------------------------------ bool SpriteBatch::getSpriteBlendMode( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return true; // Get blend mode. return mSelectedSprite->getBlendMode(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteSrcBlendFactor( GLenum srcBlendFactor ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set source blend factor. mSelectedSprite->setSrcBlendFactor( srcBlendFactor ); } //------------------------------------------------------------------------------ GLenum SpriteBatch::getSpriteSrcBlendFactor( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return GL_SRC_ALPHA; // Get source blend factor. return mSelectedSprite->getSrcBlendFactor(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteDstBlendFactor( GLenum dstBlendFactor ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return ; // Set destination blend factor. mSelectedSprite->setDstBlendFactor( dstBlendFactor ); } //------------------------------------------------------------------------------ GLenum SpriteBatch::getSpriteDstBlendFactor( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return GL_ONE_MINUS_SRC_ALPHA; // Get destination blend factor. return mSelectedSprite->getDstBlendFactor(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteBlendColor( const ColorF& blendColor ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set blend color. mSelectedSprite->setBlendColor( blendColor ); } //------------------------------------------------------------------------------ const ColorF& SpriteBatch::getSpriteBlendColor( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return ColorF::StockColor("White"); // Get blend color. return mSelectedSprite->getBlendColor(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteBlendAlpha( const F32 alpha ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set blend alpha. mSelectedSprite->setBlendAlpha( alpha ); } //------------------------------------------------------------------------------ F32 SpriteBatch::getSpriteBlendAlpha( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return 0.0f; // Get blend alpha. return mSelectedSprite->getBlendAlpha(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteAlphaTest( const F32 alphaTestMode ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set alpha-test mode. mSelectedSprite->setAlphaTest( alphaTestMode ); } //------------------------------------------------------------------------------ F32 SpriteBatch::getSpriteAlphaTest( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return -1.0f; // Get alpha-test mode. return mSelectedSprite->getAlphaTest(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteDataObject( SimObject* pDataObject ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set data object. mSelectedSprite->setDataObject( pDataObject ); } //------------------------------------------------------------------------------ SimObject* SpriteBatch::getSpriteDataObject( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return NULL; // Get data object. return mSelectedSprite->getDataObject(); } //------------------------------------------------------------------------------ void SpriteBatch::setUserData( void* pUserData ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Set user data. mSelectedSprite->setUserData( pUserData ); } //------------------------------------------------------------------------------ void* SpriteBatch::getUserData( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return NULL; // Get user data. return mSelectedSprite->getUserData(); } //------------------------------------------------------------------------------ void SpriteBatch::setSpriteName( const char* pName ) { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return; // Finish if the sprite name already exists. if ( findSpriteName( pName ) ) return; // Insert sprite name. mSpriteNames.insert( StringTable->insert( pName ), mSelectedSprite ); // Set name. mSelectedSprite->setName( pName ); } //------------------------------------------------------------------------------ StringTableEntry SpriteBatch::getSpriteName( void ) const { // Finish if a sprite is not selected. if ( !checkSpriteSelected() ) return StringTable->EmptyString; // Get name. return mSelectedSprite->getName(); } //------------------------------------------------------------------------------ SpriteBatchItem* SpriteBatch::createSprite( void ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_CreateSprite); // Allocate batch Id. const U32 batchId = ++mMasterBatchId; // Create sprite batch item, SpriteBatchItem* pSpriteBatchItem = SpriteBatchItemFactory.createObject(); // Set batch parent. pSpriteBatchItem->setBatchParent( this, batchId ); // Create sprite batch item, mSprites.insert( batchId, pSpriteBatchItem ); return pSpriteBatchItem; } //------------------------------------------------------------------------------ SpriteBatchItem* SpriteBatch::createSprite( const Vector2* explicitVertices ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_CreateSprite); // Allocate batch Id. const U32 batchId = ++mMasterBatchId; // Create sprite batch item, SpriteBatchItem* pSpriteBatchItem = SpriteBatchItemFactory.createObject(); // Set batch parent. pSpriteBatchItem->setBatchParent( this, batchId ); // Set explicit mode. pSpriteBatchItem->setExplicitMode( true ); // Set explicit vertices. // Create sprite batch item, mSprites.insert( batchId, pSpriteBatchItem ); return pSpriteBatchItem; } //------------------------------------------------------------------------------ SpriteBatchItem* SpriteBatch::findSpritePosition( const SpriteBatchItem::LogicalPosition& logicalPosition ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_FindSpritePosition); // Invalid logical positions are not stored. if ( !logicalPosition.isValid() ) return NULL; // Find sprite. typeSpritePositionHash::iterator spriteItr = mSpritePositions.find( logicalPosition ); return spriteItr == mSpritePositions.end() ? NULL : spriteItr->value; } //------------------------------------------------------------------------------ SpriteBatchItem* SpriteBatch::findSpriteId( const U32 batchId ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_FindSpriteId); // Find sprite. typeSpriteBatchHash::iterator spriteItr = mSprites.find( batchId ); return spriteItr != mSprites.end() ? spriteItr->value : NULL; } //------------------------------------------------------------------------------ SpriteBatchItem* SpriteBatch::findSpriteName( const char* pName ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_FindSpriteName); // Finish if no name specified. if ( pName == NULL || pName == StringTable->EmptyString ) return NULL; // Find sprite. typeSpriteNameHash::iterator spriteItr = mSpriteNames.find( StringTable->insert(pName) ); return spriteItr != mSpriteNames.end() ? spriteItr->value : NULL; } //------------------------------------------------------------------------------ SpriteBatchItem* SpriteBatch::createSprite( const SpriteBatchItem::LogicalPosition& logicalPosition ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_CreateSpriteAtLogicalPosition); // Reset sprite batch item. SpriteBatchItem* pSpriteBatchItem = NULL; // Do we have a valid logical position? if ( logicalPosition.isValid() ) { // Does it have the correct argument count? if ( logicalPosition.getArgCount() != 2 ) { // No, so warn. Con::warnf( "Invalid logical position specified for composite sprite." ); return NULL; } // Does the sprite already exist? if ( findSpritePosition( logicalPosition ) != NULL ) { // Yes, so warn. Con::warnf( "Cannot add sprite at logical position '%s' as one already exists.", logicalPosition.getString() ); return NULL; } // Create the sprite. pSpriteBatchItem = createSprite(); // Set the logical position. pSpriteBatchItem->setLogicalPosition( logicalPosition ); // Set the sprite default local position. pSpriteBatchItem->setLocalPosition( logicalPosition.getAsVector2() ); } else { // Create the sprite. pSpriteBatchItem = createSprite(); } // Set the sprite default size and angle. pSpriteBatchItem->setSize( getDefaultSpriteSize() ); pSpriteBatchItem->setLocalAngle( getDefaultSpriteAngle() ); return pSpriteBatchItem; } //------------------------------------------------------------------------------ void SpriteBatch::setBatchTransform( const b2Transform& batchTransform ) { // Update world transform. mBatchTransform = batchTransform; // Flag the batch transform as dirty. setBatchTransformDirty(); } //------------------------------------------------------------------------------ void SpriteBatch::updateLocalExtents( void ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_UpdateLocalExtents); // Finish if the local extents are not dirty. if ( !mLocalExtentsDirty ) return; // Flag as NOT dirty. mLocalExtentsDirty = false; // Do we have any sprites? if ( mSprites.size() == 0 ) { // No, so reset local extents. mLocalExtents.setOne(); return; } // Fetch first sprite. typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); // Set render AABB to this sprite. b2AABB localAABB = spriteItr->value->getLocalAABB(); // Combine with the rest of the sprites. for( ; spriteItr != mSprites.end(); ++spriteItr ) { localAABB.Combine( spriteItr->value->getLocalAABB() ); } // Fetch local render extents. const b2Vec2& localLowerExtent = localAABB.lowerBound; const b2Vec2& localUpperExtent = localAABB.upperBound; // Calculate maximum extents. const F32 lowerExtentX = mFabs(localLowerExtent.x); const F32 lowerExtentY = mFabs(localLowerExtent.y); const F32 upperExtentX = mFabs(localUpperExtent.x); const F32 upperExtentY = mFabs(localUpperExtent.y); // Calculate local extents. mLocalExtents.Set( mFabs(lowerExtentX > upperExtentX ? lowerExtentX : upperExtentX) * 2.0f, mFabs(lowerExtentY > upperExtentY ? lowerExtentY : upperExtentY) * 2.0f ); } //------------------------------------------------------------------------------ void SpriteBatch::createSpriteBatchQuery( void ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_CreateSpriteBatchQuery); // Finish if batch culling is off or there is already a sprite batch query. if ( !mBatchCulling || mpSpriteBatchQuery != NULL ) return; // Set the sprite batch query appropriately. mpSpriteBatchQuery = new SpriteBatchQuery( this ); // Finish if there are no sprites. if ( mSprites.size() == 0 ) return; // Add proxies for all the sprites. for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr ) { // Fetch sprite batch item. SpriteBatchItem* pSpriteBatchItem = spriteItr->value; // Create query proxy for sprite. createQueryProxy( pSpriteBatchItem ); } } //------------------------------------------------------------------------------ void SpriteBatch::destroySpriteBatchQuery( void ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_DestroySpriteBatchQuery); // Finish if there is no sprite batch query. if ( mpSpriteBatchQuery == NULL ) return; // Are there any sprites? if ( mSprites.size() > 0 ) { // Yes, so destroy proxies of all the sprites. for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr ) { // Destroy query proxy for sprite. destroyQueryProxy( spriteItr->value ); } } // Finish if sprite clipping delete mpSpriteBatchQuery; mpSpriteBatchQuery = NULL; } //------------------------------------------------------------------------------ bool SpriteBatch::destroySprite( const U32 batchId ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_DestroySprite); // Find sprite. typeSpriteBatchHash::iterator spriteItr = mSprites.find( batchId ); // Finish if sprite not found. if ( spriteItr == mSprites.end() ) return false; // Find sprite. SpriteBatchItem* pSpriteBatchItem = spriteItr->value; // Sanity! AssertFatal( pSpriteBatchItem != NULL, "SpriteBatch::destroySprite() - Found sprite but it was NULL." ); // Cache sprite. SpriteBatchItemFactory.cacheObject( pSpriteBatchItem ); // Remove from sprites. mSprites.erase( batchId ); return true; } //------------------------------------------------------------------------------ bool SpriteBatch::checkSpriteSelected( void ) const { // Finish if a sprite is selected. if ( mSelectedSprite != NULL ) return true; // No, so warn, Con::warnf( "Cannot perform sprite operation no sprite is selected." ); return false; } //------------------------------------------------------------------------------ b2AABB SpriteBatch::calculateLocalAABB( const b2AABB& renderAABB ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_CalculateLocalAABB); // Calculate local OOBB. b2Vec2 localOOBB[4]; CoreMath::mAABBtoOOBB( renderAABB, localOOBB ); CoreMath::mCalculateInverseOOBB( localOOBB, mBatchTransform, localOOBB ); // Calculate local AABB. b2AABB localAABB; CoreMath::mOOBBtoAABB( localOOBB, localAABB ); return localAABB; } //------------------------------------------------------------------------------ void SpriteBatch::onTamlCustomWrite( TamlCustomNodes& customNodes ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_TamlCustomWrite); // Finish if no sprites. if ( getSpriteCount() == 0 ) return; // Add sprites node. TamlCustomNode* pSpritesNode = customNodes.addNode( spritesNodeName ); // Write all sprites. for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr ) { // Write type with sprite item. spriteItr->value->onTamlCustomWrite( pSpritesNode ); } } //------------------------------------------------------------------------------ void SpriteBatch::onTamlCustomRead( const TamlCustomNodes& customNodes ) { // Debug Profiling. PROFILE_SCOPE(SpriteBatch_TamlCustomRead); // Find sprites custom node. const TamlCustomNode* pSpritesNode = customNodes.findNode( spritesNodeName ); // Finish if we don't have the node. if ( pSpritesNode == NULL ) return; // Fetch children nodes. const TamlCustomNodeVector& spriteNodes = pSpritesNode->getChildren(); // Iterate sprite item types. for( TamlCustomNodeVector::const_iterator spriteItr = spriteNodes.begin(); spriteItr != spriteNodes.end(); ++spriteItr ) { // Fetch sprite node. TamlCustomNode* pNode = *spriteItr; // Fetch alias name. StringTableEntry nodeName = pNode->getNodeName(); // Is this a known node name? if ( nodeName != spritesItemTypeName ) { // No, so warn. Con::warnf( "SpriteBatch - Unknown custom type '%s'.", nodeName ); continue; } // Create sprite. SpriteBatchItem* pSpriteBatchItem = createSprite(); // Read type with sprite item. pSpriteBatchItem->onTamlCustomRead( pNode ); // Fetch logical position. const SpriteBatchItem::LogicalPosition& logicalPosition = pSpriteBatchItem->getLogicalPosition(); // Did we get a logical position? if ( logicalPosition.isValid() ) { // Yes, so insert into sprite positions. mSpritePositions.insert( logicalPosition, pSpriteBatchItem ); } // Fetch sprite name. StringTableEntry spriteName = pSpriteBatchItem->getName(); // Did we get a sprite name? if ( spriteName != StringTable->EmptyString ) { // Yes, so insert into sprite names if it doesn't already exist. if ( mSpriteNames.find( spriteName ) != mSpriteNames.end() ) mSpriteNames.insert( spriteName, mSelectedSprite ); } } } //------------------------------------------------------------------------------ void SpriteBatch::WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlElement* pParentElement ) { // Sanity! AssertFatal( pClassRep != NULL, "SpriteBatch::WriteCustomTamlSchema() - ClassRep cannot be NULL." ); AssertFatal( pParentElement != NULL, "SpriteBatch::WriteCustomTamlSchema() - Parent Element cannot be NULL." ); char buffer[1024]; // Create sprite batch node element. TiXmlElement* pBatchNodeElement = new TiXmlElement( "xs:element" ); dSprintf( buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), spritesNodeName ); pBatchNodeElement->SetAttribute( "name", buffer ); pBatchNodeElement->SetAttribute( "minOccurs", 0 ); pBatchNodeElement->SetAttribute( "maxOccurs", 1 ); pParentElement->LinkEndChild( pBatchNodeElement ); // Create complex type. TiXmlElement* pBatchNodeComplexTypeElement = new TiXmlElement( "xs:complexType" ); pBatchNodeElement->LinkEndChild( pBatchNodeComplexTypeElement ); // Create choice element. TiXmlElement* pBatchNodeChoiceElement = new TiXmlElement( "xs:choice" ); pBatchNodeChoiceElement->SetAttribute( "minOccurs", 0 ); pBatchNodeChoiceElement->SetAttribute( "maxOccurs", "unbounded" ); pBatchNodeComplexTypeElement->LinkEndChild( pBatchNodeChoiceElement ); // Write sprite batch item. SpriteBatchItem::WriteCustomTamlSchema( pClassRep, pBatchNodeChoiceElement ); }