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 2 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;

Loading…
Cancel
Save