pgeRippleSprite cocos2d-x版本移植

//OpenGL ES 2.0 Shader版本的修改完成,使用GPU计算纹理的位置能显著提高效率。

参考http://blog.csdn.net/yuanhong2910/article/details/18766385


在 www.cocos2d-iphone.org/forum/topic/25090 看到了Birkemose 实现的基于cocos2d的水波纹效果,改成了cocos2d-x的C++版本。

程序尊重原作者,只做了一点小小的修改, 原程序中需要用户自己调用update, 我Porting的版本中在init的时候注册了schedule(), 不需要用户调用update.


/* pgeRippleSprite.h*/

//
//  pgeRippleSprite.h
//  rippleDemo
//
//  Created by Lars Birkemose on 02/12/11.
//  Copyright 2011 Protec Electronics. All rights reserved.
//
// --------------------------------------------------------------------------
// import headers


// porting to cplusplus by wanghong.li (wanghong.li1029@163.com)on 04/01/12
// All rights reserved


#include "cocos2d.h"
#include <list>


// --------------------------------------------------------------------------
// defines


#define RIPPLE_DEFAULT_QUAD_COUNT_X             30         
#define RIPPLE_DEFAULT_QUAD_COUNT_Y             20 


#define RIPPLE_BASE_GAIN                        0.1f        // an internal constant


#define RIPPLE_DEFAULT_RADIUS                   500         // radius in pixels  
#define RIPPLE_DEFAULT_RIPPLE_CYCLE             0.25f       // timing on ripple ( 1/frequenzy )
#define RIPPLE_DEFAULT_LIFESPAN                 3.6f        // entire ripple lifespan


#define RIPPLE_CHILD_MODIFIER                   2.0f


// --------------------------------------------------------------------------
// typedefs


typedef enum {
    RIPPLE_TYPE_RUBBER,                                     // a soft rubber sheet
    RIPPLE_TYPE_GEL,                                        // high viscosity fluid
    RIPPLE_TYPE_WATER,                                      // low viscosity fluid
} RIPPLE_TYPE;


typedef enum {
    RIPPLE_CHILD_LEFT,
    RIPPLE_CHILD_TOP,
    RIPPLE_CHILD_RIGHT,
    RIPPLE_CHILD_BOTTOM,
    RIPPLE_CHILD_COUNT
} RIPPLE_CHILD;


typedef struct _rippleData {
    bool                    parent;                         // ripple is a parent
    bool                    childCreated[ 4 ];              // child created ( in the 4 direction )
    RIPPLE_TYPE             rippleType;                     // type of ripple ( se update: )
    cocos2d::CCPoint        center;                         // ripple center ( but you just knew that, didn't you? )
    cocos2d::CCPoint        centerCoordinate;               // ripple center in texture coordinates
    float                   radius;                         // radius at which ripple has faded 100%
    float                   strength;                       // ripple strength 
    float                   runtime;                        // current run time
    float                   currentRadius;                  // current radius
    float                   rippleCycle;                    // ripple cycle timing
    float                   lifespan;                       // total life span       
} rippleData;


// --------------------------------------------------------------------------
// pgeRippleSprite




typedef list<rippleData*>::iterator         RIPPLE_DATA_LIST;
typedef list<rippleData*>::reverse_iterator REVERSE_RIPPLE_DATA_LIST;


class CCpgeRippleSprite : public cocos2d::CCNode
{
public:
    CCpgeRippleSprite();
    ~CCpgeRippleSprite();


    CC_SYNTHESIZE(cocos2d::CCTexture2D*, m_texture, Texture)
    CC_SYNTHESIZE(int, m_quadCountX, QuadCountX)
    CC_SYNTHESIZE(int, m_quadCountY, QuadCountY)
    CC_SYNTHESIZE(int, m_VerticesPrStrip, VerticesPrStrip)
    CC_SYNTHESIZE(int, m_bufferSize, BuffSize)
    CC_SYNTHESIZE(cocos2d::CCPoint*, m_vertice, Vertice)
    CC_SYNTHESIZE(cocos2d::CCPoint*, m_textureCoordinate, TextureCoordinate)
    CC_SYNTHESIZE(cocos2d::CCPoint*, m_rippleCoordinate, RippleCoordinate)
    CC_SYNTHESIZE_READONLY(bool*, m_edgeVertice, EdgeVertice)
    CC_SYNTHESIZE_READONLY_PASS_BY_REF(std::list<rippleData*>, m_rippleList, RippleList)


public:
    static CCpgeRippleSprite* rippleSpriteWithFile(const char* filename);


    bool initWithFile(const char* filename);
    virtual void draw();
    void  update(cocos2d::ccTime dt);
    void  addRipple(cocos2d::CCPoint &pos, RIPPLE_TYPE type, float strength);


protected:
    void  tesselate();
    void  addRippleChild(rippleData* parent, RIPPLE_CHILD type);
    
};


