diff --git a/src/game/LocalSubgame.cpp b/src/game/LocalSubgame.cpp index 336f886f..081421ed 100644 --- a/src/game/LocalSubgame.cpp +++ b/src/game/LocalSubgame.cpp @@ -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(*this)), biomes(std::make_unique()), diff --git a/src/game/atlas/TextureAtlas.cpp b/src/game/atlas/TextureAtlas.cpp index 5d60eef0..73049466 100644 --- a/src/game/atlas/TextureAtlas.cpp +++ b/src/game/atlas/TextureAtlas.cpp @@ -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(raw, [this](AtlasTexture* tex) { removeTexture(tex); }); + AtlasRef ref = sptr(new AtlasTexture(*this, identifier, pos, size, data), + [this](AtlasTexture* tex) { removeTexture(tex); }); + if (persistent) persistentTextures.insert(ref); + addTexture(ref); + + return ref; +} + +sptr TextureAtlas::addCrop(const string& identifier, bool persistent, + u16vec2 offset, u16vec2 size, const sptr& source) { + + textures.erase(identifier); + AtlasRef ref = sptr(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(data.texture->data)) + ? addBytes(identifier, false, data.texture->size, data.texture->getBytes()) + : addCrop(identifier, false, std::get(data.texture->data).offset, + data.texture->size, std::get(data.texture->data).source); + + if (data.tintMask) { + let mask = (std::holds_alternative(data.tintMask->second->data)) + ? addBytes(identifier + ":tint", false, data.texture->size, data.tintMask->second->getBytes()) + : addCrop(identifier + ":tint", false, + std::get(data.tintMask->second->data).offset, + data.texture->size, std::get(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(); diff --git a/src/game/atlas/TextureAtlas.h b/src/game/atlas/TextureAtlas.h index 55d5da10..aaf86656 100644 --- a/src/game/atlas/TextureAtlas.h +++ b/src/game/atlas/TextureAtlas.h @@ -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 addBytes(const string& identifier, bool persistent, u16vec2 size, const vec& data); + sptr addCrop(const string& identifier, bool persistent, + u16vec2 offset, u16vec2 size, const sptr& source); + sptr operator[](const string& identifier); sptr get(const string& identifier) const; @@ -41,6 +44,7 @@ public: const Texture& getTexture(); private: + void createMissingTexture(); optional findAtlasSpace(u16vec2 tileSize); diff --git a/src/game/atlas/TextureBuilder.cpp b/src/game/atlas/TextureBuilder.cpp index 4ba2efe2..4da226ff 100644 --- a/src/game/atlas/TextureBuilder.cpp +++ b/src/game/atlas/TextureBuilder.cpp @@ -8,14 +8,13 @@ vec& TextureBuilder::Texture::getBytes() { const vec sourceBytes = cropData.source->getBytes(); const u16 sourceWidth = cropData.source->getSize().x; - std::cout << "cropping " << cropData.source->getIdentifier() << " to " << size << std::endl; - vec 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(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(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(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> h, optional fill) { @@ -87,6 +88,9 @@ TextureBuilder::Data TextureBuilder::Data::crop(u16 x, u16 y, u16 w, variant(hV) ? std::get(hV) : w; Data data = std::holds_alternative(hV) ? std::get(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 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(b.texture->size, vec4 {}) }}; + b.tintMask->second->stack(*s->tintMask->second.get(), s->texture.get()); + } + } return b; } diff --git a/src/game/atlas/TextureBuilder.h b/src/game/atlas/TextureBuilder.h index e245d99d..54a281f5 100644 --- a/src/game/atlas/TextureBuilder.h +++ b/src/game/atlas/TextureBuilder.h @@ -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& data): data(data) {}; @@ -33,7 +33,6 @@ public: sptr 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(size.x * size.y * 4))) { @@ -138,13 +137,18 @@ public: public: Data() = default; - Data(const Data& o): texture(std::move(const_cast(o).texture)) {}; + Data(const Data& o): texture(std::move(const_cast(o).texture)), + tintMask(std::move(const_cast(o).tintMask)) {}; Data(u16vec2 size, vec4 color): texture(make_unique(size, color)) {}; Data(u16vec2 size, sptr tex): texture(make_unique(size, tex)) {}; Data(u16vec2 pos, u16vec2 size, sptr tex): texture(make_unique(pos, size, tex)) {}; - Data& operator=(const Data& o) { texture = std::move(const_cast(o).texture); return *this; }; + Data& operator=(const Data& o) { + texture = std::move(const_cast(o).texture); + tintMask = std::move(const_cast(o).tintMask); + return *this; + }; uptr texture = {}; optional>> tintMask = {}; diff --git a/src/game/atlas/asset/AtlasTexture.h b/src/game/atlas/asset/AtlasTexture.h index 1873b436..b7b5c464 100644 --- a/src/game/atlas/asset/AtlasTexture.h +++ b/src/game/atlas/asset/AtlasTexture.h @@ -36,7 +36,10 @@ class AtlasTexture { /** The image to crop from. */ sptr source; }; - + + AtlasTexture(TextureAtlas& atlas, const string& identifier, uvec2 pos, const variant& 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 data): AtlasTexture(atlas, identifier, pos, ByteData { size, data }) {} - - AtlasTexture(TextureAtlas& atlas, const string& identifier, uvec2 pos, const variant& 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& tintMask); + + /** Returns true if this texture is a crop of another texture. */ + inline bool isCrop() const { + return std::holds_alternative(data); + }; private: