commit
a02d626c0a
1
.gitignore
vendored
1
.gitignore
vendored
@ -45,3 +45,4 @@ appimage-build/
|
||||
|
||||
/res/content/*
|
||||
!/res/content/base
|
||||
*.mtl
|
||||
|
||||
@ -82,12 +82,12 @@ Block is not a physical obstacle if **false**
|
||||
An array of 6 numbers describing an offset an size of a block hitbox.
|
||||
|
||||
Array *\[0.25, 0.0, 0.5, 0.75, 0.4, 0.3\]* describes hitbox width:
|
||||
- 0.75m width (from east to west)
|
||||
- 0.4m height
|
||||
- 0.3m length (from south to north)
|
||||
- offset 0.25m east
|
||||
- offset 0.0m up
|
||||
- offset 0.5m north
|
||||
- 0.75m width (from east to west)
|
||||
- 0.4m height
|
||||
- 0.3m length (from south to north)
|
||||
|
||||
### *grounded*
|
||||
|
||||
|
||||
101
doc/en/entity-properties.md
Normal file
101
doc/en/entity-properties.md
Normal file
@ -0,0 +1,101 @@
|
||||
# Entity properties
|
||||
|
||||
## Logic
|
||||
|
||||
### *components*
|
||||
|
||||
Defines components and the order in which they are initialized.
|
||||
|
||||
```json
|
||||
"components": [
|
||||
list of components
|
||||
]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
"components": [
|
||||
"base:drop"
|
||||
]
|
||||
```
|
||||
|
||||
The components code should be in `scripts/components`.
|
||||
|
||||
## Physics
|
||||
|
||||
### *hitbox*
|
||||
|
||||
An array of three numbers indicating the size of the entity's hitbox.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
"hitbox": [0.6, 1.8, 0.6]
|
||||
```
|
||||
|
||||
|
||||
### *body-type*
|
||||
|
||||
Determines how the physics engine will work with it.
|
||||
|
||||
- *dynamic* - default type. The physics engine calculates movement and collisions.
|
||||
- *kinematic* - only movement is calculated, without collisions.
|
||||
|
||||
### *blocking*
|
||||
|
||||
Determines whether the entity blocks installation of blocks.
|
||||
|
||||
*In the future will also block other entities movement.*
|
||||
|
||||
Default value: *true*.
|
||||
|
||||
### *sensors*
|
||||
|
||||
A sensor is an area attached to a physical body that detects the entry of other bodies into it.
|
||||
|
||||
- When a body enter, the *on_sensor_enter* event is triggered.
|
||||
- When a body exit, the *on_sensor_exit* event is called.
|
||||
|
||||
Sensors are indexed in the same order as they are presented in the list, starting from 0.
|
||||
|
||||
The following types (shapes) of sensors exist:
|
||||
- *radius* is the simplest sensor. Defines the area around the center of the hitbox. The following values are specified:
|
||||
- radius - number.
|
||||
- *aabb* - a rectangular area that changes position depending on the rotation of the entity. **The area itself does not rotate.** The following values are specified:
|
||||
- three numbers x, y, z of the minimal corner of the area.
|
||||
- three numbers x, y, z of the opposite corner of the area.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
"sensors": [
|
||||
["aabb", -0.2, -0.2, -0.2, 0.2, 0.2, 0.2],
|
||||
["radius", 1.6]
|
||||
]
|
||||
```
|
||||
|
||||
0. A rectangular area with a width, height and length of 0.4 m, centered at 0.0.
|
||||
1. Radial area with a radius of 1.6 m.
|
||||
|
||||
## View
|
||||
|
||||
### *skeleton-name*
|
||||
|
||||
The default value is the same as the entity name. Determines which skeleton will be used by the entity. See [rigging](rigging.md).
|
||||
|
||||
## Saving/Loading
|
||||
|
||||
In addition to custom components, the engine automatically saves data from the built-in ones: transform, rigidbody, skeleton.
|
||||
|
||||
There is a set of flags that allow you to specify which data will be saved and which will not.
|
||||
|
||||
(Boolean values are specified)
|
||||
|
||||
| Title | Target | Default |
|
||||
| ---------------------- | ---------------------------------------------------- | ------- |
|
||||
| save | the entity itself | true |
|
||||
| save-skeleton-pose | skeleton pose | false |
|
||||
| save-skeleton-textures | dynamically assigned textures | false |
|
||||
| save-body-velocity | body velocity | true |
|
||||
| save-body-settings | changed body settings <br>(type, damping, crouching) | false |
|
||||
@ -1,6 +1,8 @@
|
||||
# Visual
|
||||
# Item properties
|
||||
|
||||
## *icon-type* and *icon* itself
|
||||
## Visual
|
||||
|
||||
### *icon-type* and *icon* itself
|
||||
|
||||
Icon type defines a source of an item image displayed in inventory.
|
||||
- **none** - invisible type, used for *core:empty* only (empty item, like the air block). May be removed in future updates.
|
||||
@ -10,9 +12,9 @@ Icon type defines a source of an item image displayed in inventory.
|
||||
- **items** (generated from *png* files in *res/textures/items/*)
|
||||
- **block** - block preview. Block ID must be specified in **icon** property. Example: *base:wood*.
|
||||
|
||||
# Behaviour
|
||||
## Behaviour
|
||||
|
||||
## *placing-block*
|
||||
### *placing-block*
|
||||
|
||||
Specifies what block will be placed on RMB click. Automatically specified in generated items.
|
||||
|
||||
@ -22,7 +24,7 @@ Example: an items that places bazalt blocks:
|
||||
"placing-block": "base:bazalt"
|
||||
```
|
||||
|
||||
## *emission*
|
||||
### *emission*
|
||||
|
||||
Light emitted when player holds the item in hand.
|
||||
|
||||
@ -34,6 +36,6 @@ Examples:
|
||||
- *\[7, 0, 0\]* - dim red light
|
||||
- *\[0, 0, 0\]* - no emission (default value)
|
||||
|
||||
## *stack-size*
|
||||
### *stack-size*
|
||||
|
||||
Maximal number of an item units in one slot. Default - 64.
|
||||
|
||||
@ -12,3 +12,5 @@
|
||||
- [Scripting](scripting.md)
|
||||
- [Console](console.md)
|
||||
- [Block models](block-models.md)
|
||||
- [Rigging](rigging.md)
|
||||
- [Resources (resources.json)](resources.md)
|
||||
|
||||
22
doc/en/resources.md
Normal file
22
doc/en/resources.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Resources
|
||||
|
||||
Resources include:
|
||||
- cameras
|
||||
- effects slots
|
||||
- framebuffers
|
||||
- and other limited resources
|
||||
|
||||
At the moment only **cameras** are implemented.
|
||||
|
||||
The resources requested by the pack are specified through the *resources.json* file in the format:
|
||||
```json
|
||||
{
|
||||
"resource-type": [
|
||||
"resources",
|
||||
"names"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
After loading the pack, resource names will have the pack prefix. For example camera
|
||||
*cinematic* in the base package will be *base:cinematic*.
|
||||
46
doc/en/rigging.md
Normal file
46
doc/en/rigging.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Rigging
|
||||
|
||||
## Skeletons
|
||||
|
||||
Entity skeletons are created via json files in the *skeletons* folder.
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> The skeleton is a non-indexable content unit. When loading it, the pack prefix is added to the name (example: *drop* in the base pack -> *base:drop*).
|
||||
|
||||
A skeletal element, or bone, consists of a transformation matrix defining its position, rotation and scale relative to the parent element (bone) or entity if the element is the root, a model and a list of sub-elements.
|
||||
|
||||
A skeleton file has the following structure:
|
||||
```json
|
||||
{
|
||||
"root": {
|
||||
"name": "name",
|
||||
"model": "model_name",
|
||||
"nodes": [
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- root - root element
|
||||
- name - name of the element to get the index (field optional)
|
||||
- model - name of the model to display the element (field optional)
|
||||
- nodes - list of elements - descendants, which are affected by the matrix of this element (the field is optional)
|
||||
|
||||
At the moment, positioning, rotation, scaling is done through scripting, as well as animation.
|
||||
|
||||
The process of working with skeletons will be simplified in the future.
|
||||
|
||||
Models are loaded automatically; adding them to preload.json is not required.
|
||||
|
||||
## Models
|
||||
|
||||
Models should be located in the models folder. Currently only OBJ format is supported.
|
||||
|
||||
>[!IMPORTANT]
|
||||
> When loading an obj model, the \*.mtl file is ignored.
|
||||
|
||||
A texture is defined by a material name that matches the texture naming format used in preload.json.
|
||||
|
||||
Textures are loaded automatically; it is not necessary to specify the textures used by the model in preload.json.
|
||||
@ -7,8 +7,12 @@ Subsections:
|
||||
- [User input](scripting/user-input.md)
|
||||
- [Filesystem and serialization](scripting/filesystem.md)
|
||||
- [UI properties and methods](scripting/ui.md)
|
||||
- [Entities and components](scripting/ecs.md)
|
||||
- [Libraries](#)
|
||||
- [block](scripting/builtins/libblock.md)
|
||||
- [entities](scripting/builtins/libentities.md)
|
||||
- [mat4](scripting/builtins/libmat4.md)
|
||||
- [vec2, vec3, vec4](scripting/builtins/libvecn.md)
|
||||
- [Module core:bit_converter](scripting/modules/core_bit_converter.md)
|
||||
- [Module core:data_buffer](scripting/modules/core_data_buffer.md)
|
||||
- [Module core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md)
|
||||
@ -104,6 +108,18 @@ player.get_selected_block(playerid: int) -> x,y,z
|
||||
|
||||
Returns position of the selected block or nil
|
||||
|
||||
```python
|
||||
player.get_selected_entity(playerid: int) -> int
|
||||
```
|
||||
|
||||
Returns unique indentifier of the entity selected by player
|
||||
|
||||
```python
|
||||
player.get_entity(playerid: int) -> int
|
||||
```
|
||||
|
||||
Returns unique identifier of the player entity
|
||||
|
||||
## *world* library
|
||||
|
||||
## Библиотека *world*
|
||||
@ -324,151 +340,6 @@ inventory.move(invA: int, slotA: int, invB: int, slotB: int)
|
||||
Move item from slotA of invA to slotB of invB. invA may be the same as invB.
|
||||
If slotB will be chosen automaticly if argument is not specified.
|
||||
|
||||
## *block* library
|
||||
|
||||
```python
|
||||
block.name(blockid: int) -> str
|
||||
```
|
||||
|
||||
Returns block string ID (name) by index.
|
||||
|
||||
```python
|
||||
block.index(name: str) -> int
|
||||
```
|
||||
|
||||
Returns block integer ID (index) by name.
|
||||
|
||||
```python
|
||||
block.material(blockid: int) -> str
|
||||
```
|
||||
|
||||
Returns the id of the block material.
|
||||
|
||||
```python
|
||||
block.caption(blockid: int) -> str
|
||||
```
|
||||
|
||||
Returns the block name displayed in the interface.
|
||||
|
||||
```python
|
||||
block.get(x: int, y: int, z: int) -> int
|
||||
```
|
||||
|
||||
Returns integer ID by block position
|
||||
|
||||
```python
|
||||
block.get_states(x: int, y: int, z: int) -> int
|
||||
```
|
||||
|
||||
Returns block state (rotation + additional information) as an integer.
|
||||
|
||||
```python
|
||||
block.set(x: int, y: int, z: int, id: int, states: int)
|
||||
```
|
||||
|
||||
Set block with specified integer ID and state (default - 0) at specified position.
|
||||
|
||||
> [!WARNING]
|
||||
> `block.set` does not trigger on_placed.
|
||||
|
||||
```python
|
||||
block.is_solid_at(x: int, y: int, z: int) -> bool
|
||||
```
|
||||
|
||||
Check if block at the specified position is solid.
|
||||
|
||||
```python
|
||||
block.is_replaceable_at(x: int, y: int, z: int) -> bool
|
||||
```
|
||||
Check if block may be placed at specified position. (Examples: air, water, grass, flower)
|
||||
|
||||
```python
|
||||
block.defs_count() -> int
|
||||
```
|
||||
|
||||
Returns count of available block IDs.
|
||||
|
||||
Following three functions return direction vectors based on block rotation.
|
||||
|
||||
|
||||
```python
|
||||
block.get_X(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Returns X: integer direction vector of the block at specified coordinates.
|
||||
Example: no rotation: 1, 0, 0
|
||||
|
||||
```python
|
||||
block.get_Y(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Returns Y: integer direction vector of the block at specified coordinates.
|
||||
Example: no rotation: 0, 1, 0
|
||||
|
||||
```python
|
||||
block.get_Z(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Returns Z: integer direction vector of the block at specified coordinates.
|
||||
Example: no rotation: 0, 0, 1
|
||||
|
||||
|
||||
```python
|
||||
block.get_rotation(x: int, y: int, z: int) -> int
|
||||
```
|
||||
|
||||
Returns block rotation index based on used profile.
|
||||
|
||||
```python
|
||||
block.set_rotation(x: int, y: int, z: int, rotation: int)
|
||||
```
|
||||
|
||||
Set block rotation by index.
|
||||
|
||||
### Extended blocks
|
||||
|
||||
Extended blocks are blocks with size greather than 1x1x1
|
||||
|
||||
```python
|
||||
block.is_extended(id: int) -> bool
|
||||
```
|
||||
|
||||
Checks whether the block is extended.
|
||||
|
||||
```python
|
||||
block.get_size(id: int) -> int, int, int
|
||||
```
|
||||
|
||||
Returns the block size.
|
||||
|
||||
```python
|
||||
block.is_segment(x: int, y: int, z: int) -> bool
|
||||
```
|
||||
|
||||
Checks whether the block is a non-origin segment of an extended block.
|
||||
|
||||
```python
|
||||
block.seek_origin(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Returns the position of the main segment of an extended block or the original position,
|
||||
if the block is not extended.
|
||||
|
||||
### User bits
|
||||
|
||||
Part of a voxel data used for scripting. Size: 8 bit.
|
||||
|
||||
```python
|
||||
block.get_user_bits(x: int, y: int, z: int, offset: int, bits: int) -> int
|
||||
```
|
||||
|
||||
Get specified bits as an unsigned integer.
|
||||
|
||||
```python
|
||||
block.set_user_bits(x: int, y: int, z: int, offset: int, bits: int, value: int) -> int
|
||||
```
|
||||
Set specified bits.
|
||||
|
||||
## *item* library
|
||||
|
||||
```python
|
||||
@ -571,6 +442,18 @@ hud.resume()
|
||||
|
||||
Closes the pause menu.
|
||||
|
||||
```python
|
||||
hud.is_paused() -> bool
|
||||
```
|
||||
|
||||
Returns true if pause menu is open.
|
||||
|
||||
```python
|
||||
hud.is_inventory_open() -> bool
|
||||
```
|
||||
|
||||
Returns true if inventory is open or overlay is shown.
|
||||
|
||||
### *time* library
|
||||
|
||||
```python
|
||||
|
||||
115
doc/en/scripting/builtins/libblock.md
Normal file
115
doc/en/scripting/builtins/libblock.md
Normal file
@ -0,0 +1,115 @@
|
||||
# *block* library
|
||||
|
||||
```lua
|
||||
-- Returns block string ID (name) by index.
|
||||
block.name(blockid: int) -> str
|
||||
|
||||
-- Returns block integer ID (index) by name.
|
||||
block.index(name: str) -> int
|
||||
|
||||
-- Returns the id of the block material.
|
||||
block.material(blockid: int) -> str
|
||||
|
||||
-- Returns the block name displayed in the UI.
|
||||
block.caption(blockid: int) -> str
|
||||
|
||||
-- Returns integer ID by block position
|
||||
block.get(x: int, y: int, z: int) -> int
|
||||
|
||||
-- Returns block state (rotation + additional information) as an integer.
|
||||
-- Used to save complete block information.
|
||||
block.get_states(x: int, y: int, z: int) -> int
|
||||
|
||||
-- Set block with specified integer ID and state (default - 0) at specified position.
|
||||
block.set(x: int, y: int, z: int, id: int, states: int)
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> `block.set` does not trigger on_placed.
|
||||
|
||||
```lua
|
||||
-- Check if block at the specified position is solid.
|
||||
block.is_solid_at(x: int, y: int, z: int) -> bool
|
||||
|
||||
-- Check if block may be placed at specified position.
|
||||
-- (Examples: air, water, grass, flower)
|
||||
block.is_replaceable_at(x: int, y: int, z: int) -> bool
|
||||
|
||||
-- Returns count of available block IDs.
|
||||
block.defs_count() -> int
|
||||
```
|
||||
|
||||
## Rotation
|
||||
|
||||
Following three functions return direction vectors based on block rotation.
|
||||
|
||||
|
||||
```lua
|
||||
-- Returns X: integer direction vector of the block at specified coordinates.
|
||||
-- Example: no rotation: 1, 0, 0.
|
||||
block.get_X(x: int, y: int, z: int) -> int, int, int
|
||||
|
||||
-- Same for axis Y. Default: 0, 1, 0.
|
||||
block.get_Y(x: int, y: int, z: int) -> int, int, int
|
||||
|
||||
-- Same for axis Z. Default: 0, 0, 1.
|
||||
block.get_Z(x: int, y: int, z: int) -> int, int, int
|
||||
|
||||
-- Returns block rotation index based on used profile.
|
||||
block.get_rotation(x: int, y: int, z: int) -> int
|
||||
|
||||
-- Set block rotation by index.
|
||||
block.set_rotation(x: int, y: int, z: int, rotation: int)
|
||||
```
|
||||
|
||||
## Extended blocks
|
||||
|
||||
Extended blocks are blocks with size greather than 1x1x1
|
||||
|
||||
```lua
|
||||
-- Checks whether the block is extended.
|
||||
block.is_extended(id: int) -> bool
|
||||
|
||||
-- Returns the block size.
|
||||
block.get_size(id: int) -> int, int, int
|
||||
|
||||
-- Checks whether the block is a non-origin segment of an extended block.
|
||||
block.is_segment(x: int, y: int, z: int) -> bool
|
||||
|
||||
-- Returns the position of the main segment of an extended block
|
||||
-- or the original position, if the block is not extended.
|
||||
block.seek_origin(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
## User bits
|
||||
|
||||
Part of a voxel data used for scripting. Size: 8 bit.
|
||||
|
||||
```python
|
||||
block.get_user_bits(x: int, y: int, z: int, offset: int, bits: int) -> int
|
||||
```
|
||||
|
||||
Get specified bits as an unsigned integer.
|
||||
|
||||
```python
|
||||
block.set_user_bits(x: int, y: int, z: int, offset: int, bits: int, value: int) -> int
|
||||
```
|
||||
Set specified bits.
|
||||
|
||||
## Raycast
|
||||
|
||||
```lua
|
||||
block.raycast(start: vec3, dir: vec3, max_distance: number, [optional] dest: table) -> {
|
||||
block: int, -- block id
|
||||
endpoint: vec3, -- point of the ray hit point
|
||||
iendpoint: vec3, -- position of the block hit by the ray
|
||||
length: number, -- ray length
|
||||
normal: vec3 -- normal vector of the surface hit by the ray
|
||||
} or nil
|
||||
```
|
||||
|
||||
Casts a ray from the start point in the direction of *dir*. Max_distance specifies the maximum ray length.
|
||||
|
||||
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.
|
||||
42
doc/en/scripting/builtins/libentities.md
Normal file
42
doc/en/scripting/builtins/libentities.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Library *entities*
|
||||
|
||||
The library is designed to work with a registry of entities.
|
||||
|
||||
```lua
|
||||
-- Returns an entity by unique identifier
|
||||
-- The table returned is the same one available in the entity components.
|
||||
entities.get(uid: int) -> table
|
||||
|
||||
-- Creates the specified entity.
|
||||
-- args - table of component parameter tables (ARGS variable)
|
||||
-- args is optional
|
||||
entities.spawn(name: str, pos: vec3, [optional] args: table)
|
||||
|
||||
-- Checks the existence of an entity by a unique identifier.
|
||||
entities.exists(uid: int) -> bool
|
||||
|
||||
-- Returns a table of all loaded entities
|
||||
entities.get_all() -> table
|
||||
|
||||
-- Returns a table of loaded entities based on the passed list of UIDs
|
||||
entities.get_all(uids: array<int>) -> table
|
||||
|
||||
-- Returns a list of UIDs of entities inside the rectangular area
|
||||
-- pos - minimal area corner
|
||||
-- size - area size
|
||||
entities.get_all_in_box(pos: vec3, size: vec3) -> array<int>
|
||||
|
||||
-- Returns a list of UIDs of entities inside the radius
|
||||
-- center - center of the area
|
||||
-- radius - radius of the area
|
||||
entities.get_all_in_radius(center: vec3, radius: number) -> array<int>
|
||||
```
|
||||
|
||||
```lua
|
||||
entities.raycast(start: vec3, dir: vec3, max_distance: number,
|
||||
ignore: int, [optional] destination: 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.
|
||||
|
||||
Accordingly, this will affect the presence of the *entity* and *block* fields.
|
||||
207
doc/en/scripting/builtins/libvecn.md
Normal file
207
doc/en/scripting/builtins/libvecn.md
Normal file
@ -0,0 +1,207 @@
|
||||
# Library vec*n*
|
||||
|
||||
*vecn* contains a set of functions for working with vectors of dimensions 2, 3 or 4.
|
||||
Most functions have several options for argument lists (overloads).
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> vecn, where n == vector dimension (2, 3, 4), i.e. vec2, vec3, vec4
|
||||
>
|
||||
|
||||
## Data types
|
||||
|
||||
Type conventions will be used on this page.
|
||||
- vector - an array of two, three or four numbers
|
||||
- vec2 - array of two numbers
|
||||
- vec3 - array of three numbers
|
||||
- vec4 - array of four numbers
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> Type annotations are part of the documentation and are not specified when calling functions.
|
||||
|
||||
|
||||
## Operations with vectors
|
||||
|
||||
#### Addition - *vecn.add(...)*
|
||||
|
||||
```lua
|
||||
-- returns the result of vector addition
|
||||
vecn.add(a: vector, b: vector)
|
||||
|
||||
-- returns the result of adding a vector and a scalar
|
||||
vecn.add(a: vector, b: number)
|
||||
|
||||
-- writes the result of adding two vectors to dst
|
||||
vecn.add(a: vector, b: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Subtraction - *vecn.sub(...)*
|
||||
|
||||
```lua
|
||||
-- returns the result of vector subtraction
|
||||
vecn.sub(a: vector, b: vector)
|
||||
|
||||
-- returns the result of subtracting a scalar from a vector
|
||||
vecn.sub(a: vector, b: number)
|
||||
|
||||
-- writes the result of subtracting two vectors to dst
|
||||
vecn.sub(a: vector, b: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Multiplication - *vecn.mul(...)*
|
||||
|
||||
```lua
|
||||
-- returns the result of vector multiplication
|
||||
vecn.mul(a: vector, b: vector)
|
||||
|
||||
-- returns the result of multiplying a vector by a scalar
|
||||
vecn.mul(a: vector, b: number)
|
||||
```
|
||||
|
||||
#### Inversion - *vecn.inv(...)*
|
||||
|
||||
```lua
|
||||
-- returns the result of the inversion (opposite) of the vector
|
||||
vecn.inverse(a: vector)
|
||||
|
||||
-- writes the inverted vector to dst
|
||||
vecn.inverse(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Division - *vecn.div(...)*
|
||||
|
||||
```lua
|
||||
-- returns the result of vector division
|
||||
vecn.div(a: vector, b: vector)
|
||||
|
||||
-- returns the result of dividing a vector by a scalar
|
||||
vecn.div(a: vector, b: number)
|
||||
|
||||
-- writes the result of dividing two vectors to dst
|
||||
vecn.div(a: vector, b: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Normalization - *vecn.norm(...)*
|
||||
|
||||
```lua
|
||||
-- returns normalized vector
|
||||
vecn.normalize(a: vector)
|
||||
|
||||
-- writes the normalized vector to dst
|
||||
vecn.normalize(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Vector length - *vecn.len(...)*
|
||||
|
||||
```lua
|
||||
-- returns the length of the vector
|
||||
vecn.length(a: vector)
|
||||
|
||||
```
|
||||
|
||||
#### Absolute value - *vecn.abs(...)*
|
||||
|
||||
```lua
|
||||
-- returns a vector with absolute values
|
||||
vecn.abs(a: vector)
|
||||
|
||||
-- writes the absolute value of the vector to dst
|
||||
vecn.abs(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Rounding - *vecn.round(...)*
|
||||
|
||||
```lua
|
||||
-- returns a vector with rounded values
|
||||
vecn.round(a: vector)
|
||||
|
||||
-- writes rounded vector to dst
|
||||
vecn.round(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Exponentiation - *vecn.pow(...)*
|
||||
|
||||
```lua
|
||||
-- returns a vector with elements raised to powers
|
||||
vecn.pow(a: vector, b: number)
|
||||
|
||||
-- writes the vector raised to a power to dst
|
||||
vecn.pow(v: vector, exponent: number, dst: vector)
|
||||
```
|
||||
|
||||
#### Dot product - *vecn.dot(...)*
|
||||
```lua
|
||||
-- returns the scalar product of vectors
|
||||
vecn.dot(a: vector, b: vector)
|
||||
```
|
||||
|
||||
#### Convert to string - *vecn.tostring(...)*
|
||||
> [!WARNING]
|
||||
> Returns only if the content is a vector
|
||||
```lua
|
||||
-- returns a string representing the contents of the vector
|
||||
vecn.tostring(a: vector)
|
||||
```
|
||||
|
||||
## Specific functions
|
||||
|
||||
Functions related to specific vector dimensions.
|
||||
|
||||
```lua
|
||||
-- returns a random vector whose coordinates are uniformly distributed on a sphere of a given radius
|
||||
vec3.spherical_rand(radius: number)
|
||||
|
||||
-- writes a random vector whose coordinates are uniformly distributed on a sphere of a given radius in dst
|
||||
vec3.spherical_rand(radius: number, dst: vec3)
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
```lua
|
||||
-- creating vectors of different dimensions
|
||||
local v1_3d = {1, 2, 2}
|
||||
local v2_3d = {10, 20, 40}
|
||||
local v3_4d = {1, 2, 4, 1}
|
||||
local v4_2d = {1, 0}
|
||||
local scal = 6 -- regular scalar
|
||||
|
||||
-- vector addition
|
||||
local result_add = vec3.add(v1_3d, v2_3d)
|
||||
print("add: " .. vec3.tostring(result_add)) -- {11, 22, 42}
|
||||
|
||||
-- vector subtraction
|
||||
local result_sub = vec3.sub(v2_3d, v1_3d)
|
||||
print("sub: " .. vec3.tostring(result_sub)) -- {9, 18, 38}
|
||||
|
||||
-- vector multiplication
|
||||
local result_mul = vec3.mul(v1_3d, v2_3d)
|
||||
print("mul: " .. vec3.tostring(result_mul)) -- {10, 40, 80}
|
||||
|
||||
-- multiplying a vector by a scalar
|
||||
local result_mul_scal = vec3.mul(v1_3d, scal)
|
||||
print("mul_scal: " .. vec3.tostring(result_mul_scal)) -- {6, 12, 12}
|
||||
|
||||
-- vector normalization
|
||||
local result_norm = vec3.normalize(v1_3d)
|
||||
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
||||
|
||||
-- vector length
|
||||
local result_len = vec3.length(v1_3d)
|
||||
print("len: " .. result_len) -- 3
|
||||
|
||||
-- absolute value of the vector
|
||||
local result_abs = vec3.abs(v1_3d)
|
||||
print("abs: " .. vec3.tostring(result_abs)) -- {1, 2, 2}
|
||||
|
||||
-- vector rounding
|
||||
local result_round = vec3.round(v1_3d)
|
||||
print("round: " .. vec3.tostring(result_round)) -- {1, 2, 2}
|
||||
|
||||
-- vector exponentiation
|
||||
local result_pow = vec3.pow(v1_3d, 2)
|
||||
print("pow: " .. vec3.tostring(result_pow)) -- {1, 4, 4}
|
||||
|
||||
-- scalar product of vectors
|
||||
local result_dot = vec3.dot(v1_3d, v2_3d)
|
||||
print("dot: " ..result_dot) -- 250
|
||||
201
doc/en/scripting/ecs.md
Normal file
201
doc/en/scripting/ecs.md
Normal file
@ -0,0 +1,201 @@
|
||||
# Entities and components
|
||||
|
||||
## Types notation used below
|
||||
|
||||
- vec3 - 3D vector (array of three numbers)
|
||||
- mat4 - 4x4 matrix (array of 16 numbers)
|
||||
|
||||
Type annotations are added for documentation purposes and are not part of the Lua syntax.
|
||||
|
||||
## Entity
|
||||
|
||||
The entity object is available in components as a global variable **entity**.
|
||||
|
||||
```lua
|
||||
-- Deletes an entity (the entity may continue to exist until the frame ends, but will not be displayed in that frame)
|
||||
entity:despawn()
|
||||
|
||||
-- Returns the name of the entity skeleton
|
||||
entity:get_skeleton() -> str
|
||||
-- Replaces the entity skeleton
|
||||
entity:set_skeleton(name: str)
|
||||
|
||||
-- Returns the unique entity identifier
|
||||
entity:get_uid() -> int
|
||||
|
||||
-- Returns the component by name
|
||||
entity:get_component(name: str) -> component or nil
|
||||
-- Checks for the presence of a component by name
|
||||
entity:has_component(name: str) -> bool
|
||||
```
|
||||
|
||||
## Built-in components
|
||||
|
||||
### Transform
|
||||
|
||||
The component is responsible for the position, scale and rotation of the entity.
|
||||
|
||||
```lua
|
||||
-- Alias
|
||||
local tsf = entity.transform
|
||||
|
||||
-- Returns the position of the entity
|
||||
tsf:get_pos() -> vec3
|
||||
-- Sets the entity position
|
||||
tsf:set_pos(pos:vec3)
|
||||
|
||||
-- Returns the entity scale
|
||||
tsf:get_size() -> vec3
|
||||
-- Sets the entity scale
|
||||
tsf:set_size(size: vec3)
|
||||
|
||||
-- Returns the entity rotation
|
||||
tsf:get_rot() -> mat4
|
||||
-- Sets entity rotation
|
||||
tsf:set_rot(size: mat4)
|
||||
```
|
||||
|
||||
### Rigidbody
|
||||
|
||||
The component is responsible for the physical body of the entity.
|
||||
|
||||
```lua
|
||||
-- Alias
|
||||
local body = entity.rigidbody
|
||||
|
||||
-- Checks if body physics calculation is enabled
|
||||
body:is_enabled() -> bool
|
||||
-- Enables/disables body physics calculation
|
||||
body:set_enabled(enabled: bool)
|
||||
|
||||
-- Returns linear velocity
|
||||
body:get_vel() -> vec3
|
||||
-- Sets linear velocity
|
||||
body:set_vel(vel: vec3)
|
||||
|
||||
-- Returns the size of the hitbox
|
||||
body:get_size() -> vec3
|
||||
-- Sets the hitbox size
|
||||
body:set_size(size: vec3)
|
||||
|
||||
-- Returns the gravity multiplier
|
||||
body:get_gravity_scale() -> vec3
|
||||
-- Sets the gravity multiplier
|
||||
body:set_gravity_scale(scale: vec3)
|
||||
|
||||
-- Returns the linear velocity attenuation multiplier (used to simulate air resistance and friction)
|
||||
body:get_linear_damping() -> number
|
||||
-- Sets the linear velocity attenuation multiplier
|
||||
body:set_linear_damping(value: number)
|
||||
|
||||
-- Checks if vertical velocity attenuation is enabled
|
||||
body:is_vdamping() -> bool
|
||||
-- Enables/disables vertical velocity attenuation
|
||||
body:set_vdamping(enabled: bool)
|
||||
|
||||
-- Checks if the entity is on the ground
|
||||
body:is_grounded() -> bool
|
||||
|
||||
-- Checks if the entity is in a "crouching" state (cannot fall from blocks)
|
||||
body:is_crouching() -> bool
|
||||
-- Enables/disables the "crouching" state
|
||||
body:set_crouching(enabled: bool)
|
||||
|
||||
-- Returns the type of physical body (dynamic/kinematic)
|
||||
body:get_body_type() -> str
|
||||
-- Sets the physical body type
|
||||
body:set_body_type(type: str)
|
||||
```
|
||||
|
||||
### Skeleton
|
||||
|
||||
The component is responsible for the entity skeleton. See [rigging](../rigging.md).
|
||||
|
||||
```lua
|
||||
-- Alias
|
||||
local rig = entity.skeleton
|
||||
|
||||
-- Returns the model name assigned to the bone at the specified index
|
||||
rig:get_model(index: int) -> str
|
||||
|
||||
-- Reassigns the bone model at the specified index
|
||||
-- Resets to original if name is not specified
|
||||
rig:set_model(index: int, name: str)
|
||||
|
||||
-- Returns the bone transformation matrix at the specified index
|
||||
rig:get_matrix(index: int) -> mat4
|
||||
-- Sets the bone transformation matrix at the specified index
|
||||
rig:set_matrix(index: int, matrix: mat4)
|
||||
|
||||
-- Returns the texture by key (dynamically assigned textures - '$name')
|
||||
rig:get_texture(key: str) -> str
|
||||
|
||||
-- Assigns texture by key
|
||||
rig:set_texture(key: str, value: str)
|
||||
|
||||
-- Returns the bone index by name or nil
|
||||
rig:index(name: str) -> int
|
||||
```
|
||||
|
||||
## Component events
|
||||
|
||||
```lua
|
||||
function on_despawn()
|
||||
```
|
||||
|
||||
Called when the entity is despawned.
|
||||
|
||||
```lua
|
||||
function on_grounded(force: number)
|
||||
```
|
||||
|
||||
Called on landing. The first argument is the impact force (Speed module).
|
||||
|
||||
```lua
|
||||
function on_fall()
|
||||
```
|
||||
|
||||
Called when the entity starts to fall.
|
||||
|
||||
```lua
|
||||
function on_save()
|
||||
```
|
||||
|
||||
Called before component data is saved. Here you can write the data you want to save into the *SAVED_DATA* table, which is available for the entire life of the component.
|
||||
|
||||
```lua
|
||||
function on_sensor_enter(index: int, entity: int)
|
||||
```
|
||||
|
||||
Called when another entity hits the sensor with the index passed as the first argument. The UID of the entity that entered the sensor is passed as the second argument.
|
||||
|
||||
```lua
|
||||
function on_sensor_exit(index: int, entity: int)
|
||||
```
|
||||
|
||||
Called when another entity exits the sensor with the index passed as the first argument. The UID of the entity that left the sensor is passed as the second argument.
|
||||
|
||||
```lua
|
||||
function on_aim_on(playerid: int)
|
||||
```
|
||||
|
||||
Called when the player aims at the entity. The player ID is passed as an argument.
|
||||
|
||||
```lua
|
||||
function on_aim_off(playerid: int)
|
||||
```
|
||||
|
||||
Called when the player takes aim away from the entity. The player ID is passed as an argument.
|
||||
|
||||
```lua
|
||||
function on_attacked(attackerid: int, playerid: int)
|
||||
```
|
||||
|
||||
Called when an entity is attacked (LMB on the entity). The first argument is the UID of the attacking entity. The attacking player ID is passed as the second argument. If the entity was not attacked by a player, the value of the second argument will be -1.
|
||||
|
||||
|
||||
```lua
|
||||
function on_used(playerid: int)
|
||||
```
|
||||
|
||||
Called when an entity is used (RMB by entity). The player ID is passed as an argument.
|
||||
@ -59,3 +59,18 @@ input.get_bindings() -> strings array
|
||||
```
|
||||
|
||||
Returns all binding names.
|
||||
|
||||
```python
|
||||
input.is_pressed(code: str) -> bool
|
||||
```
|
||||
|
||||
Checks input activity using a code consisting of:
|
||||
- input type: *key* or *mouse*
|
||||
- input code: [key name](#key names) or mouse button name (left, middle, right)
|
||||
|
||||
Example:
|
||||
```lua
|
||||
if (input.is_pressed("key:enter") {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
@ -86,13 +86,13 @@
|
||||
|
||||
Числа указываются в диапазоне [0.0, 1.0] - т.е в пределах блока.
|
||||
|
||||
Массив `[0.25, 0.0, 0.5, 0.75, 0.4, 0.3]` описывает хитбокс:
|
||||
- шириной (с востока на запад) 0.75 м
|
||||
- высотой 0.4 м
|
||||
- длиной (с юга на север) 0.3 м
|
||||
Массив `[0.25, 0.0, 0.5, 0.75, 0.4, 0.3]` описывает хитбокс:
|
||||
- смещен на 0.25 м на запад
|
||||
- смещен на 0.0 м вверх
|
||||
- смещен на 0.5 м на север
|
||||
- шириной (с востока на запад) 0.75 м
|
||||
- высотой 0.4 м
|
||||
- длиной (с юга на север) 0.3 м
|
||||
|
||||
### Приземленность - *grounded*
|
||||
|
||||
|
||||
101
doc/ru/entity-properties.md
Normal file
101
doc/ru/entity-properties.md
Normal file
@ -0,0 +1,101 @@
|
||||
# Свойства сущностей
|
||||
|
||||
## Логика
|
||||
|
||||
### Cписок компонентов - *components*
|
||||
|
||||
Определяет компоненты и порядок их инициализации.
|
||||
|
||||
```json
|
||||
"components": [
|
||||
список компонентов
|
||||
]
|
||||
```
|
||||
|
||||
Пример:
|
||||
|
||||
```json
|
||||
"components": [
|
||||
"base:drop"
|
||||
]
|
||||
```
|
||||
|
||||
Код компонентов должен находиться в `scripts/components`.
|
||||
|
||||
## Физика
|
||||
|
||||
### Хитбокс - *hitbox*
|
||||
|
||||
Массив из трех чисел, указывающих размер хитбокса сущности.
|
||||
|
||||
Пример:
|
||||
|
||||
```json
|
||||
"hitbox": [0.6, 1.8, 0.6]
|
||||
```
|
||||
|
||||
|
||||
### Тип тела - *body-type*
|
||||
|
||||
Определяет то, как с ним будет работать физический движок.
|
||||
|
||||
- *dynamic* (динамический) - тип по-умолчанию. Физический движок просчитывает движение и столкновения.
|
||||
- *kinematic* (кинематический) - просчитывается только движение, без столкновений.
|
||||
|
||||
### Блокирование - *blocking*
|
||||
|
||||
Определяет блокирует ли сущность установку блоков.
|
||||
|
||||
*В будущем будет также блокировать движение других сущностей.*
|
||||
|
||||
Значение по-умолчанию: *true*.
|
||||
|
||||
### Список сенсоров - *sensors*
|
||||
|
||||
Сенсор - область пространства, привязанная к физическому телу, детектирующее попадание в него других тел.
|
||||
|
||||
- При попадании тела вызывается событие *on_sensor_enter*.
|
||||
- При покидании тела вызывается событие *on_sensor_exit*.
|
||||
|
||||
Сенсоры индексируются в том же порядке, в каком представлены в списке, начиная с 0.
|
||||
|
||||
Существуют следующие типы (формы) сенсоров:
|
||||
- radius (радиус) - самый простой сенсор. Определяет область вокруг центра хитбокса. Указываются следующие значения:
|
||||
- радиус - число.
|
||||
- aabb (коробка) - прямоугольная область, меняющая положение в зависимости от поворота сущности. **Сама область не поворачивается.** Указываются следующие значения:
|
||||
- три числа x, y, z минимального угла области.
|
||||
- три числа x, y, z противоположного угла области.
|
||||
|
||||
Пример:
|
||||
|
||||
```json
|
||||
"sensors": [
|
||||
["aabb", -0.2, -0.2, -0.2, 0.2, 0.2, 0.2],
|
||||
["radius", 1.6]
|
||||
]
|
||||
```
|
||||
|
||||
0. Прямоугольная область шириной, высотой и длиной в 0.4 м. с центром в 0.0.
|
||||
1. Радиальная область с радиусом 1.6 м.
|
||||
|
||||
## Вид
|
||||
|
||||
### Имя скелета - *skeleton-name*
|
||||
|
||||
Значение по-умолчанию совпадает с именем сущности. Определяет то, какой скелет будет использоваться сущностью. См. [риггинг](rigging.md).
|
||||
|
||||
## Сохранение/загрузка
|
||||
|
||||
Кроме данных пользовательских компонентов, движок автоматически сохраняет данные встроенных: transform, rigidbody, skeleton.
|
||||
|
||||
Есть набор флагов, позволяющих указать какие данные будут сохранены, а какие нет.
|
||||
|
||||
(Указываются булевы значения)
|
||||
|
||||
| Название | Цель | По-умолчанию |
|
||||
| ---------------------- | -------------------------------------------------------- | ------------ |
|
||||
| save | сама сущность | true |
|
||||
| save-skeleton-pose | поза скелета сущности | false |
|
||||
| save-skeleton-textures | динамически назначенные текстуры | false |
|
||||
| save-body-velocity | скорость движения тела | true |
|
||||
| save-body-settings | измененные настройки тела <br>(type, damping, crouching) | false |
|
||||
@ -1,6 +1,8 @@
|
||||
# Вид
|
||||
# Свойства предметов
|
||||
|
||||
## Тип иконки - `icon-type` и сама иконка - `icon`
|
||||
## Вид
|
||||
|
||||
### Тип иконки - `icon-type` и сама иконка - `icon`
|
||||
|
||||
В последней версии движка существуют следующие типы иконок предметов, определяющих то, как предмет будет отображаться в инвентаре:
|
||||
- `none` - невидимый тип, используется только для `core:empty` (пустой предмет). Не влияет на появление предмета на панель доступа к контенту. Тип может быть удалён в будущем
|
||||
@ -9,9 +11,9 @@
|
||||
- items (генерируется из png файлов в `res/textures/items/`)
|
||||
- `block` - отображает предпросмотр блока. В icon указывается строковый id блока который нужно отображать. Пример `base:wood`
|
||||
|
||||
# Поведение
|
||||
## Поведение
|
||||
|
||||
## Устанавливаемый блок - `placing-block`
|
||||
### Устанавливаемый блок - `placing-block`
|
||||
|
||||
При указании строкового id блока предмет устанавливает его при нажатии ПКМ. Именно это свойство используется у всех сгенерированных для блоков предметов.
|
||||
|
||||
@ -21,7 +23,7 @@
|
||||
"placing-block": "base:bazalt"
|
||||
```
|
||||
|
||||
## Излучение - `emission`
|
||||
### Излучение - `emission`
|
||||
|
||||
Влияет на свет излучаемый предметом, когда он находится в руке игрока.
|
||||
|
||||
@ -33,6 +35,6 @@
|
||||
- `[7, 0, 0]` - слабый красный свет
|
||||
- `[0, 0, 0]` - предмет не излучает свет (по-умолчанию)
|
||||
|
||||
## Размер стопки (стека) - `stack-size`
|
||||
### Размер стопки (стека) - `stack-size`
|
||||
|
||||
Определяет максимальное количество предмета в одном слоте. Значение по-умолчанию - 64.
|
||||
|
||||
@ -12,3 +12,5 @@
|
||||
- [Скриптинг](scripting.md)
|
||||
- [Консоль](console.md)
|
||||
- [Модели блоков](block-models.md)
|
||||
- [Риггинг](rigging.md)
|
||||
- [Ресурсы (resources.json)](resources.md)
|
||||
|
||||
22
doc/ru/resources.md
Normal file
22
doc/ru/resources.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Ресурсы
|
||||
|
||||
К ресурсам относятся:
|
||||
- камеры
|
||||
- слоты для эффектов
|
||||
- фреймбуферы
|
||||
- и подобные ограниченные по количеству ресурсы
|
||||
|
||||
На данный момент реализованы только **камеры**.
|
||||
|
||||
Запрашиваемые паком ресурсы указываются через файл resources.json в формате:
|
||||
```json
|
||||
{
|
||||
"тип-ресура": [
|
||||
"имена",
|
||||
"ресурсов"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
После загрузки пака имена ресурсов получат префикс пака. Например камера
|
||||
*cinematic* в паке base получает имя *base:cinematic*.
|
||||
46
doc/ru/rigging.md
Normal file
46
doc/ru/rigging.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Риггинг
|
||||
|
||||
## Скелеты
|
||||
|
||||
Скелеты сущностей создаются через json файлы в папке skeletons.
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> Скелет является неиндексируемой единицей контента. При его загрузке к имени добавляется префикс пака (пример: *drop* в паке base -> *base:drop*).
|
||||
|
||||
Элемент скелета, или кость, состоит из матрицы транформации, определяющей её положение, вращение и масштаб относительно родительского элемента (кости) или сущности, если элемент является корневым, модели и списка под-элементов.
|
||||
|
||||
Файл скелета имеет следующую структуру:
|
||||
```json
|
||||
{
|
||||
"root": {
|
||||
"name": "имя",
|
||||
"model": "имя_модели",
|
||||
"nodes": [
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- root - корневой элемент
|
||||
- name - имя элемента для получения индекса (поле опционально)
|
||||
- model - имя модели для отображения элемента (поле опционально)
|
||||
- nodes - список элементов - потомков, на которые влияет матрица данного элемента (поле опционально)
|
||||
|
||||
На данный момент расположение, вращение, масштабирование выполняется через скриптинг, так же как и анимация.
|
||||
|
||||
Процесс работы со скелетами будет упрощен в будущем.
|
||||
|
||||
Модели загружаются автоматически, добавление их в preload.json не требуется.
|
||||
|
||||
## Модели
|
||||
|
||||
Модели должны располагаться в папке models. На данный момент поддерживается только OBJ формат.
|
||||
|
||||
>[!IMPORTANT]
|
||||
> При загрузке obj модели игнорируется файл \*.mtl.
|
||||
|
||||
Текстура определяется именем материала, соответствующем формату имен текстур, используемому в preload.json.
|
||||
|
||||
Текстуры загружаются автоматически, указывать используемые моделью текстуры в preload.json не обязательно.
|
||||
@ -7,8 +7,12 @@
|
||||
- [Пользовательский ввод](scripting/user-input.md)
|
||||
- [Файловая система и сериализация](scripting/filesystem.md)
|
||||
- [Свойства и методы UI элементов](scripting/ui.md)
|
||||
- [Сущности и компоненты](scripting/ecs.md)
|
||||
- [Библиотеки](#)
|
||||
- [block](scripting/builtins/libblock.md)
|
||||
- [entities](scripting/builtins/libentities.md)
|
||||
- [mat4](scripting/builtins/libmat4.md)
|
||||
- [vec2, vec3, vec4](scripting/builtins/libvecn.md)
|
||||
- [Модуль core:bit_converter](scripting/modules/core_bit_converter.md)
|
||||
- [Модуль core:data_buffer](scripting/modules/core_data_buffer.md)
|
||||
- [Модули core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md)
|
||||
@ -100,6 +104,18 @@ player.get_selected_block(playerid: int) -> x,y,z
|
||||
|
||||
Возвращает координаты выделенного блока, либо nil
|
||||
|
||||
```python
|
||||
player.get_selected_entity(playerid: int) -> int
|
||||
```
|
||||
|
||||
Возвращает уникальный идентификатор сущности, на которую нацелен игрок
|
||||
|
||||
```python
|
||||
player.get_entity(playerid: int) -> int
|
||||
```
|
||||
|
||||
Возвращает уникальный идентификатор сущности игрока
|
||||
|
||||
## Библиотека *world*
|
||||
|
||||
```python
|
||||
@ -319,150 +335,6 @@ inventory.move(invA: int, slotA: int, invB: int, slotB: int)
|
||||
invA и invB могут указывать на один инвентарь.
|
||||
slotB будет выбран автоматически, если не указывать явно.
|
||||
|
||||
## Библиотека *block*
|
||||
|
||||
```python
|
||||
block.name(blockid: int) -> str
|
||||
```
|
||||
|
||||
Возвращает строковый id блока по его числовому id.
|
||||
|
||||
```python
|
||||
block.index(name: str) -> int
|
||||
```
|
||||
|
||||
Возвращает числовой id блока, принимая в качестве агрумента строковый.
|
||||
|
||||
```python
|
||||
block.material(blockid: int) -> str
|
||||
```
|
||||
|
||||
Возвращает id материала блока.
|
||||
|
||||
```python
|
||||
block.caption(blockid: int) -> str
|
||||
```
|
||||
|
||||
Возвращает название блока, отображаемое в интерфейсе.
|
||||
|
||||
```python
|
||||
block.get(x: int, y: int, z: int) -> int
|
||||
```
|
||||
|
||||
Возвращает числовой id блока на указанных координатах. Если чанк на указанных координатах не загружен, возвращает -1.
|
||||
|
||||
```python
|
||||
block.get_states(x: int, y: int, z: int) -> int
|
||||
```
|
||||
|
||||
Возвращает состояние (поворот + доп. информация) в виде целого числа
|
||||
|
||||
```python
|
||||
block.set(x: int, y: int, z: int, id: int, states: int)
|
||||
```
|
||||
|
||||
Устанавливает блок с заданным числовым id и состоянием (0 - по-умолчанию) на заданных координатах.
|
||||
|
||||
> [!WARNING]
|
||||
> `block.set` не вызывает событие on_placed.
|
||||
|
||||
```python
|
||||
block.is_solid_at(x: int, y: int, z: int) -> bool
|
||||
```
|
||||
|
||||
Проверяет, является ли блок на указанных координатах полным
|
||||
|
||||
```python
|
||||
block.is_replaceable_at(x: int, y: int, z: int) -> bool
|
||||
```
|
||||
Проверяет, можно ли на заданных координатах поставить блок (примеры: воздух, трава, цветы, вода)
|
||||
|
||||
```python
|
||||
block.defs_count() -> int
|
||||
```
|
||||
|
||||
Возвращает количество id доступных в движке блоков
|
||||
|
||||
Следующие три функции используется для учёта вращения блока при обращении к соседним блокам или других целей, где направление блока имеет решающее значение.
|
||||
|
||||
|
||||
```python
|
||||
block.get_X(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Возвращает целочисленный единичный вектор X блока на указанных координатах с учётом его вращения (три целых числа).
|
||||
Если поворот отсутствует, возвращает 1, 0, 0
|
||||
|
||||
```python
|
||||
block.get_Y(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Возвращает целочисленный единичный вектор Y блока на указанных координатах с учётом его вращения (три целых числа).
|
||||
Если поворот отсутствует, возвращает 0, 1, 0
|
||||
|
||||
```python
|
||||
block.get_Z(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Возвращает целочисленный единичный вектор Z блока на указанных координатах с учётом его вращения (три целых числа).
|
||||
Если поворот отсутствует, возвращает 0, 0, 1
|
||||
|
||||
```python
|
||||
block.get_rotation(x: int, y: int, z: int) -> int
|
||||
```
|
||||
|
||||
Возвращает индекс поворота блока в его профиле вращения.
|
||||
|
||||
```python
|
||||
block.set_rotation(x: int, y: int, z: int, rotation: int)
|
||||
```
|
||||
|
||||
Устанавливает вращение блока по индексу в его профиле вращения.
|
||||
|
||||
### Расширенные блоки
|
||||
|
||||
Расширенные блоки - те, размер которых превышает 1x1x1
|
||||
|
||||
```python
|
||||
block.is_extended(id: int) -> bool
|
||||
```
|
||||
|
||||
Проверяет, является ли блок расширенным.
|
||||
|
||||
```python
|
||||
block.get_size(id: int) -> int, int, int
|
||||
```
|
||||
|
||||
Возвращает размер блока.
|
||||
|
||||
```python
|
||||
block.is_segment(x: int, y: int, z: int) -> bool
|
||||
```
|
||||
|
||||
Проверяет является ли блок сегментом расширенного блока, не являющимся главным.
|
||||
|
||||
```python
|
||||
block.seek_origin(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
Возвращает позицию главного сегмента расширенного блока или исходную позицию,
|
||||
если блок не являющийся расширенным.
|
||||
|
||||
### Пользовательские биты
|
||||
|
||||
Выделенная под использования в скриптах часть поля `voxel.states` хранящего доп-информацию о вокселе, такую как вращение блока. На данный момент выделенная часть составляет 8 бит.
|
||||
|
||||
```python
|
||||
block.get_user_bits(x: int, y: int, z: int, offset: int, bits: int) -> int
|
||||
```
|
||||
|
||||
Возвращает выбранное число бит с указанного смещения в виде целого беззнакового числа
|
||||
|
||||
```python
|
||||
block.set_user_bits(x: int, y: int, z: int, offset: int, bits: int, value: int) -> int
|
||||
```
|
||||
Записывает указанное число бит значения value в user bits по выбранному смещению
|
||||
|
||||
## Библиотека item
|
||||
|
||||
```python
|
||||
@ -562,6 +434,18 @@ hud.resume()
|
||||
|
||||
Закрывает меню паузы.
|
||||
|
||||
```python
|
||||
hud.is_paused() -> bool
|
||||
```
|
||||
|
||||
Возвращает true если открыто меню паузы.
|
||||
|
||||
```python
|
||||
hud.is_inventory_open() -> bool
|
||||
```
|
||||
|
||||
Возвращает true если открыт инвентарь или оверлей.
|
||||
|
||||
## Библиотека time
|
||||
|
||||
```python
|
||||
|
||||
143
doc/ru/scripting/builtins/libblock.md
Normal file
143
doc/ru/scripting/builtins/libblock.md
Normal file
@ -0,0 +1,143 @@
|
||||
# Библиотека *block*
|
||||
|
||||
```lua
|
||||
-- Возвращает строковый id блока по его числовому id.
|
||||
block.name(blockid: int) -> str
|
||||
|
||||
-- Возвращает числовой id блока, принимая в качестве агрумента строковый
|
||||
block.index(name: str) -> int
|
||||
|
||||
-- Возвращает id материала блока.
|
||||
block.material(blockid: int) -> str
|
||||
|
||||
-- Возвращает название блока, отображаемое в интерфейсе.
|
||||
block.caption(blockid: int) -> str
|
||||
|
||||
-- Возвращает числовой id блока на указанных координатах.
|
||||
-- Если чанк на указанных координатах не загружен, возвращает -1.
|
||||
block.get(x: int, y: int, z: int) -> int
|
||||
|
||||
-- Возвращает состояние (поворот + доп. информация) в виде целого числа
|
||||
block.get_states(x: int, y: int, z: int) -> int
|
||||
|
||||
-- Устанавливает блок с заданным числовым id и состоянием (0 - по-умолчанию) на заданных координатах.
|
||||
block.set(x: int, y: int, z: int, id: int, states: int)
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> `block.set` не вызывает событие on_placed.
|
||||
|
||||
```lua
|
||||
-- Проверяет, является ли блок на указанных координатах полным
|
||||
block.is_solid_at(x: int, y: int, z: int) -> bool
|
||||
|
||||
-- Проверяет, можно ли на заданных координатах поставить блок
|
||||
-- (примеры: воздух, трава, цветы, вода)
|
||||
block.is_replaceable_at(x: int, y: int, z: int) -> bool
|
||||
|
||||
-- Возвращает количество id доступных в загруженном контенте блоков
|
||||
block.defs_count() -> int
|
||||
|
||||
-- Возвращает числовой id предмета, указанного в свойстве *picking-item*.
|
||||
block.get_picking_item(id: int) -> int
|
||||
```
|
||||
|
||||
### Raycast
|
||||
|
||||
```lua
|
||||
block.raycast(start: vec3, dir: vec3, max_distance: number, [опционально] dest: table) -> {
|
||||
block: int, -- id блока
|
||||
endpoint: vec3, -- точка касания луча
|
||||
iendpoint: vec3, -- позиция блока, которого касается луч
|
||||
length: number, -- длина луча
|
||||
normal: vec3 -- вектор нормали поверхности, которой касается луч
|
||||
} или nil
|
||||
```
|
||||
|
||||
Бросает луч из точки start в направлении dir. Max_distance указывает максимальную длину луча.
|
||||
|
||||
Функция возвращает таблицу с результатами или nil, если луч не касается блока.
|
||||
|
||||
Для результата будет использоваться целевая (dest) таблица вместо создания новой, если указан опциональный аргумент.
|
||||
|
||||
## Вращение
|
||||
|
||||
Следующие функции используется для учёта вращения блока при обращении к соседним блокам или других целей, где направление блока имеет решающее значение.
|
||||
|
||||
|
||||
```lua
|
||||
-- Возвращает целочисленный единичный вектор X блока на указанных координатах с учётом его вращения (три целых числа).
|
||||
-- Если поворот отсутствует, возвращает 1, 0, 0
|
||||
block.get_X(x: int, y: int, z: int) -> int, int, int
|
||||
|
||||
-- То же, но для оси Y (по-умолчанию 0, 1, 0)
|
||||
block.get_Y(x: int, y: int, z: int) -> int, int, int
|
||||
|
||||
-- То же, но для оси Z (по-умолчанию 0, 0, 1)
|
||||
block.get_Z(x: int, y: int, z: int) -> int, int, int
|
||||
|
||||
-- Возвращает индекс поворота блока в его профиле вращения (не превышает 7).
|
||||
block.get_rotation(x: int, y: int, z: int) -> int
|
||||
|
||||
-- Устанавливает вращение блока по индексу в его профиле вращения.
|
||||
block.set_rotation(x: int, y: int, z: int, rotation: int)
|
||||
|
||||
-- Возвращает имя профиля вращения (none/pane/pipe)
|
||||
block.get_rotation_profile(id: int) -> str
|
||||
```
|
||||
|
||||
## Расширенные блоки
|
||||
|
||||
Расширенные блоки - те, размер которых превышает 1x1x1
|
||||
|
||||
```lua
|
||||
-- Проверяет, является ли блок расширенным.
|
||||
block.is_extended(id: int) -> bool
|
||||
|
||||
-- Возвращает размер блока.
|
||||
block.get_size(id: int) -> int, int, int
|
||||
|
||||
-- Проверяет является ли блок сегментом расширенного блока, не являющимся главным.
|
||||
block.is_segment(x: int, y: int, z: int) -> bool
|
||||
|
||||
-- Возвращает позицию главного сегмента расширенного блока или исходную позицию,
|
||||
-- если блок не является расширенным.
|
||||
block.seek_origin(x: int, y: int, z: int) -> int, int, int
|
||||
```
|
||||
|
||||
## Пользовательские биты
|
||||
|
||||
Выделенная под использования в скриптах часть поля `voxel.states` хранящего доп-информацию о вокселе, такую как вращение блока. На данный момент выделенная часть составляет 8 бит.
|
||||
|
||||
```lua
|
||||
-- Возвращает выбранное число бит с указанного смещения в виде целого беззнакового числа
|
||||
block.get_user_bits(x: int, y: int, z: int, offset: int, bits: int) -> int
|
||||
|
||||
-- Записывает указанное число бит значения value в user bits по выбранному смещению
|
||||
block.set_user_bits(x: int, y: int, z: int, offset: int, bits: int, value: int) -> int
|
||||
```
|
||||
|
||||
|
||||
## Физика
|
||||
|
||||
Информация свойствах блока, используемых физическим движком.
|
||||
|
||||
```lua
|
||||
-- Возвращает массив из двух векторов (массивов из 3 чисел):
|
||||
-- 1. Минимальная точка хитбокса
|
||||
-- 2. Размер хитбокса
|
||||
-- rotation_index - индекс поворота блока
|
||||
block.get_hitbox(id: int, rotation_index: int) -> {vec3, vec3}
|
||||
```
|
||||
|
||||
## Модель
|
||||
|
||||
Информация о модели блока.
|
||||
|
||||
```lua
|
||||
-- возвращает тип модели блока (block/aabb/custom/...)
|
||||
block.get_model(id: int) -> str
|
||||
|
||||
-- возвращает массив из 6 текстур, назначенных на стороны блока
|
||||
block.get_textures(id: int) -> таблица строк
|
||||
```
|
||||
42
doc/ru/scripting/builtins/libentities.md
Normal file
42
doc/ru/scripting/builtins/libentities.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Библиотека *entities*
|
||||
|
||||
Библиотека предназначена для работы с реестром сущностей.
|
||||
|
||||
```lua
|
||||
-- Возвращает сущность по уникальному идентификатору
|
||||
-- Возвращаемая таблица - та же, что доступна в компонентах сущности.
|
||||
entities.get(uid: int) -> table
|
||||
|
||||
-- Создает указанную сущность.
|
||||
-- args - таблица таблиц параметров компонентов (переменная ARGS)
|
||||
-- args не является обязательным
|
||||
entities.spawn(name: str, pos: vec3, [optional] args: table)
|
||||
|
||||
-- Проверяет наличие сущности по уникальному идентификатору.
|
||||
entities.exists(uid: int) -> bool
|
||||
|
||||
-- Возвращает таблицу всех загруженных сущностей
|
||||
entities.get_all() -> table
|
||||
|
||||
-- Возвращает таблицу загруженных сущностей по переданному списку UID
|
||||
entities.get_all(uids: array<int>) -> table
|
||||
|
||||
-- Возвращает список UID сущностей, попадающих в прямоугольную область
|
||||
-- pos - минимальный угол области
|
||||
-- size - размер области
|
||||
entities.get_all_in_box(pos: vec3, size: vec3) -> array<int>
|
||||
|
||||
-- Возвращает список UID сущностей, попадающих в радиус
|
||||
-- center - центр области
|
||||
-- radius - радиус области
|
||||
entities.get_all_in_radius(center: vec3, radius: number) -> array<int>
|
||||
```
|
||||
|
||||
```lua
|
||||
entities.raycast(start: vec3, dir: vec3, max_distance: number,
|
||||
ignore: int, [optional] destination: table) -> table или nil
|
||||
```
|
||||
|
||||
Функция является расширенным вариантом [block.raycast](libblock.md#raycast). Возвращает таблицу с результатами если луч касается блока, либо сущности.
|
||||
|
||||
Соответственно это повлияет на наличие полей *entity* и *block*.
|
||||
@ -33,7 +33,7 @@ vecn.add(a: vector, b: vector)
|
||||
vecn.add(a: vector, b: number)
|
||||
|
||||
-- записывает результат сложения двух векторов в dst
|
||||
vec.add(a: vector, b: vector, dst: vector)
|
||||
vecn.add(a: vector, b: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Вычитание - *vecn.sub(...)*
|
||||
@ -46,7 +46,7 @@ vecn.sub(a: vector, b: vector)
|
||||
vecn.sub(a: vector, b: number)
|
||||
|
||||
-- записывает результат вычитания двух векторов в dst
|
||||
vec.sub(a: vector, b: vector, dst: vector)
|
||||
vecn.sub(a: vector, b: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Умножение - *vecn.mul(...)*
|
||||
@ -66,7 +66,7 @@ vecn.mul(a: vector, b: number)
|
||||
vecn.inverse(a: vector)
|
||||
|
||||
-- записывает инвертированный вектор в dst
|
||||
vec.inverse(v: vector, dst: vector)
|
||||
vecn.inverse(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Деление - *vecn.div(...)*
|
||||
@ -79,7 +79,7 @@ vecn.div(a: vector, b: vector)
|
||||
vecn.div(a: vector, b: number)
|
||||
|
||||
-- записывает результат деления двух векторов в dst
|
||||
vec.div(a: vector, b: vector, dst: vector)
|
||||
vecn.div(a: vector, b: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Нормализация - *vecn.norm(...)*
|
||||
@ -89,7 +89,7 @@ vec.div(a: vector, b: vector, dst: vector)
|
||||
vecn.normalize(a: vector)
|
||||
|
||||
-- записывает нормализованный вектор в dst
|
||||
vec.normalize(v: vector, dst: vector)
|
||||
vecn.normalize(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Длина вектора - *vecn.len(...)*
|
||||
@ -107,7 +107,7 @@ vecn.length(a: vector)
|
||||
vecn.abs(a: vector)
|
||||
|
||||
-- записывает абсолютное значение вектора в dst
|
||||
vec.abs(v: vector, dst: vector)
|
||||
vecn.abs(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Округление - *vecn.round(...)*
|
||||
@ -117,7 +117,7 @@ vec.abs(v: vector, dst: vector)
|
||||
vecn.round(a: vector)
|
||||
|
||||
-- записывает округленный вектор в dst
|
||||
vec.round(v: vector, dst: vector)
|
||||
vecn.round(v: vector, dst: vector)
|
||||
```
|
||||
|
||||
#### Степень - *vecn.pow(...)*
|
||||
@ -127,7 +127,7 @@ vec.round(v: vector, dst: vector)
|
||||
vecn.pow(a: vector, b: number)
|
||||
|
||||
-- записывает вектор, возведенный в степень, в dst
|
||||
vec.pow(v: vector, exponent: number, dst: vector)
|
||||
vecn.pow(v: vector, exponent: number, dst: vector)
|
||||
```
|
||||
|
||||
#### Скалярное произведение - *vecn.dot(...)*
|
||||
@ -144,6 +144,18 @@ vecn.dot(a: vector, b: vector)
|
||||
vecn.tostring(a: vector)
|
||||
```
|
||||
|
||||
## Специфические функции
|
||||
|
||||
Функции относящиеся к конкретным размерностям векторов.
|
||||
|
||||
```lua
|
||||
-- возвращает случайный вектор, координаты которого равномерно распределены на сфере заданного радиуса
|
||||
vec3.spherical_rand(radius: number)
|
||||
|
||||
-- записывает случайный вектор, координаты которого равномерно распределены на сфере заданного радиуса в dst
|
||||
vec3.spherical_rand(radius: number, dst: vec3)
|
||||
```
|
||||
|
||||
|
||||
## Пример
|
||||
```lua
|
||||
|
||||
211
doc/ru/scripting/ecs.md
Normal file
211
doc/ru/scripting/ecs.md
Normal file
@ -0,0 +1,211 @@
|
||||
# Сущности и компоненты
|
||||
|
||||
## Обозначения типов, используемые далее
|
||||
|
||||
- vec3 - 3D вектор (массив из трех чисел)
|
||||
- mat4 - матрица 4x4 (массив из 16 чисел)
|
||||
|
||||
Аннотации типов добавлены в целях документации и не являются частью синтаксиса
|
||||
Lua.
|
||||
|
||||
## Сущность
|
||||
|
||||
Объект сущности доступен в компонентах как глобальная переменная **entity**.
|
||||
|
||||
```lua
|
||||
-- Удаляет сущность (сущность может продолжать существовать до завершения кадра, но не будет отображена в этом кадре)
|
||||
entity:despawn()
|
||||
|
||||
-- Возращает имя скелета сущности
|
||||
entity:get_skeleton() -> str
|
||||
-- Заменяет скелет сущности
|
||||
entity:set_skeleton(name: str)
|
||||
|
||||
-- Возращает уникальный идентификатор сущности
|
||||
entity:get_uid() -> int
|
||||
|
||||
-- Возвращает компонент по имени
|
||||
entity:get_component(name: str) -> компонент или nil
|
||||
-- Проверяет наличие компонента по имени
|
||||
entity:has_component(name: str) -> bool
|
||||
```
|
||||
|
||||
## Встроенные компоненты
|
||||
|
||||
### Transform
|
||||
|
||||
Компонент отвечает за позицию, масштаб и вращение сущности.
|
||||
|
||||
```lua
|
||||
-- Сокращение
|
||||
local tsf = entity.transform
|
||||
|
||||
-- Возвращает позицию сущности
|
||||
tsf:get_pos() -> vec3
|
||||
-- Устанавливает позицию сущности
|
||||
tsf:set_pos(pos: vec3)
|
||||
|
||||
-- Возвращает масштаб сущности
|
||||
tsf:get_size() -> vec3
|
||||
-- Устанавливает масштаб сущности
|
||||
tsf:set_size(size: vec3)
|
||||
|
||||
-- Возвращает вращение сущности
|
||||
tsf:get_rot() -> mat4
|
||||
-- Устанавливает вращение сущности
|
||||
tsf:set_rot(size: mat4)
|
||||
```
|
||||
|
||||
### Rigidbody
|
||||
|
||||
Компонент отвечает за физическое тело сущности.
|
||||
|
||||
```lua
|
||||
-- Сокращение
|
||||
local body = entity.rigidbody
|
||||
|
||||
-- Проверяет, включен ли рассчет физики тела
|
||||
body:is_enabled() -> bool
|
||||
-- Включает/выключает рассчет физики тела
|
||||
body:set_enabled(enabled: bool)
|
||||
|
||||
-- Возвращает линейную скорость
|
||||
body:get_vel() -> vec3
|
||||
-- Устанавливает линейную скорость
|
||||
body:set_vel(vel: vec3)
|
||||
|
||||
-- Возвращает размер хитбокса
|
||||
body:get_size() -> vec3
|
||||
-- Устанавливает размер хитбокса
|
||||
body:set_size(size: vec3)
|
||||
|
||||
-- Возвращает множитель гравитации
|
||||
body:get_gravity_scale() -> vec3
|
||||
-- Устанавливает множитель гравитации
|
||||
body:set_gravity_scale(scale: vec3)
|
||||
|
||||
-- Возвращает множитель затухания линейной скорости (используется для имитации сопротивления воздуха и трения)
|
||||
body:get_linear_damping() -> number
|
||||
-- Устанавливает множитель затухания линейной скорости
|
||||
body:set_linear_damping(value: number)
|
||||
|
||||
-- Проверяет, включено ли вертикальное затухание скорости
|
||||
body:is_vdamping() -> bool
|
||||
-- Включает/выключает вертикальное затухание скорости
|
||||
body:set_vdamping(enabled: bool)
|
||||
|
||||
-- Проверяет, находится ли сущность на земле (приземлена)
|
||||
body:is_grounded() -> bool
|
||||
|
||||
-- Проверяет, находится ли сущность в "крадущемся" состоянии (не может упасть с блоков)
|
||||
body:is_crouching() -> bool
|
||||
-- Включает/выключает "крадущееся" состояние
|
||||
body:set_crouching(enabled: bool)
|
||||
|
||||
-- Возвращает тип физического тела (dynamic/kinematic)
|
||||
body:get_body_type() -> str
|
||||
-- Устанавливает тип физического тела
|
||||
body:set_body_type(type: str)
|
||||
```
|
||||
|
||||
### Skeleton
|
||||
|
||||
Компонент отвечает за скелет сущности. См. [риггинг](../rigging.md).
|
||||
|
||||
```lua
|
||||
-- Сокращение
|
||||
local rig = entity.skeleton
|
||||
|
||||
-- Возвращает имя модели, назначенной на кость с указанным индексом
|
||||
rig:get_model(index: int) -> str
|
||||
|
||||
-- Переназначает модель кости с указанным индексом
|
||||
-- Сбрасывает до изначальной, если не указывать имя
|
||||
rig:set_model(index: int, name: str)
|
||||
|
||||
-- Возвращает матрицу трансформации кости с указанным индексом
|
||||
rig:get_matrix(index: int) -> mat4
|
||||
-- Устанавливает матрицу трансформации кости с указанным индексом
|
||||
rig:set_matrix(index: int, matrix: mat4)
|
||||
|
||||
-- Возвращает текстуру по ключу (динамически назначаемые текстуры - '$имя')
|
||||
rig:get_texture(key: str) -> str
|
||||
|
||||
-- Назначает текстуру по ключу
|
||||
rig:set_texture(key: str, value: str)
|
||||
|
||||
-- Возвращает индекс кости по имени или nil
|
||||
rig:index(name: str) -> int
|
||||
|
||||
-- Проверяет статус видимости кости по индесу
|
||||
-- или всего скелета, если индекс не указан
|
||||
rig:is_visible([optional] index: int) -> bool
|
||||
|
||||
-- Устанавливает статус видимости кости по индексу
|
||||
-- или всего скелета, если индекс не указан
|
||||
rig:set_visible([optional] index: int, status: bool)
|
||||
```
|
||||
|
||||
## События компонента
|
||||
|
||||
```lua
|
||||
function on_despawn()
|
||||
```
|
||||
|
||||
Вызывается при удалении сущности.
|
||||
|
||||
```lua
|
||||
function on_grounded(force: number)
|
||||
```
|
||||
|
||||
Вызывается при приземлении. В качестве первого аргумента передается сила удара. (На данный момент - модуль скорости).
|
||||
|
||||
```lua
|
||||
function on_fall()
|
||||
```
|
||||
|
||||
Вызывается при начале падения.
|
||||
|
||||
```lua
|
||||
function on_save()
|
||||
```
|
||||
|
||||
Вызывается перед сохранением данных компонентов. Здесь можно записать данные, которые нужно сохранить, в таблицу *SAVED_DATA*, которая доступна весь срок жизни компонента.
|
||||
|
||||
```lua
|
||||
function on_sensor_enter(index: int, entity: int)
|
||||
```
|
||||
|
||||
Вызывается при попадании другой сущности в сенсор с индексом, передаваемым первым аргументом. UID сущности, попавшей в сенсор, передается вторым аргументом.
|
||||
|
||||
```lua
|
||||
function on_sensor_exit(index: int, entity: int)
|
||||
```
|
||||
|
||||
Вызывается при выходе другой сущности из сенсора с индексом, передаваемым первым аргументом. UID сущности, покинувшей сенсор, передается вторым аргументом.
|
||||
|
||||
```lua
|
||||
function on_aim_on(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при наведении прицела игрока на сущность. ID игрока передается в качестве аргумента.
|
||||
|
||||
```lua
|
||||
function on_aim_off(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается когда игрок уводит прицел с сущности. ID игрока передается в качестве аргумента.
|
||||
|
||||
```lua
|
||||
function on_attacked(attackerid: int, playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при атаке на сущность (ЛКМ по сущности). Первым аргументом передается UID атакующей сущности. ID атакующего игрока передается вторым аргументом. Если сущность была атакована не игроком, значение второго аргумента будет равно -1.
|
||||
|
||||
|
||||
```lua
|
||||
function on_used(playerid: int)
|
||||
```
|
||||
|
||||
Вызывается при использовании сущности (ПКМ по сущности). ID игрока передается в качестве аргумента.
|
||||
|
||||
@ -57,3 +57,24 @@ input.get_bindings() -> массив строк
|
||||
```
|
||||
|
||||
Возвращает названия всех доступных привязок.
|
||||
|
||||
```python
|
||||
input.is_active(bindname: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет активность привязки.
|
||||
|
||||
```python
|
||||
input.is_pressed(code: str) -> bool
|
||||
```
|
||||
|
||||
Проверяет активность ввода по коду, состоящему из:
|
||||
- типа ввода: key (клавиша) или mouse (кнопка мыши)
|
||||
- код ввода: [имя клавиши](#имена-клавиш) или имя кнопки мыши (left, middle, right)
|
||||
|
||||
Пример:
|
||||
```lua
|
||||
if (input.is_pressed("key:enter") {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
@ -15,4 +15,5 @@ player.flight="key:f"
|
||||
player.attack="mouse:left"
|
||||
player.build="mouse:right"
|
||||
player.pick="mouse:middle"
|
||||
player.drop="key:q"
|
||||
hud.inventory="key:tab"
|
||||
|
||||
@ -14,5 +14,6 @@
|
||||
"shadeless": true,
|
||||
"obstacle": false,
|
||||
"grounded": true,
|
||||
"rotation": "pipe"
|
||||
"rotation": "pipe",
|
||||
"material": "base:wood"
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
{
|
||||
"items": [
|
||||
"bazalt_breaker"
|
||||
"entities": [
|
||||
"drop",
|
||||
"player",
|
||||
"falling_block"
|
||||
],
|
||||
"blocks": [
|
||||
"dirt",
|
||||
@ -27,5 +29,8 @@
|
||||
"lightbulb",
|
||||
"torch",
|
||||
"wooden_door"
|
||||
],
|
||||
"items": [
|
||||
"bazalt_breaker"
|
||||
]
|
||||
}
|
||||
11
res/content/base/entities/drop.json
Normal file
11
res/content/base/entities/drop.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"components": [
|
||||
"base:drop"
|
||||
],
|
||||
"hitbox": [0.4, 0.25, 0.4],
|
||||
"sensors": [
|
||||
["aabb", -0.2, -0.2, -0.2, 0.2, 0.2, 0.2],
|
||||
["radius", 1.6]
|
||||
],
|
||||
"blocking": false
|
||||
}
|
||||
7
res/content/base/entities/falling_block.json
Normal file
7
res/content/base/entities/falling_block.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"components": [
|
||||
"base:falling_block"
|
||||
],
|
||||
"skeleton-name": "base:block",
|
||||
"hitbox": [0.8, 0.8, 0.8]
|
||||
}
|
||||
3
res/content/base/entities/player.json
Normal file
3
res/content/base/entities/player.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"hitbox": [0.6, 1.8, 0.6]
|
||||
}
|
||||
48
res/content/base/models/block.obj
Normal file
48
res/content/base/models/block.obj
Normal file
@ -0,0 +1,48 @@
|
||||
o Cube
|
||||
v 0.5 -0.5 -0.5
|
||||
v 0.5 -0.5 0.5
|
||||
v -0.5 -0.5 0.5
|
||||
v -0.5 -0.5 -0.5
|
||||
v 0.5 0.5 -0.5
|
||||
v 0.5 0.5 0.5
|
||||
v -0.5 0.5 0.5
|
||||
v -0.5 0.5 -0.5
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 0.0 1.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vn 0.0 -1.0 0.0
|
||||
vn 0.0 1.0 0.0
|
||||
vn 1.0 -0.0 0.0
|
||||
vn -1.0 -0.0 -0.0
|
||||
vn 0.0 0.0 -1.0
|
||||
vn -0.0 -0.0 1.0
|
||||
usemtl $2
|
||||
s off
|
||||
f 1/1/1 2/2/1 3/3/1 4/4/1
|
||||
usemtl $3
|
||||
f 5/5/2 8/6/2 7/7/2 6/8/2
|
||||
usemtl $0
|
||||
f 1/9/3 5/10/3 6/8/3 2/11/3
|
||||
usemtl $1
|
||||
f 3/12/4 7/7/4 8/13/4 4/14/4
|
||||
usemtl $4
|
||||
f 5/15/5 1/1/5 4/16/5 8/17/5
|
||||
usemtl $5
|
||||
f 2/2/6 6/18/6 7/19/6 3/20/6
|
||||
48
res/content/base/models/drop-block.obj
Normal file
48
res/content/base/models/drop-block.obj
Normal file
@ -0,0 +1,48 @@
|
||||
o Cube
|
||||
v 0.125 -0.125 -0.125
|
||||
v 0.125 -0.125 0.125
|
||||
v -0.125 -0.125 0.125
|
||||
v -0.125 -0.125 -0.125
|
||||
v 0.125 0.125 -0.125
|
||||
v 0.125 0.125 0.125
|
||||
v -0.125 0.125 0.125
|
||||
v -0.125 0.125 -0.125
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 1.0 0.0
|
||||
vt 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vt 0.0 1.0
|
||||
vt 1.0 0.0
|
||||
vt 1.0 1.0
|
||||
vt 1.0 1.0
|
||||
vt 0.0 1.0
|
||||
vt 0.0 0.0
|
||||
vn 0.0 -1.0 0.0
|
||||
vn 0.0 1.0 0.0
|
||||
vn 1.0 -0.0 0.0
|
||||
vn -1.0 -0.0 -0.0
|
||||
vn 0.0 0.0 -1.0
|
||||
vn -0.0 -0.0 1.0
|
||||
usemtl $2
|
||||
s off
|
||||
f 1/1/1 2/2/1 3/3/1 4/4/1
|
||||
usemtl $3
|
||||
f 5/5/2 8/6/2 7/7/2 6/8/2
|
||||
usemtl $0
|
||||
f 1/9/3 5/10/3 6/8/3 2/11/3
|
||||
usemtl $1
|
||||
f 3/12/4 7/7/4 8/13/4 4/14/4
|
||||
usemtl $4
|
||||
f 5/15/5 1/1/5 4/16/5 8/17/5
|
||||
usemtl $5
|
||||
f 2/2/6 6/18/6 7/19/6 3/20/6
|
||||
113
res/content/base/models/drop-item.obj
Normal file
113
res/content/base/models/drop-item.obj
Normal file
@ -0,0 +1,113 @@
|
||||
o Cube
|
||||
v 0.282501 -0.000054 -0.282500
|
||||
v -0.282501 -0.000054 -0.282501
|
||||
v -0.282501 -0.000054 0.282500
|
||||
v 0.282500 -0.000054 0.282501
|
||||
v 0.282501 0.012502 -0.282500
|
||||
v -0.282501 0.012502 -0.282501
|
||||
v -0.282501 0.012502 0.282500
|
||||
v 0.282500 0.012502 0.282501
|
||||
v 0.282501 0.012502 -0.282500
|
||||
v 0.282500 0.012502 0.282501
|
||||
v -0.282501 0.012502 0.282500
|
||||
v -0.282501 0.012502 -0.282501
|
||||
v 0.282501 -0.000054 -0.282500
|
||||
v 0.282500 -0.000054 0.282501
|
||||
v -0.282501 -0.000054 0.282500
|
||||
v -0.282501 -0.000054 -0.282501
|
||||
v 0.282501 0.012502 -0.282500
|
||||
v -0.282501 0.012502 -0.282501
|
||||
v -0.282501 0.012502 0.282500
|
||||
v 0.282500 0.012502 0.282501
|
||||
v 0.282501 0.012502 -0.282500
|
||||
v 0.282500 0.012502 0.282501
|
||||
v -0.282501 0.012502 0.282500
|
||||
v -0.282501 0.012502 -0.282501
|
||||
v 0.282501 -0.015821 -0.282500
|
||||
v -0.282501 -0.015821 -0.282501
|
||||
v -0.282501 -0.015821 0.282500
|
||||
v 0.282500 -0.015821 0.282501
|
||||
v 0.282501 0.027439 -0.282500
|
||||
v -0.282501 0.027439 -0.282501
|
||||
v -0.282501 0.027439 0.282500
|
||||
v 0.282500 0.027439 0.282501
|
||||
v 0.282501 0.027439 -0.282500
|
||||
v 0.282500 0.027439 0.282501
|
||||
v -0.282501 0.027439 0.282500
|
||||
v -0.282501 0.027439 -0.282501
|
||||
v 0.282501 -0.015821 -0.282500
|
||||
v 0.282500 -0.015821 0.282501
|
||||
v -0.282501 -0.015821 0.282500
|
||||
v -0.282501 -0.015821 -0.282501
|
||||
v 0.282501 0.027439 -0.282500
|
||||
v -0.282501 0.027439 -0.282501
|
||||
v -0.282501 0.027439 0.282500
|
||||
v 0.282500 0.027439 0.282501
|
||||
v 0.282501 0.027439 -0.282500
|
||||
v 0.282500 0.027439 0.282501
|
||||
v -0.282501 0.027439 0.282500
|
||||
v -0.282501 0.027439 -0.282501
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vn -0.0000 1.0000 0.0000
|
||||
vn 0.0000 -1.0000 -0.0000
|
||||
usemtl $0
|
||||
s 1
|
||||
f 1/1/1 2/2/1 3/3/1 4/4/1
|
||||
f 5/5/1 6/6/1 7/7/1 8/8/1
|
||||
f 9/9/2 10/10/2 11/11/2 12/12/2
|
||||
f 13/13/2 14/14/2 15/15/2 16/16/2
|
||||
f 17/17/1 18/18/1 19/19/1 20/20/1
|
||||
f 21/21/2 22/22/2 23/23/2 24/24/2
|
||||
f 25/25/1 26/26/1 27/27/1 28/28/1
|
||||
f 29/29/1 30/30/1 31/31/1 32/32/1
|
||||
f 33/33/2 34/34/2 35/35/2 36/36/2
|
||||
f 37/37/2 38/38/2 39/39/2 40/40/2
|
||||
f 41/41/1 42/42/1 43/43/1 44/44/1
|
||||
f 45/45/2 46/46/2 47/47/2 48/48/2
|
||||
48
res/content/base/models/player-head.obj
Normal file
48
res/content/base/models/player-head.obj
Normal file
@ -0,0 +1,48 @@
|
||||
# Blender v2.79 (sub 0) OBJ File: 'player.blend'
|
||||
# www.blender.org
|
||||
mtllib player-head.mtl
|
||||
o Cube.002_Cube.003
|
||||
v -0.238204 0.476553 -0.238204
|
||||
v -0.238204 0.000145 -0.238204
|
||||
v -0.238204 0.476553 0.238204
|
||||
v -0.238204 0.000145 0.238204
|
||||
v 0.238204 0.000145 0.238204
|
||||
v 0.238204 0.476553 0.238204
|
||||
v 0.238204 0.000145 -0.238204
|
||||
v 0.238204 0.476553 -0.238204
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.209065
|
||||
vt 0.735873 0.213345
|
||||
vt 0.735873 0.739780
|
||||
vt 0.209439 0.739780
|
||||
vt 0.209439 0.213345
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.209065
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.209065
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.783122 0.209065
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
usemtl entities/player
|
||||
s off
|
||||
f 4/1/1 3/2/1 1/3/1 2/4/1
|
||||
f 2/5/2 1/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 3/15/4 4/16/4
|
||||
f 2/17/5 7/18/5 5/19/5 4/16/5
|
||||
f 8/20/6 1/21/6 3/15/6 6/22/6
|
||||
46
res/content/base/models/player.obj
Normal file
46
res/content/base/models/player.obj
Normal file
@ -0,0 +1,46 @@
|
||||
# Blender v2.79 (sub 0) OBJ File: 'player.blend'
|
||||
# www.blender.org
|
||||
mtllib player.mtl
|
||||
o Cube
|
||||
v 0.125000 -0.900000 -0.125000
|
||||
v 0.125000 -0.900000 0.125000
|
||||
v -0.125000 -0.900000 0.125000
|
||||
v -0.125000 -0.900000 -0.125000
|
||||
v 0.125000 0.491919 -0.125000
|
||||
v 0.125000 0.491919 0.125000
|
||||
v -0.125000 0.491919 0.125000
|
||||
v -0.125000 0.491919 -0.125000
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.209065
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.209065
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.209065
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.783122 0.209065
|
||||
vt 0.783122 0.009685
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.982503 0.009685
|
||||
vt 0.982503 0.209065
|
||||
vt 0.783122 0.209065
|
||||
vn 0.0000 -1.0000 -0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
usemtl entities/player
|
||||
s off
|
||||
f 1/1/1 2/2/1 3/3/1 4/4/1
|
||||
f 5/5/2 8/6/2 7/7/2 6/8/2
|
||||
f 1/1/3 5/9/3 6/10/3 2/11/3
|
||||
f 2/12/4 6/13/4 7/7/4 3/14/4
|
||||
f 3/15/5 7/16/5 8/17/5 4/4/5
|
||||
f 5/5/6 1/18/6 4/19/6 8/20/6
|
||||
@ -1,6 +1,10 @@
|
||||
{
|
||||
"sounds": [
|
||||
"blocks/door_open",
|
||||
"blocks/door_close"
|
||||
"blocks/door_close",
|
||||
"events/pickup"
|
||||
],
|
||||
"models": [
|
||||
"drop-item"
|
||||
]
|
||||
}
|
||||
|
||||
8
res/content/base/resources.json
Normal file
8
res/content/base/resources.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"camera": [
|
||||
"first-person",
|
||||
"third-person-front",
|
||||
"third-person-back",
|
||||
"cinematic"
|
||||
]
|
||||
}
|
||||
117
res/content/base/scripts/components/drop.lua
Normal file
117
res/content/base/scripts/components/drop.lua
Normal file
@ -0,0 +1,117 @@
|
||||
local tsf = entity.transform
|
||||
local body = entity.rigidbody
|
||||
local rig = entity.skeleton
|
||||
|
||||
inair = true
|
||||
ready = false
|
||||
target = -1
|
||||
|
||||
ARGS = ARGS or {}
|
||||
local dropitem = ARGS
|
||||
if SAVED_DATA.item then
|
||||
dropitem.id = item.index(SAVED_DATA.item)
|
||||
dropitem.count = SAVED_DATA.count
|
||||
end
|
||||
|
||||
local scale = {1, 1, 1}
|
||||
local rotation = mat4.rotate({
|
||||
math.random(), math.random(), math.random()
|
||||
}, 360)
|
||||
|
||||
function on_save()
|
||||
SAVED_DATA.item = item.name(dropitem.id)
|
||||
SAVED_DATA.count = dropitem.count
|
||||
end
|
||||
|
||||
do -- setup visuals
|
||||
local matrix = mat4.idt()
|
||||
local icon = item.icon(dropitem.id)
|
||||
if icon:find("^block%-previews%:") then
|
||||
local bid = block.index(icon:sub(16))
|
||||
model = block.get_model(bid)
|
||||
if model == "X" then
|
||||
body:set_size(vec3.mul(body:get_size(), {1.0, 0.3, 1.0}))
|
||||
rig:set_model(0, "drop-item")
|
||||
rig:set_texture("$0", icon)
|
||||
else
|
||||
if model == "aabb" then
|
||||
local rot = block.get_rotation_profile(bid) == "pipe" and 4 or 0
|
||||
scale = block.get_hitbox(bid, rot)[2]
|
||||
body:set_size(vec3.mul(body:get_size(), {1.0, 0.7, 1.0}))
|
||||
vec3.mul(scale, 1.5, scale)
|
||||
end
|
||||
local textures = block.get_textures(bid)
|
||||
for i,t in ipairs(textures) do
|
||||
rig:set_texture("$"..tostring(i-1), "blocks:"..textures[i])
|
||||
end
|
||||
end
|
||||
else
|
||||
body:set_size(vec3.mul(body:get_size(), {1.0, 0.3, 1.0}))
|
||||
rig:set_model(0, "drop-item")
|
||||
rig:set_texture("$0", icon)
|
||||
end
|
||||
mat4.mul(matrix, rotation, matrix)
|
||||
mat4.scale(matrix, scale, matrix)
|
||||
rig:set_matrix(0, matrix)
|
||||
end
|
||||
|
||||
function on_grounded(force)
|
||||
local matrix = mat4.idt()
|
||||
mat4.rotate(matrix, {0, 1, 0}, math.random()*360, matrix)
|
||||
if model == "aabb" then
|
||||
mat4.rotate(matrix, {1, 0, 0}, 90, matrix)
|
||||
end
|
||||
mat4.scale(matrix, scale, matrix)
|
||||
rig:set_matrix(0, matrix)
|
||||
inair = false
|
||||
ready = true
|
||||
end
|
||||
|
||||
function on_fall()
|
||||
inair = true
|
||||
end
|
||||
|
||||
function on_sensor_enter(index, oid)
|
||||
local playerentity = player.get_entity(hud.get_player())
|
||||
if ready and oid == playerentity and index == 0 then
|
||||
entity:despawn()
|
||||
inventory.add(player.get_inventory(oid), dropitem.id, dropitem.count)
|
||||
audio.play_sound_2d("events/pickup", 0.5, 0.8+math.random()*0.4, "regular")
|
||||
end
|
||||
if index == 1 and ready and oid == playerentity then
|
||||
target = oid
|
||||
end
|
||||
end
|
||||
|
||||
function on_sensor_exit(index, oid)
|
||||
if oid == target and index == 1 then
|
||||
target = -1
|
||||
end
|
||||
end
|
||||
|
||||
function on_render()
|
||||
if inair then
|
||||
local dt = time.delta();
|
||||
|
||||
mat4.rotate(rotation, {0, 1, 0}, 240*dt, rotation)
|
||||
mat4.rotate(rotation, {0, 0, 1}, 240*dt, rotation)
|
||||
|
||||
local matrix = mat4.idt()
|
||||
mat4.mul(matrix, rotation, matrix)
|
||||
mat4.scale(matrix, scale, matrix)
|
||||
rig:set_matrix(0, matrix)
|
||||
end
|
||||
end
|
||||
|
||||
function on_update()
|
||||
if target ~= -1 then
|
||||
local dir = vec3.sub(entities.get(target).transform:get_pos(), tsf:get_pos())
|
||||
vec3.normalize(dir, dir)
|
||||
vec3.mul(dir, 10.0, dir)
|
||||
body:set_vel(dir)
|
||||
end
|
||||
end
|
||||
|
||||
function on_attacked(attacker, pid)
|
||||
body:set_vel({0, 10, 0})
|
||||
end
|
||||
33
res/content/base/scripts/components/falling_block.lua
Normal file
33
res/content/base/scripts/components/falling_block.lua
Normal file
@ -0,0 +1,33 @@
|
||||
local tsf = entity.transform
|
||||
local body = entity.rigidbody
|
||||
local rig = entity.skeleton
|
||||
|
||||
ARGS = ARGS or {}
|
||||
local blockid = ARGS.block
|
||||
if SAVED_DATA.block then
|
||||
blockid = SAVED_DATA.block
|
||||
else
|
||||
SAVED_DATA.block = blockid
|
||||
end
|
||||
|
||||
do -- setup visuals
|
||||
local textures = block.get_textures(block.index(blockid))
|
||||
for i,t in ipairs(textures) do
|
||||
rig:set_texture("$"..tostring(i-1), "blocks:"..textures[i])
|
||||
end
|
||||
end
|
||||
|
||||
function on_grounded()
|
||||
local pos = tsf:get_pos()
|
||||
local ix = math.floor(pos[1])
|
||||
local iy = math.floor(pos[2])
|
||||
local iz = math.floor(pos[3])
|
||||
if block.is_replaceable_at(ix, iy, iz) then
|
||||
block.set(ix, iy, iz, block.index(blockid))
|
||||
else
|
||||
local picking_item = block.get_picking_item(block.index(blockid))
|
||||
local drop = entities.spawn("base:drop", pos, {base__drop={id=picking_item, count=1}})
|
||||
drop.rigidbody:set_vel(vec3.spherical_rand(5.0))
|
||||
end
|
||||
entity:despawn()
|
||||
end
|
||||
27
res/content/base/scripts/hud.lua
Normal file
27
res/content/base/scripts/hud.lua
Normal file
@ -0,0 +1,27 @@
|
||||
local DROP_FORCE = 8
|
||||
local DROP_INIT_VEL = {0, 3, 0}
|
||||
|
||||
function on_hud_open()
|
||||
input.add_callback("player.drop", function ()
|
||||
if hud.is_paused() or hud.is_inventory_open() then
|
||||
return
|
||||
end
|
||||
local pid = hud.get_player()
|
||||
local invid, slot = player.get_inventory(pid)
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemid == 0 then
|
||||
return
|
||||
end
|
||||
inventory.set(invid, slot, itemid, itemcount-1)
|
||||
|
||||
local pvel = {player.get_vel(pid)}
|
||||
local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0})
|
||||
local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
|
||||
local drop = entities.spawn("base:drop", ppos, {base__drop={
|
||||
id=itemid,
|
||||
count=1
|
||||
}})
|
||||
local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
|
||||
drop.rigidbody:set_vel(velocity)
|
||||
end)
|
||||
end
|
||||
15
res/content/base/scripts/sand.lua
Normal file
15
res/content/base/scripts/sand.lua
Normal file
@ -0,0 +1,15 @@
|
||||
local function update(x, y, z)
|
||||
if block.is_replaceable_at(x, y-1, z) then
|
||||
entities.spawn("base:falling_block", {x+0.5, y+0.5, z+0.5},
|
||||
{base__falling_block={block='base:sand'}})
|
||||
block.set(x, y, z, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function on_update(x, y, z)
|
||||
update(x, y, z)
|
||||
end
|
||||
|
||||
function on_placed(x, y, z)
|
||||
update(x, y, z)
|
||||
end
|
||||
5
res/content/base/skeletons/block.json
Normal file
5
res/content/base/skeletons/block.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"root": {
|
||||
"model": "block"
|
||||
}
|
||||
}
|
||||
5
res/content/base/skeletons/drop.json
Normal file
5
res/content/base/skeletons/drop.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"root": {
|
||||
"model": "drop-block"
|
||||
}
|
||||
}
|
||||
16
res/content/base/skeletons/player.json
Normal file
16
res/content/base/skeletons/player.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"root": {
|
||||
"nodes": [
|
||||
{
|
||||
"name": "body",
|
||||
"model": "player",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "head",
|
||||
"model": "player-head"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
res/content/base/sounds/events/pickup.ogg
Normal file
BIN
res/content/base/sounds/events/pickup.ogg
Normal file
Binary file not shown.
BIN
res/content/base/textures/entities/player.png
Normal file
BIN
res/content/base/textures/entities/player.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 579 B |
@ -2,10 +2,19 @@ history = session.get_entry("commands_history")
|
||||
history_pointer = #history
|
||||
|
||||
function setup_variables()
|
||||
local x,y,z = player.get_pos(hud.get_player())
|
||||
local pid = hud.get_player()
|
||||
local x,y,z = player.get_pos(pid)
|
||||
console.set('pos.x', x)
|
||||
console.set('pos.y', y)
|
||||
console.set('pos.z', z)
|
||||
local pentity = player.get_entity(pid)
|
||||
if pentity ~= 0 then
|
||||
console.set('entity.id', pentity)
|
||||
end
|
||||
local sentity = player.get_selected_entity(pid)
|
||||
if sentity ~= nil then
|
||||
console.set('entity.selected', sentity)
|
||||
end
|
||||
end
|
||||
|
||||
function on_history_up()
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<container size='668,418' color='#0F1E2DB2' context='menu'>
|
||||
<panel pos='6' size='250' color='0' interval='1'>
|
||||
<button id='s_aud' onclick='set_page("s_aud", "settings_audio")'>@Audio</button>
|
||||
<button id='s_dsp' onclick='set_page("s_dsp", "settings_display")'>@Display</button>
|
||||
<button id='s_gfx' onclick='set_page("s_gfx", "settings_graphics")'>@Graphics</button>
|
||||
<button id='s_ctl' onclick='set_page("s_ctl", "settings_controls")'>@Controls</button>
|
||||
</panel>
|
||||
|
||||
@ -8,6 +8,7 @@ end
|
||||
|
||||
function set_page(btn, page)
|
||||
document.s_aud.enabled = true
|
||||
document.s_dsp.enabled = true
|
||||
document.s_gfx.enabled = true
|
||||
document.s_ctl.enabled = true
|
||||
document[btn].enabled = false
|
||||
|
||||
2
res/layouts/pages/settings_display.xml
Normal file
2
res/layouts/pages/settings_display.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<panel size='400' color='0' interval='1' context='menu'>
|
||||
</panel>
|
||||
43
res/layouts/pages/settings_display.xml.lua
Normal file
43
res/layouts/pages/settings_display.xml.lua
Normal file
@ -0,0 +1,43 @@
|
||||
function create_setting(id, name, step, postfix, tooltip)
|
||||
local info = core.get_setting_info(id)
|
||||
postfix = postfix or ""
|
||||
tooltip = tooltip or ""
|
||||
document.root:add(gui.template("track_setting", {
|
||||
id=id,
|
||||
name=gui.str(name, "settings"),
|
||||
value=core.get_setting(id),
|
||||
min=info.min,
|
||||
max=info.max,
|
||||
step=step,
|
||||
postfix=postfix,
|
||||
tooltip=tooltip
|
||||
}))
|
||||
update_setting(core.get_setting(id), id, name, postfix)
|
||||
end
|
||||
|
||||
function update_setting(x, id, name, postfix)
|
||||
core.set_setting(id, x)
|
||||
-- updating label
|
||||
document[id..".L"].text = string.format(
|
||||
"%s: %s%s",
|
||||
gui.str(name, "settings"),
|
||||
core.str_setting(id),
|
||||
postfix
|
||||
)
|
||||
end
|
||||
|
||||
function create_checkbox(id, name, tooltip)
|
||||
tooltip = tooltip or ''
|
||||
document.root:add(string.format(
|
||||
"<checkbox consumer='function(x) core.set_setting(\"%s\", x) end' checked='%s' tooltip='%s'>%s</checkbox>",
|
||||
id, core.str_setting(id), gui.str(tooltip, "settings"), gui.str(name, "settings")
|
||||
))
|
||||
end
|
||||
|
||||
function on_open()
|
||||
create_setting("camera.fov", "FOV", 1, "°")
|
||||
create_checkbox("display.fullscreen", "Fullscreen")
|
||||
create_checkbox("display.vsync", "V-Sync")
|
||||
create_checkbox("camera.shaking", "Camera Shaking")
|
||||
create_checkbox("camera.inertia", "Camera Inertia")
|
||||
end
|
||||
@ -39,9 +39,5 @@ function on_open()
|
||||
create_setting("chunks.load-speed", "Load Speed", 1)
|
||||
create_setting("graphics.fog-curve", "Fog Curve", 0.1)
|
||||
create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip")
|
||||
create_setting("camera.fov", "FOV", 1, "°")
|
||||
create_checkbox("display.fullscreen", "Fullscreen")
|
||||
create_checkbox("display.vsync", "V-Sync")
|
||||
create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip")
|
||||
create_checkbox("camera.shaking", "Camera Shaking")
|
||||
end
|
||||
|
||||
126
res/modules/internal/stdcomp.lua
Normal file
126
res/modules/internal/stdcomp.lua
Normal file
@ -0,0 +1,126 @@
|
||||
-- Standard components OOP wrappers (__index tables of metatables)
|
||||
|
||||
local Transform = {__index={
|
||||
get_pos=function(self) return __transform.get_pos(self.eid) end,
|
||||
set_pos=function(self, v) return __transform.set_pos(self.eid, v) end,
|
||||
get_size=function(self) return __transform.get_size(self.eid) end,
|
||||
set_size=function(self, v) return __transform.set_size(self.eid, v) end,
|
||||
get_rot=function(self) return __transform.get_rot(self.eid) end,
|
||||
set_rot=function(self, m) return __transform.set_rot(self.eid, m) end,
|
||||
}}
|
||||
|
||||
local function new_Transform(eid)
|
||||
return setmetatable({eid=eid}, Transform)
|
||||
end
|
||||
|
||||
local Rigidbody = {__index={
|
||||
is_enabled=function(self) return __rigidbody.is_enabled(self.eid) end,
|
||||
set_enabled=function(self, b) return __rigidbody.set_enabled(self.eid, b) end,
|
||||
get_vel=function(self) return __rigidbody.get_vel(self.eid) end,
|
||||
set_vel=function(self, v) return __rigidbody.set_vel(self.eid, v) end,
|
||||
get_size=function(self) return __rigidbody.get_size(self.eid) end,
|
||||
set_size=function(self, v) return __rigidbody.set_size(self.eid, v) end,
|
||||
get_gravity_scale=function(self) return __rigidbody.get_gravity_scale(self.eid) end,
|
||||
set_gravity_scale=function(self, s) return __rigidbody.set_gravity_scale(self.eid, s) end,
|
||||
get_linear_damping=function(self) return __rigidbody.get_linear_damping(self.eid) end,
|
||||
set_linear_damping=function(self, f) return __rigidbody.set_linear_damping(self.eid, f) end,
|
||||
is_vdamping=function(self) return __rigidbody.is_vdamping(self.eid) end,
|
||||
set_vdamping=function(self, b) return __rigidbody.set_vdamping(self.eid, b) end,
|
||||
is_grounded=function(self) return __rigidbody.is_grounded(self.eid) end,
|
||||
is_crouching=function(self) return __rigidbody.is_crouching(self.eid) end,
|
||||
set_crouching=function(self, b) return __rigidbody.set_crouching(self.eid, b) end,
|
||||
get_body_type=function(self) return __rigidbody.get_body_type(self.eid) end,
|
||||
set_body_type=function(self, s) return __rigidbody.set_body_type(self.eid, s) end,
|
||||
}}
|
||||
|
||||
local function new_Rigidbody(eid)
|
||||
return setmetatable({eid=eid}, Rigidbody)
|
||||
end
|
||||
|
||||
local Skeleton = {__index={
|
||||
get_model=function(self, i) return __skeleton.get_model(self.eid, i) end,
|
||||
set_model=function(self, i, s) return __skeleton.set_model(self.eid, i, s) end,
|
||||
get_matrix=function(self, i) return __skeleton.get_matrix(self.eid, i) end,
|
||||
set_matrix=function(self, i, m) return __skeleton.set_matrix(self.eid, i, m) end,
|
||||
get_texture=function(self, s) return __skeleton.get_texture(self.eid, s) end,
|
||||
set_texture=function(self, s, s2) return __skeleton.set_texture(self.eid, s, s2) end,
|
||||
index=function(self, s) return __skeleton.index(self.eid, s) end,
|
||||
is_visible=function(self, i) return __skeleton.is_visible(self.eid, i) end,
|
||||
set_visible=function(self, i, b) return __skeleton.set_visible(self.eid, i, b) end,
|
||||
}}
|
||||
|
||||
local function new_Skeleton(eid)
|
||||
return setmetatable({eid=eid}, Skeleton)
|
||||
end
|
||||
|
||||
-- Entity class
|
||||
|
||||
local Entity = {__index={
|
||||
despawn=function(self) return entities.despawn(self.eid) end,
|
||||
get_skeleton=function(self) return entities.get_skeleton(self.eid) end,
|
||||
set_skeleton=function(self, s) return entities.set_skeleton(self.eid, s) end,
|
||||
get_component=function(self, name) return self.components[name] end,
|
||||
has_component=function(self, name) return self.components[name] ~= nil end,
|
||||
get_uid=function(self) return self.eid end,
|
||||
}}
|
||||
|
||||
local entities = {}
|
||||
|
||||
return {
|
||||
new_Entity = function(eid)
|
||||
local entity = setmetatable({eid=eid}, Entity)
|
||||
entity.transform = new_Transform(eid)
|
||||
entity.rigidbody = new_Rigidbody(eid)
|
||||
entity.skeleton = new_Skeleton(eid)
|
||||
entity.components = {}
|
||||
entities[eid] = entity;
|
||||
return entity
|
||||
end,
|
||||
get_Entity = function(eid)
|
||||
return entities[eid]
|
||||
end,
|
||||
remove_Entity = function(eid)
|
||||
local entity = entities[eid]
|
||||
if entity then
|
||||
entity.components = nil
|
||||
entities[eid] = nil;
|
||||
end
|
||||
end,
|
||||
update = function()
|
||||
for _,entity in pairs(entities) do
|
||||
for _, component in pairs(entity.components) do
|
||||
local callback = component.on_update
|
||||
if callback then
|
||||
local result, err = pcall(callback)
|
||||
if err then
|
||||
debug.error(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
render = function()
|
||||
for _,entity in pairs(entities) do
|
||||
for _, component in pairs(entity.components) do
|
||||
local callback = component.on_render
|
||||
if callback then
|
||||
local result, err = pcall(callback)
|
||||
if err then
|
||||
debug.error(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
get_all = function(uids)
|
||||
if uids == nil then
|
||||
return entities
|
||||
else
|
||||
local values = {}
|
||||
for _, uid in ipairs(uids) do
|
||||
values[uid] = entities[uid]
|
||||
end
|
||||
return values
|
||||
end
|
||||
end
|
||||
}
|
||||
@ -106,10 +106,14 @@ console.add_command(
|
||||
)
|
||||
|
||||
console.add_command(
|
||||
"tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z",
|
||||
"Teleport object",
|
||||
"tp entity:sel=$entity.id x:num~pos.x y:num~pos.y z:num~pos.z",
|
||||
"Teleport entity",
|
||||
function (args, kwargs)
|
||||
player.set_pos(unpack(args))
|
||||
local eid, x, y, z = unpack(args)
|
||||
local entity = entities.get(eid)
|
||||
if entity ~= nil then
|
||||
entity.transform:set_pos({x, y, z})
|
||||
end
|
||||
end
|
||||
)
|
||||
console.add_command(
|
||||
@ -142,3 +146,26 @@ console.add_command(
|
||||
return tostring(w*h*d).." blocks set"
|
||||
end
|
||||
)
|
||||
|
||||
console.add_command(
|
||||
"player.respawn player:sel=$obj.id",
|
||||
"Respawn player entity",
|
||||
function (args, kwargs)
|
||||
local eid = entities.spawn("base:player", {player.get_pos(args[1])}):get_uid()
|
||||
player.set_entity(args[1], eid)
|
||||
return "spawned new player entity #"..tostring(eid)
|
||||
end
|
||||
)
|
||||
|
||||
console.add_command(
|
||||
"entity.despawn entity:sel=$entity.selected",
|
||||
"Despawn entity",
|
||||
function (args, kwargs)
|
||||
local eid = args[1]
|
||||
local entity = entities.get(eid)
|
||||
if entity ~= nil then
|
||||
entity:despawn()
|
||||
return "despawned entity #"..tostring(eid)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
@ -252,9 +252,11 @@ function session.reset_entry(name)
|
||||
session.entries[name] = nil
|
||||
end
|
||||
|
||||
function timeit(func, ...)
|
||||
function timeit(iters, func, ...)
|
||||
local tm = time.uptime()
|
||||
func(...)
|
||||
for i=1,iters do
|
||||
func(...)
|
||||
end
|
||||
print("[time mcs]", (time.uptime()-tm) * 1000000)
|
||||
end
|
||||
|
||||
@ -303,6 +305,22 @@ function file.readlines(path)
|
||||
return lines
|
||||
end
|
||||
|
||||
stdcomp = require "core:internal/stdcomp"
|
||||
entities.get = stdcomp.get_Entity
|
||||
entities.get_all = function(uids)
|
||||
if uids == nil then
|
||||
local values = {}
|
||||
for k,v in pairs(stdcomp.get_all()) do
|
||||
values[k] = v
|
||||
end
|
||||
return values
|
||||
else
|
||||
return stdcomp.get_all(uids)
|
||||
end
|
||||
end
|
||||
|
||||
math.randomseed(time.uptime()*1536227939)
|
||||
|
||||
-- Deprecated functions
|
||||
block_index = block.index
|
||||
block_name = block.name
|
||||
|
||||
@ -29,5 +29,6 @@ player.attack=Attack / Break
|
||||
player.build=Place Block
|
||||
player.flight=Flight
|
||||
player.noclip=No-clip
|
||||
player.drop=Drop Item
|
||||
camera.zoom=Zoom
|
||||
camera.mode=Switch Camera Mode
|
||||
|
||||
@ -28,6 +28,7 @@ menu.Content Error=Ошибка Контента
|
||||
menu.Content=Контент
|
||||
menu.Continue=Продолжить
|
||||
menu.Controls=Управление
|
||||
menu.Display=Дисплей
|
||||
menu.Graphics=Графика
|
||||
menu.missing-content=Отсутствует Контент!
|
||||
menu.New World=Новый Мир
|
||||
@ -50,6 +51,7 @@ world.delete-confirm=Удалить мир безвозвратно?
|
||||
settings.Ambient=Фон
|
||||
settings.Backlight=Подсветка
|
||||
settings.Camera Shaking=Тряска Камеры
|
||||
settings.Camera Inertia=Инерция Камеры
|
||||
settings.Fog Curve=Кривая Тумана
|
||||
settings.FOV=Поле Зрения
|
||||
settings.Fullscreen=Полный экран
|
||||
@ -80,5 +82,6 @@ player.pick=Подобрать Блок
|
||||
player.attack=Атаковать / Сломать
|
||||
player.build=Поставить Блок
|
||||
player.flight=Полёт
|
||||
player.drop=Выбросить Предмет
|
||||
camera.zoom=Приближение
|
||||
camera.mode=Сменить Режим Камеры
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <typeindex>
|
||||
@ -16,6 +17,11 @@ class Assets;
|
||||
namespace assetload {
|
||||
/// @brief final work to do in the main thread
|
||||
using postfunc = std::function<void(Assets*)>;
|
||||
|
||||
using setupfunc = std::function<void(const Assets*)>;
|
||||
|
||||
template<class T>
|
||||
void assets_setup(const Assets*);
|
||||
}
|
||||
|
||||
class Assets {
|
||||
@ -23,6 +29,7 @@ class Assets {
|
||||
|
||||
using assets_map = std::unordered_map<std::string, std::shared_ptr<void>>;
|
||||
std::unordered_map<std::type_index, assets_map> assets;
|
||||
std::vector<assetload::setupfunc> setupFuncs;
|
||||
public:
|
||||
Assets() {}
|
||||
Assets(const Assets&) = delete;
|
||||
@ -49,6 +56,34 @@ public:
|
||||
}
|
||||
return static_cast<T*>(found->second.get());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::optional<const assets_map*> getMap() const {
|
||||
const auto& mapIter = assets.find(typeid(T));
|
||||
if (mapIter == assets.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return &mapIter->second;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
for (auto& setupFunc : setupFuncs) {
|
||||
setupFunc(this);
|
||||
}
|
||||
}
|
||||
|
||||
void addSetupFunc(assetload::setupfunc setupfunc) {
|
||||
setupFuncs.push_back(setupfunc);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void assetload::assets_setup(const Assets* assets) {
|
||||
if (auto mapPtr = assets->getMap<T>()) {
|
||||
for (const auto& entry : **mapPtr) {
|
||||
static_cast<T*>(entry.second.get())->setup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ASSETS_ASSETS_HPP_
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "../content/Content.hpp"
|
||||
#include "../content/ContentPack.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
#include "../objects/rigging.hpp"
|
||||
#include "../graphics/core/Texture.hpp"
|
||||
#include "../logic/scripting/scripting.hpp"
|
||||
|
||||
@ -24,13 +25,13 @@ static debug::Logger logger("assets-loader");
|
||||
AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths)
|
||||
: assets(assets), paths(paths)
|
||||
{
|
||||
addLoader(AssetType::shader, assetload::shader);
|
||||
addLoader(AssetType::texture, assetload::texture);
|
||||
addLoader(AssetType::font, assetload::font);
|
||||
addLoader(AssetType::atlas, assetload::atlas);
|
||||
addLoader(AssetType::layout, assetload::layout);
|
||||
addLoader(AssetType::sound, assetload::sound);
|
||||
addLoader(AssetType::model, assetload::model);
|
||||
addLoader(AssetType::SHADER, assetload::shader);
|
||||
addLoader(AssetType::TEXTURE, assetload::texture);
|
||||
addLoader(AssetType::FONT, assetload::font);
|
||||
addLoader(AssetType::ATLAS, assetload::atlas);
|
||||
addLoader(AssetType::LAYOUT, assetload::layout);
|
||||
addLoader(AssetType::SOUND, assetload::sound);
|
||||
addLoader(AssetType::MODEL, assetload::model);
|
||||
}
|
||||
|
||||
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
||||
@ -80,7 +81,7 @@ void addLayouts(const scriptenv& env, const std::string& prefix, const fs::path&
|
||||
if (file.extension().u8string() != ".xml")
|
||||
continue;
|
||||
std::string name = prefix+":"+file.stem().u8string();
|
||||
loader.add(AssetType::layout, file.u8string(), name, std::make_shared<LayoutCfg>(env));
|
||||
loader.add(AssetType::LAYOUT, file.u8string(), name, std::make_shared<LayoutCfg>(env));
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,18 +90,18 @@ void AssetsLoader::tryAddSound(const std::string& name) {
|
||||
return;
|
||||
}
|
||||
std::string file = SOUNDS_FOLDER+"/"+name;
|
||||
add(AssetType::sound, file, name);
|
||||
add(AssetType::SOUND, file, name);
|
||||
}
|
||||
|
||||
static std::string assets_def_folder(AssetType tag) {
|
||||
switch (tag) {
|
||||
case AssetType::font: return FONTS_FOLDER;
|
||||
case AssetType::shader: return SHADERS_FOLDER;
|
||||
case AssetType::texture: return TEXTURES_FOLDER;
|
||||
case AssetType::atlas: return TEXTURES_FOLDER;
|
||||
case AssetType::layout: return LAYOUTS_FOLDER;
|
||||
case AssetType::sound: return SOUNDS_FOLDER;
|
||||
case AssetType::model: return MODELS_FOLDER;
|
||||
case AssetType::FONT: return FONTS_FOLDER;
|
||||
case AssetType::SHADER: return SHADERS_FOLDER;
|
||||
case AssetType::TEXTURE: return TEXTURES_FOLDER;
|
||||
case AssetType::ATLAS: return TEXTURES_FOLDER;
|
||||
case AssetType::LAYOUT: return LAYOUTS_FOLDER;
|
||||
case AssetType::SOUND: return SOUNDS_FOLDER;
|
||||
case AssetType::MODEL: return MODELS_FOLDER;
|
||||
}
|
||||
return "<error>";
|
||||
}
|
||||
@ -118,7 +119,7 @@ void AssetsLoader::processPreload(
|
||||
}
|
||||
map->str("path", path);
|
||||
switch (tag) {
|
||||
case AssetType::sound:
|
||||
case AssetType::SOUND:
|
||||
add(tag, path, name, std::make_shared<SoundCfg>(
|
||||
map->get("keep-pcm", false)
|
||||
));
|
||||
@ -153,11 +154,11 @@ void AssetsLoader::processPreloadList(AssetType tag, dynamic::List* list) {
|
||||
|
||||
void AssetsLoader::processPreloadConfig(const fs::path& file) {
|
||||
auto root = files::read_json(file);
|
||||
processPreloadList(AssetType::font, root->list("fonts"));
|
||||
processPreloadList(AssetType::shader, root->list("shaders"));
|
||||
processPreloadList(AssetType::texture, root->list("textures"));
|
||||
processPreloadList(AssetType::sound, root->list("sounds"));
|
||||
processPreloadList(AssetType::model, root->list("models"));
|
||||
processPreloadList(AssetType::FONT, root->list("fonts").get());
|
||||
processPreloadList(AssetType::SHADER, root->list("shaders").get());
|
||||
processPreloadList(AssetType::TEXTURE, root->list("textures").get());
|
||||
processPreloadList(AssetType::SOUND, root->list("sounds").get());
|
||||
processPreloadList(AssetType::MODEL, root->list("models").get());
|
||||
// layouts are loaded automatically
|
||||
}
|
||||
|
||||
@ -176,18 +177,18 @@ void AssetsLoader::processPreloadConfigs(const Content* content) {
|
||||
}
|
||||
|
||||
void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
|
||||
loader.add(AssetType::font, FONTS_FOLDER+"/font", "normal");
|
||||
loader.add(AssetType::shader, SHADERS_FOLDER+"/ui", "ui");
|
||||
loader.add(AssetType::shader, SHADERS_FOLDER+"/main", "main");
|
||||
loader.add(AssetType::shader, SHADERS_FOLDER+"/lines", "lines");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/menubg", "gui/menubg");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/delete_icon", "gui/delete_icon");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_icon", "gui/no_icon");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_world_icon", "gui/no_world_icon");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/warning", "gui/warning");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/error", "gui/error");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/cross", "gui/cross");
|
||||
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/refresh", "gui/refresh");
|
||||
loader.add(AssetType::FONT, FONTS_FOLDER+"/font", "normal");
|
||||
loader.add(AssetType::SHADER, SHADERS_FOLDER+"/ui", "ui");
|
||||
loader.add(AssetType::SHADER, SHADERS_FOLDER+"/main", "main");
|
||||
loader.add(AssetType::SHADER, SHADERS_FOLDER+"/lines", "lines");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/menubg", "gui/menubg");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/delete_icon", "gui/delete_icon");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/no_icon", "gui/no_icon");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/no_world_icon", "gui/no_world_icon");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/warning", "gui/warning");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/error", "gui/error");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/cross", "gui/cross");
|
||||
loader.add(AssetType::TEXTURE, TEXTURES_FOLDER+"/gui/refresh", "gui/refresh");
|
||||
if (content) {
|
||||
loader.processPreloadConfigs(content);
|
||||
|
||||
@ -205,9 +206,19 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
|
||||
fs::path folder = info.folder / fs::path("layouts");
|
||||
addLayouts(pack->getEnvironment(), info.id, folder, loader);
|
||||
}
|
||||
|
||||
for (auto& entry : content->getSkeletons()) {
|
||||
auto& skeleton = *entry.second;
|
||||
for (auto& bone : skeleton.getBones()) {
|
||||
auto& model = bone->model.name;
|
||||
if (!model.empty()) {
|
||||
loader.add(AssetType::MODEL, MODELS_FOLDER+"/"+model, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loader.add(AssetType::atlas, TEXTURES_FOLDER+"/blocks", "blocks");
|
||||
loader.add(AssetType::atlas, TEXTURES_FOLDER+"/items", "items");
|
||||
loader.add(AssetType::ATLAS, TEXTURES_FOLDER+"/blocks", "blocks");
|
||||
loader.add(AssetType::ATLAS, TEXTURES_FOLDER+"/items", "items");
|
||||
}
|
||||
|
||||
bool AssetsLoader::loadExternalTexture(
|
||||
|
||||
@ -20,13 +20,13 @@ namespace dynamic {
|
||||
}
|
||||
|
||||
enum class AssetType {
|
||||
texture,
|
||||
shader,
|
||||
font,
|
||||
atlas,
|
||||
layout,
|
||||
sound,
|
||||
model,
|
||||
TEXTURE,
|
||||
SHADER,
|
||||
FONT,
|
||||
ATLAS,
|
||||
LAYOUT,
|
||||
SOUND,
|
||||
MODEL
|
||||
};
|
||||
|
||||
class ResPaths;
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include "../graphics/core/Font.hpp"
|
||||
#include "../graphics/core/Model.hpp"
|
||||
#include "../graphics/core/TextureAnimation.hpp"
|
||||
#include "../objects/rigging.hpp"
|
||||
#include "../frontend/UiDocument.hpp"
|
||||
#include "../constants.hpp"
|
||||
|
||||
@ -214,8 +215,10 @@ assetload::postfunc assetload::model(
|
||||
auto model = obj::parse(path.u8string(), text).release();
|
||||
return [=](Assets* assets) {
|
||||
for (auto& mesh : model->meshes) {
|
||||
auto filename = TEXTURES_FOLDER+"/"+mesh.texture;
|
||||
loader->add(AssetType::texture, filename, mesh.texture, nullptr);
|
||||
if (mesh.texture.find('$') == std::string::npos) {
|
||||
auto filename = TEXTURES_FOLDER+"/"+mesh.texture;
|
||||
loader->add(AssetType::TEXTURE, filename, mesh.texture, nullptr);
|
||||
}
|
||||
}
|
||||
assets->store(std::unique_ptr<model::Model>(model), name);
|
||||
};
|
||||
|
||||
@ -18,49 +18,49 @@ namespace assetload {
|
||||
AssetsLoader*,
|
||||
const ResPaths* paths,
|
||||
const std::string& filename,
|
||||
const std::string &name,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc shader(
|
||||
AssetsLoader*,
|
||||
const ResPaths* paths,
|
||||
const std::string& filename,
|
||||
const std::string &name,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc atlas(
|
||||
AssetsLoader*,
|
||||
const ResPaths* paths,
|
||||
const std::string &directory,
|
||||
const std::string &name,
|
||||
const std::string& directory,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc font(
|
||||
AssetsLoader*,
|
||||
const ResPaths* paths,
|
||||
const std::string& filename,
|
||||
const std::string &name,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc layout(
|
||||
AssetsLoader*,
|
||||
const ResPaths* paths,
|
||||
const std::string& file,
|
||||
const std::string &name,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc sound(
|
||||
AssetsLoader*,
|
||||
const ResPaths* paths,
|
||||
const std::string& file,
|
||||
const std::string &name,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc model(
|
||||
AssetsLoader*,
|
||||
const ResPaths* paths,
|
||||
const std::string& file,
|
||||
const std::string &name,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
}
|
||||
|
||||
@ -83,6 +83,13 @@ std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
std::vector<ubyte> json::to_binary(const Value& value, bool compress) {
|
||||
if (auto map = std::get_if<Map_sptr>(&value)) {
|
||||
return to_binary(map->get(), compress);
|
||||
}
|
||||
throw std::runtime_error("map is only supported as the root element");
|
||||
}
|
||||
|
||||
static Value value_from_binary(ByteReader& reader) {
|
||||
ubyte typecode = reader.get();
|
||||
switch (typecode) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#ifndef CODERS_BINARY_JSON_HPP_
|
||||
#define CODERS_BINARY_JSON_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
#include "../data/dynamic_fwd.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@ -26,8 +26,9 @@ namespace json {
|
||||
inline constexpr int BJSON_TYPE_NULL = 0xC;
|
||||
inline constexpr int BJSON_TYPE_CDOCUMENT = 0x1F;
|
||||
|
||||
extern std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false);
|
||||
extern std::shared_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size);
|
||||
std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false);
|
||||
std::vector<ubyte> to_binary(const dynamic::Value& obj, bool compress=false);
|
||||
std::shared_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size);
|
||||
}
|
||||
|
||||
#endif // CODERS_BINARY_JSON_HPP_
|
||||
|
||||
@ -97,6 +97,10 @@ void stringifyObj(
|
||||
const std::string& indentstr,
|
||||
bool nice
|
||||
) {
|
||||
if (obj == nullptr) {
|
||||
ss << "nullptr";
|
||||
return;
|
||||
}
|
||||
if (obj->values.empty()) {
|
||||
ss << "{}";
|
||||
return;
|
||||
@ -238,11 +242,11 @@ Value Parser::parseValue() {
|
||||
throw error("unexpected character '"+std::string({next})+"'");
|
||||
}
|
||||
|
||||
dynamic::Map_sptr json::parse(const std::string& filename, const std::string& source) {
|
||||
dynamic::Map_sptr json::parse(std::string_view filename, std::string_view source) {
|
||||
Parser parser(filename, source);
|
||||
return parser.parse();
|
||||
}
|
||||
|
||||
dynamic::Map_sptr json::parse(const std::string& source) {
|
||||
dynamic::Map_sptr json::parse(std::string_view source) {
|
||||
return parse("<string>", source);
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
#include <string>
|
||||
|
||||
namespace json {
|
||||
dynamic::Map_sptr parse(const std::string& filename, const std::string& source);
|
||||
dynamic::Map_sptr parse(const std::string& source);
|
||||
dynamic::Map_sptr parse(std::string_view filename, std::string_view source);
|
||||
dynamic::Map_sptr parse(std::string_view source);
|
||||
|
||||
std::string stringify(
|
||||
const dynamic::Map* obj,
|
||||
|
||||
@ -19,6 +19,7 @@ inline const std::string ENGINE_VERSION_STRING = "0.22";
|
||||
|
||||
inline constexpr blockid_t BLOCK_AIR = 0;
|
||||
inline constexpr itemid_t ITEM_EMPTY = 0;
|
||||
inline constexpr entityid_t ENTITY_NONE = 0;
|
||||
|
||||
inline constexpr int CHUNK_W = 16;
|
||||
inline constexpr int CHUNK_H = 256;
|
||||
@ -50,5 +51,6 @@ inline const std::string FONTS_FOLDER = "fonts";
|
||||
inline const std::string LAYOUTS_FOLDER = "layouts";
|
||||
inline const std::string SOUNDS_FOLDER = "sounds";
|
||||
inline const std::string MODELS_FOLDER = "models";
|
||||
inline const std::string SKELETONS_FOLDER = "skeletons";
|
||||
|
||||
#endif // CONSTANTS_HPP_
|
||||
|
||||
@ -7,67 +7,56 @@
|
||||
|
||||
#include "../voxels/Block.hpp"
|
||||
#include "../items/ItemDef.hpp"
|
||||
#include "../objects/EntityDef.hpp"
|
||||
#include "../objects/rigging.hpp"
|
||||
|
||||
#include "ContentPack.hpp"
|
||||
#include "../logic/scripting/scripting.hpp"
|
||||
|
||||
ContentIndices::ContentIndices(
|
||||
std::vector<Block*> blockDefs,
|
||||
std::vector<ItemDef*> itemDefs
|
||||
) : blockDefs(std::move(blockDefs)),
|
||||
itemDefs(std::move(itemDefs))
|
||||
ContentUnitIndices<Block> blocks,
|
||||
ContentUnitIndices<ItemDef> items,
|
||||
ContentUnitIndices<EntityDef> entities
|
||||
) : blocks(std::move(blocks)),
|
||||
items(std::move(items)),
|
||||
entities(std::move(entities))
|
||||
{}
|
||||
|
||||
Content::Content(
|
||||
std::unique_ptr<ContentIndices> indices,
|
||||
std::unique_ptr<DrawGroups> drawGroups,
|
||||
std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs,
|
||||
std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs,
|
||||
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs,
|
||||
std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials
|
||||
) : blockDefs(std::move(blockDefs)),
|
||||
itemDefs(std::move(itemDefs)),
|
||||
indices(std::move(indices)),
|
||||
ContentUnitDefs<Block> blocks,
|
||||
ContentUnitDefs<ItemDef> items,
|
||||
ContentUnitDefs<EntityDef> entities,
|
||||
UptrsMap<std::string, ContentPackRuntime> packs,
|
||||
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
||||
ResourceIndicesSet resourceIndices
|
||||
) : indices(std::move(indices)),
|
||||
packs(std::move(packs)),
|
||||
blockMaterials(std::move(blockMaterials)),
|
||||
drawGroups(std::move(drawGroups))
|
||||
{}
|
||||
skeletons(std::move(skeletons)),
|
||||
blocks(std::move(blocks)),
|
||||
items(std::move(items)),
|
||||
entities(std::move(entities)),
|
||||
drawGroups(std::move(drawGroups))
|
||||
{
|
||||
for (size_t i = 0; i < RESOURCE_TYPES_COUNT; i++) {
|
||||
this->resourceIndices[i] = std::move(resourceIndices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Content::~Content() {
|
||||
}
|
||||
|
||||
Block* Content::findBlock(const std::string& id) const {
|
||||
auto found = blockDefs.find(id);
|
||||
if (found == blockDefs.end()) {
|
||||
const rigging::SkeletonConfig* Content::getSkeleton(const std::string& id) const {
|
||||
auto found = skeletons.find(id);
|
||||
if (found == skeletons.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
Block& Content::requireBlock(const std::string& id) const {
|
||||
auto found = blockDefs.find(id);
|
||||
if (found == blockDefs.end()) {
|
||||
throw std::runtime_error("missing block "+id);
|
||||
}
|
||||
return *found->second;
|
||||
}
|
||||
|
||||
ItemDef* Content::findItem(const std::string& id) const {
|
||||
auto found = itemDefs.find(id);
|
||||
if (found == itemDefs.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
ItemDef& Content::requireItem(const std::string& id) const {
|
||||
auto found = itemDefs.find(id);
|
||||
if (found == itemDefs.end()) {
|
||||
throw std::runtime_error("missing item "+id);
|
||||
}
|
||||
return *found->second;
|
||||
}
|
||||
|
||||
const BlockMaterial* Content::findBlockMaterial(const std::string& id) const {
|
||||
auto found = blockMaterials.find(id);
|
||||
if (found == blockMaterials.end()) {
|
||||
@ -84,10 +73,14 @@ const ContentPackRuntime* Content::getPackRuntime(const std::string& id) const {
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, std::unique_ptr<BlockMaterial>>& Content::getBlockMaterials() const {
|
||||
const UptrsMap<std::string, BlockMaterial>& Content::getBlockMaterials() const {
|
||||
return blockMaterials;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& Content::getPacks() const {
|
||||
const UptrsMap<std::string, ContentPackRuntime>& Content::getPacks() const {
|
||||
return packs;
|
||||
}
|
||||
|
||||
const UptrsMap<std::string, rigging::SkeletonConfig>& Content::getSkeletons() const {
|
||||
return skeletons;
|
||||
}
|
||||
|
||||
@ -1,32 +1,37 @@
|
||||
#ifndef CONTENT_CONTENT_HPP_
|
||||
#define CONTENT_CONTENT_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
#include "content_fwd.hpp"
|
||||
|
||||
#include "../data/dynamic_fwd.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
using DrawGroups = std::set<ubyte>;
|
||||
template<class K, class V>
|
||||
using UptrsMap = std::unordered_map<K, std::unique_ptr<V>>;
|
||||
|
||||
class Block;
|
||||
struct BlockMaterial;
|
||||
class ItemDef;
|
||||
class Content;
|
||||
class ContentPackRuntime;
|
||||
struct ItemDef;
|
||||
struct EntityDef;
|
||||
|
||||
enum class contenttype {
|
||||
none, block, item
|
||||
};
|
||||
namespace rigging {
|
||||
class SkeletonConfig;
|
||||
}
|
||||
|
||||
inline const char* contenttype_name(contenttype type) {
|
||||
constexpr const char* contenttype_name(contenttype type) {
|
||||
switch (type) {
|
||||
case contenttype::none: return "none";
|
||||
case contenttype::block: return "block";
|
||||
case contenttype::item: return "item";
|
||||
case contenttype::entity: return "entity";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
@ -43,82 +48,165 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Runtime defs cache: indices
|
||||
class ContentIndices {
|
||||
std::vector<Block*> blockDefs;
|
||||
std::vector<ItemDef*> itemDefs;
|
||||
template<class T>
|
||||
class ContentUnitIndices {
|
||||
std::vector<T*> defs;
|
||||
public:
|
||||
ContentIndices(
|
||||
std::vector<Block*> blockDefs,
|
||||
std::vector<ItemDef*> itemDefs
|
||||
);
|
||||
ContentUnitIndices(std::vector<T*> defs) : defs(std::move(defs)) {}
|
||||
|
||||
inline Block* getBlockDef(blockid_t id) const {
|
||||
if (id >= blockDefs.size())
|
||||
inline T* get(blockid_t id) const {
|
||||
if (id >= defs.size()) {
|
||||
return nullptr;
|
||||
return blockDefs[id];
|
||||
}
|
||||
return defs[id];
|
||||
}
|
||||
|
||||
inline ItemDef* getItemDef(itemid_t id) const {
|
||||
if (id >= itemDefs.size())
|
||||
return nullptr;
|
||||
return itemDefs[id];
|
||||
inline size_t count() const {
|
||||
return defs.size();
|
||||
}
|
||||
|
||||
inline size_t countBlockDefs() const {
|
||||
return blockDefs.size();
|
||||
}
|
||||
|
||||
inline size_t countItemDefs() const {
|
||||
return itemDefs.size();
|
||||
}
|
||||
|
||||
// use this for critical spots to prevent range check overhead
|
||||
const Block* const* getBlockDefs() const {
|
||||
return blockDefs.data();
|
||||
}
|
||||
|
||||
const ItemDef* const* getItemDefs() const {
|
||||
return itemDefs.data();
|
||||
inline const T* const* getDefs() const {
|
||||
return defs.data();
|
||||
}
|
||||
};
|
||||
|
||||
/* Content is a definitions repository */
|
||||
class Content {
|
||||
std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs;
|
||||
std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs;
|
||||
std::unique_ptr<ContentIndices> indices;
|
||||
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
|
||||
std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials;
|
||||
/// @brief Runtime defs cache: indices
|
||||
class ContentIndices {
|
||||
public:
|
||||
ContentUnitIndices<Block> blocks;
|
||||
ContentUnitIndices<ItemDef> items;
|
||||
ContentUnitIndices<EntityDef> entities;
|
||||
|
||||
ContentIndices(
|
||||
ContentUnitIndices<Block> blocks,
|
||||
ContentUnitIndices<ItemDef> items,
|
||||
ContentUnitIndices<EntityDef> entities
|
||||
);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class ContentUnitDefs {
|
||||
UptrsMap<std::string, T> defs;
|
||||
public:
|
||||
ContentUnitDefs(UptrsMap<std::string, T> defs)
|
||||
: defs(std::move(defs)) {
|
||||
}
|
||||
|
||||
T* find(const std::string& id) const {
|
||||
const auto& found = defs.find(id);
|
||||
if (found == defs.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second.get();
|
||||
}
|
||||
T& require(const std::string& id) const {
|
||||
const auto& found = defs.find(id);
|
||||
if (found == defs.end()) {
|
||||
throw std::runtime_error("missing content unit "+id);
|
||||
}
|
||||
return *found->second;
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceIndices {
|
||||
std::vector<std::string> names;
|
||||
std::unordered_map<std::string, size_t> indices;
|
||||
std::unique_ptr<std::vector<dynamic::Map_sptr>> savedData;
|
||||
public:
|
||||
ResourceIndices()
|
||||
: savedData(std::make_unique<std::vector<dynamic::Map_sptr>>()){
|
||||
}
|
||||
|
||||
static constexpr size_t MISSING = SIZE_MAX;
|
||||
|
||||
void add(std::string name, dynamic::Map_sptr map) {
|
||||
indices[name] = names.size();
|
||||
names.push_back(name);
|
||||
savedData->push_back(map);
|
||||
}
|
||||
|
||||
const std::string& getName(size_t index) const {
|
||||
return names.at(index);
|
||||
}
|
||||
|
||||
size_t indexOf(const std::string& name) const {
|
||||
const auto& found = indices.find(name);
|
||||
if (found != indices.end()) {
|
||||
return found->second;
|
||||
}
|
||||
return MISSING;
|
||||
}
|
||||
|
||||
dynamic::Map_sptr getSavedData(size_t index) const {
|
||||
return savedData->at(index);
|
||||
}
|
||||
|
||||
void saveData(size_t index, dynamic::Map_sptr map) const {
|
||||
savedData->at(index) = map;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return names.size();
|
||||
}
|
||||
};
|
||||
|
||||
constexpr const char* to_string(ResourceType type) {
|
||||
switch (type) {
|
||||
case ResourceType::CAMERA: return "camera";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
inline std::optional<ResourceType> ResourceType_from(std::string_view str) {
|
||||
if (str == "camera") {
|
||||
return ResourceType::CAMERA;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
using ResourceIndicesSet = ResourceIndices[RESOURCE_TYPES_COUNT];
|
||||
|
||||
/// @brief Content is a definitions repository
|
||||
class Content {
|
||||
std::unique_ptr<ContentIndices> indices;
|
||||
UptrsMap<std::string, ContentPackRuntime> packs;
|
||||
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
||||
public:
|
||||
ContentUnitDefs<Block> blocks;
|
||||
ContentUnitDefs<ItemDef> items;
|
||||
ContentUnitDefs<EntityDef> entities;
|
||||
std::unique_ptr<DrawGroups> const drawGroups;
|
||||
ResourceIndicesSet resourceIndices {};
|
||||
|
||||
Content(
|
||||
std::unique_ptr<ContentIndices> indices,
|
||||
std::unique_ptr<DrawGroups> drawGroups,
|
||||
std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs,
|
||||
std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs,
|
||||
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs,
|
||||
std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials
|
||||
ContentUnitDefs<Block> blocks,
|
||||
ContentUnitDefs<ItemDef> items,
|
||||
ContentUnitDefs<EntityDef> entities,
|
||||
UptrsMap<std::string, ContentPackRuntime> packs,
|
||||
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
||||
ResourceIndicesSet resourceIndices
|
||||
);
|
||||
~Content();
|
||||
|
||||
inline ContentIndices* getIndices() const {
|
||||
return indices.get();
|
||||
}
|
||||
|
||||
Block* findBlock(const std::string& id) const;
|
||||
Block& requireBlock(const std::string& id) const;
|
||||
|
||||
ItemDef* findItem(const std::string& id) const;
|
||||
ItemDef& requireItem(const std::string& id) const;
|
||||
inline const ResourceIndices& getIndices(ResourceType type) const {
|
||||
return resourceIndices[static_cast<size_t>(type)];
|
||||
}
|
||||
|
||||
const rigging::SkeletonConfig* getSkeleton(const std::string& id) const;
|
||||
const BlockMaterial* findBlockMaterial(const std::string& id) const;
|
||||
|
||||
const ContentPackRuntime* getPackRuntime(const std::string& id) const;
|
||||
|
||||
const std::unordered_map<std::string, std::unique_ptr<BlockMaterial>>& getBlockMaterials() const;
|
||||
const std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>>& getPacks() const;
|
||||
const UptrsMap<std::string, BlockMaterial>& getBlockMaterials() const;
|
||||
const UptrsMap<std::string, ContentPackRuntime>& getPacks() const;
|
||||
const UptrsMap<std::string, rigging::SkeletonConfig>& getSkeletons() const;
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_HPP_
|
||||
|
||||
@ -1,31 +1,15 @@
|
||||
#include "ContentBuilder.hpp"
|
||||
|
||||
#include "../objects/rigging.hpp"
|
||||
|
||||
ContentBuilder::~ContentBuilder() {}
|
||||
|
||||
void ContentBuilder::add(std::unique_ptr<ContentPackRuntime> pack) {
|
||||
packs[pack->getId()] = std::move(pack);
|
||||
}
|
||||
|
||||
Block& ContentBuilder::createBlock(const std::string& id) {
|
||||
auto found = blockDefs.find(id);
|
||||
if (found != blockDefs.end()) {
|
||||
return *found->second;
|
||||
}
|
||||
checkIdentifier(id);
|
||||
blockIds.push_back(id);
|
||||
blockDefs[id] = std::make_unique<Block>(id);
|
||||
return *blockDefs[id];
|
||||
}
|
||||
|
||||
ItemDef& ContentBuilder::createItem(const std::string& id) {
|
||||
auto found = itemDefs.find(id);
|
||||
if (found != itemDefs.end()) {
|
||||
return *found->second;
|
||||
}
|
||||
checkIdentifier(id);
|
||||
itemIds.push_back(id);
|
||||
itemDefs[id] = std::make_unique<ItemDef>(id);
|
||||
return *itemDefs[id];
|
||||
void ContentBuilder::add(std::unique_ptr<rigging::SkeletonConfig> skeleton) {
|
||||
skeletons[skeleton->getName()] = std::move(skeleton);
|
||||
}
|
||||
|
||||
BlockMaterial& ContentBuilder::createBlockMaterial(const std::string& id) {
|
||||
@ -35,28 +19,11 @@ BlockMaterial& ContentBuilder::createBlockMaterial(const std::string& id) {
|
||||
return material;
|
||||
}
|
||||
|
||||
void ContentBuilder::checkIdentifier(const std::string& id) {
|
||||
contenttype result;
|
||||
if (((result = checkContentType(id)) != contenttype::none)) {
|
||||
throw namereuse_error("name "+id+" is already used", result);
|
||||
}
|
||||
}
|
||||
|
||||
contenttype ContentBuilder::checkContentType(const std::string& id) {
|
||||
if (blockDefs.find(id) != blockDefs.end()) {
|
||||
return contenttype::block;
|
||||
}
|
||||
if (itemDefs.find(id) != itemDefs.end()) {
|
||||
return contenttype::item;
|
||||
}
|
||||
return contenttype::none;
|
||||
}
|
||||
|
||||
std::unique_ptr<Content> ContentBuilder::build() {
|
||||
std::vector<Block*> blockDefsIndices;
|
||||
auto groups = std::make_unique<DrawGroups>();
|
||||
for (const std::string& name : blockIds) {
|
||||
Block& def = *blockDefs[name];
|
||||
for (const std::string& name : blocks.names) {
|
||||
Block& def = *blocks.defs[name];
|
||||
|
||||
// Generating runtime info
|
||||
def.rt.id = blockDefsIndices.size();
|
||||
@ -79,8 +46,8 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
||||
}
|
||||
|
||||
std::vector<ItemDef*> itemDefsIndices;
|
||||
for (const std::string& name : itemIds) {
|
||||
ItemDef& def = *itemDefs[name];
|
||||
for (const std::string& name : items.names) {
|
||||
ItemDef& def = *items.defs[name];
|
||||
|
||||
// Generating runtime info
|
||||
def.rt.id = itemDefsIndices.size();
|
||||
@ -88,22 +55,37 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
||||
itemDefsIndices.push_back(&def);
|
||||
}
|
||||
|
||||
std::vector<EntityDef*> entityDefsIndices;
|
||||
for (const std::string& name : entities.names) {
|
||||
EntityDef& def = *entities.defs[name];
|
||||
|
||||
// Generating runtime info
|
||||
def.rt.id = entityDefsIndices.size();
|
||||
entityDefsIndices.push_back(&def);
|
||||
}
|
||||
|
||||
auto content = std::make_unique<Content>(
|
||||
std::make_unique<ContentIndices>(blockDefsIndices, itemDefsIndices),
|
||||
std::move(groups),
|
||||
std::move(blockDefs),
|
||||
std::move(itemDefs),
|
||||
std::move(packs),
|
||||
std::move(blockMaterials)
|
||||
std::make_unique<ContentIndices>(
|
||||
blockDefsIndices,
|
||||
itemDefsIndices,
|
||||
entityDefsIndices),
|
||||
std::move(groups),
|
||||
blocks.build(),
|
||||
items.build(),
|
||||
entities.build(),
|
||||
std::move(packs),
|
||||
std::move(blockMaterials),
|
||||
std::move(skeletons),
|
||||
std::move(resourceIndices)
|
||||
);
|
||||
|
||||
// Now, it's time to resolve foreign keys
|
||||
for (Block* def : blockDefsIndices) {
|
||||
def->rt.pickingItem = content->requireItem(def->pickingItem).rt.id;
|
||||
def->rt.pickingItem = content->items.require(def->pickingItem).rt.id;
|
||||
}
|
||||
|
||||
for (ItemDef* def : itemDefsIndices) {
|
||||
def->rt.placingBlock = content->requireBlock(def->placingBlock).rt.id;
|
||||
def->rt.placingBlock = content->blocks.require(def->placingBlock).rt.id;
|
||||
}
|
||||
|
||||
return content;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include "../items/ItemDef.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
#include "../objects/EntityDef.hpp"
|
||||
#include "../content/Content.hpp"
|
||||
#include "../content/ContentPack.hpp"
|
||||
|
||||
@ -10,27 +11,61 @@
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
class ContentBuilder {
|
||||
std::unordered_map<std::string, std::unique_ptr<Block>> blockDefs;
|
||||
std::vector<std::string> blockIds;
|
||||
template<class T>
|
||||
class ContentUnitBuilder {
|
||||
std::unordered_map<std::string, contenttype>& allNames;
|
||||
contenttype type;
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<ItemDef>> itemDefs;
|
||||
std::vector<std::string> itemIds;
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<BlockMaterial>> blockMaterials;
|
||||
std::unordered_map<std::string, std::unique_ptr<ContentPackRuntime>> packs;
|
||||
void checkIdentifier(const std::string& id) {
|
||||
const auto& found = allNames.find(id);
|
||||
if (found != allNames.end()) {
|
||||
throw namereuse_error("name "+id+" is already used", found->second);
|
||||
}
|
||||
}
|
||||
public:
|
||||
UptrsMap<std::string, T> defs;
|
||||
std::vector<std::string> names;
|
||||
|
||||
ContentUnitBuilder(
|
||||
std::unordered_map<std::string, contenttype>& allNames,
|
||||
contenttype type
|
||||
) : allNames(allNames), type(type) {}
|
||||
|
||||
T& create(const std::string& id) {
|
||||
auto found = defs.find(id);
|
||||
if (found != defs.end()) {
|
||||
return *found->second;
|
||||
}
|
||||
checkIdentifier(id);
|
||||
allNames[id] = type;
|
||||
names.push_back(id);
|
||||
defs[id] = std::make_unique<T>(id);
|
||||
return *defs[id];
|
||||
}
|
||||
|
||||
auto build() {
|
||||
return std::move(defs);
|
||||
}
|
||||
};
|
||||
|
||||
class ContentBuilder {
|
||||
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
||||
UptrsMap<std::string, ContentPackRuntime> packs;
|
||||
std::unordered_map<std::string, contenttype> allNames;
|
||||
public:
|
||||
ContentUnitBuilder<Block> blocks {allNames, contenttype::block};
|
||||
ContentUnitBuilder<ItemDef> items {allNames, contenttype::item};
|
||||
ContentUnitBuilder<EntityDef> entities {allNames, contenttype::entity};
|
||||
ResourceIndicesSet resourceIndices {};
|
||||
|
||||
~ContentBuilder();
|
||||
|
||||
void add(std::unique_ptr<ContentPackRuntime> pack);
|
||||
void add(std::unique_ptr<rigging::SkeletonConfig> skeleton);
|
||||
|
||||
Block& createBlock(const std::string& id);
|
||||
ItemDef& createItem(const std::string& id);
|
||||
BlockMaterial& createBlockMaterial(const std::string& id);
|
||||
|
||||
void checkIdentifier(const std::string& id);
|
||||
contenttype checkContentType(const std::string& id);
|
||||
|
||||
std::unique_ptr<Content> build();
|
||||
};
|
||||
|
||||
|
||||
@ -6,31 +6,16 @@
|
||||
#include "../coders/json.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
#include "../items/ItemDef.hpp"
|
||||
#include "../data/dynamic.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
ContentLUT::ContentLUT(const Content* content, size_t blocksCount, size_t itemsCount) {
|
||||
auto* indices = content->getIndices();
|
||||
for (size_t i = 0; i < blocksCount; i++) {
|
||||
blocks.push_back(i);
|
||||
}
|
||||
for (size_t i = 0; i < indices->countBlockDefs(); i++) {
|
||||
blockNames.push_back(indices->getBlockDef(i)->name);
|
||||
}
|
||||
for (size_t i = indices->countBlockDefs(); i < blocksCount; i++) {
|
||||
blockNames.emplace_back("");
|
||||
}
|
||||
ContentLUT::ContentLUT(const ContentIndices* indices, size_t blocksCount, size_t itemsCount)
|
||||
: blocks(blocksCount, indices->blocks, BLOCK_VOID, contenttype::block),
|
||||
items(itemsCount, indices->items, ITEM_VOID, contenttype::item)
|
||||
{}
|
||||
|
||||
for (size_t i = 0; i < itemsCount; i++) {
|
||||
items.push_back(i);
|
||||
}
|
||||
for (size_t i = 0; i < indices->countItemDefs(); i++) {
|
||||
itemNames.push_back(indices->getItemDef(i)->name);
|
||||
}
|
||||
for (size_t i = indices->countItemDefs(); i < itemsCount; i++) {
|
||||
itemNames.emplace_back();
|
||||
}
|
||||
template<class T> static constexpr size_t get_entries_count(
|
||||
const ContentUnitIndices<T>& indices, const dynamic::List_sptr& list) {
|
||||
return list ? std::max(list->size(), indices.count()) : indices.count();
|
||||
}
|
||||
|
||||
std::shared_ptr<ContentLUT> ContentLUT::create(
|
||||
@ -42,38 +27,13 @@ std::shared_ptr<ContentLUT> ContentLUT::create(
|
||||
auto itemlist = root->list("items");
|
||||
|
||||
auto* indices = content->getIndices();
|
||||
size_t blocks_c = blocklist
|
||||
? std::max(blocklist->size(), indices->countBlockDefs())
|
||||
: indices->countBlockDefs();
|
||||
size_t items_c = itemlist
|
||||
? std::max(itemlist->size(), indices->countItemDefs())
|
||||
: indices->countItemDefs();
|
||||
size_t blocks_c = get_entries_count(indices->blocks, blocklist);
|
||||
size_t items_c = get_entries_count(indices->items, itemlist);
|
||||
|
||||
auto lut = std::make_shared<ContentLUT>(content, blocks_c, items_c);
|
||||
auto lut = std::make_shared<ContentLUT>(indices, blocks_c, items_c);
|
||||
|
||||
if (blocklist) {
|
||||
for (size_t i = 0; i < blocklist->size(); i++) {
|
||||
std::string name = blocklist->str(i);
|
||||
Block* def = content->findBlock(name);
|
||||
if (def) {
|
||||
lut->setBlock(i, name, def->rt.id);
|
||||
} else {
|
||||
lut->setBlock(i, name, BLOCK_VOID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemlist) {
|
||||
for (size_t i = 0; i < itemlist->size(); i++) {
|
||||
std::string name = itemlist->str(i);
|
||||
ItemDef* def = content->findItem(name);
|
||||
if (def) {
|
||||
lut->setItem(i, name, def->rt.id);
|
||||
} else {
|
||||
lut->setItem(i, name, ITEM_VOID);
|
||||
}
|
||||
}
|
||||
}
|
||||
lut->blocks.setup(blocklist.get(), content->blocks);
|
||||
lut->items.setup(itemlist.get(), content->items);
|
||||
|
||||
if (lut->hasContentReorder() || lut->hasMissingContent()) {
|
||||
return lut;
|
||||
@ -84,17 +44,7 @@ std::shared_ptr<ContentLUT> ContentLUT::create(
|
||||
|
||||
std::vector<contententry> ContentLUT::getMissingContent() const {
|
||||
std::vector<contententry> entries;
|
||||
for (size_t i = 0; i < blocks.size(); i++) {
|
||||
if (blocks[i] == BLOCK_VOID) {
|
||||
auto& name = blockNames[i];
|
||||
entries.push_back(contententry {contenttype::block, name});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < items.size(); i++) {
|
||||
if (items[i] == ITEM_VOID) {
|
||||
auto& name = itemNames[i];
|
||||
entries.push_back(contententry {contenttype::item, name});
|
||||
}
|
||||
}
|
||||
blocks.getMissingContent(entries);
|
||||
items.getMissingContent(entries);
|
||||
return entries;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
#include "../constants.hpp"
|
||||
#include "../data/dynamic.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@ -18,58 +19,82 @@ struct contententry {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// TODO: make it unified for all types of content
|
||||
template<typename T, class U>
|
||||
class ContentUnitLUT {
|
||||
std::vector<T> indices;
|
||||
std::vector<std::string> names;
|
||||
bool missingContent = false;
|
||||
bool reorderContent = false;
|
||||
T missingValue;
|
||||
contenttype type;
|
||||
public:
|
||||
ContentUnitLUT(size_t count, const ContentUnitIndices<U>& unitIndices, T missingValue, contenttype type)
|
||||
: missingValue(missingValue), type(type) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
indices.push_back(i);
|
||||
}
|
||||
for (size_t i = 0; i < unitIndices.count(); i++) {
|
||||
names.push_back(unitIndices.get(i)->name);
|
||||
}
|
||||
for (size_t i = unitIndices.count(); i < count; i++) {
|
||||
names.emplace_back("");
|
||||
}
|
||||
}
|
||||
void setup(dynamic::List* list, const ContentUnitDefs<U>& defs) {
|
||||
if (list) {
|
||||
for (size_t i = 0; i < list->size(); i++) {
|
||||
std::string name = list->str(i);
|
||||
if (auto def = defs.find(name)) {
|
||||
set(i, name, def->rt.id);
|
||||
} else {
|
||||
set(i, name, missingValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void getMissingContent(std::vector<contententry>& entries) const {
|
||||
for (size_t i = 0; i < count(); i++) {
|
||||
if (indices[i] == missingValue) {
|
||||
auto& name = names[i];
|
||||
entries.push_back(contententry {type, name});
|
||||
}
|
||||
}
|
||||
}
|
||||
inline const std::string& getName(T index) const {
|
||||
return names[index];
|
||||
}
|
||||
inline T getId(T index) const {
|
||||
return indices[index];
|
||||
}
|
||||
inline void set(T index, std::string name, T id) {
|
||||
indices[index] = id;
|
||||
names[index] = std::move(name);
|
||||
if (id == missingValue) {
|
||||
missingContent = true;
|
||||
} else if (index != id) {
|
||||
reorderContent = true;
|
||||
}
|
||||
}
|
||||
inline size_t count() const {
|
||||
return indices.size();
|
||||
}
|
||||
inline bool hasContentReorder() const {
|
||||
return reorderContent;
|
||||
}
|
||||
inline bool hasMissingContent() const {
|
||||
return missingContent;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Content indices lookup table or report
|
||||
/// used to convert world with different indices
|
||||
/// Building with indices.json
|
||||
class ContentLUT {
|
||||
std::vector<blockid_t> blocks;
|
||||
std::vector<std::string> blockNames;
|
||||
|
||||
std::vector<itemid_t> items;
|
||||
std::vector<std::string> itemNames;
|
||||
|
||||
bool reorderContent = false;
|
||||
bool missingContent = false;
|
||||
public:
|
||||
ContentLUT(const Content* content, size_t blocks, size_t items);
|
||||
ContentUnitLUT<blockid_t, Block> blocks;
|
||||
ContentUnitLUT<itemid_t, ItemDef> items;
|
||||
|
||||
inline const std::string& getBlockName(blockid_t index) const {
|
||||
return blockNames[index];
|
||||
}
|
||||
|
||||
inline blockid_t getBlockId(blockid_t index) const {
|
||||
return blocks[index];
|
||||
}
|
||||
|
||||
inline void setBlock(blockid_t index, std::string name, blockid_t id) {
|
||||
blocks[index] = id;
|
||||
blockNames[index] = std::move(name);
|
||||
if (id == BLOCK_VOID) {
|
||||
missingContent = true;
|
||||
} else if (index != id) {
|
||||
reorderContent = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline const std::string& getItemName(blockid_t index) const {
|
||||
return itemNames[index];
|
||||
}
|
||||
|
||||
inline itemid_t getItemId(itemid_t index) const {
|
||||
return items[index];
|
||||
}
|
||||
|
||||
inline void setItem(itemid_t index, std::string name, itemid_t id) {
|
||||
items[index] = id;
|
||||
itemNames[index] = std::move(name);
|
||||
if (id == ITEM_VOID) {
|
||||
missingContent = true;
|
||||
} else if (index != id) {
|
||||
reorderContent = true;
|
||||
}
|
||||
}
|
||||
ContentLUT(const ContentIndices* indices, size_t blocks, size_t items);
|
||||
|
||||
static std::shared_ptr<ContentLUT> create(
|
||||
const fs::path& filename,
|
||||
@ -77,18 +102,10 @@ public:
|
||||
);
|
||||
|
||||
inline bool hasContentReorder() const {
|
||||
return reorderContent;
|
||||
return blocks.hasContentReorder() || items.hasContentReorder();
|
||||
}
|
||||
inline bool hasMissingContent() const {
|
||||
return missingContent;
|
||||
}
|
||||
|
||||
inline size_t countBlocks() const {
|
||||
return blocks.size();
|
||||
}
|
||||
|
||||
inline size_t countItems() const {
|
||||
return items.size();
|
||||
return blocks.hasMissingContent() || items.hasMissingContent();
|
||||
}
|
||||
|
||||
std::vector<contententry> getMissingContent() const;
|
||||
|
||||
@ -9,9 +9,11 @@
|
||||
#include "../debug/Logger.hpp"
|
||||
#include "../files/files.hpp"
|
||||
#include "../items/ItemDef.hpp"
|
||||
#include "../objects/rigging.hpp"
|
||||
#include "../logic/scripting/scripting.hpp"
|
||||
#include "../typedefs.hpp"
|
||||
#include "../util/listutil.hpp"
|
||||
#include "../util/stringutil.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
|
||||
#include <iostream>
|
||||
@ -24,7 +26,37 @@ namespace fs = std::filesystem;
|
||||
|
||||
static debug::Logger logger("content-loader");
|
||||
|
||||
ContentLoader::ContentLoader(ContentPack* pack) : pack(pack) {
|
||||
ContentLoader::ContentLoader(ContentPack* pack, ContentBuilder& builder)
|
||||
: pack(pack), builder(builder)
|
||||
{
|
||||
auto runtime = std::make_unique<ContentPackRuntime>(
|
||||
*pack, scripting::create_pack_environment(*pack)
|
||||
);
|
||||
stats = &runtime->getStatsWriteable();
|
||||
env = runtime->getEnvironment();
|
||||
this->runtime = runtime.get();
|
||||
builder.add(std::move(runtime));
|
||||
}
|
||||
|
||||
static void detect_defs(
|
||||
const fs::path& folder,
|
||||
const std::string& prefix,
|
||||
std::vector<std::string>& detected
|
||||
) {
|
||||
if (fs::is_directory(folder)) {
|
||||
for (const auto& entry : fs::directory_iterator(folder)) {
|
||||
const fs::path& file = entry.path();
|
||||
std::string name = file.stem().string();
|
||||
if (name[0] == '_') {
|
||||
continue;
|
||||
}
|
||||
if (fs::is_regular_file(file) && file.extension() == ".json") {
|
||||
detected.push_back(prefix.empty() ? name : prefix + ":" + name);
|
||||
} else if (fs::is_directory(file)) {
|
||||
detect_defs(file, name, detected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ContentLoader::fixPackIndices(
|
||||
@ -33,32 +65,9 @@ bool ContentLoader::fixPackIndices(
|
||||
const std::string& contentSection
|
||||
) {
|
||||
std::vector<std::string> detected;
|
||||
std::vector<std::string> indexed;
|
||||
if (fs::is_directory(folder)) {
|
||||
for (const auto& entry : fs::directory_iterator(folder)) {
|
||||
const fs::path& file = entry.path();
|
||||
if (fs::is_regular_file(file) && file.extension() == ".json") {
|
||||
std::string name = file.stem().string();
|
||||
if (name[0] == '_')
|
||||
continue;
|
||||
detected.push_back(name);
|
||||
} else if (fs::is_directory(file)) {
|
||||
std::string space = file.stem().string();
|
||||
if (space[0] == '_')
|
||||
continue;
|
||||
for (const auto& entry : fs::directory_iterator(file)) {
|
||||
const fs::path& file = entry.path();
|
||||
if (fs::is_regular_file(file) && file.extension() == ".json") {
|
||||
std::string name = file.stem().string();
|
||||
if (name[0] == '_')
|
||||
continue;
|
||||
detected.push_back(space + ':' + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
detect_defs(folder, "", detected);
|
||||
|
||||
std::vector<std::string> indexed;
|
||||
bool modified = false;
|
||||
if (!indicesRoot->has(contentSection)) {
|
||||
indicesRoot->putList(contentSection);
|
||||
@ -90,6 +99,7 @@ void ContentLoader::fixPackIndices() {
|
||||
auto indexFile = pack->getContentFile();
|
||||
auto blocksFolder = folder/ContentPack::BLOCKS_FOLDER;
|
||||
auto itemsFolder = folder/ContentPack::ITEMS_FOLDER;
|
||||
auto entitiesFolder = folder/ContentPack::ENTITIES_FOLDER;
|
||||
|
||||
dynamic::Map_sptr root;
|
||||
if (fs::is_regular_file(indexFile)) {
|
||||
@ -99,9 +109,9 @@ void ContentLoader::fixPackIndices() {
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
|
||||
modified |= fixPackIndices(blocksFolder, root.get(), "blocks");
|
||||
modified |= fixPackIndices(itemsFolder, root.get(), "items");
|
||||
modified |= fixPackIndices(entitiesFolder, root.get(), "entities");
|
||||
|
||||
if (modified){
|
||||
// rewrite modified json
|
||||
@ -129,22 +139,19 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat
|
||||
}
|
||||
|
||||
// block model
|
||||
std::string model = "block";
|
||||
root->str("model", model);
|
||||
if (model == "block") def.model = BlockModel::block;
|
||||
else if (model == "aabb") def.model = BlockModel::aabb;
|
||||
else if (model == "custom") {
|
||||
def.model = BlockModel::custom;
|
||||
if (root->has("model-primitives")) {
|
||||
loadCustomBlockModel(def, root->map("model-primitives"));
|
||||
} else {
|
||||
logger.error() << name << ": no 'model-primitives' found";
|
||||
std::string modelName;
|
||||
root->str("model", modelName);
|
||||
if (auto model = BlockModel_from(modelName)) {
|
||||
if (*model == BlockModel::custom) {
|
||||
if (root->has("model-primitives")) {
|
||||
loadCustomBlockModel(def, root->map("model-primitives").get());
|
||||
} else {
|
||||
logger.error() << name << ": no 'model-primitives' found";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (model == "X") def.model = BlockModel::xsprite;
|
||||
else if (model == "none") def.model = BlockModel::none;
|
||||
else {
|
||||
logger.error() << "unknown model " << model;
|
||||
def.model = *model;
|
||||
} else if (!modelName.empty()) {
|
||||
logger.error() << "unknown model " << modelName;
|
||||
def.model = BlockModel::none;
|
||||
}
|
||||
|
||||
@ -173,7 +180,7 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat
|
||||
def.hitboxes[i].b = glm::vec3(box->num(3), box->num(4), box->num(5));
|
||||
def.hitboxes[i].b += def.hitboxes[i].a;
|
||||
}
|
||||
} else if (auto boxarr = root->list("hitbox")){
|
||||
} else if ((boxarr = root->list("hitbox"))){
|
||||
AABB aabb;
|
||||
aabb.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
aabb.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5));
|
||||
@ -294,33 +301,93 @@ void ContentLoader::loadItem(ItemDef& def, const std::string& name, const fs::pa
|
||||
root->num("stack-size", def.stackSize);
|
||||
|
||||
// item light emission [r, g, b] where r,g,b in range [0..15]
|
||||
auto emissionarr = root->list("emission");
|
||||
if (emissionarr) {
|
||||
if (auto emissionarr = root->list("emission")) {
|
||||
def.emission[0] = emissionarr->num(0);
|
||||
def.emission[1] = emissionarr->num(1);
|
||||
def.emission[2] = emissionarr->num(2);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadEntity(EntityDef& def, const std::string& name, const fs::path& file) {
|
||||
auto root = files::read_json(file);
|
||||
if (auto componentsarr = root->list("components")) {
|
||||
for (size_t i = 0; i < componentsarr->size(); i++) {
|
||||
def.components.push_back(componentsarr->str(i));
|
||||
}
|
||||
}
|
||||
if (auto boxarr = root->list("hitbox")) {
|
||||
def.hitbox = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
}
|
||||
if (auto sensorsarr = root->list("sensors")) {
|
||||
for (size_t i = 0; i < sensorsarr->size(); i++) {
|
||||
if (auto sensorarr = sensorsarr->list(i)) {
|
||||
auto sensorType = sensorarr->str(0);
|
||||
if (sensorType == "aabb") {
|
||||
def.boxSensors.push_back({i, {
|
||||
{sensorarr->num(1), sensorarr->num(2), sensorarr->num(3)},
|
||||
{sensorarr->num(4), sensorarr->num(5), sensorarr->num(6)}
|
||||
}});
|
||||
} else if (sensorType == "radius") {
|
||||
def.radialSensors.push_back({i, sensorarr->num(1)});
|
||||
} else {
|
||||
logger.error() << name << ": sensor #" << i << " - unknown type "
|
||||
<< util::quote(sensorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
root->flag("save", def.save.enabled);
|
||||
root->flag("save-skeleton-pose", def.save.skeleton.pose);
|
||||
root->flag("save-skeleton-textures", def.save.skeleton.textures);
|
||||
root->flag("save-body-velocity", def.save.body.velocity);
|
||||
root->flag("save-body-settings", def.save.body.settings);
|
||||
|
||||
std::string bodyTypeName;
|
||||
root->str("body-type", bodyTypeName);
|
||||
if (auto bodyType = BodyType_from(bodyTypeName)) {
|
||||
def.bodyType = *bodyType;
|
||||
}
|
||||
|
||||
root->str("skeleton-name", def.skeletonName);
|
||||
root->flag("blocking", def.blocking);
|
||||
}
|
||||
|
||||
void ContentLoader::loadEntity(EntityDef& def, const std::string& full, const std::string& name) {
|
||||
auto folder = pack->folder;
|
||||
auto configFile = folder/fs::path("entities/"+name+".json");
|
||||
if (fs::exists(configFile)) loadEntity(def, full, configFile);
|
||||
}
|
||||
|
||||
void ContentLoader::loadBlock(Block& def, const std::string& full, const std::string& name) {
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path configFile = folder/fs::path("blocks/"+name+".json");
|
||||
auto configFile = folder/fs::path("blocks/"+name+".json");
|
||||
if (fs::exists(configFile)) loadBlock(def, full, configFile);
|
||||
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
auto scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_block_script(env, full, scriptfile, def.rt.funcsset);
|
||||
}
|
||||
if (!def.hidden) {
|
||||
auto& item = builder.items.create(full+BLOCK_ITEM_SUFFIX);
|
||||
item.generated = true;
|
||||
item.caption = def.caption;
|
||||
item.iconType = item_icon_type::block;
|
||||
item.icon = full;
|
||||
item.placingBlock = full;
|
||||
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
item.emission[j] = def.emission[j];
|
||||
}
|
||||
stats->totalItems++;
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadItem(ItemDef& def, const std::string& full, const std::string& name) {
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path configFile = folder/fs::path("items/"+name+".json");
|
||||
auto configFile = folder/fs::path("items/"+name+".json");
|
||||
if (fs::exists(configFile)) loadItem(def, full, configFile);
|
||||
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
auto scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_item_script(env, full, scriptfile, def.rt.funcsset);
|
||||
}
|
||||
@ -333,69 +400,57 @@ void ContentLoader::loadBlockMaterial(BlockMaterial& def, const fs::path& file)
|
||||
root->str("break-sound", def.breakSound);
|
||||
}
|
||||
|
||||
void ContentLoader::load(ContentBuilder& builder) {
|
||||
void ContentLoader::load() {
|
||||
logger.info() << "loading pack [" << pack->id << "]";
|
||||
|
||||
auto runtime = std::make_unique<ContentPackRuntime>(
|
||||
*pack, scripting::create_pack_environment(*pack)
|
||||
);
|
||||
env = runtime->getEnvironment();
|
||||
ContentPackStats& stats = runtime->getStatsWriteable();
|
||||
builder.add(std::move(runtime));
|
||||
|
||||
fixPackIndices();
|
||||
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path scriptFile = folder/fs::path("scripts/world.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_world_script(env, pack->id, scriptFile);
|
||||
scripting::load_world_script(env, pack->id, scriptFile, runtime->worldfuncsset);
|
||||
}
|
||||
|
||||
if (!fs::is_regular_file(pack->getContentFile()))
|
||||
return;
|
||||
|
||||
auto root = files::read_json(pack->getContentFile());
|
||||
auto blocksarr = root->list("blocks");
|
||||
if (blocksarr) {
|
||||
for (uint i = 0; i < blocksarr->size(); i++) {
|
||||
|
||||
if (auto blocksarr = root->list("blocks")) {
|
||||
for (size_t i = 0; i < blocksarr->size(); i++) {
|
||||
std::string name = blocksarr->str(i);
|
||||
auto colon = name.find(':');
|
||||
std::string full = colon == std::string::npos ? pack->id + ":" + name : name;
|
||||
if (colon != std::string::npos) name[colon] = '/';
|
||||
auto& def = builder.createBlock(full);
|
||||
if (colon != std::string::npos) {
|
||||
def.scriptName = name.substr(0, colon) + '/' + def.scriptName;
|
||||
}
|
||||
auto& def = builder.blocks.create(full);
|
||||
if (colon != std::string::npos) def.scriptName = name.substr(0, colon) + '/' + def.scriptName;
|
||||
loadBlock(def, full, name);
|
||||
stats.totalBlocks++;
|
||||
if (!def.hidden) {
|
||||
auto& item = builder.createItem(full+BLOCK_ITEM_SUFFIX);
|
||||
item.generated = true;
|
||||
item.caption = def.caption;
|
||||
item.iconType = item_icon_type::block;
|
||||
item.icon = full;
|
||||
item.placingBlock = full;
|
||||
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
item.emission[j] = def.emission[j];
|
||||
}
|
||||
stats.totalItems++;
|
||||
}
|
||||
stats->totalBlocks++;
|
||||
}
|
||||
}
|
||||
|
||||
auto itemsarr = root->list("items");
|
||||
if (itemsarr) {
|
||||
for (uint i = 0; i < itemsarr->size(); i++) {
|
||||
if (auto itemsarr = root->list("items")) {
|
||||
for (size_t i = 0; i < itemsarr->size(); i++) {
|
||||
std::string name = itemsarr->str(i);
|
||||
auto colon = name.find(':');
|
||||
std::string full = colon == std::string::npos ? pack->id + ":" + name : name;
|
||||
if (colon != std::string::npos) name[colon] = '/';
|
||||
auto& def = builder.createItem(full);
|
||||
auto& def = builder.items.create(full);
|
||||
if (colon != std::string::npos) def.scriptName = name.substr(0, colon) + '/' + def.scriptName;
|
||||
loadItem(def, full, name);
|
||||
stats.totalItems++;
|
||||
stats->totalItems++;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto entitiesarr = root->list("entities")) {
|
||||
for (size_t i = 0; i < entitiesarr->size(); i++) {
|
||||
std::string name = entitiesarr->str(i);
|
||||
auto colon = name.find(':');
|
||||
std::string full = colon == std::string::npos ? pack->id + ":" + name : name;
|
||||
if (colon != std::string::npos) name[colon] = '/';
|
||||
auto& def = builder.entities.create(full);
|
||||
loadEntity(def, full, name);
|
||||
stats->totalEntities++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,4 +462,46 @@ void ContentLoader::load(ContentBuilder& builder) {
|
||||
loadBlockMaterial(builder.createBlockMaterial(name), file);
|
||||
}
|
||||
}
|
||||
|
||||
fs::path skeletonsDir = folder / fs::u8path("skeletons");
|
||||
if (fs::is_directory(skeletonsDir)) {
|
||||
for (const auto& entry : fs::directory_iterator(skeletonsDir)) {
|
||||
const fs::path& file = entry.path();
|
||||
std::string name = pack->id+":"+file.stem().u8string();
|
||||
std::string text = files::read_string(file);
|
||||
builder.add(rigging::SkeletonConfig::parse(text, file.u8string(), name));
|
||||
}
|
||||
}
|
||||
|
||||
fs::path componentsDir = folder / fs::u8path("scripts/components");
|
||||
if (fs::is_directory(componentsDir)) {
|
||||
for (const auto& entry : fs::directory_iterator(componentsDir)) {
|
||||
fs::path scriptfile = entry.path();
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
auto name = pack->id+":"+scriptfile.stem().u8string();
|
||||
scripting::load_entity_component(name, scriptfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs::path resourcesFile = folder / fs::u8path("resources.json");
|
||||
if (fs::exists(resourcesFile)) {
|
||||
auto resRoot = files::read_json(resourcesFile);
|
||||
for (const auto& [key, _] : resRoot->values) {
|
||||
if (auto resType = ResourceType_from(key)) {
|
||||
if (auto arr = resRoot->list(key)) {
|
||||
loadResources(*resType, arr.get());
|
||||
}
|
||||
} else {
|
||||
logger.warning() << "unknown resource type: " << key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadResources(ResourceType type, dynamic::List* list) {
|
||||
for (size_t i = 0; i < list->size(); i++) {
|
||||
builder.resourceIndices[static_cast<size_t>(type)].add(
|
||||
pack->id+":"+list->str(i), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,33 +1,48 @@
|
||||
#ifndef CONTENT_CONTENT_LOADER_HPP_
|
||||
#define CONTENT_CONTENT_LOADER_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
#include "content_fwd.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class Block;
|
||||
struct BlockMaterial;
|
||||
class ItemDef;
|
||||
struct ItemDef;
|
||||
struct EntityDef;
|
||||
struct ContentPack;
|
||||
|
||||
class ContentBuilder;
|
||||
class ContentPackRuntime;
|
||||
struct ContentPackStats;
|
||||
|
||||
namespace dynamic {
|
||||
class Map;
|
||||
class List;
|
||||
}
|
||||
|
||||
class ContentLoader {
|
||||
const ContentPack* pack;
|
||||
ContentPackRuntime* runtime;
|
||||
scriptenv env;
|
||||
|
||||
ContentBuilder& builder;
|
||||
ContentPackStats* stats;
|
||||
|
||||
void loadBlock(Block& def, const std::string& full, const std::string& name);
|
||||
void loadCustomBlockModel(Block& def, dynamic::Map* primitives);
|
||||
void loadItem(ItemDef& def, const std::string& full, const std::string& name);
|
||||
void loadBlockMaterial(BlockMaterial& def, const fs::path& file);
|
||||
void loadEntity(EntityDef& def, const std::string& full, const std::string& name);
|
||||
|
||||
static void loadCustomBlockModel(Block& def, dynamic::Map* primitives);
|
||||
static void loadBlockMaterial(BlockMaterial& def, const fs::path& file);
|
||||
static void loadBlock(Block& def, const std::string& name, const fs::path& file);
|
||||
static void loadItem(ItemDef& def, const std::string& name, const fs::path& file);
|
||||
static void loadEntity(EntityDef& def, const std::string& name, const fs::path& file);
|
||||
void loadResources(ResourceType type, dynamic::List* list);
|
||||
public:
|
||||
ContentLoader(ContentPack* pack);
|
||||
ContentLoader(ContentPack* pack, ContentBuilder& builder);
|
||||
|
||||
bool fixPackIndices(
|
||||
const fs::path& folder,
|
||||
@ -35,9 +50,7 @@ public:
|
||||
const std::string& contentSection
|
||||
);
|
||||
void fixPackIndices();
|
||||
void loadBlock(Block& def, const std::string& name, const fs::path& file);
|
||||
void loadItem(ItemDef& def, const std::string& name, const fs::path& file);
|
||||
void load(ContentBuilder& builder);
|
||||
void load();
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_LOADER_HPP_
|
||||
|
||||
@ -11,10 +11,6 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const std::string ContentPack::PACKAGE_FILENAME = "package.json";
|
||||
const std::string ContentPack::CONTENT_FILENAME = "content.json";
|
||||
const fs::path ContentPack::BLOCKS_FOLDER = "blocks";
|
||||
const fs::path ContentPack::ITEMS_FOLDER = "items";
|
||||
const std::vector<std::string> ContentPack::RESERVED_NAMES = {
|
||||
"res", "abs", "local", "core", "user", "world", "none", "null"
|
||||
};
|
||||
|
||||
@ -46,10 +46,11 @@ struct ContentPack {
|
||||
|
||||
fs::path getContentFile() const;
|
||||
|
||||
static const std::string PACKAGE_FILENAME;
|
||||
static const std::string CONTENT_FILENAME;
|
||||
static const fs::path BLOCKS_FOLDER;
|
||||
static const fs::path ITEMS_FOLDER;
|
||||
static inline const std::string PACKAGE_FILENAME = "package.json";
|
||||
static inline const std::string CONTENT_FILENAME = "content.json";
|
||||
static inline const fs::path BLOCKS_FOLDER = "blocks";
|
||||
static inline const fs::path ITEMS_FOLDER = "items";
|
||||
static inline const fs::path ENTITIES_FOLDER = "entities";
|
||||
static const std::vector<std::string> RESERVED_NAMES;
|
||||
|
||||
static bool is_pack(const fs::path& folder);
|
||||
@ -72,17 +73,25 @@ struct ContentPack {
|
||||
struct ContentPackStats {
|
||||
size_t totalBlocks;
|
||||
size_t totalItems;
|
||||
size_t totalEntities;
|
||||
|
||||
inline bool hasSavingContent() const {
|
||||
return totalBlocks + totalItems > 0;
|
||||
return totalBlocks + totalItems + totalEntities > 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct world_funcs_set {
|
||||
bool onblockplaced : 1;
|
||||
bool onblockbroken : 1;
|
||||
};
|
||||
|
||||
class ContentPackRuntime {
|
||||
ContentPack info;
|
||||
ContentPackStats stats {};
|
||||
scriptenv env;
|
||||
public:
|
||||
world_funcs_set worldfuncsset {};
|
||||
|
||||
ContentPackRuntime(
|
||||
ContentPack info,
|
||||
scriptenv env
|
||||
|
||||
20
src/content/content_fwd.hpp
Normal file
20
src/content/content_fwd.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef CONTENT_CONTENT_FWD_HPP_
|
||||
#define CONTENT_CONTENT_FWD_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
|
||||
class Content;
|
||||
class ContentPackRuntime;
|
||||
|
||||
enum class contenttype {
|
||||
none, block, item, entity
|
||||
};
|
||||
|
||||
enum class ResourceType : size_t {
|
||||
CAMERA,
|
||||
LAST=CAMERA
|
||||
};
|
||||
|
||||
inline constexpr auto RESOURCE_TYPES_COUNT = static_cast<size_t>(ResourceType::LAST)+1;
|
||||
|
||||
#endif // CONTENT_CONTENT_FWD_HPP_
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
// All in-game definitions (blocks, items, etc..)
|
||||
void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) {
|
||||
Block& block = builder->createBlock("core:air");
|
||||
Block& block = builder->blocks.create("core:air");
|
||||
block.replaceable = true;
|
||||
block.drawGroup = 1;
|
||||
block.lightPassing = true;
|
||||
@ -22,7 +22,7 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) {
|
||||
block.model = BlockModel::none;
|
||||
block.pickingItem = "core:empty";
|
||||
|
||||
ItemDef& item = builder->createItem("core:empty");
|
||||
ItemDef& item = builder->items.create("core:empty");
|
||||
item.iconType = item_icon_type::none;
|
||||
|
||||
auto bindsFile = paths->getResources()/fs::path("bindings.toml");
|
||||
|
||||
@ -55,9 +55,9 @@ integer_t List::integer(size_t index) const {
|
||||
}
|
||||
}
|
||||
|
||||
Map* List::map(size_t index) const {
|
||||
const Map_sptr& List::map(size_t index) const {
|
||||
if (auto* val = std::get_if<Map_sptr>(&values[index])) {
|
||||
return val->get();
|
||||
return *val;
|
||||
} else {
|
||||
throw std::runtime_error("type error");
|
||||
}
|
||||
@ -192,20 +192,20 @@ void Map::num(const std::string& key, uint& dst) const {
|
||||
dst = get(key, static_cast<integer_t>(dst));
|
||||
}
|
||||
|
||||
Map* Map::map(const std::string& key) const {
|
||||
Map_sptr Map::map(const std::string& key) const {
|
||||
auto found = values.find(key);
|
||||
if (found != values.end()) {
|
||||
if (auto* val = std::get_if<Map_sptr>(&found->second)) {
|
||||
return val->get();
|
||||
return *val;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
List* Map::list(const std::string& key) const {
|
||||
List_sptr Map::list(const std::string& key) const {
|
||||
auto found = values.find(key);
|
||||
if (found != values.end())
|
||||
return std::get<List_sptr>(found->second).get();
|
||||
return std::get<List_sptr>(found->second);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -1,42 +1,22 @@
|
||||
#ifndef DATA_DYNAMIC_HPP_
|
||||
#define DATA_DYNAMIC_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
#include "dynamic_fwd.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <variant>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dynamic {
|
||||
class Map;
|
||||
class List;
|
||||
|
||||
enum class Type {
|
||||
none=0, map, list, string, number, boolean, integer
|
||||
};
|
||||
|
||||
using Map_sptr = std::shared_ptr<Map>;
|
||||
using List_sptr = std::shared_ptr<List>;
|
||||
|
||||
struct none {};
|
||||
|
||||
inline constexpr none NONE = {};
|
||||
|
||||
using Value = std::variant<
|
||||
none,
|
||||
Map_sptr,
|
||||
List_sptr,
|
||||
std::string,
|
||||
number_t,
|
||||
bool,
|
||||
integer_t
|
||||
>;
|
||||
|
||||
const std::string& type_name(const Value& value);
|
||||
List_sptr create_list(std::initializer_list<Value> values={});
|
||||
Map_sptr create_map(std::initializer_list<std::pair<const std::string, Value>> entries={});
|
||||
@ -67,7 +47,7 @@ namespace dynamic {
|
||||
std::string str(size_t index) const;
|
||||
number_t num(size_t index) const;
|
||||
integer_t integer(size_t index) const;
|
||||
Map* map(size_t index) const;
|
||||
const Map_sptr& map(size_t index) const;
|
||||
List* list(size_t index) const;
|
||||
bool flag(size_t index) const;
|
||||
|
||||
@ -134,8 +114,8 @@ namespace dynamic {
|
||||
void num(const std::string& key, uint64_t& dst) const;
|
||||
void num(const std::string& key, ubyte& dst) const;
|
||||
void num(const std::string& key, double& dst) const;
|
||||
Map* map(const std::string& key) const;
|
||||
List* list(const std::string& key) const;
|
||||
Map_sptr map(const std::string& key) const;
|
||||
List_sptr list(const std::string& key) const;
|
||||
void flag(const std::string& key, bool& dst) const;
|
||||
|
||||
Map& put(std::string key, std::unique_ptr<Map> value) {
|
||||
@ -153,6 +133,9 @@ namespace dynamic {
|
||||
Map& put(std::string key, int64_t value) {
|
||||
return put(key, Value(static_cast<integer_t>(value)));
|
||||
}
|
||||
Map& put(std::string key, uint64_t value) {
|
||||
return put(key, Value(static_cast<integer_t>(value)));
|
||||
}
|
||||
Map& put(std::string key, float value) {
|
||||
return put(key, Value(static_cast<number_t>(value)));
|
||||
}
|
||||
|
||||
32
src/data/dynamic_fwd.hpp
Normal file
32
src/data/dynamic_fwd.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef DATA_DYNAMIC_FWD_HPP_
|
||||
#define DATA_DYNAMIC_FWD_HPP_
|
||||
|
||||
#include "../typedefs.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace dynamic {
|
||||
class Map;
|
||||
class List;
|
||||
|
||||
using Map_sptr = std::shared_ptr<Map>;
|
||||
using List_sptr = std::shared_ptr<List>;
|
||||
|
||||
struct none {};
|
||||
|
||||
inline constexpr none NONE = {};
|
||||
|
||||
using Value = std::variant<
|
||||
none,
|
||||
Map_sptr,
|
||||
List_sptr,
|
||||
std::string,
|
||||
number_t,
|
||||
bool,
|
||||
integer_t
|
||||
>;
|
||||
}
|
||||
|
||||
#endif // DATA_DYNAMIC_FWD_HPP_
|
||||
70
src/data/dynamic_util.hpp
Normal file
70
src/data/dynamic_util.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef DATA_DYNAMIC_UTIL_HPP_
|
||||
#define DATA_DYNAMIC_UTIL_HPP_
|
||||
|
||||
#include "dynamic.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace dynamic {
|
||||
template<int n>
|
||||
inline dynamic::List_sptr to_value(glm::vec<n, float> vec) {
|
||||
auto list = dynamic::create_list();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
list->put(vec[i]);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
template<int n, int m>
|
||||
inline dynamic::List_sptr to_value(glm::mat<n, m, float> mat) {
|
||||
auto list = dynamic::create_list();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
for (size_t j = 0; j < m; j++) {
|
||||
list->put(mat[i][j]);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
template<int n>
|
||||
void get_vec(const dynamic::Map_sptr& root, const std::string& name, glm::vec<n, float>& vec) {
|
||||
if (const auto& list = root->list(name)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
vec[i] = list->num(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int n>
|
||||
void get_vec(const dynamic::List_sptr& root, size_t index, glm::vec<n, float>& vec) {
|
||||
if (const auto& list = root->list(index)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
vec[i] = list->num(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int n, int m>
|
||||
void get_mat(const dynamic::Map_sptr& root, const std::string& name, glm::mat<n, m, float>& mat) {
|
||||
if (const auto& list = root->list(name)) {
|
||||
for (size_t y = 0; y < n; y++) {
|
||||
for (size_t x = 0; x < m; x++) {
|
||||
mat[y][x] = list->num(y*m+x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int n, int m>
|
||||
void get_mat(const dynamic::List_sptr& root, size_t index, glm::mat<n, m, float>& mat) {
|
||||
if (const auto& list = root->list(index)) {
|
||||
for (size_t y = 0; y < n; y++) {
|
||||
for (size_t x = 0; x < m; x++) {
|
||||
mat[y][x] = list->num(y*m+x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DATA_DYNAMIC_UTIL_HPP_
|
||||
@ -9,6 +9,7 @@
|
||||
#include "coders/imageio.hpp"
|
||||
#include "coders/json.hpp"
|
||||
#include "coders/toml.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "content/ContentBuilder.hpp"
|
||||
#include "content/ContentLoader.hpp"
|
||||
#include "core_defs.hpp"
|
||||
@ -23,6 +24,7 @@
|
||||
#include "graphics/core/ImageData.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "logic/EngineController.hpp"
|
||||
#include "logic/CommandsInterpreter.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
@ -121,6 +123,7 @@ void Engine::loadControls() {
|
||||
}
|
||||
|
||||
void Engine::onAssetsLoaded() {
|
||||
assets->setup();
|
||||
gui->onAssetsLoad(assets.get());
|
||||
}
|
||||
|
||||
@ -246,6 +249,8 @@ void Engine::loadAssets() {
|
||||
AssetsLoader loader(new_assets.get(), resPaths.get());
|
||||
AssetsLoader::addDefaults(loader, content.get());
|
||||
|
||||
// no need
|
||||
// correct log messages order is more useful
|
||||
bool threading = false;
|
||||
if (threading) {
|
||||
auto task = loader.startTask([=](){});
|
||||
@ -289,10 +294,7 @@ void Engine::loadContent() {
|
||||
std::vector<PathsRoot> resRoots;
|
||||
for (auto& pack : contentPacks) {
|
||||
resRoots.push_back({pack.id, pack.folder});
|
||||
|
||||
ContentLoader loader(&pack);
|
||||
loader.load(contentBuilder);
|
||||
|
||||
ContentLoader(&pack, contentBuilder).load();
|
||||
load_configs(pack.folder);
|
||||
}
|
||||
load_configs(paths->getResources());
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include "typedefs.hpp"
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "content/content_fwd.hpp"
|
||||
#include "content/ContentPack.hpp"
|
||||
#include "content/PacksManager.hpp"
|
||||
#include "files/engine_paths.hpp"
|
||||
@ -48,11 +48,11 @@ class Engine : public util::ObjectsKeeper {
|
||||
SettingsHandler& settingsHandler;
|
||||
EnginePaths* paths;
|
||||
|
||||
std::unique_ptr<Assets> assets = nullptr;
|
||||
std::shared_ptr<Screen> screen = nullptr;
|
||||
std::unique_ptr<Assets> assets;
|
||||
std::shared_ptr<Screen> screen;
|
||||
std::vector<ContentPack> contentPacks;
|
||||
std::unique_ptr<Content> content = nullptr;
|
||||
std::unique_ptr<ResPaths> resPaths = nullptr;
|
||||
std::unique_ptr<Content> content;
|
||||
std::unique_ptr<ResPaths> resPaths;
|
||||
std::queue<runnable> postRunnables;
|
||||
std::recursive_mutex postRunnablesMutex;
|
||||
std::unique_ptr<EngineController> controller;
|
||||
|
||||
@ -6,11 +6,13 @@
|
||||
#include "../content/Content.hpp"
|
||||
#include "../core_defs.hpp"
|
||||
#include "../data/dynamic.hpp"
|
||||
#include "../debug/Logger.hpp"
|
||||
#include "../items/Inventory.hpp"
|
||||
#include "../items/ItemDef.hpp"
|
||||
#include "../lighting/Lightmap.hpp"
|
||||
#include "../maths/voxmaths.hpp"
|
||||
#include "../objects/Player.hpp"
|
||||
#include "../objects/EntityDef.hpp"
|
||||
#include "../physics/Hitbox.hpp"
|
||||
#include "../typedefs.hpp"
|
||||
#include "../settings.hpp"
|
||||
@ -31,6 +33,8 @@
|
||||
|
||||
#define WORLD_FORMAT_MAGIC ".VOXWLD"
|
||||
|
||||
static debug::Logger logger("world-files");
|
||||
|
||||
WorldFiles::WorldFiles(const fs::path& directory) : directory(directory), regions(directory) {
|
||||
}
|
||||
|
||||
@ -55,6 +59,10 @@ fs::path WorldFiles::getPlayerFile() const {
|
||||
return directory/fs::path("player.json");
|
||||
}
|
||||
|
||||
fs::path WorldFiles::getResourcesFile() const {
|
||||
return directory/fs::path("resources.json");
|
||||
}
|
||||
|
||||
fs::path WorldFiles::getWorldFile() const {
|
||||
return directory/fs::path(WORLD_FILE);
|
||||
}
|
||||
@ -92,23 +100,19 @@ void WorldFiles::writePacks(const std::vector<ContentPack>& packs) {
|
||||
files::write_string(packsFile, ss.str());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void write_indices(const ContentUnitIndices<T>& indices, dynamic::List& list) {
|
||||
size_t count = indices.count();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
list.put(indices.get(i)->name);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldFiles::writeIndices(const ContentIndices* indices) {
|
||||
dynamic::Map root;
|
||||
uint count;
|
||||
auto& blocks = root.putList("blocks");
|
||||
count = indices->countBlockDefs();
|
||||
for (uint i = 0; i < count; i++) {
|
||||
const Block* def = indices->getBlockDef(i);
|
||||
blocks.put(def->name);
|
||||
}
|
||||
|
||||
auto& items = root.putList("items");
|
||||
count = indices->countItemDefs();
|
||||
for (uint i = 0; i < count; i++) {
|
||||
const ItemDef* def = indices->getItemDef(i);
|
||||
items.put(def->name);
|
||||
}
|
||||
|
||||
write_indices(indices->blocks, root.putList("blocks"));
|
||||
write_indices(indices->items, root.putList("items"));
|
||||
write_indices(indices->entities, root.putList("entities"));
|
||||
files::write_json(getIndicesFile(), &root);
|
||||
}
|
||||
|
||||
@ -119,15 +123,52 @@ void WorldFiles::writeWorldInfo(const World* world) {
|
||||
bool WorldFiles::readWorldInfo(World* world) {
|
||||
fs::path file = getWorldFile();
|
||||
if (!fs::is_regular_file(file)) {
|
||||
std::cerr << "warning: world.json does not exists" << std::endl;
|
||||
logger.warning() << "world.json does not exists";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto root = files::read_json(file);
|
||||
world->deserialize(root.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
static void read_resources_data(
|
||||
const Content* content,
|
||||
const dynamic::List_sptr& list,
|
||||
ResourceType type
|
||||
) {
|
||||
const auto& indices = content->getIndices(type);
|
||||
for (size_t i = 0; i < list->size(); i++) {
|
||||
auto map = list->map(i);
|
||||
std::string name;
|
||||
map->str("name", name);
|
||||
size_t index = indices.indexOf(name);
|
||||
if (index == ResourceIndices::MISSING) {
|
||||
logger.warning() << "discard " << name;
|
||||
} else {
|
||||
indices.saveData(index, map->map("saved"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WorldFiles::readResourcesData(const Content* content) {
|
||||
fs::path file = getResourcesFile();
|
||||
if (!fs::is_regular_file(file)) {
|
||||
logger.warning() << "resources.json does not exists";
|
||||
return false;
|
||||
}
|
||||
auto root = files::read_json(file);
|
||||
for (const auto& [key, _] : root->values) {
|
||||
if (auto resType = ResourceType_from(key)) {
|
||||
if (auto arr = root->list(key)) {
|
||||
read_resources_data(content, arr, *resType);
|
||||
}
|
||||
} else {
|
||||
logger.warning() << "unknown resource type: " << key;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void erase_pack_indices(dynamic::Map* root, const std::string& id) {
|
||||
auto prefix = id+":";
|
||||
auto blocks = root->list("blocks");
|
||||
|
||||
@ -46,9 +46,11 @@ public:
|
||||
~WorldFiles();
|
||||
|
||||
fs::path getPlayerFile() const;
|
||||
fs::path getResourcesFile() const;
|
||||
void createDirectories();
|
||||
|
||||
bool readWorldInfo(World* world);
|
||||
bool readResourcesData(const Content* content);
|
||||
|
||||
/// @brief Write all unsaved data to world files
|
||||
/// @param world target world
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define REGION_FORMAT_MAGIC ".VOXREG"
|
||||
|
||||
@ -36,17 +37,17 @@ std::unique_ptr<ubyte[]> regfile::read(int index, uint32_t& length) {
|
||||
|
||||
uint32_t offset;
|
||||
file.seekg(table_offset + index * 4);
|
||||
file.read((char*)(&offset), 4);
|
||||
offset = dataio::read_int32_big((const ubyte*)(&offset), 0);
|
||||
file.read(reinterpret_cast<char*>(&offset), 4);
|
||||
offset = dataio::read_int32_big(reinterpret_cast<const ubyte*>(&offset), 0);
|
||||
if (offset == 0){
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.seekg(offset);
|
||||
file.read((char*)(&offset), 4);
|
||||
length = dataio::read_int32_big((const ubyte*)(&offset), 0);
|
||||
file.read(reinterpret_cast<char*>(&offset), 4);
|
||||
length = dataio::read_int32_big(reinterpret_cast<const ubyte*>(&offset), 0);
|
||||
auto data = std::make_unique<ubyte[]>(length);
|
||||
file.read((char*)data.get(), length);
|
||||
file.read(reinterpret_cast<char*>(data.get()), length);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -88,12 +89,13 @@ uint WorldRegion::getChunkDataSize(uint x, uint z) {
|
||||
}
|
||||
|
||||
WorldRegions::WorldRegions(const fs::path& directory) : directory(directory) {
|
||||
for (uint i = 0; i < sizeof(layers)/sizeof(RegionsLayer); i++) {
|
||||
for (size_t i = 0; i < sizeof(layers)/sizeof(RegionsLayer); i++) {
|
||||
layers[i].layer = i;
|
||||
}
|
||||
layers[REGION_LAYER_VOXELS].folder = directory/fs::path("regions");
|
||||
layers[REGION_LAYER_LIGHTS].folder = directory/fs::path("lights");
|
||||
layers[REGION_LAYER_INVENTORIES].folder = directory/fs::path("inventories");
|
||||
layers[REGION_LAYER_ENTITIES].folder = directory/fs::path("entities");
|
||||
}
|
||||
|
||||
WorldRegions::~WorldRegions() {
|
||||
@ -123,7 +125,7 @@ WorldRegion* WorldRegions::getOrCreateRegion(int x, int z, int layer) {
|
||||
|
||||
std::unique_ptr<ubyte[]> WorldRegions::compress(const ubyte* src, size_t srclen, size_t& len) {
|
||||
auto buffer = bufferPool.get();
|
||||
ubyte* bytes = buffer.get();
|
||||
auto bytes = buffer.get();
|
||||
|
||||
len = extrle::encode(src, srclen, bytes);
|
||||
auto data = std::make_unique<ubyte[]>(len);
|
||||
@ -150,7 +152,7 @@ inline void calc_reg_coords(
|
||||
|
||||
std::unique_ptr<ubyte[]> WorldRegions::readChunkData(
|
||||
int x, int z, uint32_t& length, regfile* rfile
|
||||
){
|
||||
) {
|
||||
int regionX, regionZ, localX, localZ;
|
||||
calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
|
||||
int chunkIndex = localZ * REGION_SIZE + localX;
|
||||
@ -171,10 +173,7 @@ void WorldRegions::fetchChunks(WorldRegion* region, int x, int z, regfile* file)
|
||||
}
|
||||
}
|
||||
|
||||
ubyte* WorldRegions::getData(
|
||||
int x, int z, int layer,
|
||||
uint32_t& size
|
||||
) {
|
||||
ubyte* WorldRegions::getData(int x, int z, int layer, uint32_t& size) {
|
||||
if (generatorTestMode) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -301,7 +300,7 @@ void WorldRegions::writeRegion(int x, int z, int layer, WorldRegion* entry){
|
||||
offset += 4 + compressedSize;
|
||||
|
||||
file.write(intbuf, 4);
|
||||
file.write((const char*)chunk, compressedSize);
|
||||
file.write(reinterpret_cast<const char*>(chunk), compressedSize);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
|
||||
@ -313,8 +312,9 @@ void WorldRegions::writeRegion(int x, int z, int layer, WorldRegion* entry){
|
||||
void WorldRegions::writeRegions(int layer) {
|
||||
for (auto& it : layers[layer].regions){
|
||||
WorldRegion* region = it.second.get();
|
||||
if (region->getChunks() == nullptr || !region->isUnsaved())
|
||||
if (region->getChunks() == nullptr || !region->isUnsaved()) {
|
||||
continue;
|
||||
}
|
||||
glm::ivec2 key = it.first;
|
||||
writeRegion(key[0], key[1], layer, region);
|
||||
}
|
||||
@ -354,13 +354,13 @@ static std::unique_ptr<ubyte[]> write_inventories(Chunk* chunk, uint& datasize)
|
||||
}
|
||||
|
||||
/// @brief Store chunk data (voxels and lights) in region (existing or new)
|
||||
void WorldRegions::put(Chunk* chunk){
|
||||
void WorldRegions::put(Chunk* chunk, std::vector<ubyte> entitiesData){
|
||||
assert(chunk != nullptr);
|
||||
if (!chunk->flags.lighted) {
|
||||
return;
|
||||
}
|
||||
bool lightsUnsaved = !chunk->flags.loadedLights && doWriteLights;
|
||||
if (!chunk->flags.unsaved && !lightsUnsaved) {
|
||||
if (!chunk->flags.unsaved && !lightsUnsaved && !chunk->flags.entities) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -376,12 +376,21 @@ void WorldRegions::put(Chunk* chunk){
|
||||
chunk->lightmap.encode(), LIGHTMAP_DATA_LEN, true);
|
||||
}
|
||||
// Writing block inventories
|
||||
if (!chunk->inventories.empty()){
|
||||
if (!chunk->inventories.empty()) {
|
||||
uint datasize;
|
||||
auto data = write_inventories(chunk, datasize);
|
||||
put(chunk->x, chunk->z, REGION_LAYER_INVENTORIES,
|
||||
std::move(data), datasize, false);
|
||||
}
|
||||
// Writing entities
|
||||
if (!entitiesData.empty()) {
|
||||
auto data = std::make_unique<ubyte[]>(entitiesData.size());
|
||||
for (size_t i = 0; i < entitiesData.size(); i++) {
|
||||
data[i] = entitiesData[i];
|
||||
}
|
||||
put(chunk->x, chunk->z, REGION_LAYER_ENTITIES,
|
||||
std::move(data), entitiesData.size(), false);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ubyte[]> WorldRegions::getChunk(int x, int z){
|
||||
@ -398,8 +407,9 @@ std::unique_ptr<ubyte[]> WorldRegions::getChunk(int x, int z){
|
||||
std::unique_ptr<light_t[]> WorldRegions::getLights(int x, int z) {
|
||||
uint32_t size;
|
||||
auto* bytes = getData(x, z, REGION_LAYER_LIGHTS, size);
|
||||
if (bytes == nullptr)
|
||||
if (bytes == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto data = decompress(bytes, size, LIGHTMAP_DATA_LEN);
|
||||
return Lightmap::decode(data.get());
|
||||
}
|
||||
@ -412,7 +422,7 @@ chunk_inventories_map WorldRegions::fetchInventories(int x, int z) {
|
||||
return meta;
|
||||
}
|
||||
ByteReader reader(data, bytesSize);
|
||||
int count = reader.getInt32();
|
||||
auto count = reader.getInt32();
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint index = reader.getInt32();
|
||||
uint size = reader.getInt32();
|
||||
@ -425,6 +435,19 @@ chunk_inventories_map WorldRegions::fetchInventories(int x, int z) {
|
||||
return meta;
|
||||
}
|
||||
|
||||
dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) {
|
||||
uint32_t bytesSize;
|
||||
const ubyte* data = getData(x, z, REGION_LAYER_ENTITIES, bytesSize);
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto map = json::from_binary(data, bytesSize);
|
||||
if (map->size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) {
|
||||
if (getRegion(x, z, REGION_LAYER_VOXELS)) {
|
||||
throw std::runtime_error("not implemented for in-memory regions");
|
||||
@ -439,8 +462,9 @@ void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) {
|
||||
int gz = cz + z * REGION_SIZE;
|
||||
uint32_t length;
|
||||
auto data = readChunkData(gx, gz, length, regfile.get());
|
||||
if (data == nullptr)
|
||||
if (data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
data = decompress(data.get(), length, CHUNK_DATA_LEN);
|
||||
if (func(data.get())) {
|
||||
put(gx, gz, REGION_LAYER_VOXELS, std::move(data), CHUNK_DATA_LEN, true);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "../typedefs.hpp"
|
||||
#include "../util/BufferPool.hpp"
|
||||
#include "../voxels/Chunk.hpp"
|
||||
#include "../data/dynamic_fwd.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
@ -24,6 +25,7 @@ inline constexpr uint REGION_HEADER_SIZE = 10;
|
||||
inline constexpr uint REGION_LAYER_VOXELS = 0;
|
||||
inline constexpr uint REGION_LAYER_LIGHTS = 1;
|
||||
inline constexpr uint REGION_LAYER_INVENTORIES = 2;
|
||||
inline constexpr uint REGION_LAYER_ENTITIES = 3;
|
||||
|
||||
inline constexpr uint REGION_SIZE_BIT = 5;
|
||||
inline constexpr uint REGION_SIZE = (1 << (REGION_SIZE_BIT));
|
||||
@ -119,7 +121,7 @@ class WorldRegions {
|
||||
std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles;
|
||||
std::mutex regFilesMutex;
|
||||
std::condition_variable regFilesCv;
|
||||
RegionsLayer layers[3] {};
|
||||
RegionsLayer layers[4] {};
|
||||
util::BufferPool<ubyte> bufferPool {
|
||||
std::max(CHUNK_DATA_LEN, LIGHTMAP_DATA_LEN) * 2
|
||||
};
|
||||
@ -170,7 +172,7 @@ public:
|
||||
~WorldRegions();
|
||||
|
||||
/// @brief Put all chunk data to regions
|
||||
void put(Chunk* chunk);
|
||||
void put(Chunk* chunk, std::vector<ubyte> entitiesData);
|
||||
|
||||
/// @brief Store data in specified region
|
||||
/// @param x chunk.x
|
||||
@ -184,6 +186,7 @@ public:
|
||||
std::unique_ptr<ubyte[]> getChunk(int x, int z);
|
||||
std::unique_ptr<light_t[]> getLights(int x, int z);
|
||||
chunk_inventories_map fetchInventories(int x, int z);
|
||||
dynamic::Map_sptr fetchEntities(int x, int z);
|
||||
|
||||
void processRegionVoxels(int x, int z, const regionproc& func);
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
|
||||
builder.add("fov", &settings.camera.fov);
|
||||
builder.add("fov-effects", &settings.camera.fovEffects);
|
||||
builder.add("shaking", &settings.camera.shaking);
|
||||
builder.add("inertia", &settings.camera.inertia);
|
||||
|
||||
builder.section("chunks");
|
||||
builder.add("load-distance", &settings.chunks.loadDistance);
|
||||
|
||||
@ -14,11 +14,11 @@
|
||||
|
||||
ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) : content(content) {
|
||||
auto indices = content->getIndices();
|
||||
sideregions = std::make_unique<UVRegion[]>(indices->countBlockDefs() * 6);
|
||||
sideregions = std::make_unique<UVRegion[]>(indices->blocks.count() * 6);
|
||||
auto atlas = assets->get<Atlas>("blocks");
|
||||
|
||||
for (uint i = 0; i < indices->countBlockDefs(); i++) {
|
||||
Block* def = indices->getBlockDef(i);
|
||||
for (uint i = 0; i < indices->blocks.count(); i++) {
|
||||
Block* def = indices->blocks.get(i);
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
const std::string& tex = def->textureFaces[side];
|
||||
if (atlas->has(tex)) {
|
||||
|
||||
@ -22,7 +22,7 @@ LevelFrontend::LevelFrontend(LevelController* controller, Assets* assets)
|
||||
BlocksPreview::build(contentCache.get(), assets, level->content),
|
||||
"block-previews"
|
||||
);
|
||||
controller->getPlayerController()->listenBlockInteraction(
|
||||
controller->getBlocksController()->listenBlockInteraction(
|
||||
[=](Player*, glm::ivec3 pos, const Block* def, BlockInteraction type) {
|
||||
auto material = level->content->findBlockMaterial(def->material);
|
||||
if (material == nullptr) {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "../delegates.hpp"
|
||||
#include "../engine.hpp"
|
||||
#include "../settings.hpp"
|
||||
#include "../content/Content.hpp"
|
||||
#include "../graphics/core/Mesh.hpp"
|
||||
#include "../graphics/ui/elements/CheckBox.hpp"
|
||||
#include "../graphics/ui/elements/TextBox.hpp"
|
||||
@ -10,6 +11,8 @@
|
||||
#include "../graphics/render/WorldRenderer.hpp"
|
||||
#include "../logic/scripting/scripting.hpp"
|
||||
#include "../objects/Player.hpp"
|
||||
#include "../objects/Entities.hpp"
|
||||
#include "../objects/EntityDef.hpp"
|
||||
#include "../physics/Hitbox.hpp"
|
||||
#include "../util/stringutil.hpp"
|
||||
#include "../voxels/Block.hpp"
|
||||
@ -32,6 +35,7 @@ static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
|
||||
return label;
|
||||
}
|
||||
|
||||
// TODO: move to xml
|
||||
std::shared_ptr<UINode> create_debug_panel(
|
||||
Engine* engine,
|
||||
Level* level,
|
||||
@ -57,24 +61,24 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
fpsMin = fps;
|
||||
fpsMax = fps;
|
||||
});
|
||||
panel->add(create_label([](){ return L"fps: "+fpsString;}));
|
||||
panel->add(create_label([]() { return L"fps: "+fpsString;}));
|
||||
|
||||
panel->add(create_label([](){
|
||||
panel->add(create_label([]() {
|
||||
return L"meshes: " + std::to_wstring(Mesh::meshesCount);
|
||||
}));
|
||||
panel->add(create_label([](){
|
||||
panel->add(create_label([]() {
|
||||
int drawCalls = Mesh::drawCalls;
|
||||
Mesh::drawCalls = 0;
|
||||
return L"draw-calls: " + std::to_wstring(drawCalls);
|
||||
}));
|
||||
panel->add(create_label([](){
|
||||
panel->add(create_label([]() {
|
||||
return L"speakers: " + std::to_wstring(audio::count_speakers())+
|
||||
L" streams: " + std::to_wstring(audio::count_streams());
|
||||
}));
|
||||
panel->add(create_label([](){
|
||||
panel->add(create_label([]() {
|
||||
return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack());
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
panel->add(create_label([=]() {
|
||||
auto& settings = engine->getSettings();
|
||||
bool culling = settings.graphics.frustumCulling.get();
|
||||
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
|
||||
@ -83,7 +87,11 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
return L"chunks: "+std::to_wstring(level->chunks->chunksCount)+
|
||||
L" visible: "+std::to_wstring(level->chunks->visible);
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
panel->add(create_label([=]() {
|
||||
return L"entities: "+std::to_wstring(level->entities->size())+L" next: "+
|
||||
std::to_wstring(level->entities->peekNextID());
|
||||
}));
|
||||
panel->add(create_label([=]() {
|
||||
const auto& vox = player->selection.vox;
|
||||
std::wstringstream stream;
|
||||
stream << "r:" << vox.state.rotation << " s:"
|
||||
@ -96,9 +104,20 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
L" "+stream.str();
|
||||
}
|
||||
}));
|
||||
panel->add(create_label([=]() {
|
||||
auto eid = player->getSelectedEntity();
|
||||
if (eid == ENTITY_NONE) {
|
||||
return std::wstring {L"entity: -"};
|
||||
} else if (auto entity = level->entities->get(eid)) {
|
||||
return L"entity: "+util::str2wstr_utf8(entity->getDef().name)+
|
||||
L" uid: "+std::to_wstring(entity->getUID());
|
||||
} else {
|
||||
return std::wstring {L"entity: error (invalid UID)"};
|
||||
}
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
auto* indices = level->content->getIndices();
|
||||
if (auto def = indices->getBlockDef(player->selection.vox.id)) {
|
||||
if (auto def = indices->blocks.get(player->selection.vox.id)) {
|
||||
return L"name: " + util::str2wstr_utf8(def->name);
|
||||
} else {
|
||||
return std::wstring {L"name: void"};
|
||||
@ -123,20 +142,18 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
auto box = std::make_shared<TextBox>(L"");
|
||||
auto boxRef = box.get();
|
||||
box->setTextSupplier([=]() {
|
||||
Hitbox* hitbox = player->hitbox.get();
|
||||
return util::to_wstring(hitbox->position[ax], 2);
|
||||
return util::to_wstring(player->getPosition()[ax], 2);
|
||||
});
|
||||
box->setTextConsumer([=](const std::wstring& text) {
|
||||
try {
|
||||
glm::vec3 position = player->hitbox->position;
|
||||
glm::vec3 position = player->getPosition();
|
||||
position[ax] = std::stoi(text);
|
||||
player->teleport(position);
|
||||
} catch (std::exception& _){
|
||||
}
|
||||
});
|
||||
box->setOnEditStart([=](){
|
||||
Hitbox* hitbox = player->hitbox.get();
|
||||
boxRef->setText(std::to_wstring(int(hitbox->position[ax])));
|
||||
boxRef->setText(std::to_wstring(static_cast<int>(player->getPosition()[ax])));
|
||||
});
|
||||
box->setSize(glm::vec2(230, 27));
|
||||
|
||||
@ -176,6 +193,18 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
{
|
||||
auto checkbox = std::make_shared<FullCheckBox>(
|
||||
L"Show Hitboxes", glm::vec2(400, 24)
|
||||
);
|
||||
checkbox->setSupplier([=]() {
|
||||
return WorldRenderer::showEntitiesDebug;
|
||||
});
|
||||
checkbox->setConsumer([=](bool checked) {
|
||||
WorldRenderer::showEntitiesDebug = checked;
|
||||
});
|
||||
panel->add(checkbox);
|
||||
}
|
||||
panel->refresh();
|
||||
return panel;
|
||||
}
|
||||
|
||||
@ -102,9 +102,9 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
||||
auto indices = content->getIndices();
|
||||
auto inventory = player->getInventory();
|
||||
|
||||
int itemsCount = indices->countItemDefs();
|
||||
size_t itemsCount = indices->items.count();
|
||||
auto accessInventory = std::make_shared<Inventory>(0, itemsCount);
|
||||
for (int id = 1; id < itemsCount; id++) {
|
||||
for (size_t id = 1; id < itemsCount; id++) {
|
||||
accessInventory->getSlot(id-1).set(ItemStack(id, 1));
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "../../debug/Logger.hpp"
|
||||
#include "../../engine.hpp"
|
||||
#include "../../files/files.hpp"
|
||||
#include "../../content/Content.hpp"
|
||||
#include "../../graphics/core/DrawContext.hpp"
|
||||
#include "../../graphics/core/ImageData.hpp"
|
||||
#include "../../graphics/core/PostProcessing.hpp"
|
||||
@ -98,7 +99,7 @@ void LevelScreen::saveWorldPreview() {
|
||||
Viewport viewport(previewSize * 1.5, previewSize);
|
||||
DrawContext ctx(&pctx, viewport, batch.get());
|
||||
|
||||
worldRenderer->draw(ctx, &camera, false, postProcessing.get());
|
||||
worldRenderer->draw(ctx, &camera, false, true, postProcessing.get());
|
||||
auto image = postProcessing->toImage();
|
||||
image->flipY();
|
||||
imageio::write(paths->resolve("world:preview.png").u8string(), image.get());
|
||||
@ -136,9 +137,13 @@ void LevelScreen::update(float delta) {
|
||||
bool paused = hud->isPause();
|
||||
audio::get_channel("regular")->setPaused(paused);
|
||||
audio::get_channel("ambient")->setPaused(paused);
|
||||
glm::vec3 velocity {};
|
||||
if (auto hitbox = player->getHitbox()) {
|
||||
velocity = hitbox->velocity;
|
||||
}
|
||||
audio::set_listener(
|
||||
camera->position-camera->dir,
|
||||
player->hitbox->velocity,
|
||||
velocity,
|
||||
camera->dir,
|
||||
glm::vec3(0, 1, 0)
|
||||
);
|
||||
@ -157,7 +162,7 @@ void LevelScreen::draw(float) {
|
||||
Viewport viewport(Window::width, Window::height);
|
||||
DrawContext ctx(nullptr, viewport, batch.get());
|
||||
|
||||
worldRenderer->draw(ctx, camera.get(), hudVisible, postProcessing.get());
|
||||
worldRenderer->draw(ctx, camera.get(), hudVisible, hud->isPause(), postProcessing.get());
|
||||
|
||||
if (hudVisible) {
|
||||
hud->draw(ctx);
|
||||
|
||||
@ -123,7 +123,7 @@ std::unique_ptr<Atlas> BlocksPreview::build(
|
||||
const Content* content
|
||||
) {
|
||||
auto indices = content->getIndices();
|
||||
size_t count = indices->countBlockDefs();
|
||||
size_t count = indices->blocks.count();
|
||||
size_t iconSize = ITEM_ICON_SIZE;
|
||||
|
||||
auto shader = assets->get<Shader>("ui3d");
|
||||
@ -153,7 +153,7 @@ std::unique_ptr<Atlas> BlocksPreview::build(
|
||||
|
||||
fbo.bind();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto def = indices->getBlockDef(i);
|
||||
auto def = indices->blocks.get(i);
|
||||
atlas->getTexture()->bind();
|
||||
builder.add(def->name, draw(cache, shader, &fbo, &batch, def, iconSize));
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ BlocksRenderer::BlocksRenderer(
|
||||
CHUNK_W + voxelBufferPadding*2,
|
||||
CHUNK_H,
|
||||
CHUNK_D + voxelBufferPadding*2);
|
||||
blockDefsCache = content->getIndices()->getBlockDefs();
|
||||
blockDefsCache = content->getIndices()->blocks.getDefs();
|
||||
}
|
||||
|
||||
BlocksRenderer::~BlocksRenderer() {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "../core/Mesh.hpp"
|
||||
#include "../core/Model.hpp"
|
||||
#include "../core/Atlas.hpp"
|
||||
#include "../core/Texture.hpp"
|
||||
#include "../../assets/Assets.hpp"
|
||||
#include "../../window/Window.hpp"
|
||||
@ -53,16 +54,18 @@ ModelBatch::ModelBatch(size_t capacity, Assets* assets, Chunks* chunks)
|
||||
ModelBatch::~ModelBatch() {
|
||||
}
|
||||
|
||||
void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, const glm::mat3& rotation) {
|
||||
void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix,
|
||||
const glm::mat3& rotation,
|
||||
const texture_names_map* varTextures) {
|
||||
glm::vec3 gpos = matrix * glm::vec4(glm::vec3(), 1.0f);
|
||||
light_t light = chunks->getLight(gpos.x, gpos.y, gpos.z);
|
||||
light_t light = chunks->getLight(floor(gpos.x), floor(gpos.y), floor(gpos.z));
|
||||
glm::vec4 lights (
|
||||
Lightmap::extract(light, 0) / 15.0f,
|
||||
Lightmap::extract(light, 1) / 15.0f,
|
||||
Lightmap::extract(light, 2) / 15.0f,
|
||||
Lightmap::extract(light, 3) / 15.0f
|
||||
);
|
||||
setTexture(assets->get<Texture>(mesh.texture));
|
||||
setTexture(mesh.texture, varTextures);
|
||||
size_t vcount = mesh.vertices.size();
|
||||
const auto& vertexData = mesh.vertices.data();
|
||||
for (size_t i = 0; i < vcount / 3; i++) {
|
||||
@ -70,7 +73,7 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, const gl
|
||||
flush();
|
||||
}
|
||||
for (size_t j = 0; j < 3; j++) {
|
||||
const auto& vert = vertexData[i * 3 + j];
|
||||
const auto vert = vertexData[i * 3 + j];
|
||||
auto norm = rotation * vert.normal;
|
||||
float d = glm::dot(norm, SUN_VECTOR);
|
||||
d = 0.8f + d * 0.2f;
|
||||
@ -81,9 +84,10 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, const gl
|
||||
}
|
||||
}
|
||||
|
||||
void ModelBatch::draw(const model::Model* model) {
|
||||
void ModelBatch::draw(const model::Model* model,
|
||||
const texture_names_map* varTextures) {
|
||||
for (const auto& mesh : model->meshes) {
|
||||
entries.push_back({combined, rotation, &mesh});
|
||||
entries.push_back({combined, rotation, &mesh, varTextures});
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +98,7 @@ void ModelBatch::render() {
|
||||
}
|
||||
);
|
||||
for (auto& entry : entries) {
|
||||
draw(*entry.mesh, entry.matrix, entry.rotation);
|
||||
draw(*entry.mesh, entry.matrix, entry.rotation, entry.varTextures);
|
||||
}
|
||||
flush();
|
||||
entries.clear();
|
||||
@ -114,6 +118,34 @@ void ModelBatch::box(glm::vec3 pos, glm::vec3 size, glm::vec4 lights) {
|
||||
plane(pos-X*size, Z*size, Y*size, -X, lights);
|
||||
}
|
||||
|
||||
void ModelBatch::setTexture(const std::string& name,
|
||||
const texture_names_map* varTextures) {
|
||||
if (name.at(0) == '$') {
|
||||
const auto& found = varTextures->find(name);
|
||||
if (found == varTextures->end()) {
|
||||
return setTexture(nullptr);
|
||||
} else {
|
||||
return setTexture(found->second, varTextures);
|
||||
}
|
||||
}
|
||||
size_t sep = name.find(':');
|
||||
if (sep == std::string::npos) {
|
||||
setTexture(assets->get<Texture>(name));
|
||||
} else {
|
||||
auto atlas = assets->get<Atlas>(name.substr(0, sep));
|
||||
if (atlas == nullptr) {
|
||||
setTexture(nullptr);
|
||||
} else {
|
||||
setTexture(atlas->getTexture());
|
||||
if (auto reg = atlas->getIf(name.substr(sep+1))) {
|
||||
region = *reg;
|
||||
} else {
|
||||
setTexture("blocks:notfound", varTextures);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelBatch::setTexture(Texture* texture) {
|
||||
if (texture == nullptr) {
|
||||
texture = blank.get();
|
||||
@ -122,6 +154,7 @@ void ModelBatch::setTexture(Texture* texture) {
|
||||
flush();
|
||||
}
|
||||
this->texture = texture;
|
||||
region = UVRegion {0.0f, 0.0f, 1.0f, 1.0f};
|
||||
}
|
||||
|
||||
void ModelBatch::flush() {
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
#ifndef GRAPHICS_RENDER_MODEL_BATCH_HPP_
|
||||
#define GRAPHICS_RENDER_MODEL_BATCH_HPP_
|
||||
|
||||
#include "../../maths/UVRegion.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
class Mesh;
|
||||
class Texture;
|
||||
@ -15,6 +19,8 @@ namespace model {
|
||||
struct Model;
|
||||
}
|
||||
|
||||
using texture_names_map = std::unordered_map<std::string, std::string>;
|
||||
|
||||
class ModelBatch {
|
||||
std::unique_ptr<float[]> const buffer;
|
||||
size_t const capacity;
|
||||
@ -30,6 +36,7 @@ class ModelBatch {
|
||||
Assets* assets;
|
||||
Chunks* chunks;
|
||||
Texture* texture = nullptr;
|
||||
UVRegion region {0.0f, 0.0f, 1.0f, 1.0f};
|
||||
|
||||
static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f};
|
||||
|
||||
@ -40,8 +47,8 @@ class ModelBatch {
|
||||
buffer[index++] = pos.x;
|
||||
buffer[index++] = pos.y;
|
||||
buffer[index++] = pos.z;
|
||||
buffer[index++] = uv.x;
|
||||
buffer[index++] = uv.y;
|
||||
buffer[index++] = uv.x * region.getWidth() + region.u1;
|
||||
buffer[index++] = uv.y * region.getHeight() + region.v1;
|
||||
|
||||
union {
|
||||
float floating;
|
||||
@ -72,8 +79,11 @@ class ModelBatch {
|
||||
vertex(pos-right+up, {0,1}, color);
|
||||
}
|
||||
|
||||
void draw(const model::Mesh& mesh, const glm::mat4& matrix, const glm::mat3& rotation);
|
||||
void draw(const model::Mesh& mesh, const glm::mat4& matrix,
|
||||
const glm::mat3& rotation, const texture_names_map* varTextures);
|
||||
void box(glm::vec3 pos, glm::vec3 size, glm::vec4 lights);
|
||||
void setTexture(const std::string& name,
|
||||
const texture_names_map* varTextures);
|
||||
void setTexture(Texture* texture);
|
||||
void flush();
|
||||
|
||||
@ -81,6 +91,7 @@ class ModelBatch {
|
||||
glm::mat4 matrix;
|
||||
glm::mat3 rotation;
|
||||
const model::Mesh* mesh;
|
||||
const texture_names_map* varTextures;
|
||||
};
|
||||
std::vector<DrawEntry> entries;
|
||||
public:
|
||||
@ -93,7 +104,8 @@ public:
|
||||
|
||||
void pushMatrix(glm::mat4 matrix);
|
||||
void popMatrix();
|
||||
void draw(const model::Model* model);
|
||||
void draw(const model::Model* model,
|
||||
const texture_names_map* varTextures);
|
||||
|
||||
void render();
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user