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. 61
      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 #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_WIDTH=960;
constexpr const int RAY_VIEW_HEIGHT=720; constexpr const int RAY_VIEW_HEIGHT=720;
constexpr const int RAY_VIEW_X=(1280 - RAY_VIEW_WIDTH); constexpr const int RAY_VIEW_X=(1280 - RAY_VIEW_WIDTH);

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

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

@ -28,6 +28,11 @@ void TexturePack::load_textures() {
floor = load_image(assets["floor"]); floor = load_image(assets["floor"]);
ceiling = load_image(assets["ceiling"]); 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) { Image& TexturePack::get_texture(size_t num) {

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