Search code examples
libgdx

How to crop texture based on the other object it touched?


I have a line actor that might have other object that intersect with it, And I need to crop out that part.

enter image description here

Above is the image actor

enter image description here

this rectangle is also a image actor might appear randomly along the lines.

enter image description here And this is the sample of the result I wanted to get. I need advice on how to achieve this with libgdx.

[EDIT]

As suggest I am trying to use fbo to draw into a buffer. Below is the code I am currently working on.

@Override
public void draw(Batch batch, float parentAlpha) {

    fbo.begin();
    getStage().getViewport().apply();

    Gdx.gl.glClearColor(0f,0f,0f,0f);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.draw(trLine,position.x,position.y);
    batch.flush();

    fbo.end();
    getStage().getViewport().apply();

    batch.draw(fbo.getColorBufferTexture(),0,0);

}

I am able to buffer the draw into the buffer and draw it later but it happen to be different size. below is the code for creation and dispose of fbo. and it is outside of the draw loop.

fbo = new FrameBuffer(Pixmap.Format.RGBA8888,getStage().getViewport().getWidth(),getStage().getViewport().getHeight(),false,true);

[SOLVED FBO]

Below is the coding that have working fbo but the blending is not working as expected. Will keep trying until it works.

fbo.begin();

Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

batch.begin();
batch.draw(trLine,position.x,position.y);
batch.end();


int srcFunc = batch.getBlendSrcFunc();
int dstFunc = batch.getBlendDstFunc();

batch.enableBlending();
batch.begin();
batch.setBlendFunction(GL20.GL_ONE, GL20.GL_FUNC_REVERSE_SUBTRACT);
for(int i = 0 ; i < cropRectangles.size() ; i++){   batch.draw(cropTexture.get(i),cropRectangles.get(i).x,cropRectangles.get(i).y);
}
batch.end();

fbo.end();

getStage().getViewport().apply();

//reset blending before drawing the desire result
batch.begin();
batch.setBlendFunction(srcFunc, dstFunc);
batch.draw(fbo.getColorBufferTexture(),0,0);
batch.end();

But the output is not getting any blending effect. it is still a rectangle with filled white color.

enter image description here

[SOLVED FULL CODE]

I finally apply the equation correctly and able to reset it so it doesn't affect other things that I draw after this.

fbo.begin();

Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

batch.begin();
batch.draw(trLine,position.x,position.y);
batch.end();


int srcFunc = batch.getBlendSrcFunc();
int dstFunc = batch.getBlendDstFunc();



batch.enableBlending();

batch.begin();
batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glBlendEquation(GL20.GL_FUNC_REVERSE_SUBTRACT);

for(int i = 0 ; i < cropRectangles.size() ; i++){

                batch.draw(cropTexture.get(i),cropRectangles.get(i).x,cropRectangles.get(i).y);

}

batch.end();
batch.flush();

fbo.end();

Gdx.gl.glBlendEquation(GL20.GL_FUNC_ADD);

getStage().getViewport().apply();

batch.begin();
batch.setBlendFunction(srcFunc, dstFunc);
batch.draw(fbo.getColorBufferTexture(),0,0);
batch.end();

Solution

  • You can use blend mode to achieve this.Your rectangle should have 2 parts. Outer part and transparent part.

    Outer part is your actual part going to be draw as usual.

    Transparent part will be another rectangle with a full alpha and you should use blending for this part.

    Visual Blending Tool

    examplemode

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
    

    This mode clearing intersection area, it seems like correct mode .

    You can easly find example usages of blending in libgdx.

        SpriteBatch sb = (SpriteBatch)batch;
    
        // draw our destination image
        sb.draw(dst, 0, 0);
        sb.end();
    
        // remember SpriteBatch's current functions
        int srcFunc = sb.getBlendSrcFunc();
        int dstFunc = sb.getBlendDstFunc();
    
        // Let's enable blending
        sb.enableBlending();
        sb.begin();
    
        // blend them
        sb.setBlendFunction(GL20.GL_ONE, GL20.ONE_MINUS_SRC_ALPHA);
        sb.draw(src, 0, 0);
    
        // Reset
        sb.end();
        sb.begin();
        sb.setBlendFunction(srcFunc, dstFunc);
    

    Additionally you must change blend equation as well. And its not unique for sprite batch so we need to change for all game.

    //Equation for effect you want    
    Gdx.gl.glBlendEquation(GL20.GL_FUNC_REVERSE_SUBTRACT);
    
    //After draw you should also reset this
    Gdx.gl.glBlendEquation(GL20.GL_FUNC_ADD);
    

    Now we should take this drawn to FrameBufferObject because transparent area will show background color of your spritebatch. If it's okey for you then it's done but you want to see another texture at this transparent area like background image or something then we have one more step.

    You should read this article for whats the purpose of FBO(FrameBufferObject)

    Frame Buffer from official wiki

    You need to use this for merge your sprites and transparent areas so you can use those as whole image and see through background images from transparent area.

    Maybe using second viewport or sprite batch would be easier and much more efficient according to your game.