Less intensive texture atlas code, support for in-atlas crops, better structures, biome tinting with other functions :)

Todo: Make the texturebuilder take advantage of the cropping support.
master
Auri 2021-09-30 15:50:45 -07:00
parent f532ee4fe6
commit eb12fa812a
138 changed files with 1020 additions and 908 deletions

View File

@ -12,10 +12,7 @@ Client::Client(ivec2 window) :
std::cout << Log::info << "Starting Zepha Client." << Log::endl;
scene.setScene(make_unique<MainMenuScene>(*this));
while (!renderer.window.shouldClose())
{
loop();
}
while (!renderer.window.shouldClose()) loop();
}
f64 Client::getDelta() {

View File

@ -1,71 +1,65 @@
//
// Created by aurailus on 15/05/19.
//
#include <glm/glm.hpp>
#include "ParticleEntity.h"
#include "game/def/BlockDef.h"
#include "game/atlas/asset/AtlasRef.h"
#include "game/atlas/asset/AtlasTexture.h"
ParticleEntity::ParticleEntity(SubgamePtr game, DimensionPtr dim, glm::vec3 pos, BlockDef& block) :
ParticleEntity::ParticleEntity(SubgamePtr game, DimensionPtr dim, vec3 pos, BlockDef& block) :
DrawableEntity(game, dim, std::make_shared<Model>()), Entity(game, dim) {
setPos(pos + glm::vec3(.5, .3, .5));
setPos(pos + vec3 { .5, .3, .5 });
std::set<std::shared_ptr<AtlasRef>>& textureRefs = block.model.textureRefs;
auto it = textureRefs.cbegin();
advance(it, rand() % textureRefs.size());
let it = block.model.textures.cbegin();
advance(it, rand() % block.model.textures.size());
auto uv = (*it)->uv;
vec4 uv = (*it).getUVPos();
auto spanX = uv.z - uv.x;
auto spanY = uv.w - uv.y;
f32 spanX = uv.z - uv.x;
f32 spanY = uv.w - uv.y;
float offX = spanX / 16 * (rand() % 12);
float offY = spanX / 16 * (rand() % 12);
f32 offX = spanX / 16 * (rand() % 12);
f32 offY = spanX / 16 * (rand() % 12);
uv -= glm::vec4(0, 0, spanX * 0.75, spanY * 0.75);
uv += glm::vec4(offX, offY, offX, offY);
uv -= vec4(0, 0, spanX * 0.75, spanY * 0.75);
uv += vec4(offX, offY, offX, offY);
std::vector<EntityVertex> vertices{
vec<EntityVertex> vertices{
{{ -0.05, -0.05, 0 }, { uv.x, uv.w, 0, 1 }, { 1, 1, 1 }, true, { 0, 0, 1 }, {}, {}},
{{ -0.05, 0.05, 0 }, { uv.x, uv.y, 0, 1 }, { 1, 1, 1 }, true, { 0, 0, 1 }, {}, {}},
{{ 0.05, 0.05, 0 }, { uv.z, uv.y, 0, 1 }, { 1, 1, 1 }, true, { 0, 0, 1 }, {}, {}},
{{ 0.05, -0.05, 0 }, { uv.z, uv.w, 0, 1 }, { 1, 1, 1 }, true, { 0, 0, 1 }, {}, {}},
};
std::vector<unsigned int> indices{
vec<u32> indices{
0, 1, 2, 2, 3, 0,
0, 2, 1, 2, 0, 3,
};
auto dir = glm::radians(static_cast<float>(rand() % 360));
float xDir = sinf(dir);
float zDir = cosf(dir);
f32 dir = glm::radians(static_cast<f32>(rand() % 360));
f32 xDir = sinf(dir);
f32 zDir = cosf(dir);
float horiScale = 0.5f + (rand() % 100 / 100.f);
f32 horiScale = 0.5f + (rand() % 100 / 100.f);
velocity.y = 6.f;
velocity.x = xDir * 2.f * horiScale;
velocity.z = zDir * 2.f * horiScale;
vel.y = 6.f;
vel.x = xDir * 2.f * horiScale;
vel.z = zDir * 2.f * horiScale;
setPos(getPos() + glm::vec3(xDir, 0, zDir) / 4.f);
setPos(getPos() + vec3(xDir, 0, zDir) / 4.f);
std::unique_ptr<EntityMesh> mesh = std::make_unique<EntityMesh>();
uptr<EntityMesh> mesh = make_unique<EntityMesh>();
mesh->create(vertices, indices);
this->model->fromMesh(std::move(mesh));
this->setScale(0.75f + (rand() % 10 / 20.f));
model->fromMesh(std::move(mesh));
setScale(0.75f + (rand() % 10 / 20.f));
}
void ParticleEntity::update(double delta, glm::vec3 player) {
void ParticleEntity::update(f64 delta, vec3 player) {
time += delta;
setPos(getPos() + velocity * glm::vec3(static_cast<float>(delta)));
setPos(getPos() + vel * glm::vec3(static_cast<f32>(delta)));
double angle = -glm::degrees(std::atan2(player.z - pos.z, player.x - pos.x)) + 90.f;
setRotateY(static_cast<float>(angle));
f32 angle = -glm::degrees(std::atan2(player.z - pos.z, player.x - pos.x)) + 90.f;
setRotateY(angle);
velocity.y -= 32.f * delta;
vel.y -= 32.f * delta;
}
void ParticleEntity::draw(Renderer& renderer) {

View File

@ -1,23 +1,21 @@
//
// Created by aurailus on 15/05/19.
//
#pragma once
#include "util/Types.h"
#include "world/dim/ent/DrawableEntity.h"
class BlockDef;
class ParticleEntity : public DrawableEntity {
public:
ParticleEntity(SubgamePtr game, DimensionPtr dim, glm::vec3 pos, BlockDef& block);
public:
ParticleEntity(SubgamePtr game, DimensionPtr dim, vec3 pos, BlockDef& block);
void update(double delta, glm::vec3 player);
void update(f64 delta, vec3 player);
void draw(Renderer& renderer) override;
float time = 0;
private:
glm::vec3 velocity{};
f32 time = 0;
private:
vec3 vel;
};

View File

@ -1,71 +1,57 @@
//
// Created by aurailus on 13/08/19.
//
#include <cmath>
#include "Font.h"
#include "game/atlas/asset/AtlasRef.h"
#include "game/atlas/TextureAtlas.h"
#include "game/atlas/asset/AtlasTexture.h"
Font::Font(TextureAtlas& atlas, std::shared_ptr<AtlasRef> tex) :
fontTex(std::move(tex)),
atlasSize(atlas.canvasSize) {
getCharWidths(atlas);
}
u16 Font::getCharWidth(char c) {
unsigned int index = static_cast<unsigned int>(c) - 32;
if (index >= C_COUNT) return charWidths[C_COUNT];
return charWidths[index];
}
void Font::getCharWidths(TextureAtlas& atlas) {
auto& data = atlas.atlasData;
Font::Font(const AtlasTexture& texture) : texture(texture) {
charWidths[0] = 2;
const let data = texture.getBytes();
for (u16 i = 1; i < C_COUNT + 1; i++) {
glm::vec2 charPos = { i % 18 * C_WIDTH, std::floor(i / 18) * C_HEIGHT };
u32 xBase = static_cast<u32>(fontTex->rawPos.x) + static_cast<u32>(charPos.x);
u32 yBase = static_cast<u32>(fontTex->rawPos.y) + static_cast<u32>(charPos.y);
u16vec2 charPos(i % C_ROWSIZE * C_WIDTH, i / C_ROWSIZE * C_HEIGHT);
u16 width = 0;
for (u16 j = 0; j < C_WIDTH; j++) {
bool empty = true;
for (u16 k = 0; k < C_HEIGHT; k++) {
u32 xx = xBase + j;
u32 yy = yBase + k;
u32 xx = charPos.x + j;
u32 yy = charPos.y + k;
u32 offset = yy * static_cast<u32>(atlasSize.x) * 4 + xx * 4 + 3;
u32 offset = yy * texture.getSize().x * 4 + xx * 4 + 3;
if (data[offset] != 0) {
empty = false;
break;
}
}
if (!empty) width = static_cast<u16>(j);
if (!empty) {
width = j;
}
}
charWidths[i] = width;
}
}
u16 Font::getCharWidth(char c) {
u16 index = static_cast<u16>(c) - 32;
if (index >= C_COUNT) return charWidths[C_COUNT];
return charWidths[index];
}
vec4 Font::getCharUVs(char c) {
u16 index = static_cast<u16>(c) - 32;
if (index >= C_COUNT) index = C_COUNT;
vec2 charPos = { (index % 18) * C_WIDTH, std::floor(index / 18) * C_HEIGHT };
vec4 uv = {
fontTex->uv.x + (charPos.x) / atlasSize.x,
fontTex->uv.y + (charPos.y) / atlasSize.y,
fontTex->uv.x + (charPos.x + getCharWidth(c) + 1) / atlasSize.x,
fontTex->uv.y + (charPos.y + C_HEIGHT) / atlasSize.y
};
vec4 texUVs = texture.getUVPos();
u16vec2 texSize = texture.getSize();
u16vec2 charPos((index % C_ROWSIZE) * C_WIDTH, index / C_ROWSIZE * C_HEIGHT);
return uv;
return {
texUVs.x + charPos.x * (texUVs.z - texUVs.x) / texSize.x,
texUVs.y + charPos.y * (texUVs.w - texUVs.y) / texSize.y,
texUVs.x + (charPos.x + getCharWidth(c) + 1) * (texUVs.z - texUVs.x) / texSize.x,
texUVs.y + (charPos.y + C_HEIGHT) * (texUVs.w - texUVs.y) / texSize.y
};
}

View File

@ -1,29 +1,26 @@
#pragma once
#include "util/Types.h"
#include "game/atlas/asset/AtlasTexture.h"
class AtlasRef;
class TextureAtlas;
/**
* Stores information about a font,
* like the texture, UVs and widths of characters.
*/
class Font {
public:
Font() = default;
Font(TextureAtlas& atlas, std::shared_ptr<AtlasRef> tex);
Font(const AtlasTexture& texture);
u16 getCharWidth(char c);
vec4 getCharUVs(char c);
constexpr static u16 C_ROWSIZE = 18;
constexpr static u16 C_COUNT = 95;
constexpr static u16 C_WIDTH = 7;
constexpr static u16 C_HEIGHT = 9;
private:
void getCharWidths(TextureAtlas& atlas);
vec2 atlasSize {};
sptr<AtlasRef> fontTex = nullptr;
AtlasTexture texture;
array<u16, C_COUNT + 1> charWidths {};
};

View File

@ -13,7 +13,7 @@
#include "util/Log.h"
#include "util/Mat4Conv.h"
#include "game/atlas/asset/AtlasRef.h"
#include "game/atlas/asset/AtlasTexture.h"
#include "game/atlas/asset/SerializedModel.h"
void Model::fromMesh(std::unique_ptr<EntityMesh> mesh) {
@ -21,7 +21,7 @@ void Model::fromMesh(std::unique_ptr<EntityMesh> mesh) {
meshes.push_back(std::move(mesh));
}
int Model::fromFile(const std::string& path, const std::vector<std::shared_ptr<AtlasRef>>& textures) {
int Model::fromFile(const std::string& path, const std::vector<AtlasTexture>& textures) {
this->textures = textures;
Assimp::Importer importer;
@ -42,7 +42,7 @@ int Model::fromFile(const std::string& path, const std::vector<std::shared_ptr<A
return 0;
}
int Model::fromSerialized(const SerializedModel& model, const std::vector<std::shared_ptr<AtlasRef>>& textures) {
int Model::fromSerialized(const SerializedModel& model, const std::vector<AtlasTexture>& textures) {
this->textures = textures;
Assimp::Importer importer;
@ -111,10 +111,11 @@ void Model::loadMeshAndBone(aiMesh* mesh, std::unique_ptr<EntityMesh>& target) {
auto& texture = textures[mesh->mMaterialIndex];
//Set texture coordinates
vec4 uvs = texture.getUVPos();
vertex.useTex = true;
vertex.colorData = {
texture->uv.x + mesh->mTextureCoords[0][i].x * (texture->uv.z - texture->uv.x),
texture->uv.y + mesh->mTextureCoords[0][i].y * (texture->uv.w - texture->uv.y), 0, 1 //Alpha
uvs.x + mesh->mTextureCoords[0][i].x * (uvs.z - uvs.x),
uvs.y + mesh->mTextureCoords[0][i].y * (uvs.w - uvs.y), 0, 1 //Alpha
};
}
else vertex.colorData = { 1, 1, 1, 1 };

View File

@ -14,20 +14,19 @@
#include "ModelBone.h"
#include "ModelAnimation.h"
#include "client/graph/mesh/EntityMesh.h"
class AtlasRef;
#include "game/atlas/asset/AtlasTexture.h"
class SerializedModel;
class Model {
public:
public:
Model() = default;
void fromMesh(std::unique_ptr<EntityMesh> mesh);
int fromFile(const std::string& path, const std::vector<std::shared_ptr<AtlasRef>>& texture);
int fromFile(const std::string& path, const std::vector<AtlasTexture>& texture);
int fromSerialized(const SerializedModel& model, const std::vector<std::shared_ptr<AtlasRef>>& texture);
int fromSerialized(const SerializedModel& model, const std::vector<AtlasTexture>& texture);
void getTransformsByFrame(double frame, glm::ivec2 bounds, std::vector<glm::mat4>& transforms);
// void getTransformsByTime(double time, std::tuple<uint> bounds, std::vector<glm::mat4>& transforms);
@ -77,7 +76,7 @@ private:
ModelAnimation animation {};
std::vector<ModelBone*> rootBones {};
std::vector<ModelBone> bones {};
std::vector<std::shared_ptr<AtlasRef>> textures {};
std::vector<AtlasTexture> textures {};
glm::mat4 globalInverseTransform {};
};

View File

@ -10,7 +10,7 @@
Renderer::Renderer() : Renderer({ 1366, 768 }) {};
Renderer::Renderer(glm::ivec2 win) :
activeTexture(nullptr),
// activeTexture(nullptr),
window(win),
world(win, 2),
@ -62,7 +62,7 @@ void Renderer::update(double delta) {
}
void Renderer::beginChunkDeferredCalls() {
activeTexture = nullptr;
// activeTexture = nullptr;
currentModelUniform = world.uniforms.model;
glClearColor(0, 0, 0, 0);
@ -105,7 +105,7 @@ void Renderer::beginEntityDeferredCalls() {
void Renderer::endDeferredCalls() {
glDisable(GL_BLEND);
activeTexture = nullptr;
// activeTexture = nullptr;
glBindFramebuffer(GL_FRAMEBUFFER, ssao.fbo);
glClearColor(clearColor.x, clearColor.y, clearColor.z, 0);
@ -163,7 +163,7 @@ void Renderer::endDeferredCalls() {
}
void Renderer::beginGUIDrawCalls() {
activeTexture = nullptr;
// activeTexture = nullptr;
currentModelUniform = gu.model;
glClear(GL_DEPTH_BUFFER_BIT);
@ -211,11 +211,11 @@ void Renderer::setClipBounds(glm::vec4 bounds) {
guiShader.set(gu.clipBounds, { bounds.x, window.getSize().y - bounds.w, bounds.z, window.getSize().y - bounds.y });
}
void Renderer::enableTexture(Texture* texture) {
if (texture != activeTexture) {
activeTexture = texture;
texture->use(0);
}
void Renderer::enableTexture(const Texture& texture) {
// if (texture != *activeTexture) {
// activeTexture = texture;
texture.use(0);
// }
}
void Renderer::renderQuad() {

View File

@ -48,7 +48,7 @@ public:
void setClipBounds(glm::vec4 bounds);
void enableTexture(Texture* texture);
void enableTexture(const Texture& texture);
Window window;
Camera camera;
@ -60,7 +60,7 @@ private:
unsigned int quadVBO = 0;
glm::vec4 clearColor{ 0, 0, 0, 1 };
Texture* activeTexture;
// Texture* activeTexture;
WorldGeometryShader world;
EntityGeometryShader entity;

View File

@ -12,6 +12,10 @@ Texture::Texture(const std::string& file) :
loadFromFile(this->fileLocation);
}
bool Texture::operator==(const Texture& o) const {
return o.textureID == textureID;
}
void Texture::loadFromFile(std::string file) {
unsigned char* texData = stbi_load(file.c_str(), &width, &height, &bitDepth, 0);
if (!texData) throw std::runtime_error("Failed to find texture at " + file + ".");
@ -46,7 +50,7 @@ void Texture::updateTexture(int x, int y, int width, int height, unsigned char*
}
void Texture::use(GLuint position) {
void Texture::use(GLuint position) const {
glActiveTexture(GL_TEXTURE0 + position);
glBindTexture(GL_TEXTURE_2D, textureID);
}

View File

@ -8,11 +8,13 @@
#include "util/GL.h"
class Texture {
public:
public:
Texture() = default;
explicit Texture(const std::string& file);
bool operator==(const Texture& o) const;
void loadFromFile(std::string file);
void loadFromBytes(unsigned char* bytes, int width, int height, GLint interpolation = GL_NEAREST,
@ -20,13 +22,13 @@ class Texture {
void updateTexture(int x, int y, int width, int height, unsigned char* bytes);
void use(GLuint position = 0);
void use(GLuint position = 0) const;
void clear();
~Texture();
protected:
protected:
unsigned int textureID = 0;
int width = 0;
int height = 0;

View File

@ -27,7 +27,7 @@ void Gui::BoxElement::updateElement() {
else if (bgColor) {
let mesh = make_unique<EntityMesh>();
tex = nullptr;
tex = {};
mesh->create({
{ { 0, 0, 0 }, *bgColor, vec3(1), false, {}, {}, {} },
{ { 0, 1, 0 }, *bgColor, vec3(1), false, {}, {}, {} },
@ -43,11 +43,12 @@ void Gui::BoxElement::updateElement() {
let mesh = make_unique<EntityMesh>();
tex = root.atlas[bgImage];
let uv = tex->getUVPos();
mesh->create({
{ { 0, 0, 0 }, { tex->uv.x, tex->uv.y, 0, 1 }, vec3(1), true, {}, {}, {} },
{ { 0, 1, 0 }, { tex->uv.x, tex->uv.w, 0, 1 }, vec3(1), true, {}, {}, {} },
{ { 1, 1, 0 }, { tex->uv.z, tex->uv.w, 0, 1 }, vec3(1), true, {}, {}, {} },
{ { 1, 0, 0 }, { tex->uv.z, tex->uv.y, 0, 1 }, vec3(1), true, {}, {}, {} }
{ { 0, 0, 0 }, { uv.x, uv.y, 0, 1 }, vec3(1), true, {}, {}, {} },
{ { 0, 1, 0 }, { uv.x, uv.w, 0, 1 }, vec3(1), true, {}, {}, {} },
{ { 1, 1, 0 }, { uv.z, uv.w, 0, 1 }, vec3(1), true, {}, {}, {} },
{ { 1, 0, 0 }, { uv.z, uv.y, 0, 1 }, vec3(1), true, {}, {}, {} }
}, { 0, 1, 2, 2, 3, 0 });
let model = make_shared<Model>();

View File

@ -2,7 +2,7 @@
#include "Element.h"
#include "game/atlas/asset/AtlasRef.h"
#include "game/atlas/asset/AtlasTexture.h"
namespace Gui {
@ -17,7 +17,7 @@ namespace Gui {
virtual void updateElement() override;
protected:
sptr<AtlasRef> tex;
optional<any> curBg;
optional<AtlasTexture> tex;
};
}

View File

@ -140,9 +140,9 @@ void DebugDisplay::update(sptr<LocalPlayer> player, f64 delta, u32 interpolatedC
<< "R: " << posOffsetFromRegion << " [" << regionPos << "]" << std::endl
<< std::endl
<< "Texture Slots: " << game.l()->textures.textureSlotsUsed << " / " << game.l()->textures.maxTextureSlots
<< "Texture Slots: " << game.l()->textures.getTilesUsed() << " / " << game.l()->textures.getTilesTotal()
<< " ("
<< round(game.l()->textures.textureSlotsUsed / static_cast<float>(game.l()->textures.maxTextureSlots) * 100)
<< round(game.l()->textures.getTilesUsed() / static_cast<f32>(game.l()->textures.getTilesTotal()) * 100)
<< "%)" << std::endl << std::endl
<< "Biome: " << onBiomeDef.identifier << " [" << onBiomeDef.index << "]" << std::endl << std::endl;

View File

@ -9,7 +9,7 @@
void Gui::TextElement::updateElement() {
const string text = getStyle<string>(Prop::CONTENT, "");
if (!font) font = std::make_unique<Font>(root.atlas, root.atlas["font"]);
if (!font) font = make_unique<Font>(root.atlas["font"]);
usize newHash = std::hash<string>{}(text);
if (hash != newHash) {

View File

@ -54,8 +54,8 @@ void MenuSandbox::load(const SubgameDef& subgame) {
subgameName = subgame.config.name;
try {
loadMod(subgame.subgamePath + "/../../assets/base");
loadMod(subgame.subgamePath + "/menu");
loadMod(subgame.subgamePath / "../../assets/base");
loadMod(subgame.subgamePath / "menu");
}
catch (sol::error e) {
string err = static_cast<sol::error>(e).what();
@ -132,7 +132,7 @@ void MenuSandbox::loadMod(const std::filesystem::path& path) {
mod = { path, true };
if (std::filesystem::exists(path / "textures"))
menuAssets = client.game->textures.loadDirectory((path / "textures").string(), false);
menuAssets = client.game->textures.addDirectory((path / "textures"), false);
loadFile("/main");
}

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 2019-12-12.
//
#pragma once
#include "lua/LuaParser.h"
@ -9,10 +5,10 @@
#include "lua/Mod.h"
#include "client/gui/Root.h"
#include "client/gui/Element.h"
#include "game/atlas/asset/AtlasTexture.h"
class Client;
class Subgame;
class AtlasRef;
class SubgameDef;
class GuiContainer;
@ -48,7 +44,7 @@ private:
Gui::Root& root;
sptr<Gui::Element> sandboxRoot;
vec<sptr<AtlasRef>> menuAssets {};
vec<AtlasTexture> menuAssets {};
double accumulatedDelta = 0;

View File

@ -1,18 +1,11 @@
//
// Created by aurailus on 2019-12-11.
//
#pragma once
#include <memory>
#include "SubgameConfig.h"
class AtlasRef;
#include "client/menu/SubgameConfig.h"
#include "game/atlas/asset/AtlasTexture.h"
class SubgameDef {
public:
std::shared_ptr<AtlasRef> iconRef = nullptr;
SubgameConfig config{};
std::string subgamePath = "";
public:
AtlasTexture iconRef;
SubgameConfig config {};
std::filesystem::path subgamePath;
};

View File

@ -136,7 +136,7 @@ void ConnectScene::update() {
const u8* dataPtr = reinterpret_cast<u8*>(const_cast<char*>(uncompressed.data()));
const vec<u8> tex(dataPtr, dataPtr + width * height * 4);
client.game->textures.addImage(assetName, true, u16vec2(width, height), tex);
client.game->textures.addBytes(assetName, true, u16vec2(width, height), tex);
}
else if (t == AssetType::MODEL) {
string format = p.d.read<string>();
@ -210,7 +210,7 @@ void ConnectScene::draw() {
renderer.beginChunkDeferredCalls();
renderer.endDeferredCalls();
renderer.beginGUIDrawCalls();
renderer.enableTexture(&client.game->textures.texture);
renderer.enableTexture(client.game->textures.getTexture());
root.draw(renderer);
}

View File

@ -46,19 +46,19 @@ void GameScene::draw() {
Camera& camera = renderer.camera;
perf.start("draw:world");
renderer.enableTexture(client.game->textures.getTexture());
renderer.beginChunkDeferredCalls();
renderer.enableTexture(&client.game->textures.texture);
world.l()->drawChunks();
perf.start("draw:entities");
renderer.enableTexture(client.game->textures.getTexture());
renderer.beginEntityDeferredCalls();
renderer.enableTexture(&client.game->textures.texture);
world.l()->drawEntities();
renderer.endDeferredCalls();
perf.start("draw:interface");
renderer.enableTexture(client.game->textures.getTexture());
renderer.beginGUIDrawCalls();
renderer.enableTexture(&client.game->textures.texture);
world.l()->drawInterface();
perf.start("idle");

View File

@ -36,7 +36,7 @@ void LuaErrorScene::draw() {
renderer.beginChunkDeferredCalls();
renderer.endDeferredCalls();
renderer.beginGUIDrawCalls();
renderer.enableTexture(&client.game->textures.texture);
renderer.enableTexture(client.game->textures.getTexture());
root.draw(renderer);
}

View File

@ -11,7 +11,7 @@
#include "client/gui/TextElement.h"
#include "client/menu/SubgameDef.h"
#include "client/gui/basic/GuiText.h"
#include "game/atlas/asset/AtlasRef.h"
#include "game/atlas/asset/AtlasTexture.h"
#include "client/gui/basic/GuiContainer.h"
#include "client/gui/compound/GuiImageButton.h"
@ -104,11 +104,11 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
if (!json["version"].is_string() || json["version"] == "")
throw std::runtime_error("conf.json is missing 'version'");
const let icon = std::filesystem::exists(file.path() / "icon.png")
? client.game->textures.loadImage((file.path() / "icon.png").string(), json["name"])
const AtlasTexture icon = std::filesystem::exists(file.path() / "icon.png")
? client.game->textures.addFile((file.path() / "icon.png").string(), false)
: client.game->textures["menu_flag_missing"];
subgames.push_back({ icon, { json["name"], json["description"], json["version"] }, file.path().string() });
subgames.push_back({ icon, { json["name"], json["description"], json["version"] }, file.path() });
}
catch(const std::runtime_error& e) {
std::cout << Log::err << "Failed to load subgame '"
@ -124,8 +124,8 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
let elem = navigationList->append<Gui::BoxElement>({{
{ Gui::Prop::CLASS, vec<string> { "navigationButton" } },
{ Gui::Prop::BACKGROUND, string("crop(0, 0, 16, 16, " + subgame.iconRef->name + ")") },
{ Gui::Prop::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, " + subgame.iconRef->name + ")") }
{ Gui::Prop::BACKGROUND, string("crop(0, 0, 16, 16, " + subgame.iconRef.getIdentifier() + ")") },
{ Gui::Prop::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, " + subgame.iconRef.getIdentifier() + ")") }
}});
elem->onClick([&](u32 button, bool down) {
@ -171,7 +171,7 @@ void MainMenuScene::draw() {
renderer.beginChunkDeferredCalls();
renderer.endDeferredCalls();
renderer.enableTexture(client.game->textures.getTexture());
renderer.beginGUIDrawCalls();
renderer.enableTexture(&client.game->textures.texture);
root.draw(renderer);
}

View File

@ -87,7 +87,8 @@ void MeshGenStream::Thread::exec() {
assert(u.thisChunk);
for (int i = 0; i < u.adjacentChunks.size(); i++) assert(u.adjacentChunks[i]);
ChunkMeshGenerator m(u.meshDetails, game.getDefs(), game.getBiomes(),
std::move(u.thisChunk), std::move(u.adjacentChunks), offsetSamplers, ChunkMeshGenerator::Detail::HIGH);
std::move(u.thisChunk), std::move(u.adjacentChunks), offsetSamplers,
glm::distance(vec3(u.thisChunk->getPos()), vec3()) <= 16 ? ChunkMeshGenerator::Detail::HIGH : ChunkMeshGenerator::Detail::LOW);
empty = false;
u.busy = false;
}

View File

@ -12,13 +12,13 @@
*/
LocalSubgame::LocalSubgame(const std::string& baseAssets) :
textures(u16vec2(2048)),
textures(u16vec2(512)),
lua(std::make_unique<LocalLuaParser>(*this)),
biomes(std::make_unique<LocalBiomeAtlas>()),
defs(std::make_unique<LocalDefinitionAtlas>(textures)) {
textures.loadDirectory(baseAssets);
textures.addDirectory(baseAssets, true);
}

View File

@ -9,7 +9,7 @@
LocalDefinitionAtlas::LocalDefinitionAtlas(TextureAtlas& atlas) {
//Invalid Node
BlockModel invalidModel = BlockModel::createCube({ atlas["_missing"] }, {}, {});
BlockModel invalidModel = BlockModel({{ atlas["_missing"], {}, {} }});
BlockDef* invalid = new BlockDef();
invalid->identifier = "invalid";
invalid->name = "INVALID";
@ -22,7 +22,7 @@ LocalDefinitionAtlas::LocalDefinitionAtlas(TextureAtlas& atlas) {
defTable.insert({ "invalid", 0 });
//Air Node
BlockModel nullModel{};
BlockModel nullModel {};
BlockDef* air = new BlockDef();
air->lightPropagates = true;
air->identifier = "air";

View File

@ -8,7 +8,7 @@
ServerDefinitionAtlas::ServerDefinitionAtlas() {
//Invalid Node
BlockModel invalidModel = BlockModel::createCube({}, {}, {});
BlockModel invalidModel = BlockModel {};
BlockDef* invalid = new BlockDef();
invalid->identifier = "invalid";
invalid->name = "INVALID";
@ -20,7 +20,7 @@ ServerDefinitionAtlas::ServerDefinitionAtlas() {
registerDef(invalid);
//Air Node
BlockModel nullModel{};
BlockModel nullModel {};
BlockDef* air = new BlockDef();
air->lightPropagates = true;
air->identifier = "air";

View File

@ -1,144 +1,19 @@
#include <algorithm>
#include <filesystem>
#include <stb_image.h>
#include "TextureAtlas.h"
#include "util/Log.h"
#include "util/Util.h"
#include "game/atlas/asset/AtlasRef.h"
#include "game/atlas/asset/AtlasTexture.h"
TextureAtlas::TextureAtlas(uvec2 size) :
canvasSize(size),
canvasTileSize(size / 16u),
atlasData(size.x * 4 * size.y),
maxTextureSlots(canvasTileSize.x * canvasTileSize.y),
empty(canvasTileSize.x * canvasTileSize.y, true) {
/**
* Rect Function.
* Creates a rectangular image with the color specified.
*
* @param w - The width of the rect.
* @param h - The height of the rect.
* @param fill - The color to fill the rectangle with.
*/
parser.addFn<u16, u16, string>("rect", [](u16 w, u16 h, string hex) {
vec4 color = Util::hexToColorVec(hex);
vec<u8> newData(w * h * 4);
for (u32 i = 0; i < w * h * 4; i++) newData[i] = color[i % 4] * 255;
return TexParserData { std::make_shared<RawTexData>(newData, uvec2 { w, h }) };
});
/**
* Alpha Function.
* Adjusts the opacity of the provided image by a factor.
*
* @param factor - The factor that the opacity should be scaled by. 0 = transparent, 1 = opaque.
* @param tex - The texture to scale the alpha of.
*/
parser.addFn<f32, TexParserData>("alpha", [](f32 factor, TexParserData tex) {
for (u32 i = 0; i < tex.data->size.x * tex.data->size.y; i++)
tex.data->data[i * 4 + 3] = (tex.data->data[i * 4 + 3] / 255.f * factor) * 255;
return tex;
});
/**
* Crop Function.
* Crops the texture to the specified dimensions and position.
*
* @param x - The left edge of the crop on the base texture.
* @param y - The top edge of the crop on the base texture.
* @param w - The width of the crop.
* @param h - The height of the crop.
* @param tex - The texture to crop.
*/
parser.addFn<u16, u16, u16, u16, TexParserData>("crop", [](u16 x, u16 y, u16 w, u16 h, TexParserData tex) {
let newData = vec<u8>(w * h * 4);
for (u32 i = 0; i < w * h * 4; i++) newData[i] =
tex.data->data[((x + i / 4 % w) + (y + i / 4 / w) * tex.data->size.x) * 4 + i % 4];
tex.data->data = newData;
tex.data->size = { w, h };
return tex;
});
/**
* Multiply Function.
* Multiplies the texture by the specified color.
*
* @param hex - The hex code of the color to multiply the image by.
* @param tex - The texture to multiply.
*/
parser.addFn<string, TexParserData>("multiply", [](string hex, TexParserData tex) {
vec4 multiple = Util::hexToColorVec(hex);
for (u32 i = 0; i < tex.data->size.x * tex.data->size.y * 4; i++)
tex.data->data[i] *= multiple[i % 4];
return tex;
});
/**
* Stack function. ()
* Stacks the provided textures on top of each other, blending partially opaque pixels.
* Requires at least two textures. The maximum is currently eight textures,
* however that could be removed later with some tweaks to StringParser.
*
* @param b - The base texture to stack onto.
* @param s1 - A texture to stack on top of the base texture.
* @param s2 - Another texture to stack on top of s1.
* @param s3 - ...
*/
parser.addFn<TexParserData, TexParserData, optional<TexParserData>,
optional<TexParserData>, optional<TexParserData>, optional<TexParserData>,
optional<TexParserData>, optional<TexParserData>, optional<TexParserData>>("", [](
TexParserData b, TexParserData s1, optional<TexParserData> s2,
optional<TexParserData> s3, optional<TexParserData> s4, optional<TexParserData> s5,
optional<TexParserData> s6, optional<TexParserData> s7, optional<TexParserData> s8) {
vec<TexParserData> stack;
stack.reserve(8);
stack.emplace_back(s1);
if (s2) stack.emplace_back(*s2);
if (s3) stack.emplace_back(*s3);
if (s4) stack.emplace_back(*s4);
if (s5) stack.emplace_back(*s5);
if (s6) stack.emplace_back(*s6);
if (s7) stack.emplace_back(*s7);
if (s8) stack.emplace_back(*s8);
for (const let& s : stack) {
for (u32 i = 0; i < b.data->size.x * b.data->size.y; i++) {
const f32 factor = s.data->data[i * 4 + 3] / 255.f;
b.data->data[i * 4] = b.data->data[i * 4] * (1 - factor) + s.data->data[i * 4] * factor;
b.data->data[i * 4 + 1] = b.data->data[i * 4 + 1] * (1 - factor) + s.data->data[i * 4 + 1] * factor;
b.data->data[i * 4 + 2] = b.data->data[i * 4 + 2] * (1 - factor) + s.data->data[i * 4 + 2] * factor;
b.data->data[i * 4 + 3] = std::min((u16)(b.data->data[i * 4 + 3]) + s.data->data[i * 4 + 3], 255);
}
}
return b;
});
/**
* Literal Function.
* Converts raw strings to texture data by indexing the atlas.
*
* @param tex - The string of the texture to convert.
*/
parser.addLiteralFnCtx([](TexParser::Ctx& ctx, string tex) {
return TexParser::Data { std::make_shared<RawTexData>(ctx.atlas.getBytesOfTex(tex)) };
});
tilesTotal(canvasTileSize.x * canvasTileSize.y),
tiles(canvasTileSize.x * canvasTileSize.y, false) {
// Get GPU texture capabilites and log it.
i32 maxTexSize, texUnits;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
@ -148,158 +23,98 @@ TextureAtlas::TextureAtlas(uvec2 size) :
std::cout << Log::info << "This GPU supports " << texUnits << " texture units." << Log::endl;
// Initialize the texture atlas.
texture.loadFromBytes(&atlasData[0], size.x, size.y);
texture.loadFromBytes(&vec<u8>(size.x * size.y * 4)[0], size.x, size.y);
createMissingTexture();
}
vec<sptr<AtlasRef>> TextureAtlas::loadDirectory(const string& path, bool base) {
vec<sptr<AtlasRef>> refs {};
void TextureAtlas::update() {
if (texturesUnused.empty()) return;
std::cout << texturesUnused.size() << std::endl;
for (let& identifier : texturesUnused) {
let& texture = textures.at(identifier);
if (texture.shouldBeRemoved()) {
std::cout << "removing " << identifier << std::endl;
removeTexture(texture);
}
}
texturesUnused.clear();
}
vec<AtlasTexture> TextureAtlas::addDirectory(const std::filesystem::path& path, bool base) {
vec<AtlasTexture> refs {};
for (let file : std::filesystem::recursive_directory_iterator(path))
if (file.path().extension() == ".png") refs.push_back(
loadImage(file.path().string(), file.path().stem().string(), base));
if (file.path().extension() == ".png") refs.push_back(addFile(file.path(), base));
return refs;
}
sptr<AtlasRef> TextureAtlas::loadImage(const string& path, const string& name, bool base) {
AtlasTexture TextureAtlas::addFile(const std::filesystem::path& path, bool base) {
i32 width, height;
u8* rawData = stbi_load(path.data(), &width, &height, nullptr, 4);
u8* rawData = stbi_load(path.string().data(), &width, &height, nullptr, 4);
vec<u8> data(rawData, rawData + width * height * 4);
free(rawData);
let ref = addImage(name, base, u16vec2(width, height), data);
let ref = addBytes(path.stem().string(), base, u16vec2(width, height), data);
return ref;
}
void TextureAtlas::update() {
for (let it = textures.begin(); it != textures.end();) {
if (!it->second->base && it->second.unique()) {
deleteImage(it->second);
it = textures.erase(it);
}
else it++;
}
}
vec4 TextureAtlas::getPixel(const sptr<AtlasRef>& ref, ivec2 pos) {
uvec2 absPos = { ref->rawPos.x + pos.x, ref->rawPos.y + pos.y };
u32 index = (static_cast<u32>(absPos.y) * canvasSize.x + static_cast<u32>(absPos.x)) * 4;
return { atlasData[index] / 255.f, atlasData[index + 1] / 255.f,
atlasData[index + 2] / 255.f, atlasData[index + 3] / 255.f };
}
sptr<AtlasRef> TextureAtlas::addImage(const string& name, bool base, u16vec2 size, vec<u8> data) {
u16vec2 tileSize = u16vec2(glm::ceil(vec2(size) / 16.f));
let posOpt = findImageSpace(tileSize);
AtlasTexture TextureAtlas::addBytes(const string& identifier, bool persistent, u16vec2 size, vec<u8> data) {
let tileSize = u16vec2(glm::ceil(vec2(size) / 16.f));
let posOpt = findAtlasSpace(tileSize);
std::cout << tileSize << ", " << identifier << std::endl;
if (!posOpt) throw std::runtime_error("Failed to find space in the dynamic definition atlas.");
u16vec2 pos = *posOpt;
u16vec2 pos = *posOpt * static_cast<u16>(16);
textureSlotsUsed += tileSize.x * tileSize.y;
tilesUsed += tileSize.x * tileSize.y;
textures.erase(identifier);
AtlasTexture ref(*this, identifier, pos, size, data);
ref.setPersistent(persistent);
if (!persistent) texturesUnused.insert(identifier);
addTexture(ref);
textures.erase(name);
let ref = make_shared<AtlasRef>(AtlasRef {
pos,
tileSize,
uvec2(pos) * 16u,
size,
vec4(pos.x * 16 / static_cast<f32>(canvasSize.x), pos.y * 16 / static_cast<f32>(canvasSize.y),
(pos.x * 16 + size.x) / static_cast<f32>(canvasSize.x), (pos.y * 16 + size.y) / static_cast<f32>(canvasSize.y)),
name,
base
});
textures.insert({ name, ref });
updateAtlas(ref->rawPos, ref->rawSize, data);
return ref;
}
sptr<AtlasRef> TextureAtlas::generateCrackImage(const string& name, u8 crackLevel) {
RawTexData base = getBytesOfTex(name);
AtlasTexture TextureAtlas::operator[](const string& identifier) {
const let tex = get(identifier);
if (tex.getIdentifier() != "_missing") return tex;
std::string crackStr("zeus:default:crack_" + std::to_string(crackLevel));
RawTexData crack = getBytesOfTex(crackStr);
for (int i = 0; i < base.size.x * base.size.y; i++) {
float alpha = crack.data[i * 4 + 3] / 255.f;
base.data[i * 4 + 0] = static_cast<u8>(base.data[i * 4 + 0] * (1 - alpha) + crack.data[i * 4 + 0] * alpha);
base.data[i * 4 + 1] = static_cast<u8>(base.data[i * 4 + 1] * (1 - alpha) + crack.data[i * 4 + 1] * alpha);
base.data[i * 4 + 2] = static_cast<u8>(base.data[i * 4 + 2] * (1 - alpha) + crack.data[i * 4 + 2] * alpha);
}
auto ref = addImage(name + "_crack_" + std::to_string(crackLevel), false, base.size, base.data);
return ref;
const let data = texBuilder.build(identifier);
let texture = addBytes(identifier, false, data.size, data.data);
if (data.tintMask) texture.setTintData(*data.tintInd,
addBytes(identifier + ":tint", false, data.size, *data.tintMask));
return texture;
}
sptr<AtlasRef> TextureAtlas::operator[](const string& name) {
if (textures.count(name)) return textures[name];
sptr<AtlasRef> gen = generateTexture(name);
if (gen) return gen;
throw std::runtime_error("Invalid texture: '" + name + "'");
AtlasTexture TextureAtlas::get(const string& identifier) const {
if (textures.count(identifier)) return textures.at(identifier);
return textures.at("_missing");
}
sptr<AtlasRef> TextureAtlas::generateTexture(string req) {
TexParserCtx ctx { *this };
const let data = parser.parse(req, ctx);
return addImage(req, false, data.data->size, data.data->data);
const uvec2 TextureAtlas::getCanvasSize() {
return canvasSize;
}
TextureAtlas::RawTexData TextureAtlas::getBytesOfTex(const string& name) {
let it = textures.find(name);
if (it != textures.end()) return getBytesAtPos(it->second->rawPos, it->second->rawSize);
std::cout << Log::err << "Invalid base texture '" << name << "'." << Log::endl;
let& missing = textures["_missing"];
return getBytesAtPos(missing->rawPos, missing->rawSize);
}
TextureAtlas::RawTexData TextureAtlas::getBytesAtPos(uvec2 pos, uvec2 size) {
RawTexData tex { vec<u8>(size.x * size.y * 4), size };
for (u32 i = 0; i < size.x * size.y; i++) {
u32 x = pos.x + (i % size.x);
u32 y = pos.y + (i / size.x);
tex.data[i * 4 + 0] = atlasData[x * 4 + y * (canvasSize.x * 4)];
tex.data[i * 4 + 1] = atlasData[x * 4 + 1 + y * (canvasSize.x * 4)];
tex.data[i * 4 + 2] = atlasData[x * 4 + 2 + y * (canvasSize.x * 4)];
tex.data[i * 4 + 3] = atlasData[x * 4 + 3 + y * (canvasSize.x * 4)];
}
return tex;
}
optional<u16vec2> TextureAtlas::findImageSpace(u16vec2 tileSize) {
optional<u16vec2> TextureAtlas::findAtlasSpace(u16vec2 tileSize) {
for (u16 j = 0; j < canvasTileSize.y - (tileSize.y - 1); j++) {
for (u16 i = 0; i < canvasTileSize.x - (tileSize.x - 1); i++) {
if (empty[j * canvasTileSize.x + i]) {
bool space = true;
for (int k = 0; k < tileSize.y; k++) {
for (int l = 0; l < tileSize.x; l++) {
if (!empty[(j + k) * canvasTileSize.x + (i + l)]) {
space = false;
break;
}
bool space = true;
for (u16 k = 0; k < tileSize.y; k++) {
for (u16 l = 0; l < tileSize.x; l++) {
if (tiles[(j + k) * canvasTileSize.x + (i + l)]) {
space = false;
break;
}
if (!space) break;
}
if (space) {
for (int k = 0; k < tileSize.y; k++) {
for (int l = 0; l < tileSize.x; l++) {
empty[(j + k) * canvasTileSize.x + (i + l)] = false;
}
}
return u16vec2(i, j);
}
if (!space) break;
}
if (space) {
for (u16 k = 0; k < tileSize.y; k++)
for (u16 l = 0; l < tileSize.x; l++)
tiles[(j + k) * canvasTileSize.x + (i + l)] = true;
return u16vec2(i, j);
}
}
}
@ -319,38 +134,45 @@ void TextureAtlas::createMissingTexture() {
data[i * 4 + 3] = 255;
}
addImage("_missing", true, u16vec2(16), data);
addBytes("_missing", true, u16vec2(16), data);
}
void TextureAtlas::updateAtlas(u16vec2 pos, u16vec2 size, vec<u8> data) {
texture.updateTexture(pos.x, pos.y, size.x, size.y, data.data());
void TextureAtlas::addTexture(const AtlasTexture& tex) {
let pos = tex.getPos();
let size = tex.getSize();
textures.insert({ tex.getIdentifier(), tex });
texture.updateTexture(pos.x, pos.y, size.x, size.y, tex.getBytes().data());
}
void TextureAtlas::removeTexture(const AtlasTexture& tex) {
textures.erase(tex.getIdentifier());
for (u32 i = 0; i < size.x * size.y * 4; i++) {
u32 x = (i / 4) % size.x;
u32 y = (i / 4) / size.x;
u32 of = i % 4;
let tilePos = tex.getTilePos();
let tileSize = tex.getTileSize();
tilesUsed -= tileSize.x * tileSize.y;
for (u16 x = tilePos.x; x < tilePos.x + tileSize.x; x++)
for (u16 y = tilePos.y; y < tilePos.y + tileSize.y; y++)
tiles[y * canvasTileSize.x + x] = false;
atlasData[(pos.x + x + (pos.y + y) * canvasSize.x) * 4 + of] = data[(x + y * size.x) * 4 + of];
}
// For debugging
vec<u8> clear(tileSize.x * 16 * tileSize.y * 16 * 4);
texture.updateTexture(tilePos.x * 16, tilePos.y * 16, tileSize.x * 16, tileSize.y * 16, clear.data());
}
void TextureAtlas::deleteImage(sptr<AtlasRef> ref) {
// Actually delete the image from the texture (for debugging)
//auto data = new unsigned char[ref->width * ref->height * 4];
//
//for (int i = 0; i < ref->width * ref->height * 4; i++) {
// data[i] = 0;
//}
//
//updateAtlas(ref->tileX, ref->tileY, ref->width, ref->height, data);
//delete[] data;
textureSlotsUsed -= ref->size.x * ref->size.y;
for (u32 x = ref->pos.x; x < ref->pos.x + ref->size.x; x++) {
for (u32 y = ref->pos.y; y < ref->pos.y + ref->size.y; y++) {
empty[y * canvasTileSize.x + x] = true;
}
}
const u32 TextureAtlas::getTilesUsed() {
return tilesUsed;
}
const u32 TextureAtlas::getTilesTotal() {
return tilesTotal;
}
const Texture& TextureAtlas::getTexture() {
return texture;
}
void TextureAtlas::alertUnused(const string& identifier) {
texturesUnused.insert(identifier);
}

View File

@ -1,81 +1,70 @@
#pragma once
#include <iostream>
#include <filesystem>
#include <unordered_set>
#include <unordered_map>
#include "util/Types.h"
#include "util/StringParser.h"
#include "client/graph/Texture.h"
#include "game/atlas/TextureBuilder.h"
#include "game/atlas/asset/AtlasTexture.h"
class AtlasRef;
class AtlasTexture;
/**
* Manages loading and generating images into a single atlas texture,
* with reference counting and runtime manipulations.
*/
class TextureAtlas {
public:
struct RawTexData {
RawTexData(const vec<u8>& data, const uvec2& size):
data(data), size(size) {};
vec<u8> data;
uvec2 size;
};
TextureAtlas() = default;
explicit TextureAtlas(uvec2 size);
void update();
vec<sptr<AtlasRef>> loadDirectory(const string& path, bool base = true);
vec<AtlasTexture> addDirectory(const std::filesystem::path& path, bool base);
sptr<AtlasRef> loadImage(const string& path, const string& name, bool base = false);
AtlasTexture addFile(const std::filesystem::path& path, bool base);
sptr<AtlasRef> addImage(const string& name, bool base, u16vec2 size, vec<u8> data);
AtlasTexture addBytes(const string& identifier, bool base, u16vec2 size, vec<u8> data);
sptr<AtlasRef> operator[](const string& name);
AtlasTexture operator[](const string& identifier);
vec4 getPixel(const sptr<AtlasRef>& ref, ivec2 pos);
AtlasTexture get(const string& identifier) const;
sptr<AtlasRef> generateCrackImage(const string& name, u8 crackLevel);
const uvec2 getCanvasSize();
ivec2 canvasSize;
ivec2 canvasTileSize;
const u32 getTilesUsed();
Texture texture {};
vec<u8> atlasData;
const u32 getTilesTotal();
u32 textureSlotsUsed = 0;
u32 maxTextureSlots = 0;
const Texture& getTexture();
void alertUnused(const string& identifier);
private:
sptr<AtlasRef> generateTexture(string req);
RawTexData getBytesOfTex(const string& name);
RawTexData getBytesAtPos(uvec2 pos, uvec2 size);
optional<u16vec2> findImageSpace(u16vec2 tileSize);
void createMissingTexture();
void updateAtlas(u16vec2 pos, u16vec2 size, vec<u8> data);
optional<u16vec2> findAtlasSpace(u16vec2 tileSize);
void deleteImage(sptr<AtlasRef> ref);
void addTexture(const AtlasTexture& tex);
struct TexParserData {
TexParserData() = default;
explicit TexParserData(sptr<RawTexData> data): data(data) {};
sptr<RawTexData> data;
};
void removeTexture(const AtlasTexture& tex);
struct TexParserCtx {
TextureAtlas& atlas;
};
uvec2 canvasSize;
uvec2 canvasTileSize;
using TexParser = StringParser<TexParserData, TexParserCtx>;
u32 tilesUsed = 0;
u32 tilesTotal = 0;
TexParser parser {};
vec<bool> tiles;
std::unordered_set<string> texturesUnused {};
std::unordered_map<string, AtlasTexture> textures {};
vec<bool> empty;
std::unordered_map<string, sptr<AtlasRef>> textures;
Texture texture {};
const TextureBuilder texBuilder { *this };
};

View File

@ -0,0 +1,113 @@
#include "TextureAtlas.h"
#include "TextureBuilder.h"
#include "game/atlas/asset/AtlasTexture.h"
TextureBuilder::Data TextureBuilder::Data::literal(Context& ctx, string str) {
let tex = ctx.atlas.get(str);
return Data(tex.getSize(), tex.getBytes());
}
TextureBuilder::Data TextureBuilder::Data::canvas(u16 w, optional<variant<u16, string>> h, optional<string> fill) {
u16vec2 size = { w, h && std::holds_alternative<u16>(*h) ? std::get<u16>(*h) : w };
vec4 color = Util::hexToColorVec(fill ? *fill : h &&
std::holds_alternative<string>(*h) ? std::get<string>(*h) : "#0000");
vec<u8> data(size.x * size.y * 4);
for (u32 i = 0; i < size.x * size.y * 4; i++) data[i] = color[i % 4] * 255;
return Data(size, data);
}
TextureBuilder::Data TextureBuilder::Data::alpha(f32 factor, Data tex) {
for (u32 i = 0; i < tex.size.x * tex.size.y; i++)
tex.data[i * 4 + 3] = (tex.data[i * 4 + 3] / 255.f * factor) * 255;
return tex;
}
TextureBuilder::Data TextureBuilder::Data::crop(u16 x, u16 y, u16 w, variant<u16, Data> hV, optional<Data> texO) {
u16 h = std::holds_alternative<u16>(hV) ? std::get<u16>(hV) : w;
Data& tex = std::holds_alternative<Data>(hV) ? std::get<Data>(hV) : *texO;
vec<u8> data(w * h * 4);
for (u32 i = 0; i < w * h * 4; i++) data[i] =
tex.data[((x + i / 4 % w) + (y + i / 4 / w) * tex.size.x) * 4 + i % 4];
tex.data = data;
if (tex.tintMask) {
vec<u8> mask(w * h * 4);
for (u32 i = 0; i < w * h * 4; i++) mask[i] =
(*tex.tintMask)[((x + i / 4 % w) + (y + i / 4 / w) * tex.size.x) * 4 + i % 4];
tex.tintMask = mask;
}
tex.size = { w, h };
return tex;
}
TextureBuilder::Data TextureBuilder::Data::multiply(variant<string, Data> multiple, Data tex) {
if (std::holds_alternative<Data>(multiple)) {
Data& data = std::get<Data>(multiple);
for (u32 i = 0; i < tex.size.x * tex.size.y * 4; i++) tex.data[i] *= data.data[i % 4];
}
else {
vec4 mul = Util::hexToColorVec(std::get<string>(multiple));
for (u32 i = 0; i < tex.size.x * tex.size.y * 4; i++) tex.data[i] *= mul[i % 4];
}
return tex;
}
TextureBuilder::Data TextureBuilder::Data::stack(Data b, Data s1, optional<Data> s2, optional<Data> s3,
optional<Data> s4, optional<Data> s5, optional<Data> s6, optional<Data> s7, optional<Data> s8) {
vec<Data> stack;
stack.reserve(8);
stack.emplace_back(s1);
if (s2) stack.emplace_back(*s2);
if (s3) stack.emplace_back(*s3);
if (s4) stack.emplace_back(*s4);
if (s5) stack.emplace_back(*s5);
if (s6) stack.emplace_back(*s6);
if (s7) stack.emplace_back(*s7);
if (s8) stack.emplace_back(*s8);
for (const Data& s : stack) {
for (u32 i = 0; i < b.size.x * b.size.y; i++) {
const f32 factor = s.data[i * 4 + 3] / 255.f;
b.data[i * 4] = b.data[i * 4] * (1 - factor) + s.data[i * 4] * factor;
b.data[i * 4 + 1] = b.data[i * 4 + 1] * (1 - factor) + s.data[i * 4 + 1] * factor;
b.data[i * 4 + 2] = b.data[i * 4 + 2] * (1 - factor) + s.data[i * 4 + 2] * factor;
b.data[i * 4 + 3] = std::min(static_cast<u16>(b.data[i * 4 + 3]) + s.data[i * 4 + 3], 255);
if (s.tintMask) {
if (!b.tintMask) b.tintMask = canvas(b.size.x, b.size.y, {}).data;
(*b.tintMask)[i * 4] = (*b.tintMask)[i * 4] * (1 - factor) + (*s.tintMask)[i * 4] * factor;
(*b.tintMask)[i * 4 + 1] = (*b.tintMask)[i * 4 + 1] * (1 - factor) + (*s.tintMask)[i * 4 + 1] * factor;
(*b.tintMask)[i * 4 + 2] = (*b.tintMask)[i * 4 + 2] * (1 - factor) + (*s.tintMask)[i * 4 + 2] * factor;
(*b.tintMask)[i * 4 + 3] = factor * 255;
}
}
}
return b;
}
TextureBuilder::Data TextureBuilder::Data::tint(u32 tint, TextureBuilder::Data tex, optional<Data> mask) {
tex.tintInd = tint;
if (mask) tex.tintMask = mask->data;
else tex.tintMask = canvas(tex.size.x, tex.size.y, "#fff").data;
return tex;
}
TextureBuilder::TextureBuilder(TextureAtlas& atlas): ctx({ atlas }) {
parser.addLiteralFnCtx(&TextureBuilder::Data::literal);
parser.addFn<u16, optional<variant<u16, string>>, optional<string>>("canvas", &TextureBuilder::Data::canvas);
parser.addFn<f32, Data>("alpha", &TextureBuilder::Data::alpha);
parser.addFn<u16, u16, u16, variant<u16, Data>, optional<Data>>("crop", &TextureBuilder::Data::crop);
parser.addFn<variant<string, Data>, Data>("multiply", &TextureBuilder::Data::multiply);
parser.addFn<Data, Data, optional<Data>, optional<Data>, optional<Data>, optional<Data>,
optional<Data>, optional<Data>, optional<Data>>("", &TextureBuilder::Data::stack);
parser.addFn<u32, Data, optional<Data>>("tint", &TextureBuilder::Data::tint);
}
TextureBuilder::Data TextureBuilder::build(const string& str) const {
return parser.parse(str, ctx);
}

View File

@ -0,0 +1,115 @@
#pragma once
#include "util/StringParser.h"
//#include "game/atlas/TextureAtlas.h"
class TextureAtlas;
class TextureBuilder {
public:
struct Data;
private:
struct Context {
TextureAtlas& atlas;
};
typedef StringParser<Data, Context> Parser;
public:
class Data {
friend class TextureBuilder;
/**
* Loads a texture identified by a string into a Data.
*
* @param str - The string texture name to load.
*/
static Parser::Data literal(Parser::Ctx& ctx, string str);
/**
* Creates an empty canvas of a specified color.
*
* @param w - The width of the canvas.
* @param h - [optional] The height of the canvas, defaults to the width otherwise.
* @param fill - [optional] The color to fill the canvas with, defaults to transparent.
*/
static Parser::Data canvas(u16 w, optional<variant<u16, string>> h, optional<string> fill);
/**
* Adjusts the opacity of the provided texture by a factor.
*
* @param factor - The factor that the opacity should be scaled by. 0 = transparent, 1 = opaque.
* @param tex - The texture to scale the alpha of.
*/
static Parser::Data alpha(f32 factor, Parser::Data tex);
/**
* Crops the texture to the specified dimensions and offset.
*
* @param x - The left edge of the crop on the base texture.
* @param y - The top edge of the crop on the base texture.
* @param w - The width of the crop.
* @param h - The height of the crop.
* @param tex - The texture to crop.
*/
static Parser::Data crop(u16 x, u16 y, u16 w, variant<u16, Data> hV, optional<Data> texO);
/**
* Multiplies the texture by the specified color or texture.
*
* @param multiple - A color to multiply the texture by, or another texture to multiply it by.
* @param tex - The texture to multiply.
*/
static Parser::Data multiply(variant<string, Data> multiple, Data tex);
/**
* Stacks the provided textures on top of each other, blending partially opaque pixels.
* Requires at least two textures. The maximum is currently eight textures,
* however that could be removed later with some tweaks to StringParser.
*
* @param b - The base texture to stack onto.
* @param s1 - A texture to stack on top of the base texture.
* @param s2 - Another texture to stack on top of s1.
* @param s3 - ...
*/
static Parser::Data stack(Data b, Data s1, optional<Data> s2, optional<Data> s3,
optional<Data> s4, optional<Data> s5, optional<Data> s6, optional<Data> s7, optional<Data> s8);
/**
* Tints the texture specified with a specific tint map, usually controlled by biome parameters.
* @param tint - The tint map to use.
* @param tex - The texture to tint.
* @param mask - An image whose red channel determines the strength of the tint for every pixel.
*/
static Parser::Data tint(u32 tint, Data tex, optional<Data> mask);
public:
Data() = default;
explicit Data(u16vec2 size): size(size) {};
Data(u16vec2 size, const vec<u8>& data): size(size), data(data) {};
u16vec2 size;
vec<u8> data;
optional<u32> tintInd;
optional<vec<u8>> tintMask;
};
TextureBuilder(TextureAtlas& atlas);
Data build(const string& str) const;
private:
mutable Context ctx;
Parser parser {};
};

View File

@ -1,19 +0,0 @@
//
// Created by aurailus on 17/04/19.
//
#pragma once
#include "util/Util.h"
struct AtlasRef {
u16vec2 pos {};
u16vec2 size {};
uvec2 rawPos {};
uvec2 rawSize {};
vec4 uv {};
string name = "";
bool base = false;
};

View File

@ -0,0 +1,44 @@
#include "AtlasTexture.h"
#include "game/atlas/TextureAtlas.h"
vec4 AtlasTexture::getUVPos() const {
const uvec2 size = holds_alternative<ByteData>(shr->data) ?
get<ByteData>(shr->data).size : get<CropData>(shr->data).size;
const uvec2 pos = holds_alternative<ByteData>(shr->data) ?
shr->pos : shr->pos + get<CropData>(shr->data).pos;
const f32 divX = atlas.getCanvasSize().x, divY = atlas.getCanvasSize().y;
return vec4(pos.x / divX, pos.y / divY, (pos.x + size.x) / divX, (pos.y + size.y) / divY);
}
vec<u8> AtlasTexture::getBytes() const {
if (std::holds_alternative<ByteData>(shr->data)) return get<ByteData>(shr->data).data;
throw std::runtime_error("cant");
}
optional<u32> AtlasTexture::getTintInd() const {
if (!shr->tintData) return std::nullopt;
return shr->tintData->first;
}
optional<AtlasTexture> AtlasTexture::getTintMask() const {
if (!shr->tintData) return std::nullopt;
return *shr->tintData->second.get();
}
void AtlasTexture::setPersistent(bool persistent) {
shr->persistent = persistent;
}
void AtlasTexture::setTintData(u32 tintInd, const AtlasTexture& tintMask) {
shr->tintData = {{ tintInd, make_unique<AtlasTexture>(tintMask) }};
}
bool AtlasTexture::shouldBeRemoved() {
return shr.use_count() == 1 && !shr->persistent;
}
AtlasTexture::~AtlasTexture() {
if (shr.use_count() == 2 && !shr->persistent) atlas.alertUnused(shr->identifier);
}

View File

@ -0,0 +1,147 @@
#pragma once
#include "util/Util.h"
class TextureAtlas;
/**
* Represents a single drawable texture from the texture atlas.
* Will either contain its own data, or reference another atlas texture as a crop of it.
* AtlasTextures will automatically remove themselves from the texture atlas once all copies of them are destroyed.
*/
class AtlasTexture {
/** Stores byte data for an atlas texture. */
struct ByteData {
ByteData(uvec2 size, const vec<u8>& data): data(data), size(size) {};
/** The size of the texture. */
uvec2 size;
/** The raw bytes of the texture. */
vec<u8> data;
};
/** Stores a reference to another atlas texture and a crop region. */
struct CropData {
CropData(uvec2 pos, uvec2 size, const sptr<AtlasTexture>& source): pos(pos), size(size), source(source) {}
/** The top-left position of the crop region. */
uvec2 pos;
/** The sise of the crop region. */
uvec2 size;
/** The image to crop from. */
sptr<AtlasTexture> source;
};
/**
* The main data container for the AtlasTexture. It is stored in a
* shared pointer so that it may be reference counted.
* Contains the identifier, atlas position, and data of a texture in the atlas.
* The data may be a ByteData storing raw texture bytes,
* or a CropData referencing another texture to crop this one from.
* May also store a reference to a tint index and mask, if the texture is to be biome tinted.
*/
struct SharedData {
SharedData(const string& identifier, uvec2 pos, const variant<ByteData, CropData>& data):
identifier(identifier), pos(pos), data(data) {}
/** The position of the top-left point of the texture in the texture atlas. */
uvec2 pos {};
/** The identifier of the texture. */
string identifier;
/** Whether or not the texture should be kept even if it is not in use. */
bool persistent = false;
/** The data of the texture, either bytes or a crop region. */
variant<ByteData, CropData> data;
/** An optional pair containing biome tint data. */
optional<std::pair<u32, uptr<AtlasTexture>>> tintData;
};
public:
AtlasTexture(const AtlasTexture& o): shr(o.shr), atlas(o.atlas) {}
AtlasTexture& operator=(const AtlasTexture& o) { shr = o.shr; return *this; }
AtlasTexture(TextureAtlas& atlas, const string& identifier, uvec2 pos, uvec2 size, const sptr<AtlasTexture>& source):
AtlasTexture(atlas, identifier, source->getPos(), CropData { pos, size, source }) {}
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):
shr(std::make_shared<SharedData>(identifier, pos, data)), atlas(atlas) {}
/** Returns the texture's identifier. */
inline const string& getIdentifier() const {
return shr->identifier;
}
/** Returns the texture's atlas position in pixels. */
inline const uvec2& getPos() const {
return shr->pos;
}
/** Returns the texture's altas size in pixels. */
inline const uvec2& getSize() const {
return holds_alternative<ByteData>(shr->data) ?
get<ByteData>(shr->data).size : get<CropData>(shr->data).size;
}
/** Returns the texture's atlas position in tiles. */
inline u16vec2 getTilePos() const {
return u16vec2(shr->pos / 16u);
}
/** Returns the texture's atlas size in tiles. */
inline u16vec2 getTileSize() const {
return u16vec2(glm::ceil(vec2(getSize()) / 16.f));
}
/** Returns the texture's UVs, in a four-dimensional vector. */
vec4 getUVPos() const;
/**
* Returns the bytes of the texture.
* This is an expensive operation because the bytes may need to be cloned from a crop region,
* so a reference cannot be returned. In the future,
* a ref-counted object may be returned instead to reduce overhead.
*/
vec<u8> getBytes() const;
/** Returns the biome tint index of the texture, if it is to be tinted. */
optional<u32> getTintInd() const;
/** Returns the biome tint mask texture of this texture, if it is to be tinted. */
optional<AtlasTexture> getTintMask() const;
/** Sets the persistence of the texture, if it is persistent, it will not be deleted when it is no longer in use. */
void setPersistent(bool persistent);
/** Sets the tint information of the texture. */
void setTintData(u32 tintInd, const AtlasTexture& tintMask);
/** Returns true if the texture is only referenced by the texture atlas and is not persistent. */
bool shouldBeRemoved();
~AtlasTexture();
private:
// IF YOU ARE PUTTING SOMETHING ELSE HERE, YOU PROBABLY SHOULDN'T BE.
// PUT ANY ATLASTEXTURE DATA IN THE SHAREDDATA STRUCT INSTEAD.
/** A reference to the texture atlas holding this texture. */
TextureAtlas& atlas;
/** The actual ref-counted data of the AtlasTexture. */
sptr<SharedData> shr;
};

View File

@ -5,103 +5,99 @@
#include "CraftItemDef.h"
#include "game/atlas/TextureAtlas.h"
#include "game/atlas/asset/AtlasRef.h"
CraftItemDef::CraftItemDef(const std::string& identifier, const std::string& name, unsigned short maxStackSize,
const std::vector<std::string>& textures, const std::vector<std::shared_ptr<AtlasRef>>& textureRefs) :
CraftItemDef::CraftItemDef(const string& identifier, const string& name, u16 maxStackSize,
const vec<string>& textures, const vec<AtlasTexture>& textureRefs) :
CraftItemDef(identifier, 0, name, maxStackSize, textures, textureRefs) {}
CraftItemDef::CraftItemDef(const std::string& identifier, unsigned int index, const std::string& name,
unsigned short maxStackSize, const std::vector<std::string>& textures,
const std::vector<std::shared_ptr<AtlasRef>>& textureRefs) :
CraftItemDef::CraftItemDef(const string& identifier, u16 index, const string& name,
u16 maxStackSize, const vec<string>& textures, const vec<AtlasTexture>& textureRefs) :
ItemDef(ItemDef::Type::CRAFTITEM, identifier, name, index, maxStackSize),
textures(textures),
textureRefs(textureRefs) {}
textures(textures), textureRefs(textureRefs) {}
void CraftItemDef::createModel(TextureAtlas& atlas) {
std::unique_ptr<EntityMesh> mesh = std::make_unique<EntityMesh>();
uptr<EntityMesh> mesh = make_unique<EntityMesh>();
std::vector<EntityVertex> vertices;
std::vector<unsigned int> indices{ 0, 1, 2, 2, 3, 0, 4, 7, 6, 6, 5, 4 };
vec<EntityVertex> vertices;
vec<u16> indices { 0, 1, 2, 2, 3, 0, 4, 7, 6, 6, 5, 4 };
const std::shared_ptr<AtlasRef>& ref = textureRefs[0];
static const float xo = 0.040f;
static const f32 xo = 0.040f;
vec4 uv = textureRefs[0].getUVPos();
for (unsigned int i = 0; i <= 1; i++) {
float xx = xo * (i == 1 ? -1 : 1);
std::vector<EntityVertex> myVerts = {
{{ xx, -0.5, -0.5 }, { ref->uv.x, ref->uv.w, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {},
{}},
{{ xx, 0.5, -0.5 }, { ref->uv.x, ref->uv.y, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {}, {}},
{{ xx, 0.5, 0.5 }, { ref->uv.z, ref->uv.y, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {}, {}},
{{ xx, -0.5, 0.5 }, { ref->uv.z, ref->uv.w, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {}, {}}
for (u8 i = 0; i <= 1; i++) {
f32 xx = xo * (i == 1 ? -1 : 1);
vec<EntityVertex> myVerts = {
{{ xx, -0.5, -0.5 }, { uv.x, uv.w, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {}, {}},
{{ xx, 0.5, -0.5 }, { uv.x, uv.y, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {}, {}},
{{ xx, 0.5, 0.5 }, { uv.z, uv.y, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {}, {}},
{{ xx, -0.5, 0.5 }, { uv.z, uv.w, 0, 1 }, { 1, 1, 1 }, true, { (i == 1 ? -1 : 1), 0, 0 }, {}, {}}
};
vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
}
unsigned int indOffset = 8;
for (unsigned int i = 0; i < 16 * 16; i++) {
glm::vec2 samplePos = { i % 16, i / 16 };
glm::vec2 off{ samplePos.x / 16.f, samplePos.y / 16.f };
glm::vec4 col = atlas.getPixel(ref, samplePos);
if (col.w < 0.5) continue;
if (samplePos.y == 0 || atlas.getPixel(ref, { samplePos.x, samplePos.y - 1 }).w < 0.5) {
std::vector<EntityVertex> myVerts = {
{{ -xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
{{ -xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
{{ xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
{{ xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}}};
vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
std::vector<unsigned int> myInds = { indOffset, indOffset + 1, indOffset + 2, indOffset + 2, indOffset + 3,
indOffset };
indices.insert(indices.end(), myInds.begin(), myInds.end());
indOffset += 4;
}
if (samplePos.y == 15 || atlas.getPixel(ref, { samplePos.x, samplePos.y + 1 }).w < 0.5) {
std::vector<EntityVertex> myVerts = {
{{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}},
{{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}},
{{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}},
{{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}}};
vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
std::vector<unsigned int> myInds = { indOffset, indOffset + 3, indOffset + 2, indOffset + 2, indOffset + 1,
indOffset };
indices.insert(indices.end(), myInds.begin(), myInds.end());
indOffset += 4;
}
if (samplePos.x == 0 || atlas.getPixel(ref, { samplePos.x - 1, samplePos.y }).w < 0.5) {
std::vector<EntityVertex> myVerts = {
{{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}},
{{ -xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}},
{{ xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}},
{{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}}};
vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
std::vector<unsigned int> myInds = { indOffset, indOffset + 1, indOffset + 2, indOffset + 2, indOffset + 3,
indOffset };
indices.insert(indices.end(), myInds.begin(), myInds.end());
indOffset += 4;
}
if (samplePos.x == 15 || atlas.getPixel(ref, { samplePos.x + 1, samplePos.y }).w < 0.5) {
std::vector<EntityVertex> myVerts = {
{{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}},
{{ -xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}},
{{ xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}},
{{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}}};
vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
std::vector<unsigned int> myInds = { indOffset, indOffset + 3, indOffset + 2, indOffset + 2, indOffset + 1,
indOffset };
indices.insert(indices.end(), myInds.begin(), myInds.end());
indOffset += 4;
}
}
mesh->create(vertices, indices);
// u16 indOffset = 8;
// vec<u8> data = ref.getBytes();
// for (u16 i = 0; i < 16 * 16; i++) {
// vec2 samplePos = { i % 16, i / 16 };
// vec2 off { samplePos.x / 16.f, samplePos.y / 16.f };
// vec4 col = atlas.getPixel(ref, samplePos);
//
// if (col.w < 0.5) continue;
//
// if (samplePos.y == 0 || atlas.getPixel(ref, { samplePos.x, samplePos.y - 1 }).w < 0.5) {
// std::vector<EntityVertex> myVerts = {
// {{ -xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
// {{ -xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
// {{ xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
// {{ xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}}};
// vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
// std::vector<unsigned int> myInds = { indOffset, indOffset + 1, indOffset + 2, indOffset + 2, indOffset + 3,
// indOffset };
// indices.insert(indices.end(), myInds.begin(), myInds.end());
// indOffset += 4;
// }
//
// if (samplePos.y == 15 || atlas.getPixel(ref, { samplePos.x, samplePos.y + 1 }).w < 0.5) {
// std::vector<EntityVertex> myVerts = {
// {{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}},
// {{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}},
// {{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}},
// {{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, -1, 0 }, {}, {}}};
// vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
// std::vector<unsigned int> myInds = { indOffset, indOffset + 3, indOffset + 2, indOffset + 2, indOffset + 1,
// indOffset };
// indices.insert(indices.end(), myInds.begin(), myInds.end());
// indOffset += 4;
// }
//
// if (samplePos.x == 0 || atlas.getPixel(ref, { samplePos.x - 1, samplePos.y }).w < 0.5) {
// std::vector<EntityVertex> myVerts = {
// {{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}},
// {{ -xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}},
// {{ xo, 0.5 - off.y, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}},
// {{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x }, col, { 1, 1, 1 }, false, { 0, 0, 1 }, {}, {}}};
// vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
// std::vector<unsigned int> myInds = { indOffset, indOffset + 1, indOffset + 2, indOffset + 2, indOffset + 3,
// indOffset };
// indices.insert(indices.end(), myInds.begin(), myInds.end());
// indOffset += 4;
// }
//
// if (samplePos.x == 15 || atlas.getPixel(ref, { samplePos.x + 1, samplePos.y }).w < 0.5) {
// std::vector<EntityVertex> myVerts = {
// {{ -xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}},
// {{ -xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}},
// {{ xo, 0.5 - off.y, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}},
// {{ xo, 0.5 - off.y - 0.0625, -0.5 + off.x + 0.0625 }, col, { 1, 1, 1 }, false, { 0, 0, -1 }, {}, {}}};
// vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
// std::vector<unsigned int> myInds = { indOffset, indOffset + 3, indOffset + 2, indOffset + 2, indOffset + 1,
// indOffset };
// indices.insert(indices.end(), myInds.begin(), myInds.end());
// indOffset += 4;
// }
// }
// mesh->create(vertices, indices);
entityModel->fromMesh(std::move(mesh));
}

View File

@ -1,11 +1,10 @@
#pragma once
#include <vector>
#include "lua/Lua.h"
#include "ItemDef.h"
class AtlasRef;
#include "lua/Lua.h"
#include "game/atlas/asset/AtlasTexture.h"
class TextureAtlas;
/**
@ -22,19 +21,18 @@ public:
CraftItemDef(): ItemDef(ItemDef::Type::CRAFTITEM) {};
CraftItemDef(const std::string& identifier, const std::string& name, unsigned short maxStackSize,
const std::vector<std::string>& textures, const std::vector<std::shared_ptr<AtlasRef>>& textureRefs);
CraftItemDef(const string& identifier, const string& name, u16 maxStackSize,
const vec<string>& textures, const vec<AtlasTexture>& texturesRefs);
CraftItemDef(const std::string& identifier, unsigned int index, const std::string& name,
unsigned short maxStackSize, const std::vector<std::string>& textures,
const std::vector<std::shared_ptr<AtlasRef>>& textureRefs);
CraftItemDef(const string& identifier, u16 index, const string& name,
u16 maxStackSize, const vec<string>& textures, const vec<AtlasTexture>& texturesRefs);
void createModel(TextureAtlas& atlas);
bool hasUse();
std::vector<std::string> textures {};
std::vector<std::shared_ptr<AtlasRef>> textureRefs {};
vec<string> textures {};
vec<AtlasTexture> textureRefs {};
std::unordered_map<Callback, sol::protected_function, Util::EnumClassHash> callbacks {};
};

View File

@ -1,95 +1,84 @@
//
// Created by aurailus on 2020-07-06.
//
#include "BlockModel.h"
#include "util/Vec.h"
BlockModel
BlockModel::createCube(std::vector<std::shared_ptr<AtlasRef>> textureRefs,
std::vector<unsigned int> blendInds, std::vector<std::shared_ptr<AtlasRef>> blendMaskRefs) {
BlockModel::BlockModel(vec<std::tuple<AtlasTexture, optional<u32>, optional<AtlasTexture>>> data):
visible(true), culls(true) {
BlockModel blockModel;
blockModel.visible = true;
blockModel.culls = true;
textures.reserve(data.size());
for (let& tuple : data) textures.emplace_back(get<0>(tuple));
if (textures.size() == 0) throw std::runtime_error("Created cube with empty textures array.");
std::vector<BlockModelVertex> vertices{};
std::vector<unsigned int> indices{};
unsigned int accessInd;
for (auto& ref : textureRefs) if (ref != nullptr) blockModel.textureRefs.insert(ref);
if (textureRefs.empty()) textureRefs.emplace_back(nullptr);
if (blendInds.empty()) blendInds.emplace_back(0);
if (blendMaskRefs.empty()) blendMaskRefs.emplace_back(nullptr);
//Left Face
vertices = {
{{ 0, 0, 0 }, {}, { 0, 1 }, { 0, 1 }},
{{ 0, 0, 1 }, {}, { 1, 1 }, { 1, 1 }},
{{ 0, 1, 1 }, {}, { 1, 0 }, { 1, 0 }},
{{ 0, 1, 0 }, {}, { 0, 0 }, { 0, 0 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<int>(textureRefs.size() - 1), 2));
MeshPart leftMeshPart(vertices, indices, textureRefs[accessInd], blendInds[accessInd], blendMaskRefs[accessInd]);
blockModel.parts[static_cast<int>(EVec::LEFT)].push_back(leftMeshPart);
//Right Face
vertices = {
{{ 1, 1, 1 }, {}, { 1, 0 }, { 1, 0 }},
{{ 1, 0, 1 }, {}, { 1, 1 }, { 1, 1 }},
{{ 1, 0, 0 }, {}, { 0, 1 }, { 0, 1 }},
{{ 1, 1, 0 }, {}, { 0, 0 }, { 0, 0 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<int>(textureRefs.size() - 1), 3));
MeshPart rightMeshPart(vertices, indices, textureRefs[accessInd], blendInds[accessInd], blendMaskRefs[accessInd]);
blockModel.parts[static_cast<int>(EVec::RIGHT)].push_back(rightMeshPart);
//Top Face
vertices = {
/* Top Face */
vec<BlockModelVertex> vertices = {
{{ 0, 1, 0 }, {}, { 0, 0 }, { 0, 0 }},
{{ 0, 1, 1 }, {}, { 0, 1 }, { 0, 1 }},
{{ 1, 1, 1 }, {}, { 1, 1 }, { 1, 1 }},
{{ 1, 1, 0 }, {}, { 1, 0 }, { 1, 0 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<int>(textureRefs.size() - 1), 0));
MeshPart topMeshPart(vertices, indices, textureRefs[accessInd], blendInds[accessInd], blendMaskRefs[accessInd]);
blockModel.parts[static_cast<int>(EVec::TOP)].push_back(topMeshPart);
vec<u32> indices = { 0, 1, 2, 2, 3, 0 };
//Bottom Face
u32 accessInd = (std::max)(0, (std::min)(static_cast<i32>(data.size() - 1), 0));
parts[static_cast<i32>(EVec::TOP)].emplace_back(vertices, indices,
get<0>(data[accessInd]), get<1>(data[accessInd]), get<2>(data[accessInd]));
/* Bottom Face */
vertices = {
{{ 0, 0, 0 }, {}, { 0, 0 }, { 0, 0 }},
{{ 1, 0, 0 }, {}, { 1, 0 }, { 1, 0 }},
{{ 1, 0, 1 }, {}, { 1, 1 }, { 1, 1 }},
{{ 0, 0, 1 }, {}, { 0, 1 }, { 0, 1 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<int>(textureRefs.size() - 1), 1));
MeshPart bottomMeshPart(vertices, indices, textureRefs[accessInd], blendInds[accessInd], blendMaskRefs[accessInd]);
blockModel.parts[static_cast<int>(EVec::BOTTOM)].push_back(bottomMeshPart);
//Front Face
accessInd = (std::max)(0, (std::min)(static_cast<i32>(data.size() - 1), 1));
parts[static_cast<i32>(EVec::BOTTOM)].emplace_back(vertices, indices,
get<0>(data[accessInd]), get<1>(data[accessInd]), get<2>(data[accessInd]));
/* Back Face */
vertices = {
{{ 0, 0, 0 }, {}, { 0, 1 }, { 0, 1 }},
{{ 0, 0, 1 }, {}, { 1, 1 }, { 1, 1 }},
{{ 0, 1, 1 }, {}, { 1, 0 }, { 1, 0 }},
{{ 0, 1, 0 }, {}, { 0, 0 }, { 0, 0 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<i32>(data.size() - 1), 2));
parts[static_cast<i32>(EVec::BACK)].emplace_back(vertices, indices,
get<0>(data[accessInd]), get<1>(data[accessInd]), get<2>(data[accessInd]));
/* Front Face */
vertices = {
{{ 1, 1, 1 }, {}, { 1, 0 }, { 1, 0 }},
{{ 1, 0, 1 }, {}, { 1, 1 }, { 1, 1 }},
{{ 1, 0, 0 }, {}, { 0, 1 }, { 0, 1 }},
{{ 1, 1, 0 }, {}, { 0, 0 }, { 0, 0 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<i32>(data.size() - 1), 3));
parts[static_cast<i32>(EVec::FRONT)].emplace_back(vertices, indices,
get<0>(data[accessInd]), get<1>(data[accessInd]), get<2>(data[accessInd]));
/* Left Face */
vertices = {
{{ 0, 0, 1 }, {}, { 0, 1 }, { 0, 1 }},
{{ 1, 0, 1 }, {}, { 1, 1 }, { 1, 1 }},
{{ 1, 1, 1 }, {}, { 1, 0 }, { 1, 0 }},
{{ 0, 1, 1 }, {}, { 0, 0 }, { 0, 0 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<int>(textureRefs.size() - 1), 4));
MeshPart frontMeshPart(vertices, indices, textureRefs[accessInd], blendInds[accessInd], blendMaskRefs[accessInd]);
blockModel.parts[static_cast<int>(EVec::FRONT)].push_back(frontMeshPart);
//Back Face
accessInd = (std::max)(0, (std::min)(static_cast<i32>(data.size() - 1), 4));
parts[static_cast<i32>(EVec::LEFT)].emplace_back(vertices, indices,
get<0>(data[accessInd]), get<1>(data[accessInd]), get<2>(data[accessInd]));
/* Back Face */
vertices = {
{{ 0, 0, 0 }, {}, { 0, 1 }, { 0, 1 }},
{{ 0, 1, 0 }, {}, { 0, 0 }, { 0, 0 }},
{{ 1, 1, 0 }, {}, { 1, 0 }, { 1, 0 }},
{{ 1, 0, 0 }, {}, { 1, 1 }, { 1, 1 }}};
indices = { 0, 1, 2, 2, 3, 0 };
accessInd = (std::max)(0, (std::min)(static_cast<int>(textureRefs.size() - 1), 5));
MeshPart backMeshPart(vertices, indices, textureRefs[accessInd], blendInds[accessInd], blendMaskRefs[accessInd]);
blockModel.parts[static_cast<int>(EVec::BACK)].push_back(backMeshPart);
return blockModel;
accessInd = (std::max)(0, (std::min)(static_cast<i32>(data.size() - 1), 5));
parts[static_cast<i32>(EVec::RIGHT)].emplace_back(vertices, indices,
get<0>(data[accessInd]), get<1>(data[accessInd]), get<2>(data[accessInd]));
}

View File

@ -1,22 +1,16 @@
//
// Created by aurailus on 13/08/19.
//
#pragma once
#include <set>
#include <vector>
#include "MeshPart.h"
#include "util/Types.h"
struct BlockModel {
std::array<std::vector<MeshPart>, 7> parts {};
std::vector<std::pair<MeshMod, float>> meshMods {};
std::set<std::shared_ptr<AtlasRef>> textureRefs {};
BlockModel() = default;
explicit BlockModel(vec<std::tuple<AtlasTexture, optional<u32>, optional<AtlasTexture>>> data);
vec<AtlasTexture> textures {};
array<vec<MeshPart>, 7> parts {};
vec<std::pair<MeshMod, f32>> meshMods {};
bool culls = false;
bool visible = false;
static BlockModel createCube(std::vector<std::shared_ptr<AtlasRef>> textureRefs,
std::vector<unsigned int> blendInds, std::vector<std::shared_ptr<AtlasRef>> blendMaskRefs);
};

View File

@ -1,70 +1,71 @@
//
// Created by aurailus on 02/12/18.
//
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/normal.hpp>
#include "MeshPart.h"
#include "game/atlas/asset/AtlasRef.h"
MeshPart::MeshPart(const std::vector<BlockModelVertex>& vertices, const std::vector<unsigned int>& indices,
std::shared_ptr<AtlasRef> texture, unsigned int blendInd, std::shared_ptr<AtlasRef> blendMask) :
MeshPart::MeshPart(const vec<BlockModelVertex>& vertices, const vec<u32>& indices,
optional<AtlasTexture> texture, optional<u32> blendInd, optional<AtlasTexture> blendTexture) :
vertices(vertices),
indices(indices),
texture(texture),
blendInd(blendInd),
blendMask(blendMask) {
blendTexture(blendTexture) {
//We assume these vertex structs do not have normals, and generate them here from the triangle information.
//To do this, we have to assume that each group of 3 indices is a triangle (which it would be hard for it to not be)
//and that no vertexes are shared on corners or places where vectors should be interpolated.
/*
* We assume these vertex structs do not have normals, and generate them here from the triangle information.
* To do this, we have to assume that each group of 3 indices is a triangle (which it would be hard for it to not be)
* and that no vertexes are shared on corners or places where vectors should be interpolated.
*/
//Iterate through the indices to find all used vertices to add normals and adjust texture coordinates.
for (int i = 0; i < this->indices.size() / 3; i++) {
//Get the three vertices
BlockModelVertex& p1 = this->vertices[this->indices[i * 3]];
BlockModelVertex& p2 = this->vertices[this->indices[i * 3 + 1]];
BlockModelVertex& p3 = this->vertices[this->indices[i * 3 + 2]];
/* Iterate through the indices to find all used vertices to add normals and adjust texture coordinates. */
for (usize i = 0; i < this->indices.size() / 3; i++) {
/* Get the three vertices. */
let& p1 = this->vertices[this->indices[i * 3]];
let& p2 = this->vertices[this->indices[i * 3 + 1]];
let& p3 = this->vertices[this->indices[i * 3 + 2]];
//Get the normal of the formed triangle
glm::vec3 normal = glm::triangleNormal(p1.pos, p2.pos, p3.pos);
/* Get the normal of the formed triangle. */
vec3 normal = glm::triangleNormal(p1.pos, p2.pos, p3.pos);
//Set the normal on the vertices
/* Set the normal on the vertices. */
p1.nml = normal;
p2.nml = normal;
p3.nml = normal;
}
//If the MeshPart is being initialized on the client with an AtlasRef to base it's values off of,
//it will set the UVs to the coordinates of the texture relative to the TextureAtlas.
/*
* If the MeshPart is being initialized on the client with an AtlasRef to base its values off of,
* it will set the UVs to the coordinates of the texture relative to the TextureAtlas.
*/
if (texture) {
auto uv = texture->uv;
auto uv = texture->getUVPos();
//Iterate through the vertices to adjust the texture coordinates to fit the textureAtlas.
for (BlockModelVertex& vertex : this->vertices) {
//Store the old positions in texUVs
/* Iterate through the vertices to adjust the texture coordinates to fit the textureAtlas. */
for (BlockModelVertex& vertex: this->vertices) {
/* Store the old positions in texUVs. */
vertex.texUVs.x = vertex.tex.x;
vertex.texUVs.y = vertex.tex.y;
//Generate solid coordinates for the defs positions
/* Generate solid coordinates for the defs positions. */
vertex.tex.x = uv.x + ((uv.z - uv.x) * vertex.tex.x);
vertex.tex.y = uv.y + ((uv.w - uv.y) * vertex.tex.y);
if (blendMask) {
auto bUV = blendMask->uv;
if (blendTexture) {
auto bUV = blendTexture->getUVPos();
//Store the old positions in blendMaskUVs
/* Store the old positions in blendMaskUVs. */
vertex.blendMaskUVs.x = vertex.blendMask.x;
vertex.blendMaskUVs.y = vertex.blendMask.y;
//Generate solid coordinates for the defs positions
/* Generate solid coordinates for the defs positions. */
vertex.blendMask.x = bUV.x + ((bUV.z - bUV.x) * vertex.blendMask.x);
vertex.blendMask.y = bUV.y + ((bUV.w - bUV.y) * vertex.blendMask.y);
}
else {
// Negative One denotes full blend on fragment shader.
/* Negative One denotes full blend on fragment shader. */
vertex.blendMask.x = -1;
vertex.blendMask.y = -1;
}

View File

@ -1,29 +1,22 @@
//
// Created by aurailus on 02/12/18.
//
#pragma once
#include <vector>
#include <memory>
#include "ShaderMod.h"
#include "BlockModelVertex.h"
class AtlasRef;
#include "util/Types.h"
#include "game/def/mesh/ShaderMod.h"
#include "game/atlas/asset/AtlasTexture.h"
#include "game/def/mesh/BlockModelVertex.h"
struct MeshPart {
MeshPart(const std::vector<BlockModelVertex>& vertices, const std::vector<unsigned int>& indices,
std::shared_ptr<AtlasRef> texture, unsigned int blendInd, std::shared_ptr<AtlasRef> blendMask);
MeshPart(const vec<BlockModelVertex>& vertices, const vec<u32>& indices,
optional<AtlasTexture> texture = std::nullopt, optional<u32> blendInd = std::nullopt,
optional<AtlasTexture> blendTexture = std::nullopt);
std::vector<BlockModelVertex> vertices;
std::vector<unsigned int> indices;
vec<BlockModelVertex> vertices;
vec<u32> indices;
std::shared_ptr<AtlasRef> texture;
unsigned int blendInd = 0;
std::shared_ptr<AtlasRef> blendMask;
optional<u32> blendInd;
optional<AtlasTexture> texture;
optional<AtlasTexture> blendTexture;
ShaderMod shaderMod = ShaderMod::NONE;
float modValue = 0;
f32 modValue = 0;
};

View File

@ -9,21 +9,12 @@
#include "game/def/CraftItemDef.h"
#include "game/def/mesh/BlockModel.h"
#include "game/def/mesh/SelectionBox.h"
#include "game/atlas/asset/AtlasTexture.h"
#include "game/atlas/LocalDefinitionAtlas.h"
#include "game/atlas/ServerDefinitionAtlas.h"
namespace RegisterBlock {
namespace {
struct TintParserData {
string tex;
optional<u32> tint;
optional<string> mask;
};
using TintParser = StringParser<TintParserData>;
TintParser parser {};
bool parserReady = false;
/**
* Takes a lua selection box table list, and returns a vector of selection boxes.
@ -32,16 +23,15 @@ namespace RegisterBlock {
* @returns a vector of selection boxes.
*/
static std::vector<SelectionBox> parseBoxes(sol::table boxesTable) {
std::vector<SelectionBox> boxes{};
static vec<SelectionBox> parseBoxes(sol::table boxesTable) {
vec<SelectionBox> boxes{};
for (auto pair : boxesTable) {
if (!pair.second.is<sol::table>()) throw std::runtime_error("must be a table");
sol::table table = pair.second;
if (table.size() != 6) throw std::runtime_error("must contain exactly 6 elements");
boxes.emplace_back(glm::vec3{ table[1], table[2], table[3] },
glm::vec3{ table[4], table[5], table[6] });
boxes.emplace_back(vec3 { table[1], table[2], table[3] }, vec3 { table[4], table[5], table[6] });
}
return boxes;
@ -60,8 +50,8 @@ namespace RegisterBlock {
static std::pair<BlockModel, BlockModel>
createBlockModel(sol::table blockTable, sol::table blockModels, TextureAtlas* atlas) {
// Get the specified block model
auto modelStr = blockTable.get_or<std::string>("model", "zepha:base:block");
auto modelOpt = blockModels.get<sol::optional<sol::table>>(modelStr);
let modelStr = blockTable.get_or<string>("model", "zepha:base:block");
let modelOpt = blockModels.get<optional<sol::table>>(modelStr);
if (!modelOpt) throw std::runtime_error("Non-existent model \"" + modelStr + "\" specified");
sol::table modelTable = *modelOpt;
@ -72,55 +62,55 @@ namespace RegisterBlock {
model.visible = blockTable.get_or("visible", true);
// Convert textures and low-def textures into vectors
auto texturesOpt = blockTable.get<sol::optional<sol::table >>("textures");
auto ldTexturesOpt = blockTable.get<sol::optional<sol::table >>("lowdef_textures");
let texturesOpt = blockTable.get<sol::optional<sol::table>>("textures");
let ldTexturesOpt = blockTable.get<sol::optional<sol::table>>("lowdef_textures");
if (!texturesOpt) throw std::runtime_error("Missing textures property");
std::vector<std::string> textures;
for (auto pair : *texturesOpt) {
if (!pair.second.is<std::string>())
throw std::runtime_error("textures table contains non-string value");
textures.push_back(pair.second.as<std::string>());
vec<string> textures;
for (let& pair : *texturesOpt) {
if (!pair.second.is<string>()) throw std::runtime_error(
"textures table contains non-string value");
textures.push_back(pair.second.as<string>());
}
if (textures.size() == 0) textures.push_back("_missing");
if (textures.empty()) textures.push_back("_missing");
std::vector<std::string> lowdef_textures;
vec<string> lowdef_textures;
if (!ldTexturesOpt) lowdef_textures = textures;
else {
for (auto pair : *ldTexturesOpt) {
if (!pair.second.is<std::string>())
throw std::runtime_error("lowdef_textures table has non-string value!");
lowdef_textures.push_back(pair.second.as<std::string>());
for (let& pair : *ldTexturesOpt) {
if (!pair.second.is<string>()) throw std::runtime_error(
"lowdef_textures table has non-string value!");
lowdef_textures.push_back(pair.second.as<string>());
}
}
if (lowdef_textures.size() == 0) lowdef_textures.push_back("_missing");
if (lowdef_textures.empty()) lowdef_textures.push_back("_missing");
// Parse through mesh mods and add them
sol::optional<sol::table> meshModTable = modelTable.get<sol::optional<sol::table>>("mesh_mods");
optional<sol::table> meshModTable = modelTable.get<optional<sol::table>>("mesh_mods");
if (meshModTable) {
for (auto& modEntry : *meshModTable) {
auto modTable = modEntry.second.as<sol::table>();
std::string meshMod = modTable.get_or<std::string>("type", "none");
string meshMod = modTable.get_or<string>("type", "none");
if (meshMod == "none") continue;
else if (meshMod == "offset_x")
model.meshMods.emplace_back(MeshMod::OFFSET_X, modTable.get_or<float>("amplitude", 1));
model.meshMods.emplace_back(MeshMod::OFFSET_X, modTable.get_or<f32>("amplitude", 1));
else if (meshMod == "offset_y")
model.meshMods.emplace_back(MeshMod::OFFSET_Y, modTable.get_or<float>("amplitude", 1));
model.meshMods.emplace_back(MeshMod::OFFSET_Y, modTable.get_or<f32>("amplitude", 1));
else if (meshMod == "offset_z")
model.meshMods.emplace_back(MeshMod::OFFSET_Z, modTable.get_or<float>("amplitude", 1));
model.meshMods.emplace_back(MeshMod::OFFSET_Z, modTable.get_or<f32>("amplitude", 1));
else if (meshMod == "rotate_x")
model.meshMods.emplace_back(MeshMod::ROTATE_X, modTable.get_or<float>("amplitude", 1));
model.meshMods.emplace_back(MeshMod::ROTATE_X, modTable.get_or<f32>("amplitude", 1));
else if (meshMod == "rotate_y")
model.meshMods.emplace_back(MeshMod::ROTATE_Y, modTable.get_or<float>("amplitude", 1));
model.meshMods.emplace_back(MeshMod::ROTATE_Y, modTable.get_or<f32>("amplitude", 1));
else if (meshMod == "rotate_z")
model.meshMods.emplace_back(MeshMod::ROTATE_Z, modTable.get_or<float>("amplitude", 1));
model.meshMods.emplace_back(MeshMod::ROTATE_Z, modTable.get_or<f32>("amplitude", 1));
}
}
// Parse through all the parts and add them to the model
auto partsOpt = modelTable.get<sol::optional<sol::table>>("parts");
let partsOpt = modelTable.get<optional<sol::table>>("parts");
if (!partsOpt) throw std::runtime_error("blockmodel is missing parts table");
partsOpt->for_each([&](sol::object key, sol::object value) {
@ -128,7 +118,7 @@ namespace RegisterBlock {
if (!value.is<sol::table>()) throw std::runtime_error("meshpart must be a table");
sol::table meshPartTable = value.as<sol::table>();
auto points_optional = meshPartTable.get<sol::optional<sol::table>>("points");
let points_optional = meshPartTable.get<sol::optional<sol::table>>("points");
if (!points_optional) throw std::runtime_error("Meshpart is missing a points table");
sol::table points = *points_optional;
@ -136,20 +126,20 @@ namespace RegisterBlock {
throw std::runtime_error("Points table must contain a multiple of 20 values");
// Populate the Vertices and Indices vectors from the points table
std::vector<BlockModelVertex> vertices;
std::vector<unsigned int> indices;
vec<BlockModelVertex> vertices;
vec<u32> indices;
for (int i = 1; i <= points.size() / 5; i++) {
int offset = (i - 1) * 5 + 1;
for (u32 i = 1; i <= points.size() / 5; i++) {
u32 offset = (i - 1) * 5 + 1;
glm::vec3 pos(points[offset], points[offset + 1], points[offset + 2]);
glm::vec2 tex(points[offset + 3], points[offset + 4]);
vec3 pos(points[offset], points[offset + 1], points[offset + 2]);
vec2 tex(points[offset + 3], points[offset + 4]);
vertices.push_back(BlockModelVertex{ pos, {}, tex, tex, {}, {}});
vertices.push_back(BlockModelVertex { pos, {}, tex, tex, {}, {} });
}
int ind = 0;
for (int i = 1; i <= points.size() / 20; i++) {
u32 ind = 0;
for (u32 i = 1; i <= points.size() / 20; i++) {
indices.push_back(ind);
indices.push_back(ind + 1);
indices.push_back(ind + 2);
@ -159,25 +149,22 @@ namespace RegisterBlock {
ind += 4;
}
// Get the part's texture
int tex = std::max(static_cast<int>(meshPartTable.get_or<float>("tex", 1)), 1);
let data = parser.parse(textures[std::min(tex - 1, (int) textures.size() - 1)]);
u32 blendInd = data.tint ? *data.tint + 1 : 0;
// Get texture, and add texture refs to blockModel if the atlas is provided
u32 tex = std::max(meshPartTable.get_or<u32>("tex", 1), 1u);
optional<u32> tintInd {};
optional<AtlasTexture> textureRef {};
optional<AtlasTexture> tintTextureRef = {};
// Add texture refs to blockModel if the textures table is provided
std::shared_ptr<AtlasRef> textureRef = nullptr, blendMaskRef = nullptr;
if (atlas) {
textureRef = (*atlas)[data.tex];
model.textureRefs.insert(textureRef);
if (blendInd && data.mask) {
blendMaskRef = (*atlas)[*data.mask];
model.textureRefs.insert(blendMaskRef);
}
textureRef = (*atlas)[textures[std::min(tex - 1, static_cast<u32>(textures.size()) - 1)]];
model.textures.push_back(*textureRef);
tintInd = textureRef->getTintInd();
tintTextureRef = textureRef->getTintMask();
}
// Create the meshpart object
MeshPart meshPart(std::move(vertices), std::move(indices), textureRef, blendInd, blendMaskRef);
MeshPart meshPart(std::move(vertices), std::move(indices), textureRef, tintInd, tintTextureRef);
// Add the shader mod, if it exists
sol::optional<sol::table> shaderModTable = meshPartTable.get<sol::optional<sol::table>>("shader_mod");
@ -225,27 +212,18 @@ namespace RegisterBlock {
});
// Create the far model
BlockModel farModel;
BlockModel farModel {};
auto ldRender = blockTable.get_or("lowdef_render", true);
if (atlas) {
std::vector<std::shared_ptr<AtlasRef>> textureRefs;
std::vector<unsigned int> blendInds;
std::vector<std::shared_ptr<AtlasRef>> blendMaskRefs;
vec<std::tuple<AtlasTexture, optional<u32>, optional<AtlasTexture>>> modelData;
for (auto i = 0; i < lowdef_textures.size(); i++) {
let data = parser.parse(lowdef_textures[i]);
u32 blendInd = data.tint ? *data.tint + 1 : 0;
textureRefs.push_back((*atlas)[data.tex]);
blendInds.push_back(blendInd);
blendMaskRefs.push_back(data.mask ? (*atlas)[*data.mask] : nullptr);
for (let i = 0; i < lowdef_textures.size(); i++) {
let textureRef = (*atlas)[lowdef_textures[i]];
modelData.emplace_back(textureRef, textureRef.getTintInd(), textureRef.getTintMask());
}
farModel = BlockModel::createCube(textureRefs, blendInds, blendMaskRefs);
}
else {
farModel = BlockModel::createCube({}, {}, {});
farModel = BlockModel { modelData };
}
farModel.culls = ldRender;
@ -386,28 +364,6 @@ namespace RegisterBlock {
}
}
/**
* Initializes the parser with the necessary parse functions.
* In the future, this could be cleaned up, but as it is it's totally fine.
*/
static void initParser() {
if (parserReady) return;
parser.setUnknownFnsAreLiteral(true);
parser.addFn<u32, TintParserData, optional<TintParserData>>("tint",
[](u32 tint, TintParserData tex, optional<TintParserData> mask) {
return TintParserData { tex.tex, tint, mask ? optional(mask->tex) : std::nullopt };
});
parser.addLiteralFn([](string tex) {
return TintParserData { tex, std::nullopt, std::nullopt };
});
parserReady = true;
}
/**
* Server method to register a block. Calls registerBlock with the necessary parameters.
* Registers a block to the DefinitionAtlas.
@ -417,8 +373,7 @@ namespace RegisterBlock {
* @param identifier - The identifier of the block to register.
*/
static void server(sol::table& core, ServerSubgame& game, const std::string& identifier) {
initParser();
static void server(sol::table& core, ServerSubgame& game, const string& identifier) {
registerBlock(core["registered_blocks"], core["registered_blockmodels"],
identifier, game.getDefs(), nullptr);
}
@ -432,8 +387,7 @@ namespace RegisterBlock {
* @param identifier - The identifier of the block to register.
*/
static void client(sol::table& core, LocalSubgame& game, const std::string& identifier) {
initParser();
static void client(sol::table& core, LocalSubgame& game, const string& identifier) {
registerBlock(core["registered_blocks"], core["registered_blockmodels"],
identifier, game.getDefs(), &game.textures);
}

View File

@ -5,13 +5,13 @@
#include "game/def/BiomeDef.h"
#include "game/ServerSubgame.h"
#include "game/def/CraftItemDef.h"
#include "game/atlas/asset/AtlasTexture.h"
#include "game/atlas/LocalDefinitionAtlas.h"
#include "game/atlas/ServerDefinitionAtlas.h"
namespace RegisterItem {
namespace {
/**
* Attempts to add a callback of the type specified from the block table provided
* to the block definition provided. Does nothing if the block table doesn't have a callback of said type.
@ -23,9 +23,9 @@ namespace RegisterItem {
*/
static void addCallback(CraftItemDef& itemDef, sol::table& itemTable,
const std::string& name, CraftItemDef::Callback cbType) {
const string& name, CraftItemDef::Callback cbType) {
auto cb = itemTable.get<sol::optional<sol::protected_function>>(name);
auto cb = itemTable.get<optional<sol::protected_function>>(name);
if (cb) itemDef.callbacks.insert({ cbType, *cb });
}
@ -40,26 +40,25 @@ namespace RegisterItem {
* @param atlas - The Texture Atlas, pass in if running on the client, otherwise pass nullptr.
*/
static void
registerItem(sol::table items, const std::string& identifier, DefinitionAtlas& defs, TextureAtlas* atlas) {
static void registerItem(sol::table items, const string& identifier,
DefinitionAtlas& defs, TextureAtlas* atlas) {
sol::table itemTable = items[identifier];
// Basic item properties
auto nameOpt = itemTable.get<sol::optional<std::string>>("name");
auto texturesOpt = itemTable.get<sol::optional<sol::table>>("textures");
auto maxStack = itemTable.get_or("stack", 64);
let nameOpt = itemTable.get<optional<string>>("name");
let texturesOpt = itemTable.get<optional<sol::table>>("textures");
let maxStack = itemTable.get_or("stack", 64);
if (!nameOpt) throw std::runtime_error(identifier + " is missing name property!");
if (!texturesOpt) throw std::runtime_error(identifier + " is missing textures property!");
//Convert Textures Table to Vector
std::vector<std::string> textures;
std::vector<std::shared_ptr<AtlasRef>> textureRefs;
vec<string> textures;
vec<AtlasTexture> textureRefs;
for (auto pair : *texturesOpt) {
if (!pair.second.is<std::string>()) throw std::runtime_error("textures table has non-string value");
textures.push_back(pair.second.as<std::string>());
if (atlas) textureRefs.push_back((*atlas)[pair.second.as<std::string>()]);
if (!pair.second.is<string>()) throw std::runtime_error("textures table has non-string value");
textures.push_back(pair.second.as<string>());
if (atlas) textureRefs.push_back((*atlas)[pair.second.as<string>()]);
}
if (textures.size() == 0) {
textures.push_back("_missing");
@ -89,7 +88,6 @@ namespace RegisterItem {
}
}
/**
* Server method to register an item. Calls registerItem with the necessary parameters.
* Registers an item to the DefinitionAtlas.
@ -99,11 +97,10 @@ namespace RegisterItem {
* @param identifier - The identifier of the item to register.
*/
static void server(sol::table& core, ServerSubgame& game, const std::string& identifier) {
static void server(sol::table& core, ServerSubgame& game, const string& identifier) {
registerItem(core["registered_items"], identifier, game.getDefs(), nullptr);
}
/**
* Client method to register an item. Calls registerItem with the necessary parameters.
* Registers an item to the DefinitionAtlas.
@ -113,7 +110,7 @@ namespace RegisterItem {
* @param identifier - The identifier of the item to register.
*/
static void client(sol::table& core, LocalSubgame& game, const std::string& identifier) {
static void client(sol::table& core, LocalSubgame& game, const string& identifier) {
registerItem(core["registered_items"], identifier, game.getDefs(), &game.textures);
}
}

View File

@ -79,10 +79,10 @@ private:
vec<ivec3> generateOrder;
/** The range in mapblocks to generate around clients. */
const ivec2 mapBlockGenRange = { 4, 4 };
const ivec2 mapBlockGenRange = { 6, 6 };
/** The range in mapblocks to send to clients. */
const ivec2 sendRange = { 4, 4 };
const ivec2 sendRange = { 6, 6 };
/** The range around clients that chunks should be updated. */
const ivec2 activeChunkRange = { 4, 4 };

View File

@ -101,7 +101,7 @@ private:
u32 mapBlockScanY = 0;
const static u32 MAPBLOCK_SCAN_Y_INTERVAL = 8;
const ivec2 retainMapBlockRange = { 4, 4 };
const ivec2 retainMapBlockRange = { 6, 6 };
i64 entityInd = -1;
};

View File

@ -20,11 +20,6 @@ class DefinitionAtlas;
class MapGen {
public:
/** The precision of the Biome map, as a divisor of the chunk size. */
// constexpr static u8 BIOP = 4;
/** The precision of the Terrain maps, as a divisor of the chunk size. */
// constexpr static u8 TERP = 4;
/** A type alias for the type the map of Chunks stored in the Job. */
typedef std::unordered_map<ivec3, sptr<Chunk>, Vec::ivec3> ChunkMap;

BIN
subgames/zeus/key_mock.xcf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -5,13 +5,13 @@ zepha.register_block(":grass", {
textures = {
"tint(0, zeus:default:grass_top)",
"zeus:default:dirt",
"tint(0, zeus:default:grass_side_under, zeus:default:grass_side_under_mask)",
"(zeus:default:dirt, tint(0, zeus:default:grass_side))",
"tint(0, zeus:default:grass_floating)",
},
lowdef_textures = {
"tint(0, zeus:default:grass_top)",
"zeus:default:dirt",
"tint(0, zeus:default:grass_side_ld, zeus:default:grass_side_ld_mask)"
"(zeus:default:dirt, tint(0, zeus:default:grass_floating))"
},
tool_props = {

View File

@ -8,7 +8,7 @@ zepha.register_block(":leaves", {
"zeus:default:leaves_puff"
},
lowdef_textures = {
"tint(0, zeus:default:leaves_opaque)",
"zeus:default:leaves_opaque",
},
tool_props = {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 521 B

View File

Before

Width:  |  Height:  |  Size: 739 B

After

Width:  |  Height:  |  Size: 739 B

View File

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 255 B

View File

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 272 B

View File

Before

Width:  |  Height:  |  Size: 338 B

After

Width:  |  Height:  |  Size: 338 B

View File

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 433 B

View File

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 459 B

View File

Before

Width:  |  Height:  |  Size: 460 B

After

Width:  |  Height:  |  Size: 460 B

View File

Before

Width:  |  Height:  |  Size: 492 B

After

Width:  |  Height:  |  Size: 492 B

View File

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 490 B

View File

Before

Width:  |  Height:  |  Size: 792 B

After

Width:  |  Height:  |  Size: 792 B

View File

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 545 B

View File

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 208 B

After

Width:  |  Height:  |  Size: 208 B

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 804 B

After

Width:  |  Height:  |  Size: 804 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 866 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 469 B

After

Width:  |  Height:  |  Size: 469 B

Some files were not shown because too many files have changed in this diff Show More