diff --git a/README.md b/README.md index b9b3d90..8213c6f 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,11 @@ By default, four versions of each mesh will be available: Sample of natural terrain capture: ![Non wool capture](/screenshots/non-wool-capture.png) +Collision boxes will be built automatically depending on the extent of your mesh: +![Auto collision box](/screenshots/auto-collision-box.png) + +Collision boxes are simple cuboids so you cannot create stairs (but you can create slabs, frames, carpets and so forth). + 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) @@ -83,7 +88,7 @@ Looking at the filename (or knowing how the name gets converted) you can also wo - 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) -# Using custom textures +# Specifying custom properties In the .dat file of each mesh you'll find something like this: return { @@ -112,6 +117,8 @@ 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. +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). + 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) diff --git a/init.lua b/init.lua index 1b4b491..18bdde4 100644 --- a/init.lua +++ b/init.lua @@ -219,28 +219,29 @@ function wesh.on_receive_fields(player, formname, fields) canvas.node = minetest.get_node_or_nil(canvas.pos) if not canvas.node then return end - local canv_size = canvas.node.name:gsub(".*(%d%d)$", "%1") - if not canv_size then canv_size = 16 end - - canv_size = tonumber(canv_size) - if canv_size ~= 2 and canv_size ~= 4 and canv_size ~= 8 and canv_size ~= 32 then - canv_size = 16 + canvas.size = canvas.node.name:gsub(".*(%d%d)$", "%1") + canvas.size = tonumber(canvas.size) + local valid = { [2] = true, [4] = true, [8] = true, [32] = true} + if not valid[canvas.size] then + canvas.size = 16 end - wesh.save_new_mesh(canvas.pos, canv_size, canvas.facedir, player, fields.meshname) + canvas.collision = {} + wesh.save_new_mesh(canvas, player, fields.meshname) end end -function wesh.save_new_mesh(canvas_pos, canv_size, facedir, player, description) +function wesh.save_new_mesh(canvas, player, description) + -- empty all helper variables - wesh._reset_geometry(canv_size) + wesh._reset_geometry(canvas.size) -- read all nodes from the canvas space in the world -- extract the colors and put them into a helper matrix of color voxels - wesh.traverse_matrix(wesh.node_to_voxel, canv_size, canv_size, canvas_pos, facedir) + wesh.traverse_matrix(wesh.node_to_voxel, canvas.size, canvas) -- generate faces according to voxels - wesh.traverse_matrix(wesh.voxel_to_faces, canv_size, canv_size) + wesh.traverse_matrix(wesh.voxel_to_faces, canvas.size, canvas) -- this will be the actual content of the .obj file local vt_section = wesh.vertex_textures @@ -249,14 +250,14 @@ function wesh.save_new_mesh(canvas_pos, canv_size, facedir, player, description) local f_section = table.concat(wesh.faces, "\n") local meshdata = vt_section .. v_section .. vn_section .. f_section - wesh.save_mesh_to_file(meshdata, description, player) + wesh.save_mesh_to_file(meshdata, description, player, canvas) end -- ======================================================================== -- mesh management helpers -- ======================================================================== -function wesh.save_mesh_to_file(meshdata, description, player) +function wesh.save_mesh_to_file(meshdata, description, player, canvas) local sanitized_meshname = wesh.check_plain(description) if sanitized_meshname:len() < 3 then wesh.notify(player, "Mesh name too short, try again (min. 3 chars)") @@ -289,7 +290,7 @@ function wesh.save_mesh_to_file(meshdata, description, player) wesh.notify(player, "Unable to write to file '" .. data_filename .. "' from '" .. wesh.temp_path .. "' - error: " .. errmsg) return end - file:write(wesh.prepare_data_file(description)) + file:write(wesh.prepare_data_file(description, canvas)) file:close() -- save .matrix.dat file @@ -323,7 +324,9 @@ function wesh.get_all_files() return all end -function wesh.prepare_data_file(description) +function wesh.prepare_data_file(description, canvas) + local min = canvas.collision.min + local max = canvas.collision.max local output = [[ return { description = ]] .. ("%q"):format(description) .. [[, @@ -333,6 +336,15 @@ return { wool = "wool-72.png", woolborder = "wool-border-72.png", }, + collision_box = { + type = "fixed", + fixed = { + { + ]] .. min.x .. [[, ]] .. min.y .. [[, ]] .. min.z .. [[, + ]] .. max.x .. [[, ]] .. max.y .. [[, ]] .. max.z .. [[, + }, + } + }, } ]] return output @@ -371,7 +383,7 @@ function wesh._load_mesh(obj_filename) local nodename = obj_filename:gsub("[^%w]+", "_"):gsub("_obj", "") for variant, tile in pairs(variants) do - minetest.register_node("wesh:" .. nodename .. "_" .. variant, { + local props = { drawtype = "mesh", mesh = obj_filename, paramtype2 = "facedir", @@ -379,7 +391,16 @@ function wesh._load_mesh(obj_filename) tiles = { tile }, walkable = true, groups = { dig_immediate = 3 }, - }) + } + for prop, value in pairs(data) do + if prop ~= "variants" and prop ~= "description" then + props[prop] = value + end + end + if props.collision_box and not props.selection_box then + props.selection_box = props.collision_box + end + minetest.register_node("wesh:" .. nodename .. "_" .. variant, props) end end @@ -387,6 +408,31 @@ end -- mesh generation helpers -- ======================================================================== +function wesh.update_collision_bounds(rel_pos, canvas) + local min = {} + local max = {} + min.x = (rel_pos.x - 1) / canvas.size - 0.5 + min.y = (rel_pos.y - 1) / canvas.size - 0.5 + min.z = (rel_pos.z - 1) / canvas.size - 0.5 + max.x = rel_pos.x / canvas.size - 0.5 + max.y = rel_pos.y / canvas.size - 0.5 + max.z = rel_pos.z / canvas.size - 0.5 + if not canvas.collision.min then + canvas.collision.min = min + else + canvas.collision.min.x = math.min(min.x, canvas.collision.min.x) + canvas.collision.min.y = math.min(min.y, canvas.collision.min.y) + canvas.collision.min.z = math.min(min.z, canvas.collision.min.z) + end + if not canvas.collision.max then + canvas.collision.max = max + else + canvas.collision.max.x = math.max(max.x, canvas.collision.max.x) + canvas.collision.max.y = math.max(max.y, canvas.collision.max.y) + canvas.collision.max.z = math.max(max.z, canvas.collision.max.z) + end +end + function wesh.construct_face(rel_pos, canv_size, texture_vertices, facename, vertices, normal_index) local normal = wesh.face_normals[normal_index] local hider_pos = vector.add(rel_pos, normal) @@ -443,18 +489,21 @@ function wesh.transform(facedir, pos) return (wesh._transfunc[facedir + 1] or wesh._transfunc[1])(pos) end -function wesh.node_to_voxel(rel_pos, canv_size, canvas_pos, facedir) - local abs_pos = wesh.make_absolute(canvas_pos, canv_size, facedir, rel_pos) +function wesh.node_to_voxel(rel_pos, canvas) + local abs_pos = wesh.make_absolute(canvas.pos, canvas.size, canvas.facedir, rel_pos) local color = wesh.get_node_color(abs_pos) + if color ~= "air" then + wesh.update_collision_bounds(rel_pos, canvas) + end wesh.set_voxel_color(rel_pos, color) end -function wesh.voxel_to_faces(rel_pos, canv_size) +function wesh.voxel_to_faces(rel_pos, canvas) local color = wesh.get_voxel_color(rel_pos) if color == "air" then return end for facename, facedata in pairs(wesh.face_construction) do local texture_vertices = wesh.get_texture_vertices(color) - wesh.construct_face(rel_pos, canv_size, texture_vertices, facename, facedata.vertices, facedata.normal) + wesh.construct_face(rel_pos, canvas.size, texture_vertices, facename, facedata.vertices, facedata.normal) end end diff --git a/screenshots/auto-collision-box.png b/screenshots/auto-collision-box.png new file mode 100644 index 0000000..3681a70 Binary files /dev/null and b/screenshots/auto-collision-box.png differ