/*pgeRippleSprite.cpp*/

//
//  pgeRippleSprite.m
//  rippleDemo
//
//  Created by Lars Birkemose on 02/12/11.
//  Copyright 2011 Protec Electronics. All rights reserved.
//


// porting to cplusplus by wanghong.li (wanghong.li1029@163.com)on 04/01/12
// All rights reserved
// --------------------------------------------------------------------------
// include headers


#include "pgeRippleSprite.h"


// --------------------------------------------------------------------------
// implementation


using namespace cocos2d;


CCpgeRippleSprite* CCpgeRippleSprite::rippleSpriteWithFile(const char* filename)
{
    CCpgeRippleSprite* pgeRippleSprite = new CCpgeRippleSprite();
    if(pgeRippleSprite && pgeRippleSprite->initWithFile(filename))
    {
        pgeRippleSprite->autorelease();
        return pgeRippleSprite;
    }


    CC_SAFE_DELETE(pgeRippleSprite);
    return NULL;
}


CCpgeRippleSprite::CCpgeRippleSprite()
:m_texture(NULL),
m_vertice(NULL),
m_textureCoordinate(NULL),
m_rippleCoordinate(NULL),
m_edgeVertice(NULL)
{


}


CCpgeRippleSprite::~CCpgeRippleSprite()
{
    CC_SAFE_RELEASE(m_texture);
    CC_SAFE_DELETE_ARRAY(m_vertice);
    CC_SAFE_DELETE_ARRAY(m_textureCoordinate);
    CC_SAFE_DELETE_ARRAY(m_rippleCoordinate);
    CC_SAFE_DELETE_ARRAY(m_edgeVertice);


    RIPPLE_DATA_LIST iterBegin = m_rippleList.begin();


    while (iterBegin != m_rippleList.end())
    {
        rippleData* date = *iterBegin;


        CC_SAFE_DELETE(date);


       iterBegin++;
    }
    m_rippleList.clear();
}


bool CCpgeRippleSprite::initWithFile(const char* filename)
{
    m_texture = CCTextureCache::sharedTextureCache()->addImage(filename);
    if (!m_texture)
    {
        return false;
    }
    m_texture->retain();


    m_vertice = NULL;
    m_textureCoordinate = NULL;
    CC_SAFE_DELETE_ARRAY(m_vertice);
    CC_SAFE_DELETE_ARRAY(m_textureCoordinate);
    CC_SAFE_DELETE_ARRAY(m_rippleCoordinate);
    CC_SAFE_DELETE_ARRAY(m_edgeVertice);
    m_quadCountX = RIPPLE_DEFAULT_QUAD_COUNT_X;
    m_quadCountY = RIPPLE_DEFAULT_QUAD_COUNT_Y;


    tesselate();


    schedule(schedule_selector(CCpgeRippleSprite::update));


    return true;
}


void CCpgeRippleSprite::draw()
{
    if (false == getIsVisible())
    {
        return;
    }


    glPushMatrix();


    glDisableClientState(GL_COLOR_ARRAY);


    glBindTexture(GL_TEXTURE_2D, m_texture->getName());


    glTexCoordPointer(2, GL_FLOAT, 0, (m_rippleList.size() == 0) ? m_textureCoordinate : m_rippleCoordinate);


    glVertexPointer(2, GL_FLOAT, 0, m_vertice);


    for (int strip = 0; strip < m_quadCountY; strip++)
    {
        glDrawArrays(GL_TRIANGLE_STRIP, strip * m_VerticesPrStrip, m_VerticesPrStrip);
    }


    glEnableClientState( GL_COLOR_ARRAY );


    // restore
    glPopMatrix( );
}


