VOXCONVERT: added scripting support

master
Martin Gerhardy 2021-12-15 22:31:35 +01:00
parent fb37f11b36
commit 36a873a333
14 changed files with 77 additions and 14 deletions

1
debian/changelog vendored
View File

@ -12,6 +12,7 @@ vengi (0.0.15.0-1) UNRELEASED; urgency=low
* Added option to keep the input file palette and don't perform quantization * Added option to keep the input file palette and don't perform quantization
* Allow to export the palette to png * Allow to export the palette to png
* Allow to generate models from heightmap images * Allow to generate models from heightmap images
* Allow to run lua scripts to modify volumes
* Thumbnailer: * Thumbnailer:
* Try to use the built-in palette for models * Try to use the built-in palette for models

View File

@ -18,6 +18,7 @@ VoxConvert:
- Added option to keep the input file palette and don't perform quantization - Added option to keep the input file palette and don't perform quantization
- Allow to export the palette to png - Allow to export the palette to png
- Allow to generate models from heightmap images - Allow to generate models from heightmap images
- Allow to run lua scripts to modify volumes
Thumbnailer: Thumbnailer:

View File

@ -1,11 +1,29 @@
# Scripting api # Scripting api
There is a console command (called `xs`) in voxedit to execute lua scripts for generating voxels. This command expects the lua script filename (`.lua` can be omitted) and the additional arguments for the `main()` method. There is a console command (called `xs`) in [voxedit](voxedit/Index.md) and a command line parameter in [voxconvert](voxconvert/Index.md) to execute lua scripts for generating voxels. This command expects the lua script filename (`.lua` can be omitted) and the additional arguments for the `main()` method.
Calling `xs <script> help` will print the supported arguments for the given script. ---
> **voxedit**
>
> Calling `xs <script> help` (in the script console) will print the supported arguments for the given script file in voxedit.
---
> **voxconvert**
>
> ```
> ./vengi-voxconvert --script "<script> help" in.qb out.qb
> ```
---
By default the script files will be searched in a `scripts` folder next to where the binary is located and in the usual search paths (see [configuration](Configuration.md) for more details). You can also give the full path to the script file.
There are two functions in each script. One is called `arguments` and one `main`. `arguments` returns a list of parameters for the `main` function. The default parameters for `main` are `volume`, `region` and `color`. `color` is the palette index starting from `0`. There are two functions in each script. One is called `arguments` and one `main`. `arguments` returns a list of parameters for the `main` function. The default parameters for `main` are `volume`, `region` and `color`. `color` is the palette index starting from `0`.
Those functionalities that are marked with `voxedit` are not available outside of the editor (e.g. for the command line tools like [voxconvert](voxconvert/Index.md)).
# Example without parameters # Example without parameters
```lua ```lua
@ -57,7 +75,7 @@ A `default` value can get set, too.
The order in the arguments table defines the order in which the arguments are passed over to the script. The order in the arguments table defines the order in which the arguments are passed over to the script.
# LayerManager # LayerManager (voxedit)
`layerMgr` lets you access different layers or create new ones. `layerMgr` lets you access different layers or create new ones.
@ -67,7 +85,7 @@ The functions are:
* `get([layerId])`: Returns the `layer` for the given `layerId` - if the `layerId` is not given, it will return the current active layer. Which by default is the layer for the volume the script is currently executed for. * `get([layerId])`: Returns the `layer` for the given `layerId` - if the `layerId` is not given, it will return the current active layer. Which by default is the layer for the volume the script is currently executed for.
# Layer # Layer (voxedit)
* `name()`: Returns the current name of the layer. * `name()`: Returns the current name of the layer.
@ -160,7 +178,7 @@ To get a full list of commands and cvars use the console command `cmdlist` and `
Generates perlin noise with the frequency and amplitude as parameters with the current selected color. Generates perlin noise with the frequency and amplitude as parameters with the current selected color.
![noise](lua-noise.png) ![noise](img/lua-noise.png)
`xs noise.lua 0.3 1.0` `xs noise.lua 0.3 1.0`
@ -168,7 +186,7 @@ Generates perlin noise with the frequency and amplitude as parameters with the c
Generates a new voxel on top of others with the current selected color and the specified height. Generates a new voxel on top of others with the current selected color and the specified height.
![cover](lua-cover.png) ![cover](img/lua-cover.png)
`xs cover.lua 1` `xs cover.lua 1`
@ -176,15 +194,15 @@ Generates a new voxel on top of others with the current selected color and the s
Generates a pyramid with the current selected color and with each level being 3 voxels high. Generates a pyramid with the current selected color and with each level being 3 voxels high.
![pyramid](lua-pyramid.png) ![pyramid](img/lua-pyramid.png)
`xs pyramid.lua 3` `xs pyramid.lua 3`
## thicken.lua ## thicken.lua (voxedit)
Thickens the voxel - take 1 voxel and convert to 8 voxels. Thickens the voxel - take 1 voxel and convert to 8 voxels.
![thickenbefore](lua-thicken-before.png) ![thickenafter](lua-thicken-after.png) ![thickenbefore](img/lua-thicken-before.png) ![thickenafter](img/lua-thicken-after.png)
`xs thicken.lua 1` `xs thicken.lua 1`
@ -192,6 +210,6 @@ Thickens the voxel - take 1 voxel and convert to 8 voxels.
Generate grass on top of voxels. Generate grass on top of voxels.
![grass](grass.png) ![grass](img/grass.png)
`xs grass.lua` `xs grass.lua`

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -31,4 +31,4 @@ You can also use other user/password combinations by setting some cvars:
* **db_pw** * **db_pw**
* **db_user** * **db_user**
See the [configuration](Configuration.md) documentation for more details. See the [configuration](../Configuration.md) documentation for more details.

View File

@ -8,7 +8,7 @@ You can load and save a lot of different [voxel formats](../Formats.md).
## Features ## Features
* LUA [scripting](LUAScript.md) api * LUA [scripting](../LUAScript.md) api
* Layer support * Layer support
* Auto cropping volumes * Auto cropping volumes
* Auto generate content like trees or noise volumes * Auto generate content like trees or noise volumes

View File

@ -24,9 +24,9 @@ nav:
- Configuration.md - Configuration.md
- Formats.md - Formats.md
- CHANGELOG.md - CHANGELOG.md
- LUAScript.md
- VoxEdit: - VoxEdit:
- voxedit/Index.md - voxedit/Index.md
- voxedit/LUAScript.md
- Games: - Games:
- OpenWorld: openworld/Index.md - OpenWorld: openworld/Index.md
- Tools: - Tools:

View File

@ -4,4 +4,4 @@ set(SRCS
) )
engine_add_executable(TARGET ${PROJECT_NAME} SRCS ${SRCS}) engine_add_executable(TARGET ${PROJECT_NAME} SRCS ${SRCS})
engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES app voxelformat) engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES app voxelformat voxelgenerator)

View File

@ -6,6 +6,7 @@
#include "core/Color.h" #include "core/Color.h"
#include "core/GameConfig.h" #include "core/GameConfig.h"
#include "core/StringUtil.h" #include "core/StringUtil.h"
#include "core/Tokenizer.h"
#include "core/Var.h" #include "core/Var.h"
#include "command/Command.h" #include "command/Command.h"
#include "image/Image.h" #include "image/Image.h"
@ -17,6 +18,7 @@
#include "voxel/MaterialColor.h" #include "voxel/MaterialColor.h"
#include "voxelformat/VolumeFormat.h" #include "voxelformat/VolumeFormat.h"
#include "voxelformat/Format.h" #include "voxelformat/Format.h"
#include "voxelgenerator/LUAGenerator.h"
#include "voxelutil/ImageUtils.h" #include "voxelutil/ImageUtils.h"
#include "voxelutil/VolumeRescaler.h" #include "voxelutil/VolumeRescaler.h"
@ -33,6 +35,7 @@ app::AppState VoxConvert::onConstruct() {
registerArg("--scale").setShort("-s").setDescription("Scale layer to 50% of its original size"); registerArg("--scale").setShort("-s").setDescription("Scale layer to 50% of its original size");
registerArg("--force").setShort("-f").setDescription("Overwrite existing files"); registerArg("--force").setShort("-f").setDescription("Overwrite existing files");
registerArg("--export-palette").setDescription("Export the used palette data into an image. Use in combination with --src-palette"); registerArg("--export-palette").setDescription("Export the used palette data into an image. Use in combination with --src-palette");
registerArg("--script").setDefaultValue("script.lua").setDescription("Apply the given lua script to the output volume");
_mergeQuads = core::Var::get(cfg::VoxformatMergequads, "true", core::CV_NOPERSIST); _mergeQuads = core::Var::get(cfg::VoxformatMergequads, "true", core::CV_NOPERSIST);
_mergeQuads->setHelp("Merge similar quads to optimize the mesh"); _mergeQuads->setHelp("Merge similar quads to optimize the mesh");
@ -51,6 +54,10 @@ app::AppState VoxConvert::onConstruct() {
_palette = core::Var::get("palette", voxel::getDefaultPaletteName()); _palette = core::Var::get("palette", voxel::getDefaultPaletteName());
_palette->setHelp("This is the NAME part of palette-<NAME>.png or absolute png file to use (1x256)"); _palette->setHelp("This is the NAME part of palette-<NAME>.png or absolute png file to use (1x256)");
if (!filesystem()->registerPath("scripts/")) {
Log::warn("Failed to register lua generator script path");
}
return state; return state;
} }
@ -100,6 +107,11 @@ app::AppState VoxConvert::onInit() {
} }
Log::info("* infile: - %s", infile.c_str()); Log::info("* infile: - %s", infile.c_str());
Log::info("* outfile: - %s", outfile.c_str()); Log::info("* outfile: - %s", outfile.c_str());
core::String scriptParameters;
if (hasArg("--script")) {
scriptParameters = getArgVal("--script");
Log::info("* script: - %s", scriptParameters.c_str());
}
Log::info("* merge volumes: - %s", (mergeVolumes ? "true" : "false")); Log::info("* merge volumes: - %s", (mergeVolumes ? "true" : "false"));
Log::info("* scale volumes: - %s", (scaleVolumes ? "true" : "false")); Log::info("* scale volumes: - %s", (scaleVolumes ? "true" : "false"));
Log::info("* use source file palette: - %s", (srcPalette ? "true" : "false")); Log::info("* use source file palette: - %s", (srcPalette ? "true" : "false"));
@ -204,6 +216,37 @@ app::AppState VoxConvert::onInit() {
} }
} }
if (!scriptParameters.empty()) {
voxelgenerator::LUAGenerator script;
if (!script.init()) {
Log::warn("Failed to initialize the script bindings");
} else {
core::DynamicArray<core::String> tokens;
core::string::splitString(scriptParameters, tokens);
const core::String &luaScript = script.load(tokens[0]);
if (luaScript.empty()) {
Log::error("Failed to load %s", tokens[0].c_str());
} else {
const voxel::Voxel voxel = voxel::createVoxel(voxel::VoxelType::Generic, 1);
core::DynamicArray<voxelgenerator::LUAParameterDescription> argsInfo;
if (!script.argumentInfo(luaScript, argsInfo)) {
Log::warn("Failed to get argument details");
}
core::DynamicArray<core::String> args(tokens.size() - 1);
for (size_t i = 1; i < tokens.size(); ++i) {
args[i - 1] = tokens[i];
}
Log::info("Execute script %s", tokens[0].c_str());
for (auto& v : volumes) {
voxel::RawVolumeWrapper wrapper(v.volume);
script.exec(luaScript, &wrapper, wrapper.region(), voxel, args);
}
}
}
script.shutdown();
}
Log::debug("Save"); Log::debug("Save");
if (!voxelformat::saveFormat(outputFile, volumes)) { if (!voxelformat::saveFormat(outputFile, volumes)) {
voxelformat::clearVolumes(volumes); voxelformat::clearVolumes(volumes);