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
|
@ -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() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {};
|
||||
};
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 {};
|
||||
};
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {};
|
||||
};
|
||||
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
};
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Before Width: | Height: | Size: 886 B After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 495 B |
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 457 B |
Before Width: | Height: | Size: 672 B After Width: | Height: | Size: 672 B |
Before Width: | Height: | Size: 747 B After Width: | Height: | Size: 747 B |
Before Width: | Height: | Size: 690 B After Width: | Height: | Size: 690 B |
Before Width: | Height: | Size: 719 B After Width: | Height: | Size: 719 B |
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 884 B |
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 668 B After Width: | Height: | Size: 668 B |
Before Width: | Height: | Size: 521 B After Width: | Height: | Size: 521 B |
Before Width: | Height: | Size: 739 B After Width: | Height: | Size: 739 B |
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 740 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 460 B |
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 492 B |
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 680 B After Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 792 B After Width: | Height: | Size: 792 B |
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 631 B After Width: | Height: | Size: 631 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 545 B |
Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 712 B |
After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 208 B |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 804 B After Width: | Height: | Size: 804 B |
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 480 B After Width: | Height: | Size: 480 B |
Before Width: | Height: | Size: 523 B After Width: | Height: | Size: 523 B |
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 572 B |
Before Width: | Height: | Size: 658 B After Width: | Height: | Size: 658 B |
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 740 B |
Before Width: | Height: | Size: 431 B After Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 866 B After Width: | Height: | Size: 866 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 469 B After Width: | Height: | Size: 469 B |
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 474 B |