How to draw image using open gl on Cocos2dx

Hello everyone.
I’m use cocos2d-x v3.1 . I try to make a lingting effect. it show ok when i use DrawPrimitives::drawLine to draw a segments of lighting.
But when t draw a segment by a sprite use SpriteBatchNode, it very lag, the Fps down to 30 because it was add 100 sprite child.
I’m looking for how to draw a segment without addChild, maybe is draw texture use Open gl.
Can i do that and how to?
Thanks for your help.

Can you post your code where you use the SpriteBatchNode? I believe, if done properly, it should be fairly efficient… so maybe there’s something in that code we can optimize.

I’m use a SpriteBatchNode pixel = CCSpriteBatchNode::create(“Pixel.png”);
i draw sprite in func:

void Lightning::drawASegment(Vec2 pt1,Vec2 pt2,float thinkness){

Vec2 tangent = pt2 - pt1;
float rotation = (float)atan2(tangent.y, tangent.x);
float degs = -rotation * 180 / PI; 
const float ImageThickness = 8;
float thicknessScale = 1 / ImageThickness;

Vec2 middleScale = Vec2(tangent.length(), thicknessScale);

Sprite* _pixel = Sprite::createWithTexture(pixel->getTexture());
_pixel->setPosition(tangent/2+pt1);
_pixel->setScaleX(middleScale.x);
_pixel->setRotation(degs);
pixel->addChild(_pixel);

}

usual, there will be approximately add over 100 child

Here is screen shot when t draw a lighting.

So the drawASegment() function gets called about ~100 times?

Is it possible to have a function that just gets called once and loops ~100 times to create the line segments to see if that would improve performance?

I’m at a loss for other ideas, so far. 100 children, especially on a batch node, shouldn’t be a problem, I don’t think. It shouldn’t be too different from particle systems, as they behave like batch nodes and frequently have potentially more than 100 particles.

Yes, this function will called 100 times. I loop the list segments and call every segment. I think its have no difference if i call once and loop 100 times. Now i cant test this. I will test this case later. Thanks you.

Ok, let us know the results and I’ll continue thinking about this.

I’m so sorry for the delay!
I try to test the case and it show no difference. Here is my draw function:

void Lightning::draw(Renderer *renderer, const Mat4 &transform, bool transformUpdated){
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glLineWidth(1);
glEnable(GL_LINE_SMOOTH);

if (segmentLists.size() > 0)
{
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	std::list<Segment>::iterator temp = segmentLists.begin();
	for (int i = 0;i < segmentLists.size() ;i++) {
		auto l_front = temp;
		std::advance(l_front, i);
		Segment segment = (Segment)*l_front;

		Vec2 tangent = segment.pt2 - segment.pt1;
		float rotation = (float)atan2(tangent.y, tangent.x);
		float degs = -rotation * 180 / PI; 
		const float ImageThickness = 8;
		float thicknessScale = 1 / ImageThickness;

		CCLog("tangent %f %f deg = %f  rotation = %f",tangent.x,tangent.y,degs,rotation);

		Vec2 middleScale = Vec2(tangent.length(), thicknessScale);

		CCSprite* _pixel = CCSprite::createWithTexture(pixel->getTexture());
		_pixel->setPosition(tangent/2+segment.pt1);
		_pixel->setScaleX(middleScale.x);
		//_pixel->setScaleY(middleScale.y);
		_pixel->setRotation(degs);
		pixel->addChild(_pixel);
	}
	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnable(GL_TEXTURE_2D);

	}

}

Ok, maybe I need to ask what kind of device you’re running on, now.

I took the code from your original post and implemented a demo app to see the slow down. In my demo app, a touch anywhere on the screen creates a lightning strike from the touch to the edge of the screen (either bottom, left or right). I put in a counter to count the lightning segments and print them via CCLOG.

I tapped on the screen several times to make multiple lightning strikes and did not see the FPS fall below 45 or 50. and usually, shot immediately back up to 60.

Here are some results:

These 4 lightning strikes had:

cocos2d: Lighting segments: 401
cocos2d: Lighting segments: 356
cocos2d: Lighting segments: 415
cocos2d: Lighting segments: 380

Here is the code I used:

HelloWorldScene.h:

class HelloWorld : public cocos2d::Layer
{
private:
    float _linger;
    cocos2d::Size _visibleSize;
    CC_SYNTHESIZE_RETAIN(cocos2d::SpriteBatchNode *, pixel, Pixel);
    
public:
    static cocos2d::Scene* createScene();

    virtual bool init();  
    
    CREATE_FUNC(HelloWorld);
    
    void drawASegment(cocos2d::Vec2 pt1, cocos2d::Vec2 pt2, float thickness);
};

HelloWorldScene.cpp:

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    _visibleSize = Director::getInstance()->getVisibleSize();
    
    SpriteBatchNode *pixelBatch = SpriteBatchNode::create("blank.png");
    this->setPixel(pixelBatch);
    this->addChild(pixelBatch);
    
    auto eventListener = EventListenerTouchOneByOne::create();
    eventListener->onTouchBegan = [=](Touch *touch, Event *event)->bool {
        auto location = touch->getLocation();
        Vec2 last(location.x, location.y);

        _linger = 0.5;
        
        float direction = CCRANDOM_MINUS1_1() * 3;
        int cnt = 0;
        while (last.y > 0.0 && last.x > 0.0 && last.x < _visibleSize.width)
        {
            float deltaX = CCRANDOM_MINUS1_1() * 6 + direction;
            float deltaY = -CCRANDOM_0_1() * 6;
            Vec2 curr = last + Vec2(deltaX, deltaY);
            this->drawASegment(last, curr, 10.0);
            last = curr;
            _linger += 0.005;
            cnt++;
        }
        CCLOG("Lighting segments: %d", cnt);
        return true;
    };
    
    this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(eventListener, this);
    
    return true;
}


void HelloWorld::drawASegment(Vec2 pt1, Vec2 pt2, float thickness)
{
    Vec2 tangent = pt2 - pt1;
    float rotation = (float)atan2(tangent.y, tangent.x);
    float degs = -rotation * 180 / M_PI;
    const float ImageThickness = 8;
    float thicknessScale = 1 / ImageThickness;
    
    Vec2 middleScale = Vec2(tangent.length(), thicknessScale);
    
    Sprite *_pixel = Sprite::createWithTexture(pixel->getTexture());
    _pixel->setPosition(tangent/2+pt1);
    _pixel->setScaleX(middleScale.x);
    _pixel->setRotation(degs);

    DelayTime *delay = DelayTime::create(_linger);
    CallFunc *removePixel = CallFunc::create([_pixel](){
        _pixel->removeFromParent();
    });

    pixel->addChild(_pixel);
    this->runAction(Sequence::create(delay, removePixel, NULL));
}

blank.png is a png with a single white pixel. I did modify your drawASegment function slightly to include a feature that would remove the lightning segments piece by piece for a nice little effect. This was mostly so I wouldn’t have to rerun the app to clear the screen… but it looks nice too.

I’m running this on an iPad Air.

I left out the createScene function from the code snippet, as it’s the standard one that comes from the template.

i’m so sorry for my careless.
I has found a big misstake. On draw loop, i draw the lists of 100 Segments, every segment i drew with a sprite and add to SpriteBatchNode. But in new draw loop, i was create new 100 sprite and every draw loop , 100 new ones are created ><".
When i fix this bug, everyting run fine.
here is my demo with 144 segment

Thank you very much for your help!

Awesome! Glad to hear it!

Can you share your demo project ? Thanks you very much !