Merge pull request #297 from REDxEYE/raycast-filter

Lua/C++ api: Allow to specify what blocks to ignore during raycast
This commit is contained in:
MihailRis 2024-08-20 21:46:33 +03:00 committed by GitHub
commit 2689e13cea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 68 additions and 18 deletions

View File

@ -115,17 +115,20 @@ Set specified bits.
## Raycast ## Raycast
```lua ```lua
block.raycast(start: vec3, dir: vec3, max_distance: number, [optional] dest: table) -> { block.raycast(start: vec3, dir: vec3, max_distance: number, [optional] dest: table, [optional] filter: table) -> {
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
length: number, -- ray length length: number, -- ray length
normal: vec3 -- normal vector of the surface hit by the ray normal: vec3, -- normal vector of the surface hit by the ray
} or nil } or nil
``` ```
Casts a ray from the start point in the direction of *dir*. Max_distance specifies the maximum ray length. Casts a ray from the start point in the direction of *dir*. Max_distance specifies the maximum ray length.
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
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

@ -51,7 +51,7 @@ 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) -> table or nil ignore: int, [optional] destination: table, [optional] filter: table) -> 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

@ -60,17 +60,20 @@ block.get_picking_item(id: int) -> int
### Raycast ### Raycast
```lua ```lua
block.raycast(start: vec3, dir: vec3, max_distance: number, [опционально] dest: table) -> { block.raycast(start: vec3, dir: vec3, max_distance: number, [опционально] dest: table, [опционально] filter: table) -> {
block: int, -- id блока block: int, -- id блока
endpoint: vec3, -- точка касания луча endpoint: vec3, -- точка касания луча
iendpoint: vec3, -- позиция блока, которого касается луч iendpoint: vec3, -- позиция блока, которого касается луч
length: number, -- длина луча length: number, -- длина луча
normal: vec3 -- вектор нормали поверхности, которой касается луч normal: vec3, -- вектор нормали поверхности, которой касается луч
} или nil } или nil
``` ```
Бросает луч из точки start в направлении dir. Max_distance указывает максимальную длину луча. Бросает луч из точки start в направлении dir. Max_distance указывает максимальную длину луча.
Аргумент `filter` позволяет указать какие блоки являются "прозрачными" для луча, прим.: {"base:glass","base:water"}.
Для использования агрумент `dest` нужно чем-то заполнить(можно nil), это сделано для обратной совместимости
Функция возвращает таблицу с результатами или nil, если луч не касается блока. Функция возвращает таблицу с результатами или nil, если луч не касается блока.
Для результата будет использоваться целевая (dest) таблица вместо создания новой, если указан опциональный аргумент. Для результата будет использоваться целевая (dest) таблица вместо создания новой, если указан опциональный аргумент.

View File