void CCpgeRippleSprite::tesselate()
{
    int vertexPos = 0;
    CCPoint normalized;


    CC_SAFE_DELETE_ARRAY(m_vertice);
    CC_SAFE_DELETE_ARRAY(m_textureCoordinate);
    CC_SAFE_DELETE_ARRAY(m_rippleCoordinate);
    CC_SAFE_DELETE_ARRAY(m_edgeVertice);


    m_VerticesPrStrip = 2 * (m_quadCountX + 1);
    m_bufferSize = m_VerticesPrStrip * m_quadCountY;


    //allocate buffers


    m_vertice = new CCPoint[m_bufferSize];
    m_textureCoordinate = new CCPoint[m_bufferSize];
    m_rippleCoordinate  = new CCPoint[m_bufferSize];
    m_edgeVertice = new bool[m_bufferSize];


    vertexPos = 0;


    for (int y = 0; y < m_quadCountY; y++)
    {
        for (int x = 0; x < (m_quadCountX + 1); x++)
        {
            for ( int yy = 0; yy < 2; yy ++ ) {


                // first simply calculate a normalized position into rectangle
                normalized.x = ( float )x / ( float )m_quadCountX;
                normalized.y = ( float )( y + yy ) / ( float )m_quadCountY;


                // calculate vertex by multiplying rectangle ( texture ) size
                CCSize contentSize = m_texture->getContentSize();
                m_vertice[ vertexPos ] = ccp( normalized.x * contentSize.width, normalized.y * contentSize.height);


                // adjust texture coordinates according to texture size
                // as a texture is always in the power of 2, maxS and maxT are the fragment of the size actually used
                // invert y on texture coordinates
                m_textureCoordinate[ vertexPos ] = ccp( normalized.x * m_texture->getMaxS(), m_texture->getMaxT()- ( normalized.y * m_texture->getMaxT() ) );


                // check if vertice is an edge vertice, because edge vertices are never modified to keep outline consistent
                m_edgeVertice[ vertexPos ] = ( 
                    ( x == 0 ) || 
                    ( x == m_quadCountX ) ||
                    ( ( y == 0 ) && ( yy == 0 ) ) || 
                    ( ( y == ( m_quadCountY - 1 ) ) && ( yy > 0 ) ) );


                // next buffer pos
                vertexPos ++;


            }
        }
    }
}


void CCpgeRippleSprite::addRipple(cocos2d::CCPoint &pos, RIPPLE_TYPE type, float strength)
{
    rippleData* newRipple;


    // allocate new ripple
    newRipple = new rippleData;


    // initialize ripple
    newRipple->parent = true;
    for ( int count = 0; count < 4; count ++ ) newRipple->childCreated[ count ] = false;
    newRipple->rippleType = type;
    newRipple->center = pos;


    CCSize contentSize = m_texture->getContentSize();
    newRipple->centerCoordinate = ccp( pos.x / contentSize.width * m_texture->getMaxS(), m_texture->getMaxT() - ( pos.y / contentSize.height * m_texture->getMaxT()) );
    newRipple->radius = RIPPLE_DEFAULT_RADIUS; // * strength;
    newRipple->strength = strength;
    newRipple->runtime = 0;
    newRipple->currentRadius = 0;
    newRipple->rippleCycle = RIPPLE_DEFAULT_RIPPLE_CYCLE;
    newRipple->lifespan = RIPPLE_DEFAULT_LIFESPAN;


    // add ripple to running list 
    m_rippleList.push_back(newRipple);
}


void CCpgeRippleSprite::addRippleChild(rippleData* parent, RIPPLE_CHILD type)
{
    rippleData* newRipple;
    CCPoint pos;


    // allocate new ripple
    newRipple = new rippleData;


    // new ripple is pretty much a copy of its parent
    memcpy( newRipple, parent, sizeof( rippleData ) );


    // not a parent
    newRipple->parent = false;


    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
    // mirror position
    switch ( type ) {
        case RIPPLE_CHILD_LEFT:
            pos = ccp( -parent->center.x, parent->center.y );
            break;
        case RIPPLE_CHILD_TOP:
            pos = ccp( parent->center.x, winSize.height + ( winSize.height - parent->center.y ) );
            break;
        case RIPPLE_CHILD_RIGHT:
            pos = ccp( winSize.width + ( winSize.width - parent->center.x ), parent->center.y );            
            break;
        case RIPPLE_CHILD_BOTTOM:
        default:
            pos = ccp( parent->center.x, -parent->center.y );            
            break;
    }


    newRipple->center = pos;


    CCSize contentSize = m_texture->getContentSize();


    newRipple->centerCoordinate = ccp( pos.x / contentSize.width * m_texture->getMaxS(), m_texture->getMaxT()- ( pos.y / contentSize.height * m_texture->getMaxT()) );
    newRipple->strength *= RIPPLE_CHILD_MODIFIER;


    // indicate child used
    parent->childCreated[ type ] = true;        


    // add ripple to running list 
    m_rippleList.push_back(newRipple);
}


