[SOLVED] Box2dlights Incorrect / No shadows using libgdx with a TiledMap

I am testing libgdx with box2dlights for a small 2d game. I got the point light to follow the mouse cursor and have one direction light setup. I also have a .tmx rendered and created some Box2D bodies. The problem is I cant get the shadows working.

Video: https://www.youtube.com/watch?v=Rhjj1TGavDU
Source code: http://pastebin.com/wVZP5WNc


    package com.mygdx.game;
     
    import box2dLight.DirectionalLight;
    import box2dLight.PointLight;
    import box2dLight.RayHandler;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.Input.Keys;
    import com.badlogic.gdx.Screen;
    import com.badlogic.gdx.graphics.Color;
    import com.badlogic.gdx.graphics.GL20;
    import com.badlogic.gdx.graphics.OrthographicCamera;
    import com.badlogic.gdx.graphics.g2d.SpriteBatch;
    import com.badlogic.gdx.maps.MapObjects;
    import com.badlogic.gdx.maps.tiled.TiledMap;
    import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
    import com.badlogic.gdx.maps.tiled.TmxMapLoader;
    import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
    import com.badlogic.gdx.math.Vector2;
    import com.badlogic.gdx.physics.box2d.Body;
    import com.badlogic.gdx.physics.box2d.BodyDef;
    import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
    import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
    import com.badlogic.gdx.physics.box2d.FixtureDef;
    import com.badlogic.gdx.physics.box2d.PolygonShape;
    import com.badlogic.gdx.physics.box2d.World;
     
    public class LightingTest implements Screen {
        final Drop game;
     
        float screenW, screenH;
     
        OrthographicCamera cam;
        OrthogonalTiledMapRenderer mapRenderer;
        MapObjects mapObjects;
     
        SpriteBatch mapBatch;
     
        World world;
        OrthographicCamera boxLightCamera;
        RayHandler rayHandler;
        Box2DDebugRenderer debugRenderer;
     
        int TILE_SIZE = 64;
        float PIXEL_PER_METER = 1 / 64f;
     
        PointLight pl;
        DirectionalLight dl;
     
        public LightingTest(final Drop gam) {
            this.game = gam;
     
            screenW = Gdx.graphics.getWidth();
            screenH = Gdx.graphics.getHeight();
     
            cam = new OrthographicCamera(30, 30 * (screenH / screenW));
            cam.position.set(cam.viewportWidth / 2f, cam.viewportHeight / 2f, 0);
            cam.update();
     
            TiledMap map = new TmxMapLoader().load("assets/map01.tmx");
     
            mapBatch = new SpriteBatch();
            mapRenderer = new OrthogonalTiledMapRenderer(map, PIXEL_PER_METER, mapBatch);
            mapRenderer.setView(cam);
     
            mapObjects = map.getLayers().get("objects").getObjects();
     
            debugRenderer = new Box2DDebugRenderer();
     
            boxLightCamera = createBox2DCam();
            world = new World(new Vector2(), true);
            rayHandler = createRayHandler(boxLightCamera);
     
            createLights();
            createWorldScenery(map);
        }
     
        private RayHandler createRayHandler(OrthographicCamera boxLightCamera) {
            RayHandler.useDiffuseLight(true);
            RayHandler rayHandler = new RayHandler(world);
            rayHandler.setCombinedMatrix(boxLightCamera.combined);
            rayHandler.setShadows(true);
     
            //rayHandler.setAmbientLight(0.2f, 0.2f, 0.2f, 0.1f);
           
            return rayHandler;
        }
     
        private void createLights() {
            pl = new PointLight(rayHandler, 128);
            pl.setDistance(500);
            pl.setPosition(150, 150);
            pl.setColor(new Color(0.4f, 0.4f, 0f, 1f));
            pl.setSoftnessLength(0);
            pl.setSoft(true);
     
            dl = new DirectionalLight(rayHandler, 128, new Color(0.3f, 0.3f, 0.3f, 1f), 45);
            dl.setSoftnessLength(0);
            dl.setSoft(true);
        }
     
        private OrthographicCamera createBox2DCam() {
            // light camera
            OrthographicCamera boxLightCamera = new OrthographicCamera();
            float boxLightViewportWidth = cam.viewportWidth / PIXEL_PER_METER;
            float boxLightViewportHeight = cam.viewportHeight / PIXEL_PER_METER;
            boxLightCamera.setToOrtho(true, boxLightViewportWidth, boxLightViewportHeight);
            boxLightCamera.update(true);
     
            return boxLightCamera;
        }
     
        protected void createWorldScenery(TiledMap tileMap) {
            // build plan for wall bodies
            float halfBody = TILE_SIZE / 64f / 2;
     
            PolygonShape tileShape = new PolygonShape();
            tileShape.setAsBox(0.5f, 0.5f);
     
            BodyDef tileBodyDef = new BodyDef();
            tileBodyDef.type = BodyType.StaticBody;
     
            FixtureDef fixtureDef = new FixtureDef();
            fixtureDef.shape = tileShape;
            fixtureDef.filter.groupIndex = 0;
     
            // create box2d bodies for all wall tiles
            TiledMapTileLayer layer = (TiledMapTileLayer) tileMap.getLayers().get(0);
            int cols = layer.getWidth();
            int rows = layer.getHeight();
     
            for (int row = 0; row < rows; row++) {
                for (int col = 0; col < cols; col++) {
     
                    if (col == 0 || col == cols - 1 || row == 0 || row == rows - 1) {
                        continue;
                    }
     
                    int tileId = layer.getCell(col, row).getTile().getId();
     
                    if (tileId == 1) {
                        float bodyX = col + halfBody;
                        float bodyY = row + halfBody;
                        tileBodyDef.position.set(bodyX, bodyY);
                        Body tileBody = world.createBody(tileBodyDef);
                        tileBody.createFixture(fixtureDef);
                    }
     
                }
            }
     
            tileShape.dispose();
        }
     
        @Override
        public void render(float delta) {
            Gdx.gl.glClearColor(0, 0, 0.2f, 1);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
     
            if (Gdx.input.isKeyPressed(Keys.ESCAPE)) {
                Gdx.app.exit();
            }
     
            pl.setPosition(Gdx.input.getX(), Gdx.input.getY());
     
            mapRenderer.render();
     
            game.batch.begin();
     
            game.batch.end();
     
            boxLightCamera.update();
     
            rayHandler.setCombinedMatrix(boxLightCamera.combined,
                    boxLightCamera.position.x, boxLightCamera.position.y,
                    boxLightCamera.viewportWidth * boxLightCamera.zoom,
                    boxLightCamera.viewportHeight * boxLightCamera.zoom);
     
            rayHandler.updateAndRender();
     
            debugRenderer.render(world, cam.combined);
        }
     
        @Override
        public void dispose() {
            game.dispose();
            mapRenderer.dispose();
            mapBatch.dispose();
            world.dispose();
            rayHandler.dispose();
            debugRenderer.dispose();
        }
     
        @Override
        public void resize(int width, int height) {
        }
     
        @Override
        public void hide() {
        }
     
        @Override
        public void pause() {
        }
     
        @Override
        public void resume() {
        }
     
        @Override
        public void show() {
        }
    }

I am expecting when I move the Point light for shadows to appear behind/around the collision shapes. Or some shadows behind the collision shapes opposite of where the directional light is placed.

I have used box2dlights succesfully in the past, very long time ago though. If you can post all the files so I can run it, I’ll happily have a go at fixing

I have a zipped version of the assets (tmx + tileset png) and source file here. http://code4all.nl/files/mygdx-test.zip

Solved. Was using different cameras with wrong coordinates. Code with working shadows:


package com.mygdx.game;

import box2dLight.DirectionalLight;
import box2dLight.PointLight;
import box2dLight.RayHandler;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.maps.MapObjects;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapRenderer;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class LightingTest implements Screen {

    final Drop game;

    OrthographicCamera cam;
    TiledMapRenderer mapRenderer;
    MapObjects mapObjects;

    World world;
    RayHandler rayHandler;
    Box2DDebugRenderer debugRenderer;

    int TILE_SIZE = 64;

    float screenW, screenH;
    
    PointLight pointLight;
    PointLight moonLight;
    
    float sunTimer = 0;
    float sunPosX = 100;    
    float dayTime = 0;    
    
    Color dawnColor = new Color(0.3f, 0.15f, 0.08f, 1f);
    Color afternoonColor = new Color(0.45f, 0.45f, 0.45f, 1f);
    Color duskColor = new Color(0.2f, 0.2f, 0.2f, 1f);
    Color nightColor = new Color(0f, 0f, 0f, 1f);

    public LightingTest(final Drop gam) {
        this.game = gam;

        screenW = Gdx.graphics.getWidth();
        screenH = Gdx.graphics.getHeight();

        cam = new OrthographicCamera();
        cam.setToOrtho(false, screenW, screenH);
        cam.update();

        TiledMap map = new TmxMapLoader().load("assets/map01.tmx");
        mapRenderer = new OrthogonalTiledMapRenderer(map);
        mapRenderer.setView(cam);
        
        mapObjects = map.getLayers().get("objects").getObjects();

        debugRenderer = new Box2DDebugRenderer();

        world = new World(new Vector2(), true);
        rayHandler = createRayHandler();
        
        createLight();
        
        createWorldScenery(map);
    }

    private RayHandler createRayHandler() {
        RayHandler.useDiffuseLight(true);
        rayHandler = new RayHandler(world);
        rayHandler.setCombinedMatrix(cam.combined);

        return rayHandler;
    }
    
    private void createLight() {
        Color color = new Color(0.2f, 0.2f, 0.2f, 1f);
        
        pointLight = new PointLight(rayHandler, 256);
        pointLight.setColor(color);
        pointLight.setDistance(2500);
        pointLight.setPosition(sunPosX, screenH - 300);
        pointLight.setActive(true);
        
        Color moonColor = new Color(0.1f, 0.3f, 0.8f, 1f);
        
        moonLight = new PointLight(rayHandler, 256);
        moonLight.setColor(moonColor);
        moonLight.setDistance(2500);
        moonLight.setPosition(screenW / 2, screenH / 2);
        moonLight.setActive(false);
    }

    protected void createWorldScenery(TiledMap tileMap) {
        TiledMapTileLayer layer = (TiledMapTileLayer) tileMap.getLayers().get(0);
        int cols = layer.getWidth();
        int rows = layer.getHeight();

        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                int tileId = layer.getCell(col, row).getTile().getId();

                if (tileId == 1 || tileId == 2) { // wall or sliding block
                    float bodyX = col * TILE_SIZE + TILE_SIZE / 2;
                    float bodyY = row * TILE_SIZE + TILE_SIZE / 2;

                    BodyDef tileBodyDef = new BodyDef();
                    tileBodyDef.type = BodyType.StaticBody;
                    tileBodyDef.position.set(bodyX, bodyY);

                    PolygonShape tileShape = new PolygonShape();
                    tileShape.setAsBox(TILE_SIZE / 2, TILE_SIZE / 2);
                    
                    Body tileBody = world.createBody(tileBodyDef);
                    tileBody.createFixture(tileShape, 0);

                    tileShape.dispose();
                }
            }
        }
    }

    @Override
    public void render(float delta) {
        Gdx.gl.glClearColor(0, 0, 0, 1f);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        if (Gdx.input.isKeyPressed(Keys.ESCAPE)) {
            Gdx.app.exit();
        }
        
        // change daytime
        dayTime += delta;
        
        if (dayTime >= 20) {
            dayTime = 0;
            sunPosX = 0;
            pointLight.setActive(true);
            moonLight.setActive(false);   
            rayHandler.setAmbientLight(dawnColor);
        }
        
        if (dayTime > 0 && dayTime < 5) {
            pointLight.setActive(true);
            rayHandler.setAmbientLight(dawnColor.lerp(afternoonColor, delta));
        }          
        if (dayTime > 5 && dayTime < 10) {
            rayHandler.setAmbientLight(afternoonColor.lerp(duskColor, delta));
        }
        if (dayTime > 10 && dayTime < 15) {
            pointLight.setActive(false);
            moonLight.setActive(true);
            rayHandler.setAmbientLight(duskColor.lerp(nightColor, delta));
        }
        if (dayTime > 15 && dayTime < 20) {
            moonLight.setActive(false);
            rayHandler.setAmbientLight(nightColor.lerp(dawnColor, delta));
        }
        
        // move sun light
        sunTimer += delta;
        if (sunTimer > 1) {
            sunTimer = 0;
            
            sunPosX += 100;
            
            if (sunPosX > screenW - 100) {
                sunPosX = 100;
            }
            
            pointLight.setPosition(sunPosX, screenH - 300);
        }
        
        
        mapRenderer.render();

        game.batch.begin();

        game.batch.end();
        
        rayHandler.setCombinedMatrix(cam.combined,
                cam.position.x, cam.position.y,
                cam.viewportWidth * cam.zoom,
                cam.viewportHeight * cam.zoom);
        
        rayHandler.updateAndRender();

        //debugRenderer.render(world, cam.combined);
    }

    @Override
    public void dispose() {
        game.dispose();
        world.dispose();
        rayHandler.dispose();
        debugRenderer.dispose();
    }

    @Override
    public void resize(int width, int height
    ) {
    }

    @Override
    public void hide() {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }

    @Override
    public void show() {
    }
}

I removed the file. Problem is solved.

See video with shadows: https://www.youtube.com/watch?v=pxexNqZ7Ptc

Cool, glad you solved it :slight_smile: