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

View file

@ -61,12 +61,16 @@ Determines how the physics engine will work with it.
### *blocking* ### *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.* *In the future will also block other entities movement.*
Default value: *true*. Default value: *true*.
### *selectable*
If set to `false` the cursor will ignore the entity, passing the ray.
### *sensors* ### *sensors*
A sensor is an area attached to a physical body that detects the entry of other bodies into it. 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: -- exclusion filtering mode:
-- if true, filter_entities defines the list of ignored entity types -- if true, filter_entities defines the list of ignored entity types
entities_exclusion: bool = false, entities_exclusion: bool = false,
-- include entities with selectable=false
nonselect_entities: bool = false;
[==[ additional parameters (blocks) ]==] [==[ additional parameters (blocks) ]==]
-- id of the block types used for filtering -- id of the block types used for filtering

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -506,6 +506,7 @@ void ContentLoader::loadScripts(Content& content) {
scripting::on_scripts_loading(); scripting::on_scripts_loading();
load_scripts(content, content.blocks); load_scripts(content, content.blocks);
load_scripts(content, content.items); load_scripts(content, content.items);
auto& tmpContent = content;
for (const auto& [packid, runtime] : content.getPacks()) { for (const auto& [packid, runtime] : content.getPacks()) {
auto env = runtime->getEnvironment(); auto env = runtime->getEnvironment();
@ -516,7 +517,7 @@ void ContentLoader::loadScripts(Content& content) {
// Load entity components // Load entity components
io::path componentsDir = folder / "scripts/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(); auto name = pack.id + ":" + file.stem();
scripting::load_entity_component( scripting::load_entity_component(
env, env,
@ -524,9 +525,21 @@ void ContentLoader::loadScripts(Content& content) {
file, file,
pack.id + ":scripts/components/" + file.name() 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(); scripting::on_content_loaded();
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -58,7 +58,10 @@ void TextsRenderer::renderNote(
} }
opacity = preset.xrayOpacity; 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 xvec = note.getAxisX();
glm::vec3 yvec = note.getAxisY(); glm::vec3 yvec = note.getAxisY();
@ -76,7 +79,10 @@ void TextsRenderer::renderNote(
yvec = camera.up; yvec = camera.up;
} }
float scale = 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; xvec *= 1.0f + scale;
yvec *= 1.0f + scale; yvec *= 1.0f + scale;
} }
@ -118,6 +124,7 @@ void TextsRenderer::renderNote(
} }
auto color = preset.color; auto color = preset.color;
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity)); batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
font.draw( font.draw(
batch, batch,
text, text,

View file

@ -311,6 +311,10 @@ static int l_world_raycast(lua::State* L) {
entityRaycast.entityFilterExcludeMode = lua::toboolean(L, -1); entityRaycast.entityFilterExcludeMode = lua::toboolean(L, -1);
lua::pop(L); 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)) { if (lua::getfield(L, "filter_blocks", 1)) {
load_blocks_filter(L, -1, filteredBlocks); load_blocks_filter(L, -1, filteredBlocks);
blocksRaycast.filter = &filteredBlocks; blocksRaycast.filter = &filteredBlocks;

View file

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

View file

@ -153,7 +153,12 @@ void scripting::on_entity_spawn(
lua::call(L, 1); lua::call(L, 1);
} }
for (auto& component : components) { 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()) { for (auto& [packid, pack] : content->getPacks()) {
@ -171,6 +176,9 @@ static void process_entity_callback(
const std::string& name, const std::string& name,
std::function<int(lua::State*)> args std::function<int(lua::State*)> args
) { ) {
if (env == nullptr) {
return;
}
auto L = lua::get_main_state(); auto L = lua::get_main_state();
lua::pushenv(L, *env); lua::pushenv(L, *env);
if (lua::hasfield(L, "__disabled")) { 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()) { for (auto [entity, eid, transform, body] : view.each()) {
if (eid.uid == settings.ignoredUid || !body.enabled || if (eid.uid == settings.ignoredUid || !body.enabled ||
(settings.solidEntitiesOnly && !eid.def.solid)) { (settings.solidEntitiesOnly && !eid.def.solid) ||
(!eid.def.selectable && !settings.includeNonSelectable)) {
continue; continue;
} }
if (settings.entitiesFilter) { if (settings.entitiesFilter) {

View file

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

View file

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

View file

@ -42,6 +42,9 @@ struct EntityDef {
/// @brief Solid entity material material /// @brief Solid entity material material
std::string 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 /// @brief Does entity prevent blocks setup
bool blocking = true; bool blocking = true;

View file

@ -13,6 +13,7 @@ dv::value NotePreset::serialize() const {
{"render_distance", renderDistance}, {"render_distance", renderDistance},
{"xray_opacity", xrayOpacity}, {"xray_opacity", xrayOpacity},
{"perspective", perspective}, {"perspective", perspective},
{"font", font},
}); });
} }
@ -27,4 +28,5 @@ void NotePreset::deserialize(const dv::value& src) {
src.at("render_distance").get(renderDistance); src.at("render_distance").get(renderDistance);
src.at("xray_opacity").get(xrayOpacity); src.at("xray_opacity").get(xrayOpacity);
src.at("perspective").get(perspective); 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 renderDistance = 32.0f;
float xrayOpacity = 0.0f; float xrayOpacity = 0.0f;
float perspective = 1.0f; float perspective = 1.0f;
std::string font {};
dv::value serialize() const override; dv::value serialize() const override;
void deserialize(const dv::value& src) override; void deserialize(const dv::value& src) override;