Blocks atlas is auto-assembling now
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
+156
-1
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user