mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-07 03:51:50 +00:00
Merge branch 'main' into animation
This commit is contained in:
commit
7c06a74244
67 changed files with 1231 additions and 260 deletions
|
|
@ -22,9 +22,26 @@ namespace {
|
|||
}
|
||||
|
||||
inline double power(double base, int64_t power) {
|
||||
if (power == 0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
uint64_t exp;
|
||||
if (power < 0) {
|
||||
base = 1.0 / base;
|
||||
exp = static_cast<uint64_t>(-(power + 1)) + 1;
|
||||
} else {
|
||||
exp = static_cast<uint64_t>(power);
|
||||
}
|
||||
|
||||
double result = 1.0;
|
||||
for (int64_t i = 0; i < power; i++) {
|
||||
result *= base;
|
||||
|
||||
while (exp > 0) {
|
||||
if (exp & 1)
|
||||
result *= base;
|
||||
|
||||
base *= base;
|
||||
exp >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@ static util::Buffer<ubyte> write_to_memory(uint width, uint height, const ubyte*
|
|||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
auto row = std::make_unique<png_byte[]>(pixsize * width);
|
||||
for (uint y = 0; y < height; y++) {
|
||||
for (uint x = 0; x < width; x++) {
|
||||
for (uint i = 0; i < pixsize; i++) {
|
||||
for (int y = height-1; y >= 0; y--) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int i = 0; i < pixsize; i++) {
|
||||
row[x * pixsize + i] =
|
||||
(png_byte)data[(y * width + x) * pixsize + i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -528,3 +528,11 @@ std::string xml::stringify(
|
|||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string xml::stringify(
|
||||
const Node& element, bool nice, const std::string& indentStr
|
||||
) {
|
||||
std::stringstream ss;
|
||||
stringifyElement(ss, element, nice, indentStr, 0);
|
||||
return ss.str();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,12 @@ namespace xml {
|
|||
const std::string& indentStr = " "
|
||||
);
|
||||
|
||||
std::string stringify(
|
||||
const Node& element,
|
||||
bool nice = true,
|
||||
const std::string& indentStr = " "
|
||||
);
|
||||
|
||||
/// @brief Read XML Document from string
|
||||
/// @param filename file name will be shown in error messages
|
||||
/// @param source xml source code string
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ class Font;
|
|||
struct FontMetrics {
|
||||
std::optional<std::weak_ptr<Font>> font;
|
||||
|
||||
int lineHeight;
|
||||
int yoffset;
|
||||
int lineHeight = 0;
|
||||
int yoffset = 0;
|
||||
int _glyphInterval = 8;
|
||||
|
||||
~FontMetrics();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include "ImageData.hpp"
|
||||
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
|
@ -7,6 +9,8 @@
|
|||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
static debug::Logger logger("image-data");
|
||||
|
||||
ImageData::ImageData(ImageFormat format, uint width, uint height)
|
||||
: format(format), width(width), height(height) {
|
||||
size_t pixsize;
|
||||
|
|
@ -106,47 +110,83 @@ std::unique_ptr<ImageData> ImageData::cropped(int x, int y, int width, int heigh
|
|||
return subImage;
|
||||
}
|
||||
|
||||
static bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) {
|
||||
const int left = 0;
|
||||
const int right = width;
|
||||
const int bottom = 0;
|
||||
const int top = height;
|
||||
static bool clip_line(
|
||||
int& x0, int& y0, int& x1, int& y1, int width, int height
|
||||
) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
constexpr int INSIDE = 0;
|
||||
constexpr int LEFT = 1;
|
||||
constexpr int RIGHT = 2;
|
||||
constexpr int BOTTOM = 4;
|
||||
constexpr int TOP = 8;
|
||||
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
const int xmin = 0;
|
||||
const int ymin = 0;
|
||||
const int xmax = width - 1;
|
||||
const int ymax = height - 1;
|
||||
|
||||
float t0 = 0.0f;
|
||||
float t1 = 1.0f;
|
||||
auto outcode = [&](int x, int y) {
|
||||
int code = INSIDE;
|
||||
|
||||
auto clip = [](int p, int q, float& t0, float& t1) {
|
||||
if (p == 0) {
|
||||
return q >= 0;
|
||||
}
|
||||
float t = static_cast<float>(q) / p;
|
||||
if (p < 0) {
|
||||
if (t > t1) return false;
|
||||
if (t > t0) t0 = t;
|
||||
} else {
|
||||
if (t < t0) return false;
|
||||
if (t < t1) t1 = t;
|
||||
}
|
||||
return true;
|
||||
if (x < xmin)
|
||||
code |= LEFT;
|
||||
else if (x > xmax)
|
||||
code |= RIGHT;
|
||||
|
||||
if (y < ymin)
|
||||
code |= BOTTOM;
|
||||
else if (y > ymax)
|
||||
code |= TOP;
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
if (!clip(-dx, x1 - left, t0, t1)) return false;
|
||||
if (!clip( dx, right - x1, t0, t1)) return false;
|
||||
if (!clip(-dy, y1 - bottom, t0, t1)) return false;
|
||||
if (!clip( dy, top - y1, t0, t1)) return false;
|
||||
int c0 = outcode(x0, y0);
|
||||
int c1 = outcode(x1, y1);
|
||||
|
||||
if (t1 < 1.0f) {
|
||||
x2 = x1 + static_cast<int>(std::round(t1 * dx));
|
||||
y2 = y1 + static_cast<int>(std::round(t1 * dy));
|
||||
while (true) {
|
||||
if (!(c0 | c1)) {
|
||||
return true;
|
||||
}
|
||||
if (c0 & c1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int out = c0 ? c0 : c1;
|
||||
|
||||
double x = 0.0;
|
||||
double y = 0.0;
|
||||
|
||||
if (out & TOP) {
|
||||
y = ymax;
|
||||
x = x0 + (x1 - x0) * static_cast<double>(ymax - y0) / (y1 - y0);
|
||||
} else if (out & BOTTOM) {
|
||||
y = ymin;
|
||||
x = x0 + (x1 - x0) * static_cast<double>(ymin - y0) / (y1 - y0);
|
||||
} else if (out & RIGHT) {
|
||||
x = xmax;
|
||||
y = y0 + (y1 - y0) * static_cast<double>(xmax - x0) / (x1 - x0);
|
||||
} else { // LEFT
|
||||
x = xmin;
|
||||
y = y0 + (y1 - y0) * static_cast<double>(xmin - x0) / (x1 - x0);
|
||||
}
|
||||
|
||||
if (out == c0) {
|
||||
x0 = std::clamp(static_cast<int>(std::lround(x)), xmin, xmax);
|
||||
y0 = std::clamp(static_cast<int>(std::lround(y)), ymin, ymax);
|
||||
c0 = outcode(x0, y0);
|
||||
} else {
|
||||
x1 = std::clamp(static_cast<int>(std::lround(x)), xmin, xmax);
|
||||
y1 = std::clamp(static_cast<int>(std::lround(y)), ymin, ymax);
|
||||
c1 = outcode(x1, y1);
|
||||
}
|
||||
}
|
||||
if (t0 > 0.0f) {
|
||||
x1 = x1 + static_cast<int>(std::round(t0 * dx));
|
||||
y1 = y1 + static_cast<int>(std::round(t0 * dy));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_point_outside(int x, int y, int width, int height) {
|
||||
return x < 0 || y < 0 || x >= width || y >= height;
|
||||
}
|
||||
|
||||
template<uint channels>
|
||||
|
|
@ -155,11 +195,24 @@ static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const gl
|
|||
uint width = image.getWidth();
|
||||
uint height = image.getHeight();
|
||||
|
||||
glm::ivec4 init {x1, y1, x2, y2};
|
||||
|
||||
if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width ||
|
||||
y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) &&
|
||||
!clip_line(x1, y1, x2, y2, width, height)) {
|
||||
return;
|
||||
}
|
||||
// spam info for bug report
|
||||
if (is_point_outside(x1, y1, width, height) || is_point_outside(x2, y2, width, height)) {
|
||||
logger.warning() << "clip_line fault: [" << init.x << ", " << init.y
|
||||
<< "] - [" << init.z << ", " << init.w << "] --> ["
|
||||
<< x1 << ", " << y1 << "] - [" << x2 << ", " << y2
|
||||
<< "]";
|
||||
x1 = glm::clamp(x1, 0, static_cast<int>(width) - 1);
|
||||
y1 = glm::clamp(y1, 0, static_cast<int>(height) - 1);
|
||||
x2 = glm::clamp(x2, 0, static_cast<int>(width) - 1);
|
||||
y2 = glm::clamp(y2, 0, static_cast<int>(height) - 1);
|
||||
}
|
||||
|
||||
int dx = std::abs(x2 - x1);
|
||||
int dy = -std::abs(y2 - y1);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "voxels/blocks_agent.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "WorldRenderer.hpp"
|
||||
|
|
@ -98,7 +99,7 @@ void Decorator::updateAcoustics(const Camera& camera) {
|
|||
|
||||
auto& chunks = *player.chunks;
|
||||
const auto& start = camera.position;
|
||||
float rayLength = 40.0f;
|
||||
float length = 40.0f;
|
||||
|
||||
auto& blocks = level.content.getIndices()->blocks;
|
||||
|
||||
|
|
@ -107,7 +108,7 @@ void Decorator::updateAcoustics(const Camera& camera) {
|
|||
float averageDistance = 0.0f;
|
||||
float averageAbsorption = 0.0f;
|
||||
float speedOfSound = 300.0f;
|
||||
float minDistance = rayLength;
|
||||
float minDistance = length;
|
||||
for (int i = 0; i < rays; i++) {
|
||||
float u1 = random.randFloat();
|
||||
float u2 = random.randFloat();
|
||||
|
|
@ -121,12 +122,13 @@ void Decorator::updateAcoustics(const Camera& camera) {
|
|||
glm::vec3 end;
|
||||
glm::ivec3 norm;
|
||||
glm::ivec3 iend;
|
||||
auto vox = chunks.rayCast(start, dir, rayLength, end, norm, iend);
|
||||
blocks_agent::RaycastSettings raycast {};
|
||||
auto vox = chunks.rayCast(start, dir, length, end, norm, iend, raycast);
|
||||
if (vox == nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto distance = glm::distance(start, end);
|
||||
if (distance >= rayLength * 0.98f) {
|
||||
if (distance >= length * 0.98f) {
|
||||
continue;
|
||||
}
|
||||
if (distance < minDistance) {
|
||||
|
|
@ -149,7 +151,7 @@ void Decorator::updateAcoustics(const Camera& camera) {
|
|||
averageAbsorption /= hit;
|
||||
}
|
||||
|
||||
float decayTime = 20.0f * (averageDistance / rayLength);
|
||||
float decayTime = 20.0f * (averageDistance / length);
|
||||
float escapeRatio = static_cast<float>(rays - hit) / static_cast<float>(rays);
|
||||
|
||||
acoustics.reverbGain = glm::pow(hit / static_cast<float>(rays), 3.0f);
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@ uint Label::getLinesNumber() const {
|
|||
void Label::draw(const DrawContext& pctx, const Assets& assets) {
|
||||
auto batch = pctx.getBatch2D();
|
||||
auto font = assets.getShared<Font>(fontName);
|
||||
if (font == nullptr) {
|
||||
return;
|
||||
}
|
||||
cache.prepare(
|
||||
font,
|
||||
font->getMetrics(),
|
||||
|
|
|
|||
|
|
@ -353,7 +353,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
|
||||
void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) {
|
||||
auto font = assets.getShared<Font>(label->getFontName());
|
||||
rawTextCache.prepare(font, font->getMetrics(), label->getSize().x);
|
||||
if (font != nullptr) {
|
||||
rawTextCache.prepare(font, font->getMetrics(), label->getSize().x);
|
||||
}
|
||||
|
||||
glm::vec2 pos = calcPos();
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,16 @@ public:
|
|||
return fields;
|
||||
}
|
||||
|
||||
void setFields(dv::value&& table, bool clear) {
|
||||
if (clear) {
|
||||
fields = std::move(table);
|
||||
return;
|
||||
}
|
||||
for (auto&& [key, value] : table.asObject()) {
|
||||
fields[std::move(key)] = std::move(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasFields() const {
|
||||
return fields != nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ void LevelController::update(float delta, bool pause) {
|
|||
continue;
|
||||
}
|
||||
player->rotationInterpolation.updateTimer(delta);
|
||||
player->updateEntity();
|
||||
glm::vec3 position = player->getPosition();
|
||||
player->chunks->configure(
|
||||
glm::floor(position.x),
|
||||
|
|
@ -96,9 +95,9 @@ void LevelController::update(float delta, bool pause) {
|
|||
*player,
|
||||
player.get() == clientPlayer
|
||||
);
|
||||
player->updateEntity();
|
||||
}
|
||||
if (!pause) {
|
||||
// update all objects that needed
|
||||
blocks->update(delta, settings.chunks.padding.get());
|
||||
level->entities->update(delta);
|
||||
for (const auto& [_, player] : *level->players) {
|
||||
|
|
|
|||
|
|
@ -200,8 +200,13 @@ void CameraControl::update(
|
|||
auto castRay = [](Player& player, Camera& camera, const glm::vec3& front)
|
||||
-> glm::vec3 {
|
||||
auto blockEnd = player.chunks->rayCastToObstacle(camera.position, front, 3.0f);
|
||||
|
||||
Entities::RaycastSettings raycast {};
|
||||
raycast.ignoredUid = player.getEntity();
|
||||
raycast.solidEntitiesOnly = true;
|
||||
|
||||
auto entityRay = player.getLevel().entities->rayCast(
|
||||
camera.position, front, 3.0f, player.getEntity(), true
|
||||
camera.position, front, 3.0f, raycast
|
||||
);
|
||||
if (entityRay.has_value() &&
|
||||
entityRay->distance < glm::distance(camera.position, blockEnd)) {
|
||||
|
|
@ -350,7 +355,13 @@ voxel* PlayerController::updateSelection(float maxDistance) {
|
|||
glm::ivec3 iend;
|
||||
glm::ivec3 norm;
|
||||
voxel* vox = chunks.rayCast(
|
||||
camera->position, camera->front, maxDistance, end, norm, iend
|
||||
camera->position,
|
||||
camera->front,
|
||||
maxDistance,
|
||||
end,
|
||||
norm,
|
||||
iend,
|
||||
blocks_agent::RaycastSettings {}
|
||||
);
|
||||
if (vox) {
|
||||
maxDistance = glm::distance(camera->position, end);
|
||||
|
|
@ -358,8 +369,12 @@ voxel* PlayerController::updateSelection(float maxDistance) {
|
|||
auto prevEntity = selection.entity;
|
||||
selection.entity = ENTITY_NONE;
|
||||
selection.actualPosition = iend;
|
||||
|
||||
Entities::RaycastSettings raycast {};
|
||||
raycast.ignoredUid = player.getEntity();
|
||||
|
||||
if (auto result = level.entities->rayCast(
|
||||
camera->position, camera->front, maxDistance, player.getEntity()
|
||||
camera->position, camera->front, maxDistance, raycast
|
||||
)) {
|
||||
selection.entity = result->entity;
|
||||
selection.hitPosition =
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ extern const luaL_Reg vec3lib[]; // vecn.cpp
|
|||
extern const luaL_Reg vec4lib[]; // vecn.cpp
|
||||
extern const luaL_Reg weatherlib[]; // gfx.weather
|
||||
extern const luaL_Reg worldlib[];
|
||||
extern const luaL_Reg xmllib[];
|
||||
extern const luaL_Reg yamllib[];
|
||||
|
||||
// Components
|
||||
|
|
|
|||
|
|
@ -543,13 +543,16 @@ static int l_destruct(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_raycast(lua::State* L) {
|
||||
auto& level = require_level();
|
||||
const auto& level = require_level();
|
||||
|
||||
auto start = lua::tovec<3>(L, 1);
|
||||
auto dir = lua::tovec<3>(L, 2);
|
||||
auto maxDistance = lua::tonumber(L, 3);
|
||||
bool includeNonSelectable = false;
|
||||
|
||||
blocks_agent::RaycastSettings raycast {};
|
||||
std::set<blockid_t> filteredBlocks {};
|
||||
raycast.filter = &filteredBlocks;
|
||||
|
||||
const int luaStackSize = lua::gettop(L);
|
||||
if (luaStackSize >= 5) {
|
||||
if (lua::istable(L, 5)) {
|
||||
|
|
@ -568,21 +571,14 @@ static int l_raycast(lua::State* L) {
|
|||
}
|
||||
}
|
||||
if (luaStackSize >= 6) {
|
||||
includeNonSelectable = lua::toboolean(L, 6);
|
||||
raycast.includeNonSelectable = lua::toboolean(L, 6);
|
||||
}
|
||||
glm::vec3 end;
|
||||
glm::ivec3 normal;
|
||||
glm::ivec3 iend;
|
||||
|
||||
if (auto voxel = blocks_agent::raycast(
|
||||
*level.chunks,
|
||||
start,
|
||||
dir,
|
||||
maxDistance,
|
||||
end,
|
||||
normal,
|
||||
iend,
|
||||
filteredBlocks,
|
||||
includeNonSelectable
|
||||
*level.chunks, start, dir, maxDistance, end, normal, iend, raycast
|
||||
)) {
|
||||
if (luaStackSize >= 4 && !lua::isnil(L, 4)) {
|
||||
lua::pushvalue(L, 4);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#include "voxels/blocks_agent.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static const EntityDef* require_entity_def(lua::State* L) {
|
||||
|
|
@ -48,6 +50,13 @@ static int l_def_hitbox(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_def_solid(lua::State* L) {
|
||||
if (auto def = require_entity_def(L)) {
|
||||
return lua::pushboolean(L, def->solid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_defs_count(lua::State* L) {
|
||||
return lua::pushinteger(L, indices->entities.count());
|
||||
}
|
||||
|
|
@ -145,34 +154,59 @@ static int l_get_all_in_radius(lua::State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int l_raycast(lua::State* L) {
|
||||
auto start = lua::tovec<3>(L, 1);
|
||||
auto dir = lua::tovec<3>(L, 2);
|
||||
auto maxDistance = lua::tonumber(L, 3);
|
||||
auto ignoreEntityId = lua::tointeger(L, 4);
|
||||
bool includeNonSelectable = false;
|
||||
std::set<blockid_t> filteredBlocks {};
|
||||
const int luaStackSize = lua::gettop(L);
|
||||
if (luaStackSize >= 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));
|
||||
const 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");
|
||||
}
|
||||
static int l_push_ray(
|
||||
lua::State* L,
|
||||
const std::optional<Entities::RaycastResult>& ray,
|
||||
blockid_t block,
|
||||
const glm::vec3& start,
|
||||
const glm::vec3& dir,
|
||||
const glm::vec3& end,
|
||||
const glm::ivec3& iend,
|
||||
const glm::ivec3& normal
|
||||
) {
|
||||
if (!ray.has_value() && block == BLOCK_VOID) {
|
||||
return 0;
|
||||
}
|
||||
if (luaStackSize >= 7) {
|
||||
includeNonSelectable = lua::toboolean(L, 6);
|
||||
if (lua::gettop(L) >= 5 && lua::istable(L, 5)) {
|
||||
lua::pushvalue(L, 5);
|
||||
} else {
|
||||
lua::createtable(L, 0, 6);
|
||||
}
|
||||
|
||||
lua::pushvec3(L, ray ? (start + dir * ray->distance) : end);
|
||||
lua::setfield(L, "endpoint");
|
||||
|
||||
lua::pushvec3(L, ray ? ray->normal : normal);
|
||||
lua::setfield(L, "normal");
|
||||
|
||||
lua::pushnumber(L, glm::distance(start, end));
|
||||
lua::setfield(L, "length");
|
||||
|
||||
lua::pushvec3(L, iend);
|
||||
lua::setfield(L, "iendpoint");
|
||||
|
||||
lua::pushinteger(L, block);
|
||||
lua::setfield(L, "block");
|
||||
|
||||
if (ray.has_value()) {
|
||||
lua::pushinteger(L, ray->entity);
|
||||
lua::setfield(L, "entity");
|
||||
} else if (block != BLOCK_VOID) {
|
||||
lua::pushinteger(L, block);
|
||||
lua::setfield(L, "block");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_raycast(
|
||||
lua::State* L,
|
||||
const glm::vec3& start,
|
||||
const glm::vec3& dir,
|
||||
float maxDistance,
|
||||
const blocks_agent::RaycastSettings& voxelRaycast,
|
||||
const Entities::RaycastSettings& entityRaycast,
|
||||
bool entities
|
||||
) {
|
||||
glm::vec3 end;
|
||||
glm::ivec3 normal;
|
||||
glm::ivec3 iend;
|
||||
|
|
@ -187,61 +221,139 @@ static int l_raycast(lua::State* L) {
|
|||
end,
|
||||
normal,
|
||||
iend,
|
||||
filteredBlocks,
|
||||
includeNonSelectable
|
||||
voxelRaycast
|
||||
)) {
|
||||
maxDistance = glm::distance(start, end);
|
||||
block = voxel->id;
|
||||
}
|
||||
if (auto ray =
|
||||
level->entities->rayCast(start, dir, maxDistance, ignoreEntityId)) {
|
||||
if (luaStackSize >= 5 && !lua::isnil(L, 5)) {
|
||||
lua::pushvalue(L, 5);
|
||||
auto ray =
|
||||
entities
|
||||
? level->entities->rayCast(start, dir, maxDistance, entityRaycast)
|
||||
: std::nullopt;
|
||||
return l_push_ray(L, ray, block, start, dir, end, iend, normal);
|
||||
}
|
||||
|
||||
static void load_blocks_filter(lua::State* L, int idx, std::set<blockid_t>& dst) {
|
||||
int count = lua::objlen(L, idx);
|
||||
for (int i = 0; i < count; i++) {
|
||||
lua::rawgeti(L, i + 1, idx);
|
||||
if (lua::isnumber(L, -1)) {
|
||||
dst.insert(lua::touinteger(L, -1));
|
||||
} else {
|
||||
lua::createtable(L, 0, 6);
|
||||
auto blockName = std::string(lua::tostring(L, -1));
|
||||
const Block* block = content->blocks.find(blockName);
|
||||
if (block != nullptr) {
|
||||
dst.insert(block->rt.id);
|
||||
}
|
||||
}
|
||||
|
||||
lua::pushvec3(L, start + dir * ray->distance);
|
||||
lua::setfield(L, "endpoint");
|
||||
|
||||
lua::pushvec3(L, ray->normal);
|
||||
lua::setfield(L, "normal");
|
||||
|
||||
lua::pushnumber(L, glm::distance(start, end));
|
||||
lua::setfield(L, "length");
|
||||
|
||||
lua::pushvec3(L, iend);
|
||||
lua::setfield(L, "iendpoint");
|
||||
|
||||
lua::pushinteger(L, block);
|
||||
lua::setfield(L, "block");
|
||||
|
||||
lua::pushinteger(L, ray->entity);
|
||||
lua::setfield(L, "entity");
|
||||
return 1;
|
||||
} else if (block != BLOCK_VOID) {
|
||||
if (lua::gettop(L) >= 5 && !lua::isnil(L, 5)) {
|
||||
lua::pushvalue(L, 5);
|
||||
} else {
|
||||
lua::createtable(L, 0, 5);
|
||||
}
|
||||
lua::pushvec3(L, end);
|
||||
lua::setfield(L, "endpoint");
|
||||
|
||||
lua::pushvec3(L, normal);
|
||||
lua::setfield(L, "normal");
|
||||
|
||||
lua::pushnumber(L, glm::distance(start, end));
|
||||
lua::setfield(L, "length");
|
||||
|
||||
lua::pushvec3(L, iend);
|
||||
lua::setfield(L, "iendpoint");
|
||||
|
||||
lua::pushinteger(L, block);
|
||||
lua::setfield(L, "block");
|
||||
return 1;
|
||||
lua::pop(L);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void load_entities_filter(lua::State* L, int idx, std::set<entitydefid_t>& dst) {
|
||||
int count = lua::objlen(L, idx);
|
||||
for (int i = 0; i < count; i++) {
|
||||
lua::rawgeti(L, i + 1, idx);
|
||||
if (lua::isnumber(L, -1)) {
|
||||
dst.insert(lua::touinteger(L, -1));
|
||||
} else {
|
||||
auto entityName = std::string(lua::tostring(L, -1));
|
||||
const EntityDef* entity = content->entities.find(entityName);
|
||||
if (entity != nullptr) {
|
||||
dst.insert(entity->rt.id);
|
||||
}
|
||||
}
|
||||
lua::pop(L);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_world_raycast(lua::State* L) {
|
||||
blocks_agent::RaycastSettings blocksRaycast {};
|
||||
Entities::RaycastSettings entityRaycast {};
|
||||
|
||||
std::set<blockid_t> filteredBlocks {};
|
||||
std::set<entitydefid_t> filteredEntities {};
|
||||
|
||||
glm::vec3 start, dir;
|
||||
float maxDistance;
|
||||
bool entities = true;
|
||||
|
||||
if (!lua::istable(L, 1)) {
|
||||
throw std::runtime_error("invalid function overload");
|
||||
}
|
||||
|
||||
lua::requirefield(L, "start", 1);
|
||||
start = lua::tovec3(L, -1);
|
||||
|
||||
lua::requirefield(L, "dir", 1);
|
||||
dir = lua::tovec3(L, -1);
|
||||
|
||||
lua::requirefield(L, "distance", 1);
|
||||
maxDistance = lua::tonumber(L, -1);
|
||||
|
||||
lua::pop(L, 3);
|
||||
|
||||
if (lua::getfield(L, "entities", 1)) {
|
||||
entities = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
if (lua::getfield(L, "ignore_uid", 1)) {
|
||||
entityRaycast.ignoredUid = lua::tointeger(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "filter_entities")) {
|
||||
load_entities_filter(L, -1, filteredEntities);
|
||||
blocksRaycast.filter = &filteredEntities;
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "entities_exclusion")) {
|
||||
entityRaycast.entityFilterExcludeMode = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "filter_blocks", 1)) {
|
||||
load_blocks_filter(L, -1, filteredBlocks);
|
||||
blocksRaycast.filter = &filteredBlocks;
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "blocks_exclusion")) {
|
||||
blocksRaycast.blocksFilterExcludeMode = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "nonselect_blocks", 1)) {
|
||||
blocksRaycast.includeNonSelectable = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
return l_raycast(
|
||||
L, start, dir, maxDistance, blocksRaycast, entityRaycast, entities
|
||||
);
|
||||
}
|
||||
|
||||
static int l_raycast(lua::State* L) {
|
||||
blocks_agent::RaycastSettings blocksRaycast {};
|
||||
Entities::RaycastSettings entityRaycast {};
|
||||
std::set<blockid_t> filteredBlocks {};
|
||||
|
||||
const int luaStackSize = lua::gettop(L);
|
||||
glm::vec3 start = lua::tovec<3>(L, 1);
|
||||
glm::vec3 dir = lua::tovec<3>(L, 2);
|
||||
float maxDistance = lua::tonumber(L, 3);
|
||||
|
||||
entityRaycast.ignoredUid = lua::tointeger(L, 4);
|
||||
if (luaStackSize >= 6) {
|
||||
if (lua::istable(L, 6)) {
|
||||
load_blocks_filter(L, 6, filteredBlocks);
|
||||
blocksRaycast.filter = &filteredBlocks;
|
||||
} else {
|
||||
throw std::runtime_error("table expected for filter");
|
||||
}
|
||||
}
|
||||
if (luaStackSize >= 7) {
|
||||
blocksRaycast.includeNonSelectable = lua::toboolean(L, 6);
|
||||
}
|
||||
return l_raycast(
|
||||
L, start, dir, maxDistance, blocksRaycast, entityRaycast, true
|
||||
);
|
||||
}
|
||||
|
||||
static int l_reload_component(lua::State* L) {
|
||||
|
|
@ -268,6 +380,7 @@ const luaL_Reg entitylib[] = {
|
|||
{"def_index", lua::wrap<l_def_index>},
|
||||
{"def_name", lua::wrap<l_def_name>},
|
||||
{"def_hitbox", lua::wrap<l_def_hitbox>},
|
||||
{"def_solid", lua::wrap<l_def_solid>},
|
||||
{"get_def", lua::wrap<l_get_def>},
|
||||
{"defs_count", lua::wrap<l_defs_count>},
|
||||
{"spawn", lua::wrap<l_spawn>},
|
||||
|
|
@ -278,6 +391,7 @@ const luaL_Reg entitylib[] = {
|
|||
{"get_all_in_box", lua::wrap<l_get_all_in_box>},
|
||||
{"get_all_in_radius", lua::wrap<l_get_all_in_radius>},
|
||||
{"raycast", lua::wrap<l_raycast>},
|
||||
{"__world_raycast", lua::wrap<l_world_raycast>},
|
||||
{"reload_component", lua::wrap<l_reload_component>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -237,6 +237,14 @@ static int l_get_all_data(lua::State* L, ItemStack& stack) {
|
|||
return lua::pushvalue(L, stack.getFields());
|
||||
}
|
||||
|
||||
static int l_set_all_data(lua::State* L, ItemStack& stack) {
|
||||
if (!lua::istable(L, 3)) {
|
||||
throw std::runtime_error("table expected as argument 1");
|
||||
}
|
||||
stack.setFields(lua::tovalue(L, 3), lua::toboolean(L, 4));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_has_data(lua::State* L, ItemStack& stack) {
|
||||
auto key = lua::tostring(L, 3);
|
||||
if (key == nullptr) {
|
||||
|
|
@ -267,6 +275,7 @@ const luaL_Reg inventorylib[] = {
|
|||
{"get_data", lua::wrap<wrap_slot<l_get_data>>},
|
||||
{"set_data", lua::wrap<wrap_slot<l_set_data>>},
|
||||
{"get_all_data", lua::wrap<wrap_slot<l_get_all_data>>},
|
||||
{"set_all_data", lua::wrap<wrap_slot<l_set_all_data>>},
|
||||
{"has_data", lua::wrap<wrap_slot<l_has_data>>},
|
||||
{"create", lua::wrap<l_create>},
|
||||
{"remove", lua::wrap<l_remove>},
|
||||
|
|
|
|||
105
src/logic/scripting/lua/libs/libxml.cpp
Normal file
105
src/logic/scripting/lua/libs/libxml.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#include "coders/xml.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
||||
static const char* TAG_ATTR = "#";
|
||||
static const char* DEFAULT_ROOT_TAG = "root";
|
||||
|
||||
static int push_xml(lua::State* L, const xml::xmlelement& elem) {
|
||||
if (elem.isText()) {
|
||||
return lua::pushlstring(L, elem.getInnerText());
|
||||
}
|
||||
|
||||
const auto& attrs = elem.getAttributes();
|
||||
const auto& elems = elem.getElements();
|
||||
const auto& tag = elem.getTag();
|
||||
|
||||
lua::createtable(L, elem.size(), attrs.size() + 1);
|
||||
lua::pushlstring(L, tag);
|
||||
lua::setfield(L, TAG_ATTR);
|
||||
|
||||
for (int i = 0; i < elems.size(); i++) {
|
||||
push_xml(L, *elems[i]);
|
||||
lua::rawseti(L, i + 1);
|
||||
}
|
||||
|
||||
for (const auto& [key, value] : attrs) {
|
||||
lua::pushlstring(L, value.getText());
|
||||
lua::setfield(L, key);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static std::unique_ptr<xml::xmlelement> toxml(lua::State* L) {
|
||||
lua::getfield(L, TAG_ATTR);
|
||||
auto tag = lua::require_lstring(L, -1);
|
||||
lua::pop(L);
|
||||
|
||||
auto elem = std::make_unique<xml::xmlelement>(std::string(tag));
|
||||
int length = lua::objlen(L, -1);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
lua::rawgeti(L, i + 1);
|
||||
elem->add(toxml(L));
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
lua::pushnil(L);
|
||||
while (lua::next(L, -2)) {
|
||||
lua::pushvalue(L, -2);
|
||||
do {
|
||||
if (!lua::isstring(L, -1)) {
|
||||
break;
|
||||
}
|
||||
auto key = lua::require_lstring(L, -1);
|
||||
if (key == "@") {
|
||||
break;
|
||||
}
|
||||
auto value = lua::require_lstring(L, -2);
|
||||
elem->set(std::string(key), std::string(value));
|
||||
} while (false);
|
||||
|
||||
lua::pop(L, 2);
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
static int l_tostring(lua::State* L) {
|
||||
bool nice = lua::isboolean(L, 2) ? lua::toboolean(L, 2) : true;
|
||||
lua::pushvalue(L, 1);
|
||||
auto xml = toxml(L);
|
||||
lua::pop(L);
|
||||
return lua::pushstring(L, xml::stringify(*xml, nice));
|
||||
}
|
||||
|
||||
static int l_parse(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto document = xml::parse("[string]", string);
|
||||
return push_xml(L, *document->getRoot());
|
||||
}
|
||||
|
||||
|
||||
static int l_parse_vcd(lua::State* L) {
|
||||
auto string = lua::require_string(L, 1);
|
||||
auto rootTag = lua::tostring(L, 2);
|
||||
auto document = xml::parse_vcm(
|
||||
"[string]", string, rootTag ? rootTag : ""
|
||||
);
|
||||
const auto& root = *document->getRoot();
|
||||
if (rootTag != nullptr) {
|
||||
return push_xml(L, root);
|
||||
}
|
||||
int rootElements = root.size();
|
||||
if (rootElements != 1) {
|
||||
throw std::runtime_error(
|
||||
"one root element expected, got " + std::to_string(rootElements)
|
||||
);
|
||||
}
|
||||
return push_xml(L, *root.getElements()[0]);
|
||||
}
|
||||
|
||||
const luaL_Reg xmllib[] = {
|
||||
{"tostring", lua::wrap<l_tostring>},
|
||||
{"parse", lua::wrap<l_parse>},
|
||||
{"parse_vcd", lua::wrap<l_parse_vcd>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
@ -66,6 +66,7 @@ static void create_libs(State* L, StateType stateType) {
|
|||
openlib(L, "vec2", vec2lib);
|
||||
openlib(L, "vec3", vec3lib);
|
||||
openlib(L, "vec4", vec4lib);
|
||||
openlib(L, "xml", xmllib);
|
||||
openlib(L, "yaml", yamllib);
|
||||
|
||||
openlib(L, "__vc_app", applib);
|
||||
|
|
|
|||
|
|
@ -81,16 +81,17 @@ union RGBA {
|
|||
uint8_t arr[4];
|
||||
uint32_t rgba;
|
||||
};
|
||||
static_assert(sizeof(RGBA) == 4);
|
||||
|
||||
static RGBA* get_at(const ImageData& data, uint index) {
|
||||
if (index >= data.getWidth() * data.getHeight()) {
|
||||
return nullptr;
|
||||
}
|
||||
static RGBA* get_at(const ImageData& data, size_t index) {
|
||||
return reinterpret_cast<RGBA*>(data.getData() + index * sizeof(RGBA));
|
||||
}
|
||||
|
||||
static RGBA* get_at(const ImageData& data, uint x, uint y) {
|
||||
return get_at(data, y * data.getWidth() + x);
|
||||
if (x >= data.getWidth() || y >= data.getHeight()) {
|
||||
return nullptr;
|
||||
}
|
||||
return get_at(data, static_cast<size_t>(y) * data.getWidth() + x);
|
||||
}
|
||||
|
||||
static RGBA* get_at(State* L, uint x, uint y) {
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ namespace scripting {
|
|||
void on_entity_grounded(const Entity& entity, float force);
|
||||
void on_entity_fall(const Entity& entity);
|
||||
void on_entity_save(const Entity& entity);
|
||||
void on_entity_player_set(const Entity& entity, int64_t pid);
|
||||
void on_entities_update(int tps, int parts, int part);
|
||||
void on_entities_physics_update(float delta);
|
||||
void on_entities_render(float delta);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ static void init_component_events(
|
|||
funcsset.on_aim_off = lua::hasfield(L, "on_aim_off");
|
||||
funcsset.on_attacked = lua::hasfield(L, "on_attacked");
|
||||
funcsset.on_used = lua::hasfield(L, "on_used");
|
||||
funcsset.on_player_set = lua::hasfield(L, "on_player_set");
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +239,15 @@ void scripting::on_entity_save(const Entity& entity) {
|
|||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_player_set(const Entity& entity, int64_t pid) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_player_set",
|
||||
&EntityFuncsSet::on_player_set,
|
||||
[pid](auto L) { return lua::pushinteger(L, pid); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_sensor_enter(
|
||||
const Entity& entity, size_t index, entityid_t oid
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -166,8 +166,7 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
|||
glm::vec3 start,
|
||||
glm::vec3 dir,
|
||||
float maxDistance,
|
||||
entityid_t ignore,
|
||||
bool solidOnly
|
||||
const RaycastSettings& settings
|
||||
) {
|
||||
Ray ray(start, dir);
|
||||
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||
|
|
@ -176,10 +175,18 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
|||
glm::ivec3 foundNormal;
|
||||
|
||||
for (auto [entity, eid, transform, body] : view.each()) {
|
||||
if (eid.uid == ignore || !body.enabled || (solidOnly && !eid.def.solid)) {
|
||||
if (eid.uid == settings.ignoredUid || !body.enabled ||
|
||||
(settings.solidEntitiesOnly && !eid.def.solid)) {
|
||||
continue;
|
||||
}
|
||||
auto& hitbox = body.hitbox;
|
||||
if (settings.entitiesFilter) {
|
||||
bool matches = settings.entitiesFilter->find(eid.def.rt.id) !=
|
||||
settings.entitiesFilter->end();
|
||||
if (matches == settings.entityFilterExcludeMode) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const auto& hitbox = body.hitbox;
|
||||
glm::ivec3 normal;
|
||||
double distance;
|
||||
if (ray.intersectAABB(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "constants.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "Transform.hpp"
|
||||
#include "Rigidbody.hpp"
|
||||
|
|
@ -46,12 +47,6 @@ class Entities final {
|
|||
);
|
||||
void preparePhysics(float delta);
|
||||
public:
|
||||
struct RaycastResult {
|
||||
entityid_t entity;
|
||||
glm::ivec3 normal;
|
||||
float distance;
|
||||
};
|
||||
|
||||
Entities(Level& level);
|
||||
|
||||
~Entities();
|
||||
|
|
@ -82,20 +77,33 @@ public:
|
|||
|
||||
std::optional<Entity> get(entityid_t id);
|
||||
|
||||
struct RaycastSettings {
|
||||
/// @param ignore Ignored entity uid
|
||||
entityid_t ignoredUid;
|
||||
/// @param solidEntitiesOnly If true, only entities with solid hitboxes
|
||||
/// will be checked
|
||||
bool solidEntitiesOnly = false;
|
||||
bool entityFilterExcludeMode = false;
|
||||
const std::set<entitydefid_t>* entitiesFilter = nullptr;
|
||||
};
|
||||
|
||||
struct RaycastResult {
|
||||
entityid_t entity;
|
||||
glm::ivec3 normal;
|
||||
float distance;
|
||||
};
|
||||
|
||||
/// @brief Entities raycast. No blocks check included, use combined with
|
||||
/// Chunks.rayCast
|
||||
/// @param start Ray start
|
||||
/// @param dir Ray direction normalized vector
|
||||
/// @param maxDistance Max ray length
|
||||
/// @param ignore Ignored entity ID
|
||||
/// @param solidOnly If true, only entities with solid hitboxes will be checked
|
||||
/// @return An optional structure containing entity, normal and distance
|
||||
std::optional<RaycastResult> rayCast(
|
||||
glm::vec3 start,
|
||||
glm::vec3 dir,
|
||||
float maxDistance,
|
||||
entityid_t ignore = -1,
|
||||
bool solidOnly = false
|
||||
const RaycastSettings& settings
|
||||
);
|
||||
|
||||
void loadEntities(dv::value map);
|
||||
|
|
|
|||
|
|
@ -113,6 +113,11 @@ int64_t Entity::getPlayer() const {
|
|||
}
|
||||
|
||||
void Entity::setPlayer(int64_t id) {
|
||||
registry.get<EntityId>(entity).player = id;
|
||||
auto& eid = registry.get<EntityId>(entity);
|
||||
if (eid.player == id) {
|
||||
return;
|
||||
}
|
||||
eid.player = id;
|
||||
scripting::on_entity_player_set(*this, id);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ void Player::updateEntity() {
|
|||
}
|
||||
}
|
||||
|
||||
Hitbox* Player::getHitbox() {
|
||||
Hitbox* Player::getHitbox() const {
|
||||
if (auto entity = level.entities->get(eid)) {
|
||||
return &entity->getRigidbody().hitbox;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ public:
|
|||
|
||||
bool isCurrentCameraBuiltin() const;
|
||||
|
||||
Hitbox* getHitbox();
|
||||
Hitbox* getHitbox() const;
|
||||
|
||||
void setSpawnPoint(glm::vec3 point);
|
||||
glm::vec3 getSpawnPoint() const;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ struct EntityFuncsSet {
|
|||
bool on_aim_off;
|
||||
bool on_attacked;
|
||||
bool on_used;
|
||||
bool on_player_set;
|
||||
};
|
||||
|
||||
struct UserComponent {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@
|
|||
|
||||
namespace util {
|
||||
template<typename T>
|
||||
inline T read_int_le(const uint8_t* src, size_t offset=0) {
|
||||
return dataio::le2h(*(reinterpret_cast<const T*>(src) + offset));
|
||||
inline T read_int_le(const uint8_t* src) {
|
||||
T tmp;
|
||||
std::memcpy(&tmp, src, sizeof(T));
|
||||
return dataio::le2h(tmp);
|
||||
}
|
||||
|
||||
/// @brief Simple heap implementation for memory-optimal sparse array of
|
||||
|
|
@ -87,8 +89,7 @@ namespace util {
|
|||
std::memset(found, 0, entrySize);
|
||||
return found;
|
||||
}
|
||||
this->free(found);
|
||||
return allocate(index, size);
|
||||
free(found);
|
||||
}
|
||||
for (size_t i = 0; i < entriesCount; i++) {
|
||||
auto data = buffer.data() + offset;
|
||||
|
|
@ -110,9 +111,12 @@ namespace util {
|
|||
entriesCount++;
|
||||
|
||||
auto data = buffer.data() + offset;
|
||||
*reinterpret_cast<Tindex*>(data) = dataio::h2le(index);
|
||||
|
||||
Tindex indexTmp = dataio::h2le(index);
|
||||
Tsize sizeTmp = dataio::h2le(size);
|
||||
std::memcpy(data, &indexTmp, sizeof(Tindex));
|
||||
data += sizeof(Tindex);
|
||||
*reinterpret_cast<Tsize*>(data) = dataio::h2le(size);
|
||||
std::memcpy(data, &sizeTmp, sizeof(Tsize));
|
||||
return data + sizeof(Tsize);
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +126,7 @@ namespace util {
|
|||
if (ptr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return read_int_le<Tsize>(ptr, -1);
|
||||
return read_int_le<Tsize>(ptr - sizeof(Tsize));
|
||||
}
|
||||
|
||||
/// @return number of entries
|
||||
|
|
@ -147,7 +151,8 @@ namespace util {
|
|||
ubyte* dst = out.data();
|
||||
const ubyte* src = buffer.data();
|
||||
|
||||
*reinterpret_cast<Tindex*>(dst) = dataio::h2le(entriesCount);
|
||||
Tindex countTmp = dataio::h2le(entriesCount);
|
||||
std::memcpy(dst, &countTmp, sizeof(Tindex));
|
||||
dst += sizeof(Tindex);
|
||||
|
||||
std::memcpy(dst, src, buffer.size());
|
||||
|
|
@ -172,7 +177,7 @@ namespace util {
|
|||
) : buffer(buffer), index(index), offset(offset) {}
|
||||
|
||||
Tsize size() const {
|
||||
return read_int_le<Tsize>(buffer.data() + offset, -1);
|
||||
return read_int_le<Tsize>(buffer.data() + offset - sizeof(Tsize));
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& o) const {
|
||||
|
|
|
|||
|
|
@ -215,11 +215,10 @@ voxel* Chunks::rayCast(
|
|||
glm::vec3& end,
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter,
|
||||
bool includeNonSelectable
|
||||
const blocks_agent::RaycastSettings& settings
|
||||
) const {
|
||||
return blocks_agent::raycast(
|
||||
*this, start, dir, maxDist, end, norm, iend, std::move(filter), includeNonSelectable
|
||||
*this, start, dir, maxDist, end, norm, iend, settings
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ class VoxelsVolume;
|
|||
|
||||
template <int w, int h, int d> class StaticVoxelsVolume;
|
||||
|
||||
namespace blocks_agent {
|
||||
struct RaycastSettings;
|
||||
}
|
||||
|
||||
/// Player-centred chunks matrix
|
||||
class Chunks {
|
||||
LevelEvents* events;
|
||||
|
|
@ -112,8 +116,7 @@ public:
|
|||
glm::vec3& end,
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter = {},
|
||||
bool includeNonSelectable = false
|
||||
const blocks_agent::RaycastSettings& settings
|
||||
) const;
|
||||
|
||||
glm::vec3 rayCastToObstacle(
|
||||
|
|
|
|||
|
|
@ -223,8 +223,7 @@ static inline voxel* raycast_blocks(
|
|||
glm::vec3& end,
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter,
|
||||
bool includeNonSelectable
|
||||
const RaycastSettings& settings
|
||||
) {
|
||||
const auto& blocks = chunks.getContentIndices().blocks;
|
||||
float px = start.x;
|
||||
|
|
@ -240,9 +239,8 @@ static inline voxel* raycast_blocks(
|
|||
int iy = std::floor(py);
|
||||
int iz = std::floor(pz);
|
||||
|
||||
int stepx = (dx > 0.0f) ? 1 : -1;
|
||||
int stepy = (dy > 0.0f) ? 1 : -1;
|
||||
int stepz = (dz > 0.0f) ? 1 : -1;
|
||||
glm::ivec3 step {
|
||||
(dx > 0.0f) ? 1 : -1, (dy > 0.0f) ? 1 : -1, (dz > 0.0f) ? 1 : -1};
|
||||
|
||||
constexpr float infinity = std::numeric_limits<float>::infinity();
|
||||
constexpr float epsilon = 1e-6f; // 0.000001
|
||||
|
|
@ -250,31 +248,31 @@ static inline voxel* raycast_blocks(
|
|||
float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy);
|
||||
float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz);
|
||||
|
||||
float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix);
|
||||
float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy);
|
||||
float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz);
|
||||
float xdist = (step.x > 0) ? (ix + 1 - px) : (px - ix);
|
||||
float ydist = (step.y > 0) ? (iy + 1 - py) : (py - iy);
|
||||
float zdist = (step.z > 0) ? (iz + 1 - pz) : (pz - iz);
|
||||
|
||||
float txMax = (txDelta < infinity) ? txDelta * xdist : infinity;
|
||||
float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity;
|
||||
float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity;
|
||||
|
||||
int steppedIndex = -1;
|
||||
|
||||
bool includeNonSelectable = settings.includeNonSelectable;
|
||||
|
||||
while (t <= maxDist) {
|
||||
voxel* voxel = get(chunks, ix, iy, iz);
|
||||
if (voxel == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto filter = settings.filter;
|
||||
const auto& def = blocks.require(voxel->id);
|
||||
if (voxel->id != BLOCK_AIR && (def.selectable || includeNonSelectable) &&
|
||||
(filter.empty() || filter.find(def.rt.id) == filter.end())) {
|
||||
end.x = px + t * dx;
|
||||
end.y = py + t * dy;
|
||||
end.z = pz + t * dz;
|
||||
iend.x = ix;
|
||||
iend.y = iy;
|
||||
iend.z = iz;
|
||||
if (voxel->id != BLOCK_AIR &&
|
||||
(def.selectable || includeNonSelectable) &&
|
||||
((filter == nullptr || filter->find(def.rt.id) == filter->end()) ==
|
||||
settings.blocksFilterExcludeMode)) {
|
||||
end = start + t * dir;
|
||||
iend = {ix, iy, iz};
|
||||
|
||||
if (!def.rt.solid) {
|
||||
const std::vector<AABB>& hitboxes =
|
||||
|
|
@ -309,51 +307,43 @@ static inline voxel* raycast_blocks(
|
|||
|
||||
if (hit) return voxel;
|
||||
} else {
|
||||
iend.x = ix;
|
||||
iend.y = iy;
|
||||
iend.z = iz;
|
||||
|
||||
norm.x = norm.y = norm.z = 0;
|
||||
if (steppedIndex == 0) norm.x = -stepx;
|
||||
if (steppedIndex == 1) norm.y = -stepy;
|
||||
if (steppedIndex == 2) norm.z = -stepz;
|
||||
iend = {ix, iy, iz};
|
||||
norm = {0, 0, 0};
|
||||
if (steppedIndex != -1) {
|
||||
norm[steppedIndex] = -step[steppedIndex];
|
||||
}
|
||||
return voxel;
|
||||
}
|
||||
}
|
||||
if (txMax < tyMax) {
|
||||
if (txMax < tzMax) {
|
||||
ix += stepx;
|
||||
ix += step.x;
|
||||
t = txMax;
|
||||
txMax += txDelta;
|
||||
steppedIndex = 0;
|
||||
} else {
|
||||
iz += stepz;
|
||||
iz += step.z;
|
||||
t = tzMax;
|
||||
tzMax += tzDelta;
|
||||
steppedIndex = 2;
|
||||
}
|
||||
} else {
|
||||
if (tyMax < tzMax) {
|
||||
iy += stepy;
|
||||
iy += step.y;
|
||||
t = tyMax;
|
||||
tyMax += tyDelta;
|
||||
steppedIndex = 1;
|
||||
} else {
|
||||
iz += stepz;
|
||||
iz += step.z;
|
||||
t = tzMax;
|
||||
tzMax += tzDelta;
|
||||
steppedIndex = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
iend.x = ix;
|
||||
iend.y = iy;
|
||||
iend.z = iz;
|
||||
|
||||
end.x = px + t * dx;
|
||||
end.y = py + t * dy;
|
||||
end.z = pz + t * dz;
|
||||
norm.x = norm.y = norm.z = 0;
|
||||
iend = {ix, iy, iz};
|
||||
end = start + t * dir;
|
||||
norm = {0, 0, 0};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -365,10 +355,9 @@ voxel* blocks_agent::raycast(
|
|||
glm::vec3& end,
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter,
|
||||
bool includeNonSelectable
|
||||
const RaycastSettings& settings
|
||||
) {
|
||||
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter, includeNonSelectable);
|
||||
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, settings);
|
||||
}
|
||||
|
||||
voxel* blocks_agent::raycast(
|
||||
|
|
@ -379,10 +368,9 @@ voxel* blocks_agent::raycast(
|
|||
glm::vec3& end,
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter,
|
||||
bool includeNonSelectable
|
||||
const RaycastSettings& settings
|
||||
) {
|
||||
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter, includeNonSelectable);
|
||||
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, settings);
|
||||
}
|
||||
|
||||
// reduce nesting on next modification
|
||||
|
|
|
|||
|
|
@ -412,6 +412,12 @@ inline void set_rotation(
|
|||
}
|
||||
}
|
||||
|
||||
struct RaycastSettings {
|
||||
const std::set<blockid_t>* filter = nullptr;
|
||||
bool blocksFilterExcludeMode = true;
|
||||
bool includeNonSelectable = false;
|
||||
};
|
||||
|
||||
/// @brief Cast ray to a selectable block with filter based on id.
|
||||
/// @param chunks chunks matrix
|
||||
/// @param start ray start position
|
||||
|
|
@ -431,8 +437,7 @@ voxel* raycast(
|
|||
glm::vec3& end,
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter,
|
||||
bool includeNonSelectable
|
||||
const RaycastSettings& settings
|
||||
);
|
||||
|
||||
/// @brief Cast ray to a selectable block with filter based on id.
|
||||
|
|
@ -454,8 +459,7 @@ voxel* raycast(
|
|||
glm::vec3& end,
|
||||
glm::ivec3& norm,
|
||||
glm::ivec3& iend,
|
||||
std::set<blockid_t> filter,
|
||||
bool includeNonSelectable
|
||||
const RaycastSettings& settings
|
||||
);
|
||||
|
||||
void get_voxels(
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ static void init_gl_extensions_list() {
|
|||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static bool is_gl_extension_supported(const char *extension) {
|
||||
if (!extension || !*extension) {
|
||||
return false;
|
||||
|
|
@ -40,6 +41,7 @@ static bool is_gl_extension_supported(const char *extension) {
|
|||
return supported_gl_extensions.find(extension) != supported_gl_extensions.end();
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
static const char* gl_error_name(int error) {
|
||||
switch (error) {
|
||||
case GL_DEBUG_TYPE_ERROR: return "ERROR";
|
||||
|
|
@ -80,6 +82,7 @@ static void GLAPIENTRY gl_message_callback(
|
|||
logger.warning() << "GL:" << gl_error_name(type) << ":"
|
||||
<< gl_severity_name(severity) << ": " << message;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool initialize_gl(int width, int height) {
|
||||
glewExperimental = GL_TRUE;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue