I'm trying to render sprites/textures in my raycasting engine, but I don't quite understand the math behind drawing the columns of the texture I intend to project. I've tried looking at several tutorials, e.g., lodev, Wynnliam, and several posts on Allegro.cc, but none of them seem to explain the sprite-casting component in an easy-to-digest way. Can anyone help? Note that I'm currently drawing a yellow square in the location of the sprite and it appears as if the sprite is not positioned in the correct spot... My main problem is that I don’t understand how to compute the column of the texture to render. Once I figure this out, I can easily add z-depth sorting, but I really have no idea how to do this step.
final int TEXTURE_SIZE = 64;
final int PROJ_HEIGHT = 640;
final int PROJ_WIDTH = 640;
ArrayList<TextureSprite> sprites = this.RAYCASTER_PANEL.getTileMap().getSprites();
for (int s = 0; s < sprites.size(); s++) {
TextureSprite sp = sprites.get(s);
// computes the distance and angle of the sprite...
double fov = this.RAYCASTER_PANEL.getCamera().getFov();
double ca = this.getCamera().getCurrentAngle();
double dx = sp.getX() - this.RAYCASTER_PANEL.getCamera().getX();
double dy = sp.getY() - this.RAYCASTER_PANEL.getCamera().getY();
double d = Point2D.distanceSq(sp.getX(), sp.getY(), this.RAYCASTER_PANEL.getCamera().getX(), this.RAYCASTER_PANEL.getCamera().getY());
double theta_temp = Math.toDegrees(Math.atan2(dy, dx));
if(theta_temp < 0) theta_temp += 360;
if (theta_temp > 360) theta_temp -= 360;
double y = ca + fov/2 - theta_temp;
if(theta_temp > 270 && ca < 90) y = ca + fov/2 - theta_temp + 360;
if(ca > 270 && theta_temp < 90) y = ca + fov/2 - theta_temp - 360;
// attempt to compute the sprite's x, y, width, and height
double sw = (TEXTURE_SIZE / Math.sqrt(d)) * this.getCamera().getDistanceToProjectionPlane();
double sh = sw;
double sx = (y * PROJ_HEIGHT / fov) - sw/2.f;
double sy = (PROJ_HEIGHT / 2.f) - (TEXTURE_SIZE / 2.f);
// Just a temporary until I understand how to draw the textures themselves...
g2.setColor(Color.YELLOW);
g2.fillRect((int)sx,(int)sy,(int)sw,(int)sh);
Code after @lightxbulb's post suggestion:
final int PROJ_WIDTH=1280; // changed
TextureSprite sp = sprites.get(s);
double fov = this.getCamera().getFov();
double ca = this.getCamera().getCurrentAngle();
double px = this.getCamera().getX();
double py = this.getCamera().getY();
double sx = sp.getX();
double sy = sp.getY();
double dx = sx - px;
double dy = sy - py;
double sprite_dir = Math.toDegrees(Math.atan2(dy, dx));
while (sprite_dir - ca > 180) sprite_dir -= 360;
while (sprite_dir - ca < -180) sprite_dir += 360;
double sprite_dist = Math.sqrt(Math.pow(px - sp.getX(), 2) + Math.pow(py - sp.getY(), 2));
double sprite_screen_size = Math.min(2000, PROJ_HEIGHT / sprite_dist);
int h_offset = (int) ((sprite_dir - ca) * (PROJ_WIDTH/2) / (fov) + (PROJ_WIDTH/2)/2 - sprite_screen_size / 2);
int v_offset = (int) (PROJ_HEIGHT/2 - sprite_screen_size / 2);
g2.setColor(Color.YELLOW);
for (int i = 0; i < sprite_screen_size; i++) {
// Adding these range guards prevents any rendering whatsoever
if (h_offset+i<0||h_offset+i>PROJ_WIDTH/2) continue;
for (int j = 0; j < sprite_screen_size; j++) {
g2.fillRect(PROJ_WIDTH/2+h_offset+i,v_offset+j,1,1);
}
}