Working texture atlas!

(segfault on game close but ignore that)
master
Auri 2021-10-10 16:00:34 -07:00
parent 3eccd8e7e7
commit b7825b84ce
6 changed files with 81 additions and 35 deletions

View File

@ -11,8 +11,7 @@
* @param baseAssets - The relative path to the base texture assets.
*/
LocalSubgame::LocalSubgame(const std::string& baseAssets) :
textures(u16vec2(512)),
LocalSubgame::LocalSubgame(const std::string& baseAssets) : textures(),
lua(std::make_unique<LocalLuaParser>(*this)),
biomes(std::make_unique<LocalBiomeAtlas>()),

View File

@ -51,8 +51,20 @@ AtlasRef TextureAtlas::addBytes(const string& identifier, bool persistent, u16ve
tilesUsed += tileSize.x * tileSize.y;
textures.erase(identifier);
AtlasTexture* raw = new AtlasTexture(*this, identifier, pos, size, data);
AtlasRef ref = sptr<AtlasTexture>(raw, [this](AtlasTexture* tex) { removeTexture(tex); });
AtlasRef ref = sptr<AtlasTexture>(new AtlasTexture(*this, identifier, pos, size, data),
[this](AtlasTexture* tex) { removeTexture(tex); });
if (persistent) persistentTextures.insert(ref);
addTexture(ref);
return ref;
}
sptr<AtlasTexture> TextureAtlas::addCrop(const string& identifier, bool persistent,
u16vec2 offset, u16vec2 size, const sptr<AtlasTexture>& source) {
textures.erase(identifier);
AtlasRef ref = sptr<AtlasTexture>(new AtlasTexture(*this, identifier, offset, size, source),
[this](AtlasTexture* tex) { removeTexture(tex); });
if (persistent) persistentTextures.insert(ref);
addTexture(ref);
@ -64,9 +76,20 @@ AtlasRef TextureAtlas::operator[](const string& identifier) {
if (tex->getIdentifier() != "_missing") return tex;
let data = texBuilder.build(identifier);
let texture = addBytes(identifier, false, data.texture->size, data.texture->getBytes());
// if (data.tintMask) texture->setTintData(*data.tintInd,
// addBytes(identifier + ":tint", false, data.size, *data.tintMask));
let texture = (std::holds_alternative<TextureBuilder::Texture::ByteData>(data.texture->data))
? addBytes(identifier, false, data.texture->size, data.texture->getBytes())
: addCrop(identifier, false, std::get<TextureBuilder::Texture::CropData>(data.texture->data).offset,
data.texture->size, std::get<TextureBuilder::Texture::CropData>(data.texture->data).source);
if (data.tintMask) {
let mask = (std::holds_alternative<TextureBuilder::Texture::ByteData>(data.tintMask->second->data))
? addBytes(identifier + ":tint", false, data.texture->size, data.tintMask->second->getBytes())
: addCrop(identifier + ":tint", false,
std::get<TextureBuilder::Texture::CropData>(data.tintMask->second->data).offset,
data.texture->size, std::get<TextureBuilder::Texture::CropData>(data.tintMask->second->data).source);
texture->setTintData(data.tintMask->first, mask);
}
return texture;
}
@ -126,12 +149,13 @@ void TextureAtlas::addTexture(const AtlasRef& tex) {
let pos = tex->getPos();
let size = tex->getSize();
textures.insert({ tex->getIdentifier(), std::weak_ptr(tex) });
texture.updateTexture(pos.x, pos.y, size.x, size.y, tex->getBytes().data());
if (!tex->isCrop()) texture.updateTexture(pos.x, pos.y, size.x, size.y, tex->getBytes().data());
}
void TextureAtlas::removeTexture(const AtlasTexture* tex) {
std::cout << tex->getIdentifier() << std::endl;
std::cerr << "Removed " << tex->getIdentifier() << std::endl;
textures.erase(tex->getIdentifier());
if (tex->isCrop()) return;
let tilePos = tex->getTilePos();
let tileSize = tex->getTileSize();

View File

@ -19,7 +19,7 @@ class AtlasTexture;
class TextureAtlas {
public:
TextureAtlas() = default;
TextureAtlas(): TextureAtlas(uvec2(2048)) {};
explicit TextureAtlas(uvec2 size);
@ -29,6 +29,9 @@ public:
sptr<AtlasTexture> addBytes(const string& identifier, bool persistent, u16vec2 size, const vec<u8>& data);
sptr<AtlasTexture> addCrop(const string& identifier, bool persistent,
u16vec2 offset, u16vec2 size, const sptr<AtlasTexture>& source);
sptr<AtlasTexture> operator[](const string& identifier);
sptr<AtlasTexture> get(const string& identifier) const;
@ -41,6 +44,7 @@ public:
const Texture& getTexture();
private:
void createMissingTexture();
optional<u16vec2> findAtlasSpace(u16vec2 tileSize);

View File

@ -8,14 +8,13 @@ vec<u8>& TextureBuilder::Texture::getBytes() {
const vec<u8> sourceBytes = cropData.source->getBytes();
const u16 sourceWidth = cropData.source->getSize().x;
std::cout << "cropping " << cropData.source->getIdentifier() << " to " << size << std::endl;
vec<u8> newBytes(size.x * size.y * 4);
for (u32 i = 0; i < size.x * size.y * 4; i++) newBytes[i] =
sourceBytes[(((cropData.offset.x + i / 4) % size.x) +
(cropData.offset.y + i / 4 / size.x) * sourceWidth) * 4 + i % 4];
for (u32 i = 0; i < size.x * size.y * 4; i++) {
newBytes[i] = sourceBytes[(
(cropData.offset.x + ((i / 4) % size.x)) +
(cropData.offset.y + (i / 4 / size.x)) * sourceWidth) * 4 + i % 4];
}
// data = ByteData(sourceBytes);
data = ByteData(newBytes);
return get<ByteData>(data).data;
}
@ -53,22 +52,24 @@ void TextureBuilder::Texture::multiply(Texture& texture) {
for (u32 i = 0; i < size.x * size.y * 4; i++) data[i] *= mul[i];
}
void TextureBuilder::Texture::stack(Texture& texture) {
void TextureBuilder::Texture::stack(Texture& texture, Texture* mask) {
if (!mask) mask = &texture;
let& data = getBytes();
const let& otherData = texture.getBytes();
const let& maskData = mask->getBytes();
for (u32 i = 0; i < size.x * size.y; i++) {
const let& oData = texture.getBytes();
const f32 factor = oData[i * 4 + 3] / 255.f;
data[i * 4] = data[i * 4] * (1 - factor) + oData[i * 4] * factor;
data[i * 4 + 1] = data[i * 4 + 1] * (1 - factor) + oData[i * 4 + 1] * factor;
data[i * 4 + 2] = data[i * 4 + 2] * (1 - factor) + oData[i * 4 + 2] * factor;
data[i * 4 + 3] = std::min(static_cast<u16>(data[i * 4 + 3]) + oData[i * 4 + 3], 255);
const f32 factor = maskData[i * 4 + 3] / 255.f;
data[i * 4] = data[i * 4] * (1 - factor) + otherData[i * 4] * factor;
data[i * 4 + 1] = data[i * 4 + 1] * (1 - factor) + otherData[i * 4 + 1] * factor;
data[i * 4 + 2] = data[i * 4 + 2] * (1 - factor) + otherData[i * 4 + 2] * factor;
data[i * 4 + 3] = std::min(static_cast<u16>(data[i * 4 + 3]) + otherData[i * 4 + 3], 255);
}
}
TextureBuilder::Data TextureBuilder::Data::literal(Context& ctx, string str) {
let tex = ctx.atlas.get(str);
return Data(tex->getSize(), {}, tex);
return Data({}, tex->getSize(), tex);
}
TextureBuilder::Data TextureBuilder::Data::canvas(u16 w, optional<variant<u16, string>> h, optional<string> fill) {
@ -87,6 +88,9 @@ TextureBuilder::Data TextureBuilder::Data::crop(u16 x, u16 y, u16 w, variant<u16
u16 h = std::holds_alternative<u16>(hV) ? std::get<u16>(hV) : w;
Data data = std::holds_alternative<Data>(hV) ? std::get<Data>(hV) : *texO;
if (w + x > data.texture->size.x || h + y > data.texture->size.y)
throw std::runtime_error("Invalid crop region.");
data.texture->crop({ x, y }, { w, h });
return data;
}
@ -111,7 +115,13 @@ TextureBuilder::Data TextureBuilder::Data::stack(Data b, Data s1, optional<Data>
if (s7) stack.emplace_back(&*s7);
if (s8) stack.emplace_back(&*s8);
for (const Data* s : stack) b.texture->stack(*s->texture.get());
for (const Data* s : stack) {
b.texture->stack(*s->texture.get());
if (s->tintMask) {
if (!b.tintMask) b.tintMask = {{ s->tintMask->first, std::make_unique<Texture>(b.texture->size, vec4 {}) }};
b.tintMask->second->stack(*s->tintMask->second.get(), s->texture.get());
}
}
return b;
}

View File

@ -1,7 +1,6 @@
#pragma once
#include "util/StringParser.h"
//#include "game/atlas/TextureAtlas.h"
class TextureAtlas;
class AtlasTexture;
@ -19,6 +18,7 @@ private:
public:
class Texture {
public:
struct ByteData {
ByteData() = default;
ByteData(const vec<u8>& data): data(data) {};
@ -33,7 +33,6 @@ public:
sptr<AtlasTexture> source;
};
public:
void alpha(f32 factor);
void crop(u16vec2 pos, u16vec2 size);
@ -42,7 +41,7 @@ public:
void multiply(Texture& texture);
void stack(Texture& texture);
void stack(Texture& texture, Texture* mask = nullptr);
Texture(u16vec2 size, vec4 color):
size(size), data(ByteData(vec<u8>(size.x * size.y * 4))) {
@ -138,13 +137,18 @@ public:
public:
Data() = default;
Data(const Data& o): texture(std::move(const_cast<Data&>(o).texture)) {};
Data(const Data& o): texture(std::move(const_cast<Data&>(o).texture)),
tintMask(std::move(const_cast<Data&>(o).tintMask)) {};
Data(u16vec2 size, vec4 color): texture(make_unique<Texture>(size, color)) {};
Data(u16vec2 size, sptr<AtlasTexture> tex): texture(make_unique<Texture>(size, tex)) {};
Data(u16vec2 pos, u16vec2 size, sptr<AtlasTexture> tex): texture(make_unique<Texture>(pos, size, tex)) {};
Data& operator=(const Data& o) { texture = std::move(const_cast<Data&>(o).texture); return *this; };
Data& operator=(const Data& o) {
texture = std::move(const_cast<Data&>(o).texture);
tintMask = std::move(const_cast<Data&>(o).tintMask);
return *this;
};
uptr<Texture> texture = {};
optional<std::pair<u32, uptr<Texture>>> tintMask = {};

View File

@ -36,7 +36,10 @@ class AtlasTexture {
/** The image to crop from. */
sptr<AtlasTexture> source;
};
AtlasTexture(TextureAtlas& atlas, const string& identifier, uvec2 pos, const variant<ByteData, CropData>& data):
identifier(identifier), pos(pos), data(data), atlas(atlas) {}
public:
AtlasTexture(const AtlasTexture& o) = delete;
@ -46,9 +49,6 @@ public:
AtlasTexture(TextureAtlas& atlas, const string& identifier, uvec2 pos, uvec2 size, vec<u8> data):
AtlasTexture(atlas, identifier, pos, ByteData { size, data }) {}
AtlasTexture(TextureAtlas& atlas, const string& identifier, uvec2 pos, const variant<ByteData, CropData>& data):
identifier(identifier), pos(pos), data(data), atlas(atlas) {}
/** Returns the texture's identifier. */
inline const string& getIdentifier() const {
@ -95,6 +95,11 @@ public:
/** Sets the tint information of the texture. */
void setTintData(u32 tintInd, const sptr<AtlasTexture>& tintMask);
/** Returns true if this texture is a crop of another texture. */
inline bool isCrop() const {
return std::holds_alternative<CropData>(data);
};
private: