/******************************************************************************* * Copyright 2015 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.mygdx.game.debugdrawers; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.ai.pfa.Connection; import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Quaternion; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Bits; import com.mygdx.game.objects.GameCharacter; import com.mygdx.game.pathfinding.*; import com.mygdx.game.steerers.FollowPathSteerer; import com.mygdx.game.utilities.MyShapeRenderer; /** * @author jsjolund */ public class NavMeshDebugDrawer { private final Vector3 tmp1 = new Vector3(); private final Vector3 tmp2 = new Vector3(); private final Vector3 tmp3 = new Vector3(); private final Vector3 tmp4 = new Vector3(); private final Matrix4 tmpMatrix = new Matrix4(); private final Quaternion tmpQuat = new Quaternion(); private Bits visibleLayers; private MyShapeRenderer shapeRenderer; private NavMesh navMesh; private boolean triangleIsVisible(Triangle t) { return (visibleLayers.nextSetBit(t.meshPartIndex) != -1); } private void drawVertex(Vector3 pos, float size, Color color) { shapeRenderer.setColor(color); float offset = size / 2; shapeRenderer.box(pos.x - offset, pos.y - offset, pos.z + offset, size, size, size); } private void drawNavMeshTriangles() { shapeRenderer.set(MyShapeRenderer.ShapeType.Line); for (int i = 0; i < navMesh.graph.getNodeCount(); i++) { Triangle t = navMesh.graph.getTriangleFromGraphIndex(i); if (triangleIsVisible(t)) { drawTriangle(t, Color.LIGHT_GRAY, 1); } } } private void drawNavMeshIndices(SpriteBatch spriteBatch, Camera camera, BitmapFont font) { // TODO: Get rid of all the transform matrix setting if (spriteBatch.isDrawing()) { spriteBatch.end(); } spriteBatch.begin(); spriteBatch.setProjectionMatrix(camera.combined); for (int i = 0; i < navMesh.graph.getNodeCount(); i++) { Triangle t = navMesh.graph.getTriangleFromGraphIndex(i); if (triangleIsVisible(t)) { tmpMatrix.set(camera.view).inv().getRotation(tmpQuat); tmpMatrix.setToTranslation(t.centroid).rotate(tmpQuat); spriteBatch.setTransformMatrix(tmpMatrix); font.draw(spriteBatch, Integer.toString(t.triIndex), 0, 0); } } spriteBatch.end(); } private void drawTriangle(Triangle tri, Color color, float alpha) { shapeRenderer.setColor(color.r, color.g, color.b, alpha); shapeRenderer.triangle(tri.a, tri.b, tri.c); } private void drawPathTriangles(NavMeshGraphPath navMeshGraphPath, Triangle currentTriangle) { shapeRenderer.set(MyShapeRenderer.ShapeType.Filled); if (navMeshGraphPath != null && navMeshGraphPath.getCount() > 0) { for (int i = 0; i < navMeshGraphPath.getCount(); i++) { Edge e = (Edge) navMeshGraphPath.get(i); if (triangleIsVisible(e.fromNode)) { if (currentTriangle.getIndex() == e.fromNode.getIndex()) { drawTriangle(e.fromNode, Color.RED, 0.2f); } else { drawTriangle(e.fromNode, Color.YELLOW, 0.2f); } } if (i == navMeshGraphPath.getCount() - 1) { if (triangleIsVisible(e.toNode)) { if (currentTriangle.getIndex() == e.toNode.getIndex()) { drawTriangle(e.toNode, Color.RED, 0.2f); } else { drawTriangle(e.toNode, Color.YELLOW, 0.2f); } } } } // Shared triangle edges shapeRenderer.set(MyShapeRenderer.ShapeType.Line); for (Connection<Triangle> connection : navMeshGraphPath) { Edge e = (Edge) connection; if (triangleIsVisible(e.fromNode) || triangleIsVisible(e.toNode)) { shapeRenderer.line(e.rightVertex, e.leftVertex, Color.GREEN, Color.RED); } } } else if (currentTriangle != null) { drawTriangle(currentTriangle, Color.RED, 0.2f); } } private void drawPathPoints(NavMeshPointPath navMeshPointPath) { shapeRenderer.set(MyShapeRenderer.ShapeType.Line); Vector3 q; Vector3 p = navMeshPointPath.getVector(navMeshPointPath.getSize() - 1); shapeRenderer.setColor(Color.WHITE); for (int i = navMeshPointPath.getSize() - 1; i >= 0; i--) { q = navMeshPointPath.getVector(i); shapeRenderer.setColor(Color.CYAN); shapeRenderer.line(p, q); p = q; drawVertex(p, 0.02f, Color.WHITE); } } public void drawClosestPointDebug(GameCharacter character) { Vector3 position = tmp1; Vector3 direction = tmp2; Vector3 closestPoint = tmp3; Vector3 aimPoint = tmp4; float radius = 2; character.getDirection(direction); character.getGroundPosition(position); aimPoint.set(direction).scl(radius).add(position); Triangle closest = navMesh.getClosestValidPointAt(position, direction, radius, closestPoint, character.visibleOnLayers); shapeRenderer.set(MyShapeRenderer.ShapeType.Filled); shapeRenderer.setColor(0, 0, 1, 0.2f); shapeRenderer.triangle(closest.a, closest.b, closest.c); drawVertex(aimPoint, 0.05f, Color.GREEN); drawVertex(closestPoint, 0.05f, Color.WHITE); } public void drawNavMesh(MyShapeRenderer shapeRenderer, SpriteBatch spriteBatch, NavMesh navMesh, GameCharacter character, Bits visibleLayers, Camera camera, BitmapFont font) { this.visibleLayers = visibleLayers; this.shapeRenderer = shapeRenderer; this.navMesh = navMesh; if (shapeRenderer.isDrawing()) { shapeRenderer.end(); } Gdx.gl.glEnable(GL20.GL_BLEND); Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); shapeRenderer.begin(MyShapeRenderer.ShapeType.Line); drawNavMeshTriangles(); if (character != null && character.steerer instanceof FollowPathSteerer) { FollowPathSteerer fpSteerer = (FollowPathSteerer)character.steerer; drawPathTriangles(fpSteerer.navMeshGraphPath, character.getCurrentTriangle()); if (fpSteerer.navMeshPointPath.getSize() > 0) { drawPathPoints(fpSteerer.navMeshPointPath); } drawClosestPointDebug(character); } shapeRenderer.end(); Gdx.gl.glDisable(GL20.GL_BLEND); drawNavMeshIndices(spriteBatch, camera, font); } }