Blocks atlas is auto-assembling now
|
Before Width: | Height: | Size: 17 KiB |
BIN
res/textures/blocks/air.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
res/textures/blocks/bedrock.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
res/textures/blocks/brick.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
res/textures/blocks/dirt.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
res/textures/blocks/flower.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
res/textures/blocks/glass.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
res/textures/blocks/grass.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
res/textures/blocks/grass_side.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
res/textures/blocks/grass_top.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
res/textures/blocks/lamp.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
res/textures/blocks/leaves.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
res/textures/blocks/metal.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
res/textures/blocks/planks.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
res/textures/blocks/rust.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
res/textures/blocks/sand.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
res/textures/blocks/stone.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
res/textures/blocks/water.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
res/textures/blocks/wood.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
res/textures/blocks/wood_top.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 257 KiB |
@ -2,6 +2,7 @@
|
||||
|
||||
#include "../graphics/Texture.h"
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Font.h"
|
||||
|
||||
Assets::~Assets() {
|
||||
@ -16,6 +17,10 @@ Assets::~Assets() {
|
||||
for (auto& iter : fonts){
|
||||
delete iter.second;
|
||||
}
|
||||
|
||||
for (auto& iter : atlases) {
|
||||
delete iter.second;
|
||||
}
|
||||
}
|
||||
|
||||
Texture* Assets::getTexture(std::string name) const {
|
||||
@ -52,3 +57,14 @@ Font* Assets::getFont(std::string name) const {
|
||||
void Assets::store(Font* font, std::string name){
|
||||
fonts[name] = font;
|
||||
}
|
||||
|
||||
Atlas* Assets::getAtlas(std::string name) const {
|
||||
auto found = atlases.find(name);
|
||||
if (found == atlases.end())
|
||||
return nullptr;
|
||||
return found->second;
|
||||
}
|
||||
|
||||
void Assets::store(Atlas* atlas, std::string name){
|
||||
atlases[name] = atlas;
|
||||
}
|
||||
|
||||
@ -7,11 +7,13 @@
|
||||
class Texture;
|
||||
class Shader;
|
||||
class Font;
|
||||
class Atlas;
|
||||
|
||||
class Assets {
|
||||
std::unordered_map<std::string, Texture*> textures;
|
||||
std::unordered_map<std::string, Shader*> shaders;
|
||||
std::unordered_map<std::string, Font*> fonts;
|
||||
std::unordered_map<std::string, Atlas*> atlases;
|
||||
public:
|
||||
~Assets();
|
||||
Texture* getTexture(std::string name) const;
|
||||
@ -22,6 +24,9 @@ public:
|
||||
|
||||
Font* getFont(std::string name) const;
|
||||
void store(Font* font, std::string name);
|
||||
|
||||
Atlas* getAtlas(std::string name) const;
|
||||
void store(Atlas* atlas, std::string name);
|
||||
};
|
||||
|
||||
#endif /* ASSETS_ASSETS_H_ */
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "Assets.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
#include "../constants.h"
|
||||
@ -42,6 +43,7 @@ bool AssetsLoader::loadNext() {
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/ImageData.h"
|
||||
#include "../graphics/Texture.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Font.h"
|
||||
|
||||
bool _load_shader(Assets* assets, const std::string& filename, const std::string& name) {
|
||||
@ -65,18 +67,19 @@ bool _load_texture(Assets* assets, const std::string& filename, const std::strin
|
||||
}
|
||||
|
||||
bool _load_atlas(Assets* assets, const std::string& filename, const std::string& name) {
|
||||
unique_ptr<ImageData> image (png::load_image(filename));
|
||||
if (image == nullptr) {
|
||||
std::cerr << "failed to load image '" << name << "'" << std::endl;
|
||||
return false;
|
||||
AtlasBuilder builder;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(filename)) {
|
||||
std::filesystem::path file = entry.path();
|
||||
if (file.extension() == ".png") {
|
||||
std::string name = file.stem().string();
|
||||
std::unique_ptr<ImageData> image (png::load_image(file.string()));
|
||||
//image->flipY();
|
||||
image->fixAlphaColor();
|
||||
builder.add(name, image.release());
|
||||
}
|
||||
for (int i = 0; i < ATLAS_MARGIN_SIZE; i++) {
|
||||
ImageData* newimage = add_atlas_margins(image.get(), 16);
|
||||
image.reset(newimage);
|
||||
}
|
||||
|
||||
Texture* texture = Texture::from(image.get());
|
||||
assets->store(texture, name);
|
||||
Atlas* atlas = builder.build(2);
|
||||
assets->store(atlas, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -107,9 +110,7 @@ void AssetsLoader::addDefaults(AssetsLoader& loader) {
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/lines", "lines");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui");
|
||||
|
||||
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/block.png", "block");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/block.png", "block_tex");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/slot.png", "slot");
|
||||
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/menubg.png", "menubg");
|
||||
|
||||
loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal");
|
||||
|
||||
@ -18,8 +18,6 @@ inline uint vox_index(int x, int y, int z, int w=CHUNK_W, int d=CHUNK_D) {
|
||||
return (y * d + z) * w + x;
|
||||
}
|
||||
|
||||
#define ATLAS_MARGIN_SIZE 2
|
||||
|
||||
#define RES_FLODER "res/"
|
||||
|
||||
#define SHADERS_FOLDER "res/shaders"
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
// All in-game definitions (blocks, items, etc..)
|
||||
void setup_definitions(ContentBuilder* builder) {
|
||||
// TODO: automatic atlas generation instead of using texture indices
|
||||
Block* block = new Block("core:air", 0);
|
||||
Block* block = new Block("core:air", "air");
|
||||
block->drawGroup = 1;
|
||||
block->lightPassing = true;
|
||||
block->skyLightPassing = true;
|
||||
@ -18,41 +18,41 @@ void setup_definitions(ContentBuilder* builder) {
|
||||
block->model = BlockModel::none;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:dirt", 2);
|
||||
block = new Block("base:dirt", "dirt");
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:grass_block", 4);
|
||||
block->textureFaces[2] = 2;
|
||||
block->textureFaces[3] = 1;
|
||||
block = new Block("base:grass_block", "grass_side");
|
||||
block->textureFaces[2] = "dirt";
|
||||
block->textureFaces[3] = "grass_top";
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:lamp", 3);
|
||||
block = new Block("base:lamp", "lamp");
|
||||
block->emission[0] = 15;
|
||||
block->emission[1] = 14;
|
||||
block->emission[2] = 13;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:glass",5);
|
||||
block = new Block("base:glass", "glass");
|
||||
block->drawGroup = 2;
|
||||
block->lightPassing = true;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:planks", 6);
|
||||
block = new Block("base:planks", "planks");
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:wood", 7);
|
||||
block->textureFaces[2] = 8;
|
||||
block->textureFaces[3] = 8;
|
||||
block = new Block("base:wood", "wood");
|
||||
block->textureFaces[2] = "wood_top";
|
||||
block->textureFaces[3] = "wood_top";
|
||||
block->rotatable = true;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:leaves", 9);
|
||||
block = new Block("base:leaves", "leaves");
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:stone", 10);
|
||||
block = new Block("base:stone", "stone");
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:water", 11);
|
||||
block = new Block("base:water", "water");
|
||||
block->drawGroup = 4;
|
||||
block->lightPassing = true;
|
||||
block->skyLightPassing = false;
|
||||
@ -60,14 +60,14 @@ void setup_definitions(ContentBuilder* builder) {
|
||||
block->selectable = false;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:sand", 12);
|
||||
block = new Block("base:sand", "sand");
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:bedrock", 13);
|
||||
block = new Block("base:bedrock", "bedrock");
|
||||
block->breakable = false;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:grass", 14);
|
||||
block = new Block("base:grass", "grass");
|
||||
block->drawGroup = 5;
|
||||
block->lightPassing = true;
|
||||
block->obstacle = false;
|
||||
@ -75,20 +75,20 @@ void setup_definitions(ContentBuilder* builder) {
|
||||
block->hitboxScale = 0.5f;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:flower", 16);
|
||||
block = new Block("base:flower", "flower");
|
||||
block->drawGroup = 5;
|
||||
block->lightPassing = true;
|
||||
block->obstacle = false;
|
||||
block->model = BlockModel::xsprite;
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:brick", 17);
|
||||
block = new Block("base:brick", "brick");
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:metal", 18);
|
||||
block = new Block("base:metal", "metal");
|
||||
builder->add(block);
|
||||
|
||||
block = new Block("base:rust", 19);
|
||||
block = new Block("base:rust", "rust");
|
||||
builder->add(block);
|
||||
}
|
||||
|
||||
|
||||
@ -162,8 +162,7 @@ InputBindBox::InputBindBox(Binding& binding, vec4 padding)
|
||||
: Panel(vec2(100,32), padding, 0, false),
|
||||
binding(binding) {
|
||||
label = new Label(L"");
|
||||
//label->align(Align::center);
|
||||
add(shared_ptr<UINode>(label));
|
||||
add(label);
|
||||
}
|
||||
|
||||
shared_ptr<UINode> InputBindBox::getAt(vec2 pos, shared_ptr<UINode> self) {
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Batch2D.h"
|
||||
#include "../graphics/Font.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Mesh.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../window/Window.h"
|
||||
@ -146,6 +147,7 @@ void HudRenderer::drawContentAccess(const GfxContext& ctx, Player* player) {
|
||||
|
||||
const Viewport& viewport = ctx.getViewport();
|
||||
const uint width = viewport.getWidth();
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
|
||||
uint count = contentIds->countBlockDefs();
|
||||
uint icon_size = 48;
|
||||
@ -171,7 +173,7 @@ void HudRenderer::drawContentAccess(const GfxContext& ctx, Player* player) {
|
||||
batch->rect(inv_x, inv_y, inv_w, inv_h);
|
||||
|
||||
// blocks & items
|
||||
batch->texture(assets->getTexture("block_tex"));
|
||||
batch->texture(atlas->getTexture());
|
||||
for (uint i = 0; i < count-1; i++) {
|
||||
Block* cblock = contentIds->getBlockDef(i+1);
|
||||
if (cblock == nullptr)
|
||||
@ -190,9 +192,10 @@ void HudRenderer::drawContentAccess(const GfxContext& ctx, Player* player) {
|
||||
}
|
||||
|
||||
if (cblock->model == BlockModel::block){
|
||||
batch->blockSprite(x, y, icon_size, icon_size, 16, cblock->textureFaces, tint);
|
||||
batch->blockSprite(x, y, icon_size, icon_size, (const UVRegion*)cblock->uvdata, tint);
|
||||
} else if (cblock->model == BlockModel::xsprite){
|
||||
batch->sprite(x, y, icon_size, icon_size, 16, cblock->textureFaces[3], tint);
|
||||
const UVRegion& region = (reinterpret_cast<UVRegion*>(cblock->uvdata))[3];
|
||||
batch->sprite(x, y, icon_size, icon_size, region, tint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,6 +234,8 @@ void HudRenderer::draw(const GfxContext& ctx){
|
||||
const uint width = viewport.getWidth();
|
||||
const uint height = viewport.getHeight();
|
||||
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
|
||||
debugPanel->visible(level->player->debug);
|
||||
|
||||
uicamera->fov = height;
|
||||
@ -239,10 +244,9 @@ void HudRenderer::draw(const GfxContext& ctx){
|
||||
uishader->use();
|
||||
uishader->uniformMatrix("u_projview", uicamera->getProjection()*uicamera->getView());
|
||||
|
||||
// Chosen block preview
|
||||
Texture* blocks = assets->getTexture("block_tex");
|
||||
batch->begin();
|
||||
|
||||
batch->texture(nullptr);
|
||||
// Chosen block preview
|
||||
batch->color = vec4(1.0f);
|
||||
if (Events::_cursor_locked && !level->player->debug) {
|
||||
batch->lineWidth(2);
|
||||
@ -258,14 +262,15 @@ void HudRenderer::draw(const GfxContext& ctx){
|
||||
batch->rect(width - 68, height - 68, 68, 68);
|
||||
|
||||
batch->color = vec4(1.0f);
|
||||
batch->texture(blocks);
|
||||
batch->texture(atlas->getTexture());
|
||||
{
|
||||
Block* cblock = contentIds->getBlockDef(player->choosenBlock);
|
||||
assert(cblock != nullptr);
|
||||
if (cblock->model == BlockModel::block){
|
||||
batch->blockSprite(width-56, uicamera->fov - 56, 48, 48, 16, cblock->textureFaces, vec4(1.0f));
|
||||
batch->blockSprite(width-56, uicamera->fov - 56, 48, 48, (const UVRegion*)cblock->uvdata, vec4(1.0f));
|
||||
} else if (cblock->model == BlockModel::xsprite){
|
||||
batch->sprite(width-56, uicamera->fov - 56, 48, 48, 16, cblock->textureFaces[3], vec4(1.0f));
|
||||
const UVRegion& region = (reinterpret_cast<UVRegion*>(cblock->uvdata))[3];
|
||||
batch->sprite(width-56, uicamera->fov - 56, 48, 48, region, vec4(1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "../window/Window.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../graphics/Mesh.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Texture.h"
|
||||
#include "../graphics/LineBatch.h"
|
||||
@ -27,6 +28,7 @@
|
||||
#include "../engine.h"
|
||||
|
||||
using glm::vec3;
|
||||
using std::string;
|
||||
using std::shared_ptr;
|
||||
|
||||
WorldRenderer::WorldRenderer(Engine* engine, Level* level)
|
||||
@ -37,6 +39,26 @@ WorldRenderer::WorldRenderer(Engine* engine, Level* level)
|
||||
level->events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type type, Chunk* chunk) {
|
||||
renderer->unload(chunk);
|
||||
});
|
||||
|
||||
// TODO: move to some proper place
|
||||
// otrefactoryu dnem
|
||||
Assets* assets = engine->getAssets();
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
const Content* content = level->content;
|
||||
const ContentIndices* contentIds = content->indices;
|
||||
for (uint i = 0; i < contentIds->countBlockDefs(); i++) {
|
||||
Block* def = contentIds->getBlockDef(i);
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
string tex = def->textureFaces[side];
|
||||
if (atlas->has(tex)) {
|
||||
UVRegion region = atlas->get(tex);
|
||||
float* data = reinterpret_cast<float*>(®ion);
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
def->uvdata[side * 4 + j] = data[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorldRenderer::~WorldRenderer() {
|
||||
@ -99,7 +121,7 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera, bool occlusion)
|
||||
const Content* content = level->content;
|
||||
const ContentIndices* contentIds = content->indices;
|
||||
Assets* assets = engine->getAssets();
|
||||
Texture* texture = assets->getTexture("block");
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
Shader* shader = assets->getShader("main");
|
||||
Shader* linesShader = assets->getShader("lines");
|
||||
|
||||
@ -139,7 +161,7 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera, bool occlusion)
|
||||
cblock->emission[1] / 15.0f * multiplier,
|
||||
cblock->emission[2] / 15.0f * multiplier);
|
||||
shader->uniform1f("u_torchlightDistance", 6.0f);
|
||||
texture->bind();
|
||||
atlas->getTexture()->bind();
|
||||
|
||||
Chunks* chunks = level->chunks;
|
||||
drawChunks(chunks, camera, shader, occlusion);
|
||||
|
||||
86
src/graphics/Atlas.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "Atlas.h"
|
||||
|
||||
#include "../maths/LMPacker.h"
|
||||
#include "Texture.h"
|
||||
#include "ImageData.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
using std::shared_ptr;
|
||||
using std::unordered_map;
|
||||
|
||||
Atlas::Atlas(ImageData* image,
|
||||
unordered_map<string, UVRegion> regions)
|
||||
: texture(Texture::from(image)),
|
||||
image(image),
|
||||
regions(regions) {
|
||||
}
|
||||
|
||||
Atlas::~Atlas() {
|
||||
delete image;
|
||||
delete texture;
|
||||
}
|
||||
|
||||
bool Atlas::has(string name) const {
|
||||
return regions.find(name) != regions.end();
|
||||
}
|
||||
|
||||
const UVRegion& Atlas::get(string name) const {
|
||||
return regions.at(name);
|
||||
}
|
||||
|
||||
Texture* Atlas::getTexture() const {
|
||||
return texture;
|
||||
}
|
||||
|
||||
ImageData* Atlas::getImage() const {
|
||||
return image;
|
||||
}
|
||||
|
||||
void AtlasBuilder::add(string name, ImageData* image) {
|
||||
entries.push_back(atlasentry{name, shared_ptr<ImageData>(image)});
|
||||
}
|
||||
|
||||
Atlas* AtlasBuilder::build(uint extrusion) {
|
||||
unique_ptr<uint[]> sizes (new uint[entries.size() * 2]);
|
||||
for (uint i = 0; i < entries.size(); i++) {
|
||||
auto& entry = entries[i];
|
||||
auto image = entry.image;
|
||||
sizes[i*2] = image->getWidth();
|
||||
sizes[i*2+1] = image->getHeight();
|
||||
}
|
||||
LMPacker packer(sizes.get(), entries.size()*2);
|
||||
sizes.reset(nullptr);
|
||||
|
||||
int width = 32;
|
||||
int height = 32;
|
||||
while (!packer.buildCompact(width, height, extrusion)) {
|
||||
if (width > height) {
|
||||
height *= 2;
|
||||
} else {
|
||||
width *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
unordered_map<string, UVRegion> regions;
|
||||
unique_ptr<ImageData> canvas (new ImageData(ImageFormat::rgba8888, width, height));
|
||||
vector<rectangle> rects = packer.getResult();
|
||||
for (uint i = 0; i < entries.size(); i++) {
|
||||
const rectangle& rect = rects[i];
|
||||
const atlasentry& entry = entries[i];
|
||||
uint x = rect.x;
|
||||
uint y = rect.y;
|
||||
uint w = rect.width;
|
||||
uint h = rect.height;
|
||||
canvas->blit(entry.image.get(), rect.x, rect.y);
|
||||
for (uint j = 0; j < extrusion; j++) {
|
||||
canvas->extrude(x - j, y - j, w + j*2, h + j*2);
|
||||
}
|
||||
float unitX = 1.0f / width;
|
||||
float unitY = 1.0f / height;
|
||||
regions[entry.name] = UVRegion(unitX * x, unitY * y,
|
||||
unitX * (x + w), unitY * (y + h));
|
||||
}
|
||||
return new Atlas(canvas.release(), regions);
|
||||
}
|
||||
44
src/graphics/Atlas.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef GRAPHICS_ATLAS_H_
|
||||
#define GRAPHICS_ATLAS_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "UVRegion.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
class ImageData;
|
||||
class Texture;
|
||||
|
||||
class Atlas {
|
||||
Texture* texture;
|
||||
ImageData* image;
|
||||
std::unordered_map<std::string, UVRegion> regions;
|
||||
public:
|
||||
Atlas(ImageData* image, std::unordered_map<std::string, UVRegion> regions);
|
||||
~Atlas();
|
||||
|
||||
bool has(std::string name) const;
|
||||
const UVRegion& get(std::string name) const;
|
||||
|
||||
Texture* getTexture() const;
|
||||
ImageData* getImage() const;
|
||||
};
|
||||
|
||||
|
||||
struct atlasentry {
|
||||
std::string name;
|
||||
std::shared_ptr<ImageData> image;
|
||||
};
|
||||
|
||||
class AtlasBuilder {
|
||||
std::vector<atlasentry> entries;
|
||||
public:
|
||||
AtlasBuilder() {}
|
||||
void add(std::string name, ImageData* image);
|
||||
|
||||
Atlas* build(uint extrusion);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_ATLAS_H_
|
||||
@ -288,6 +288,10 @@ void Batch2D::sprite(Sprite* sprite) {
|
||||
sprite->color);
|
||||
}
|
||||
|
||||
void Batch2D::sprite(float x, float y, float w, float h, const UVRegion& region, vec4 tint){
|
||||
rect(x, y, w, h, region.u1, region.v1, region.u2-region.u1, region.v2-region.v1, tint.r, tint.g, tint.b, tint.a);
|
||||
}
|
||||
|
||||
void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index, vec4 tint){
|
||||
float scale = 1.0f / (float)atlasRes;
|
||||
float u = (index % atlasRes) * scale;
|
||||
@ -295,12 +299,16 @@ void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index
|
||||
rect(x, y, w, h, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a);
|
||||
}
|
||||
|
||||
void Batch2D::blockSprite(float x, float y, float w, float h, int atlasRes, int index[6], vec4 tint){
|
||||
float scale = 1.0f / (float)atlasRes;
|
||||
float uu = (index[3] % atlasRes) * scale;
|
||||
float vu = 1.0f - ((index[3] / atlasRes) * scale) - scale;
|
||||
float uf = (index[0] % atlasRes) * scale;
|
||||
float vf = 1.0f - ((index[0] / atlasRes) * scale) - scale;
|
||||
void Batch2D::blockSprite(float x, float y, float w, float h, const UVRegion regions[], vec4 tint){
|
||||
// TODO: rewrite it
|
||||
float uu = (regions[3].u1);
|
||||
float vu = (regions[3].v1);
|
||||
|
||||
float uf = (regions[0].u1);
|
||||
float vf = (regions[0].v1);
|
||||
|
||||
float scalex = regions[3].u2-regions[3].u1;
|
||||
float scaley = regions[3].v2-regions[3].v1;
|
||||
|
||||
if (this->index + 18*VERTEX_SIZE >= capacity)
|
||||
render();
|
||||
@ -317,13 +325,13 @@ void Batch2D::blockSprite(float x, float y, float w, float h, int atlasRes, int
|
||||
vec2(ox-sx, y+(h*0.75f))};
|
||||
|
||||
vec2 uvpoints[8] = {vec2(uu, vu),
|
||||
vec2(uu+scale, vu),
|
||||
vec2(uu+scale, vu+scale),
|
||||
vec2(uu, vu+scale),
|
||||
vec2(uu+scalex, vu),
|
||||
vec2(uu+scalex, vu+scalex),
|
||||
vec2(uu, vu+scalex),
|
||||
vec2(uf, vf),
|
||||
vec2(uf+scale, vf),
|
||||
vec2(uf+scale, vf+scale),
|
||||
vec2(uf, vf+scale)};
|
||||
vec2(uf+scaley, vf),
|
||||
vec2(uf+scaley, vf+scaley),
|
||||
vec2(uf, vf+scaley)};
|
||||
|
||||
vertex(points[0], uvpoints[3], tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(points[1], uvpoints[0], tint.r, tint.g, tint.b, tint.a);
|
||||
|
||||
@ -37,9 +37,10 @@ public:
|
||||
|
||||
void begin();
|
||||
void texture(Texture* texture);
|
||||
void sprite(float x, float y, float w, float h, int atlasRes, int index, vec4 tint);
|
||||
void sprite(float x, float y, float w, float h, const UVRegion& region, vec4 tint);
|
||||
void sprite(Sprite* sprite);
|
||||
void blockSprite(float x, float y, float w, float h, int atlasRes, int index[6], vec4 tint);
|
||||
void sprite(float x, float y, float w, float h, int atlasRes, int index, vec4 tint);
|
||||
void blockSprite(float x, float y, float w, float h, const UVRegion regions[], vec4 tint);
|
||||
void point(float x, float y, float r, float g, float b, float a);
|
||||
void line(float x1, float y1, float x2, float y2, float r, float g, float b, float a);
|
||||
void rect(float x, float y,
|
||||
|
||||
@ -303,12 +303,7 @@ vec4 BlocksRenderer::pickSoftLight(int x, int y, int z, const ivec3& right, cons
|
||||
|
||||
// Get texture atlas UV region for block face
|
||||
inline UVRegion uvfor(const Block& def, uint face, int atlas_size) {
|
||||
float uvsize = 1.0f / (float)atlas_size;
|
||||
float us = 1.0f / (float)atlas_size / (float)atlas_size * ATLAS_MARGIN_SIZE * 0.8f;
|
||||
const uint id = def.textureFaces[face];
|
||||
float u = (id % atlas_size) * uvsize;
|
||||
float v = 1.0f - (id / atlas_size + 1) * uvsize;
|
||||
return UVRegion(u + us, v + us, u + uvsize - us, v + uvsize - us);
|
||||
return *reinterpret_cast<const UVRegion*>(def.uvdata + face * 4);
|
||||
}
|
||||
|
||||
void BlocksRenderer::render(const voxel* voxels, int atlas_size) {
|
||||
|
||||
@ -11,6 +11,16 @@ inline int max(int a, int b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
ImageData::ImageData(ImageFormat format, uint width, uint height)
|
||||
: format(format), width(width), height(height) {
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: data = new ubyte[width*height*3]{}; break;
|
||||
case ImageFormat::rgba8888: data = new ubyte[width*height*4]{}; break;
|
||||
default:
|
||||
throw std::runtime_error("format is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
ImageData::ImageData(ImageFormat format, uint width, uint height, void* data)
|
||||
: format(format), width(width), height(height), data(data) {
|
||||
}
|
||||
@ -70,6 +80,150 @@ void ImageData::flipY() {
|
||||
}
|
||||
}
|
||||
|
||||
void ImageData::blit(const ImageData* image, int x, int y) {
|
||||
if (format != image->format) {
|
||||
throw std::runtime_error("mismatching format");
|
||||
}
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
ubyte* pixels = static_cast<ubyte*>(data);
|
||||
ubyte* source = static_cast<ubyte*>(image->getData());
|
||||
uint srcwidth = image->getWidth();
|
||||
uint srcheight = image->getHeight();
|
||||
|
||||
for (uint srcy = max(0, -y); (int)srcy < min(srcheight, height-y); srcy++) {
|
||||
for (uint srcx = max(0, -x); (int)srcx < min(srcwidth, width-x); srcx++) {
|
||||
uint dstx = srcx + x;
|
||||
uint dsty = srcy + y;
|
||||
uint dstidx = (dsty * width + dstx) * comps;
|
||||
uint srcidx = (srcy * srcwidth + srcx) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = source[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Extrude rectangle zone border pixels out by 1 pixel.
|
||||
Used to remove atlas texture border artifacts */
|
||||
void ImageData::extrude(int x, int y, int w, int h) {
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
ubyte* pixels = static_cast<ubyte*>(data);
|
||||
|
||||
int rx = x + w - 1;
|
||||
int ry = y + h - 1;
|
||||
|
||||
// top-left pixel
|
||||
if (x > 0 && (uint)x < width && y > 0 && (uint)y < height) {
|
||||
uint srcidx = (y * width + x) * comps;
|
||||
uint dstidx = ((y - 1) * width + x - 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// top-right pixel
|
||||
if (rx >= 0 && (uint)rx < width-1 && y > 0 && (uint)y < height) {
|
||||
uint srcidx = (y * width + rx) * comps;
|
||||
uint dstidx = ((y - 1) * width + rx + 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// bottom-left pixel
|
||||
if (x > 0 && (uint)x < width && ry >= 0 && (uint)ry < height-1) {
|
||||
uint srcidx = (ry * width + x) * comps;
|
||||
uint dstidx = ((ry + 1) * width + x - 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// bottom-right pixel
|
||||
if (rx >= 0 && (uint)rx < width-1 && ry >= 0 && (uint)ry < height-1) {
|
||||
uint srcidx = (ry * width + rx) * comps;
|
||||
uint dstidx = ((ry + 1) * width + rx + 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// left border
|
||||
if (x > 0 && (uint)x < width) {
|
||||
for (uint ey = max(y, 0); (int)ey < y + h; ey++) {
|
||||
uint srcidx = (ey * width + x) * comps;
|
||||
uint dstidx = (ey * width + x - 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// top border
|
||||
if (y > 0 && (uint)y < height) {
|
||||
for (uint ex = max(x, 0); (int)ex < x + w; ex++) {
|
||||
uint srcidx = (y * width + ex) * comps;
|
||||
uint dstidx = ((y-1) * width + ex) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// right border
|
||||
if (rx >= 0 && (uint)rx < width-1) {
|
||||
for (uint ey = max(y, 0); (int)ey < y + h; ey++) {
|
||||
uint srcidx = (ey * width + rx) * comps;
|
||||
uint dstidx = (ey * width + rx + 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bottom border
|
||||
if (ry >= 0 && (uint)ry < height-1) {
|
||||
for (uint ex = max(x, 0); (int)ex < x + w; ex++) {
|
||||
uint srcidx = (ry * width + ex) * comps;
|
||||
uint dstidx = ((ry+1) * width + ex) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageData::fixAlphaColor() {
|
||||
ubyte* pixels = static_cast<ubyte*>(data);
|
||||
|
||||
// Fixing black transparent pixels for Mip-Mapping
|
||||
for (int ly = 0; ly < height-1; ly++) {
|
||||
for (int lx = 0; lx < width-1; lx++) {
|
||||
if (pixels[((ly) * width + lx) * 4 + 3]) {
|
||||
for (int c = 0; c < 3; c++) {
|
||||
int val = pixels[((ly) + + lx) * 4 + c];
|
||||
if (pixels[((ly) * width + lx + 1) * 4 + 3] == 0)
|
||||
pixels[((ly) * width + lx + 1) * 4 + c] = val;
|
||||
if (pixels[((ly + 1) * width + lx) * 4 + 3] == 0)
|
||||
pixels[((ly + 1) * width + lx) * 4 + c] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageData* add_atlas_margins(ImageData* image, int grid_size) {
|
||||
// RGBA is only supported
|
||||
assert(image->getFormat() == ImageFormat::rgba8888);
|
||||
@ -95,7 +249,8 @@ ImageData* add_atlas_margins(ImageData* image, int grid_size) {
|
||||
int sy = max(min(ly, imgres-1), 0);
|
||||
int sx = max(min(lx, imgres-1), 0);
|
||||
for (int c = 0; c < 4; c++)
|
||||
dstdata[((doy+ly) * dstwidth + dox + lx) * 4 + c] = srcdata[((soy+sy) * srcwidth + sox + sx) * 4 + c];
|
||||
dstdata[((doy+ly) * dstwidth + dox + lx) * 4 + c] =
|
||||
srcdata[((soy+sy) * srcwidth + sox + sx) * 4 + c];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,12 +14,17 @@ class ImageData {
|
||||
uint height;
|
||||
void* data;
|
||||
public:
|
||||
ImageData(ImageFormat format, uint width, uint height);
|
||||
ImageData(ImageFormat format, uint width, uint height, void* data);
|
||||
~ImageData();
|
||||
|
||||
void flipX();
|
||||
void flipY();
|
||||
|
||||
void blit(const ImageData* image, int x, int y);
|
||||
void extrude(int x, int y, int w, int h);
|
||||
void fixAlphaColor();
|
||||
|
||||
void* getData() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
149
src/maths/LMPacker.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include "LMPacker.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
inline int getPackerScore(rectangle& rect) {
|
||||
if (rect.width * rect.height > 100)
|
||||
return rect.height * rect.height * 1000;
|
||||
return (rect.width * rect.height * rect.height);
|
||||
}
|
||||
|
||||
LMPacker::LMPacker(const uint32_t sizes[], size_t length) {
|
||||
for (unsigned int i = 0; i < length/2; i++) {
|
||||
rectangle rect(0, 0, (int)sizes[i * 2], (int)sizes[i * 2 + 1]);
|
||||
rects.push_back(rect);
|
||||
}
|
||||
sort(rects.begin(), rects.end(), [](rectangle a, rectangle b) {
|
||||
return -getPackerScore(a) < -getPackerScore(b);
|
||||
});
|
||||
}
|
||||
|
||||
LMPacker::~LMPacker() {
|
||||
if (matrix) {
|
||||
for (unsigned int y = 0; y < (height >> mbit); y++) {
|
||||
delete[] matrix[y];
|
||||
}
|
||||
delete[] matrix;
|
||||
}
|
||||
}
|
||||
|
||||
void LMPacker::cleanup() {
|
||||
placed.clear();
|
||||
}
|
||||
|
||||
bool LMPacker::build(uint32_t width, uint32_t height,
|
||||
uint16_t extension, uint32_t mbit, uint32_t vstep) {
|
||||
cleanup();
|
||||
this->mbit = mbit;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
int mpix = 1 << mbit;
|
||||
|
||||
const unsigned int mwidth = width >> mbit;
|
||||
const unsigned int mheight = height >> mbit;
|
||||
matrix = new rectangle**[mheight];
|
||||
for (unsigned int y = 0; y < mheight; y++) {
|
||||
matrix[y] = new rectangle*[mwidth];
|
||||
for (unsigned int x = 0; x < mwidth; x++) {
|
||||
matrix[y][x] = nullptr;
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i < rects.size(); i++) {
|
||||
rectangle& rect = rects[i];
|
||||
rect = rectangle(0, 0, rect.width, rect.height);
|
||||
rect.width += extension * 2;
|
||||
rect.height += extension * 2;
|
||||
if (mpix > 1) {
|
||||
if (rect.width % mpix > 0) {
|
||||
rect.extX = mpix - (rect.width % mpix);
|
||||
}
|
||||
if (rect.height % mpix > 0) {
|
||||
rect.extY = mpix - (rect.height % mpix);
|
||||
}
|
||||
}
|
||||
rect.width += rect.extX;
|
||||
rect.height += rect.extY;
|
||||
}
|
||||
bool built = true;
|
||||
for (unsigned int i = 0; i < rects.size(); i++) {
|
||||
rectangle* rect = &rects[i];
|
||||
if (!place(rect, vstep)) {
|
||||
built = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i < rects.size(); i++) {
|
||||
rectangle& rect = rects[i];
|
||||
rect.x += extension;
|
||||
rect.y += extension;
|
||||
rect.width -= extension * 2 + rect.extX;
|
||||
rect.height -= extension * 2 + rect.extY;
|
||||
}
|
||||
return built;
|
||||
}
|
||||
|
||||
inline rectangle* findCollision(rectangle*** matrix, int x, int y, int w, int h) {
|
||||
for (int row = y; row < y+h; row++) {
|
||||
for (int col = x; col < x+w; col++) {
|
||||
rectangle* rect = matrix[row][col];
|
||||
if (rect) {
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void fill(rectangle*** matrix, rectangle* rect, int x, int y, int w, int h) {
|
||||
for (int row = y; row < y+h; row++) {
|
||||
for (int col = x; col < x+w; col++) {
|
||||
matrix[row][col] = rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LMPacker::place(rectangle* rectptr, uint32_t vstep) {
|
||||
rectangle& rect = *rectptr;
|
||||
const unsigned int rw = rect.width >> mbit;
|
||||
const unsigned int rh = rect.height >> mbit;
|
||||
if (vstep > 1) {
|
||||
vstep = (vstep > rh ? vstep : rh);
|
||||
}
|
||||
const unsigned int mwidth = width >> mbit;
|
||||
const unsigned int mheight = height >> mbit;
|
||||
for (unsigned int y = 0; y + rh < mheight; y += vstep) {
|
||||
rectangle** line = matrix[y];
|
||||
bool skiplines = true;
|
||||
rectangle** lower = matrix[y + rh - 1];
|
||||
for (unsigned int x = 0; x + rw < mwidth; x++) {
|
||||
rectangle* prect = line[x];
|
||||
if (prect) {
|
||||
x = (prect->x >> mbit) + (prect->width >> mbit) - 1;
|
||||
} else {
|
||||
if (skiplines) {
|
||||
unsigned int lfree = 0;
|
||||
while (lfree + x < mwidth && !lower[x + lfree] && lfree < rw) {
|
||||
lfree++;
|
||||
}
|
||||
if (lfree >= rw)
|
||||
skiplines = false;
|
||||
}
|
||||
prect = findCollision(matrix, x, y, rw, rh);
|
||||
if (prect) {
|
||||
x = (prect->x >> mbit) + (prect->width >> mbit) - 1;
|
||||
continue;
|
||||
}
|
||||
fill(matrix, rectptr, x, y, rw, rh);
|
||||
rectptr->x = x << mbit;
|
||||
rectptr->y = y << mbit;
|
||||
placed.push_back(rectptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (skiplines) {
|
||||
y += rh - vstep;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
52
src/maths/LMPacker.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
C++ LMPacker port
|
||||
https://github.com/MihailRis/LMPacker
|
||||
*/
|
||||
#ifndef LMPACKER_H_
|
||||
#define LMPACKER_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
struct rectangle {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
int extX = 0;
|
||||
int extY = 0;
|
||||
|
||||
rectangle(int x, int y, int width, int height)
|
||||
: x(x), y(y), width(width), height(height){
|
||||
}
|
||||
};
|
||||
|
||||
class LMPacker {
|
||||
std::vector<rectangle> rects;
|
||||
std::vector<rectangle*> placed;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
rectangle*** matrix = nullptr;
|
||||
uint32_t mbit = 0;
|
||||
|
||||
void cleanup();
|
||||
bool place(rectangle* rect, uint32_t vstep);
|
||||
public:
|
||||
LMPacker(const uint32_t sizes[], size_t length);
|
||||
virtual ~LMPacker();
|
||||
|
||||
bool buildCompact(uint32_t width, uint32_t height, uint16_t extension) {
|
||||
return build(width, height, extension, 0, 1);
|
||||
}
|
||||
bool buildFast(uint32_t width, uint32_t height, uint16_t extension) {
|
||||
return build(width, height, extension, 1, 2);
|
||||
}
|
||||
bool build(uint32_t width, uint32_t height, uint16_t extension, uint32_t mbit, uint32_t vstep);
|
||||
|
||||
std::vector<rectangle> getResult() {
|
||||
return rects;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* LMPACKER_H_ */
|
||||
@ -13,6 +13,10 @@
|
||||
#include "files/settings_io.h"
|
||||
#include "content/Content.h"
|
||||
|
||||
#include "coders/png.h"
|
||||
#include "graphics/Atlas.h"
|
||||
#include "graphics/ImageData.h"
|
||||
|
||||
int main() {
|
||||
platform::configure_encoding();
|
||||
ContentBuilder contentBuilder;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "Block.h"
|
||||
|
||||
Block::Block(std::string name, int texture) : name(name),
|
||||
Block::Block(std::string name, std::string texture) : name(name),
|
||||
textureFaces{texture,texture,texture,texture,texture,texture},
|
||||
emission{0,0,0}{
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ public:
|
||||
std::string const name;
|
||||
unsigned int id;
|
||||
// 0 1 2 3 4 5
|
||||
int textureFaces[6]; // -x,x, -y,y, -z,z
|
||||
std::string textureFaces[6]; // -x,x, -y,y, -z,z
|
||||
unsigned char emission[3];
|
||||
unsigned char drawGroup = 0;
|
||||
BlockModel model = BlockModel::block;
|
||||
@ -31,7 +31,9 @@ public:
|
||||
bool rotatable = false;
|
||||
float hitboxScale = 1;
|
||||
|
||||
Block(std::string name, int texture);
|
||||
float uvdata[4*6];
|
||||
|
||||
Block(std::string name, std::string texture);
|
||||
};
|
||||
|
||||
#endif /* VOXELS_BLOCK_H_ */
|
||||
|
||||