void CCpgeRippleSprite::update(cocos2d::ccTime dt)
{
    rippleData* ripple;
    CCPoint pos;
    float distance, correction;


    // test if any ripples at all
    if ( m_rippleList.size() == 0 ) return;


    // ripples are simulated by altering texture coordinates
    // on all updates, an entire new array is calculated from the base array 
    // not maintainng an original set of texture coordinates, could result in accumulated errors
    memcpy( m_rippleCoordinate, m_textureCoordinate, m_bufferSize * sizeof( CCPoint ) );


    // scan through running ripples
    // the scan is backwards, so that ripples can be removed on the fly


    CCSize winSize = CCDirector::sharedDirector()->getWinSize();


    REVERSE_RIPPLE_DATA_LIST iterRipple = m_rippleList.rbegin();


    while (  iterRipple != m_rippleList.rend()) 
    {


        // get ripple data
        ripple = *iterRipple;


        // scan through all texture coordinates
        for ( int count = 0; count < m_bufferSize; count ++ ) {


            // dont modify edge vertices
            if ( m_edgeVertice[ count ] == false ) {


                // calculate distance
                // you might think it would be faster to do a box check first
                // but it really isnt, 
                // ccpDistance is like my sexlife - BAM! - and its all over
                distance = ccpDistance( ripple->center, m_vertice[ count ] );


                // only modify vertices within range
                if ( distance <= ripple->currentRadius ) {


                    // load the texture coordinate into an easy to use var
                    pos = m_rippleCoordinate[ count ];  


                    // calculate a ripple 
                    switch ( ripple->rippleType ) {


                        case RIPPLE_TYPE_RUBBER:
                            // method A
                            // calculate a sinus, based only on time
                            // this will make the ripples look like poking a soft rubber sheet, since sinus position is fixed
                            correction = sinf( 2 * M_PI * ripple->runtime / ripple->rippleCycle );
                            break;


                        case RIPPLE_TYPE_GEL:
                            // method B
                            // calculate a sinus, based both on time and distance
                            // this will look more like a high viscosity fluid, since sinus will travel with radius                            
                            correction = sinf( 2 * M_PI * ( ripple->currentRadius - distance ) / ripple->radius * ripple->lifespan / ripple->rippleCycle );
                            break;


                        case RIPPLE_TYPE_WATER:
                        default:
                            // method c
                            // like method b, but faded for time and distance to center
                            // this will look more like a low viscosity fluid, like water     


                            correction = ( ripple->radius * ripple->rippleCycle / ripple->lifespan ) / ( ripple->currentRadius - distance );
                            if ( correction > 1.0f ) correction = 1.0f;


                            // fade center of quicker
                            correction *= correction;


                            correction *= sinf( 2 * M_PI * ( ripple->currentRadius - distance ) / ripple->radius * ripple->lifespan / ripple->rippleCycle );
                            break;


                    }


                    // fade with distance
                    correction *= 1 - ( distance / ripple->currentRadius );


                    // fade with time
                    correction *= 1 - ( ripple->runtime / ripple->lifespan );


                    // adjust for base gain and user strength
                    correction *= RIPPLE_BASE_GAIN;
                    correction *= ripple->strength;


                    // finally modify the coordinate by interpolating
                    // because of interpolation, adjustment for distance is needed, 
                    correction /= ccpDistance( ripple->centerCoordinate, pos );
                    pos = ccpAdd( pos, ccpMult( ccpSub( pos, ripple->centerCoordinate ), correction ) );


                    // another approach for applying correction, would be to calculate slope from center to pos
                    // and then adjust based on this


                    // clamp texture coordinates to avoid artifacts
                    pos = ccpClamp( pos, CCPointZero, ccp( m_texture->getMaxS(), m_texture->getMaxT()) );


                    // save modified coordinate
                    m_rippleCoordinate[ count ] = pos;


                }
            }
        }


        // calculate radius
        ripple->currentRadius = ripple->radius * ripple->runtime / ripple->lifespan;


        // check if ripple should expire
        ripple->runtime += dt;
        if ( ripple->runtime >= ripple->lifespan )
        {
            // free memory, and remove from list
            CC_SAFE_DELETE( ripple );


            RIPPLE_DATA_LIST it = --iterRipple.base() ;  
            RIPPLE_DATA_LIST it_after_del = m_rippleList.erase(it); 
            iterRipple = list<rippleData*>::reverse_iterator(it_after_del); 
        } 
        else
        {
            // check for creation of child ripples
            if ( ripple->parent == true ) {


                // left ripple
                if ( ( ripple->childCreated[ RIPPLE_CHILD_LEFT ] == false ) && ( ripple->currentRadius > ripple->center.x ) ) {
                    addRippleChild(ripple, RIPPLE_CHILD_LEFT);
                } 


                // top ripple
                if ( ( ripple->childCreated[ RIPPLE_CHILD_TOP ] == false ) && ( ripple->currentRadius > winSize.height - ripple->center.y ) ) {
                    addRippleChild(ripple, RIPPLE_CHILD_TOP);
                }


                // right ripple
                if ( ( ripple->childCreated[ RIPPLE_CHILD_RIGHT ] == false ) && ( ripple->currentRadius > winSize.width - ripple->center.x ) ) {
                    addRippleChild(ripple, RIPPLE_CHILD_RIGHT);
                }


                // bottom ripple
                if ( ( ripple->childCreated[ RIPPLE_CHILD_BOTTOM ] == false ) && ( ripple->currentRadius > ripple->center.y ) ) {
                    addRippleChild(ripple, RIPPLE_CHILD_BOTTOM);
                } 
            }
            iterRipple++;
        }
    }
}






  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值