From 2332955ae66891560e0a1771555ffad2a39e6ff5 Mon Sep 17 00:00:00 2001 From: entuland Date: Wed, 6 Jun 2018 16:56:23 +0200 Subject: [PATCH] Moved variants to separate file to allow for customization and extension - added a limited serialization helper function --- README.md | 40 ++++++------ default.nodevariants.lua | 6 ++ init.lua | 129 ++++++++++++++++++++++++++++++--------- 3 files changed, 126 insertions(+), 49 deletions(-) create mode 100644 default.nodevariants.lua diff --git a/README.md b/README.md index 6978250..eb593df 100644 --- a/README.md +++ b/README.md @@ -57,14 +57,14 @@ When you confirm such name (you can cancel it by hitting the ESC key) you'll lik If you confirm the name by hitting ENTER you may not be presented with the above confirmation. It will appear in the chat as well just in case. -Upon saving a few temporary files will be created in the "/mod_storage/wesh" subfolder in your world's folder: -- the .obj file will contain a model with your build scaled down to fit exactly one block -- the .dat file will contain the original name you have chosen for your mesh, along with some other data (read the section about using custom textures below) -- the .matrix.dat file will contain a serialized version of your build, that may eventually get used to rebuild / reimport it in the game allowing you to alter it (right now you can't import them, so make sure you don't dismantle your build if you want to alter and capture it again) +Upon saving a few temporary files will be created in the `/mod_storage/wesh` subfolder in your world's folder: +- the `.obj` file will contain a model with your build scaled down to fit exactly one block +- the `.obj.dat` file will contain the original name you have chosen for your mesh, along with some other data (read the section about using custom textures below) +- the `.obj.matrix.dat` file will contain a serialized version of your build, that may eventually get used to rebuild / reimport it in the game allowing you to alter it (right now you can't import them, so make sure you don't dismantle your build if you want to alter and capture it again) The above files are saved there only temporarily because mods don't have writing permission in their own folder while the world is running. In order to use your new meshes in the game you need to restart the world. -During world startup the mod will move all the temporary files to the "/models" folder and will load them. +During world startup the mod will move all the temporary files to the `/models` folder and will load them. You can't have two meshes with the same name (during the saving process the mod checks both the temporary meshes that haven't been loaded yet and those that have been already moved to the mod's folder). @@ -87,17 +87,17 @@ Collision boxes will be built automatically depending on the extent of your mesh Up to 8 collision boxes will be created according to the mesh geometry allowing you to create stairs, slabs, frames, carpets and so forth, collision boxes will be merged into larger ones when fitting. -Such new blocks can't be crafted (I plan to make sort of a crafting station where you put some material and chose the model you want to craft), so you either need to give them to yourself or to find them in the Creative inventory. All such meshes show up if you filter for either "wesh" or "mesh". +Such new blocks can't be crafted (I plan to make sort of a crafting station where you put some material and chose the model you want to craft), so you either need to give them to yourself or to find them in the Creative inventory. All such meshes show up if you filter for either `wesh` or `mesh`. ![Creative search](/screenshots/creative-search.png) -Looking at the filename (or knowing how the name gets converted) you can also work out the actual nodename to be used in your "/give" or "/giveme" chat command, for example: -- chosen name: "Test One!" -- resulting filename: "mesh_test_one.obj" -- resulting nodename: "wesh:mesh_test_one_VERSION" where "VERSION" will actually be something like "wool" or "plainborder" or whatever other variant has been enabled (see the following section for details about this) +Looking at the filename (or knowing how the name gets converted) you can also work out the actual nodename to be used in your `/give` or `/giveme` chat command, for example: +- chosen name: `Test One!` +- resulting filename: `mesh_test_one.obj` +- resulting nodename: `wesh:mesh_test_one_VERSION` where `VERSION` will actually be something like `wool` or `plainborder` or whatever other variant has been enabled (see the following section for details about this) # Specifying custom properties -In the .dat file of each mesh you'll find something like this: +In the `.obj.dat` file of each mesh you'll find something like this: return { description = "Your mesh name", @@ -109,11 +109,13 @@ In the .dat file of each mesh you'll find something like this: }, } -(please consider that the number "16" here above indicates the size of the texture, it has nothing to do with the size of the canvas you use to capture your build) - -In order to add a new variant simply add a line with your texture name and make sure you save such texture file in the "/textures" folder of the mod. You can also remove the lines you're not interested in and the mod will not generate those variants. +(please consider that the number `16` here above indicates the size of the texture, it has nothing to do with the size of the canvas you use to capture your build) -For example, here we remove all but the "plain" version and add a custom one: +Default variants are stored in the file [default.nodevariants.lua](/default.nodevariants.lua) which gets copied over to `nodevariants.lua` when starting up the mod if no such file exists. + +In order to add a new variant simply add a line with your texture name and make sure you save such texture file in the `/textures` folder of the mod. You can also remove the lines you're not interested in and the mod will not generate those variants. You can do this operation either on the `nodevariants.lua` file (it will affect all new captures) or in the `.obj.dat` file associated to each mesh (will affect only that mesh). + +For example, here we remove all but the `plain` version and add a custom one: return { description = "Your mesh name", @@ -123,15 +125,15 @@ For example, here we remove all but the "plain" version and add a custom one: }, } -Have a look at "wool-72.png" to see where each color goes, or use the included [textures-72.xcf](/textures/textures-72.xcf) file (GIMP format) which has layers for adding the borders as well. +Have a look at `wool-72.png` to see where each color goes, or use the included [textures-72.xcf](/textures/textures-72.xcf) file (GIMP format) which has layers for adding the borders as well. -You can as well override any property you would normally pass to node_register(), such as "walkable", "groups", "collision_box", "selection_box" and so forth. The only property that doesn't get really overridden but just "mangled" according to the variants is the "description" one. You shouldn't even be overriding "tiles" cause they will be built according to the "variants" property (which is specific to this mod). +You can as well override any property you would normally pass to node_register(), such as `walkable`, `groups`, `collision_box`, `selection_box` and so forth. The only property that doesn't get really overridden but just _mangled_ according to the variants is the `description` one. You shouldn't even be overriding `tiles` cause they will be built according to the `variants` property (property which is specific to this mod's `.obj.dat` files). A couple considerations: - the bottom-right transparent area never gets used -- the used texture for each face will actually be a bit smaller (in the "wool-72.png" file the squares are 18 pixels in side, but the texture will only use a 16x16 square inside of it) +- the used texture for each face will actually be a bit smaller (in the `wool-72.png` file the squares are 18 pixels in side, but the texture will only use a 16x16 square inside of it) - you're not forced to use any particular size for your texture as long as it's square (I guess, let me know if you find any problems) # Changing default colors assigned to nodes -The file [default.nodecolors.conf](/default.nodecolors.conf) contains the `modname:nodename = color` associations for all the nodes that get loaded in a minetest_game world. This file will be copied over to `nodecolors.conf` at startup (if no such file exists); in `nodecolors.conf` you're free to alter existing colors and to add new nodes, just make sure you stick to wool colors cause any invalid color will be replaced by "air". Do not store your changes into `default.nodecolors.conf` cause it will be overwritten updating the mod (and it wouldn't get used anyway if a `nodecolors.conf` file is there at all). +The file [default.nodecolors.conf](/default.nodecolors.conf) contains the `modname:nodename = color` associations for all the nodes that get loaded in a minetest_game world. This file will be copied over to `nodecolors.conf` at startup (if no such file exists); in `nodecolors.conf` you're free to alter existing colors and to add new nodes, just make sure you stick to wool colors cause any invalid color will be replaced by `air`. Do not store your changes into `default.nodecolors.conf` cause it will be overwritten updating the mod (and it wouldn't get used anyway if a `nodecolors.conf` file is there at all). diff --git a/default.nodevariants.lua b/default.nodevariants.lua new file mode 100644 index 0000000..88fdfbd --- /dev/null +++ b/default.nodevariants.lua @@ -0,0 +1,6 @@ +return { + plain = "plain-16.png", + plainborder = "plain-border-72.png", + wool = "wool-72.png", + woolborder = "wool-border-72.png", +} diff --git a/init.lua b/init.lua index 6d321b2..b49fe4d 100644 --- a/init.lua +++ b/init.lua @@ -20,6 +20,7 @@ function wesh._init() wesh._init_vertex_textures() wesh._init_colors() wesh._init_geometry() + wesh._init_variants() wesh._move_temp_files() wesh._load_mod_meshes() wesh._register_canvas_nodes() @@ -225,6 +226,29 @@ function wesh._reset_geometry(canv_size) wesh.traverse_matrix(reset, canv_size) end +function wesh._init_variants() + wesh.variants = { + plain = "plain-16.png", + } + local variants_filename = "nodevariants.lua" + local file = io.open(wesh.modpath .. "/" .. variants_filename, "rb") + if not file then + minetest.debug("[wesh] Copying default." .. variants_filename .. " to " .. variants_filename) + local success, err = wesh.copy_file(wesh.modpath .. "/default." .. variants_filename, wesh.modpath .. "/" .. variants_filename) + if not success then + minetest.debug("[wesh] " .. err) + return + end + file = io.open(wesh.modpath .. "/" .. variants_filename, "rb") + if not file then + minetest.debug("[wesh] Unable to load " .. variants_filename .. " file from mod folder") + return + end + end + wesh.variants = minetest.deserialize(file:read("*all")) + file:close() +end + -- ======================================================================== -- core functions -- ======================================================================== @@ -335,33 +359,20 @@ function wesh.save_mesh_to_file(meshdata, description, player, canvas) end function wesh.prepare_data_file(description, canvas) - local min = canvas.boundary.min - local max = canvas.boundary.max - local output = [[ -return { - description = ]] .. ("%q"):format(description) .. [[, - variants = { - plain = "plain-16.png", - plainborder = "plain-border-72.png", - wool = "wool-72.png", - woolborder = "wool-border-72.png", - }, - collision_box = { - type = "fixed", - fixed = { -]] - - wesh.merge_collision_boxes(canvas) - + local boxes = {} + wesh.merge_collision_boxes(canvas) for _, box in ipairs(canvas.boxes) do - output = output .. wesh.box_to_collision_box(box, canvas.size) + table.insert(boxes, wesh.box_to_collision_box(box, canvas.size)) end - - return output .. [[ + local data = { + description = description, + variants = wesh.variants, + collision_box = { + type = "fixed", + fixed = boxes, } - }, -} -]] + } + return wesh.serialize(data, 2) end function wesh.get_temp_files() @@ -453,12 +464,7 @@ function wesh.box_to_collision_box(box, size) local max = vector.add(box.max, 0) max = vector.multiply(max, subvoxel) max = vector.subtract(max, 0.5) - return [[ - { - ]] .. min.x .. [[, ]] .. min.y .. [[, ]] .. min.z .. [[, - ]] .. max.x .. [[, ]] .. max.y .. [[, ]] .. max.z .. [[, - }, -]] + return { min.x, min.y, min.z, max.x, max.y, max.z } end function wesh.generate_secondary_boundaries(canvas) @@ -782,6 +788,69 @@ function wesh.transform(facedir, pos) return (wesh._transfunc[facedir + 1] or wesh._transfunc[1])(pos) end +function wesh.serialize(object, max_wrapping) + local function helper(obj, max_depth, depth, seen) + if not depth then + depth = 0 + end + if not seen then + seen = {} + end + + local wrap = max_depth and max_depth > depth or false + + local out = "" + local t = type(obj) + if t == nil then + return "nil" + elseif t == "string" then + return string.format("%q", obj) + elseif t == "boolean" then + return obj and "true" or "false" + elseif t == "number" then + if math.floor(obj) == obj then + return string.format("%d", obj) + else + return tostring(obj) + end + elseif t == "table" then + if seen[tostring(obj)] then + error("[wesh] serialize(): Cyclic references not supported") + end + seen[tostring(obj)] = true; + + local output = "{\n" + local post_table = string.rep("\t", depth) .. "}" + local pre_key = string.rep("\t", depth + 1) + local post_value = ",\n"; + + if not wrap then + output = "{ " + post_table = "}" + pre_key = "" + post_value = ", " + end + + for k, v in pairs(obj) do + local key = k .. " = " + if type(k) == "number" then + -- remove numeric indices on purpose + key = "" + elseif type(k) ~= "string" or k:match("[^%w_]") then + error("[wesh] serialize(): Unsupported array key " .. helper(k)) + end + output = output .. pre_key .. key .. helper(v, max_depth, depth + 1, seen) .. post_value + end + + return output .. post_table + else + error("[wesh] serialize(): Data type " .. t .. " not supported") + end + end + + return "return " .. helper(object, max_wrapping) +end + function wesh.traverse_matrix(callback, boundary, ...) if type(boundary) == "table" then for x = boundary.min.x, boundary.max.x do