@@ -10,13 +10,13 @@ void WGDefault::genChunk(Swan::WorldPlane &plane, Swan::Chunk &chunk) { | |||
chunk.pos_.x_ * Swan::CHUNK_WIDTH, chunk.pos_.y_ * Swan::CHUNK_HEIGHT); | |||
if (tpos.y_ == height) | |||
chunk.tiles_[cx][cy] = tGrass_; | |||
chunk.setTileID(Swan::TilePos(cx, cy), tGrass_); | |||
else if (tpos.y_ > height && tpos.y_ <= depth) | |||
chunk.tiles_[cx][cy] = tDirt_; | |||
chunk.setTileID(Swan::TilePos(cx, cy), tDirt_); | |||
else if (tpos.y_ > depth) | |||
chunk.tiles_[cx][cy] = tStone_; | |||
chunk.setTileID(Swan::TilePos(cx, cy), tStone_); | |||
else | |||
chunk.tiles_[cx][cy] = tAir_; | |||
chunk.setTileID(Swan::TilePos(cx, cy), tAir_); | |||
} | |||
} | |||
} |
@@ -22,7 +22,7 @@ private: | |||
static constexpr float FORCE = 3000; | |||
static constexpr float JUMP_FORCE = 10; | |||
static constexpr float MASS = 80; | |||
static constexpr Swan::Vec2 FRICTION = Swan::Vec2(400, 25); | |||
static constexpr Swan::Vec2 FRICTION = Swan::Vec2(400, 50); | |||
static constexpr Swan::Vec2 SIZE = Swan::Vec2(0.6, 1.9); | |||
enum class State { |
@@ -2,6 +2,8 @@ | |||
#include <SFML/Graphics/Texture.hpp> | |||
#include <string.h> | |||
#include <stdint.h> | |||
#include <memory> | |||
#include "common.h" | |||
#include "Tile.h" | |||
@@ -9,32 +11,46 @@ | |||
namespace Swan { | |||
class World; | |||
class Game; | |||
class Chunk { | |||
public: | |||
using RelPos = TilePos; | |||
Chunk(ChunkPos pos): pos_(pos) { | |||
texture_.create(CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE); | |||
sprite_ = sf::Sprite(texture_); | |||
data_.reset(new uint8_t[CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)]); | |||
visuals_.reset(new Visuals()); | |||
visuals_->tex_.create(CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE); | |||
visuals_->sprite_ = sf::Sprite(visuals_->tex_); | |||
visuals_->dirty_ = false; | |||
} | |||
void setTileID(World &world, RelPos pos, Tile::ID id); | |||
Tile &getTile(World &world, RelPos pos); | |||
Tile::ID *getTileData(); | |||
Tile::ID getTileID(RelPos pos); | |||
void setTileID(RelPos pos, Tile::ID id); | |||
void drawBlock(RelPos pos, const Tile &t); | |||
void compress(); | |||
void decompress(); | |||
void render(World &world); | |||
void draw(Win &win); | |||
void draw(Game &game, Win &win); | |||
ChunkPos pos_; | |||
Tile::ID tiles_[CHUNK_WIDTH][CHUNK_HEIGHT]; | |||
private: | |||
static sf::Uint8 *imgbuf; | |||
static sf::Uint8 *renderbuf; | |||
void drawBlock(RelPos pos, const Tile &t); | |||
void drawBlock(World &world, RelPos pos, Tile::ID id); | |||
bool dirty_ = false; | |||
sf::Texture texture_; | |||
sf::Sprite sprite_; | |||
std::unique_ptr<uint8_t[]> data_; | |||
int compressed_size_ = -1; // -1 if not compressed, a positive number if compressed | |||
bool need_render_ = false; | |||
struct Visuals { | |||
sf::Texture tex_; | |||
sf::Sprite sprite_; | |||
bool dirty_; | |||
}; | |||
std::unique_ptr<Visuals> visuals_; | |||
}; | |||
} |
@@ -14,6 +14,8 @@ | |||
namespace Swan { | |||
class Game; | |||
class World { | |||
public: | |||
WorldPlane &addPlane(std::string gen); | |||
@@ -32,7 +34,7 @@ public: | |||
Tile &getTileByID(Tile::ID id); | |||
Tile &getTile(const std::string &name); | |||
void draw(Win &win); | |||
void draw(Game &game, Win &win); | |||
void update(Game &game, float dt); | |||
void tick(); | |||
@@ -13,6 +13,7 @@ | |||
namespace Swan { | |||
class World; | |||
class Game; | |||
class WorldPlane { | |||
public: | |||
@@ -31,7 +32,7 @@ public: | |||
Entity &spawnPlayer(); | |||
void draw(Win &win); | |||
void draw(Game &game, Win &win); | |||
void update(Game &game, float dt); | |||
void tick(); | |||
@@ -42,7 +43,7 @@ public: | |||
std::shared_ptr<WorldGen> gen_; | |||
private: | |||
std::map<std::pair<int, int>, std::unique_ptr<Chunk>> chunks_; | |||
std::map<std::pair<int, int>, Chunk> chunks_; | |||
std::vector<std::unique_ptr<Entity>> entities_; | |||
std::vector<TilePos> debug_boxes_; | |||
}; |
@@ -9,8 +9,8 @@ namespace Swan { | |||
static constexpr int TILE_SIZE = 32; | |||
static constexpr int TICK_RATE = 20; | |||
static constexpr int CHUNK_HEIGHT = 16; | |||
static constexpr int CHUNK_WIDTH = 24; | |||
static constexpr int CHUNK_HEIGHT = 32; | |||
static constexpr int CHUNK_WIDTH = 32; | |||
using TilePos = Vec2i; | |||
using ChunkPos = Vec2i; |
@@ -1,27 +1,92 @@ | |||
#include "Chunk.h" | |||
#include <SFML/System/Clock.hpp> | |||
#include <zlib.h> | |||
#include "World.h" | |||
#include "Game.h" | |||
namespace Swan { | |||
sf::Uint8 *Chunk::imgbuf = new sf::Uint8[CHUNK_WIDTH * TILE_SIZE * CHUNK_HEIGHT * TILE_SIZE * 4]; | |||
sf::Uint8 *Chunk::renderbuf = new sf::Uint8[CHUNK_WIDTH * TILE_SIZE * CHUNK_HEIGHT * TILE_SIZE * 4]; | |||
Tile::ID *Chunk::getTileData() { | |||
if (compressed_size_ != -1) | |||
decompress(); | |||
void Chunk::setTileID(World &world, RelPos pos, Tile::ID id) { | |||
tiles_[pos.x_][pos.y_] = id; | |||
drawBlock(world, pos, id); | |||
return (Tile::ID *)data_.get(); | |||
} | |||
Tile &Chunk::getTile(World &world, RelPos pos) { | |||
return world.getTileByID(tiles_[pos.x_][pos.y_]); | |||
Tile::ID Chunk::getTileID(RelPos pos) { | |||
return getTileData()[pos.y_ * CHUNK_WIDTH + pos.x_]; | |||
} | |||
void Chunk::setTileID(RelPos pos, Tile::ID id) { | |||
getTileData()[pos.y_ * CHUNK_WIDTH + pos.x_] = id; | |||
} | |||
void Chunk::drawBlock(RelPos pos, const Tile &t) { | |||
texture_.update(t.image_, pos.x_ * TILE_SIZE, pos.y_ * TILE_SIZE); | |||
dirty_ = true; | |||
if (compressed_size_ != -1) | |||
decompress(); | |||
visuals_->tex_.update(t.image_, pos.x_ * TILE_SIZE, pos.y_ * TILE_SIZE); | |||
visuals_->dirty_ = true; | |||
} | |||
void Chunk::compress() { | |||
if (compressed_size_ != -1) | |||
return; | |||
sf::Clock clock; | |||
// We only need a fixed-length temp buffer; | |||
// if the compressed data gets too big, there's no point in compressing | |||
uint8_t dest[CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)]; | |||
uLongf destlen = sizeof(dest); | |||
int ret = compress2( | |||
(Bytef *)dest, &destlen, | |||
(Bytef *)data_.get(), CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID), | |||
Z_BEST_COMPRESSION); | |||
if (ret == Z_OK) { | |||
data_.reset(new uint8_t[destlen]); | |||
memcpy(data_.get(), dest, destlen); | |||
visuals_.reset(); | |||
compressed_size_ = destlen; | |||
fprintf(stderr, "Compressed chunk %i,%i from %lu bytes to %lu bytes in %.3fs.\n", | |||
pos_.x_, pos_.y_, CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID), destlen, | |||
clock.getElapsedTime().asSeconds()); | |||
} else if (ret == Z_BUF_ERROR) { | |||
fprintf(stderr, "Didn't compress chunk %i,%i because compressing it would've made it bigger.\n", | |||
pos_.x_, pos_.y_); | |||
} else { | |||
fprintf(stderr, "Chunk compression error: %i (Out of memory?)\n", ret); | |||
} | |||
} | |||
void Chunk::drawBlock(World &world, RelPos pos, Tile::ID id) { | |||
drawBlock(pos, world.getTileByID(id)); | |||
void Chunk::decompress() { | |||
uint8_t *dest = new uint8_t[CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)]; | |||
uLongf destlen = CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID); | |||
int ret = uncompress( | |||
dest, &destlen, | |||
(Bytef *)data_.get(), compressed_size_); | |||
if (ret != Z_OK) { | |||
fprintf(stderr, "Decompressing chunk failed: %i\n", ret); | |||
delete[] dest; | |||
abort(); | |||
} | |||
data_.reset(dest); | |||
visuals_.reset(new Visuals()); | |||
visuals_->tex_.create(CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE); | |||
visuals_->sprite_ = sf::Sprite(); | |||
visuals_->dirty_ = true; | |||
need_render_ = true; | |||
compressed_size_ = -1; | |||
} | |||
void Chunk::render(World &world) { | |||
@@ -30,7 +95,7 @@ void Chunk::render(World &world) { | |||
for (int x = 0; x < CHUNK_WIDTH; ++x) { | |||
for (int y = 0; y < CHUNK_HEIGHT; ++y) { | |||
Tile::ID id = tiles_[x][y]; | |||
Tile::ID id = getTileID(RelPos(x, y)); | |||
if (id != prevID) { | |||
prevID = id; | |||
tile = world.getTileByID(id); | |||
@@ -40,7 +105,7 @@ void Chunk::render(World &world) { | |||
for (int imgy = 0; imgy < TILE_SIZE; ++imgy) { | |||
int pixx = x * TILE_SIZE; | |||
int pixy = y * TILE_SIZE + imgy; | |||
sf::Uint8 *pix = imgbuf + | |||
sf::Uint8 *pix = renderbuf + | |||
pixy * CHUNK_WIDTH * TILE_SIZE * 4 + | |||
pixx * 4; | |||
memcpy(pix, imgptr + imgy * TILE_SIZE * 4, TILE_SIZE * 4); | |||
@@ -48,17 +113,26 @@ void Chunk::render(World &world) { | |||
} | |||
} | |||
texture_.update(imgbuf, CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE, 0, 0); | |||
visuals_->tex_.update(renderbuf, CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE, 0, 0); | |||
visuals_->dirty_ = true; | |||
} | |||
void Chunk::draw(Win &win) { | |||
if (dirty_) { | |||
sprite_.setTexture(texture_); | |||
dirty_ = false; | |||
void Chunk::draw(Game &game, Win &win) { | |||
if (compressed_size_ != -1) | |||
decompress(); | |||
if (need_render_) { | |||
render(*game.world_); | |||
need_render_ = false; | |||
} | |||
if (visuals_->dirty_) { | |||
visuals_->sprite_.setTexture(visuals_->tex_); | |||
visuals_->dirty_ = false; | |||
} | |||
win.setPos(pos_ * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT)); | |||
win.draw(sprite_); | |||
win.draw(visuals_->sprite_); | |||
} | |||
} |
@@ -61,7 +61,7 @@ bool Game::isMousePressed() { | |||
} | |||
void Game::draw() { | |||
world_->draw(win_); | |||
world_->draw(*this, win_); | |||
} | |||
void Game::update(float dt) { |
@@ -2,6 +2,8 @@ | |||
#include <SFML/System/Clock.hpp> | |||
#include "Game.h" | |||
namespace Swan { | |||
static bool chunkLine(int l, sf::Clock &clock, WorldPlane &plane, ChunkPos &abspos, const Vec2i &dir) { | |||
@@ -24,7 +26,7 @@ void World::ChunkRenderer::tick(WorldPlane &plane, ChunkPos abspos) { | |||
sf::Clock clock; | |||
int l = 0; | |||
for (int i = 0; i < 8; ++i) { | |||
for (int i = 0; i < 4; ++i) { | |||
if (chunkLine(l, clock, plane, abspos, Vec2i(0, -1))) break; | |||
if (chunkLine(l, clock, plane, abspos, Vec2i(1, 0))) break; | |||
l += 1; | |||
@@ -116,9 +118,9 @@ WorldPlane &World::addPlane(std::string gen) { | |||
return planes_[id]; | |||
} | |||
void World::draw(Win &win) { | |||
void World::draw(Game &game, Win &win) { | |||
win.cam_ = player_->getPos() - (win.getSize() / 2) + 0.5; | |||
planes_[current_plane_].draw(win); | |||
planes_[current_plane_].draw(game, win); | |||
} | |||
void World::update(Game &game, float dt) { |
@@ -1,8 +1,10 @@ | |||
#include "WorldPlane.h" | |||
#include <math.h> | |||
#include <SFML/System/Clock.hpp> | |||
#include "World.h" | |||
#include "Game.h" | |||
#include "Timer.h" | |||
namespace Swan { | |||
@@ -44,16 +46,19 @@ Chunk &WorldPlane::getChunk(ChunkPos pos) { | |||
auto iter = chunks_.find(pos); | |||
if (iter == chunks_.end()) { | |||
iter = chunks_.emplace(pos, new Chunk(pos)).first; | |||
gen_->genChunk(*this, *iter->second); | |||
iter->second->render(*world_); | |||
iter = chunks_.emplace(pos, Chunk(pos)).first; | |||
gen_->genChunk(*this, iter->second); | |||
iter->second.render(*world_); | |||
} | |||
return *iter->second; | |||
return iter->second; | |||
} | |||
void WorldPlane::setTileID(TilePos pos, Tile::ID id) { | |||
getChunk(chunkPos(pos)).setTileID(*world_, relPos(pos), id); | |||
Chunk &chunk = getChunk(chunkPos(pos)); | |||
Chunk::RelPos rp = relPos(pos); | |||
chunk.setTileID(rp, id); | |||
chunk.drawBlock(rp, world_->getTileByID(id)); | |||
} | |||
void WorldPlane::setTile(TilePos pos, const std::string &name) { | |||
@@ -61,14 +66,16 @@ void WorldPlane::setTile(TilePos pos, const std::string &name) { | |||
} | |||
Tile &WorldPlane::getTile(TilePos pos) { | |||
return getChunk(chunkPos(pos)).getTile(*world_, relPos(pos)); | |||
Chunk &chunk = getChunk(chunkPos(pos)); | |||
Tile::ID id = chunk.getTileID(relPos(pos)); | |||
return world_->getTileByID(id); | |||
} | |||
Entity &WorldPlane::spawnPlayer() { | |||
return gen_->spawnPlayer(*this); | |||
} | |||
void WorldPlane::draw(Win &win) { | |||
void WorldPlane::draw(Game &game, Win &win) { | |||
const Vec2 &ppos = world_->player_->getPos(); | |||
ChunkPos pcpos = ChunkPos( | |||
(int)floor(ppos.x_ / CHUNK_WIDTH), | |||
@@ -78,12 +85,10 @@ void WorldPlane::draw(Win &win) { | |||
for (int y = -1; y <= 1; ++y) { | |||
auto chunk = chunks_.find(pcpos + ChunkPos(x, y)); | |||
if (chunk != chunks_.end()) | |||
chunk->second->draw(win); | |||
chunk->second.draw(game, win); | |||
} | |||
} | |||
//for (auto &ch: chunks_) | |||
// ch.second->draw(win); | |||
for (auto &ent: entities_) | |||
ent->draw(win); | |||