Blocks atlas is auto-assembling now

This commit is contained in:
MihailRis 2023-11-22 01:53:48 +03:00
parent ea5c117652
commit fb8d220cca
41 changed files with 622 additions and 75 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

BIN
res/textures/blocks/air.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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");

View File

@ -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"

View File

@ -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);
}

View File

@ -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) {

View File

@ -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));
}
}

View File

@ -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*>(&region);
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
View 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
View 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_

View File

@ -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);

View File

@ -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,

View File

@ -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) {

View File

@ -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];
}
}

View File

@ -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
View 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
View 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_ */

View File

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

View File

@ -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}{
}

View File

@ -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_ */