diff --git a/docs/batch.md b/docs/batch.md index f155b76..34dfe89 100644 --- a/docs/batch.md +++ b/docs/batch.md @@ -8,7 +8,7 @@ See also the [GUI usage](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/usag ``` MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license -./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b|-B] [-r remapfile] [-m map] <.mts|.schematic> [out.mts] +./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b|-B|-V] [-r remapfile] [-m map] <.mts|.schematic|.tmx|.gox|.vox|.qb> [out.mts] ./mtsedit [-G|-C|-T] ./mtsedit -g ./mtsedit -t [blockid] @@ -25,6 +25,7 @@ MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license -g: generate slab and stairs from block image -t: generate block images from texture data -i: install Minetest mod + -V: output to VOX out.mts: output to file ``` @@ -39,10 +40,14 @@ This will print out mapping and each block as hex values layer by layer. ``` ./mtsedit -d structure.mts ``` +Also works with all the supported [input formats](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/import.md), for example: +``` +./mtsedit -d structure.schem +``` -### Generate Blueprint +### Generate BluePrint -Pretty much the same as dump, but instead of dumping the MTS to the console, it saves "structure_bp.png". +Pretty much the same as dump, but instead of dumping the structure to the console, it saves "structure_bp.png". ``` ./mtsedit -b structure.mts ``` @@ -91,6 +96,18 @@ tile, use the name "Air". If you have used Minecraft NBT IDs in the input file, then remapping is unnecessary. +### Converting to VOX + +Normally convertion creates only MTS files. But to support bidirectional workflows, MTSEdit can write VOX files too using `-V`. +``` +./mtsedit -V structure.mts structure.vox +``` +Note that VOX files will loose a lot of schematic's details (node names, rotation info, probabilities, force placement etc.), but +at least you will be able load the structure in Goxel and Magicavoxel. When saved, you can convert VOX (or GOX) files back to MTS: +``` +./mtsedit structure.vox structure.mts +``` + ### Generate Preview ``` diff --git a/docs/import.md b/docs/import.md index 227c3a6..d616f40 100644 --- a/docs/import.md +++ b/docs/import.md @@ -33,7 +33,7 @@ Sponge Schematic Files ---------------------- These are [Minecraft NBT](https://minecraft.gamepedia.com/Schematic_file_format) files too, used by WorldEdit. Typical extension -is `.schematic` too. Instead of Blocks and Data chunks, these have [BlockData](https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-2.md) +is `.schematic` too, or sometimes `.schem`. Instead of Blocks and Data chunks, these have [BlockData](https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-2.md) and Palette chunks. Although BlockData is a byte array, it contains bigger than 255 values using some messed up "varint" encoding. Limitations: rotation info are not imported at all. @@ -86,6 +86,9 @@ Used by [Magicavoxel](https://ephtracy.github.io). Typical extension is `.vox`. the same limitations apply. They are not perticularly good for storing schematics, as nodes has to be matched by color some nodes might be incorrectly identified. +To aid workflow in both ways, as an exception MTSEdit can convert to VOX files too. This is the only format that's written which +is not MTS. + Limitations: this format can't store nodes, nor rotation info, only colorized voxels. This is a voxel image format, not a true schematic format. diff --git a/mtsedit-i686-win.zip b/mtsedit-i686-win.zip index 350af68..80db753 100644 Binary files a/mtsedit-i686-win.zip and b/mtsedit-i686-win.zip differ diff --git a/mtsedit-intel-macosx.zip b/mtsedit-intel-macosx.zip index 03e499a..0721a94 100644 Binary files a/mtsedit-intel-macosx.zip and b/mtsedit-intel-macosx.zip differ diff --git a/mtsedit-x86_64-linux.tgz b/mtsedit-x86_64-linux.tgz index 0ba5d79..096e151 100644 Binary files a/mtsedit-x86_64-linux.tgz and b/mtsedit-x86_64-linux.tgz differ diff --git a/src/lang.c b/src/lang.c index f4cc5f5..2612557 100644 --- a/src/lang.c +++ b/src/lang.c @@ -46,6 +46,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "remap numeric ids in input", "replace block type mapping", "install Minetest mod", + "output to VOX", "output to file", "Loading...", "Memory allocation error", @@ -127,6 +128,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "bemeneti fájl numerikus azonostóinak lecserélése", "lecseréli a palettát", "Minetest mod telepítése", + "VOX lementése", "kimenet lementése", "Betöltés...", "Memória foglalási hiba", @@ -208,6 +210,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "reasignar identificadores numéricos en la entrada", "reemplazar el mapeo de tipo de bloque", "instalar Minetest mod", + "salida a VOX archivo", "salida a archivo", "Cargando...", "Error de asignación de memoria", @@ -289,6 +292,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "remapper les identifiants numériques en entrée", "remplacer la correspondance de type de bloc", "installer le mod Minetest", + "sortie dans un VOX fichier", "sortie dans un fichier", "Chargement...", "Erreur d'allocation de mémoire", @@ -370,6 +374,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "Ordnen Sie numerische IDs in der Eingabe neu zu", "Blocktypzuordnung ersetzen", "installiere Minetest mod", + "speichern VOX Datei", "speichern Datei", "Wird geladen...", "Speicherzuordnungsfehler", @@ -451,6 +456,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "mapuj identyfikatory numeryczne na wejściu", "zastąpić mapowanie typów bloków", "zainstaluj Minetest mod", + "wyjście do pliku VOX", "wyjście do pliku", "Ładuję...", "Błąd przydziału pamięci", @@ -532,6 +538,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "переназначить числовые идентификаторы на входе", "заменить отображение типа блока", "установить мод Minetest", + "вывод в файл VOX", "вывод в файл", "Загрузка ...", "Ошибка выделения памяти", @@ -613,6 +620,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "remapear IDs numéricos na entrada", "substituir o mapeamento do tipo de bloco", "instalar mod Minetest", + "saída para VOX arquivo", "saída para arquivo", "Carregando...", "Erro de alocação de memória", @@ -694,6 +702,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = { "rimappa gli ID numerici nell'input", "sostituire la mappatura del tipo di blocco", "installa Minetest mod", + "output su VOX file", "output su file", "Caricamento in corso...", "Errore di allocazione della memoria", diff --git a/src/lang.h b/src/lang.h index e051724..6475dfe 100644 --- a/src/lang.h +++ b/src/lang.h @@ -39,6 +39,7 @@ enum { INF_REMAP, INF_MAP, INF_MOD, + INF_VOX, INF_OUT, LOADING, ERR_MEM, diff --git a/src/main.c b/src/main.c index 59cc983..1e3d5aa 100644 --- a/src/main.c +++ b/src/main.c @@ -175,15 +175,15 @@ int main(int argc, char** argv, char** envp) if(argc < 2) { usage: printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license\r\n\r\n" - "./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b|-B] [-r remapfile] [-m map] [out.mts]\r\n" + "./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b|-B|-V] [-r remapfile] [-m map] [out.mts]\r\n" "./mtsedit [-G|-C|-T]\r\n" "./mtsedit -g \r\n" "./mtsedit -t [blockid]\r\n" "./mtsedit -i [Minetest mods dir]\r\n" "\r\n -v: %s\r\n -l lang: %s\r\n -d: %s\r\n -p: %s\r\n -P: %s\r\n -b, -B: %s\r\n -r remapfile: %s\r\n" - " -m map: %s\r\n -g: %s\r\n -t: %s\r\n -i: %s\r\n out.mts: %s\r\n", + " -m map: %s\r\n -g: %s\r\n -t: %s\r\n -i: %s\r\n -V: %s\r\n out.mts: %s\r\n", lang[INF_VERB], lang[INF_LANG], lang[INF_DUMP], lang[INF_PRE1], lang[INF_PRE2], lang[INF_BPRINT], lang[INF_REMAP], - lang[INF_MAP], lang[INF_GEN], lang[INF_BLK], lang[INF_MOD], lang[INF_OUT]); + lang[INF_MAP], lang[INF_GEN], lang[INF_BLK], lang[INF_MOD], lang[INF_VOX], lang[INF_OUT]); exit(0); } for(i = j = 1; j < argc; j++) { @@ -199,6 +199,7 @@ usage: printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license case 't': opt = 6; break; case 'i': opt = 7; break; case 'm': if(!opt) { opt = 8; } newmap = argv[++j]; i++; break; + case 'V': opt = 9; break; case 'r': remapfile = argv[++j]; i++; break; case 'l': j++; i++; break; case 'G': gimppal(); return 0; break; @@ -236,7 +237,7 @@ usage: printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license mts_x = mts_y = mts_z = 0; if(i+1 < argc && argv[i+1] && !opt) opt = 8; - if(opt && (opt < 5 || opt == 8)) { + if(opt && (opt < 5 || opt >= 8)) { blocks_parse(); readschem(); if(!mts_y || !mts_y || !mts_x) @@ -257,7 +258,7 @@ usage: printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license } else #endif strncpy(mtsfile, argv[i+1], sizeof(mtsfile) - 1); - ret = mts_save(); + ret = opt == 9 ? vox_save() : mts_save(); } } switch(opt) { diff --git a/src/main.h b/src/main.h index 9f530c9..f0b3061 100644 --- a/src/main.h +++ b/src/main.h @@ -213,6 +213,7 @@ void m3d_load(unsigned char *data, unsigned int size); void tmx_load(unsigned char *data, unsigned int size); void gox_load(unsigned char *data, unsigned int size); void vox_load(unsigned char *data, unsigned int size); +int vox_save(); void qb_load(unsigned char *data, unsigned int size); /* mts.c */ diff --git a/src/schemimp.c b/src/schemimp.c index 0dc7bbe..ccb4fc1 100644 --- a/src/schemimp.c +++ b/src/schemimp.c @@ -588,8 +588,8 @@ void vox_load(unsigned char *data, unsigned int size) pal[0] = 0; } else if(!memcmp(s, "XYZI", 4)) { - siz = *((uint32_t*)(s + 4)) / 4; - s += 12; + siz = (*((uint32_t*)(s + 4)) / 4) - 1; + s += 16; for(i = 0; i < siz; i++, s += 4) { if(s[0] < mi_x) mi_x = s[0]; if(s[0] > ma_x) ma_x = s[0]; @@ -621,8 +621,8 @@ void vox_load(unsigned char *data, unsigned int size) /* read in the layers (possibly from multiple chunks) */ for(s = data + 20; s < e; s++) { if(!memcmp(s, "XYZI", 4)) { - siz = *((uint32_t*)(s + 4)) / 4; - s += 12; + siz = (*((uint32_t*)(s + 4)) / 4) - 1; + s += 16; for(i = 0; i < siz; i++, s += 4) { x = s[0] - mi_x; z = s[1] - mi_z; @@ -643,6 +643,87 @@ void vox_load(unsigned char *data, unsigned int size) status = lang[LOADED]; } +/** + * Save a Magicavoxel VOX file (this is an exception to support bidirectional workflows) + */ +int vox_save() +{ + char outfile[MAXPATHLEN], *c; + unsigned short *tr, *tr2; + unsigned char hdr[12 + 1024]; + int i, j, n = 0, x, y, z; + FILE *f; + + status = lang[ERR_SAVE]; + tr = (unsigned short*)malloc(numblocks * sizeof(unsigned short)); + if(!tr) error(lang[ERR_MEM]); + memset(tr, 0, numblocks * sizeof(unsigned short)); + tr2 = (unsigned short*)malloc(numblocks * sizeof(unsigned short)); + if(!tr2) error(lang[ERR_MEM]); + memset(tr2, 0, numblocks * sizeof(unsigned short)); + + j = mts_getbounds(1, tr, tr2); + if(mix > max) { free(tr); free(tr2); return 1; } + + for(y = miy; y <= may; y++) + for(z = miz; z <= maz; z++) + for(x = mix; x <= max; x++) + if(nodes[y][z][x].param0) n++; + + /* get filename, buffer and open file */ + strcpy(outfile, mtsfile); + c = strrchr(outfile, '.'); + if(!c) c = &outfile[strlen(outfile)]; + strcpy(c, ".vox"); + f = fopen(outfile,"wb"); + if(!f) return 1; + + /* write header */ + memcpy(hdr, "VOX ", 4); + *((uint32_t*)(hdr+4)) = 150; + memcpy(hdr + 8, "MAIN", 4); + *((uint32_t*)(hdr+12)) = 0; + *((uint32_t*)(hdr+16)) = 40 + n*4 + 1036; + memcpy(hdr + 20, "SIZE", 4); + *((uint32_t*)(hdr+24)) = 12; + *((uint32_t*)(hdr+28)) = 0; + *((uint32_t*)(hdr+32)) = max-mix+1; + *((uint32_t*)(hdr+36)) = may-miy+1; + *((uint32_t*)(hdr+40)) = maz-miz+1; + memcpy(hdr + 44, "XYZI", 4); + *((uint32_t*)(hdr+48)) = n*4 + 4; + *((uint32_t*)(hdr+52)) = 0; + *((uint32_t*)(hdr+56)) = n; + fwrite(hdr, 60, 1, f); + + /* write data */ + for(y = miy; y <= may; y++) + for(z = miz; z <= maz; z++) + for(x = mix; x <= max; x++) + if(nodes[y][z][x].param0) { + hdr[0] = x - mix; + hdr[1] = maz - z; + hdr[2] = y - miy; + hdr[3] = tr2[nodes[y][z][x].param0]; + fwrite(hdr, 4, 1, f); + } + + /* write palette */ + memset(hdr, 0, sizeof(hdr)); + memcpy(hdr, "RGBA", 4); + for(i = 0; i < j; i++) + memcpy(hdr + 8 + i * 4, &blocks[tr[i]].color, 4); + *((uint32_t*)(hdr+4)) = 1024; + *((uint32_t*)(hdr+8)) = 0; + fwrite(hdr, 12+1024, 1, f); + + status = lang[SAVED]; + fclose(f); + free(tr); + free(tr2); + return 0; +} + /** * Load a Qubicle QB file */