Merge branch 'instancing'
3
SCsub
@ -33,6 +33,8 @@ voxel_files = [
|
||||
"util/noise/fast_noise_lite_gradient.cpp",
|
||||
|
||||
"terrain/*.cpp",
|
||||
"terrain/instancing/*.cpp",
|
||||
|
||||
"server/*.cpp",
|
||||
"math/*.cpp",
|
||||
"edition/*.cpp",
|
||||
@ -48,6 +50,7 @@ if env["tools"]:
|
||||
"editor/graph/*.cpp",
|
||||
"editor/terrain/*.cpp",
|
||||
"editor/fast_noise_lite/*.cpp",
|
||||
"editor/instance_library/*.cpp",
|
||||
]
|
||||
voxel_files += voxel_editor_files
|
||||
|
||||
|
@ -17,6 +17,9 @@ def get_doc_classes():
|
||||
"Voxel",
|
||||
"VoxelLibrary",
|
||||
"VoxelColorPalette",
|
||||
"VoxelInstanceLibrary",
|
||||
"VoxelInstanceLibraryItem",
|
||||
"VoxelInstanceGenerator",
|
||||
|
||||
"VoxelBuffer",
|
||||
|
||||
@ -24,11 +27,13 @@ def get_doc_classes():
|
||||
"VoxelTerrain",
|
||||
"VoxelLodTerrain",
|
||||
"VoxelViewer",
|
||||
"VoxelInstancer",
|
||||
|
||||
"VoxelStream",
|
||||
"VoxelStreamFile",
|
||||
"VoxelStreamBlockFiles",
|
||||
"VoxelStreamRegionFiles",
|
||||
"VoxelStreamSQLite",
|
||||
"VoxelStreamScript",
|
||||
|
||||
"VoxelGenerator",
|
||||
|
@ -12,16 +12,16 @@
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="out_buffer" type="VoxelBuffer">
|
||||
Buffer in which voxel data will be generated. It should not be [code]null[/code] and should be given the requested size.
|
||||
</argument>
|
||||
<argument index="1" name="origin_in_voxels" type="Vector3">
|
||||
Coordinates of the lower corner of the box to generate, relative to LOD0.
|
||||
</argument>
|
||||
<argument index="2" name="lod" type="int">
|
||||
Level of detail index to use for this block. Some generators might not support LOD, in which case it can be left 0.
|
||||
</argument>
|
||||
<description>
|
||||
Generates a block of voxels within the specified world area.
|
||||
[code]out_buffer[/code]: Buffer in which voxel data will be generated. It should not be [code]null[/code] and should be given the requested size. Do not keep a reference on it after the call.
|
||||
[code]origin_in_voxels[/code]: Coordinates of the lower corner of the box to generate, relative to LOD0.
|
||||
[code]lod[/code]: Level of detail index to use for this block. Some generators might not support LOD, in which case it can be left 0.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
@ -13,23 +13,22 @@
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="out_buffer" type="VoxelBuffer">
|
||||
Buffer in which to populate voxel data. It will never be [code]null[/code] and will have the requested size. It is only valid for this function, do not store it anywhere after the end.
|
||||
</argument>
|
||||
<argument index="1" name="origin_in_voxels" type="Vector3">
|
||||
Coordinates of the lower corner of the box to generate, relative to LOD0. The size of the box is known from [code]out_buffer[/code].
|
||||
</argument>
|
||||
<argument index="2" name="lod" type="int">
|
||||
Level of detail index to use for this block. It can be ignored if you don't use LOD.
|
||||
This may be used as a power of two, telling how big is one voxel. For example, if you use a loop to fill the buffer using noise, you should sample that noise at steps of 2^lod, starting from [code]origin_in_voxels[/code] (in code you can use [code]1 << lod[/code] for fast computation, instead of [code]pow(2, lod)[/code]). You may want to separate variables that iterate the coordinates in [code]out_buffer[/code] and variables used to generate voxel values in space.
|
||||
</argument>
|
||||
<description>
|
||||
[code]out_buffer[/code]: Buffer in which to populate voxel data. It will never be [code]null[/code] and will have the requested size. It is only valid for this function, do not store it anywhere after the end.
|
||||
[code]origin_in_voxels[/code]: Coordinates of the lower corner of the box to generate, relative to LOD0. The size of the box is known from [code]out_buffer[/code].
|
||||
[code]lod[/code]: Level of detail index to use for this block. It can be ignored if you don't use LOD. This may be used as a power of two, telling how big is one voxel. For example, if you use a loop to fill the buffer using noise, you should sample that noise at steps of 2^lod, starting from [code]origin_in_voxels[/code] (in code you can use [code]1 << lod[/code] for fast computation, instead of [code]pow(2, lod)[/code]). You may want to separate variables that iterate the coordinates in [code]out_buffer[/code] and variables used to generate voxel values in space.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_used_channels_mask" qualifiers="virtual">
|
||||
<return type="int">
|
||||
</return>
|
||||
<description>
|
||||
Use this to indicate which channels your generator will use. It returns a bitmask, so for example you may provide information like this: [code](1 << channel1) | (1 << channel2)[/code]
|
||||
Use this to indicate which channels your generator will use. It returns a bitmask, so for example you may provide information like this: [code](1 << channel1) | (1 << channel2)[/code]
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
52
doc/classes/VoxelInstanceGenerator.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelInstanceGenerator" inherits="Resource" version="3.2">
|
||||
<brief_description>
|
||||
Decides where to spawn instances on top of a voxel surface.
|
||||
</brief_description>
|
||||
<description>
|
||||
Generates the necessry information to spawn instances on top of a voxel surface. This may be used by a [VoxelInstancer].
|
||||
Note: to generate voxels, see [VoxelGenerator].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="density" type="float" setter="set_density" getter="get_density" default="0.1">
|
||||
</member>
|
||||
<member name="emit_mode" type="int" setter="set_emit_mode" getter="get_emit_mode" enum="VoxelInstanceGenerator.EmitMode" default="0">
|
||||
</member>
|
||||
<member name="max_height" type="float" setter="set_max_height" getter="get_max_height" default="3.40282e+38">
|
||||
</member>
|
||||
<member name="max_scale" type="float" setter="set_max_scale" getter="get_max_scale" default="1.0">
|
||||
</member>
|
||||
<member name="max_slope_degrees" type="float" setter="set_max_slope_degrees" getter="get_max_slope_degrees" default="180.0">
|
||||
</member>
|
||||
<member name="min_height" type="float" setter="set_min_height" getter="get_min_height" default="1.17549e-38">
|
||||
</member>
|
||||
<member name="min_scale" type="float" setter="set_min_scale" getter="get_min_scale" default="1.0">
|
||||
</member>
|
||||
<member name="min_slope_degrees" type="float" setter="set_min_slope_degrees" getter="get_min_slope_degrees" default="0.0">
|
||||
</member>
|
||||
<member name="noise" type="FastNoiseLite" setter="set_noise" getter="get_noise">
|
||||
</member>
|
||||
<member name="noise_dimension" type="int" setter="set_noise_dimension" getter="get_noise_dimension" enum="VoxelInstanceGenerator.Dimension" default="1">
|
||||
</member>
|
||||
<member name="noise_on_scale" type="float" setter="set_noise_on_scale" getter="get_noise_on_scale" default="0.0">
|
||||
</member>
|
||||
<member name="offset_along_normal" type="float" setter="set_offset_along_normal" getter="get_offset_along_normal" default="0.0">
|
||||
</member>
|
||||
<member name="random_vertical_flip" type="bool" setter="set_random_vertical_flip" getter="get_random_vertical_flip" default="false">
|
||||
</member>
|
||||
<member name="scale_distribution" type="int" setter="set_scale_distribution" getter="get_scale_distribution" enum="VoxelInstanceGenerator.Distribution" default="1">
|
||||
</member>
|
||||
<member name="vertical_alignment" type="float" setter="set_vertical_alignment" getter="get_vertical_alignment" default="1.0">
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="EMIT_FROM_VERTICES" value="0" enum="EmitMode">
|
||||
</constant>
|
||||
<constant name="EMIT_FROM_FACES" value="1" enum="EmitMode">
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
56
doc/classes/VoxelInstanceLibrary.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelInstanceLibrary" inherits="Resource" version="3.2">
|
||||
<brief_description>
|
||||
Contains a list of models that can be used by [VoxelInstancer], associated with a unique ID
|
||||
</brief_description>
|
||||
<description>
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="add_item">
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="id" type="int">
|
||||
</argument>
|
||||
<argument index="1" name="arg1" type="VoxelInstanceLibraryItem">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="clear">
|
||||
<return type="void">
|
||||
</return>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="find_item_by_name" qualifiers="const">
|
||||
<return type="int">
|
||||
</return>
|
||||
<argument index="0" name="name" type="String">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_item">
|
||||
<return type="VoxelInstanceLibraryItem">
|
||||
</return>
|
||||
<argument index="0" name="id" type="int">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_item">
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="id" type="int">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<constants>
|
||||
<constant name="MAX_ID" value="65535">
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
80
doc/classes/VoxelInstanceLibraryItem.xml
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelInstanceLibraryItem" inherits="Resource" version="3.2">
|
||||
<brief_description>
|
||||
Settings for a model that can be used by [VoxelInstancer]
|
||||
</brief_description>
|
||||
<description>
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_collision_shapes" qualifiers="const">
|
||||
<return type="Array">
|
||||
</return>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_mesh" qualifiers="const">
|
||||
<return type="Mesh">
|
||||
</return>
|
||||
<argument index="0" name="mesh_lod_index" type="int">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_collision_shapes">
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="shape_infos" type="Array">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_mesh">
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="mesh" type="Mesh">
|
||||
</argument>
|
||||
<argument index="1" name="mesh_lod_index" type="int">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="setup_from_template">
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="node" type="Node">
|
||||
</argument>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
|
||||
</member>
|
||||
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
|
||||
</member>
|
||||
<member name="generator" type="VoxelInstanceGenerator" setter="set_generator" getter="get_generator">
|
||||
</member>
|
||||
<member name="lod_index" type="int" setter="set_lod_index" getter="get_lod_index" default="0">
|
||||
</member>
|
||||
<member name="material_override" type="Material" setter="set_material_override" getter="get_material_override">
|
||||
</member>
|
||||
<member name="mesh" type="Mesh" setter="_set_mesh_lod0" getter="_get_mesh_lod0">
|
||||
</member>
|
||||
<member name="mesh_lod1" type="Mesh" setter="_set_mesh_lod1" getter="_get_mesh_lod1">
|
||||
</member>
|
||||
<member name="mesh_lod2" type="Mesh" setter="_set_mesh_lod2" getter="_get_mesh_lod2">
|
||||
</member>
|
||||
<member name="mesh_lod3" type="Mesh" setter="_set_mesh_lod3" getter="_get_mesh_lod3">
|
||||
</member>
|
||||
<member name="name" type="String" setter="set_item_name" getter="get_item_name" default="""">
|
||||
</member>
|
||||
<member name="persistent" type="bool" setter="set_persistent" getter="is_persistent" default="false">
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="MAX_MESH_LODS" value="4">
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
33
doc/classes/VoxelInstancer.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelInstancer" inherits="Spatial" version="3.2">
|
||||
<brief_description>
|
||||
Spawns items on top of voxel surfaces.
|
||||
</brief_description>
|
||||
<description>
|
||||
Add-on to voxel nodes, allowing to spawn elements on the surface. These elements are rendered with hardware instancing, can have collisions, and also be persistent.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="debug_get_block_count" qualifiers="const">
|
||||
<return type="int">
|
||||
</return>
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="library" type="VoxelInstanceLibrary" setter="set_library" getter="get_library">
|
||||
</member>
|
||||
<member name="up_mode" type="int" setter="set_up_mode" getter="get_up_mode" enum="VoxelInstancer.UpMode" default="0">
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="MAX_LOD" value="8">
|
||||
</constant>
|
||||
<constant name="UP_MODE_POSITIVE_Y" value="0" enum="UpMode">
|
||||
</constant>
|
||||
<constant name="UP_MODE_SPHERE" value="1" enum="UpMode">
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
@ -36,13 +36,13 @@
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="buffer" type="VoxelBuffer">
|
||||
Block of voxels to save. It is strongly recommended to not keep a reference to that data afterward, because streams are allowed to cache it, and saved data must represent either snapshots (copies) or last references to the data after the volume they belonged to is destroyed.
|
||||
</argument>
|
||||
<argument index="1" name="origin_in_voxels" type="Vector3">
|
||||
</argument>
|
||||
<argument index="2" name="lod" type="int">
|
||||
</argument>
|
||||
<description>
|
||||
[code]buffer[/code]: Block of voxels to save. It is strongly recommended to not keep a reference to that data afterward, because streams are allowed to cache it, and saved data must represent either snapshots (copies) or last references to the data after the volume they belonged to is destroyed.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelStreamBlockFiles" inherits="VoxelStreamFile" version="3.2">
|
||||
<class name="VoxelStreamBlockFiles" inherits="VoxelStream" version="3.2">
|
||||
<brief_description>
|
||||
Loads and saves blocks as individual files under a directory.
|
||||
</brief_description>
|
||||
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelStreamFile" inherits="VoxelStream" version="3.2">
|
||||
<brief_description>
|
||||
</brief_description>
|
||||
<description>
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
</methods>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelStreamRegionFiles" inherits="VoxelStreamFile" version="3.2">
|
||||
<class name="VoxelStreamRegionFiles" inherits="VoxelStream" version="3.2">
|
||||
<brief_description>
|
||||
Loads and saves blocks to region files indexed by world position, under a directory.
|
||||
</brief_description>
|
||||
|
18
doc/classes/VoxelStreamSQLite.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VoxelStreamSQLite" inherits="VoxelStream" version="3.2">
|
||||
<brief_description>
|
||||
Saves voxel data into a single SQLite database file.
|
||||
</brief_description>
|
||||
<description>
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="database_path" type="String" setter="set_database_path" getter="get_database_path" default="""">
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
@ -12,13 +12,13 @@
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="out_buffer" type="VoxelBuffer">
|
||||
Buffer in which to populate voxel data. It will never be [code]null[/code] and will have the requested size. It is only valid for this function, do not store it anywhere after the end.
|
||||
</argument>
|
||||
<argument index="1" name="origin_in_voxels" type="Vector3">
|
||||
</argument>
|
||||
<argument index="2" name="lod" type="int">
|
||||
</argument>
|
||||
<description>
|
||||
[code]out_buffer[/code]: Buffer in which to populate voxel data. It will never be [code]null[/code] and will have the requested size. It is only valid for this function, do not store it anywhere after the end.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_used_channels_mask" qualifiers="virtual">
|
||||
@ -31,13 +31,13 @@
|
||||
<return type="void">
|
||||
</return>
|
||||
<argument index="0" name="buffer" type="VoxelBuffer">
|
||||
Buffer of voxel data to save. It is allowed to keep a reference to it for caching purposes, as saved data will either be snapshots or only references left after removal of a volume.
|
||||
</argument>
|
||||
<argument index="1" name="origin_in_voxels" type="Vector3">
|
||||
</argument>
|
||||
<argument index="2" name="lod" type="int">
|
||||
</argument>
|
||||
<description>
|
||||
[code]buffer[/code]: Buffer of voxel data to save. It is allowed to keep a reference to it for caching purposes, as saved data will either be snapshots or only references left after removal of a volume.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
@ -138,7 +138,7 @@
|
||||
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="VoxelTool.Mode" default="0">
|
||||
Sets how [code]do_*[/code] functions will behave. This may vary depending on the channel.
|
||||
</member>
|
||||
<member name="sdf_scale" type="float" setter="set_sdf_scale" getter="get_sdf_scale" default="0.1">
|
||||
<member name="sdf_scale" type="float" setter="set_sdf_scale" getter="get_sdf_scale" default="0.002">
|
||||
When working with smooth voxels, applies a scale to the signed distance field. A high scale (1 or higher) will tend to produce blocky results, and a low scale (below 1, but not too close to zero) will tend to be smoother.
|
||||
|
||||
This is related to the [enum VoxelBuffer.Depth] configuration on voxels. For 8-bit and 16-bit, there is a limited range of values the Signed Distance Field can take, and by default it is clamped to -1..1, so the gradient can only range across 2 voxels. But when LOD is used, it is better to stretch that range over a longer distance, and this is achieved by scaling SDF values.
|
||||
|
@ -16,6 +16,7 @@ nav:
|
||||
- 'smooth_terrain.md'
|
||||
- 'generators.md'
|
||||
- 'streams.md'
|
||||
- 'instancing.md'
|
||||
- 'editor.md'
|
||||
- 'scripting.md'
|
||||
- 'access_to_voxels_and_multithreading.md'
|
||||
@ -40,6 +41,10 @@ nav:
|
||||
- api/VoxelGeneratorNoise2D.md
|
||||
- api/VoxelGeneratorScript.md
|
||||
- api/VoxelGeneratorWaves.md
|
||||
- api/VoxelInstanceGenerator.md
|
||||
- api/VoxelInstanceLibrary.md
|
||||
- api/VoxelInstanceLibraryItem.md
|
||||
- api/VoxelInstancer.md
|
||||
- api/VoxelLibrary.md
|
||||
- api/VoxelLodTerrain.md
|
||||
- api/VoxelMesher.md
|
||||
@ -52,8 +57,8 @@ nav:
|
||||
- api/VoxelServer.md
|
||||
- api/VoxelStream.md
|
||||
- api/VoxelStreamBlockFiles.md
|
||||
- api/VoxelStreamFile.md
|
||||
- api/VoxelStreamRegionFiles.md
|
||||
- api/VoxelStreamSQLite.md
|
||||
- api/VoxelStreamScript.md
|
||||
- api/VoxelTerrain.md
|
||||
- api/VoxelTool.md
|
||||
@ -65,6 +70,7 @@ nav:
|
||||
- 'Serialization formats':
|
||||
- 'specs/block_format_v1.md'
|
||||
- 'specs/block_format_v2.md'
|
||||
- 'specs/instances_format.md'
|
||||
- 'specs/region_format_v2.md'
|
||||
- 'specs/region_format_v3.md'
|
||||
- 'specs/sqlite_format.md'
|
||||
|
@ -3,7 +3,7 @@
|
||||
Inherits: [Resource](https://docs.godotengine.org/en/stable/classes/class_resource.html)
|
||||
|
||||
|
||||
Generates coherent and fractal noise using the FastNoiseLite library.
|
||||
Generates coherent and fractal noise using the [url=https://github.com/Auburn/FastNoise](https://docs.godotengine.org/en/stable/classes/class_url=https://github.com/auburn/fastnoise.html)FastNoiseLite[/url](https://docs.godotengine.org/en/stable/classes/class_/url.html) library.
|
||||
|
||||
## Properties:
|
||||
|
||||
@ -128,17 +128,13 @@ enum **CellularReturnType**:
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_get_noise_2d"></span> **get_noise_2d**( [float](https://docs.godotengine.org/en/stable/classes/class_float.html) x, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) y )
|
||||
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_get_noise_2dv"></span> **get_noise_2dv**( [Vector2](https://docs.godotengine.org/en/stable/classes/class_vector2.html) position )
|
||||
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_get_noise_3d"></span> **get_noise_3d**( [float](https://docs.godotengine.org/en/stable/classes/class_float.html) x, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) y, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) z )
|
||||
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_get_noise_3dv"></span> **get_noise_3dv**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) position )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -3,7 +3,7 @@
|
||||
Inherits: [Resource](https://docs.godotengine.org/en/stable/classes/class_resource.html)
|
||||
|
||||
|
||||
Generates coherent and fractal noise gradients using the FastNoiseLite library.
|
||||
Generates coherent and fractal noise gradients using the [url=https://github.com/Auburn/FastNoise](https://docs.godotengine.org/en/stable/classes/class_url=https://github.com/auburn/fastnoise.html)FastNoiseLite[/url](https://docs.godotengine.org/en/stable/classes/class_/url.html) library.
|
||||
|
||||
## Properties:
|
||||
|
||||
@ -71,4 +71,4 @@ enum **RotationType3D**:
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_seed"></span> **seed** = 0
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -76,9 +76,11 @@ enum **Side**:
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_material_id"></span> **material_id** = 0
|
||||
|
||||
ID of the material that will be used. It corresponds to the index of materials found on [VoxelTerrain](VoxelTerrain.md).
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_random_tickable"></span> **random_tickable** = false
|
||||
|
||||
If enabled, voxels having this ID in the TYPE channel will be used by method VoxelToolTerrain.run_blocky_random_tick.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_transparency_index"></span> **transparency_index** = 0
|
||||
@ -88,6 +90,7 @@ If enabled, voxels having this ID in the TYPE channel will be used by method Vox
|
||||
|
||||
|
||||
- [String](https://docs.godotengine.org/en/stable/classes/class_string.html)<span id="i_voxel_name"></span> **voxel_name** = ""
|
||||
|
||||
Name that can be used for convenience, when looking up a specific [Voxel](Voxel.md) from [VoxelLibrary](VoxelLibrary.md).
|
||||
|
||||
## Method Descriptions
|
||||
@ -95,13 +98,10 @@ Name that can be used for convenience, when looking up a specific [Voxel](Voxel.
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_id"></span> **get_id**( )
|
||||
|
||||
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_is_empty"></span> **is_empty**( )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_id"></span> **set_id**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -50,6 +50,6 @@ Reads the data of a [VoxelBuffer](VoxelBuffer.md) from a [StreamPeer](https://do
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_serialize"></span> **serialize**( [StreamPeer](https://docs.godotengine.org/en/stable/classes/class_streampeer.html) peer, [VoxelBuffer](VoxelBuffer.md) voxel_buffer, [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html) compress )
|
||||
|
||||
Stores the data of a [VoxelBuffer](VoxelBuffer.md) into a [StreamPeer](https://docs.godotengine.org/en/stable/classes/class_streampeer.html). To be able to read it back, you should use the
|
||||
Stores the data of a [VoxelBuffer](VoxelBuffer.md) into a [StreamPeer](https://docs.godotengine.org/en/stable/classes/class_streampeer.html).
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -33,7 +33,6 @@ Return |
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_collision_mask"></span> **get_collision_mask**( )
|
||||
|
||||
|
||||
|
||||
- [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html)<span id="i_get_motion"></span> **get_motion**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) motion, [AABB](https://docs.godotengine.org/en/stable/classes/class_aabb.html) aabb, [Node](https://docs.godotengine.org/en/stable/classes/class_node.html) terrain )
|
||||
|
||||
Given a motion vector, returns a modified vector telling you by how much to move your character. This is similar to method KinematicBody.move_and_slide, except you have to apply the movement.
|
||||
@ -41,5 +40,4 @@ Given a motion vector, returns a modified vector telling you by how much to move
|
||||
- [void](#)<span id="i_set_collision_mask"></span> **set_collision_mask**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) mask )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -198,7 +198,6 @@ Checks if every voxel within a channel has the same value.
|
||||
- [void](#)<span id="i_optimize"></span> **optimize**( )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_block_metadata"></span> **set_block_metadata**( [Variant](https://docs.godotengine.org/en/stable/classes/class_variant.html) meta )
|
||||
|
||||
Sets arbitrary data on this buffer. Old data is replaced. Note, this is separate storage from per-voxel metadata.
|
||||
@ -226,5 +225,4 @@ If this [VoxelBuffer](VoxelBuffer.md) is saved, this metadata will also be saved
|
||||
- [void](#)<span id="i_set_voxel_v"></span> **set_voxel_v**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) value, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) channel=0 )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -36,9 +36,7 @@ Return | Sign
|
||||
- [Color](https://docs.godotengine.org/en/stable/classes/class_color.html)<span id="i_get_color"></span> **get_color**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) arg0 )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_color"></span> **set_color**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) color, [Color](https://docs.godotengine.org/en/stable/classes/class_color.html) arg1 )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -3,9 +3,7 @@
|
||||
Inherits: [Resource](https://docs.godotengine.org/en/stable/classes/class_resource.html)
|
||||
|
||||
|
||||
Base class to all voxel procedural generators. If you want to define a custom one with a script, this is the class you should extend from.
|
||||
|
||||
Important: this engine makes heavy use of threads. Generators will run in one of them, so make sure you don't access the scene tree or other unsafe APIs from within a generator.
|
||||
Base class to all voxel procedural generators. If you want to define a custom one with a script, this is the class you should extend from. All implementations must be thread safe.
|
||||
|
||||
## Methods:
|
||||
|
||||
@ -21,4 +19,10 @@ Return | Signature
|
||||
|
||||
Generates a block of voxels within the specified world area.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
`out_buffer`: Buffer in which voxel data will be generated. It should not be `null` and should be given the requested size. Do not keep a reference on it after the call.
|
||||
|
||||
`origin_in_voxels`: Coordinates of the lower corner of the box to generate, relative to LOD0.
|
||||
|
||||
`lod`: Level of detail index to use for this block. Some generators might not support LOD, in which case it can be left 0.
|
||||
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -26,4 +26,4 @@ Type | Name | Default
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_voxel_type"></span> **voxel_type** = 1
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -92,101 +92,76 @@ enum **NodeTypeID**:
|
||||
- [void](#)<span id="i_add_connection"></span> **add_connection**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) src_node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) src_port_index, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) dst_node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) dst_port_index )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_bake_sphere_bumpmap"></span> **bake_sphere_bumpmap**( [Image](https://docs.godotengine.org/en/stable/classes/class_image.html) im, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) ref_radius, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) sdf_min, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) sdf_max )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_bake_sphere_normalmap"></span> **bake_sphere_normalmap**( [Image](https://docs.godotengine.org/en/stable/classes/class_image.html) im, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) ref_radius, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) strength )
|
||||
|
||||
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_can_connect"></span> **can_connect**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) src_node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) src_port_index, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) dst_node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) dst_port_index )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_clear"></span> **clear**( )
|
||||
|
||||
|
||||
|
||||
- [Dictionary](https://docs.godotengine.org/en/stable/classes/class_dictionary.html)<span id="i_compile"></span> **compile**( )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_create_node"></span> **create_node**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) type_id, [Vector2](https://docs.godotengine.org/en/stable/classes/class_vector2.html) position, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id=0 )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_debug_load_waves_preset"></span> **debug_load_waves_preset**( )
|
||||
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_debug_measure_microseconds_per_voxel"></span> **debug_measure_microseconds_per_voxel**( [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html) use_singular_queries )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_find_node_by_name"></span> **find_node_by_name**( [String](https://docs.godotengine.org/en/stable/classes/class_string.html) name )
|
||||
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_generate_single"></span> **generate_single**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) arg0 )
|
||||
|
||||
|
||||
|
||||
- [Array](https://docs.godotengine.org/en/stable/classes/class_array.html)<span id="i_get_connections"></span> **get_connections**( )
|
||||
|
||||
|
||||
|
||||
- [Variant](https://docs.godotengine.org/en/stable/classes/class_variant.html)<span id="i_get_node_default_input"></span> **get_node_default_input**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) input_index )
|
||||
|
||||
|
||||
|
||||
- [Vector2](https://docs.godotengine.org/en/stable/classes/class_vector2.html)<span id="i_get_node_gui_position"></span> **get_node_gui_position**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id )
|
||||
|
||||
|
||||
|
||||
- [PoolIntArray](https://docs.godotengine.org/en/stable/classes/class_poolintarray.html)<span id="i_get_node_ids"></span> **get_node_ids**( )
|
||||
|
||||
|
||||
|
||||
- [Variant](https://docs.godotengine.org/en/stable/classes/class_variant.html)<span id="i_get_node_param"></span> **get_node_param**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) param_index )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_node_type_count"></span> **get_node_type_count**( )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_node_type_id"></span> **get_node_type_id**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id )
|
||||
|
||||
|
||||
|
||||
- [Dictionary](https://docs.godotengine.org/en/stable/classes/class_dictionary.html)<span id="i_get_node_type_info"></span> **get_node_type_info**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) type_id )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_remove_connection"></span> **remove_connection**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) src_node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) src_port_index, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) dst_node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) dst_port_index )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_remove_node"></span> **remove_node**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_node_default_input"></span> **set_node_default_input**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) input_index, [Variant](https://docs.godotengine.org/en/stable/classes/class_variant.html) value )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_node_gui_position"></span> **set_node_gui_position**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id, [Vector2](https://docs.godotengine.org/en/stable/classes/class_vector2.html) position )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_node_param"></span> **set_node_param**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) param_index, [Variant](https://docs.godotengine.org/en/stable/classes/class_variant.html) value )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_node_param_null"></span> **set_node_param_null**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) node_id, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) param_index )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -30,4 +30,4 @@ Type | Name | Default
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_iso_scale"></span> **iso_scale** = 0.1
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -22,4 +22,4 @@ Type | Name | Default
|
||||
- [Image](https://docs.godotengine.org/en/stable/classes/class_image.html)<span id="i_image"></span> **image**
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -30,4 +30,4 @@ Type | Name | Default
|
||||
- [OpenSimplexNoise](https://docs.godotengine.org/en/stable/classes/class_opensimplexnoise.html)<span id="i_noise"></span> **noise**
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -22,4 +22,4 @@ Type | Name | Default
|
||||
- [OpenSimplexNoise](https://docs.godotengine.org/en/stable/classes/class_opensimplexnoise.html)<span id="i_noise"></span> **noise**
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -5,6 +5,8 @@ Inherits: [VoxelGenerator](VoxelGenerator.md)
|
||||
|
||||
Base class for custom generators defined with a script.
|
||||
|
||||
Important: this engine makes heavy use of threads. Generators will run in one of them, so make sure you don't access the scene tree or other unsafe APIs from within a generator.
|
||||
|
||||
## Methods:
|
||||
|
||||
|
||||
@ -18,10 +20,14 @@ Return | Signatur
|
||||
|
||||
- [void](#)<span id="i__generate_block"></span> **_generate_block**( [VoxelBuffer](VoxelBuffer.md) out_buffer, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) origin_in_voxels, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) lod )
|
||||
|
||||
`out_buffer`: Buffer in which to populate voxel data. It will never be `null` and will have the requested size. It is only valid for this function, do not store it anywhere after the end.
|
||||
|
||||
`origin_in_voxels`: Coordinates of the lower corner of the box to generate, relative to LOD0. The size of the box is known from `out_buffer`.
|
||||
|
||||
`lod`: Level of detail index to use for this block. It can be ignored if you don't use LOD. This may be used as a power of two, telling how big is one voxel. For example, if you use a loop to fill the buffer using noise, you should sample that noise at steps of 2^lod, starting from `origin_in_voxels` (in code you can use `1 << lod` for fast computation, instead of `pow(2, lod)`). You may want to separate variables that iterate the coordinates in `out_buffer` and variables used to generate voxel values in space.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i__get_used_channels_mask"></span> **_get_used_channels_mask**( )
|
||||
|
||||
Use this to indicate which channels your generator will use. It returns a bitmask, so for example you may provide information like this: `(1 << channel1) | (1 << channel2)`
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -26,4 +26,4 @@ Type | Name | Default
|
||||
- [Vector2](https://docs.godotengine.org/en/stable/classes/class_vector2.html)<span id="i_pattern_size"></span> **pattern_size** = Vector2( 30, 30 )
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
91
doc/source/api/VoxelInstanceGenerator.md
Normal file
@ -0,0 +1,91 @@
|
||||
# VoxelInstanceGenerator
|
||||
|
||||
Inherits: [Resource](https://docs.godotengine.org/en/stable/classes/class_resource.html)
|
||||
|
||||
|
||||
Decides where to spawn instances on top of a voxel surface.
|
||||
|
||||
## Description:
|
||||
|
||||
Generates the necessry information to spawn instances on top of a voxel surface. This may be used by a [VoxelInstancer](VoxelInstancer.md).
|
||||
|
||||
Note: to generate voxels, see [VoxelGenerator](VoxelGenerator.md).
|
||||
|
||||
## Properties:
|
||||
|
||||
|
||||
Type | Name | Default
|
||||
---------------- | ------------------------------------------------ | ------------
|
||||
`float` | [density](#i_density) | 0.1
|
||||
`int` | [emit_mode](#i_emit_mode) | 0
|
||||
`float` | [max_height](#i_max_height) | 3.40282e+38
|
||||
`float` | [max_scale](#i_max_scale) | 1.0
|
||||
`float` | [max_slope_degrees](#i_max_slope_degrees) | 180.0
|
||||
`float` | [min_height](#i_min_height) | 1.17549e-38
|
||||
`float` | [min_scale](#i_min_scale) | 1.0
|
||||
`float` | [min_slope_degrees](#i_min_slope_degrees) | 0.0
|
||||
`FastNoiseLite` | [noise](#i_noise) |
|
||||
`int` | [noise_dimension](#i_noise_dimension) | 1
|
||||
`float` | [noise_on_scale](#i_noise_on_scale) | 0.0
|
||||
`float` | [offset_along_normal](#i_offset_along_normal) | 0.0
|
||||
`bool` | [random_vertical_flip](#i_random_vertical_flip) | false
|
||||
`int` | [scale_distribution](#i_scale_distribution) | 1
|
||||
`float` | [vertical_alignment](#i_vertical_alignment) | 1.0
|
||||
<p></p>
|
||||
|
||||
## Enumerations:
|
||||
|
||||
enum **EmitMode**:
|
||||
|
||||
- **EMIT_FROM_VERTICES** = **0**
|
||||
- **EMIT_FROM_FACES** = **1**
|
||||
|
||||
|
||||
## Property Descriptions
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_density"></span> **density** = 0.1
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_emit_mode"></span> **emit_mode** = 0
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_max_height"></span> **max_height** = 3.40282e+38
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_max_scale"></span> **max_scale** = 1.0
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_max_slope_degrees"></span> **max_slope_degrees** = 180.0
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_min_height"></span> **min_height** = 1.17549e-38
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_min_scale"></span> **min_scale** = 1.0
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_min_slope_degrees"></span> **min_slope_degrees** = 0.0
|
||||
|
||||
|
||||
- [FastNoiseLite](FastNoiseLite.md)<span id="i_noise"></span> **noise**
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_noise_dimension"></span> **noise_dimension** = 1
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_noise_on_scale"></span> **noise_on_scale** = 0.0
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_offset_along_normal"></span> **offset_along_normal** = 0.0
|
||||
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_random_vertical_flip"></span> **random_vertical_flip** = false
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_scale_distribution"></span> **scale_distribution** = 1
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_vertical_alignment"></span> **vertical_alignment** = 1.0
|
||||
|
||||
|
||||
_Generated on Feb 16, 2021_
|
41
doc/source/api/VoxelInstanceLibrary.md
Normal file
@ -0,0 +1,41 @@
|
||||
# VoxelInstanceLibrary
|
||||
|
||||
Inherits: [Resource](https://docs.godotengine.org/en/stable/classes/class_resource.html)
|
||||
|
||||
|
||||
Contains a list of models that can be used by [VoxelInstancer](VoxelInstancer.md), associated with a unique ID
|
||||
|
||||
## Methods:
|
||||
|
||||
|
||||
Return | Signature
|
||||
--------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
[void](#) | [add_item](#i_add_item) ( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id, [VoxelInstanceLibraryItem](VoxelInstanceLibraryItem.md) arg1 )
|
||||
[void](#) | [clear](#i_clear) ( )
|
||||
[int](https://docs.godotengine.org/en/stable/classes/class_int.html) | [find_item_by_name](#i_find_item_by_name) ( [String](https://docs.godotengine.org/en/stable/classes/class_string.html) name ) const
|
||||
[VoxelInstanceLibraryItem](VoxelInstanceLibraryItem.md) | [get_item](#i_get_item) ( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id )
|
||||
[void](#) | [remove_item](#i_remove_item) ( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id )
|
||||
<p></p>
|
||||
|
||||
## Constants:
|
||||
|
||||
- **MAX_ID** = **65535**
|
||||
|
||||
## Method Descriptions
|
||||
|
||||
- [void](#)<span id="i_add_item"></span> **add_item**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id, [VoxelInstanceLibraryItem](VoxelInstanceLibraryItem.md) arg1 )
|
||||
|
||||
|
||||
- [void](#)<span id="i_clear"></span> **clear**( )
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_find_item_by_name"></span> **find_item_by_name**( [String](https://docs.godotengine.org/en/stable/classes/class_string.html) name )
|
||||
|
||||
|
||||
- [VoxelInstanceLibraryItem](VoxelInstanceLibraryItem.md)<span id="i_get_item"></span> **get_item**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id )
|
||||
|
||||
|
||||
- [void](#)<span id="i_remove_item"></span> **remove_item**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id )
|
||||
|
||||
|
||||
_Generated on Feb 16, 2021_
|
94
doc/source/api/VoxelInstanceLibraryItem.md
Normal file
@ -0,0 +1,94 @@
|
||||
# VoxelInstanceLibraryItem
|
||||
|
||||
Inherits: [Resource](https://docs.godotengine.org/en/stable/classes/class_resource.html)
|
||||
|
||||
|
||||
Settings for a model that can be used by [VoxelInstancer](VoxelInstancer.md)
|
||||
|
||||
## Properties:
|
||||
|
||||
|
||||
Type | Name | Default
|
||||
------------------------- | ------------------------------------------ | --------
|
||||
`int` | [collision_layer](#i_collision_layer) | 1
|
||||
`int` | [collision_mask](#i_collision_mask) | 1
|
||||
`VoxelInstanceGenerator` | [generator](#i_generator) |
|
||||
`int` | [lod_index](#i_lod_index) | 0
|
||||
`Material` | [material_override](#i_material_override) |
|
||||
`Mesh` | [mesh](#i_mesh) |
|
||||
`Mesh` | [mesh_lod1](#i_mesh_lod1) |
|
||||
`Mesh` | [mesh_lod2](#i_mesh_lod2) |
|
||||
`Mesh` | [mesh_lod3](#i_mesh_lod3) |
|
||||
`String` | [name](#i_name) | ""
|
||||
`bool` | [persistent](#i_persistent) | false
|
||||
<p></p>
|
||||
|
||||
## Methods:
|
||||
|
||||
|
||||
Return | Signature
|
||||
------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
[Array](https://docs.godotengine.org/en/stable/classes/class_array.html) | [get_collision_shapes](#i_get_collision_shapes) ( ) const
|
||||
[Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html) | [get_mesh](#i_get_mesh) ( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) mesh_lod_index ) const
|
||||
[void](#) | [set_collision_shapes](#i_set_collision_shapes) ( [Array](https://docs.godotengine.org/en/stable/classes/class_array.html) shape_infos )
|
||||
[void](#) | [set_mesh](#i_set_mesh) ( [Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html) mesh, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) mesh_lod_index )
|
||||
[void](#) | [setup_from_template](#i_setup_from_template) ( [Node](https://docs.godotengine.org/en/stable/classes/class_node.html) node )
|
||||
<p></p>
|
||||
|
||||
## Constants:
|
||||
|
||||
- **MAX_MESH_LODS** = **4**
|
||||
|
||||
## Property Descriptions
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_collision_layer"></span> **collision_layer** = 1
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_collision_mask"></span> **collision_mask** = 1
|
||||
|
||||
|
||||
- [VoxelInstanceGenerator](VoxelInstanceGenerator.md)<span id="i_generator"></span> **generator**
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_lod_index"></span> **lod_index** = 0
|
||||
|
||||
|
||||
- [Material](https://docs.godotengine.org/en/stable/classes/class_material.html)<span id="i_material_override"></span> **material_override**
|
||||
|
||||
|
||||
- [Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html)<span id="i_mesh"></span> **mesh**
|
||||
|
||||
|
||||
- [Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html)<span id="i_mesh_lod1"></span> **mesh_lod1**
|
||||
|
||||
|
||||
- [Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html)<span id="i_mesh_lod2"></span> **mesh_lod2**
|
||||
|
||||
|
||||
- [Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html)<span id="i_mesh_lod3"></span> **mesh_lod3**
|
||||
|
||||
|
||||
- [String](https://docs.godotengine.org/en/stable/classes/class_string.html)<span id="i_name"></span> **name** = ""
|
||||
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_persistent"></span> **persistent** = false
|
||||
|
||||
|
||||
## Method Descriptions
|
||||
|
||||
- [Array](https://docs.godotengine.org/en/stable/classes/class_array.html)<span id="i_get_collision_shapes"></span> **get_collision_shapes**( )
|
||||
|
||||
|
||||
- [Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html)<span id="i_get_mesh"></span> **get_mesh**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) mesh_lod_index )
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_collision_shapes"></span> **set_collision_shapes**( [Array](https://docs.godotengine.org/en/stable/classes/class_array.html) shape_infos )
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_mesh"></span> **set_mesh**( [Mesh](https://docs.godotengine.org/en/stable/classes/class_mesh.html) mesh, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) mesh_lod_index )
|
||||
|
||||
|
||||
- [void](#)<span id="i_setup_from_template"></span> **setup_from_template**( [Node](https://docs.godotengine.org/en/stable/classes/class_node.html) node )
|
||||
|
||||
|
||||
_Generated on Feb 16, 2021_
|
54
doc/source/api/VoxelInstancer.md
Normal file
@ -0,0 +1,54 @@
|
||||
# VoxelInstancer
|
||||
|
||||
Inherits: [Spatial](https://docs.godotengine.org/en/stable/classes/class_spatial.html)
|
||||
|
||||
|
||||
Spawns items on top of voxel surfaces.
|
||||
|
||||
## Description:
|
||||
|
||||
Add-on to voxel nodes, allowing to spawn elements on the surface. These elements are rendered with hardware instancing, can have collisions, and also be persistent.
|
||||
|
||||
## Properties:
|
||||
|
||||
|
||||
Type | Name | Default
|
||||
----------------------- | ---------------------- | --------
|
||||
`VoxelInstanceLibrary` | [library](#i_library) |
|
||||
`int` | [up_mode](#i_up_mode) | 0
|
||||
<p></p>
|
||||
|
||||
## Methods:
|
||||
|
||||
|
||||
Return | Signature
|
||||
--------------------------------------------------------------------- | ------------------------------------------------------------
|
||||
[int](https://docs.godotengine.org/en/stable/classes/class_int.html) | [debug_get_block_count](#i_debug_get_block_count) ( ) const
|
||||
<p></p>
|
||||
|
||||
## Enumerations:
|
||||
|
||||
enum **UpMode**:
|
||||
|
||||
- **UP_MODE_POSITIVE_Y** = **0**
|
||||
- **UP_MODE_SPHERE** = **1**
|
||||
|
||||
|
||||
## Constants:
|
||||
|
||||
- **MAX_LOD** = **8**
|
||||
|
||||
## Property Descriptions
|
||||
|
||||
- [VoxelInstanceLibrary](VoxelInstanceLibrary.md)<span id="i_library"></span> **library**
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_up_mode"></span> **up_mode** = 0
|
||||
|
||||
|
||||
## Method Descriptions
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_debug_get_block_count"></span> **debug_get_block_count**( )
|
||||
|
||||
|
||||
_Generated on Feb 16, 2021_
|
@ -37,6 +37,7 @@ Return | Signatur
|
||||
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_bake_tangents"></span> **bake_tangents** = true
|
||||
|
||||
Enable this option if you need normal mapping on your voxels. If you don't need it, disabling can reduce memory usage and give a small speed boost.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_voxel_count"></span> **voxel_count** = 0
|
||||
@ -47,21 +48,16 @@ Enable this option if you need normal mapping on your voxels. If you don't need
|
||||
- [void](#)<span id="i_bake"></span> **bake**( )
|
||||
|
||||
|
||||
|
||||
- [Voxel](Voxel.md)<span id="i_create_voxel"></span> **create_voxel**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id, [String](https://docs.godotengine.org/en/stable/classes/class_string.html) name )
|
||||
|
||||
|
||||
|
||||
- [Voxel](Voxel.md)<span id="i_get_voxel"></span> **get_voxel**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id )
|
||||
|
||||
|
||||
|
||||
- [Voxel](Voxel.md)<span id="i_get_voxel_by_name"></span> **get_voxel_by_name**( [String](https://docs.godotengine.org/en/stable/classes/class_string.html) name )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_voxel_index_from_name"></span> **get_voxel_index_from_name**( [String](https://docs.godotengine.org/en/stable/classes/class_string.html) name )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -85,39 +85,30 @@ enum **ProcessMode**:
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_debug_dump_as_scene"></span> **debug_dump_as_scene**( [String](https://docs.godotengine.org/en/stable/classes/class_string.html) path )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_debug_get_block_count"></span> **debug_get_block_count**( )
|
||||
|
||||
|
||||
|
||||
- [Dictionary](https://docs.godotengine.org/en/stable/classes/class_dictionary.html)<span id="i_debug_get_block_info"></span> **debug_get_block_info**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) block_pos, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) lod )
|
||||
|
||||
|
||||
|
||||
- [Array](https://docs.godotengine.org/en/stable/classes/class_array.html)<span id="i_debug_get_octrees"></span> **debug_get_octrees**( )
|
||||
|
||||
|
||||
|
||||
- [Array](https://docs.godotengine.org/en/stable/classes/class_array.html)<span id="i_debug_print_sdf_top_down"></span> **debug_print_sdf_top_down**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) center, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) extents )
|
||||
|
||||
|
||||
|
||||
- [Array](https://docs.godotengine.org/en/stable/classes/class_array.html)<span id="i_debug_raycast_block"></span> **debug_raycast_block**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) origin, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) dir )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_block_region_extent"></span> **get_block_region_extent**( )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_block_size"></span> **get_block_size**( )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_process_mode"></span> **get_process_mode**( )
|
||||
|
||||
|
||||
|
||||
- [Dictionary](https://docs.godotengine.org/en/stable/classes/class_dictionary.html)<span id="i_get_statistics"></span> **get_statistics**( )
|
||||
|
||||
Gets debug information about how much time is spent processing the terrain.
|
||||
@ -143,17 +134,13 @@ The returned dictionary has the following structure:
|
||||
- [VoxelTool](VoxelTool.md)<span id="i_get_voxel_tool"></span> **get_voxel_tool**( )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_save_modified_blocks"></span> **save_modified_blocks**( )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_process_mode"></span> **set_process_mode**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) mode )
|
||||
|
||||
|
||||
|
||||
- [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html)<span id="i_voxel_to_block_position"></span> **voxel_to_block_position**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) lod_index, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) arg1 )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -33,4 +33,4 @@ Gets by how much voxels must be padded before their lower corner in order for th
|
||||
|
||||
Gets by how much voxels must be padded after their upper corner in order for the mesher to work.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -30,4 +30,4 @@ Type | Name | Default
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_occlusion_enabled"></span> **occlusion_enabled** = true
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -41,4 +41,4 @@ enum **ColorMode**:
|
||||
- [VoxelColorPalette](VoxelColorPalette.md)<span id="i_palette"></span> **palette**
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -66,13 +66,10 @@ enum **SeamMode**:
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_get_geometric_error"></span> **get_geometric_error**( )
|
||||
|
||||
|
||||
|
||||
- [Dictionary](https://docs.godotengine.org/en/stable/classes/class_dictionary.html)<span id="i_get_statistics"></span> **get_statistics**( )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_geometric_error"></span> **set_geometric_error**( [float](https://docs.godotengine.org/en/stable/classes/class_float.html) error )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -18,5 +18,4 @@ Return
|
||||
- [ArrayMesh](https://docs.godotengine.org/en/stable/classes/class_arraymesh.html)<span id="i_build_transition_mesh"></span> **build_transition_mesh**( [VoxelBuffer](VoxelBuffer.md) voxel_buffer, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) direction )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -18,12 +18,15 @@ Type | Name | Default
|
||||
## Property Descriptions
|
||||
|
||||
- [VoxelGenerator](VoxelGenerator.md)<span id="i_generator"></span> **generator**
|
||||
|
||||
Procedural generator used to load voxel blocks when not present in the stream.
|
||||
|
||||
- [VoxelMesher](VoxelMesher.md)<span id="i_mesher"></span> **mesher**
|
||||
|
||||
Defines how voxels are transformed into visible meshes.
|
||||
|
||||
- [VoxelStream](VoxelStream.md)<span id="i_stream"></span> **stream**
|
||||
|
||||
Primary source of persistent voxel data. If left unassigned, the whole volume will use the generator.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -17,9 +17,11 @@ Type | Name | Default
|
||||
## Property Descriptions
|
||||
|
||||
- [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html)<span id="i_position"></span> **position** = Vector3( 0, 0, 0 )
|
||||
|
||||
Integer position of the voxel that was hit.
|
||||
|
||||
- [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html)<span id="i_previous_position"></span> **previous_position** = Vector3( 0, 0, 0 )
|
||||
|
||||
Integer position of the previous voxel along the ray before the final hit.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -37,4 +37,4 @@ The returned dictionary has the following structure:
|
||||
|
||||
```
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -36,24 +36,22 @@ enum **Result**:
|
||||
## Property Descriptions
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_save_generator_output"></span> **save_generator_output** = false
|
||||
When this is enabled, if a block cannot be found in the stream and it gets generated, then the generated block will immediately be saved into the stream. This can be used if the generator is too expensive to run on the fly (like Minecraft does), but it will require more disk space and eventual network traffic. If this setting is off, only modified blocks will be saved.
|
||||
|
||||
When this is enabled, if a block cannot be found in the stream and it gets generated, then the generated block will immediately be saved into the stream. This can be used if the generator is too expensive to run on the fly (like Minecraft does), but it will require more disk usage (amount of I/Os and space) and eventual network traffic. If this setting is off, only modified blocks will be saved.
|
||||
|
||||
## Method Descriptions
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_emerge_block"></span> **emerge_block**( [VoxelBuffer](VoxelBuffer.md) out_buffer, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) origin_in_voxels, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) lod )
|
||||
|
||||
|
||||
|
||||
- [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html)<span id="i_get_block_size"></span> **get_block_size**( )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_used_channels_mask"></span> **get_used_channels_mask**( )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_immerge_block"></span> **immerge_block**( [VoxelBuffer](VoxelBuffer.md) buffer, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) origin_in_voxels, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) lod )
|
||||
|
||||
`buffer`: Block of voxels to save. It is strongly recommended to not keep a reference to that data afterward, because streams are allowed to cache it, and saved data must represent either snapshots (copies) or last references to the data after the volume they belonged to is destroyed.
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -1,6 +1,6 @@
|
||||
# VoxelStreamBlockFiles
|
||||
|
||||
Inherits: [VoxelStreamFile](VoxelStreamFile.md)
|
||||
Inherits: [VoxelStream](VoxelStream.md)
|
||||
|
||||
|
||||
Loads and saves blocks as individual files under a directory.
|
||||
@ -20,6 +20,7 @@ Type | Name | Default
|
||||
## Property Descriptions
|
||||
|
||||
- [String](https://docs.godotengine.org/en/stable/classes/class_string.html)<span id="i_directory"></span> **directory** = ""
|
||||
|
||||
Directory under which the data is saved.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -1,8 +0,0 @@
|
||||
# VoxelStreamFile
|
||||
|
||||
Inherits: [VoxelStream](VoxelStream.md)
|
||||
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
@ -1,6 +1,6 @@
|
||||
# VoxelStreamRegionFiles
|
||||
|
||||
Inherits: [VoxelStreamFile](VoxelStreamFile.md)
|
||||
Inherits: [VoxelStream](VoxelStream.md)
|
||||
|
||||
|
||||
Loads and saves blocks to region files indexed by world position, under a directory.
|
||||
@ -39,6 +39,7 @@ Return |
|
||||
|
||||
|
||||
- [String](https://docs.godotengine.org/en/stable/classes/class_string.html)<span id="i_directory"></span> **directory** = ""
|
||||
|
||||
Directory under which the data is saved.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_lod_count"></span> **lod_count** = 1
|
||||
@ -55,13 +56,10 @@ Directory under which the data is saved.
|
||||
- [void](#)<span id="i_convert_files"></span> **convert_files**( [Dictionary](https://docs.godotengine.org/en/stable/classes/class_dictionary.html) new_settings )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_block_size_po2"></span> **get_block_size_po2**( )
|
||||
|
||||
|
||||
|
||||
- [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html)<span id="i_get_region_size"></span> **get_region_size**( )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
21
doc/source/api/VoxelStreamSQLite.md
Normal file
@ -0,0 +1,21 @@
|
||||
# VoxelStreamSQLite
|
||||
|
||||
Inherits: [VoxelStream](VoxelStream.md)
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties:
|
||||
|
||||
|
||||
Type | Name | Default
|
||||
--------- | ---------------------------------- | --------
|
||||
`String` | [database_path](#i_database_path) | ""
|
||||
<p></p>
|
||||
|
||||
## Property Descriptions
|
||||
|
||||
- [String](https://docs.godotengine.org/en/stable/classes/class_string.html)<span id="i_database_path"></span> **database_path** = ""
|
||||
|
||||
|
||||
_Generated on Feb 16, 2021_
|
@ -19,14 +19,13 @@ Return | Signatur
|
||||
|
||||
- [void](#)<span id="i__emerge_block"></span> **_emerge_block**( [VoxelBuffer](VoxelBuffer.md) out_buffer, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) origin_in_voxels, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) lod )
|
||||
|
||||
|
||||
`out_buffer`: Buffer in which to populate voxel data. It will never be `null` and will have the requested size. It is only valid for this function, do not store it anywhere after the end.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i__get_used_channels_mask"></span> **_get_used_channels_mask**( )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i__immerge_block"></span> **_immerge_block**( [VoxelBuffer](VoxelBuffer.md) buffer, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) origin_in_voxels, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) lod )
|
||||
|
||||
`buffer`: Buffer of voxel data to save. It is allowed to keep a reference to it for caching purposes, as saved data will either be snapshots or only references left after removal of a volume.
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -46,9 +46,11 @@ Emitted when a block unloaded due to being outside view distance.
|
||||
## Property Descriptions
|
||||
|
||||
- [AABB](https://docs.godotengine.org/en/stable/classes/class_aabb.html)<span id="i_bounds"></span> **bounds** = AABB( -5.36871e+08, -5.36871e+08, -5.36871e+08, 1.07374e+09, 1.07374e+09, 1.07374e+09 )
|
||||
|
||||
Defines the bounds within which the terrain is allowed to have voxels. If an infinite world generator is used, blocks will only generate within this region. Everything outside will be left empty.
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_generate_collisions"></span> **generate_collisions** = true
|
||||
|
||||
Enables the generation of collision shapes using the classic physics engine. Use this feature if you need realistic or non-trivial collisions or physics.
|
||||
|
||||
Note 1: you also need [VoxelViewer](VoxelViewer.md) to request collisions, otherwise they won't generate.
|
||||
@ -56,11 +58,13 @@ Note 1: you also need [VoxelViewer](VoxelViewer.md) to request collisions, other
|
||||
Note 2: If you need simple Minecraft/AABB physics, you can use [VoxelBoxMover](VoxelBoxMover.md) which may perform better in blocky worlds.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_max_view_distance"></span> **max_view_distance** = 128
|
||||
|
||||
Sets the maximum distance this terrain can support. If a [VoxelViewer](VoxelViewer.md) requests more, it will be clamped.
|
||||
|
||||
Note: there is an internal limit of 512 for constant LOD terrains, because going further can affect performance and memory very badly at the moment.
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_run_stream_in_editor"></span> **run_stream_in_editor** = true
|
||||
|
||||
Makes the terrain appear in the editor.
|
||||
|
||||
Important: this option will turn off automatically if you setup a script world generator. Modifying scripts while they are in use by threads causes undefined behaviors. You can still turn on this option if you need a preview, but it is strongly advised to turn it back off and wait until all generation has finished before you edit the script again.
|
||||
@ -74,7 +78,6 @@ Converts block coordinates into voxel coordinates. Voxel coordinates of a block
|
||||
- [Material](https://docs.godotengine.org/en/stable/classes/class_material.html)<span id="i_get_material"></span> **get_material**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id )
|
||||
|
||||
|
||||
|
||||
- [Dictionary](https://docs.godotengine.org/en/stable/classes/class_dictionary.html)<span id="i_get_statistics"></span> **get_statistics**( )
|
||||
|
||||
Gets debug information about how much time is spent processing the terrain.
|
||||
@ -125,9 +128,8 @@ Note 3: saving is asynchronous and won't block the game. the save may complete o
|
||||
- [void](#)<span id="i_set_material"></span> **set_material**( [int](https://docs.godotengine.org/en/stable/classes/class_int.html) id, [Material](https://docs.godotengine.org/en/stable/classes/class_material.html) material )
|
||||
|
||||
|
||||
|
||||
- [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html)<span id="i_voxel_to_block"></span> **voxel_to_block**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) voxel_pos )
|
||||
|
||||
Converts voxel coordinates into block coordinates.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -19,7 +19,7 @@ Type | Name | Default
|
||||
`int` | [channel](#i_channel) | 0
|
||||
`int` | [eraser_value](#i_eraser_value) | 0
|
||||
`int` | [mode](#i_mode) | 0
|
||||
`float` | [sdf_scale](#i_sdf_scale) | 0.1
|
||||
`float` | [sdf_scale](#i_sdf_scale) | 0.002
|
||||
`int` | [value](#i_value) | 0
|
||||
<p></p>
|
||||
|
||||
@ -54,15 +54,19 @@ enum **Mode**:
|
||||
## Property Descriptions
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_channel"></span> **channel** = 0
|
||||
|
||||
Set which channel will be edited. When used on a terrain node, it will default to the first available channel, based on the stream and generator.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_eraser_value"></span> **eraser_value** = 0
|
||||
|
||||
Sets which value will be used to erase voxels when editing the enum VoxelBuffer.CHANNEL_TYPE channel in enum MODE_REMOVE mode.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_mode"></span> **mode** = 0
|
||||
|
||||
Sets how `do_*` functions will behave. This may vary depending on the channel.
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_sdf_scale"></span> **sdf_scale** = 0.1
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_sdf_scale"></span> **sdf_scale** = 0.002
|
||||
|
||||
When working with smooth voxels, applies a scale to the signed distance field. A high scale (1 or higher) will tend to produce blocky results, and a low scale (below 1, but not too close to zero) will tend to be smoother.
|
||||
|
||||
|
||||
@ -70,6 +74,7 @@ When working with smooth voxels, applies a scale to the signed distance field. A
|
||||
This is related to the enum VoxelBuffer.Depth configuration on voxels. For 8-bit and 16-bit, there is a limited range of values the Signed Distance Field can take, and by default it is clamped to -1..1, so the gradient can only range across 2 voxels. But when LOD is used, it is better to stretch that range over a longer distance, and this is achieved by scaling SDF values.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_value"></span> **value** = 0
|
||||
|
||||
Sets which voxel value will be used. This is not relevant when editing enum VoxelBuffer.CHANNEL_SDF.
|
||||
|
||||
## Method Descriptions
|
||||
@ -81,45 +86,34 @@ Operate on a rectangular cuboid section of the terrain. `begin` and `end` are in
|
||||
- [void](#)<span id="i_do_point"></span> **do_point**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_do_sphere"></span> **do_sphere**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) center, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) radius )
|
||||
|
||||
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_get_voxel"></span> **get_voxel**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos )
|
||||
|
||||
|
||||
|
||||
- [float](https://docs.godotengine.org/en/stable/classes/class_float.html)<span id="i_get_voxel_f"></span> **get_voxel_f**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos )
|
||||
|
||||
|
||||
|
||||
- [Variant](https://docs.godotengine.org/en/stable/classes/class_variant.html)<span id="i_get_voxel_metadata"></span> **get_voxel_metadata**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos )
|
||||
|
||||
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_is_area_editable"></span> **is_area_editable**( [AABB](https://docs.godotengine.org/en/stable/classes/class_aabb.html) box )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_paste"></span> **paste**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) dst_pos, [Reference](https://docs.godotengine.org/en/stable/classes/class_reference.html) src_buffer, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) src_mask_value )
|
||||
|
||||
|
||||
|
||||
- [VoxelRaycastResult](VoxelRaycastResult.md)<span id="i_raycast"></span> **raycast**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) origin, [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) direction, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) max_distance=10.0, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) collision_mask=4294967295 )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_voxel"></span> **set_voxel**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos, [int](https://docs.godotengine.org/en/stable/classes/class_int.html) v )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_voxel_f"></span> **set_voxel_f**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos, [float](https://docs.godotengine.org/en/stable/classes/class_float.html) v )
|
||||
|
||||
|
||||
|
||||
- [void](#)<span id="i_set_voxel_metadata"></span> **set_voxel_metadata**( [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) pos, [Variant](https://docs.godotengine.org/en/stable/classes/class_variant.html) meta )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -5,4 +5,4 @@ Inherits: [VoxelTool](VoxelTool.md)
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -19,4 +19,4 @@ Return | Signature
|
||||
|
||||
Picks random voxels within the specified area and executes a function on them. This only works for terrains using [VoxelMesherBlocky](VoxelMesherBlocky.md). Only voxels where member Voxel.random_tickable is `true` will be picked.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -24,12 +24,15 @@ Type | Name | Default
|
||||
## Property Descriptions
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_requires_collisions"></span> **requires_collisions** = false
|
||||
|
||||
If set to `true`, the engine will generate classic collision shapes around this viewer.
|
||||
|
||||
- [bool](https://docs.godotengine.org/en/stable/classes/class_bool.html)<span id="i_requires_visuals"></span> **requires_visuals** = true
|
||||
|
||||
If set to `true`, the engine will generate meshes around this viewer. This may be enabled for the local player.
|
||||
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_view_distance"></span> **view_distance** = 128
|
||||
|
||||
How far should voxels generate around this viewer.
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -18,5 +18,4 @@ Return | Signatur
|
||||
- [int](https://docs.godotengine.org/en/stable/classes/class_int.html)<span id="i_load_from_file"></span> **load_from_file**( [String](https://docs.godotengine.org/en/stable/classes/class_string.html) fpath, [VoxelBuffer](VoxelBuffer.md) voxels, [VoxelColorPalette](VoxelColorPalette.md) arg2 )
|
||||
|
||||
|
||||
|
||||
_Generated on Jan 24, 2021_
|
||||
_Generated on Feb 16, 2021_
|
||||
|
@ -27,7 +27,7 @@ Fast collisions alternative
|
||||
|
||||
### Move and slide
|
||||
|
||||
Mesh-based collisions are quite accurate and feature-rich in Godot, however it some drawbacks:
|
||||
Mesh-based collisions are quite accurate and feature-rich in Godot, however it has some drawbacks:
|
||||
|
||||
- Trimesh collision shapes have to be built each time the terrain is modified, which is [very slow](https://github.com/Zylann/godot_voxel/issues/54).
|
||||
- The physics engine has to process arbitrary triangles near the player, which can't take advantage of particular situations, such as everything being cubes
|
||||
@ -53,7 +53,7 @@ func _physics_process(delta):
|
||||
```
|
||||
|
||||
!!! note
|
||||
this technique mainly works if you use `VoxelMesherBlocky`. It might have some limited support in other meshers though.
|
||||
this technique mainly works if you use `VoxelMesherBlocky`, because it gets information about which block is collidable from the `VoxelLibrary` used with it. It might have some limited support in other meshers though.
|
||||
|
||||
### Raycast
|
||||
|
||||
|
@ -32,6 +32,7 @@ Ongoing development - `master`
|
||||
- The SDF channel is now 16-bit by default instead of 8-bit, which reduces terracing in big terrains
|
||||
- Optimized `VoxelGeneratorGraph` by making it detect empty blocks more accurately and process by buffers
|
||||
- Added `SdfSphereHeightmap` and `Normalize` nodes to voxel graph, which can help making planets
|
||||
- Added `VoxelInstancer` to instantiate items on top of `VoxelLodTerrain`, aimed at spawning natural elements such as rocks and foliage
|
||||
|
||||
- Blocky voxels
|
||||
- Introduced a second blocky mesher dedicated to colored cubes, with greedy meshing and palette support
|
||||
@ -49,6 +50,7 @@ Ongoing development - `master`
|
||||
- The meshing system no longer "guesses" how voxels will look like. Instead it uses the mesher assigned to the terrain.
|
||||
- SDF and TYPE channels have different default depth, so if you relied on 8-bit depth, you may have to explicitely set that format in your generator, to avoid mismatch with existing savegames
|
||||
- The block serialization format has changed, and migration is not implemented, so old saves using it cannot be used. See documentation for more information.
|
||||
- Terrains no longer auto-save when they are destroyed while having a `stream` assigned. You have to call `save_modified_blocks()` explicitely before doing that.
|
||||
|
||||
- Fixes
|
||||
- C# should be able to properly implement generator/stream functions
|
||||
|
BIN
doc/source/images/instance_library_menu.png
Normal file
After Width: | Height: | Size: 214 KiB |
BIN
doc/source/images/instances_lod_index.png
Normal file
After Width: | Height: | Size: 567 KiB |
BIN
doc/source/images/instances_procgen.png
Normal file
After Width: | Height: | Size: 767 KiB |
BIN
doc/source/images/landscape_with_instances.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
doc/source/images/mesh_lod_properties.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
doc/source/images/mesh_lods.png
Normal file
After Width: | Height: | Size: 992 KiB |
BIN
doc/source/images/misaligned_instances.png
Normal file
After Width: | Height: | Size: 637 KiB |
112
doc/source/instancing.md
Normal file
@ -0,0 +1,112 @@
|
||||
Instancing
|
||||
=============
|
||||
|
||||
The module provides an instancing system with the [VoxelInstancer](api/VoxelInstancer.md) node. It allows to spawn 3D models on top of the terrain's surface. It uses `MultiMeshInstance` internally to allow rendering a lot of them at once, and can support collisions and removal. The node must be added as child of [VoxelLodTerrain](api/VoxelLodTerrain.md).
|
||||
|
||||
This system is primarily intented at natural spawning: grass, rocks, trees and other kinds of semi-random foliage. It is not suited for complex man-made structures like houses or villages.
|
||||
|
||||
This feature is currently only available under `VoxelLodTerrain`.
|
||||
|
||||
|
||||
VoxelInstanceLibrary
|
||||
---------------------
|
||||
|
||||
### Library setup
|
||||
|
||||
In order to spawn items, `VoxelInstancer` needs a [VoxelInstanceLibrary](api/VoxelInstanceLibrary.md) resource. This resource contains a list of all the items that can be spawned, and how they will be placed.
|
||||
|
||||
Select a `VoxelInstancer`. In the inspector, assign a library to the `library` property, or create a new embedded one. Then click on the library resource. Now a menu should show up in the top bar of the main viewport:
|
||||
|
||||
![Screenshot of the VoxelInstanceLibrary menu](images/instance_library_menu.png)
|
||||
|
||||
In this menu, you can add items to the library by clicking `VoxelInstanceLibrary -> Add item`.
|
||||
|
||||
Items created this way come with a default setup, so you should be able to see something appear on top of the voxel surface.
|
||||
|
||||
!!! note
|
||||
If you are making a planet, you may want to set the `up_mode` of `VoxelInstancer` to `Sphere` mode. This will tell the instancer where the upward direction is, and will align the items according to the local origin of the terrain.
|
||||
|
||||
|
||||
### Block LOD
|
||||
|
||||
The range at which items spawn is based on the LOD system of the voxel terrain itself. This is configured in the `lod_index` property of [VoxelInstanceLibraryItem](api/VoxelInstanceLibraryItem.md). For example, choosing `0` will make the item spawn at the closest range, and fade quickly in the distance. Higher indexes will spawn on a larger range, so will also start to appear earlier as the player gets closer. Instances spawn in the same "blocks" as the ground.
|
||||
|
||||
![Screenshot showing the effect of lod_index on the range of instances](images/instances_lod_index.png)
|
||||
|
||||
Usually landscapes may be composed of multiple layers so that the closer you get, the more details come in. Bigger items use high lod indexes to be seen from far away, while smaller items may use lower indexes.
|
||||
|
||||
![Screenshot of landscape using layers of instances](images/landscape_with_instances.png)
|
||||
|
||||
There is a balance to consider when choosing the appropriate `lod_index`: currently, larger indexes are *much more imprecise*, because they work on top of a lower-resolution mesh. When getting closer, it's possible that such instances are seen floating above ground, or sinking into it. This mostly happens in areas with sharp changes such as ridges, crevices or caves:
|
||||
|
||||
![Screemshot of misaligned instances](images/misaligned_instances.png)
|
||||
|
||||
To combat this, you can adjust the `offset_along_normal` parameter in the `generator` associated to the item. This depends on the asset, so designing them such that they can have part of their bottom sunk into the ground can give some margin of error.
|
||||
|
||||
Sometimes it might not be enough, so this problem still has to be worked out in the future. Possible approaches include:
|
||||
- Querying the world generator to approximate the surface without using the mesh (not suitable if the ground was edited)
|
||||
- Gradually snap the instances somehow as higher-resolution data becomes available
|
||||
- Load edited voxels for the entire world at once so they can be queried even from far distance (takes more memory)
|
||||
|
||||
!!! note
|
||||
When making grass or other items, it may be a good idea to fade meshes based on distance from the camera using a custom shader, so they won't disappear abruptly. Using a ground texture of similar colors also helps to make it blend.
|
||||
|
||||
|
||||
### Mesh LOD
|
||||
|
||||
A secondary LOD system is included, which applies to meshes themselves, to some limited extent. Godot 3 does not have a LOD system (Godot 4 will), so this allows to reduce vertex count over distance from within a set of visible meshes.
|
||||
|
||||
To use this, you have to fill the 3 mesh LOD properties on your `VoxelInstanceLibraryItem`:
|
||||
|
||||
![Screenshot of mesh LOD properties](images/mesh_lod_properties.png)
|
||||
|
||||
If only the `mesh` property is set, no LOD will be used.
|
||||
|
||||
The distance at which a LOD will be chosen is currently hardcoded, because it depends on the `lod_index` the blocks for that item are loaded into, which in turn depends on the `split_scale` property of the parent voxel terrain.
|
||||
|
||||
![Screenshot of mesh LODs with colors](images/mesh_lods.png)
|
||||
|
||||
If you need fewer LODs, you can assign twice the same mesh. This system is quite rigid because in Godot 4 it might be changed to only have a single slot dedicated to impostor meshes. Indeed, Godot 4 might support LOD on meshes, but it is not planned for the last LODs to become impostors, so this should still be possible to achieve.
|
||||
|
||||
!!! note
|
||||
Impostor meshes are simple quads that can fake the presence of the real model over far distances. For example, this is a really fast way to render forests from afar, while being able to use detailed trees when coming closer.
|
||||
|
||||
|
||||
### Edition
|
||||
|
||||
Editing instances manually in the editor is not supported yet. It is only possible to define where instances spawn by using procedural generation.
|
||||
|
||||
It is however possible to remove them from within the game, when digging the ground they spawned on.
|
||||
|
||||
|
||||
### Persistence
|
||||
|
||||
It is possible for some items to be persistent. This option can be enabled with the `persistent` property of [VoxelInstanceLibraryItem](api/VoxelInstanceLibraryItem.md). If the parent terrain has a `VoxelStream` supporting it, then instances from edited blocks will be saved into the stream, and won't respawn next time the player gets close to the area. Non-persistent instances will always respawn on each surface where procedural conditions are fulfilled.
|
||||
|
||||
The ID of persistent items is important, because it will be used in saved data. If you delete an item and try to load instances from a stream that still contains them, warnings will occur.
|
||||
|
||||
At time of writing, only [VoxelStreamSQLite](api/VoxelStreamSQLite.md) supports saving instances.
|
||||
|
||||
The save format is described in [this document](specs/instances_format.md).
|
||||
|
||||
|
||||
Procedural generation
|
||||
-----------------------
|
||||
|
||||
### Built-in generator
|
||||
|
||||
![Screenshot of a layer of instances using noise](images/instances_procgen.png)
|
||||
|
||||
Items are added with a default built-in generator, so they will already spawn based on procedural rules rather than being painted manually. You can tweak the generator by inspecting the `generator` property of [VoxelInstanceLibraryItem](api/VoxelInstanceLibraryItem.md).
|
||||
|
||||
Persistent instances located in a block that was edited in the game will no longer regenerate.
|
||||
|
||||
### Custom instance generator
|
||||
|
||||
The feature is recent and the API may still change, so this is not available to scripts at the moment.
|
||||
|
||||
|
||||
Streaming events (advanced)
|
||||
----------------------------
|
||||
|
||||
`VoxelInstancer` knows when to spawn things by registering itself to its parent's block events. This is currently not available to the script API of `VoxelLodTerrain`, but may be added in the future.
|
@ -1,4 +1,4 @@
|
||||
Block format
|
||||
Voxel block format
|
||||
====================
|
||||
|
||||
Version: 2
|
||||
@ -28,7 +28,7 @@ This is the format provided by the `VoxelBlockSerializer` utility class. If you
|
||||
Compressed data starts with one byte. Depending on its value, what follows is different.
|
||||
|
||||
- 0: no compression. Following bytes can be read as as block format directly. This is rarely used and could be for debugging.
|
||||
- 1: LZ4 compression. The next 32-bit unsigned integer is the size of the decompressed data, and following bytes are compressed data using LZ4 default parameters. This mode is used by default.
|
||||
- 1: LZ4 compression. The next big-endian 32-bit unsigned integer is the size of the decompressed data, and following bytes are compressed data using LZ4 default parameters. This mode is used by default.
|
||||
|
||||
Knowing the size of the decompressed data may be important when parsing the block later.
|
||||
|
||||
|
65
doc/source/specs/instances_format.md
Normal file
@ -0,0 +1,65 @@
|
||||
Instance block format
|
||||
=======================
|
||||
|
||||
This page describes the binary format used by the module to save instances to files or databases.
|
||||
|
||||
Specification
|
||||
---------------
|
||||
|
||||
### Compressed container
|
||||
|
||||
A block is usually serialized as compressed data.
|
||||
|
||||
Compressed data starts with one byte. Depending on its value, what follows is different.
|
||||
|
||||
- 0: no compression. Following bytes can be read as as block format directly. This is rarely used and could be for debugging.
|
||||
- 1: LZ4 compression. The next big-endian 32-bit unsigned integer is the size of the decompressed data, and following bytes are compressed data using LZ4 default parameters. This mode is used by default.
|
||||
|
||||
|
||||
### Binary data
|
||||
|
||||
This data uses big-endian.
|
||||
|
||||
In pseudo-code:
|
||||
|
||||
```cpp
|
||||
// Root structure
|
||||
struct InstanceBlockData {
|
||||
// Version tag in case more stuff is added in the future
|
||||
uint8_t version = 0;
|
||||
// There can be up to 256 different layers in one block
|
||||
uint8_t layer_count;
|
||||
// To compress positions we need to know their range.
|
||||
// It's local to the block so we know it starts from zero.
|
||||
float position_range;
|
||||
LayerData layers[layer_count];
|
||||
// Magic number to signal the end of the data block
|
||||
uint32_t control_end = 0x900df00d;
|
||||
};
|
||||
|
||||
struct LayerData {
|
||||
uint16_t id; // Identifies the type of instances (rocks, grass, pebbles, bushes etc)
|
||||
uint16_t count;
|
||||
// To be able to compress scale we must know its range
|
||||
float scale_min;
|
||||
float scale_max;
|
||||
// This tells which format instances of this layer use. For now I always use the same format,
|
||||
// But maybe some types of instances will need more, or less data?
|
||||
uint8_t format = 0;
|
||||
InstanceData data[count];
|
||||
};
|
||||
|
||||
struct InstanceData {
|
||||
// Position is lossy-compressed based on the size of the block
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint16_t z;
|
||||
// Scale is uniform and is lossy-compressed to 256 values
|
||||
uint8_t scale;
|
||||
// Rotation is a compressed quaternion
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t z;
|
||||
uint8_t w;
|
||||
};
|
||||
```
|
@ -127,7 +127,7 @@ The obtained buffer can be read using the block format.
|
||||
Block format
|
||||
--------------
|
||||
|
||||
See [Block format](block_format.md)
|
||||
See [Block format](block_format_v1.md)
|
||||
|
||||
|
||||
Current Issues
|
||||
|
@ -155,7 +155,7 @@ The obtained buffer can be read using the block format.
|
||||
Block format
|
||||
--------------
|
||||
|
||||
See [Block format](block_format.md)
|
||||
See [Block format](block_format_v2.md)
|
||||
|
||||
|
||||
Current Issues
|
||||
|
@ -36,7 +36,7 @@ Contains every block of the volume. There can be thousands of them.
|
||||
|
||||
- `loc` is a 64-bit integer packing the coordinates and LOD index of the block using little-endian. Coordinates are equal to the origin of the block in voxels, divided by the size of the block + lod index using euclidean division (`coord >> (block_size_po2 + lod_index)`). XYZ are 16-bit signed integers, and LOD is a 8-bit unsigned integer: `0LXXYYZZ`
|
||||
- `vb` contains compressed voxel data using the [Block format](block_format_v2.md).
|
||||
- `instances` is currently unused but is planned to contain instancing data.
|
||||
- `instances` contains compressed instance data using the [Instance format](instances_format.md).
|
||||
|
||||
|
||||
### `channels`
|
||||
|
@ -20,5 +20,5 @@ Save formats
|
||||
--------------
|
||||
|
||||
- [Region format](specs/region_format_v3.md)
|
||||
- [Block format](specs/block_format.md)
|
||||
- [Block format](specs/block_format_v2.md)
|
||||
|
||||
|
@ -105,6 +105,8 @@ def update_mkdocs_file(mkdocs_config_fpath, md_classes_dir):
|
||||
processed_lines.append(line)
|
||||
for class_file in class_files:
|
||||
processed_lines.append(indent + "- " + class_file + "\n")
|
||||
else:
|
||||
processed_lines.append(line)
|
||||
|
||||
yml = "".join(processed_lines)
|
||||
with open(mkdocs_config_fpath, 'w', encoding='utf-8') as f:
|
||||
|
@ -328,8 +328,12 @@ def process_xml(f_xml, f_out, module_class_names):
|
||||
out += "\n\n"
|
||||
|
||||
if member.text is not None:
|
||||
out += make_text(member.text, module_class_names)
|
||||
out += "\n\n"
|
||||
text = make_text(member.text, module_class_names)
|
||||
if text != "":
|
||||
out += text
|
||||
out += "\n"
|
||||
|
||||
out += "\n"
|
||||
|
||||
# Method descriptions
|
||||
if len(methods) > 0:
|
||||
@ -350,9 +354,11 @@ def process_xml(f_xml, f_out, module_class_names):
|
||||
desc = method.find('description')
|
||||
if desc is not None:
|
||||
text = make_text(desc.text, module_class_names)
|
||||
out += text
|
||||
if text != "":
|
||||
out += text
|
||||
out += "\n"
|
||||
|
||||
out += "\n\n"
|
||||
out += "\n"
|
||||
|
||||
# Footer
|
||||
out += "_Generated on " + strftime("%b %d, %Y", gmtime()) + "_\n"
|
||||
|
@ -73,11 +73,11 @@ Ref<VoxelRaycastResult> VoxelTool::raycast(Vector3 pos, Vector3 dir, float max_d
|
||||
// See derived classes for implementations
|
||||
}
|
||||
|
||||
uint64_t VoxelTool::get_voxel(Vector3i pos) {
|
||||
uint64_t VoxelTool::get_voxel(Vector3i pos) const {
|
||||
return _get_voxel(pos);
|
||||
}
|
||||
|
||||
float VoxelTool::get_voxel_f(Vector3i pos) {
|
||||
float VoxelTool::get_voxel_f(Vector3i pos) const {
|
||||
return _get_voxel_f(pos);
|
||||
}
|
||||
|
||||
@ -122,12 +122,12 @@ void VoxelTool::do_circle(Vector3i pos, int radius, Vector3i direction) {
|
||||
ERR_PRINT("Not implemented");
|
||||
}
|
||||
|
||||
uint64_t VoxelTool::_get_voxel(Vector3i pos) {
|
||||
uint64_t VoxelTool::_get_voxel(Vector3i pos) const {
|
||||
ERR_PRINT("Not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
float VoxelTool::_get_voxel_f(Vector3i pos) {
|
||||
float VoxelTool::_get_voxel_f(Vector3i pos) const {
|
||||
ERR_PRINT("Not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ public:
|
||||
void set_eraser_value(uint64_t value);
|
||||
uint64_t get_eraser_value() const;
|
||||
|
||||
uint64_t get_voxel(Vector3i pos);
|
||||
float get_voxel_f(Vector3i pos);
|
||||
uint64_t get_voxel(Vector3i pos) const;
|
||||
float get_voxel_f(Vector3i pos) const;
|
||||
|
||||
float get_sdf_scale() const;
|
||||
void set_sdf_scale(float s);
|
||||
@ -82,8 +82,8 @@ protected:
|
||||
|
||||
// These methods never go alone, but may be used in others.
|
||||
// They don't represent an edit, they only abstract the lower-level API
|
||||
virtual uint64_t _get_voxel(Vector3i pos);
|
||||
virtual float _get_voxel_f(Vector3i pos);
|
||||
virtual uint64_t _get_voxel(Vector3i pos) const;
|
||||
virtual float _get_voxel_f(Vector3i pos) const;
|
||||
virtual void _set_voxel(Vector3i pos, uint64_t v);
|
||||
virtual void _set_voxel_f(Vector3i pos, float v);
|
||||
virtual void _post_edit(const Rect3i &box);
|
||||
|
@ -11,12 +11,12 @@ bool VoxelToolBuffer::is_area_editable(const Rect3i &box) const {
|
||||
return Rect3i(Vector3i(), _buffer->get_size()).encloses(box);
|
||||
}
|
||||
|
||||
uint64_t VoxelToolBuffer::_get_voxel(Vector3i pos) {
|
||||
uint64_t VoxelToolBuffer::_get_voxel(Vector3i pos) const {
|
||||
ERR_FAIL_COND_V(_buffer.is_null(), 0);
|
||||
return _buffer->get_voxel(pos, _channel);
|
||||
}
|
||||
|
||||
float VoxelToolBuffer::_get_voxel_f(Vector3i pos) {
|
||||
float VoxelToolBuffer::_get_voxel_f(Vector3i pos) const {
|
||||
ERR_FAIL_COND_V(_buffer.is_null(), 0);
|
||||
return _buffer->get_voxel_f(pos.x, pos.y, pos.z, _channel);
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ public:
|
||||
Variant get_voxel_metadata(Vector3i pos) override;
|
||||
|
||||
protected:
|
||||
uint64_t _get_voxel(Vector3i pos) override;
|
||||
float _get_voxel_f(Vector3i pos) override;
|
||||
uint64_t _get_voxel(Vector3i pos) const override;
|
||||
float _get_voxel_f(Vector3i pos) const override;
|
||||
void _set_voxel(Vector3i pos, uint64_t v) override;
|
||||
void _set_voxel_f(Vector3i pos, float v) override;
|
||||
void _post_edit(const Rect3i &box) override;
|
||||
|
@ -15,12 +15,12 @@ bool VoxelToolLodTerrain::is_area_editable(const Rect3i &box) const {
|
||||
return _map.is_area_fully_loaded(box.padded(1));
|
||||
}
|
||||
|
||||
uint64_t VoxelToolLodTerrain::_get_voxel(Vector3i pos) {
|
||||
uint64_t VoxelToolLodTerrain::_get_voxel(Vector3i pos) const {
|
||||
ERR_FAIL_COND_V(_terrain == nullptr, 0);
|
||||
return _map.get_voxel(pos, _channel);
|
||||
}
|
||||
|
||||
float VoxelToolLodTerrain::_get_voxel_f(Vector3i pos) {
|
||||
float VoxelToolLodTerrain::_get_voxel_f(Vector3i pos) const {
|
||||
ERR_FAIL_COND_V(_terrain == nullptr, 0);
|
||||
return _map.get_voxel_f(pos, _channel);
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ public:
|
||||
bool is_area_editable(const Rect3i &box) const override;
|
||||
|
||||
protected:
|
||||
uint64_t _get_voxel(Vector3i pos) override;
|
||||
float _get_voxel_f(Vector3i pos) override;
|
||||
uint64_t _get_voxel(Vector3i pos) const override;
|
||||
float _get_voxel_f(Vector3i pos) const override;
|
||||
void _set_voxel(Vector3i pos, uint64_t v) override;
|
||||
void _set_voxel_f(Vector3i pos, float v) override;
|
||||
void _post_edit(const Rect3i &box) override;
|
||||
|
@ -80,12 +80,12 @@ Ref<VoxelRaycastResult> VoxelToolTerrain::raycast(Vector3 pos, Vector3 dir, floa
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t VoxelToolTerrain::_get_voxel(Vector3i pos) {
|
||||
uint64_t VoxelToolTerrain::_get_voxel(Vector3i pos) const {
|
||||
ERR_FAIL_COND_V(_terrain == nullptr, 0);
|
||||
return _terrain->get_storage().get_voxel(pos, _channel);
|
||||
}
|
||||
|
||||
float VoxelToolTerrain::_get_voxel_f(Vector3i pos) {
|
||||
float VoxelToolTerrain::_get_voxel_f(Vector3i pos) const {
|
||||
ERR_FAIL_COND_V(_terrain == nullptr, 0);
|
||||
return _terrain->get_storage().get_voxel_f(pos, _channel);
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ public:
|
||||
void run_blocky_random_tick(AABB voxel_area, int voxel_count, Ref<FuncRef> callback, int block_batch_count) const;
|
||||
|
||||
protected:
|
||||
uint64_t _get_voxel(Vector3i pos) override;
|
||||
float _get_voxel_f(Vector3i pos) override;
|
||||
uint64_t _get_voxel(Vector3i pos) const override;
|
||||
float _get_voxel_f(Vector3i pos) const override;
|
||||
void _set_voxel(Vector3i pos, uint64_t v) override;
|
||||
void _set_voxel_f(Vector3i pos, float v) override;
|
||||
void _post_edit(const Rect3i &box) override;
|
||||
|
116
editor/instance_library/voxel_instance_library_editor_plugin.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
#include "voxel_instance_library_editor_plugin.h"
|
||||
|
||||
#include <scene/gui/dialogs.h>
|
||||
#include <scene/gui/menu_button.h>
|
||||
#include <scene/resources/primitive_meshes.h>
|
||||
|
||||
VoxelInstanceLibraryEditorPlugin::VoxelInstanceLibraryEditorPlugin(EditorNode *p_node) {
|
||||
_menu_button = memnew(MenuButton);
|
||||
_menu_button->set_text(TTR("VoxelInstanceLibrary"));
|
||||
// TODO Icon
|
||||
//_menu_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshLibrary", "EditorIcons"));
|
||||
_menu_button->get_popup()->add_item(TTR("Add Item"), MENU_ADD_ITEM);
|
||||
_menu_button->get_popup()->add_item(TTR("Remove Selected Item"), MENU_REMOVE_ITEM);
|
||||
// TODO Add and update from scene
|
||||
_menu_button->get_popup()->connect("id_pressed", this, "_on_menu_id_pressed");
|
||||
_menu_button->hide();
|
||||
|
||||
Control *base_control = get_editor_interface()->get_base_control();
|
||||
|
||||
_confirmation_dialog = memnew(ConfirmationDialog);
|
||||
_confirmation_dialog->connect("confirmed", this, "_on_remove_item_confirmed");
|
||||
base_control->add_child(_confirmation_dialog);
|
||||
|
||||
_info_dialog = memnew(AcceptDialog);
|
||||
base_control->add_child(_info_dialog);
|
||||
|
||||
add_control_to_container(EditorPlugin::CONTAINER_SPATIAL_EDITOR_MENU, _menu_button);
|
||||
}
|
||||
|
||||
bool VoxelInstanceLibraryEditorPlugin::handles(Object *p_object) const {
|
||||
VoxelInstanceLibrary *lib = Object::cast_to<VoxelInstanceLibrary>(p_object);
|
||||
return lib != nullptr;
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::edit(Object *p_object) {
|
||||
VoxelInstanceLibrary *lib = Object::cast_to<VoxelInstanceLibrary>(p_object);
|
||||
_library.reference_ptr(lib);
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::make_visible(bool visible) {
|
||||
_menu_button->set_visible(visible);
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::_on_menu_id_pressed(int id) {
|
||||
switch (id) {
|
||||
case MENU_ADD_ITEM: {
|
||||
ERR_FAIL_COND(_library.is_null());
|
||||
|
||||
Ref<VoxelInstanceLibraryItem> item;
|
||||
item.instance();
|
||||
// Setup some defaults
|
||||
Ref<CubeMesh> mesh;
|
||||
mesh.instance();
|
||||
item->set_mesh(mesh, 0);
|
||||
item->set_lod_index(2);
|
||||
Ref<VoxelInstanceGenerator> generator;
|
||||
generator.instance();
|
||||
item->set_generator(generator);
|
||||
|
||||
const int item_id = _library->get_next_available_id();
|
||||
|
||||
UndoRedo &ur = get_undo_redo();
|
||||
ur.create_action("Add item");
|
||||
ur.add_do_method(*_library, "add_item", item_id, item);
|
||||
ur.add_undo_method(*_library, "remove_item", item_id);
|
||||
ur.commit_action();
|
||||
} break;
|
||||
|
||||
case MENU_REMOVE_ITEM: {
|
||||
ERR_FAIL_COND(_library.is_null());
|
||||
|
||||
String path = get_editor_interface()->get_inspector()->get_selected_path();
|
||||
String prefix = "item_";
|
||||
|
||||
if (path.begins_with(prefix)) {
|
||||
_item_id_to_remove = path.substr(prefix.size()).to_int();
|
||||
_confirmation_dialog->set_text(vformat(TTR("Remove item %d?"), _item_id_to_remove));
|
||||
_confirmation_dialog->popup_centered();
|
||||
|
||||
} else {
|
||||
// The inspector won't let us know which item the current resource is stored into...
|
||||
// Gridmap works because it simulates every item as properties of the library,
|
||||
// but our current resource does not do that,
|
||||
// and I don't want to modify the API just because the built-in inspector is bad.
|
||||
_info_dialog->set_text(TTR(
|
||||
"Could not determine selected item.\n"
|
||||
"You must select the `item_X` property label of the item you want to remove."));
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
ERR_PRINT("Unknown menu item");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::_on_remove_item_confirmed() {
|
||||
ERR_FAIL_COND(_library.is_null());
|
||||
ERR_FAIL_COND(_item_id_to_remove == -1);
|
||||
|
||||
Ref<VoxelInstanceLibraryItem> item = _library->get_item(_item_id_to_remove);
|
||||
|
||||
UndoRedo &ur = get_undo_redo();
|
||||
ur.create_action("Remove item");
|
||||
ur.add_do_method(*_library, "remove_item", _item_id_to_remove);
|
||||
ur.add_undo_method(*_library, "add_item", _item_id_to_remove, item);
|
||||
ur.commit_action();
|
||||
|
||||
_item_id_to_remove = -1;
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_on_menu_id_pressed", "id"), &VoxelInstanceLibraryEditorPlugin::_on_menu_id_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_on_remove_item_confirmed"),
|
||||
&VoxelInstanceLibraryEditorPlugin::_on_remove_item_confirmed);
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
#ifndef VOXEL_INSTANCE_LIBRARY_EDITOR_PLUGIN_H
|
||||
#define VOXEL_INSTANCE_LIBRARY_EDITOR_PLUGIN_H
|
||||
|
||||
#include "../../terrain/instancing/voxel_instance_library.h"
|
||||
#include <editor/editor_plugin.h>
|
||||
|
||||
class MenuButton;
|
||||
class ConfirmationDialog;
|
||||
|
||||
class VoxelInstanceLibraryEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(VoxelInstanceLibraryEditorPlugin, EditorPlugin)
|
||||
public:
|
||||
virtual String get_name() const { return "VoxelInstanceLibrary"; }
|
||||
|
||||
VoxelInstanceLibraryEditorPlugin(EditorNode *p_node);
|
||||
|
||||
bool handles(Object *p_object) const override;
|
||||
void edit(Object *p_object) override;
|
||||
void make_visible(bool visible) override;
|
||||
|
||||
private:
|
||||
void _on_menu_id_pressed(int id);
|
||||
void _on_remove_item_confirmed();
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
enum MenuOption {
|
||||
MENU_ADD_ITEM,
|
||||
MENU_REMOVE_ITEM
|
||||
};
|
||||
|
||||
MenuButton *_menu_button = nullptr;
|
||||
ConfirmationDialog *_confirmation_dialog = nullptr;
|
||||
AcceptDialog *_info_dialog = nullptr;
|
||||
int _item_id_to_remove = -1;
|
||||
|
||||
Ref<VoxelInstanceLibrary> _library;
|
||||
};
|
||||
|
||||
#endif // VOXEL_INSTANCE_LIBRARY_EDITOR_PLUGIN_H
|
@ -236,6 +236,7 @@ _FORCE_INLINE_ int Vector3i::distance_sq(const Vector3i &other) const {
|
||||
return (other - *this).length_sq();
|
||||
}
|
||||
|
||||
// For Godot
|
||||
struct Vector3iHasher {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector3i &v) {
|
||||
uint32_t hash = hash_djb2_one_32(v.x);
|
||||
@ -244,4 +245,14 @@ struct Vector3iHasher {
|
||||
}
|
||||
};
|
||||
|
||||
// For STL
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Vector3i> {
|
||||
size_t operator()(const Vector3i &v) const {
|
||||
return Vector3iHasher::hash(v);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
#endif // VOXEL_VECTOR3I_H
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "editor/fast_noise_lite/fast_noise_lite_editor_plugin.h"
|
||||
#include "editor/graph/voxel_graph_editor_plugin.h"
|
||||
#include "editor/instance_library/voxel_instance_library_editor_plugin.h"
|
||||
#include "editor/terrain/voxel_terrain_editor_plugin.h"
|
||||
#include "generators/graph/voxel_generator_graph.h"
|
||||
#include "generators/graph/voxel_graph_node_db.h"
|
||||
@ -27,6 +28,7 @@
|
||||
#include "streams/vox_loader.h"
|
||||
#include "streams/voxel_stream_block_files.h"
|
||||
#include "streams/voxel_stream_script.h"
|
||||
#include "terrain/instancing/voxel_instancer.h"
|
||||
#include "terrain/voxel_box_mover.h"
|
||||
#include "terrain/voxel_lod_terrain.h"
|
||||
#include "terrain/voxel_map.h"
|
||||
@ -55,9 +57,12 @@ void register_voxel_types() {
|
||||
// TODO Can I prevent users from instancing it? is "register_virtual_class" correct for a class that's not abstract?
|
||||
ClassDB::register_class<VoxelServer>();
|
||||
|
||||
// Misc
|
||||
ClassDB::register_class<Voxel>();
|
||||
ClassDB::register_class<VoxelLibrary>();
|
||||
ClassDB::register_class<VoxelColorPalette>();
|
||||
ClassDB::register_class<VoxelInstanceLibrary>();
|
||||
ClassDB::register_class<VoxelInstanceLibraryItem>();
|
||||
|
||||
// Storage
|
||||
ClassDB::register_class<VoxelBuffer>();
|
||||
@ -67,6 +72,8 @@ void register_voxel_types() {
|
||||
ClassDB::register_class<VoxelTerrain>();
|
||||
ClassDB::register_class<VoxelLodTerrain>();
|
||||
ClassDB::register_class<VoxelViewer>();
|
||||
ClassDB::register_class<VoxelInstanceGenerator>();
|
||||
ClassDB::register_class<VoxelInstancer>();
|
||||
|
||||
// Streams
|
||||
ClassDB::register_virtual_class<VoxelStream>();
|
||||
@ -116,6 +123,7 @@ void register_voxel_types() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorPlugins::add_by_type<VoxelGraphEditorPlugin>();
|
||||
EditorPlugins::add_by_type<VoxelTerrainEditorPlugin>();
|
||||
EditorPlugins::add_by_type<VoxelInstanceLibraryEditorPlugin>();
|
||||
EditorPlugins::add_by_type<FastNoiseLiteEditorPlugin>();
|
||||
#endif
|
||||
}
|
||||
|
@ -50,15 +50,18 @@ VoxelServer::VoxelServer() {
|
||||
// TODO Automatic thread assignment and project settings
|
||||
|
||||
// Can't be more than 1 thread. File access with more threads isn't worth it.
|
||||
_streaming_thread_pool.set_name("Voxel streaming");
|
||||
_streaming_thread_pool.set_thread_count(1);
|
||||
_streaming_thread_pool.set_priority_update_period(300);
|
||||
_streaming_thread_pool.set_batch_count(16);
|
||||
|
||||
_generation_thread_pool.set_name("Voxel generation");
|
||||
_generation_thread_pool.set_thread_count(2);
|
||||
_generation_thread_pool.set_priority_update_period(300);
|
||||
_generation_thread_pool.set_batch_count(1);
|
||||
|
||||
// This pool works on visuals so it must have lower latency
|
||||
_meshing_thread_pool.set_name("Voxel meshing");
|
||||
_meshing_thread_pool.set_thread_count(2);
|
||||
_meshing_thread_pool.set_priority_update_period(64);
|
||||
_meshing_thread_pool.set_batch_count(1);
|
||||
@ -256,23 +259,23 @@ void VoxelServer::request_block_mesh(uint32_t volume_id, BlockMeshInput &input)
|
||||
_meshing_thread_pool.enqueue(r);
|
||||
}
|
||||
|
||||
void VoxelServer::request_block_load(uint32_t volume_id, Vector3i block_pos, int lod) {
|
||||
void VoxelServer::request_block_load(uint32_t volume_id, Vector3i block_pos, int lod, bool request_instances) {
|
||||
const Volume &volume = _world.volumes.get(volume_id);
|
||||
ERR_FAIL_COND(volume.stream_dependency == nullptr);
|
||||
|
||||
if (volume.stream_dependency->stream.is_valid()) {
|
||||
BlockDataRequest r;
|
||||
r.volume_id = volume_id;
|
||||
r.position = block_pos;
|
||||
r.lod = lod;
|
||||
r.type = BlockDataRequest::TYPE_LOAD;
|
||||
r.block_size = volume.block_size;
|
||||
r.stream_dependency = volume.stream_dependency;
|
||||
BlockDataRequest *r = memnew(BlockDataRequest);
|
||||
r->volume_id = volume_id;
|
||||
r->position = block_pos;
|
||||
r->lod = lod;
|
||||
r->type = BlockDataRequest::TYPE_LOAD;
|
||||
r->block_size = volume.block_size;
|
||||
r->stream_dependency = volume.stream_dependency;
|
||||
r->request_instances = request_instances;
|
||||
|
||||
init_priority_dependency(r.priority_dependency, block_pos, lod, volume);
|
||||
init_priority_dependency(r->priority_dependency, block_pos, lod, volume);
|
||||
|
||||
BlockDataRequest *rp = memnew(BlockDataRequest(r));
|
||||
_streaming_thread_pool.enqueue(rp);
|
||||
_streaming_thread_pool.enqueue(r);
|
||||
|
||||
} else {
|
||||
// Directly generate the block without checking the stream
|
||||
@ -292,24 +295,48 @@ void VoxelServer::request_block_load(uint32_t volume_id, Vector3i block_pos, int
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelServer::request_block_save(uint32_t volume_id, Ref<VoxelBuffer> voxels, Vector3i block_pos, int lod) {
|
||||
void VoxelServer::request_voxel_block_save(uint32_t volume_id, Ref<VoxelBuffer> voxels, Vector3i block_pos, int lod) {
|
||||
const Volume &volume = _world.volumes.get(volume_id);
|
||||
ERR_FAIL_COND(volume.stream.is_null());
|
||||
CRASH_COND(volume.stream_dependency == nullptr);
|
||||
|
||||
BlockDataRequest r;
|
||||
r.voxels = voxels;
|
||||
r.volume_id = volume_id;
|
||||
r.position = block_pos;
|
||||
r.lod = lod;
|
||||
r.type = BlockDataRequest::TYPE_SAVE;
|
||||
r.block_size = volume.block_size;
|
||||
r.stream_dependency = volume.stream_dependency;
|
||||
BlockDataRequest *r = memnew(BlockDataRequest);
|
||||
r->voxels = voxels;
|
||||
r->volume_id = volume_id;
|
||||
r->position = block_pos;
|
||||
r->lod = lod;
|
||||
r->type = BlockDataRequest::TYPE_SAVE;
|
||||
r->block_size = volume.block_size;
|
||||
r->stream_dependency = volume.stream_dependency;
|
||||
r->request_instances = false;
|
||||
r->request_voxels = true;
|
||||
|
||||
// No priority data, saving doesnt need sorting
|
||||
|
||||
BlockDataRequest *rp = memnew(BlockDataRequest(r));
|
||||
_streaming_thread_pool.enqueue(rp);
|
||||
_streaming_thread_pool.enqueue(r);
|
||||
}
|
||||
|
||||
void VoxelServer::request_instance_block_save(uint32_t volume_id, std::unique_ptr<VoxelInstanceBlockData> instances,
|
||||
Vector3i block_pos, int lod) {
|
||||
|
||||
const Volume &volume = _world.volumes.get(volume_id);
|
||||
ERR_FAIL_COND(volume.stream.is_null());
|
||||
CRASH_COND(volume.stream_dependency == nullptr);
|
||||
|
||||
BlockDataRequest *r = memnew(BlockDataRequest);
|
||||
r->instances = std::move(instances);
|
||||
r->volume_id = volume_id;
|
||||
r->position = block_pos;
|
||||
r->lod = lod;
|
||||
r->type = BlockDataRequest::TYPE_SAVE;
|
||||
r->block_size = volume.block_size;
|
||||
r->stream_dependency = volume.stream_dependency;
|
||||
r->request_instances = true;
|
||||
r->request_voxels = false;
|
||||
|
||||
// No priority data, saving doesnt need sorting
|
||||
|
||||
_streaming_thread_pool.enqueue(r);
|
||||
}
|
||||
|
||||
void VoxelServer::request_block_generate_from_data_request(BlockDataRequest *src) {
|
||||
@ -336,19 +363,19 @@ void VoxelServer::request_block_save_from_generate_request(BlockGenerateRequest
|
||||
|
||||
ERR_FAIL_COND(src->voxels.is_null());
|
||||
|
||||
BlockDataRequest r;
|
||||
r.voxels = src->voxels->duplicate(true);
|
||||
r.volume_id = src->volume_id;
|
||||
r.position = src->position;
|
||||
r.lod = src->lod;
|
||||
r.type = BlockDataRequest::TYPE_SAVE;
|
||||
r.block_size = src->block_size;
|
||||
r.stream_dependency = src->stream_dependency;
|
||||
BlockDataRequest *r = memnew(BlockDataRequest());
|
||||
r->voxels = src->voxels->duplicate(true);
|
||||
r->volume_id = src->volume_id;
|
||||
r->position = src->position;
|
||||
r->lod = src->lod;
|
||||
r->type = BlockDataRequest::TYPE_SAVE;
|
||||
r->block_size = src->block_size;
|
||||
r->stream_dependency = src->stream_dependency;
|
||||
|
||||
// No instances, generators are not designed to produce them at this stage yet.
|
||||
// No priority data, saving doesnt need sorting
|
||||
|
||||
BlockDataRequest *rp = memnew(BlockDataRequest(r));
|
||||
_streaming_thread_pool.enqueue(rp);
|
||||
_streaming_thread_pool.enqueue(r);
|
||||
}
|
||||
|
||||
void VoxelServer::remove_volume(uint32_t volume_id) {
|
||||
@ -438,6 +465,7 @@ void VoxelServer::process() {
|
||||
r->type != BlockDataRequest::TYPE_FALLBACK_ON_GENERATOR) {
|
||||
BlockDataOutput o;
|
||||
o.voxels = r->voxels;
|
||||
o.instances = std::move(r->instances);
|
||||
o.position = r->position;
|
||||
o.lod = r->lod;
|
||||
o.dropped = !r->has_run;
|
||||
@ -455,7 +483,7 @@ void VoxelServer::process() {
|
||||
CRASH_NOW_MSG("Unexpected data request response type");
|
||||
}
|
||||
|
||||
volume->reception_buffers->data_output.push_back(o);
|
||||
volume->reception_buffers->data_output.push_back(std::move(o));
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -482,7 +510,7 @@ void VoxelServer::process() {
|
||||
o.lod = r->lod;
|
||||
o.dropped = !r->has_run;
|
||||
o.type = BlockDataOutput::TYPE_LOAD;
|
||||
volume->reception_buffers->data_output.push_back(o);
|
||||
volume->reception_buffers->data_output.push_back(std::move(o));
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -552,29 +580,6 @@ void VoxelServer::process() {
|
||||
}
|
||||
}
|
||||
|
||||
// void VoxelServer::get_min_max_block_padding(
|
||||
// bool blocky_enabled, bool smooth_enabled, unsigned int &out_min_padding, unsigned int &out_max_padding) const {
|
||||
|
||||
// // const Volume &volume = _world.volumes.get(volume_id);
|
||||
|
||||
// // bool smooth_enabled = volume.stream->get_used_channels_mask() & (1 << VoxelBuffer::CHANNEL_SDF);
|
||||
// // bool blocky_enabled = volume.voxel_library.is_valid() &&
|
||||
// // volume.stream->get_used_channels_mask() & (1 << VoxelBuffer::CHANNEL_TYPE);
|
||||
|
||||
// out_min_padding = 0;
|
||||
// out_max_padding = 0;
|
||||
|
||||
// if (blocky_enabled) {
|
||||
// out_min_padding = max(out_min_padding, _blocky_meshers[0]->get_minimum_padding());
|
||||
// out_max_padding = max(out_max_padding, _blocky_meshers[0]->get_maximum_padding());
|
||||
// }
|
||||
|
||||
// if (smooth_enabled) {
|
||||
// out_min_padding = max(out_min_padding, _smooth_meshers[0]->get_minimum_padding());
|
||||
// out_max_padding = max(out_max_padding, _smooth_meshers[0]->get_maximum_padding());
|
||||
// }
|
||||
// }
|
||||
|
||||
static unsigned int debug_get_active_thread_count(const VoxelThreadPool &pool) {
|
||||
unsigned int active_count = 0;
|
||||
for (unsigned int i = 0; i < pool.get_thread_count(); ++i) {
|
||||
@ -625,12 +630,17 @@ void VoxelServer::BlockDataRequest::run(VoxelTaskContext ctx) {
|
||||
case TYPE_LOAD: {
|
||||
voxels.instance();
|
||||
voxels->create(block_size, block_size, block_size);
|
||||
// TODO No longer using batches? If that works ok, we should get rid of batch queries in files
|
||||
const VoxelStream::Result result = stream->emerge_block(voxels, origin_in_voxels, lod);
|
||||
if (result == VoxelStream::RESULT_ERROR) {
|
||||
ERR_PRINT("Error loading block");
|
||||
}
|
||||
if (result == VoxelStream::RESULT_BLOCK_NOT_FOUND) {
|
||||
|
||||
// TODO We should consider batching this again, but it needs to be done carefully.
|
||||
// Each task is one block, and priority depends on distance to closest viewer.
|
||||
// If we batch blocks, we have to do it by distance too.
|
||||
|
||||
const VoxelStream::Result voxel_result = stream->emerge_block(voxels, origin_in_voxels, lod);
|
||||
|
||||
if (voxel_result == VoxelStream::RESULT_ERROR) {
|
||||
ERR_PRINT("Error loading voxel block");
|
||||
|
||||
} else if (voxel_result == VoxelStream::RESULT_BLOCK_NOT_FOUND) {
|
||||
Ref<VoxelGenerator> generator = stream_dependency->generator;
|
||||
if (generator.is_valid()) {
|
||||
VoxelServer::get_singleton()->request_block_generate_from_data_request(this);
|
||||
@ -642,16 +652,56 @@ void VoxelServer::BlockDataRequest::run(VoxelTaskContext ctx) {
|
||||
// TODO Define format on volume?
|
||||
}
|
||||
}
|
||||
|
||||
if (request_instances && stream->supports_instance_blocks()) {
|
||||
ERR_FAIL_COND(instances != nullptr);
|
||||
|
||||
VoxelStreamInstanceDataRequest instance_data_request;
|
||||
instance_data_request.lod = lod;
|
||||
instance_data_request.position = position;
|
||||
VoxelStream::Result instances_result;
|
||||
stream->load_instance_blocks(
|
||||
ArraySlice<VoxelStreamInstanceDataRequest>(&instance_data_request, 1),
|
||||
ArraySlice<VoxelStream::Result>(&instances_result, 1));
|
||||
|
||||
if (instances_result == VoxelStream::RESULT_ERROR) {
|
||||
ERR_PRINT("Error loading instance block");
|
||||
|
||||
} else if (voxel_result == VoxelStream::RESULT_BLOCK_FOUND) {
|
||||
instances = std::move(instance_data_request.data);
|
||||
}
|
||||
// If not found, instances will return null,
|
||||
// which means it can be generated by the instancer after the meshing process
|
||||
}
|
||||
} break;
|
||||
|
||||
case TYPE_SAVE: {
|
||||
Ref<VoxelBuffer> voxels_copy;
|
||||
{
|
||||
RWLockRead lock(voxels->get_lock());
|
||||
voxels_copy = voxels->duplicate(true);
|
||||
if (request_voxels) {
|
||||
Ref<VoxelBuffer> voxels_copy;
|
||||
// TODO Is that copy necessary? It's possible it was already done while issuing the request
|
||||
if (voxels.is_valid()) {
|
||||
RWLockRead lock(voxels->get_lock());
|
||||
voxels_copy = voxels->duplicate(true);
|
||||
}
|
||||
voxels.unref();
|
||||
stream->immerge_block(voxels_copy, origin_in_voxels, lod);
|
||||
}
|
||||
|
||||
if (request_instances && stream->supports_instance_blocks()) {
|
||||
// If the provided data is null, it means this instance block was never modified.
|
||||
// Since we are in a save request, the saved data will revert to unmodified.
|
||||
// On the other hand, if we want to represent the fact that "everything was deleted here",
|
||||
// this should not be null.
|
||||
|
||||
PRINT_VERBOSE(String("Saving instance block {0} lod {1} with data {2}")
|
||||
.format(varray(position.to_vec3(), lod, ptr2s(instances.get()))));
|
||||
|
||||
VoxelStreamInstanceDataRequest instance_data_request;
|
||||
instance_data_request.lod = lod;
|
||||
instance_data_request.position = position;
|
||||
instance_data_request.data = std::move(instances);
|
||||
stream->save_instance_blocks(ArraySlice<VoxelStreamInstanceDataRequest>(&instance_data_request, 1));
|
||||
}
|
||||
voxels.unref();
|
||||
stream->immerge_block(voxels_copy, origin_in_voxels, lod);
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
// TODO Don't inherit Object. Instead have a Godot wrapper, there is very little use for Object stuff
|
||||
|
||||
// Access point for asynchronous voxel processing APIs.
|
||||
// Functions must be used from the main thread.
|
||||
class VoxelServer : public Object {
|
||||
@ -36,6 +38,7 @@ public:
|
||||
|
||||
Type type;
|
||||
Ref<VoxelBuffer> voxels;
|
||||
std::unique_ptr<VoxelInstanceBlockData> instances;
|
||||
Vector3i position;
|
||||
uint8_t lod;
|
||||
bool dropped;
|
||||
@ -88,8 +91,10 @@ public:
|
||||
void set_volume_octree_split_scale(uint32_t volume_id, float split_scale);
|
||||
void invalidate_volume_mesh_requests(uint32_t volume_id);
|
||||
void request_block_mesh(uint32_t volume_id, BlockMeshInput &input);
|
||||
void request_block_load(uint32_t volume_id, Vector3i block_pos, int lod);
|
||||
void request_block_save(uint32_t volume_id, Ref<VoxelBuffer> voxels, Vector3i block_pos, int lod);
|
||||
void request_block_load(uint32_t volume_id, Vector3i block_pos, int lod, bool request_instances);
|
||||
void request_voxel_block_save(uint32_t volume_id, Ref<VoxelBuffer> voxels, Vector3i block_pos, int lod);
|
||||
void request_instance_block_save(uint32_t volume_id, std::unique_ptr<VoxelInstanceBlockData> instances,
|
||||
Vector3i block_pos, int lod);
|
||||
void remove_volume(uint32_t volume_id);
|
||||
|
||||
// TODO Rename functions to C convention
|
||||
@ -245,6 +250,7 @@ private:
|
||||
bool is_cancelled() override;
|
||||
|
||||
Ref<VoxelBuffer> voxels;
|
||||
std::unique_ptr<VoxelInstanceBlockData> instances;
|
||||
Vector3i position;
|
||||
uint32_t volume_id;
|
||||
uint8_t lod;
|
||||
@ -252,6 +258,8 @@ private:
|
||||
uint8_t type;
|
||||
bool has_run = false;
|
||||
bool too_far = false;
|
||||
bool request_instances = false;
|
||||
bool request_voxels = false;
|
||||
PriorityDependency priority_dependency;
|
||||
std::shared_ptr<StreamingDependency> stream_dependency;
|
||||
// TODO Find a way to separate save, it doesnt need sorting
|
||||
|
@ -39,6 +39,9 @@ void VoxelThreadPool::create_thread(ThreadData &d, uint32_t i) {
|
||||
d.stop = false;
|
||||
d.waiting = false;
|
||||
d.index = i;
|
||||
if (!_name.empty()) {
|
||||
d.name = String("{0} {1}").format(varray(_name, i));
|
||||
}
|
||||
d.thread = Thread::create(thread_func_static, &d);
|
||||
}
|
||||
|
||||
@ -63,6 +66,10 @@ void VoxelThreadPool::destroy_all_threads() {
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelThreadPool::set_name(String name) {
|
||||
_name = name;
|
||||
}
|
||||
|
||||
void VoxelThreadPool::set_thread_count(uint32_t count) {
|
||||
if (count > MAX_THREADS) {
|
||||
count = MAX_THREADS;
|
||||
@ -116,6 +123,16 @@ void VoxelThreadPool::enqueue(ArraySlice<IVoxelTask *> tasks) {
|
||||
void VoxelThreadPool::thread_func_static(void *p_data) {
|
||||
ThreadData &data = *static_cast<ThreadData *>(p_data);
|
||||
VoxelThreadPool &pool = *data.pool;
|
||||
|
||||
if (!data.name.empty()) {
|
||||
Thread::set_name(data.name);
|
||||
|
||||
#ifdef VOXEL_PROFILER_ENABLED
|
||||
CharString thread_name = data.name.utf8();
|
||||
VOXEL_PROFILE_SET_THREAD_NAME(thread_name.get_data());
|
||||
#endif
|
||||
}
|
||||
|
||||
pool.thread_func(data);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,10 @@ public:
|
||||
VoxelThreadPool();
|
||||
~VoxelThreadPool();
|
||||
|
||||
// Set name prefix to recognize threads of this pool in debug tools.
|
||||
// Must be called before configuring thread count.
|
||||
void set_name(String name);
|
||||
|
||||
// TODO Add ability to change it while running without skipping tasks
|
||||
// Can't be changed after tasks have been queued
|
||||
void set_thread_count(uint32_t count);
|
||||
@ -92,6 +96,7 @@ private:
|
||||
bool stop = false;
|
||||
bool waiting = false;
|
||||
State debug_state = STATE_STOPPED;
|
||||
String name;
|
||||
};
|
||||
|
||||
static void thread_func_static(void *p_data);
|
||||
@ -114,6 +119,8 @@ private:
|
||||
uint32_t _batch_count = 1;
|
||||
uint32_t _priority_update_period = 32;
|
||||
|
||||
String _name;
|
||||
|
||||
unsigned int _debug_received_tasks = 0;
|
||||
unsigned int _debug_completed_tasks = 0;
|
||||
};
|
||||
|
98
streams/compressed_data.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "compressed_data.h"
|
||||
#include "../thirdparty/lz4/lz4.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "../util/serialization.h"
|
||||
|
||||
#include <core/io/file_access_memory.h>
|
||||
#include <core/variant.h>
|
||||
|
||||
namespace VoxelCompressedData {
|
||||
|
||||
bool decompress(ArraySlice<const uint8_t> src, std::vector<uint8_t> &dst) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
VoxelUtility::MemoryReader f(src, VoxelUtility::ENDIANESS_BIG_ENDIAN);
|
||||
|
||||
const Compression comp = static_cast<Compression>(f.get_8());
|
||||
ERR_FAIL_INDEX_V(comp, COMPRESSION_COUNT, false);
|
||||
|
||||
switch (comp) {
|
||||
case COMPRESSION_NONE: {
|
||||
// We still have to do a copy. The point of this container is compression,
|
||||
// so we don't worry too much about the performance impact of not using `src` directly.
|
||||
dst.resize(src.size() - 1);
|
||||
memcpy(dst.data(), src.data() + 1, dst.size());
|
||||
} break;
|
||||
|
||||
case COMPRESSION_LZ4: {
|
||||
const uint32_t decompressed_size = f.get_32();
|
||||
const uint32_t header_size = sizeof(uint8_t) + sizeof(uint32_t);
|
||||
|
||||
dst.resize(decompressed_size);
|
||||
|
||||
const uint32_t actually_decompressed_size = LZ4_decompress_safe(
|
||||
(const char *)src.data() + header_size,
|
||||
(char *)dst.data(),
|
||||
src.size() - header_size,
|
||||
dst.size());
|
||||
|
||||
ERR_FAIL_COND_V_MSG(actually_decompressed_size < 0, false,
|
||||
String("LZ4 decompression error {0}").format(varray(actually_decompressed_size)));
|
||||
|
||||
ERR_FAIL_COND_V_MSG(actually_decompressed_size != decompressed_size, false,
|
||||
String("Expected {0} bytes, obtained {1}")
|
||||
.format(varray(decompressed_size, actually_decompressed_size)));
|
||||
} break;
|
||||
|
||||
default:
|
||||
ERR_PRINT("Invalid compression header");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compress(ArraySlice<const uint8_t> src, std::vector<uint8_t> &dst, Compression comp) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
switch (comp) {
|
||||
case COMPRESSION_NONE: {
|
||||
dst.resize(src.size() + 1);
|
||||
dst[0] = comp;
|
||||
memcpy(dst.data() + 1, src.data(), src.size());
|
||||
} break;
|
||||
|
||||
case COMPRESSION_LZ4: {
|
||||
ERR_FAIL_COND_V(src.size() > std::numeric_limits<uint32_t>::max(), false);
|
||||
|
||||
// Write header
|
||||
// Must clear first because MemoryWriter writes from the end
|
||||
dst.clear();
|
||||
VoxelUtility::MemoryWriter f(dst, VoxelUtility::ENDIANESS_BIG_ENDIAN);
|
||||
f.store_8(comp);
|
||||
f.store_32(src.size());
|
||||
|
||||
const uint32_t header_size = sizeof(uint8_t) + sizeof(uint32_t);
|
||||
dst.resize(header_size + LZ4_compressBound(src.size()));
|
||||
|
||||
const uint32_t compressed_size = LZ4_compress_default(
|
||||
(const char *)src.data(),
|
||||
(char *)dst.data() + header_size,
|
||||
src.size(),
|
||||
dst.size() - header_size);
|
||||
|
||||
ERR_FAIL_COND_V(compressed_size < 0, false);
|
||||
ERR_FAIL_COND_V(compressed_size == 0, false);
|
||||
|
||||
dst.resize(header_size + compressed_size);
|
||||
} break;
|
||||
|
||||
default:
|
||||
ERR_PRINT("Invalid compression header");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace VoxelCompressedData
|
27
streams/compressed_data.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef VOXEL_COMPRESSED_DATA_H
|
||||
#define VOXEL_COMPRESSED_DATA_H
|
||||
|
||||
#include "../util/array_slice.h"
|
||||
|
||||
namespace VoxelCompressedData {
|
||||
|
||||
// Compressed data starts with a single byte telling which compression format is used.
|
||||
// What follows depends on it.
|
||||
|
||||
enum Compression {
|
||||
// No compression. All following bytes can be read as-is.
|
||||
// Could be used for debugging.
|
||||
COMPRESSION_NONE = 0,
|
||||
// The next uint32_t will be the size of decompressed data.
|
||||
// All following bytes are compressed data using LZ4 defaults.
|
||||
// This is the fastest compression format.
|
||||
COMPRESSION_LZ4 = 1,
|
||||
COMPRESSION_COUNT = 2
|
||||
};
|
||||
|
||||
bool compress(ArraySlice<const uint8_t> src, std::vector<uint8_t> &dst, Compression comp);
|
||||
bool decompress(ArraySlice<const uint8_t> src, std::vector<uint8_t> &dst);
|
||||
|
||||
} // namespace VoxelCompressedData
|
||||
|
||||
#endif // VOXEL_COMPRESSED_DATA_H
|
145
streams/instance_data.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include "instance_data.h"
|
||||
#include "../util/array_slice.h"
|
||||
#include "../util/serialization.h"
|
||||
#include "../util/utility.h"
|
||||
#include "../voxel_constants.h"
|
||||
|
||||
namespace {
|
||||
const uint32_t TRAILING_MAGIC = 0x900df00d;
|
||||
}
|
||||
|
||||
inline uint8_t norm_to_u8(float x) {
|
||||
return clamp(static_cast<int>(128.f * x + 128.f), 0, 0xff);
|
||||
}
|
||||
|
||||
inline float u8_to_norm(uint8_t v) {
|
||||
return (static_cast<real_t>(v) - 0x7f) * VoxelConstants::INV_0x7f;
|
||||
}
|
||||
|
||||
struct CompressedQuaternion4b {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t z;
|
||||
uint8_t w;
|
||||
|
||||
static CompressedQuaternion4b from_quat(Quat q) {
|
||||
CompressedQuaternion4b c;
|
||||
c.x = norm_to_u8(q.x);
|
||||
c.y = norm_to_u8(q.y);
|
||||
c.z = norm_to_u8(q.z);
|
||||
c.w = norm_to_u8(q.w);
|
||||
return c;
|
||||
}
|
||||
|
||||
Quat to_quat() const {
|
||||
Quat q;
|
||||
q.x = u8_to_norm(x);
|
||||
q.y = u8_to_norm(y);
|
||||
q.z = u8_to_norm(z);
|
||||
q.w = u8_to_norm(w);
|
||||
return q.normalized();
|
||||
}
|
||||
};
|
||||
|
||||
void serialize_instance_block_data(const VoxelInstanceBlockData &src, std::vector<uint8_t> &dst) {
|
||||
const uint8_t version = 0;
|
||||
const uint8_t instance_format = 0;
|
||||
|
||||
VoxelUtility::MemoryWriter w(dst, VoxelUtility::ENDIANESS_BIG_ENDIAN);
|
||||
|
||||
w.store_8(version);
|
||||
w.store_8(src.layers.size());
|
||||
w.store_float(src.position_range);
|
||||
|
||||
// TODO Introduce a margin to position coordinates, stuff can spawn offset from the ground.
|
||||
// Or just compute the ranges
|
||||
const float pos_norm_scale = 1.f / src.position_range;
|
||||
|
||||
for (size_t i = 0; i < src.layers.size(); ++i) {
|
||||
const VoxelInstanceBlockData::LayerData &layer = src.layers[i];
|
||||
|
||||
w.store_16(layer.id);
|
||||
w.store_16(layer.instances.size());
|
||||
w.store_float(layer.scale_min);
|
||||
w.store_float(layer.scale_max);
|
||||
w.store_8(instance_format);
|
||||
|
||||
const float scale_norm_scale = 1.f / (layer.scale_max - layer.scale_min);
|
||||
|
||||
for (size_t j = 0; j < layer.instances.size(); ++j) {
|
||||
const VoxelInstanceBlockData::InstanceData &instance = layer.instances[j];
|
||||
|
||||
w.store_16(static_cast<uint16_t>(pos_norm_scale * instance.transform.origin.x * 0xffff));
|
||||
w.store_16(static_cast<uint16_t>(pos_norm_scale * instance.transform.origin.y * 0xffff));
|
||||
w.store_16(static_cast<uint16_t>(pos_norm_scale * instance.transform.origin.z * 0xffff));
|
||||
|
||||
const float scale = instance.transform.get_basis().get_scale().y;
|
||||
w.store_8(static_cast<uint8_t>(scale_norm_scale * (scale - layer.scale_min) * 0xff));
|
||||
|
||||
const Quat q = instance.transform.get_basis().get_rotation_quat();
|
||||
const CompressedQuaternion4b cq = CompressedQuaternion4b::from_quat(q);
|
||||
w.store_8(cq.x);
|
||||
w.store_8(cq.y);
|
||||
w.store_8(cq.z);
|
||||
w.store_8(cq.w);
|
||||
}
|
||||
}
|
||||
|
||||
w.store_32(TRAILING_MAGIC);
|
||||
}
|
||||
|
||||
bool deserialize_instance_block_data(VoxelInstanceBlockData &dst, ArraySlice<const uint8_t> src) {
|
||||
const uint8_t expected_version = 0;
|
||||
const uint8_t expected_instance_format = 0;
|
||||
|
||||
VoxelUtility::MemoryReader r(src, VoxelUtility::ENDIANESS_BIG_ENDIAN);
|
||||
|
||||
const uint8_t version = r.get_8();
|
||||
ERR_FAIL_COND_V(version != expected_version, false);
|
||||
|
||||
const uint8_t layers_count = r.get_8();
|
||||
dst.layers.resize(layers_count);
|
||||
|
||||
dst.position_range = r.get_float();
|
||||
|
||||
for (size_t i = 0; i < dst.layers.size(); ++i) {
|
||||
VoxelInstanceBlockData::LayerData &layer = dst.layers[i];
|
||||
|
||||
layer.id = r.get_16();
|
||||
|
||||
const uint16_t instance_count = r.get_16();
|
||||
layer.instances.resize(instance_count);
|
||||
|
||||
layer.scale_min = r.get_float();
|
||||
layer.scale_max = r.get_float();
|
||||
ERR_FAIL_COND_V(layer.scale_max < layer.scale_min, false);
|
||||
const float scale_range = layer.scale_max - layer.scale_min;
|
||||
|
||||
const uint8_t instance_format = r.get_8();
|
||||
ERR_FAIL_COND_V(instance_format != expected_instance_format, false);
|
||||
|
||||
for (size_t j = 0; j < layer.instances.size(); ++j) {
|
||||
const float x = (static_cast<float>(r.get_16()) / 0xffff) * dst.position_range;
|
||||
const float y = (static_cast<float>(r.get_16()) / 0xffff) * dst.position_range;
|
||||
const float z = (static_cast<float>(r.get_16()) / 0xffff) * dst.position_range;
|
||||
|
||||
const float s = (static_cast<float>(r.get_8()) / 0xff) * scale_range + layer.scale_min;
|
||||
|
||||
CompressedQuaternion4b cq;
|
||||
cq.x = r.get_8();
|
||||
cq.y = r.get_8();
|
||||
cq.z = r.get_8();
|
||||
cq.w = r.get_8();
|
||||
const Quat q = cq.to_quat();
|
||||
|
||||
VoxelInstanceBlockData::InstanceData &instance = layer.instances[j];
|
||||
instance.transform = Transform(Basis(q).scaled(Vector3(s, s, s)), Vector3(x, y, z));
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t control_end = r.get_32();
|
||||
ERR_FAIL_COND_V_MSG(control_end != TRAILING_MAGIC, false,
|
||||
String("Expected {0}, found {1}").format(varray(TRAILING_MAGIC, control_end)));
|
||||
|
||||
return true;
|
||||
}
|
32
streams/instance_data.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef VOXEL_INSTANCE_DATA_H
|
||||
#define VOXEL_INSTANCE_DATA_H
|
||||
|
||||
#include "../util/array_slice.h"
|
||||
#include <core/math/transform.h>
|
||||
|
||||
// Stores data to pass around until it either gets saved or turned into actual instances
|
||||
struct VoxelInstanceBlockData {
|
||||
struct InstanceData {
|
||||
Transform transform;
|
||||
};
|
||||
|
||||
struct LayerData {
|
||||
uint16_t id;
|
||||
float scale_min;
|
||||
float scale_max;
|
||||
std::vector<InstanceData> instances;
|
||||
};
|
||||
|
||||
float position_range;
|
||||
std::vector<LayerData> layers;
|
||||
|
||||
void copy_to(VoxelInstanceBlockData &dst) const {
|
||||
// It's all POD so it should work for now
|
||||
dst = *this;
|
||||
}
|
||||
};
|
||||
|
||||
void serialize_instance_block_data(const VoxelInstanceBlockData &src, std::vector<uint8_t> &dst);
|
||||
bool deserialize_instance_block_data(VoxelInstanceBlockData &dst, ArraySlice<const uint8_t> src);
|
||||
|
||||
#endif // VOXEL_INSTANCE_DATA_H
|
@ -2,20 +2,32 @@
|
||||
#include "../../thirdparty/sqlite/sqlite3.h"
|
||||
#include "../../util/macros.h"
|
||||
#include "../../util/profiling.h"
|
||||
#include "../compressed_data.h"
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
// TODO Is the struct really useful?
|
||||
struct BlockLocation {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
uint8_t lod;
|
||||
|
||||
static uint64_t encode(BlockLocation loc) {
|
||||
static bool validate(const Vector3i pos, uint8_t lod) {
|
||||
ERR_FAIL_COND_V(pos.x < std::numeric_limits<int16_t>::min(), false);
|
||||
ERR_FAIL_COND_V(pos.y < std::numeric_limits<int16_t>::min(), false);
|
||||
ERR_FAIL_COND_V(pos.z < std::numeric_limits<int16_t>::min(), false);
|
||||
ERR_FAIL_COND_V(pos.x > std::numeric_limits<int16_t>::max(), false);
|
||||
ERR_FAIL_COND_V(pos.y > std::numeric_limits<int16_t>::max(), false);
|
||||
ERR_FAIL_COND_V(pos.z > std::numeric_limits<int16_t>::max(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t encode() const {
|
||||
// 0l xx yy zz
|
||||
return ((static_cast<uint64_t>(loc.lod) & 0xffff) << 48) |
|
||||
((static_cast<uint64_t>(loc.x) & 0xffff) << 32) |
|
||||
((static_cast<uint64_t>(loc.y) & 0xffff) << 16) |
|
||||
(static_cast<uint64_t>(loc.z) & 0xffff);
|
||||
return ((static_cast<uint64_t>(lod) & 0xffff) << 48) |
|
||||
((static_cast<uint64_t>(x) & 0xffff) << 32) |
|
||||
((static_cast<uint64_t>(y) & 0xffff) << 16) |
|
||||
(static_cast<uint64_t>(z) & 0xffff);
|
||||
}
|
||||
|
||||
static BlockLocation decode(uint64_t id) {
|
||||
@ -47,6 +59,11 @@ public:
|
||||
FixedArray<Channel, VoxelBuffer::MAX_CHANNELS> channels;
|
||||
};
|
||||
|
||||
enum BlockType {
|
||||
VOXELS,
|
||||
INSTANCES
|
||||
};
|
||||
|
||||
VoxelStreamSQLiteInternal();
|
||||
~VoxelStreamSQLiteInternal();
|
||||
|
||||
@ -54,13 +71,21 @@ public:
|
||||
void close();
|
||||
|
||||
bool is_open() const { return _db != nullptr; }
|
||||
|
||||
// Returns the file path from SQLite
|
||||
const char *get_file_path() const;
|
||||
|
||||
// Return the file path that was used to open the connection.
|
||||
// You may use this one if you want determinism, as SQLite seems to globalize its path.
|
||||
const char *get_opened_file_path() const {
|
||||
return _opened_path.c_str();
|
||||
}
|
||||
|
||||
bool begin_transaction();
|
||||
bool end_transaction();
|
||||
|
||||
bool save_block(BlockLocation loc, const std::vector<uint8_t> &block_data);
|
||||
VoxelStream::Result load_block(BlockLocation loc, std::vector<uint8_t> &out_block_data);
|
||||
bool save_block(BlockLocation loc, const std::vector<uint8_t> &block_data, BlockType type);
|
||||
VoxelStream::Result load_block(BlockLocation loc, std::vector<uint8_t> &out_block_data, BlockType type);
|
||||
|
||||
Meta load_meta();
|
||||
void save_meta(Meta meta);
|
||||
@ -93,11 +118,14 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
std::string _opened_path;
|
||||
sqlite3 *_db = nullptr;
|
||||
sqlite3_stmt *_begin_statement = nullptr;
|
||||
sqlite3_stmt *_end_statement = nullptr;
|
||||
sqlite3_stmt *_update_block_statement = nullptr;
|
||||
sqlite3_stmt *_get_block_statement = nullptr;
|
||||
sqlite3_stmt *_update_voxel_block_statement = nullptr;
|
||||
sqlite3_stmt *_get_voxel_block_statement = nullptr;
|
||||
sqlite3_stmt *_update_instance_block_statement = nullptr;
|
||||
sqlite3_stmt *_get_instance_block_statement = nullptr;
|
||||
sqlite3_stmt *_load_meta_statement = nullptr;
|
||||
sqlite3_stmt *_save_meta_statement = nullptr;
|
||||
sqlite3_stmt *_load_channels_statement = nullptr;
|
||||
@ -112,6 +140,7 @@ VoxelStreamSQLiteInternal::~VoxelStreamSQLiteInternal() {
|
||||
}
|
||||
|
||||
bool VoxelStreamSQLiteInternal::open(const char *fpath) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
close();
|
||||
|
||||
int rc = sqlite3_open_v2(fpath, &_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
|
||||
@ -141,11 +170,20 @@ bool VoxelStreamSQLiteInternal::open(const char *fpath) {
|
||||
}
|
||||
|
||||
// Prepare statements
|
||||
if (!prepare(db, &_update_block_statement,
|
||||
"INSERT INTO blocks VALUES (:loc, :vb, null) ON CONFLICT(loc) DO UPDATE SET vb=excluded.vb")) {
|
||||
if (!prepare(db, &_update_voxel_block_statement,
|
||||
"INSERT INTO blocks VALUES (:loc, :vb, null) "
|
||||
"ON CONFLICT(loc) DO UPDATE SET vb=excluded.vb")) {
|
||||
return false;
|
||||
}
|
||||
if (!prepare(db, &_get_block_statement, "SELECT vb FROM blocks WHERE loc=:loc")) {
|
||||
if (!prepare(db, &_get_voxel_block_statement, "SELECT vb FROM blocks WHERE loc=:loc")) {
|
||||
return false;
|
||||
}
|
||||
if (!prepare(db, &_update_instance_block_statement,
|
||||
"INSERT INTO blocks VALUES (:loc, null, :instances) "
|
||||
"ON CONFLICT(loc) DO UPDATE SET instances=excluded.instances")) {
|
||||
return false;
|
||||
}
|
||||
if (!prepare(db, &_get_instance_block_statement, "SELECT instances FROM blocks WHERE loc=:loc")) {
|
||||
return false;
|
||||
}
|
||||
if (!prepare(db, &_begin_statement, "BEGIN")) {
|
||||
@ -164,7 +202,8 @@ bool VoxelStreamSQLiteInternal::open(const char *fpath) {
|
||||
return false;
|
||||
}
|
||||
if (!prepare(db, &_save_channel_statement,
|
||||
"INSERT INTO channels VALUES (:idx, :depth) ON CONFLICT(idx) DO UPDATE SET depth=excluded.depth")) {
|
||||
"INSERT INTO channels VALUES (:idx, :depth) "
|
||||
"ON CONFLICT(idx) DO UPDATE SET depth=excluded.depth")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -183,6 +222,7 @@ bool VoxelStreamSQLiteInternal::open(const char *fpath) {
|
||||
save_meta(meta);
|
||||
}
|
||||
|
||||
_opened_path = fpath;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -192,14 +232,17 @@ void VoxelStreamSQLiteInternal::close() {
|
||||
}
|
||||
finalize(_begin_statement);
|
||||
finalize(_end_statement);
|
||||
finalize(_update_block_statement);
|
||||
finalize(_get_block_statement);
|
||||
finalize(_update_voxel_block_statement);
|
||||
finalize(_get_voxel_block_statement);
|
||||
finalize(_update_instance_block_statement);
|
||||
finalize(_get_instance_block_statement);
|
||||
finalize(_load_meta_statement);
|
||||
finalize(_save_meta_statement);
|
||||
finalize(_load_channels_statement);
|
||||
finalize(_save_channel_statement);
|
||||
sqlite3_close(_db);
|
||||
_db = nullptr;
|
||||
_opened_path.clear();
|
||||
}
|
||||
|
||||
const char *VoxelStreamSQLiteInternal::get_file_path() const {
|
||||
@ -237,11 +280,22 @@ bool VoxelStreamSQLiteInternal::end_transaction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoxelStreamSQLiteInternal::save_block(BlockLocation loc, const std::vector<uint8_t> &block_data) {
|
||||
bool VoxelStreamSQLiteInternal::save_block(BlockLocation loc, const std::vector<uint8_t> &block_data, BlockType type) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
sqlite3 *db = _db;
|
||||
sqlite3_stmt *update_block_statement = _update_block_statement;
|
||||
|
||||
sqlite3_stmt *update_block_statement;
|
||||
switch (type) {
|
||||
case VOXELS:
|
||||
update_block_statement = _update_voxel_block_statement;
|
||||
break;
|
||||
case INSTANCES:
|
||||
update_block_statement = _update_instance_block_statement;
|
||||
break;
|
||||
default:
|
||||
CRASH_NOW();
|
||||
}
|
||||
|
||||
int rc = sqlite3_reset(update_block_statement);
|
||||
if (rc != SQLITE_OK) {
|
||||
@ -249,7 +303,7 @@ bool VoxelStreamSQLiteInternal::save_block(BlockLocation loc, const std::vector<
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t eloc = BlockLocation::encode(loc);
|
||||
const uint64_t eloc = loc.encode();
|
||||
|
||||
rc = sqlite3_bind_int64(update_block_statement, 1, eloc);
|
||||
if (rc != SQLITE_OK) {
|
||||
@ -257,8 +311,12 @@ bool VoxelStreamSQLiteInternal::save_block(BlockLocation loc, const std::vector<
|
||||
return false;
|
||||
}
|
||||
|
||||
// We use SQLITE_TRANSIENT so SQLite will make its own copy of the data
|
||||
rc = sqlite3_bind_blob(update_block_statement, 2, block_data.data(), block_data.size(), SQLITE_TRANSIENT);
|
||||
if (block_data.size() == 0) {
|
||||
rc = sqlite3_bind_null(update_block_statement, 2);
|
||||
} else {
|
||||
// We use SQLITE_TRANSIENT so SQLite will make its own copy of the data
|
||||
rc = sqlite3_bind_blob(update_block_statement, 2, block_data.data(), block_data.size(), SQLITE_TRANSIENT);
|
||||
}
|
||||
if (rc != SQLITE_OK) {
|
||||
ERR_PRINT(sqlite3_errmsg(db));
|
||||
return false;
|
||||
@ -273,9 +331,22 @@ bool VoxelStreamSQLiteInternal::save_block(BlockLocation loc, const std::vector<
|
||||
return true;
|
||||
}
|
||||
|
||||
VoxelStream::Result VoxelStreamSQLiteInternal::load_block(BlockLocation loc, std::vector<uint8_t> &out_block_data) {
|
||||
VoxelStream::Result VoxelStreamSQLiteInternal::load_block(
|
||||
BlockLocation loc, std::vector<uint8_t> &out_block_data, BlockType type) {
|
||||
|
||||
sqlite3 *db = _db;
|
||||
sqlite3_stmt *get_block_statement = _get_block_statement;
|
||||
|
||||
sqlite3_stmt *get_block_statement;
|
||||
switch (type) {
|
||||
case VOXELS:
|
||||
get_block_statement = _get_voxel_block_statement;
|
||||
break;
|
||||
case INSTANCES:
|
||||
get_block_statement = _get_instance_block_statement;
|
||||
break;
|
||||
default:
|
||||
CRASH_NOW();
|
||||
}
|
||||
|
||||
int rc;
|
||||
|
||||
@ -285,7 +356,7 @@ VoxelStream::Result VoxelStreamSQLiteInternal::load_block(BlockLocation loc, std
|
||||
return VoxelStream::RESULT_ERROR;
|
||||
}
|
||||
|
||||
const uint64_t eloc = BlockLocation::encode(loc);
|
||||
const uint64_t eloc = loc.encode();
|
||||
|
||||
rc = sqlite3_bind_int64(get_block_statement, 1, eloc);
|
||||
if (rc != SQLITE_OK) {
|
||||
@ -450,20 +521,25 @@ void VoxelStreamSQLiteInternal::save_meta(Meta meta) {
|
||||
|
||||
thread_local VoxelBlockSerializerInternal VoxelStreamSQLite::_voxel_block_serializer;
|
||||
thread_local std::vector<uint8_t> VoxelStreamSQLite::_temp_block_data;
|
||||
thread_local std::vector<uint8_t> VoxelStreamSQLite::_temp_compressed_block_data;
|
||||
|
||||
VoxelStreamSQLite::VoxelStreamSQLite() {
|
||||
_connection_mutex = Mutex::create();
|
||||
}
|
||||
|
||||
VoxelStreamSQLite::~VoxelStreamSQLite() {
|
||||
PRINT_VERBOSE("~VoxelStreamSQLite");
|
||||
if (!_connection_path.empty() && _cache.get_indicative_block_count() > 0) {
|
||||
PRINT_VERBOSE("~VoxelStreamSQLite flushy flushy");
|
||||
flush_cache();
|
||||
PRINT_VERBOSE("~VoxelStreamSQLite flushy done");
|
||||
}
|
||||
for (auto it = _connection_pool.begin(); it != _connection_pool.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
_connection_pool.clear();
|
||||
memdelete(_connection_mutex);
|
||||
PRINT_VERBOSE("~VoxelStreamSQLite done");
|
||||
}
|
||||
|
||||
void VoxelStreamSQLite::set_database_path(String path) {
|
||||
@ -519,27 +595,21 @@ void VoxelStreamSQLite::immerge_block(Ref<VoxelBuffer> buffer, Vector3i origin_i
|
||||
}
|
||||
|
||||
void VoxelStreamSQLite::emerge_blocks(Vector<VoxelBlockRequest> &p_blocks, Vector<Result> &out_results) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
// TODO Get block size from database
|
||||
const int bs_po2 = 4;
|
||||
const int bs_po2 = VoxelConstants::DEFAULT_BLOCK_SIZE_PO2;
|
||||
|
||||
out_results.resize(p_blocks.size());
|
||||
|
||||
// Check the cache first
|
||||
Vector<int> blocks_to_load;
|
||||
for (int i = 0; i < p_blocks.size(); ++i) {
|
||||
const VoxelBlockRequest &r = p_blocks[i];
|
||||
const Vector3i pos = r.origin_in_voxels >> bs_po2;
|
||||
VoxelBlockRequest &wr = p_blocks.write[i];
|
||||
const Vector3i pos = wr.origin_in_voxels >> bs_po2;
|
||||
|
||||
Ref<VoxelBuffer> vb;
|
||||
if (_cache.load_voxel_block(pos, r.lod, vb)) {
|
||||
VoxelBlockRequest &wr = p_blocks.write[i];
|
||||
|
||||
// Copying is required since the cache has ownership on its data,
|
||||
// and the requests wants us to populate the buffer it provides
|
||||
wr.voxel_buffer->copy_format(**vb);
|
||||
wr.voxel_buffer->copy_from(**vb);
|
||||
wr.voxel_buffer->copy_voxel_metadata(**vb);
|
||||
|
||||
if (_cache.load_voxel_block(pos, wr.lod, wr.voxel_buffer)) {
|
||||
out_results.write[i] = RESULT_BLOCK_FOUND;
|
||||
|
||||
} else {
|
||||
@ -568,7 +638,7 @@ void VoxelStreamSQLite::emerge_blocks(Vector<VoxelBlockRequest> &p_blocks, Vecto
|
||||
loc.z = r.origin_in_voxels.z >> bs_po2;
|
||||
loc.lod = r.lod;
|
||||
|
||||
const Result res = con->load_block(loc, _temp_block_data);
|
||||
const Result res = con->load_block(loc, _temp_block_data, VoxelStreamSQLiteInternal::VOXELS);
|
||||
|
||||
if (res == RESULT_BLOCK_FOUND) {
|
||||
VoxelBlockRequest &wr = p_blocks.write[ri];
|
||||
@ -587,15 +657,109 @@ void VoxelStreamSQLite::emerge_blocks(Vector<VoxelBlockRequest> &p_blocks, Vecto
|
||||
|
||||
void VoxelStreamSQLite::immerge_blocks(const Vector<VoxelBlockRequest> &p_blocks) {
|
||||
// TODO Get block size from database
|
||||
const int bs_po2 = 4;
|
||||
const int bs_po2 = VoxelConstants::DEFAULT_BLOCK_SIZE_PO2;
|
||||
|
||||
// First put in cache
|
||||
for (int i = 0; i < p_blocks.size(); ++i) {
|
||||
const VoxelBlockRequest &r = p_blocks[i];
|
||||
const Vector3i pos = r.origin_in_voxels >> bs_po2;
|
||||
|
||||
if (!BlockLocation::validate(pos, r.lod)) {
|
||||
ERR_PRINT(String("Block position {0} is outside of supported range").format(varray(pos.to_vec3())));
|
||||
continue;
|
||||
}
|
||||
|
||||
_cache.save_voxel_block(pos, r.lod, r.voxel_buffer);
|
||||
}
|
||||
|
||||
// TODO We should consider using a serialized cache, and measure the threshold in bytes
|
||||
if (_cache.get_indicative_block_count() >= CACHE_SIZE) {
|
||||
flush_cache();
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelStreamSQLite::supports_instance_blocks() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void VoxelStreamSQLite::load_instance_blocks(
|
||||
ArraySlice<VoxelStreamInstanceDataRequest> out_blocks, ArraySlice<Result> out_results) {
|
||||
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
// TODO Get block size from database
|
||||
const int bs_po2 = VoxelConstants::DEFAULT_BLOCK_SIZE_PO2;
|
||||
|
||||
// Check the cache first
|
||||
Vector<int> blocks_to_load;
|
||||
for (int i = 0; i < out_blocks.size(); ++i) {
|
||||
VoxelStreamInstanceDataRequest &r = out_blocks[i];
|
||||
|
||||
if (_cache.load_instance_block(r.position, r.lod, r.data)) {
|
||||
out_results[i] = RESULT_BLOCK_FOUND;
|
||||
|
||||
} else {
|
||||
blocks_to_load.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (blocks_to_load.size() == 0) {
|
||||
// Everything was cached, no need to query the database
|
||||
return;
|
||||
}
|
||||
|
||||
VoxelStreamSQLiteInternal *con = get_connection();
|
||||
ERR_FAIL_COND(con == nullptr);
|
||||
|
||||
// TODO We should handle busy return codes
|
||||
// TODO recycle on error
|
||||
ERR_FAIL_COND(con->begin_transaction() == false);
|
||||
|
||||
for (int i = 0; i < blocks_to_load.size(); ++i) {
|
||||
const int ri = blocks_to_load[i];
|
||||
VoxelStreamInstanceDataRequest &r = out_blocks[ri];
|
||||
|
||||
BlockLocation loc;
|
||||
loc.x = r.position.x;
|
||||
loc.y = r.position.y;
|
||||
loc.z = r.position.z;
|
||||
loc.lod = r.lod;
|
||||
|
||||
const Result res = con->load_block(loc, _temp_compressed_block_data, VoxelStreamSQLiteInternal::INSTANCES);
|
||||
|
||||
if (res == RESULT_BLOCK_FOUND) {
|
||||
if (!VoxelCompressedData::decompress(to_slice_const(_temp_compressed_block_data), _temp_block_data)) {
|
||||
ERR_PRINT("Failed to decompress instance block");
|
||||
out_results[i] = RESULT_ERROR;
|
||||
continue;
|
||||
}
|
||||
r.data = std::make_unique<VoxelInstanceBlockData>();
|
||||
if (!deserialize_instance_block_data(*r.data, to_slice_const(_temp_block_data))) {
|
||||
ERR_PRINT("Failed to deserialize instance block");
|
||||
out_results[i] = RESULT_ERROR;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
out_results[i] = res;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(con->end_transaction() == false);
|
||||
|
||||
recycle_connection(con);
|
||||
}
|
||||
|
||||
void VoxelStreamSQLite::save_instance_blocks(ArraySlice<VoxelStreamInstanceDataRequest> p_blocks) {
|
||||
// TODO Get block size from database
|
||||
const int bs_po2 = VoxelConstants::DEFAULT_BLOCK_SIZE_PO2;
|
||||
|
||||
// First put in cache
|
||||
for (int i = 0; i < p_blocks.size(); ++i) {
|
||||
VoxelStreamInstanceDataRequest &r = p_blocks[i];
|
||||
_cache.save_instance_block(r.position, r.lod, std::move(r.data));
|
||||
}
|
||||
|
||||
// TODO We should consider using a serialized cache, and measure the threshold in bytes
|
||||
if (_cache.get_indicative_block_count() >= CACHE_SIZE) {
|
||||
flush_cache();
|
||||
}
|
||||
@ -619,17 +783,44 @@ void VoxelStreamSQLite::flush_cache(VoxelStreamSQLiteInternal *con) {
|
||||
ERR_FAIL_COND(con == nullptr);
|
||||
ERR_FAIL_COND(con->begin_transaction() == false);
|
||||
|
||||
_cache.flush([&serializer, con](VoxelStreamCache::Block &block) {
|
||||
std::vector<uint8_t> &temp_data = _temp_block_data;
|
||||
std::vector<uint8_t> &temp_compressed_data = _temp_compressed_block_data;
|
||||
|
||||
// TODO Needs better error rollback handling
|
||||
_cache.flush([&serializer, con, &temp_data, &temp_compressed_data](VoxelStreamCache::Block &block) {
|
||||
ERR_FAIL_COND(!BlockLocation::validate(block.position, block.lod));
|
||||
|
||||
BlockLocation loc;
|
||||
loc.x = block.position.x;
|
||||
loc.y = block.position.y;
|
||||
loc.z = block.position.z;
|
||||
loc.lod = block.lod;
|
||||
// TODO We might want to handle null blocks too, in case they intentionally get deleted
|
||||
ERR_FAIL_COND(block.voxels.is_null());
|
||||
VoxelBlockSerializerInternal::SerializeResult res = serializer.serialize_and_compress(**block.voxels);
|
||||
ERR_FAIL_COND(!res.success);
|
||||
con->save_block(loc, res.data);
|
||||
|
||||
// Save voxels
|
||||
if (block.has_voxels) {
|
||||
if (block.voxels.is_valid()) {
|
||||
VoxelBlockSerializerInternal::SerializeResult res = serializer.serialize_and_compress(**block.voxels);
|
||||
ERR_FAIL_COND(!res.success);
|
||||
con->save_block(loc, res.data, VoxelStreamSQLiteInternal::VOXELS);
|
||||
} else {
|
||||
const std::vector<uint8_t> empty;
|
||||
con->save_block(loc, empty, VoxelStreamSQLiteInternal::VOXELS);
|
||||
}
|
||||
}
|
||||
|
||||
// Save instances
|
||||
temp_compressed_data.clear();
|
||||
if (block.instances != nullptr) {
|
||||
temp_data.clear();
|
||||
|
||||
serialize_instance_block_data(*block.instances, temp_data);
|
||||
|
||||
ERR_FAIL_COND(!VoxelCompressedData::compress(
|
||||
to_slice_const(temp_data), temp_compressed_data, VoxelCompressedData::COMPRESSION_NONE));
|
||||
}
|
||||
con->save_block(loc, temp_compressed_data, VoxelStreamSQLiteInternal::INSTANCES);
|
||||
|
||||
// TODO Optimization: add a version of the query that can update both at once
|
||||
});
|
||||
|
||||
ERR_FAIL_COND(con->end_transaction() == false);
|
||||
@ -663,7 +854,7 @@ VoxelStreamSQLiteInternal *VoxelStreamSQLite::get_connection() {
|
||||
}
|
||||
|
||||
void VoxelStreamSQLite::recycle_connection(VoxelStreamSQLiteInternal *con) {
|
||||
String con_path = con->get_file_path();
|
||||
String con_path = con->get_opened_file_path();
|
||||
_connection_mutex->lock();
|
||||
// If path differs, delete this connection
|
||||
if (_connection_path != con_path) {
|
||||
|
@ -27,6 +27,11 @@ public:
|
||||
void emerge_blocks(Vector<VoxelBlockRequest> &p_blocks, Vector<Result> &out_results) override;
|
||||
void immerge_blocks(const Vector<VoxelBlockRequest> &p_blocks) override;
|
||||
|
||||
bool supports_instance_blocks() const override;
|
||||
void load_instance_blocks(
|
||||
ArraySlice<VoxelStreamInstanceDataRequest> out_blocks, ArraySlice<Result> out_results) override;
|
||||
void save_instance_blocks(ArraySlice<VoxelStreamInstanceDataRequest> p_blocks) override;
|
||||
|
||||
void flush_cache();
|
||||
|
||||
private:
|
||||
@ -58,6 +63,7 @@ private:
|
||||
// TODO I should consider specialized memory allocators
|
||||
static thread_local VoxelBlockSerializerInternal _voxel_block_serializer;
|
||||
static thread_local std::vector<uint8_t> _temp_block_data;
|
||||
static thread_local std::vector<uint8_t> _temp_compressed_block_data;
|
||||
};
|
||||
|
||||
#endif // VOXEL_STREAM_SQLITE_H
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "../math/vector3i.h"
|
||||
#include "../storage/voxel_buffer.h"
|
||||
#include "instance_data.h"
|
||||
#include <memory>
|
||||
|
||||
// TODO Rename VoxelStreamBlockRequest
|
||||
struct VoxelBlockRequest {
|
||||
@ -11,4 +13,10 @@ struct VoxelBlockRequest {
|
||||
int lod;
|
||||
};
|
||||
|
||||
struct VoxelStreamInstanceDataRequest {
|
||||
std::unique_ptr<VoxelInstanceBlockData> data;
|
||||
Vector3i position;
|
||||
uint8_t lod;
|
||||
};
|
||||
|
||||
#endif // VOXEL_BLOCK_REQUEST_H
|
||||
|
@ -2,9 +2,9 @@
|
||||
#include "../math/vector3i.h"
|
||||
#include "../storage/voxel_buffer.h"
|
||||
#include "../storage/voxel_memory_pool.h"
|
||||
#include "../thirdparty/lz4/lz4.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "compressed_data.h"
|
||||
|
||||
#include <core/io/marshalls.h>
|
||||
#include <core/io/stream_peer.h>
|
||||
@ -12,112 +12,6 @@
|
||||
#include <core/os/file_access.h>
|
||||
#include <limits>
|
||||
|
||||
namespace VoxelCompressedData {
|
||||
|
||||
// Compressed data starts with a single byte telling which compression format is used.
|
||||
// What follows depends on it.
|
||||
|
||||
enum Compression {
|
||||
// No compression. All following bytes can be read as-is.
|
||||
// Could be used for debugging.
|
||||
COMPRESSION_NONE = 0,
|
||||
// The next uint32_t will be the size of decompressed data.
|
||||
// All following bytes are compressed data using LZ4 defaults.
|
||||
// This is the fastest compression format.
|
||||
COMPRESSION_LZ4 = 1,
|
||||
COMPRESSION_COUNT = 2
|
||||
};
|
||||
|
||||
bool decompress(ArraySlice<const uint8_t> src, std::vector<uint8_t> &dst) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
FileAccessMemory f;
|
||||
f.open_custom(src.data(), src.size());
|
||||
|
||||
const Compression comp = static_cast<Compression>(f.get_8());
|
||||
ERR_FAIL_INDEX_V(comp, COMPRESSION_COUNT, false);
|
||||
|
||||
switch (comp) {
|
||||
case COMPRESSION_NONE: {
|
||||
// We still have to do a copy. The point of this container is compression,
|
||||
// so we don't worry too much about the performance impact of not using `src` directly.
|
||||
dst.resize(src.size() - 1);
|
||||
memcpy(dst.data(), src.data() + 1, dst.size());
|
||||
} break;
|
||||
|
||||
case COMPRESSION_LZ4: {
|
||||
const uint32_t decompressed_size = f.get_32();
|
||||
const uint32_t header_size = sizeof(uint8_t) + sizeof(uint32_t);
|
||||
|
||||
dst.resize(decompressed_size);
|
||||
|
||||
const uint32_t actually_decompressed_size = LZ4_decompress_safe(
|
||||
(const char *)src.data() + header_size,
|
||||
(char *)dst.data(),
|
||||
src.size() - header_size,
|
||||
dst.size());
|
||||
|
||||
ERR_FAIL_COND_V_MSG(actually_decompressed_size < 0, false,
|
||||
String("LZ4 decompression error {0}").format(varray(actually_decompressed_size)));
|
||||
|
||||
ERR_FAIL_COND_V_MSG(actually_decompressed_size != decompressed_size, false,
|
||||
String("Expected {0} bytes, obtained {1}")
|
||||
.format(varray(decompressed_size, actually_decompressed_size)));
|
||||
} break;
|
||||
|
||||
default:
|
||||
ERR_PRINT("Invalid compression header");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compress(ArraySlice<const uint8_t> src, std::vector<uint8_t> &dst, Compression comp) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
switch (comp) {
|
||||
case COMPRESSION_NONE: {
|
||||
dst.resize(src.size() + 1);
|
||||
dst[0] = comp;
|
||||
memcpy(dst.data() + 1, src.data(), src.size());
|
||||
} break;
|
||||
|
||||
case COMPRESSION_LZ4: {
|
||||
const uint32_t header_size = sizeof(uint8_t) + sizeof(uint32_t);
|
||||
dst.resize(header_size + LZ4_compressBound(src.size()));
|
||||
|
||||
// Write header
|
||||
FileAccessMemory f;
|
||||
f.open_custom(dst.data(), dst.size());
|
||||
f.store_8(comp);
|
||||
f.store_32(src.size());
|
||||
f.close();
|
||||
|
||||
const uint32_t compressed_size = LZ4_compress_default(
|
||||
(const char *)src.data(),
|
||||
(char *)dst.data() + header_size,
|
||||
src.size(),
|
||||
dst.size() - header_size);
|
||||
|
||||
ERR_FAIL_COND_V(compressed_size < 0, false);
|
||||
ERR_FAIL_COND_V(compressed_size == 0, false);
|
||||
|
||||
dst.resize(header_size + compressed_size);
|
||||
} break;
|
||||
|
||||
default:
|
||||
ERR_PRINT("Invalid compression header");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace VoxelCompressedData
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
const uint8_t BLOCK_VERSION = 2;
|
||||
const unsigned int BLOCK_TRAILING_MAGIC = 0x900df00d;
|
||||
|