Merge branch 'dev' into generated-pcm-stream
This commit is contained in:
commit
25808d2b21
@ -98,6 +98,32 @@ Properties available for variance:
|
||||
|
||||
Variants are managed via `block.set_variant(x, y, z, index)`.
|
||||
|
||||
### Custom model variants (geometry switching)
|
||||
|
||||
You can use different custom models for different variants. Provide a separate `model-name` for each variant that needs different geometry. The renderer caches geometry per (block id, variant).
|
||||
|
||||
The base model (specified in root) becomes variant 0. The variants array maps to indices 1+.
|
||||
|
||||
Example (default + two custom variants):
|
||||
```json
|
||||
{
|
||||
"model": "custom",
|
||||
"model-name": "stairs_middle",
|
||||
"state-based": {
|
||||
"bits": 4,
|
||||
"variants": [
|
||||
{ "model": "custom", "model-name": "stairs_left" },
|
||||
{ "model": "custom", "model-name": "stairs_right" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example:
|
||||
- Variant 0 = `stairs_middle` (from root)
|
||||
- Variant 1 = `stairs_left` (from variants[0])
|
||||
- Variant 2 = `stairs_right` (from variants[1])
|
||||
|
||||
## Lighting
|
||||
|
||||
### *emission*
|
||||
|
||||
@ -48,6 +48,12 @@ input.get_mouse_pos() --> {int, int}
|
||||
|
||||
Returns cursor screen position.
|
||||
|
||||
```lua
|
||||
input.get_mouse_delta() --> {int, int}
|
||||
```
|
||||
|
||||
Returns cursor movement delta.
|
||||
|
||||
```lua
|
||||
input.get_bindings() --> strings array
|
||||
```
|
||||
|
||||
@ -119,6 +119,18 @@ player.get_name(playerid: int) -> str
|
||||
|
||||
Player name setter and getter
|
||||
|
||||
```lua
|
||||
player.get_camera(playerid: int) -> int
|
||||
```
|
||||
|
||||
Returns the index of the player's current camera.
|
||||
|
||||
```lua
|
||||
player.set_camera(playerid: int, camera_index: int)
|
||||
```
|
||||
|
||||
Switches the player's camera. See [cameras](libcameras.md).
|
||||
|
||||
```lua
|
||||
player.set_selected_slot(playerid: int, slotid: int)
|
||||
```
|
||||
|
||||
@ -190,6 +190,12 @@ function on_hud_open(playerid: int)
|
||||
|
||||
Called after world open.
|
||||
|
||||
```lua
|
||||
function on_hud_render()
|
||||
```
|
||||
|
||||
Called every frame. Used for client-side tasks such as animation and camera control.
|
||||
|
||||
```lua
|
||||
function on_hud_close(playerid: int)
|
||||
```
|
||||
|
||||
@ -106,6 +106,32 @@
|
||||
|
||||
Управление состоянием производится через `block.set_variant(x, y, z, index)`.
|
||||
|
||||
### Кастомные модели по вариантам (переключение геометрии)
|
||||
|
||||
Для custom-моделей можно переключать геометрию по варианту. Укажите отдельный `model-name` в нужных вариантах — рендерер кэширует геометрию по паре (id блока, вариант).
|
||||
|
||||
Базовая модель (из корня) становится вариантом 0. Массив variants соответствует индексам 1+.
|
||||
|
||||
Пример (база + два кастомных варианта):
|
||||
```json
|
||||
{
|
||||
"model": "custom",
|
||||
"model-name": "stairs_middle",
|
||||
"state-based": {
|
||||
"bits": 4,
|
||||
"variants": [
|
||||
{ "model": "custom", "model-name": "stairs_left" },
|
||||
{ "model": "custom", "model-name": "stairs_right" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
В этом примере:
|
||||
- Вариант 0 = `stairs_middle` (из корня)
|
||||
- Вариант 1 = `stairs_left` (из variants[0])
|
||||
- Вариант 2 = `stairs_right` (из variants[1])
|
||||
|
||||
## Освещение
|
||||
|
||||
### Излучение - *emission*:
|
||||
|
||||
@ -48,6 +48,12 @@ input.get_mouse_pos() --> {int, int}
|
||||
|
||||
Возвращает позицию курсора на экране.
|
||||
|
||||
```lua
|
||||
input.get_mouse_delta() --> {int, int}
|
||||
```
|
||||
|
||||
Возращает дельту позиции курсора.
|
||||
|
||||
```lua
|
||||
input.get_bindings() --> массив строк
|
||||
```
|
||||
|
||||
@ -119,6 +119,18 @@ player.get_name(playerid: int) -> str
|
||||
|
||||
Сеттер и геттер имени игрока
|
||||
|
||||
```lua
|
||||
player.get_camera(playerid: int) -> int
|
||||
```
|
||||
|
||||
Возвращает индекс текущей камеры игрока
|
||||
|
||||
```lua
|
||||
player.set_camera(playerid: int, camera_index: int)
|
||||
```
|
||||
|
||||
Переключает камеру игрока. См. [камеры](libcameras.md).
|
||||
|
||||
```lua
|
||||
player.set_selected_slot(playerid: int, slotid: int)
|
||||
```
|
||||
|
||||
@ -190,6 +190,12 @@ function on_hud_open(playerid: int)
|
||||
|
||||
Вызывается после входа в мир, когда становится доступна библиотека hud. Здесь на экран добавляются постоянные элементы.
|
||||
|
||||
```lua
|
||||
function on_hud_render()
|
||||
```
|
||||
|
||||
Вызывается каждый кадр. Используется для клиентских задач, таких как анимация, управление камерой.
|
||||
|
||||
```lua
|
||||
function on_hud_close(playerid: int)
|
||||
```
|
||||
|
||||
@ -62,7 +62,7 @@ void ContentGfxCache::refreshVariant(
|
||||
}
|
||||
}
|
||||
}
|
||||
models[def.rt.id] = std::move(model);
|
||||
models[modelKey(def.rt.id, variantIndex)] = std::move(model);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,8 +94,8 @@ void ContentGfxCache::refresh() {
|
||||
|
||||
ContentGfxCache::~ContentGfxCache() = default;
|
||||
|
||||
const model::Model& ContentGfxCache::getModel(blockid_t id) const {
|
||||
const auto& found = models.find(id);
|
||||
const model::Model& ContentGfxCache::getModel(blockid_t id, uint8_t variant) const {
|
||||
const auto& found = models.find(modelKey(id, variant));
|
||||
if (found == models.end()) {
|
||||
throw std::runtime_error("model not found");
|
||||
}
|
||||
|
||||
@ -27,7 +27,11 @@ class ContentGfxCache {
|
||||
|
||||
// array of block sides uv regions (6 per block)
|
||||
std::unique_ptr<UVRegion[]> sideregions;
|
||||
std::unordered_map<blockid_t, model::Model> models;
|
||||
std::unordered_map<uint64_t, model::Model> models;
|
||||
|
||||
static inline uint64_t modelKey(blockid_t id, uint8_t variant) {
|
||||
return (uint64_t(id) << 8) | uint64_t(variant & 0xFF);
|
||||
}
|
||||
|
||||
void refreshVariant(
|
||||
const Block& def,
|
||||
@ -53,7 +57,7 @@ public:
|
||||
return sideregions[getRegionIndex(id, variant, side, !dense)];
|
||||
}
|
||||
|
||||
const model::Model& getModel(blockid_t id) const;
|
||||
const model::Model& getModel(blockid_t id, uint8_t variant) const;
|
||||
|
||||
void refresh(const Block& block, const Atlas& atlas);
|
||||
|
||||
|
||||
@ -49,8 +49,7 @@ LevelFrontend::LevelFrontend(
|
||||
auto sound = rassets.get<audio::Sound>(material->stepsSound);
|
||||
glm::vec3 pos {};
|
||||
auto soundsCamera = currentPlayer->currentCamera.get();
|
||||
if (soundsCamera == currentPlayer->spCamera.get() ||
|
||||
soundsCamera == currentPlayer->tpCamera.get()) {
|
||||
if (currentPlayer->isCurrentCameraBuiltin()) {
|
||||
soundsCamera = currentPlayer->fpCamera.get();
|
||||
}
|
||||
bool relative = player == currentPlayer &&
|
||||
|
||||
@ -67,7 +67,7 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
|
||||
glm::vec3 poff = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
offset.y += (1.0f - hitbox).y * 0.5f;
|
||||
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
const auto& model = cache.getModel(def.rt.id);
|
||||
const auto& model = cache.getModel(def.rt.id, 0);
|
||||
|
||||
for (const auto& mesh : model.meshes) {
|
||||
for (const auto& vertex : mesh.vertices) {
|
||||
|
||||
@ -308,7 +308,7 @@ void BlocksRenderer::blockCustomModel(
|
||||
Z = orient.axes[2];
|
||||
}
|
||||
|
||||
const auto& model = cache.getModel(block.rt.id);
|
||||
const auto& model = cache.getModel(block.rt.id, block.getVariantIndex(states.userbits));
|
||||
for (const auto& mesh : model.meshes) {
|
||||
if (vertexCount + mesh.vertices.size() >= capacity) {
|
||||
overflow = true;
|
||||
|
||||
@ -207,8 +207,7 @@ void CameraControl::update(
|
||||
tpCamera->front = camera->front;
|
||||
tpCamera->right = camera->right;
|
||||
}
|
||||
if (player.currentCamera == spCamera || player.currentCamera == tpCamera ||
|
||||
player.currentCamera == camera) {
|
||||
if (player.isCurrentCameraBuiltin()) {
|
||||
player.currentCamera->setFov(glm::radians(settings.fov.get()));
|
||||
}
|
||||
}
|
||||
@ -280,7 +279,7 @@ void PlayerController::postUpdate(
|
||||
updateFootsteps(delta);
|
||||
}
|
||||
|
||||
if (!pause && input) {
|
||||
if (!pause && input && player.isCurrentCameraBuiltin()) {
|
||||
camControl.updateMouse(this->input, windowHeight);
|
||||
}
|
||||
camControl.refreshRotation();
|
||||
|
||||
@ -82,6 +82,12 @@ static int l_get_mouse_pos(lua::State* L) {
|
||||
return lua::pushvec2(L, engine->getInput().getCursor().pos);
|
||||
}
|
||||
|
||||
static int l_get_mouse_delta(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
return lua::pushvec2(L, engine->getInput().getCursor().delta);
|
||||
}
|
||||
|
||||
static int l_get_bindings(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
@ -171,6 +177,7 @@ const luaL_Reg inputlib[] = {
|
||||
{"mousecode", lua::wrap<l_mousecode>},
|
||||
{"add_callback", lua::wrap<l_add_callback>},
|
||||
{"get_mouse_pos", lua::wrap<l_get_mouse_pos>},
|
||||
{"get_mouse_delta", lua::wrap<l_get_mouse_delta>},
|
||||
{"get_bindings", lua::wrap<l_get_bindings>},
|
||||
{"get_binding_text", lua::wrap<l_get_binding_text>},
|
||||
{"is_active", lua::wrap<l_is_active>},
|
||||
|
||||
@ -118,6 +118,11 @@ namespace util {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
/// @return integer dot product of two vectors
|
||||
inline int dot(const glm::ivec3& a, const glm::ivec3& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
/// @brief Find nearest point on segment to given
|
||||
/// @param a segment point A
|
||||
/// @param b segment point B
|
||||
@ -144,12 +149,17 @@ namespace util {
|
||||
inline glm::ivec3 closest_point_on_segment(
|
||||
const glm::ivec3& a, const glm::ivec3& b, const glm::ivec3& point
|
||||
) {
|
||||
auto vec = b - a;
|
||||
float da = distance2(point, a);
|
||||
float db = distance2(point, b);
|
||||
float len = length2(vec);
|
||||
float t = (((da - db) / len) * 0.5f + 0.5f);
|
||||
t = std::min(1.0f, std::max(0.0f, t));
|
||||
return a + glm::ivec3(glm::vec3(vec) * t);
|
||||
glm::ivec3 vec = b - a;
|
||||
int len2 = length2(vec);
|
||||
|
||||
if (len2 == 0) return a;
|
||||
|
||||
glm::ivec3 ap = point - a;
|
||||
int dot_product = dot(ap, vec);
|
||||
|
||||
float t = static_cast<float>(dot_product) / static_cast<float>(len2);
|
||||
t = glm::clamp(t, 0.0f, 1.0f);
|
||||
|
||||
return a + glm::ivec3(glm::round(glm::vec3(vec) * t));
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +84,12 @@ Hitbox* Player::getHitbox() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Player::isCurrentCameraBuiltin() const {
|
||||
return currentCamera.get() == fpCamera.get() ||
|
||||
currentCamera.get() == spCamera.get() ||
|
||||
currentCamera.get() == tpCamera.get();
|
||||
}
|
||||
|
||||
void Player::updateSelectedEntity() {
|
||||
selectedEid = selection.entity;
|
||||
}
|
||||
|
||||
@ -131,6 +131,8 @@ public:
|
||||
return position;
|
||||
}
|
||||
|
||||
bool isCurrentCameraBuiltin() const;
|
||||
|
||||
Hitbox* getHitbox();
|
||||
|
||||
void setSpawnPoint(glm::vec3 point);
|
||||
@ -147,4 +149,8 @@ public:
|
||||
inline u64id_t getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
Level& getLevel() const {
|
||||
return level;
|
||||
}
|
||||
};
|
||||
|
||||
@ -547,13 +547,13 @@ void WorldGenerator::generateLine(
|
||||
auto b = line.b;
|
||||
|
||||
int minX = std::max(0, std::min(a.x-radius-cgx, b.x-radius-cgx));
|
||||
int maxX = std::min(CHUNK_W, std::max(a.x+radius-cgx, b.x+radius-cgx));
|
||||
int maxX = std::min(CHUNK_W, std::max(a.x+radius-cgx, b.x+radius-cgx) + 1);
|
||||
|
||||
int minZ = std::max(0, std::min(a.z-radius-cgz, b.z-radius-cgz));
|
||||
int maxZ = std::min(CHUNK_D, std::max(a.z+radius-cgz, b.z+radius-cgz));
|
||||
int maxZ = std::min(CHUNK_D, std::max(a.z+radius-cgz, b.z+radius-cgz) + 1);
|
||||
|
||||
int minY = std::max(0, std::min(a.y-radius, b.y-radius));
|
||||
int maxY = std::min(CHUNK_H, std::max(a.y+radius, b.y+radius));
|
||||
int maxY = std::min(CHUNK_H, std::max(a.y+radius, b.y+radius) + 1);
|
||||
|
||||
for (int y = minY; y < maxY; y++) {
|
||||
for (int z = minZ; z < maxZ; z++) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user