#ifndef _H_RAYCASTER_H #define _H_RAYCASTER_H #include #include #include #include "utility/debugdraw.h" class Raycaster { public: Raycaster( float FOV, float viewDist, const std::vector>& collisionMap, float tileSize) : FOV(FOV), viewDist(viewDist), collisionMap(collisionMap), tileSize(tileSize) {} bool bresenhamRaycast(const glm::vec3& origin, const float rotation, const glm::vec3& target) { float rayAngle = rotation - (FOV / 2); float angleIncrement = 2.0f; bool hitTarget = false; glm::ivec2 lastEnd = glm::ivec2(0); for (int rayCount = 0; rayCount <= FOV / 2; rayCount++) { float endX = (origin.x + (glm::cos(glm::radians(rayAngle)) * viewDist)); float endY = (origin.y + (glm::sin(glm::radians(rayAngle)) * viewDist)); glm::ivec2 targetTile= glm::ivec2(static_cast(target.x / tileSize), static_cast(target.y / tileSize)); glm::ivec2 startTile = glm::ivec2(static_cast((origin.x + tileSize * 0.1f) / tileSize), static_cast((origin.y + tileSize * 0.1f) / tileSize)); glm::ivec2 endTile = glm::ivec2(static_cast(endX / tileSize), static_cast(endY / tileSize)); int dx = glm::abs(endTile.x - startTile.x); int dy = glm::abs(endTile.y - startTile.y); int sx = startTile.x < endTile.x ? 1 : -1; int sy = startTile.y < endTile.y ? 1 : -1; int err = (dx > dy ? dx : -dy) / 2; glm::ivec2 step = startTile; while (step != endTile) { if (step.x < 0 || step.x >= collisionMap[0].size() || step.y < 0 || step.y >= collisionMap.size()) { if (glm::abs(rayAngle - rotation) <= 2) distFromWall = distFromWall < glm::distance(glm::vec2(startTile), glm::vec2(step)) ? distFromWall : glm::distance(glm::vec2(startTile), glm::vec2(step)); break; } if (collisionMap[step.y][step.x] != 0) { if (glm::abs(rayAngle - rotation) <= 2) distFromWall = distFromWall < glm::distance(glm::vec2(startTile), glm::vec2(step)) ? distFromWall : glm::distance(glm::vec2(startTile), glm::vec2(step)); break; } if (step == targetTile) hitTarget = true; int e2 = err; if (e2 > -dx) { err -= dy; step.x += sx; } if (e2 < dy) { err += dx; step.y += sy; } } //LineDrawer::getInstance().addLine(origin, glm::vec3(step.x * tileSize, step.y * tileSize, 0.f), glm::vec4(1.0 - (1.0f / rayCount), 1.0f / rayCount, 0.0f, 0.7f)); if (step == endTile) distFromWall = std::numeric_limits::infinity(); rayAngle += angleIncrement; } return hitTarget; } // We need to collision map and tile size so we can hide behind tiles // returns true if the raycast lands on the targets tile bool performRaycast(const glm::vec3& origin, const float rotation, const glm::vec3& target) { float rayStepSize = 0.05f; float rayAngle = rotation - (FOV / 2); float angleInc = 2.0f; glm::ivec2 targetTile = glm::ivec2(static_cast(target.x / tileSize), static_cast(target.y / tileSize)); bool hitTarget = false; for (int rayCount = 0; rayCount <= FOV / 2; rayCount++) { glm::vec2 ray = glm::vec2(origin); while (glm::distance(ray, glm::vec2(origin)) < viewDist) { ray.x += cos(glm::radians(rayAngle)) * rayStepSize; ray.y += sin(glm::radians(rayAngle)) * rayStepSize; glm::ivec2 rayTile = glm::ivec2(static_cast(ray.x / tileSize), static_cast(ray.y / tileSize)); if (rayTile.x < 0 || rayTile.x >= collisionMap[0].size() || rayTile.y < 0 || rayTile.y >= collisionMap.size()) { if (rayCount == FOV / 2) distFromWall = glm::distance(glm::vec2(origin), ray); break; } if (collisionMap[rayTile.y][rayTile.x] != 0) { if (rayCount == FOV / 2) distFromWall = glm::distance(glm::vec2(origin), ray); break; } if (rayTile == targetTile) hitTarget = true; } DebugDrawer::getInstance().addLine(origin, glm::vec3(ray.x, ray.y, 0.f), glm::vec4(1.0f, 0.0f, 0.0f, 0.2f)); if (glm::distance(ray, glm::vec2(origin)) >= viewDist) distFromWall = std::numeric_limits::infinity(); rayAngle += angleInc; } return hitTarget; } float getDistanceFromWall() const { return distFromWall; } float getTileSize() const { return tileSize; } private: float FOV; float viewDist; float distFromWall = std::numeric_limits::infinity(); std::vector> collisionMap; float tileSize; }; #endif // _H_RAYCASTER_H