[Box2D][LibGDX] Raycasting bug?

Hi!

I’m messing around with box2d trying to implement a LoS algorithm by casting a ray (from the mouse for now) to every edge point of the box2d bodies.
If the fraction of the RayCastCallback is 1.0 meaning that the ray hit the target without collision then I’ll do another raycast with the starting position as the last raycast’s end position and a random distance in the given direction as the end point.
It is working fine mostly but there are a few cases where the ray ignores collision with certain bodies for no reason.

Here is a picture of the problem:
(The yellow circles are the end points after 1 or 2 raycasts, the blue circle is where the bug occoured. If you trace that ray back to the start you’ll see that it doesnt register collision with the 1st body)

Eventhough it’s messy because it’s only a sandbox project I’ll provide source code.

public class RayCast extends ApplicationAdapter {
	public static final float PPM = 64f;

	OrthographicCamera cam, b2;

	SpriteBatch batch;
	Box2DDebugRenderer dr;
	ShapeRenderer sr;

	World world;
	RayCastCallback rccb;

	Vector2 start;
	Vector2 end;
	Array<Vector2> endPoints;
	Array<Vector2> finalEndPoints;

	@Override
	public void create() {
		batch = new SpriteBatch();

		world = new World(new Vector2(0, 0), false);
		dr = new Box2DDebugRenderer();
		sr = new ShapeRenderer();

		b2 = new OrthographicCamera();
		cam = new OrthographicCamera();

		b2.setToOrtho(false, Gdx.graphics.getWidth() / PPM, Gdx.graphics.getHeight() / PPM);
		cam.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

		endPoints = new Array<Vector2>();
		finalEndPoints = new Array<Vector2>();

		//		float w = Gdx.graphics.getWidth() / PPM;
		//		float h = Gdx.graphics.getHeight() / PPM;
		//
		//		finalEndPoints.add(new Vector2(0, 0));
		//		finalEndPoints.add(new Vector2(0, h));
		//		finalEndPoints.add(new Vector2(w, h));
		//		finalEndPoints.add(new Vector2(w, 0));

		// Left
		createBody(16, Gdx.graphics.getHeight() / 2, 32, Gdx.graphics.getHeight(), false);
		// Right
		createBody(Gdx.graphics.getWidth() - 16, Gdx.graphics.getHeight() / 2, 32, Gdx.graphics.getHeight(), false);
		// Top
		createBody(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() - 16, Gdx.graphics.getWidth() - 66, 32, false);
		// Bottom
		createBody(Gdx.graphics.getWidth() / 2, 16, Gdx.graphics.getWidth() - 66, 32, false);

		//		createBody(800, 400, 64, 64, true);
		//		createBody(500, 300, 128, 64, true);
		//		createBody(150, 200, 64, 64, true);
		//		createBody(450, 500, 64, 64, true);
		//		createBody(200, 100, 256, 64, true);
		//
		//		createBody(200, 600, 64, 64, true);
		//		createBody(274, 600, 64, 64, true);
		createBody(348, 600, 64, 64, true);
		createBody(422, 600, 64, 64, true);
		start = new Vector2(2, 2);
		end = new Vector2(2, 2);
		tmp = new Vector2();

		for(int i = 0; i < finalEndPoints.size; i++) {
			endPoints.add(new Vector2());
		}

		rccb = new RayCastCallback() {
			@Override
			public float reportRayFixture(Fixture fixture, Vector2 point, Vector2 normal, float fraction) {
				if(fraction == 1.0f) {
					newCast = true;
				}
				;
				endPoints.get(index).set(point);
				return fraction;
			}
		};
	}

	int index = 0;
	boolean newCast = false;
	Vector2 tmp;

	@Override
	public void render() {
		if(Gdx.input.isKeyJustPressed(Keys.ESCAPE))
			Gdx.app.exit();

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

		float mx = Gdx.input.getX() / PPM;
		float my = (Gdx.graphics.getHeight() - Gdx.input.getY()) / PPM;
		start.set(mx, my);

		sr.begin(ShapeType.Line);
		for(index = 0; index < finalEndPoints.size; index++) {
			// First raycast
			world.rayCast(rccb, start, finalEndPoints.get(index));

			tmp.set(endPoints.get(index));
			float angle = (float) (Math.atan2(tmp.y - start.y, tmp.x - start.x));
			angle *= MathUtils.radiansToDegrees;
			if(angle < 0)
				angle += 360;

			// 0, 0
			if(angle > 0 && angle < 90 && index % 4 == 0)
				newCast = false;
			// 0, 1
			else if(angle > 270 && angle < 360 && index % 4 == 1)
				newCast = false;
			// 1, 1
			else if(angle > 180 && angle < 270 && index % 4 == 2)
				newCast = false;
			// 1, 0
			else if(angle > 90 && angle < 180 && index % 4 == 3)
				newCast = false;

			if(!newCast) {
				//				sr.setColor(Color.WHITE);
				//				sr.line(start.x * PPM, start.y * PPM, endPoints.get(index).x * PPM, endPoints.get(index).y * PPM);
			}
			else {
				newCast = false;
				angle *= MathUtils.degreesToRadians;

				// 25 * tileSize distance for the ray cast target 
				float x = (float) Math.cos(angle) * 25f;
				float y = (float) Math.sin(angle) * 25f;

				tmp.add(x, y);
				world.rayCast(rccb, finalEndPoints.get(index), tmp);

				sr.setColor(Color.YELLOW);
				sr.line(finalEndPoints.get(index).x * PPM, finalEndPoints.get(index).y * PPM, endPoints.get(index).x * PPM, endPoints.get(index).y * PPM);

			}
		}
		sr.end();

		dr.render(world, b2.combined);

		sr.begin(ShapeType.Line);
		for(int i = 0; i < endPoints.size; i++) {

			// Start -> end
			sr.setColor(Color.RED);
			sr.line(start.x * PPM, start.y * PPM, endPoints.get(i).x * PPM, endPoints.get(i).y * PPM);
			sr.setColor(Color.YELLOW);
			sr.circle(endPoints.get(i).x * PPM, endPoints.get(i).y * PPM, 10);
		}
		sr.end();

	}

	public Body createBody(float x, float y, float w, float h, boolean add) {
		Body body;
		w /= 2;
		w /= PPM;
		h /= 2;
		h /= PPM;
		x /= PPM;
		y /= PPM;

		if(add) {
			finalEndPoints.add(new Vector2(x - w, y - h));
			finalEndPoints.add(new Vector2(x - w, y + h));
			finalEndPoints.add(new Vector2(x + w, y + h));
			finalEndPoints.add(new Vector2(x + w, y - h));
		}

		BodyDef def = new BodyDef();
		def.type = BodyType.DynamicBody;
		def.fixedRotation = false;
		def.gravityScale = 1f;
		def.bullet = true;
		body = world.createBody(def);

		PolygonShape poly = new PolygonShape();
		poly.setAsBox(w, h);
		body.createFixture(poly, 1);

		body.setTransform(x, y, 0);

		return body;
	}
}

Any help would be greatly appriciated! :wink: