World: Implement raycaster
Implement the basic raycaster, checking every block intersection and the block linked to that position. Split the screen uniformally along the width. Add data needed for camera maths in the Player class Specify constraints on the raycast parameters. Render the screen using the raycasts. Call the renderer in the main loop.
This commit is contained in:
parent
c6b09d668c
commit
cad775f88e
4 changed files with 126 additions and 28 deletions
6
Player.h
6
Player.h
|
@ -5,6 +5,9 @@
|
||||||
#ifndef RAYCASTING_PLAYER_H
|
#ifndef RAYCASTING_PLAYER_H
|
||||||
#define RAYCASTING_PLAYER_H
|
#define RAYCASTING_PLAYER_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
static constexpr float deg_to_rad = 3.14159265/180;
|
||||||
|
|
||||||
class Player {
|
class Player {
|
||||||
public:
|
public:
|
||||||
|
@ -14,7 +17,10 @@ public:
|
||||||
float y;
|
float y;
|
||||||
float orientation;
|
float orientation;
|
||||||
|
|
||||||
|
/* View properties. */
|
||||||
float fov = 70;
|
float fov = 70;
|
||||||
|
float sensorSize = 0.035; /* 35mm, about equivalent to human eye ? */
|
||||||
|
float focalLength = sensorSize / (2*tanf((fov*deg_to_rad)/2));
|
||||||
|
|
||||||
void move(float dx, float dy);
|
void move(float dx, float dy);
|
||||||
void rotate(float alpha);
|
void rotate(float alpha);
|
||||||
|
|
125
World.cpp
125
World.cpp
|
@ -3,6 +3,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
|
||||||
World::World(int w, int h, sf::Color groundColor, sf::Color ceilingColor, std::vector<BlockType> worldMap) : w(w), h(h),
|
World::World(int w, int h, sf::Color groundColor, sf::Color ceilingColor, std::vector<BlockType> worldMap) : w(w), h(h),
|
||||||
|
@ -92,32 +93,119 @@ void World::fillColumn(sf::RenderWindow& window, int column, float scale, sf::Co
|
||||||
window.draw(pixelColumn);
|
window.draw(pixelColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
float World::castRay(float originX, float originY, float orientation)
|
float World::castRay(float originX, float originY, float orientation) const
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Reference used for ray intersection computations :
|
* Reference used for ray intersection computations :
|
||||||
* https://web.archive.org/web/20220628034315/https://yunes.informatique.univ-paris-diderot.fr/wp-content/uploads/cours/INFOGRAPHIE/08-Raycasting.pdf
|
* https://web.archive.org/web/20220628034315/https://yunes.informatique.univ-paris-diderot.fr/wp-content/uploads/cours/INFOGRAPHIE/08-Raycasting.pdf
|
||||||
|
* The logic is as follows :
|
||||||
|
* - This computes one set of point per edge crossings (horizontal/vertical)
|
||||||
|
* - The origin not being confined to the grid, offsets are computed to
|
||||||
|
* align the intersections properly
|
||||||
|
* - The intersections are at multiples of the tangent of the relevant
|
||||||
|
* angle for the axis of interest, and simply on successive edges of
|
||||||
|
* the grid for the other one
|
||||||
|
* - Depending on the orientation, signs must be taken into account
|
||||||
|
* to work 360°
|
||||||
*/
|
*/
|
||||||
float deltaX;
|
/* Offsets to get back on the grid from the ray's origin. */
|
||||||
float deltaY;
|
float hOffsetX;
|
||||||
if(orientation < 45 || orientation > 315)
|
float hOffsetY;
|
||||||
{
|
float vOffsetX;
|
||||||
|
float vOffsetY;
|
||||||
|
|
||||||
|
/* Signs controlling the direction of travel. */
|
||||||
|
float hDir;
|
||||||
|
float vDir;
|
||||||
|
/* Need offset for rounding in the right direction ? */
|
||||||
|
float hRound;
|
||||||
|
float vRound;
|
||||||
|
|
||||||
|
float rads = orientation * deg_to_rad;
|
||||||
|
/* Used for vertical intersections. */
|
||||||
|
float rads_offset = (90 - orientation) * deg_to_rad;
|
||||||
|
|
||||||
|
/* Tangents used for the different axes. */
|
||||||
|
float hTan = tanf(rads);
|
||||||
|
float vTan = tanf(rads_offset);
|
||||||
|
|
||||||
|
/* Check if cos > 0 for horizontal hits formulas. */
|
||||||
|
if (orientation < 90 || orientation > 270) {
|
||||||
|
hOffsetX = ceilf(originY) - originY;
|
||||||
|
hOffsetY = ceilf(originY);
|
||||||
|
hDir = +1;
|
||||||
|
hRound = 0;
|
||||||
|
} else {
|
||||||
|
hOffsetX = originY - floorf(originY);
|
||||||
|
hOffsetY = floorf(originY);
|
||||||
|
hDir = -1;
|
||||||
|
hRound = -1;
|
||||||
}
|
}
|
||||||
else if(orientation < 135)
|
hTan *= hDir;
|
||||||
{
|
hOffsetX *= hTan;
|
||||||
|
|
||||||
|
/* Check if sin > 0 for vertical hits formulas. */
|
||||||
|
if (orientation < 180) {
|
||||||
|
vOffsetX = ceilf(originX);
|
||||||
|
vOffsetY = ceilf(originX) - originX;
|
||||||
|
vDir = +1;
|
||||||
|
vRound = 0;
|
||||||
|
} else {
|
||||||
|
vOffsetX = floorf(originX);
|
||||||
|
vOffsetY = originX - floorf(originX);
|
||||||
|
vDir = -1;
|
||||||
|
vRound = -1;
|
||||||
}
|
}
|
||||||
else if(orientation < 225)
|
vTan *= vDir;
|
||||||
{
|
vOffsetY *= vTan;
|
||||||
|
|
||||||
}
|
/*
|
||||||
else
|
* Now we have all the constants and deltas to work with, cast the ray.
|
||||||
{
|
* Generated points follow the formulas :
|
||||||
|
* - h-intersect : (originX + hOffsetX + hTan*i, hOffsetY + hDir*i)
|
||||||
|
* - v-intersect : (vOffsetX + vDir*i, originY + vOffsetY + vTan*i)
|
||||||
|
*/
|
||||||
|
int i = 0;
|
||||||
|
float hCheckX = originX + hOffsetX;
|
||||||
|
float hCheckY = hOffsetY;
|
||||||
|
/* Bounds + sanity check. */
|
||||||
|
while (hCheckX >= 0 && hCheckX <= w && hCheckY >= 0 && hCheckY <= h && i < h) {
|
||||||
|
if (getBlock(floorf(hCheckX), floorf(hCheckY) + hRound) == BlockType::WALL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hCheckX += hTan;
|
||||||
|
hCheckY += hDir;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
i = 0;
|
||||||
|
float vCheckX = vOffsetX;
|
||||||
|
float vCheckY = originY + vOffsetY;
|
||||||
|
|
||||||
|
/* Bounds + sanity check. */
|
||||||
|
while (vCheckX >= 0 && vCheckX < w && vCheckY >= 0 && vCheckY < h && i < w) {
|
||||||
|
if (getBlock(floorf(vCheckX) + vRound, floorf(vCheckY)) == BlockType::WALL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vCheckX += vDir;
|
||||||
|
vCheckY += vTan;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We may or may not have hit something. Check which coordinates are closest
|
||||||
|
* and use those for computing the apparent size on screen.
|
||||||
|
*/
|
||||||
|
float hDist = sqrtf((originX - hCheckX)*(originX - hCheckX) +
|
||||||
|
(originY - hCheckY)*(originY - hCheckY));
|
||||||
|
float vDist = sqrtf((originX - vCheckX)*(originX - vCheckX) +
|
||||||
|
(originY - vCheckY)*(originY - vCheckY));
|
||||||
|
float finalDist = hDist > vDist ? vDist : hDist;
|
||||||
|
|
||||||
|
/* 2 Is wall height in meters. */
|
||||||
|
return player.focalLength*2/finalDist;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::render(sf::RenderWindow& window) const
|
void World::render(sf::RenderWindow& window) const
|
||||||
|
@ -143,7 +231,16 @@ void World::render(sf::RenderWindow& window) const
|
||||||
*/
|
*/
|
||||||
for(int i = 0;i<window.getSize().x;i++)
|
for(int i = 0;i<window.getSize().x;i++)
|
||||||
{
|
{
|
||||||
fillColumn(window, i, 0.5);
|
float deltaAngle = (player.fov/window.getSize().x) * (i-window.getSize().x/2.0);
|
||||||
|
float rayAngle = player.orientation + deltaAngle;
|
||||||
|
if (rayAngle < 0) {
|
||||||
|
rayAngle += 360;
|
||||||
|
} else if (rayAngle > 360) {
|
||||||
|
rayAngle -= 360;
|
||||||
|
}
|
||||||
|
float obstacleScale = castRay(player.x, player.y, rayAngle)/player.sensorSize;
|
||||||
|
fillColumn(window, i, obstacleScale);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
8
World.h
8
World.h
|
@ -48,12 +48,12 @@ private:
|
||||||
sf::Color wallColor = sf::Color(84,56,34)) const;
|
sf::Color wallColor = sf::Color(84,56,34)) const;
|
||||||
/**
|
/**
|
||||||
* Cast a ray from a given position and return the on-screen scale.
|
* Cast a ray from a given position and return the on-screen scale.
|
||||||
* @param originX Ray X origin
|
* @param originX Ray X origin, strictly positive
|
||||||
* @param originY Ray Y origin
|
* @param originY Ray Y origin, strictly positive
|
||||||
* @param orientation Angle to cast to, in degrees
|
* @param orientation Angle to cast to, in degrees between 0 and 360
|
||||||
* @return Scale on the screen of the hit wall.
|
* @return Scale on the screen of the hit wall.
|
||||||
*/
|
*/
|
||||||
float castRay(float originX, float originY, float orientation);
|
float castRay(float originX, float originY, float orientation) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
15
main.cpp
15
main.cpp
|
@ -1,22 +1,13 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "SFML/Graphics.hpp"
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
|
|
||||||
// TODO: Handle inputs to move player
|
// TODO: Handle inputs to move player
|
||||||
// TODO: Raycast from player position
|
|
||||||
// TODO: Fix raycasts and use it correctly
|
|
||||||
// TODO: Render world in event loop
|
|
||||||
// TODO: Understand what the code was doing (looks like scale from fill column is the output of the raycast)
|
|
||||||
// Ref: https://lodev.org/cgtutor/raycasting.html
|
|
||||||
// Ref: http://yunes.informatique.univ-paris-diderot.fr/wp-content/uploads/cours/INFOGRAPHIE/08-Raycasting.pdf
|
|
||||||
// TODO: Update player for camera plane
|
|
||||||
// TODO: Camera plane represents screen, rays go through it proportional to the screen
|
|
||||||
// TODO: Find a way to go to edges instead of just equally split (?)
|
// TODO: Find a way to go to edges instead of just equally split (?)
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::cout << "Hello, World!" << std::endl;
|
|
||||||
World world(10,10);
|
World world(10,10);
|
||||||
world.setBlock(BlockType::AIR,1,1,8,8);
|
world.setBlock(BlockType::AIR,1,1,8,8);
|
||||||
world.setBlock(BlockType::WALL,4,4,2,2);
|
world.setBlock(BlockType::WALL,4,4,2,2);
|
||||||
|
@ -35,6 +26,10 @@ int main()
|
||||||
if (event.type == sf::Event::Closed)
|
if (event.type == sf::Event::Closed)
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
window.clear();
|
||||||
|
|
||||||
|
world.render(window);
|
||||||
|
window.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue