Merge pull request #766 from clasher113/main

blocks raycast enhancement
This commit is contained in:
MihailRis 2026-01-06 13:45:43 +03:00 • committed by GitHub
commit 4c7043d4c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 57 additions and 24 deletions

View file

@ -136,7 +136,9 @@ Set specified bits.
## Raycast ## Raycast
```lua ```lua
block.raycast(start: vec3, dir: vec3, max_distance: number, [optional] dest: table, [optional] filter: table) -> { block.raycast(start: vec3, dir: vec3, max_distance: number, [optional] dest: table,
[optional] filter: table, [optional] include_non_selectable = false
) -> {
block: int, -- block id block: int, -- block id
endpoint: vec3, -- point of the ray hit point endpoint: vec3, -- point of the ray hit point
iendpoint: vec3, -- position of the block hit by the ray iendpoint: vec3, -- position of the block hit by the ray
@ -150,6 +152,9 @@ Casts a ray from the start point in the direction of *dir*. Max_distance specifi
Argument `filter` can be used to tell ray what blocks can be skipped(passed through) during ray-casting. Argument `filter` can be used to tell ray what blocks can be skipped(passed through) during ray-casting.
To use filter `dest` argument must be filled with some value(can be nil), it's done for backwards compatability To use filter `dest` argument must be filled with some value(can be nil), it's done for backwards compatability
The `include_non_selectable` argument determines whether blocks that cannot be selected by the cursor will be included.
Example - `base:water`
The function returns a table with the results or nil if the ray does not hit any block. The function returns a table with the results or nil if the ray does not hit any block.
The result will use the destination table instead of creating a new one if the optional argument specified. The result will use the destination table instead of creating a new one if the optional argument specified.

View file

@ -63,8 +63,9 @@ entities.get_all_in_radius(center: vec3, radius: number) -> array<int>
``` ```
```lua ```lua
entities.raycast(start: vec3, dir: vec3, max_distance: number, entities.raycast(start: vec3, dir: vec3, max_distance: number, ignore: int, [optional] destination: table,
ignore: int, [optional] destination: table, [optional] filter: table) -> table or nil [optional] filter: table, [optional] include_non_selectable = false
) -> table or nil
``` ```
The function is an extended version of [block.raycast](libblock.md#raycast). Returns a table with the results if the ray touches a block or entity. The function is an extended version of [block.raycast](libblock.md#raycast). Returns a table with the results if the ray touches a block or entity.

View file

@ -92,7 +92,9 @@ block.material(blockid: int) -> string
## Raycast ## Raycast
```lua ```lua
block.raycast(start: vec3, dir: vec3, max_distance: number, [опционально] dest: table, [опционально] filter: table) -> { block.raycast(start: vec3, dir: vec3, max_distance: number, [опционально] dest: table,
[опционально] filter: table, [опционально] include_non_selectable = false
) -> {
block: int, -- id блока block: int, -- id блока
endpoint: vec3, -- точка касания луча endpoint: vec3, -- точка касания луча
iendpoint: vec3, -- позиция блока, которого касается луч iendpoint: vec3, -- позиция блока, которого касается луч
@ -106,6 +108,9 @@ block.raycast(start: vec3, dir: vec3, max_distance: number, [опциональ
Аргумент `filter` позволяет указать какие блоки являются "прозрачными" для луча, прим.: {"base:glass","base:water"}. Аргумент `filter` позволяет указать какие блоки являются "прозрачными" для луча, прим.: {"base:glass","base:water"}.
Для использования агрумент `dest` нужно чем-то заполнить(можно nil), это сделано для обратной совместимости Для использования агрумент `dest` нужно чем-то заполнить(можно nil), это сделано для обратной совместимости
Аргумент `include_non_selectable` определяет, будут ли учтены блоки, которые нельзя выбрать курсором.
Пример - `base:water`
Функция возвращает таблицу с результатами или nil, если луч не касается блока. Функция возвращает таблицу с результатами или nil, если луч не касается блока.
Для результата будет использоваться целевая (dest) таблица вместо создания новой, если указан опциональный аргумент. Для результата будет использоваться целевая (dest) таблица вместо создания новой, если указан опциональный аргумент.

View file

@ -62,8 +62,9 @@ entities.get_all_in_radius(center: vec3, radius: number) -> table<int>
``` ```
```lua ```lua
entities.raycast(start: vec3, dir: vec3, max_distance: number, entities.raycast(start: vec3, dir: vec3, max_distance: number, ignore: int, [optional] destination: table,
ignore: int, [optional] destination: table, [optional] filter: table) -> table или nil [optional] filter: table, [optional] include_non_selectable = false
) -> table или nil
``` ```
Функция является расширенным вариантом [block.raycast](libblock.md#raycast). Возвращает таблицу с результатами если луч касается блока, либо сущности. Функция является расширенным вариантом [block.raycast](libblock.md#raycast). Возвращает таблицу с результатами если луч касается блока, либо сущности.

View file

@ -476,8 +476,10 @@ static int l_raycast(lua::State* L) {
auto start = lua::tovec<3>(L, 1); auto start = lua::tovec<3>(L, 1);
auto dir = lua::tovec<3>(L, 2); auto dir = lua::tovec<3>(L, 2);
auto maxDistance = lua::tonumber(L, 3); auto maxDistance = lua::tonumber(L, 3);
bool includeNonSelectable = false;
std::set<blockid_t> filteredBlocks {}; std::set<blockid_t> filteredBlocks {};
if (lua::gettop(L) >= 5) { const int luaStackSize = lua::gettop(L);
if (luaStackSize >= 5) {
if (lua::istable(L, 5)) { if (lua::istable(L, 5)) {
int addLen = lua::objlen(L, 5); int addLen = lua::objlen(L, 5);
for (int i = 0; i < addLen; i++) { for (int i = 0; i < addLen; i++) {
@ -493,6 +495,9 @@ static int l_raycast(lua::State* L) {
throw std::runtime_error("table expected for filter"); throw std::runtime_error("table expected for filter");
} }
} }
if (luaStackSize >= 6) {
includeNonSelectable = lua::toboolean(L, 6);
}
glm::vec3 end; glm::vec3 end;
glm::ivec3 normal; glm::ivec3 normal;
glm::ivec3 iend; glm::ivec3 iend;
@ -504,9 +509,10 @@ static int l_raycast(lua::State* L) {
end, end,
normal, normal,
iend, iend,
filteredBlocks filteredBlocks,
includeNonSelectable
)) { )) {
if (lua::gettop(L) >= 4 && !lua::isnil(L, 4)) { if (luaStackSize >= 4 && !lua::isnil(L, 4)) {
lua::pushvalue(L, 4); lua::pushvalue(L, 4);
} else { } else {
lua::createtable(L, 0, 5); lua::createtable(L, 0, 5);

View file

@ -141,8 +141,10 @@ static int l_raycast(lua::State* L) {
auto dir = lua::tovec<3>(L, 2); auto dir = lua::tovec<3>(L, 2);
auto maxDistance = lua::tonumber(L, 3); auto maxDistance = lua::tonumber(L, 3);
auto ignoreEntityId = lua::tointeger(L, 4); auto ignoreEntityId = lua::tointeger(L, 4);
bool includeNonSelectable = false;
std::set<blockid_t> filteredBlocks {}; std::set<blockid_t> filteredBlocks {};
if (lua::gettop(L) >= 6) { const int luaStackSize = lua::gettop(L);
if (luaStackSize >= 6) {
if (lua::istable(L, 6)) { if (lua::istable(L, 6)) {
int addLen = lua::objlen(L, 6); int addLen = lua::objlen(L, 6);
for (int i = 0; i < addLen; i++) { for (int i = 0; i < addLen; i++) {
@ -158,6 +160,9 @@ static int l_raycast(lua::State* L) {
throw std::runtime_error("table expected for filter"); throw std::runtime_error("table expected for filter");
} }
} }
if (luaStackSize >= 7) {
includeNonSelectable = lua::toboolean(L, 6);
}
glm::vec3 end; glm::vec3 end;
glm::ivec3 normal; glm::ivec3 normal;
@ -173,14 +178,15 @@ static int l_raycast(lua::State* L) {
end, end,
normal, normal,
iend, iend,
filteredBlocks filteredBlocks,
includeNonSelectable
)) { )) {
maxDistance = glm::distance(start, end); maxDistance = glm::distance(start, end);
block = voxel->id; block = voxel->id;
} }
if (auto ray = if (auto ray =
level->entities->rayCast(start, dir, maxDistance, ignoreEntityId)) { level->entities->rayCast(start, dir, maxDistance, ignoreEntityId)) {
if (lua::gettop(L) >= 5 && !lua::isnil(L, 5)) { if (luaStackSize >= 5 && !lua::isnil(L, 5)) {
lua::pushvalue(L, 5); lua::pushvalue(L, 5);
} else { } else {
lua::createtable(L, 0, 6); lua::createtable(L, 0, 6);

View file

@ -215,10 +215,11 @@ voxel* Chunks::rayCast(
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend, glm::ivec3& iend,
std::set<blockid_t> filter std::set<blockid_t> filter,
bool includeNonSelectable
) const { ) const {
return blocks_agent::raycast( return blocks_agent::raycast(
*this, start, dir, maxDist, end, norm, iend, std::move(filter) *this, start, dir, maxDist, end, norm, iend, std::move(filter), includeNonSelectable
); );
} }

View file

@ -111,7 +111,8 @@ public:
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend, glm::ivec3& iend,
std::set<blockid_t> filter = {} std::set<blockid_t> filter = {},
bool includeNonSelectable = false
) const; ) const;
glm::vec3 rayCastToObstacle( glm::vec3 rayCastToObstacle(

View file

@ -221,7 +221,8 @@ static inline voxel* raycast_blocks(
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend, glm::ivec3& iend,
std::set<blockid_t> filter std::set<blockid_t> filter,
bool includeNonSelectable
) { ) {
const auto& blocks = chunks.getContentIndices().blocks; const auto& blocks = chunks.getContentIndices().blocks;
float px = start.x; float px = start.x;
@ -264,8 +265,8 @@ static inline voxel* raycast_blocks(
} }
const auto& def = blocks.require(voxel->id); const auto& def = blocks.require(voxel->id);
if ((filter.empty() && def.selectable) || if (voxel->id != BLOCK_AIR && (def.selectable || includeNonSelectable) &&
(!filter.empty() && filter.find(def.rt.id) == filter.end())) { (filter.empty() || filter.find(def.rt.id) == filter.end())) {
end.x = px + t * dx; end.x = px + t * dx;
end.y = py + t * dy; end.y = py + t * dy;
end.z = pz + t * dz; end.z = pz + t * dz;
@ -362,9 +363,10 @@ voxel* blocks_agent::raycast(
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend, glm::ivec3& iend,
std::set<blockid_t> filter std::set<blockid_t> filter,
bool includeNonSelectable
) { ) {
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter, includeNonSelectable);
} }
voxel* blocks_agent::raycast( voxel* blocks_agent::raycast(
@ -375,9 +377,10 @@ voxel* blocks_agent::raycast(
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend, glm::ivec3& iend,
std::set<blockid_t> filter std::set<blockid_t> filter,
bool includeNonSelectable
) { ) {
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter, includeNonSelectable);
} }
// reduce nesting on next modification // reduce nesting on next modification

View file

@ -419,6 +419,7 @@ inline void set_rotation(
/// @param norm [out] surface normal vector /// @param norm [out] surface normal vector
/// @param iend [out] ray end integer position (voxel position + normal) /// @param iend [out] ray end integer position (voxel position + normal)
/// @param filter filtered ids /// @param filter filtered ids
/// @param includeNonSelectable will non-selectable blocks be included
/// @return voxel pointer or nullptr /// @return voxel pointer or nullptr
voxel* raycast( voxel* raycast(
const Chunks& chunks, const Chunks& chunks,
@ -428,7 +429,8 @@ voxel* raycast(
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend, glm::ivec3& iend,
std::set<blockid_t> filter std::set<blockid_t> filter,
bool includeNonSelectable
); );
/// @brief Cast ray to a selectable block with filter based on id. /// @brief Cast ray to a selectable block with filter based on id.
@ -440,6 +442,7 @@ voxel* raycast(
/// @param norm [out] surface normal vector /// @param norm [out] surface normal vector
/// @param iend [out] ray end integer position (voxel position + normal) /// @param iend [out] ray end integer position (voxel position + normal)
/// @param filter filtered ids /// @param filter filtered ids
/// @param includeNonSelectable will non-selectable blocks be included
/// @return voxel pointer or nullptr /// @return voxel pointer or nullptr
voxel* raycast( voxel* raycast(
const GlobalChunks& chunks, const GlobalChunks& chunks,
@ -449,7 +452,8 @@ voxel* raycast(
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend, glm::ivec3& iend,
std::set<blockid_t> filter std::set<blockid_t> filter,
bool includeNonSelectable
); );
void get_voxels(const Chunks& chunks, VoxelsVolume* volume, bool backlight=false); void get_voxels(const Chunks& chunks, VoxelsVolume* volume, bool backlight=false);