@ -51,7 +51,7 @@ 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) -> table или nil ignore: int, [optional] destination: table, [optional] filter: table) -> table или nil
``` ```
Функция является расширенным вариантом [block.raycast](libblock.md#raycast). Возвращает таблицу с результатами если луч касается блока, либо сущности. Функция является расширенным вариантом [block.raycast](libblock.md#raycast). Возвращает таблицу с результатами если луч касается блока, либо сущности.

View File

@ -353,13 +353,30 @@ 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);
std::set<blockid_t> filteredBlocks {};
if (lua::gettop(L) >= 5) {
if (lua::istable(L, 5)) {
int addLen = lua::objlen(L, 5);
for (int i = 0; i < addLen; i++) {
lua::rawgeti(L, i + 1, 5);
auto blockName = std::string(lua::tostring(L, -1));
Block* block = content->blocks.find(blockName);
if (block != nullptr) {
filteredBlocks.insert(block->rt.id);
}
lua::pop(L);
}
} else {
throw std::runtime_error("table expected for filter");
}
}
glm::vec3 end; glm::vec3 end;
glm::ivec3 normal; glm::ivec3 normal;
glm::ivec3 iend; glm::ivec3 iend;
if (auto voxel = level->chunks->rayCast( if (auto voxel = level->chunks->rayCast(
start, dir, maxDistance, end, normal, iend start, dir, maxDistance, end, normal, iend, filteredBlocks
)) { )) {
if (lua::gettop(L) >= 4) { if (lua::gettop(L) >= 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);
@ -452,4 +469,5 @@ const luaL_Reg blocklib[] = {
{"raycast", lua::wrap<l_raycast>}, {"raycast", lua::wrap<l_raycast>},
{"compose_state", lua::wrap<l_compose_state>}, {"compose_state", lua::wrap<l_compose_state>},
{"decompose_state", lua::wrap<l_decompose_state>}, {"decompose_state", lua::wrap<l_decompose_state>},
{NULL, NULL}}; {NULL, NULL}
};

View File

@ -8,6 +8,7 @@
#include "objects/rigging.hpp" #include "objects/rigging.hpp"
#include "physics/Hitbox.hpp" #include "physics/Hitbox.hpp"
#include "voxels/Chunks.hpp" #include "voxels/Chunks.hpp"
#include "voxels/Block.hpp"
#include "window/Camera.hpp" #include "window/Camera.hpp"
using namespace scripting; using namespace scripting;
@ -118,7 +119,25 @@ 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);
auto ignore = lua::tointeger(L, 4); auto ignoreEntityId = lua::tointeger(L, 4);
std::set<blockid_t> filteredBlocks {};
if (lua::gettop(L) >= 6) {
if (lua::istable(L, 6)) {
int addLen = lua::objlen(L, 6);
for (int i = 0; i < addLen; i++) {
lua::rawgeti(L, i + 1, 6);
auto blockName = std::string(lua::tostring(L, -1));
Block* block = content->blocks.find(blockName);
if (block != nullptr) {
filteredBlocks.insert(block->rt.id);
}
lua::pop(L);
}
} else {
throw std::runtime_error("table expected for filter");
}
}
glm::vec3 end; glm::vec3 end;
glm::ivec3 normal; glm::ivec3 normal;
glm::ivec3 iend; glm::ivec3 iend;
@ -126,13 +145,14 @@ static int l_raycast(lua::State* L) {
blockid_t block = BLOCK_VOID; blockid_t block = BLOCK_VOID;
if (auto voxel = level->chunks->rayCast( if (auto voxel = level->chunks->rayCast(
start, dir, maxDistance, end, normal, iend start, dir, maxDistance, end, normal, iend, filteredBlocks
)) { )) {
maxDistance = glm::distance(start, end); maxDistance = glm::distance(start, end);
block = voxel->id; block = voxel->id;
} }
if (auto ray = level->entities->rayCast(start, dir, maxDistance, ignore)) { if (auto ray =
if (lua::gettop(L) >= 5) { level->entities->rayCast(start, dir, maxDistance, ignoreEntityId)) {
if (lua::gettop(L) >= 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);
@ -157,7 +177,7 @@ static int l_raycast(lua::State* L) {
lua::setfield(L, "entity"); lua::setfield(L, "entity");
return 1; return 1;
} else if (block != BLOCK_VOID) { } else if (block != BLOCK_VOID) {
if (lua::gettop(L) >= 5) { if (lua::gettop(L) >= 5 && !lua::isnil(L, 5)) {
lua::pushvalue(L, 5); lua::pushvalue(L, 5);
} else { } else {
lua::createtable(L, 0, 5); lua::createtable(L, 0, 5);
@ -194,4 +214,5 @@ const luaL_Reg entitylib[] = {
{"get_all_in_box", lua::wrap<l_get_all_in_box>}, {"get_all_in_box", lua::wrap<l_get_all_in_box>},
{"get_all_in_radius", lua::wrap<l_get_all_in_radius>}, {"get_all_in_radius", lua::wrap<l_get_all_in_radius>},
{"raycast", lua::wrap<l_raycast>}, {"raycast", lua::wrap<l_raycast>},
{NULL, NULL}}; {NULL, NULL}
};

View File

@ -414,7 +414,8 @@ voxel* Chunks::rayCast(
float maxDist, float maxDist,
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend glm::ivec3& iend,
std::set<blockid_t> filter
) { ) {
float px = start.x; float px = start.x;
float py = start.y; float py = start.y;
@ -454,8 +455,10 @@ voxel* Chunks::rayCast(
if (voxel == nullptr) { if (voxel == nullptr) {
return nullptr; return nullptr;
} }
const auto& def = indices->blocks.require(voxel->id); const auto& def = indices->blocks.require(voxel->id);
if (def.selectable) { if ((filter.empty() && def.selectable) ||
(!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;

View File

@ -4,6 +4,7 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <memory> #include <memory>
#include <set>
#include <vector> #include <vector>
#include "typedefs.hpp" #include "typedefs.hpp"
@ -93,7 +94,8 @@ public:
float maxLength, float maxLength,
glm::vec3& end, glm::vec3& end,
glm::ivec3& norm, glm::ivec3& norm,
glm::ivec3& iend glm::ivec3& iend,
std::set<blockid_t> filter = {}
); );
glm::vec3 rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDist); glm::vec3 rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDist);