Search code examples
matrixlibgdxrenderingbox2doffset

LibGDX Box2D matrix rendering question when using viewports


I have a question regarding rendering with box2d and libgdx. As you can see in the screenshot below I have a problem when changing the window resolution.

Box2d gets scaled over the entire screen although the viewport is only using a small portion of it. Also the lights get scaled and do not match the real position anymore (but I think this is related to the same issue).

My idea is that I somehow need to adjust the matrix (b2dCombinedMatrix) for box2d before rendering but I have no idea how.

Personally I think that I need to tell it that it should use the same "render boundaries" as the viewport but I cannot figure out how to do that.

Here is the render method (the issue is after the draw lights comments part):

public void render(final float alpha) {
    viewport.apply();

    spriteBatch.begin();
    AnimatedTiledMapTile.updateAnimationBaseTime();
    if (mapRenderer.getMap() != null) {
        mapRenderer.setView(gameCamera);
        for (TiledMapTileLayer layer : layersToRender) {
            mapRenderer.renderTileLayer(layer);
        }
    }

    // render game objects first because they are in the same texture atlas as the map so we avoid a texture binding --> better performance
    for (final Entity entity : gameObjectsForRender) {
        renderEntity(entity, alpha);
    }
    for (final Entity entity : charactersForRender) {
        renderEntity(entity, alpha);
    }
    spriteBatch.end();

    // draw lights
    b2dCombinedMatrix.set(spriteBatch.getProjectionMatrix());
    b2dCombinedMatrix.translate(0, RENDER_OFFSET_Y, 0);
    rayHandler.setCombinedMatrix(b2dCombinedMatrix, gameCamera.position.x, gameCamera.position.y, gameCamera.viewportWidth, gameCamera.viewportHeight);
    rayHandler.updateAndRender();
    if (DEBUG) {
        b2dRenderer.render(world, b2dCombinedMatrix);
        Gdx.app.debug(TAG, "Last number of render calls: " + spriteBatch.renderCalls);
    }
}

And this is the resize method which moves the viewport up by 4 world units:

public void resize(final int width, final int height) {
    viewport.update(width, height, false);
    // offset viewport by y-axis (get distance from viewport to viewport with offset)
    renderOffsetVector.set(gameCamera.position.x - gameCamera.viewportWidth * 0.5f, RENDER_OFFSET_Y + gameCamera.position.y - gameCamera.viewportHeight * 0.5f, 0);
    gameCamera.project(renderOffsetVector, viewport.getScreenX(), viewport.getScreenY(), viewport.getScreenWidth(), viewport.getScreenHeight());
    viewport.setScreenY((int) renderOffsetVector.y);
}

Unscaled

Scaled image


Solution

  • After hours of fiddling around with the matrix I finally got it to work but there is actually a very easy solution to my problem :D

    Basically the render method of the rayhandler was messing up my matrix calculations all the time and the reason is that I did not tell it to use a custom viewport.

    So adjusting the resize method to this

    public void resize(final int width, final int height) {
        viewport.update(width, height, false);
        // offset viewport by y-axis (get distance from viewport to viewport with offset)
        renderOffsetVector.set(gameCamera.position.x - gameCamera.viewportWidth * 0.5f, RENDER_OFFSET_Y + gameCamera.position.y - gameCamera.viewportHeight * 0.5f, 0);
        gameCamera.project(renderOffsetVector, viewport.getScreenX(), viewport.getScreenY(), viewport.getScreenWidth(), viewport.getScreenHeight());
        viewport.setScreenY((int) renderOffsetVector.y);
    
        rayHandler.useCustomViewport(viewport.getScreenX(), viewport.getScreenY(), viewport.getScreenWidth(), viewport.getScreenHeight());
    }
    

    and simplifying the render method to

     // draw lights
        rayHandler.setCombinedMatrix(gameCamera);
        rayHandler.updateAndRender();
        if (DEBUG) {
            b2dRenderer.render(world, b2dCombinedMatrix);
            Gdx.app.debug(TAG, "Last number of render calls: " + spriteBatch.renderCalls);
        }
    

    solved my problem.

    Maybe I am stupid but I did not find useCustomViewport method in any of the documentations. Anyway , solved!