mirror of
https://github.com/minetest/minetest.git
synced 2024-12-23 22:52:25 +01:00
Generalize mesh chunking, and make it configurable. (#13179)
* Generalize mesh chunking. Set 3x3x3 chunks. * Make mesh chunk size configurable... Default to 1 (off). * Extract all mesh grid maths into a dedicated class --------- Co-authored-by: x2048 <codeforsmile@gmail.com>
This commit is contained in:
parent
56d2567b5d
commit
d3a6ee00e6
@ -1713,6 +1713,13 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc
|
||||
# Warning: This option is EXPERIMENTAL!
|
||||
autoscale_mode (Autoscaling mode) enum disable disable,enable,force
|
||||
|
||||
# Side length of a cube of map blocks that the client will consider together
|
||||
# when generating meshes.
|
||||
# Larger values increase the utilization of the GPU by reducing the number of
|
||||
# draw calls, benefiting especially high-end GPUs.
|
||||
# Systems with a low-end GPU (or no GPU) would benefit from smaller values.
|
||||
client_mesh_chunk (Client Mesh Chunksize) int 1 1 16
|
||||
|
||||
[**Font]
|
||||
|
||||
font_bold (Font bold by default) bool false
|
||||
|
@ -143,6 +143,7 @@ Client::Client(
|
||||
}
|
||||
|
||||
m_cache_save_interval = g_settings->getU16("server_map_save_interval");
|
||||
m_mesh_grid = { g_settings->getU16("client_mesh_chunk") };
|
||||
}
|
||||
|
||||
void Client::migrateModStorage()
|
||||
@ -564,7 +565,7 @@ void Client::step(float dtime)
|
||||
MapBlock *block = sector->getBlockNoCreateNoEx(r.p.Y);
|
||||
|
||||
// The block in question is not visible (perhaps it is culled at the server),
|
||||
// create a blank block just to hold the 2x2x2 mesh.
|
||||
// create a blank block just to hold the chunk's mesh.
|
||||
// If the block becomes visible later it will replace the blank block.
|
||||
if (!block && r.mesh)
|
||||
block = sector->createBlankBlock(r.p.Y);
|
||||
@ -607,10 +608,10 @@ void Client::step(float dtime)
|
||||
v3s16 ofs;
|
||||
|
||||
// See also mapblock_mesh.cpp for the code that creates the array of minimap blocks.
|
||||
for (ofs.Z = 0; ofs.Z <= 1; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y <= 1; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X <= 1; ofs.X++) {
|
||||
size_t i = ofs.Z * 4 + ofs.Y * 2 + ofs.X;
|
||||
for (ofs.Z = 0; ofs.Z < m_mesh_grid.cell_size; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y < m_mesh_grid.cell_size; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X < m_mesh_grid.cell_size; ofs.X++) {
|
||||
size_t i = m_mesh_grid.getOffsetIndex(ofs);
|
||||
if (i < minimap_mapblocks.size() && minimap_mapblocks[i])
|
||||
m_minimap->addBlock(r.p + ofs, minimap_mapblocks[i]);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "network/peerhandler.h"
|
||||
#include "gameparams.h"
|
||||
#include <fstream>
|
||||
#include "util/numeric.h"
|
||||
|
||||
#define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f
|
||||
|
||||
@ -437,6 +438,11 @@ public:
|
||||
{
|
||||
return m_env.getLocalPlayer()->formspec_prepend;
|
||||
}
|
||||
inline MeshGrid getMeshGrid()
|
||||
{
|
||||
return m_mesh_grid;
|
||||
}
|
||||
|
||||
private:
|
||||
void loadMods();
|
||||
|
||||
@ -602,4 +608,7 @@ private:
|
||||
u32 m_csm_restriction_noderange = 8;
|
||||
|
||||
std::unique_ptr<ModChannelMgr> m_modchannel_mgr;
|
||||
|
||||
// The number of blocks the client will combine for mesh generation.
|
||||
MeshGrid m_mesh_grid;
|
||||
};
|
||||
|
@ -304,6 +304,7 @@ void ClientMap::updateDrawList()
|
||||
blocks_seen.getChunk(camera_block).getBits(camera_block) = 0x07; // mark all sides as visible
|
||||
|
||||
std::set<v3s16> shortlist;
|
||||
MeshGrid mesh_grid = m_client->getMeshGrid();
|
||||
|
||||
// Recursively walk the space and pick mapblocks for drawing
|
||||
while (blocks_to_consider.size() > 0) {
|
||||
@ -330,7 +331,6 @@ void ClientMap::updateDrawList()
|
||||
|
||||
MapBlockMesh *mesh = block ? block->mesh : nullptr;
|
||||
|
||||
|
||||
// Calculate the coordinates for range and frutum culling
|
||||
v3f mesh_sphere_center;
|
||||
f32 mesh_sphere_radius;
|
||||
@ -376,13 +376,21 @@ void ClientMap::updateDrawList()
|
||||
continue;
|
||||
}
|
||||
|
||||
// Block meshes are stored in blocks where all coordinates are even (lowest bit set to 0)
|
||||
// Add them to the de-dup set.
|
||||
shortlist.emplace(block_coord.X & ~1, block_coord.Y & ~1, block_coord.Z & ~1);
|
||||
// All other blocks we can grab and add to the keeplist right away.
|
||||
if (block) {
|
||||
m_keeplist.push_back(block);
|
||||
if (mesh_grid.cell_size > 1) {
|
||||
// Block meshes are stored in the corner block of a chunk
|
||||
// (where all coordinate are divisible by the chunk size)
|
||||
// Add them to the de-dup set.
|
||||
shortlist.emplace(mesh_grid.getMeshPos(block_coord.X), mesh_grid.getMeshPos(block_coord.Y), mesh_grid.getMeshPos(block_coord.Z));
|
||||
// All other blocks we can grab and add to the keeplist right away.
|
||||
if (block) {
|
||||
m_keeplist.push_back(block);
|
||||
block->refGrab();
|
||||
}
|
||||
}
|
||||
else if (mesh) {
|
||||
// without mesh chunking we can add the block to the drawlist
|
||||
block->refGrab();
|
||||
m_drawlist.emplace(block_coord, block);
|
||||
}
|
||||
|
||||
// Decide which sides to traverse next or to block away
|
||||
@ -485,7 +493,7 @@ void ClientMap::updateDrawList()
|
||||
|
||||
g_profiler->avg("MapBlocks shortlist [#]", shortlist.size());
|
||||
|
||||
assert(m_drawlist.empty());
|
||||
assert(m_drawlist.empty() || shortlist.empty());
|
||||
for (auto pos : shortlist) {
|
||||
MapBlock * block = getBlockNoCreateNoEx(pos);
|
||||
if (block) {
|
||||
@ -612,6 +620,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
||||
|
||||
auto is_frustum_culled = m_client->getCamera()->getFrustumCuller();
|
||||
|
||||
const MeshGrid mesh_grid = m_client->getMeshGrid();
|
||||
for (auto &i : m_drawlist) {
|
||||
v3s16 block_pos = i.first;
|
||||
MapBlock *block = i.second;
|
||||
@ -740,7 +749,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
||||
material.TextureLayer[ShadowRenderer::TEXTURE_LAYER_SHADOW].Texture = nullptr;
|
||||
}
|
||||
|
||||
v3f block_wpos = intToFloat(descriptor.m_pos / 8 * 8 * MAP_BLOCKSIZE, BS);
|
||||
v3f block_wpos = intToFloat(mesh_grid.getMeshPos(descriptor.m_pos) * MAP_BLOCKSIZE, BS);
|
||||
m.setTranslation(block_wpos - offset);
|
||||
|
||||
driver->setTransform(video::ETS_WORLD, m);
|
||||
@ -978,6 +987,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
||||
return;
|
||||
}
|
||||
|
||||
const MeshGrid mesh_grid = m_client->getMeshGrid();
|
||||
for (const auto &i : m_drawlist_shadow) {
|
||||
// only process specific part of the list & break early
|
||||
++count;
|
||||
@ -1066,7 +1076,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
||||
++material_swaps;
|
||||
}
|
||||
|
||||
v3f block_wpos = intToFloat(descriptor.m_pos / 8 * 8 * MAP_BLOCKSIZE, BS);
|
||||
v3f block_wpos = intToFloat(mesh_grid.getMeshPos(descriptor.m_pos) * MAP_BLOCKSIZE, BS);
|
||||
m.setTranslation(block_wpos - offset);
|
||||
|
||||
driver->setTransform(video::ETS_WORLD, m);
|
||||
|
@ -38,7 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
MeshMakeData::MeshMakeData(Client *client, bool use_shaders):
|
||||
m_client(client),
|
||||
m_use_shaders(use_shaders)
|
||||
m_use_shaders(use_shaders),
|
||||
m_mesh_grid(client->getMeshGrid()),
|
||||
side_length(MAP_BLOCKSIZE * m_mesh_grid.cell_size)
|
||||
{}
|
||||
|
||||
void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
|
||||
@ -53,12 +55,11 @@ void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
|
||||
m_vmanip.addArea(voxel_area);
|
||||
}
|
||||
|
||||
void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
|
||||
void MeshMakeData::fillBlockData(const v3s16 &bp, MapNode *data)
|
||||
{
|
||||
v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
|
||||
VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
|
||||
|
||||
v3s16 bp = m_blockpos + block_offset;
|
||||
v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
|
||||
m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
|
||||
}
|
||||
@ -1179,18 +1180,18 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
|
||||
v3s16 bp = data->m_blockpos;
|
||||
// Only generate minimap mapblocks at even coordinates.
|
||||
if (((bp.X | bp.Y | bp.Z) & 1) == 0 && data->m_client->getMinimap()) {
|
||||
m_minimap_mapblocks.resize(8, nullptr);
|
||||
if (data->m_mesh_grid.isMeshPos(bp) && data->m_client->getMinimap()) {
|
||||
m_minimap_mapblocks.resize(data->m_mesh_grid.getCellVolume(), nullptr);
|
||||
v3s16 ofs;
|
||||
|
||||
// See also client.cpp for the code that reads the array of minimap blocks.
|
||||
for (ofs.Z = 0; ofs.Z <= 1; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y <= 1; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X <= 1; ofs.X++) {
|
||||
for (ofs.Z = 0; ofs.Z < data->m_mesh_grid.cell_size; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y < data->m_mesh_grid.cell_size; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X < data->m_mesh_grid.cell_size; ofs.X++) {
|
||||
v3s16 p = (bp + ofs) * MAP_BLOCKSIZE;
|
||||
if (data->m_vmanip.getNodeNoEx(p).getContent() != CONTENT_IGNORE) {
|
||||
MinimapMapblock *block = new MinimapMapblock;
|
||||
m_minimap_mapblocks[ofs.Z * 4 + ofs.Y * 2 + ofs.X] = block;
|
||||
m_minimap_mapblocks[data->m_mesh_grid.getOffsetIndex(ofs)] = block;
|
||||
block->getMinimapNodes(&data->m_vmanip, p);
|
||||
}
|
||||
}
|
||||
@ -1221,7 +1222,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
Convert FastFaces to MeshCollector
|
||||
*/
|
||||
|
||||
v3f offset = intToFloat((data->m_blockpos - data->m_blockpos / 8 * 8) * MAP_BLOCKSIZE, BS);
|
||||
v3f offset = intToFloat((data->m_blockpos - data->m_mesh_grid.getMeshPos(data->m_blockpos)) * MAP_BLOCKSIZE, BS);
|
||||
MeshCollector collector(m_bounding_sphere_center, offset);
|
||||
|
||||
{
|
||||
@ -1584,10 +1585,11 @@ std::unordered_map<v3s16, u8> get_solid_sides(MeshMakeData *data)
|
||||
{
|
||||
std::unordered_map<v3s16, u8> results;
|
||||
v3s16 ofs;
|
||||
const u16 mesh_chunk = data->side_length / MAP_BLOCKSIZE;
|
||||
|
||||
for (ofs.X = 0; ofs.X < 2; ofs.X++)
|
||||
for (ofs.Y = 0; ofs.Y < 2; ofs.Y++)
|
||||
for (ofs.Z = 0; ofs.Z < 2; ofs.Z++) {
|
||||
for (ofs.X = 0; ofs.X < mesh_chunk; ofs.X++)
|
||||
for (ofs.Y = 0; ofs.Y < mesh_chunk; ofs.Y++)
|
||||
for (ofs.Z = 0; ofs.Z < mesh_chunk; ofs.Z++) {
|
||||
v3s16 blockpos = data->m_blockpos + ofs;
|
||||
v3s16 blockpos_nodes = blockpos * MAP_BLOCKSIZE;
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
|
@ -43,6 +43,7 @@ struct MeshMakeData
|
||||
v3s16 m_blockpos = v3s16(-1337,-1337,-1337);
|
||||
v3s16 m_crack_pos_relative = v3s16(-1337,-1337,-1337);
|
||||
bool m_smooth_lighting = false;
|
||||
MeshGrid m_mesh_grid;
|
||||
u16 side_length = MAP_BLOCKSIZE;
|
||||
|
||||
Client *m_client;
|
||||
@ -54,7 +55,7 @@ struct MeshMakeData
|
||||
Copy block data manually (to allow optimizations by the caller)
|
||||
*/
|
||||
void fillBlockDataBegin(const v3s16 &blockpos);
|
||||
void fillBlockData(const v3s16 &block_offset, MapNode *data);
|
||||
void fillBlockData(const v3s16 &bp, MapNode *data);
|
||||
|
||||
/*
|
||||
Set the (node) position of a crack
|
||||
|
@ -77,9 +77,11 @@ bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool
|
||||
|
||||
MutexAutoLock lock(m_mutex);
|
||||
|
||||
// Mesh is placed at even positions at all coordinates
|
||||
// (every 8-th block) and will cover 8 blocks
|
||||
v3s16 mesh_position(p.X & ~1, p.Y & ~1, p.Z & ~1);
|
||||
MeshGrid mesh_grid = m_client->getMeshGrid();
|
||||
|
||||
// Mesh is placed at the corner block of a chunk
|
||||
// (where all coordinate are divisible by the chunk size)
|
||||
v3s16 mesh_position(mesh_grid.getMeshPos(p));
|
||||
/*
|
||||
Mark the block as urgent if requested
|
||||
*/
|
||||
@ -99,14 +101,19 @@ bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool
|
||||
q->crack_level = m_client->getCrackLevel();
|
||||
q->crack_pos = m_client->getCrackPos();
|
||||
q->urgent |= urgent;
|
||||
for (std::size_t i = 0; i < q->map_blocks.size(); i++) {
|
||||
v3s16 pos;
|
||||
int i = 0;
|
||||
for (pos.X = q->p.X - 1; pos.X <= q->p.X + mesh_grid.cell_size; pos.X++)
|
||||
for (pos.Z = q->p.Z - 1; pos.Z <= q->p.Z + mesh_grid.cell_size; pos.Z++)
|
||||
for (pos.Y = q->p.Y - 1; pos.Y <= q->p.Y + mesh_grid.cell_size; pos.Y++) {
|
||||
if (!q->map_blocks[i]) {
|
||||
MapBlock *block = map->getBlockNoCreateNoEx(q->p + g_64dirs[i]);
|
||||
MapBlock *block = map->getBlockNoCreateNoEx(pos);
|
||||
if (block) {
|
||||
block->refGrab();
|
||||
q->map_blocks[i] = block;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -116,9 +123,12 @@ bool MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool
|
||||
Make a list of blocks necessary for mesh generation and lock the blocks in memory.
|
||||
*/
|
||||
std::vector<MapBlock *> map_blocks;
|
||||
map_blocks.reserve(4*4*4);
|
||||
for (v3s16 dp : g_64dirs) {
|
||||
MapBlock *block = map->getBlockNoCreateNoEx(mesh_position + dp);
|
||||
map_blocks.reserve((mesh_grid.cell_size+2)*(mesh_grid.cell_size+2)*(mesh_grid.cell_size+2));
|
||||
v3s16 pos;
|
||||
for (pos.X = mesh_position.X - 1; pos.X <= mesh_position.X + mesh_grid.cell_size; pos.X++)
|
||||
for (pos.Z = mesh_position.Z - 1; pos.Z <= mesh_position.Z + mesh_grid.cell_size; pos.Z++)
|
||||
for (pos.Y = mesh_position.Y - 1; pos.Y <= mesh_position.Y + mesh_grid.cell_size; pos.Y++) {
|
||||
MapBlock *block = map->getBlockNoCreateNoEx(pos);
|
||||
map_blocks.push_back(block);
|
||||
if (block)
|
||||
block->refGrab();
|
||||
@ -182,13 +192,16 @@ void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
|
||||
{
|
||||
MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
|
||||
q->data = data;
|
||||
data->side_length = 2 * MAP_BLOCKSIZE;
|
||||
|
||||
data->fillBlockDataBegin(q->p);
|
||||
|
||||
for (std::size_t i = 0; i < 64; i++) {
|
||||
MapBlock *block = q->map_blocks[i];
|
||||
data->fillBlockData(g_64dirs[i], block ? block->getData() : block_placeholder.data);
|
||||
v3s16 pos;
|
||||
int i = 0;
|
||||
for (pos.X = q->p.X - 1; pos.X <= q->p.X + data->m_mesh_grid.cell_size; pos.X++)
|
||||
for (pos.Z = q->p.Z - 1; pos.Z <= q->p.Z + data->m_mesh_grid.cell_size; pos.Z++)
|
||||
for (pos.Y = q->p.Y - 1; pos.Y <= q->p.Y + data->m_mesh_grid.cell_size; pos.Y++) {
|
||||
MapBlock *block = q->map_blocks[i++];
|
||||
data->fillBlockData(pos, block ? block->getData() : block_placeholder.data);
|
||||
}
|
||||
|
||||
data->setCrack(q->crack_level, q->crack_pos);
|
||||
|
@ -185,6 +185,7 @@ void set_default_settings()
|
||||
settings->setDefault("fps_max", "60");
|
||||
settings->setDefault("fps_max_unfocused", "20");
|
||||
settings->setDefault("viewing_range", "190");
|
||||
settings->setDefault("client_mesh_chunk", "1");
|
||||
#if ENABLE_GLES
|
||||
settings->setDefault("near_plane", "0.1");
|
||||
#endif
|
||||
|
@ -110,78 +110,6 @@ const v3s16 g_27dirs[27] =
|
||||
v3s16(0,0,0),
|
||||
};
|
||||
|
||||
const v3s16 g_64dirs[64] =
|
||||
{
|
||||
// +right, +top, +back
|
||||
v3s16( -1, -1, -1),
|
||||
v3s16( -1, 0, -1),
|
||||
v3s16( -1, 1, -1),
|
||||
v3s16( -1, 2, -1),
|
||||
v3s16( -1, -1, 0),
|
||||
v3s16( -1, 0, 0),
|
||||
v3s16( -1, 1, 0),
|
||||
v3s16( -1, 2, 0),
|
||||
v3s16( -1, -1, 1),
|
||||
v3s16( -1, 0, 1),
|
||||
v3s16( -1, 1, 1),
|
||||
v3s16( -1, 2, 1),
|
||||
v3s16( -1, -1, 2),
|
||||
v3s16( -1, 0, 2),
|
||||
v3s16( -1, 1, 2),
|
||||
v3s16( -1, 2, 2),
|
||||
|
||||
v3s16( 0, -1, -1),
|
||||
v3s16( 0, 0, -1),
|
||||
v3s16( 0, 1, -1),
|
||||
v3s16( 0, 2, -1),
|
||||
v3s16( 0, -1, 0),
|
||||
v3s16( 0, 0, 0),
|
||||
v3s16( 0, 1, 0),
|
||||
v3s16( 0, 2, 0),
|
||||
v3s16( 0, -1, 1),
|
||||
v3s16( 0, 0, 1),
|
||||
v3s16( 0, 1, 1),
|
||||
v3s16( 0, 2, 1),
|
||||
v3s16( 0, -1, 2),
|
||||
v3s16( 0, 0, 2),
|
||||
v3s16( 0, 1, 2),
|
||||
v3s16( 0, 2, 2),
|
||||
|
||||
v3s16( 1, -1, -1),
|
||||
v3s16( 1, 0, -1),
|
||||
v3s16( 1, 1, -1),
|
||||
v3s16( 1, 2, -1),
|
||||
v3s16( 1, -1, 0),
|
||||
v3s16( 1, 0, 0),
|
||||
v3s16( 1, 1, 0),
|
||||
v3s16( 1, 2, 0),
|
||||
v3s16( 1, -1, 1),
|
||||
v3s16( 1, 0, 1),
|
||||
v3s16( 1, 1, 1),
|
||||
v3s16( 1, 2, 1),
|
||||
v3s16( 1, -1, 2),
|
||||
v3s16( 1, 0, 2),
|
||||
v3s16( 1, 1, 2),
|
||||
v3s16( 1, 2, 2),
|
||||
|
||||
v3s16( 2, -1, -1),
|
||||
v3s16( 2, 0, -1),
|
||||
v3s16( 2, 1, -1),
|
||||
v3s16( 2, 2, -1),
|
||||
v3s16( 2, -1, 0),
|
||||
v3s16( 2, 0, 0),
|
||||
v3s16( 2, 1, 0),
|
||||
v3s16( 2, 2, 0),
|
||||
v3s16( 2, -1, 1),
|
||||
v3s16( 2, 0, 1),
|
||||
v3s16( 2, 1, 1),
|
||||
v3s16( 2, 2, 1),
|
||||
v3s16( 2, -1, 2),
|
||||
v3s16( 2, 0, 2),
|
||||
v3s16( 2, 1, 2),
|
||||
v3s16( 2, 2, 2),
|
||||
};
|
||||
|
||||
const u8 wallmounted_to_facedir[6] = {
|
||||
20,
|
||||
0,
|
||||
|
@ -31,9 +31,6 @@ extern const v3s16 g_26dirs[26];
|
||||
// 26th is (0,0,0)
|
||||
extern const v3s16 g_27dirs[27];
|
||||
|
||||
// all positions around an octablock in sector-first order
|
||||
extern const v3s16 g_64dirs[64];
|
||||
|
||||
extern const u8 wallmounted_to_facedir[6];
|
||||
|
||||
extern const v3s16 wallmounted_dirs[8];
|
||||
|
@ -145,6 +145,37 @@ inline v3s16 componentwise_max(const v3s16 &a, const v3s16 &b)
|
||||
return v3s16(MYMAX(a.X, b.X), MYMAX(a.Y, b.Y), MYMAX(a.Z, b.Z));
|
||||
}
|
||||
|
||||
/// @brief Describes a grid with given step, oirginating at (0,0,0)
|
||||
struct MeshGrid {
|
||||
u16 cell_size;
|
||||
|
||||
u32 getCellVolume() const { return cell_size * cell_size * cell_size; }
|
||||
|
||||
/// @brief returns closest step of the grid smaller than p
|
||||
s16 getMeshPos(s16 p) const
|
||||
{
|
||||
return ((p - (p < 0) * (cell_size - 1)) / cell_size * cell_size);
|
||||
}
|
||||
|
||||
/// @brief Returns coordinates of the origin of the grid cell containing p
|
||||
v3s16 getMeshPos(v3s16 p) const
|
||||
{
|
||||
return v3s16(getMeshPos(p.X), getMeshPos(p.Y), getMeshPos(p.Z));
|
||||
}
|
||||
|
||||
/// @brief Returns true if p is an origin of a cell in the grid.
|
||||
bool isMeshPos(v3s16 &p) const
|
||||
{
|
||||
return ((p.X + p.Y + p.Z) % cell_size) == 0;
|
||||
}
|
||||
|
||||
/// @brief Returns index of the given offset in a grid cell
|
||||
/// All offset coordinates must be smaller than the size of the cell
|
||||
u16 getOffsetIndex(v3s16 offset) const
|
||||
{
|
||||
return (offset.Z * cell_size + offset.Y) * cell_size + offset.X;
|
||||
}
|
||||
};
|
||||
|
||||
/** Returns \p f wrapped to the range [-360, 360]
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user