Now have a basic sprite system that uses SFML's sprites, but the algorithm for determining how much of a sprite to display is wrong. Need to use the alternate algorithm from LODE's tutorial that draws sprites after rendering.

master
Zed A. Shaw 11 months ago
parent 024d5b30e1
commit da7075864b
  1. BIN
      assets/undead_peasant-256.png
  2. 2
      constants.hpp
  3. 1
      main.cpp
  4. 57
      raycaster.cpp
  5. 5
      texture.cpp
  6. 5
      texture.hpp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 20 KiB

@ -1,5 +1,7 @@
#pragma once
constexpr const int TEXTURE_WIDTH=256;
constexpr const int TEXTURE_HEIGHT=256;
constexpr const int RAY_VIEW_WIDTH=960;
constexpr const int RAY_VIEW_HEIGHT=720;
constexpr const int RAY_VIEW_X=(1280 - RAY_VIEW_WIDTH);

@ -67,7 +67,6 @@ int main() {
auto elapsed = std::chrono::duration<double>(end - start);
stats.sample(1/elapsed.count());
draw_gui(window, text, stats);
window.display();

@ -17,7 +17,7 @@ union ColorConv {
};
inline uint32_t dumb_lighting(uint32_t pixel, double distance) {
if(distance < 1) return pixel;
if(distance < 1.0) return pixel;
ColorConv conv{.as_int=pixel};
conv.as_color.r /= distance;
@ -84,7 +84,8 @@ void Raycaster::sprite_casting() {
for(int i = 0; i < $textures.NUM_SPRITES; i++) {
int sprite_index = spriteOrder[i];
Sprite& sprite_rec = $textures.get_sprite(sprite_index);
auto& sprite_texture = $textures.get_texture(sprite_rec.texture);
// auto& sprite_texture = $textures.get_texture(sprite_rec.texture);
sf::Sprite *sf_sprite = sprite_rec.sprite;
double spriteX = sprite_rec.x - $posX;
double spriteY = sprite_rec.y - $posY;
@ -103,18 +104,10 @@ void Raycaster::sprite_casting() {
int spriteScreenX = int(($width / 2) * (1 + transformX / transformY));
int vMoveScreen = int(sprite_rec.elevation * -1 / transformY);
// calculate the height of the sprite on screen
//using "transformY" instead of the real distance prevents fisheye
int spriteHeight = abs(int($height / transformY)) / sprite_rec.vDiv;
//calculate lowest and highest pixel to fill in current stripe
int drawStartY = -spriteHeight / 2 + $height / 2 + vMoveScreen;
if(drawStartY < 0) drawStartY = 0;
int drawEndY = spriteHeight / 2 + $height / 2 + vMoveScreen;
if(drawEndY >= $height) drawEndY = $height - 1;
// calculate width the the sprite
// same as height of sprite, given that it's square
int spriteWidth = abs(int($height / transformY)) / sprite_rec.uDiv;
@ -123,29 +116,29 @@ void Raycaster::sprite_casting() {
int drawEndX = spriteWidth / 2 + spriteScreenX;
if(drawEndX > $width) drawEndX = $width;
//loop through every vertical stripe of the sprite on screen
for(int stripe = drawStartX; stripe < drawEndX; stripe++) {
int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * textureWidth / spriteWidth) / 256;
// the conditions in the if are:
// 1) it's in front of the camera plane so you don't see things behind you
// 2) ZBuffer, with perpendicular distance
if(transformY > 0 && transformY < ZBuffer[stripe]) {
for(int y = drawStartY; y < drawEndY; y++) {
//256 and 128 factors to avoid floats
if(drawStartX < drawEndX && transformY > 0 && transformY < ZBuffer[drawStartX]) {
int vMoveScreen = int(sprite_rec.elevation * -1 / transformY);
//calculate lowest and highest pixel to fill in current stripe
int drawStartY = -spriteHeight / 2 + $height / 2 + vMoveScreen;
if(drawStartY < 0) drawStartY = 0;
int drawEndY = spriteHeight / 2 + $height / 2 + vMoveScreen;
if(drawEndY >= $height) drawEndY = $height - 1;
float x = float(drawStartX + RAY_VIEW_X);
float y = float(drawStartY + RAY_VIEW_Y);
float sprite_w = float(spriteWidth) / float(textureWidth);
float sprite_h = float(spriteHeight) / float(textureHeight);
int texX = int(256 * (drawStartX - (-spriteWidth / 2 + spriteScreenX)) * textureWidth / spriteWidth) / 256;
int texX_end = int(256 * (drawEndX - (-spriteWidth / 2 + spriteScreenX)) * textureWidth / spriteWidth) / 256;
int d = (y - vMoveScreen) * 256 - $height * 128 + spriteHeight * 128;
int texY = ((d * textureHeight) / spriteHeight) / 256;
//get current color from the texture
// BUG: this crashes sometimes when the math goes out of bounds
int index = textureWidth * texY + texX;
if(index < 0 || (size_t)index >= sprite_texture.size()) continue;
uint32_t color = sprite_texture[index];
// poor person's transparency, get current color from the texture
if((color & 0x00FFFFFF) != 0) {
RGBA pixel = color;
$pixels[pixcoord(stripe, y)] = pixel;
}
}
}
sf_sprite->setScale({sprite_h, sprite_w});
sf_sprite->setTextureRect(sf::IntRect({texX, texY}, {texX_end, textureHeight}));
sf_sprite->setPosition({x, y});
$window.draw(*sf_sprite);
}
}
}
@ -328,8 +321,8 @@ void Raycaster::draw_ceiling_floor() {
void Raycaster::render() {
draw_ceiling_floor();
cast_rays();
sprite_casting();
draw_pixel_buffer();
sprite_casting();
}
bool Raycaster::empty_space(int new_x, int new_y) {

@ -28,6 +28,11 @@ void TexturePack::load_textures() {
floor = load_image(assets["floor"]);
ceiling = load_image(assets["ceiling"]);
sf::Texture* sprite_texture = new sf::Texture("assets/undead_peasant-256.png");
sf::Sprite* sf_sprite = new sf::Sprite(*sprite_texture);
sprites.push_back({4.0, 3.55, 6, sf_sprite, sprite_texture});
}
Image& TexturePack::get_texture(size_t num) {

@ -3,11 +3,14 @@
#include <cstdint>
#include <vector>
#include <string>
#include <SFML/Graphics.hpp>
struct Sprite {
double x;
double y;
int texture;
sf::Sprite *sprite = nullptr;
sf::Texture *sprite_texture = nullptr;
// ZED: this should be a separate transform parameter
double elevation=0;
int uDiv=1;
@ -22,7 +25,7 @@ struct TexturePack {
constexpr static const int TEXTURE_HEIGHT=256; // must be power of two
std::vector<Image> images;
std::vector<Sprite> sprites{{4.0, 3.55, 6}};
std::vector<Sprite> sprites;
Image floor;
Image ceiling;