HELP - Converting some C++ Logic to Javascript

The following piece of C code is used as a solution to check a point on a sprite and return the alpha value of that point (ie. is the point that was checked on the sprite transparent or not).

I need help figuring out how I can achieve the same logic in Cocos2d JS. Here is the reference link to the code I got this from: http://stackoverflow.com/questions/10889170/cocos2d-2-0-ignoring-touches-to-transparent-areas-of-layers-sprites

- (BOOL)isPixelTransparentAtLocation:(CGPoint)loc 
{   
    //Convert the location to the node space
    CGPoint location = [self convertToNodeSpace:loc];

    //This is the pixel we will read and test
    UInt8 pixel[4];

    //Prepare a render texture to draw the receiver on, so you are able to read the required pixel and test it    
    CGSize screenSize = [[CCDirector sharedDirector] winSize];
    CCRenderTexture* renderTexture = [[CCRenderTexture alloc] initWithWidth:screenSize.width
                                                                     height:screenSize.height
                                                                pixelFormat:kCCTexture2DPixelFormat_RGBA8888];

    [renderTexture begin];

    //Draw the layer
    [self draw];    

    //Read the pixel
    glReadPixels((GLint)location.x,(GLint)location.y, kHITTEST_WIDTH, kHITTEST_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, pixel);

    //Cleanup
    [renderTexture end];
    [renderTexture release];

    //Test if the pixel's alpha byte is transparent
    return (pixel[3] == 0);
}

As I understand, since the sprite cannot be read directly, a texture is made with the sprite inserted, AND FROM THAT POINT, we can check the transparency of a point on the texture.

This is what I have so far, However I do not know the Cocos2d JS equivilent of “glReadPixels” function. Is there some other implementation I could use?

 var size = cc.winSize;
        var sprite = new cc.Sprite(res.CloseNormal_png); // Add Sprite
        sprite.setPosition(size.width/2,size.height/2); // Set to middle of screen
        
        spriteTexture = new cc.RenderTexture(size.width,size.height); // Initialize Texture to the entire screen size
        spriteTexture.setPosition(size.width/2,size.height/2); // Set the position of this texture to the middle of screen
        this.addChild(spriteTexture,1); // Add the texture to the layer
        spriteTexture.begin(); // Begin rendering the texture
        sprite.visit(); // Add the sprite to the texture
        

      /*  NEED HELP FINDING/WRITING CODE HERE.
        
            Get transparency / alpha value (RGBA, the A part) of texture at point(x,y) size.width/2,size.height/2.
        */
        
        spriteTexture.end(); // Finish rendering the texture

This looks like a 2.x code, I don’t think it will work for 3.x, are you doing this for the web? I have idea how to do it in native platforms…

Hi nite,

No it won’t be for web, just for android at the moment.

I was hoping someone would know a function for “read pixel at cc.Point on a texture and return RGBA values” in JS, if one exists.

It’s not easy

So basically you need a texture that supports access it’s pixel data, I remember someone created a CCTexture2DMutable A while ago, not sure if it still works… Add that to your project and use js-binding generator to generate the api you need.

Or depends on your game, you can make creative use of things like tilemap, have a transparent tile and you can test the id of it…

Thanks Nite, I think I might investigate that CCTexture2DMutable.

Unfortunately I am not too experienced with Cocos.
When you say “Add that to your project” would I add the CCTexture2dMutable.h and CCTexture2dMutable.cpp into:

root/frameworks/js-bindings/cocos2d-x/cocos/renderer ?

Also, if you don’t mind me asking for resources/document links, what is the js-binding generator?

In v2.x, gl.readPixels is how to read the colour of pixels.

Unfortunately, while the function works in v3.x, it doesn’t appear to work well for your intended use. There are a number of topics about per-pixel touch that are trying to use the technique in this code and they find the same thing; the render texture image isn’t available immediately after renderTexture.end() is called. (If I remember correctly, it’s something like because the render texture sends draw commands to the draw queue or something like that, you have to wait until the renderer has rendered everything before the render texture is updated.)

If you are using v3.0, you will either have to use nite’s suggested method or find a v3.0 compatible method, which will probably involve writing C++ code and binding it to JavaScript.

Hi grim. Funny you mention that… because for the past 3 hours I have been unable to figure out why this simple code isn’t working: I am expecting the following result:

JS: {“0”:0,“1”:0,“2”:255,“3”:255}

but I keep getting:

JS: {“0”:0,“1”:0,“2”:0,“3”:255}

var renderTexture = new cc.RenderTexture(winSize.width, winSize.height);
  renderTexture.begin();
  blue.visit(); // this adds a full screen layercolor to the texture (of rgba 0,0,255,255)

  var pixel = new Uint8Array(4);
  gl.readPixels(winSize.width/2, winSize.height/2, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  cc.log(JSON.stringify(pixel));

  renderTexture.end();

As you mentioned, it may have something to do with the draw commands being in queue. NOW, from other sources I have read that gl.readPixels actually reads the buffer? And if I haven’t sent a texture to the buffer, then the readPixels won’t give me accurate results.

Can you help me add a gl.buffer? (I don’t know the syntax).

Here is the blue layer if you want to debug my code to see your results:

    var winSize = cc.winSize;
    
    if( 'opengl' in cc.sys.capabilities ) {
        
        
        var blue = new cc.LayerColor(cc.color(0, 0, 255, 255));

        blue.scale = 2;
        blue.x = -winSize.width / 4;
        blue.y = -winSIze.height / 4;

        blue.visit();
        this.addChild(blue);
    }

What do you mean by adding gl.buffer?

Hi Marteh, please, did you succeed?
More than one week I’m trying to get out from same troubles… Reading pixels from RenderTexture and wondering what the hell is there in. Currently I asked [here][1]
I hope you did it and you could share your discovery :smiley:

Thank you,
Zbynek
[1]: RenderTexture does not clear buffer

you can draw the texture as a sprite to ideally a render texture, then you can use glread pixel

Only if you don’t want to use glReadPixels immediately after drawing to the render texture.

Yes, that’s the problem. I need to use glReadPixels immediately. In C++ we could call Director::getInstance()->getRenderer()->render(); to force the renderer to proceed pending commands immediately. But in javaScript I can not find way to obtain this renderer nor to call its functions. I develop for native platforms like Android, iOS and win32, not for browsers. I investigate how to use JSB but not succeed yet. I wonder that I can not find a way to obtain a director’s renderer through the Runtime’s calls. Folks what do you think about this approach?

Well, one problem with doing it through JSB is that the renderer doesn’t appear to have bindings, so to do it “properly” you would have to create bindings for the renderer and then bind the director’s getRenderer() function. Or you could create your own C++ helper class, add a function to call the renderer and then just create bindings for your helper class.

Or you could resort to a quick hack: Add a static function to the JSB for the director to call the renderer’s render function. I have no idea about using the bindings generator (or if it would work for this), but I can tell you how to change the JSB files to achieve this. If you would be comfortable messing around with them, there’s the 3 steps involved. (Hopefully there’s no reason why they didn’t choose to bind the renderer.)

Step 1:
In jsb_cocos2dx_auto.hpp, find the line bool js_cocos2dx_Director_getInstance(JSContext *cx, uint32_t argc, jsval *vp);. Copy and paste directly underneath it and change getInstance to render, so you now have:

bool js_cocos2dx_Director_getInstance(JSContext *cx, uint32_t argc, jsval *vp);
bool js_cocos2dx_Director_render(JSContext *cx, uint32_t argc, jsval *vp);

Step 2:
In jsb_cocos2dx_auto.cpp (may take a couple seconds to load because it has over 60,000 lines of code), search for the js_cocos2dx_Director_getInstance function. Under this function, add:

bool js_cocos2dx_Director_render(JSContext *cx, uint32_t argc, jsval *vp)
{
	if (argc == 0) {
		cocos2d::Director::getInstance()->getRenderer()->render();
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return true;
	}
	JS_ReportError(cx, "js_cocos2dx_Director_render : wrong number of arguments");
	return false;
}

Step 3:
2 functions down from Step 2 there should be the function js_register_cocos2dx_Director. Inside this you will find the line

static JSFunctionSpec st_funcs[] = {
	JS_FN("getInstance", js_cocos2dx_Director_getInstance, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),        
	JS_FS_END
};

Change this to:

static JSFunctionSpec st_funcs[] = {
	JS_FN("getInstance", js_cocos2dx_Director_getInstance, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),
        JS_FN("render", js_cocos2dx_Director_render, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),
	JS_FS_END
};

And you’re done. Step 1 adds the function declaration to the JSB header file. Step 2 adds the code that calls the renderer. And Step 3 basically adds the function to Javascript.

After this, you should be able to call cc.Director.render();

I think it could should work.

Wow! Thank you grimfate very very much!
Actually last two days I’ve been trying to call own C++ helper class from js. I came out from approach on http://www.cocos2d-x.org/wiki/How_to_bind_C++_to_Javascript where a tutorial is available and downloadable from https://github.com/iTyran/Tutorials/tree/master/jsb/JSBTest
Unfortunately I was not able to build the Runtime with this example. Beside other problems I crashed with JSBool type undefined… that I was not able to find.

Back to your suggested hack. It is perfect to test quickly if the render() function solve my issue. It also helps to understand better what is happening inside JSB. I’m trying it. I can build Runtime successfully.
But calling cc.Director.render(); from my js I got “TypeError: cc.Director.render is not a function”
Trying whatever - restarting cocos IDE, rebuilding Runtime, running in simulator as well as on device… still same error.

Grimfate, would you please have some suggestions? I guess some step 4 is missing.
Thank you very much.

Step 3 is the step that adds the function to JavaScript and allows you to call cc.Director.render(). Can you please double check you did this step correctly? Make sure you added the render line to st_funcs[] and not funcs[]?

Yes, I’m sure.

static JSFunctionSpec st_funcs[] = {
    JS_FN("getInstance", js_cocos2dx_Director_getInstance, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),
    JS_FN("render", js_cocos2dx_Director_render, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),
    JS_FS_END
};

The cpp file I have here ./frameworks/js-bindings/bindings/auto

but I also see subfolder “api” there and there is a file jsb_cocos2dx_auto_api.js
In it I can find some functions like “getInstance” on Director too.
Like this

/**
 * @method getInstance
 * @return {cc.Director}
 */
getInstance : function (
)
{
    return cc.Director;
},

};

May be the render() needs be here too?

I added it there and it did not help. Same result.

I didn’t add anything to that file, but you could try it. Try cc.director.render() too just in case.

I took another project I have with Cocos-JS 3.2 and copied and pasted my 3 steps, and it works for me. (I got the same error as you did until I realised I copied the “before” code instead of the “after” code.) I have tested in XCode on Mac and iPad.

Which version of Cocos are you using? And which platform are you testing on?

I work on Windows 8.1, Cocos Code IDE, cocos2d-x 3.3, cocos2d-js 3.2
testing on Android Nexus 5 and in runtime on windows directly from IDE.

I also tested cc.director.render(). Same error.
I even tested cc.Director._render(); Why: it seem strange but for example when I’m typing in IDE cc.Director. I got pop-up with functions and properties and there I can find _getInstance() but NOT getInstance()… Anyway, if I want to get the instance I have to use getInstance() version. Don’t know why.
Anyway thank you for your support.

Sorry, but I did not understand what you mean by copying “before” vs. “after” …