Integrating LiquidFun with Cocos2d-x: Part II

In Part I I described to how integrated LiquidFun with Cocos2d-x.
In this part (part II) I’ll describe how to render the particles using a basic water effect.

LiquidFun + Cocos2d-x
LiquidFun + Cocos2d-X using Render-To-Texture technique to simulate water.

Part I uses just one glDrawArrays(GL_POINTS, 0, total); to draw the particles. And although that works to draw “particles”, it is not enough to draw “water”.

Drawing “water” requires a more complex rendering algorithm, like the one used in this example. And implementing an algorithm similar that one is what this article describes.

The algorithm works more or less like this:

  • Choose a white circle and blur it.
    • You can blur the circle at runtime
    • Or you can blur it off-line.
  • Create an a new frame-buffer (think of a clean off-screen buffer where you can render whatever you want)
  • Render the particles into the newly created frame-buffer using the blurred circle
  • Now render the frame-buffer into the main color-buffer using a threshold. The threshold could be something like this:
    • If pixel.r < 0.1, discard the pixel (the pixel won’t be drawn)
    • If pixel.r < 0.2, draw a blue pixel (for the border, although this is optional)
    • else draw a white pixel (the inner part of the water)

How to do it using Cocos2d-x and LiquidFun

Let’s take the LFParticleSystemNode from Part I, and “evolve” it:

The first thing to do is to add the “off-screen” frame-buffer into the LFParticleSystemNode class. In Cocos2d-x, the “off-screen” buffers are created with the RenderTexture class. Example:

bool LFParticleSystemNode::init(b2ParticleSystem* particleSystem, float ratio)
{
...
    // create an off-screen frame-buffer with the size of the screen
    auto s = Director::getInstance()->getWinSize();
    _renderTexture = cocos2d::RenderTexture::create(s.width, s.height, Texture2D::PixelFormat::RGBA8888);
    this->addChild(_renderTexture);
    _renderTexture->setAnchorPoint(Point::ANCHOR_MIDDLE);
    _renderTexture->setPosition(Point(s.width/2, s.height/2));

    // Change the default shader. Use a the threshold shader
    auto program = GLProgram::createWithByteArrays(_renderTextureShaderVert, _renderTextureShaderFrag);
    auto programState = GLProgramState::getOrCreateWithGLProgram(program);
    programState->setUniformFloat("u_threshold_discard", 0.15);
    programState->setUniformFloat("u_threshold_border", 0.3);

...
}

And, as mentioned earlier, the RenderTexture (the off-screen frame-buffer) needs a shader with a threshold. The threshold shader should look like the following:

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform float u_threshold_discard;
uniform float u_threshold_border;

void main()
{
    vec4 color = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
    if( color.r < u_threshold_discard)
        // black or discard
        color = vec4(0,0,0,0);
    else if( color.r < u_threshold_border)
        // blue for the border
        color = vec4(0.2,0.2,0.9,1);
    else
        // white for the center
        color = vec4(1,1,1,1);
    gl_FragColor = color;
}

The values u_threshold_discard, and u_threshold_border are defined at runtime. In the example, they are set at 0.15 and 0.3 respectively.

The next thing to do is, to render the particles in the RenderTexture.

void LFParticleSystemNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t transformFlags)
{
    // tell RenderTexture to "capture" the particles
    _renderTexture->beginWithClear(0,0,0,0);

    _customCommand.init(_globalZOrder);
    _customCommand.func = CC_CALLBACK_0(LFParticleSystemNode::onDraw, this, transform, transformFlags);
    renderer->addCommand(&_customCommand);

    // tell RenderTexture to stop "capturing" the particles
    _renderTexture->end();
}

 The result is the following

Comparing simple glDraw() with glDraw() + RenderTexture
Left: Without RenderTexutre.  Right: RenderTexture + custom shader with threshold

Continue reading “Integrating LiquidFun with Cocos2d-x: Part II”

Advertisements

Integrating LiquidFun with Cocos2d-x: Part I

LiquidFun Testbed + Cocos2d-x
LiquidFun Testbed + Cocos2d-x

From LiquidFun’s site:

Based on Box2d, LiquidFun features particle-based fluid simulation. Game developers can use it for new game mechanics and add realistic physics to game play. Designers can use the library to create beautiful fluid interactive experiences.

Basically LiquidFun is Box2d plus an extension to simulate fluids using a particle system. To test it, download and install the official LiquidFun – Testbed, and LiquidFun – EyeCandy for Android.

Cocos2d-x already has Box2d integration, so in order to integrate Cocos2d-x with LiquidFun, we only need to integrate this new class: b2ParticleSystem.

LiquidFun’s b2ParticleSystem

I’m not going to describe how to use LiquidFun (for that, read its programmers guide). Instead, I’m going to describe how to integrate b2ParticleSystem in Cocos2d-x (also applicable to any other game engine).

For the integration, what we need is a Cocos2d-x node that knows how to render a b2ParticleSystem. And b2ParticleSystem has these 4 useful methods:

class b2ParticleSystem {
  ...
  // Get the number of particles.
  int32 GetParticleCount() const;

  // Get the particle radius.
  float32 GetRadius() const;

  // Get the position of each particle in Box2d's coordinate system
  // Array is length GetParticleCount()
  b2Vec2* GetPositionBuffer();

  // Get the color of each particle in RGBA Uint8 format.
  // Array is length GetParticleCount()
  b2ParticleColor* GetColorBuffer();
};

Ideally we should be able to reuse cocos2d::ParticleSystemQuad for the rendering, but we can’t because:

  • cocos2d::ParticleSystemQuad doesn’t support changing the attractor (this is a design bug, we need to fix it). A nil attractor would be needed for this case.
  • ParticleSystemQuad works with Quads, and not Points. And even if Points were supported (like in Cocos2d-x v1), it wouldn’t work because the points and colors should be in an interleaved array.
  • The other issue is the conversion between Box2d and Cocos2d-x coordinate system, but it would be easy to fix.

Continue reading “Integrating LiquidFun with Cocos2d-x: Part I”

Vistual Studio: First steps

Goals

  1. Compile and run cocos2d-x tests on the emulator
  2. Set a breakpoint in Visual Studio.

Running cpp-test on the Emulator

1. Download cocos2d-x v3.0

2. Unzip it and then go to cocos2d-x/build directory

$ cd cocos2d-x/build

3. Open cocos2d-wp8.vc2012.sln with Visual Studio

$ start cocos2d-wp8.vc2012.sln

4. Set cpp-tests (Windows Phone Silverlight 8) as the default project:

  • Go to the Solution Explorer
  • Right click on cpp-tests (Windows Phone Silverlight 8)
  • Click on Set as StartUp Project

vs_default_project

5. Run cpp-tests on the Emulator

  • Press the Emulator 8.1 WVGA 4 inch button

run-emulator

6. If the following Dialog pops-up, just press Retry:

hypervisor

And that’s all. You should see the cpp-tests running on the Emulator:

cocos2d-emulator

Continue reading “Vistual Studio: First steps”