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
* Allow to export the palette to png
* Allow to generate models from heightmap images
* Allow to run lua scripts to modify volumes
* Thumbnailer:
* 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
- Allow to export the palette to png
- Allow to generate models from heightmap images
- Allow to run lua scripts to modify volumes
Thumbnailer:

View File

@ -1,11 +1,29 @@
# 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`.
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
```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.
# LayerManager
# LayerManager (voxedit)
`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.
# Layer
# Layer (voxedit)
* `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.
![noise](lua-noise.png)
![noise](img/lua-noise.png)
`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.
![cover](lua-cover.png)
![cover](img/lua-cover.png)
`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.
![pyramid](lua-pyramid.png)
![pyramid](img/lua-pyramid.png)
`xs pyramid.lua 3`
## thicken.lua
## thicken.lua (voxedit)
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`
@ -192,6 +210,6 @@ Thickens the voxel - take 1 voxel and convert to 8 voxels.
Generate grass on top of voxels.
![grass](grass.png)
![grass](img/grass.png)
`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_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
* LUA [scripting](LUAScript.md) api
* LUA [scripting](../LUAScript.md) api
* Layer support
* Auto cropping volumes
* Auto generate content like trees or noise volumes

View File

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

View File

@ -4,4 +4,4 @@ set(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/GameConfig.h"
#include "core/StringUtil.h"
#include "core/Tokenizer.h"
#include "core/Var.h"
#include "command/Command.h"
#include "image/Image.h"
@ -17,6 +18,7 @@
#include "voxel/MaterialColor.h"
#include "voxelformat/VolumeFormat.h"
#include "voxelformat/Format.h"
#include "voxelgenerator/LUAGenerator.h"
#include "voxelutil/ImageUtils.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("--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("--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->setHelp("Merge similar quads to optimize the mesh");
@ -51,6 +54,10 @@ app::AppState VoxConvert::onConstruct() {
_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)");
if (!filesystem()->registerPath("scripts/")) {
Log::warn("Failed to register lua generator script path");
}
return state;
}
@ -100,6 +107,11 @@ app::AppState VoxConvert::onInit() {
}
Log::info("* infile: - %s", infile.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("* scale volumes: - %s", (scaleVolumes ? "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");
if (!voxelformat::saveFormat(outputFile, volumes)) {
voxelformat::clearVolumes(volumes);