Merge branch 'main' into animation

This commit is contained in:
MihailRis 2026-07-27 23:09:14 +03:00
commit a5b1259c5b
24 changed files with 111 additions and 28 deletions

View file

@ -4,14 +4,15 @@
The appearance of 3D text, is configured via a table, like [particles](particles.md). All fields are optional.
| Field | Description | Default |
| --------------- | ---------------------------- | ---------------- |
| display | Display format | static_billboard |
| color | Text color | {1, 1, 1, 1} |
| scale | Text scale | 1 |
| render_distance | Text rendering distance | 32 |
| xray_opacity | Visibility through obstacles | 0 |
| perspective | Perspective coefficient | 1 |
| Field | Description | Default |
| --------------- | ---------------------------------------- | ---------------- |
| display | Display format | static_billboard |
| color | Text color | {1, 1, 1, 1} |
| scale | Text scale | 1 |
| render_distance | Text rendering distance | 32 |
| xray_opacity | Visibility through obstacles | 0 |
| perspective | Perspective coefficient | 1 |
| font | Font. If absent, the default one is used | "" |
Available display formats:

View file

@ -61,12 +61,16 @@ Determines how the physics engine will work with it.
### *blocking*
Determines whether the entity blocks installation of blocks.
If true - the entity blocks the placement of a block that intersects with the entity's hitbox.
*In the future will also block other entities movement.*
Default value: *true*.
### *selectable*
If set to `false` the cursor will ignore the entity, passing the ray.
### *sensors*
A sensor is an area attached to a physical body that detects the entry of other bodies into it.

View file

@ -92,6 +92,8 @@ world.raycast(
-- exclusion filtering mode:
-- if true, filter_entities defines the list of ignored entity types
entities_exclusion: bool = false,
-- include entities with selectable=false
nonselect_entities: bool = false;
[==[ additional parameters (blocks) ]==]
-- id of the block types used for filtering

View file

@ -12,6 +12,7 @@
| render_distance | Дистанция отрисовки текста | 32 |
| xray_opacity | Коэффициент видимости через препятствия (просвечивание) | 0 |
| perspective | Коэффициент перспективы | 1 |
| font | Шрифт. При отсутствии используется основной | "" |
Доступные форматы отображения:

View file

@ -61,12 +61,16 @@
### Блокирование - *blocking*
Определяет блокирует ли сущность установку блоков.
Определяет, блокирует ли сущность установку блока, пересекающегося с хитбоксом сущности.
*В будущем будет также блокировать движение других сущностей.*
Значение по-умолчанию: *true*.
### Выделяемость - *selectable*
При значении в `false` курсор будет игнорировать cущность, пропуская луч далее.
### Список сенсоров - *sensors*
Сенсор - область пространства, привязанная к физическому телу, детектирующее попадание в него других тел.

View file

@ -91,6 +91,8 @@ world.raycast(
-- исключающий режим фильтрации:
-- при значении (true) filter_entities определяет список игнорируемых
entities_exclusion: bool = false,
-- учитывать сущности с selectable=false
nonselect_entities: bool = false;
[==[ дополнительные параметры (блоки) ]==]
-- id типов блоков используемых для фильтрации

View file

@ -10,7 +10,7 @@ dropitem = ARGS
if dropitem.item then
dropitem.id = item.index(dropitem.item)
end
if dropitem then
if dropitem then
timer = dropitem.pickup_delay or timer
end
if SAVED_DATA.item then
@ -26,6 +26,7 @@ function on_save()
end
if vc.is_client() then
local SCALE = 0.3
local scale = {1, 1, 1}
local rotation = mat4.rotate({
math.random(), math.random(), math.random()
@ -35,7 +36,7 @@ if vc.is_client() then
local matrix = mat4.idt()
rig:set_model(0, item.model_name(dropitem.id))
local bodysize = math.min(scale[1], scale[2], scale[3])
body:set_size({scale[1], bodysize, scale[3]})
body:set_size(vec3.mul({scale[1], bodysize, scale[3]}, SCALE))
mat4.mul(matrix, rotation, matrix)
mat4.scale(matrix, scale, matrix)
rig:set_matrix(0, matrix)

View file

@ -188,6 +188,7 @@ public:
ContentUnitDefs<GeneratorDef> generators;
std::unique_ptr<DrawGroups> const drawGroups;
ResourceIndicesSet resourceIndices {};
std::set<std::string> components {};
Content(
std::unique_ptr<ContentIndices> indices,

View file

@ -26,17 +26,18 @@ std::unique_ptr<Content> ContentBuilder::build() {
def.rt.tags.insert(tags.add(tag));
}
auto isSolid = [](const Variant& variant) {
return variant.model.type == BlockModelType::BLOCK ||
variant.culling == CullingMode::OPTIONAL;
};
if (def.variants) {
for (auto& variant : def.variants->variants) {
variant.rt.solid =
variant.model.type == BlockModelType::BLOCK ||
def.explictlySolid;
variant.rt.solid = isSolid(variant) || def.explictlySolid;
}
def.defaults = def.variants->variants.at(0);
} else {
def.defaults.rt.solid =
def.defaults.model.type == BlockModelType::BLOCK ||
def.explictlySolid;
def.defaults.rt.solid = isSolid(def.defaults) || def.explictlySolid;
}
const float EPSILON = 0.01f;

View file

@ -506,6 +506,7 @@ void ContentLoader::loadScripts(Content& content) {
scripting::on_scripts_loading();
load_scripts(content, content.blocks);
load_scripts(content, content.items);
auto& tmpContent = content;
for (const auto& [packid, runtime] : content.getPacks()) {
auto env = runtime->getEnvironment();
@ -516,7 +517,7 @@ void ContentLoader::loadScripts(Content& content) {
// Load entity components
io::path componentsDir = folder / "scripts/components";
foreach_file(componentsDir, [&pack, env](const io::path& file) {
foreach_file(componentsDir, [&pack, env, &tmpContent](const io::path& file) {
auto name = pack.id + ":" + file.stem();
scripting::load_entity_component(
env,
@ -524,9 +525,21 @@ void ContentLoader::loadScripts(Content& content) {
file,
pack.id + ":scripts/components/" + file.name()
);
tmpContent.components.insert(name);
});
}
for (const auto& [eid, def] : content.entities.getDefs()) {
for (const auto& instance : def->components) {
if (content.components.find(instance.component) == content.components.end()) {
throw std::runtime_error(
"component " + instance.component +
" is not available (required by entity " + eid + ")"
);
}
}
}
scripting::on_content_loaded();
}

View file

@ -89,6 +89,7 @@ template<> void ContentUnitLoader<EntityDef>::loadUnit(
root.at("skeleton-name").get(def.skeletonName);
root.at("material").get(def.material);
root.at("blocking").get(def.blocking);
root.at("selectable").get(def.selectable);
root.at("solid").get(def.solid);
root.at("mass").get(def.mass);
root.at("elasticity").get(def.elasticity);

View file

@ -30,6 +30,7 @@ void ContentGfxCache::refreshVariant(
uint8_t variantIndex,
const Atlas& atlas
) {
std::unordered_map<std::string, std::string> optionalTextures;
bool denseRender = settings.denseRender.get();
for (uint side = 0; side < 6; side++) {
std::string tex = variant.textureFaces[side];
@ -43,6 +44,7 @@ void ContentGfxCache::refreshVariant(
texOpaque = tex;
} else if (variant.culling == CullingMode::OPTIONAL && !denseRender) {
tex = texOpaque;
optionalTextures[variant.textureFaces[side]] = texOpaque;
}
size_t index = getRegionIndex(def.rt.id, variantIndex, side, false);
sideregions[index] = atlas.get(tex);
@ -56,7 +58,12 @@ void ContentGfxCache::refreshVariant(
if (pos == std::string::npos) {
continue;
}
if (auto region = atlas.getIf(mesh.texture.substr(pos+1))) {
std::string texture = mesh.texture.substr(pos+1);
const auto& found = optionalTextures.find(texture);
if (found != optionalTextures.end()) {
texture = found->second;
}
if (auto region = atlas.getIf(texture)) {
for (auto& vertex : mesh.vertices) {
vertex.uv = region->apply(vertex.uv);
}

View file

@ -162,6 +162,8 @@ static inline void draw_text(
const FontStylesScheme* styles,
size_t styleMapOffset
) {
bool is3d = std::is_same<Batch, Batch3D>();
static FontStylesScheme defStyles {{{}}, {0}};
if (styles == nullptr) {
@ -194,13 +196,26 @@ static inline void draw_text(
float advance = baseAdvance;
if (auto glyph = font.getGlyph(c)) {
yOffset = glyph->yOffset;
advance = glyph->xAdvance;
advance = glyph->xAdvance /
static_cast<float>(font.getLineHeight()) * 2.0f *
baseAdvance;
}
uint charpage = c >> 8;
if (charpage == page){
batch.texture(font.getPage(charpage));
draw_glyph(
batch, pos, glm::vec2(x, y - yOffset / static_cast<float>(font.getLineHeight())), c, right, up, interval, style
batch,
pos,
glm::vec2(
x,
y - yOffset * (is3d ? -1 : 1) /
static_cast<float>(font.getLineHeight())
),
c,
right,
up,
interval,
style
);
}
else if (charpage > page && charpage < next){

View file

@ -538,7 +538,6 @@ void BlocksRenderer::render(
break;
}
case BlockModelType::CUSTOM: {
if (!denseRender)
blockCustomModel(
{x, y, z},
def,

View file

@ -58,7 +58,10 @@ void TextsRenderer::renderNote(
}
opacity = preset.xrayOpacity;
}
auto& font = assets.require<Font>(FONT_DEFAULT);
auto specifiedFont = assets.get<Font>(preset.font);
auto& font =
specifiedFont ? *specifiedFont : assets.require<Font>(FONT_DEFAULT);
glm::vec3 xvec = note.getAxisX();
glm::vec3 yvec = note.getAxisY();
@ -76,7 +79,10 @@ void TextsRenderer::renderNote(
yvec = camera.up;
}
float scale =
(1.0f - preset.perspective) * glm::pow(glm::distance(camera.position, pos), 1.0f-preset.perspective);
(1.0f - preset.perspective) *
glm::pow(
glm::distance(camera.position, pos), 1.0f - preset.perspective
);
xvec *= 1.0f + scale;
yvec *= 1.0f + scale;
}
@ -118,6 +124,7 @@ void TextsRenderer::renderNote(
}
auto color = preset.color;
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
font.draw(
batch,
text,

View file

@ -311,6 +311,10 @@ static int l_world_raycast(lua::State* L) {
entityRaycast.entityFilterExcludeMode = lua::toboolean(L, -1);
lua::pop(L);
}
if (lua::getfield(L, "nonselect_entities", 1)) {
entityRaycast.includeNonSelectable = lua::toboolean(L, -1);
lua::pop(L);
}
if (lua::getfield(L, "filter_blocks", 1)) {
load_blocks_filter(L, -1, filteredBlocks);
blocksRaycast.filter = &filteredBlocks;

View file

@ -744,9 +744,10 @@ void scripting::load_entity_component(
const io::path& file,
const std::string& fileName
) {
logger.info() << "script (component) " << file.string();
auto L = lua::get_main_state();
std::string src = io::read_string(file);
logger.info() << "script (component) " << file.string();
lua::loadbuffer(L, *env, src, fileName);
lua::store_in_registry(L, lua::CHUNKS_TABLE, name);
}

View file

@ -153,7 +153,12 @@ void scripting::on_entity_spawn(
lua::call(L, 1);
}
for (auto& component : components) {
create_component(L, -1, *component, args, saved);
try {
create_component(L, -1, *component, args, saved);
} catch (const std::runtime_error& err) {
logger.error() << "could not to initialize user component "
<< component->name << ": " << err.what();
}
}
for (auto& [packid, pack] : content->getPacks()) {
@ -171,6 +176,9 @@ static void process_entity_callback(
const std::string& name,
std::function<int(lua::State*)> args
) {
if (env == nullptr) {
return;
}
auto L = lua::get_main_state();
lua::pushenv(L, *env);
if (lua::hasfield(L, "__disabled")) {

View file

@ -176,7 +176,8 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
for (auto [entity, eid, transform, body] : view.each()) {
if (eid.uid == settings.ignoredUid || !body.enabled ||
(settings.solidEntitiesOnly && !eid.def.solid)) {
(settings.solidEntitiesOnly && !eid.def.solid) ||
(!eid.def.selectable && !settings.includeNonSelectable)) {
continue;
}
if (settings.entitiesFilter) {

View file

@ -85,6 +85,7 @@ public:
bool solidEntitiesOnly = false;
bool entityFilterExcludeMode = false;
const std::set<entitydefid_t>* entitiesFilter = nullptr;
bool includeNonSelectable = false;
};
struct RaycastResult {

View file

@ -71,6 +71,9 @@ dv::value Entity::serialize() const {
if (!scripts.components.empty()) {
auto& compsMap = root.object("comps");
for (auto& comp : scripts.components) {
if (comp->env == nullptr) {
continue;
}
auto data =
scripting::get_component_value(comp->env, SAVED_DATA_VARNAME);
compsMap[comp->name] = data;

View file

@ -42,6 +42,9 @@ struct EntityDef {
/// @brief Solid entity material material
std::string material;
/// @brief Can the entity be interacted with cursor (on_attack, on_use)
bool selectable = true;
/// @brief Does entity prevent blocks setup
bool blocking = true;

View file

@ -13,6 +13,7 @@ dv::value NotePreset::serialize() const {
{"render_distance", renderDistance},
{"xray_opacity", xrayOpacity},
{"perspective", perspective},
{"font", font},
});
}
@ -27,4 +28,5 @@ void NotePreset::deserialize(const dv::value& src) {
src.at("render_distance").get(renderDistance);
src.at("xray_opacity").get(xrayOpacity);
src.at("perspective").get(perspective);
src.at("font").get(font);
}

View file

@ -28,6 +28,7 @@ struct NotePreset : public Serializable {
float renderDistance = 32.0f;
float xrayOpacity = 0.0f;
float perspective = 1.0f;
std::string font {};
dv::value serialize() const override;
void deserialize(const dv::value& src) override;