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
+86
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
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_
+20 -12
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);
+3 -2
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,
+1 -6
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) {
+156 -1
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];
}
}
+5
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;
}