#include "ModelBatch.hpp" #include "assets/assets_util.hpp" #include "graphics/core/Mesh.hpp" #include "graphics/core/Model.hpp" #include "graphics/core/Atlas.hpp" #include "graphics/core/Texture.hpp" #include "assets/Assets.hpp" #include "window/Window.hpp" #include "voxels/Chunks.hpp" #include "lighting/Lightmap.hpp" #include "settings.hpp" #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include /// xyz, uv, color, compressed lights inline constexpr uint VERTEX_SIZE = 9; static const vattr attrs[] = { {3}, {2}, {3}, {1}, {0} }; inline constexpr glm::vec3 X(1, 0, 0); inline constexpr glm::vec3 Y(0, 1, 0); inline constexpr glm::vec3 Z(0, 0, 1); struct DecomposedMat4 { glm::vec3 scale; glm::mat3 rotation; glm::vec3 translation; glm::vec3 skew; glm::vec4 perspective; }; static glm::mat4 extract_rotation(glm::mat4 matrix) { DecomposedMat4 decomposed = {}; glm::quat rotation; glm::decompose( matrix, decomposed.scale, rotation, decomposed.translation, decomposed.skew, decomposed.perspective ); return glm::toMat3(rotation); } ModelBatch::ModelBatch( size_t capacity, Assets* assets, Chunks* chunks, const EngineSettings* settings ) : buffer(std::make_unique(capacity * VERTEX_SIZE)), capacity(capacity), index(0), mesh(std::make_unique(buffer.get(), 0, attrs)), assets(assets), chunks(chunks), settings(settings) { const ubyte pixels[] = { 255, 255, 255, 255, }; ImageData image(ImageFormat::rgba8888, 1, 1, pixels); blank = Texture::from(&image); } ModelBatch::~ModelBatch() = default; void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, const glm::mat3& rotation, glm::vec3 tint, const texture_names_map* varTextures, bool backlight) { glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); gpos += lightsOffset; light_t light = chunks->getLight( std::floor(gpos.x), std::floor(std::min(CHUNK_H-1.0f, gpos.y)), std::floor(gpos.z)); light_t minIntensity = backlight ? 1 : 0; glm::vec4 lights( glm::max(Lightmap::extract(light, 0), minIntensity) / 15.0f, glm::max(Lightmap::extract(light, 1), minIntensity) / 15.0f, glm::max(Lightmap::extract(light, 2), minIntensity) / 15.0f, glm::max(Lightmap::extract(light, 3), minIntensity) / 15.0f ); setTexture(mesh.texture, varTextures); size_t vcount = mesh.vertices.size(); const auto& vertexData = mesh.vertices.data(); for (size_t i = 0; i < vcount / 3; i++) { if (index + VERTEX_SIZE * 3 > capacity * VERTEX_SIZE) { flush(); } for (size_t j = 0; j < 3; j++) { const auto vert = vertexData[i * 3 + j]; auto norm = rotation * vert.normal; float d = glm::dot(norm, SUN_VECTOR); d = 0.8f + d * 0.2f; vertex(matrix * glm::vec4(vert.coord, 1.0f), vert.uv, lights*d, tint); } } } void ModelBatch::draw(glm::mat4 matrix, glm::vec3 tint, const model::Model* model, const texture_names_map* varTextures) { for (const auto& mesh : model->meshes) { entries.push_back({ matrix, extract_rotation(matrix), tint, &mesh, varTextures }); } } void ModelBatch::render() { std::sort(entries.begin(), entries.end(), [](const DrawEntry& a, const DrawEntry& b) { return a.mesh->texture < b.mesh->texture; } ); bool backlight = settings->graphics.backlight.get(); for (auto& entry : entries) { draw( *entry.mesh, entry.matrix, entry.rotation, entry.tint, entry.varTextures, backlight ); } flush(); entries.clear(); } void ModelBatch::setLightsOffset(const glm::vec3& offset) { lightsOffset = offset; } void ModelBatch::setTexture(const std::string& name, const texture_names_map* varTextures) { if (varTextures && name.at(0) == '$') { const auto& found = varTextures->find(name); if (found == varTextures->end()) { return setTexture(nullptr); } else { return setTexture(found->second, varTextures); } } auto textureRegion = util::get_texture_region(*assets, name, "blocks:notfound"); setTexture(textureRegion.texture); region = textureRegion.region; } void ModelBatch::setTexture(const Texture* texture) { if (texture == nullptr) { texture = blank.get(); } if (texture != this->texture) { flush(); } this->texture = texture; region = UVRegion {0.0f, 0.0f, 1.0f, 1.0f}; } void ModelBatch::flush() { if (index == 0) { return; } if (texture == nullptr) { texture = blank.get(); } texture->bind(); mesh->reload(buffer.get(), index / VERTEX_SIZE); mesh->draw(); index = 0; }