#ifndef VOXEL_DISTANCE_NORMALMAPS_H #define VOXEL_DISTANCE_NORMALMAPS_H #include "../util/fixed_array.h" #include "../util/math/vector3f.h" #include "../util/span.h" #include #include class Texture2DArray; class Texture2D; class Image; namespace zylann::voxel { class VoxelGenerator; struct VoxelDataLodMap; // TODO This system could be extended to more than just normals // - Texturing data // - Color // - Some kind of depth (could be useful to fake water from far away) // UV-mapping a voxel mesh is not trivial, but if mapping is required, an alternative is to subdivide the mesh into a // grid of cells (we can use Transvoxel cells). In each cell, pick an axis-aligned projection working best with // triangles of the cell using the average of their normals. A tile can then be generated by projecting its pixels on // triangles, and be stored in an atlas. A shader can then read the atlas using a lookup texture to find the tile. struct NormalMapSettings { // If enabled, an atlas of normalmaps will be generated for each cell of the voxel mesh, in order to add // more visual details using a shader. bool enabled = false; // LOD index from which normalmaps will start being generated. uint8_t begin_lod_index = 2; // Tile resolution that will be used starting from the beginning LOD. Resolution will double at each following // LOD index. uint8_t tile_resolution_min = 4; uint8_t tile_resolution_max = 8; // If enabled, encodes normalmaps using octahedral compression, which trades a bit of quality for // significantly reduced memory usage (using 2 bytes per pixel instead of 3). bool octahedral_encoding_enabled = false; }; unsigned int get_virtual_texture_tile_resolution_for_lod(const NormalMapSettings &settings, unsigned int lod_index); struct NormalMapData { // Encoded normals std::vector normals; struct Tile { uint8_t x; uint8_t y; uint8_t z; uint8_t axis; }; std::vector tiles; inline void clear() { normals.clear(); tiles.clear(); } }; // To hold the current cell only. Not optimized for space. May use a more efficient structure per implementation of // `ICellIterator`. struct CurrentCellInfo { static const unsigned int MAX_TRIANGLES = 5; FixedArray triangle_begin_indices; uint32_t triangle_count = 0; Vector3i position; }; class ICellIterator { public: virtual ~ICellIterator() {} virtual unsigned int get_count() const = 0; virtual bool next(CurrentCellInfo &info) = 0; }; // For each non-empty cell of the mesh, choose an axis-aligned projection based on triangle normals in the cell. // Sample voxels inside the cell to compute a tile of world space normals from the SDF. void compute_normalmap(ICellIterator &cell_iterator, Span mesh_vertices, Span mesh_normals, Span mesh_indices, NormalMapData &normal_map_data, unsigned int tile_resolution, VoxelGenerator &generator, const VoxelDataLodMap *voxel_data, Vector3i origin_in_voxels, unsigned int lod_index, bool octahedral_encoding); struct NormalMapImages { Vector> atlas; Ref lookup; }; struct NormalMapTextures { Ref atlas; Ref lookup; }; NormalMapImages store_normalmap_data_to_images( const NormalMapData &data, unsigned int tile_resolution, Vector3i block_size, bool octahedral_encoding); // Converts normalmap data into textures. They can be used in a shader to apply normals and obtain extra visual details. // This may not be allowed to run in a different thread than the main thread if the renderer is not using Vulkan. NormalMapTextures store_normalmap_data_to_textures(const NormalMapImages &data); struct VirtualTextureOutput { // Normalmap atlas used for smooth voxels. // If textures can't be created from threads, images are returned instead. NormalMapImages normalmap_images; NormalMapTextures normalmap_textures; // Can be false if textures are computed asynchronously. Will become true when it's done (and not change after). std::atomic_bool valid; }; } // namespace zylann::voxel #endif // VOXEL_DISTANCE_NORMALMAPS_H