Add bag, line, and polygon tools
This commit is contained in:
parent
403a24e860
commit
725d199191
30
README.md
30
README.md
@ -28,6 +28,8 @@ This mod was inspired by the Fill Start and Fill End blocks in Manic Digger.
|
||||
| Circle | edit:circle | ![](textures/edit_circle.png) |
|
||||
| Mirror | edit:mirror | ![](textures/edit_mirror.png) |
|
||||
| Screwdriver | edit:screwdriver | ![](textures/edit_screwdriver.png) |
|
||||
| Polygon | edit:polygon | ![](textures/edit_polygon.png) |
|
||||
| Bag | edit:bag | ![](textures/edit_bag.png) |
|
||||
|
||||
|
||||
## Dependencies
|
||||
@ -39,9 +41,7 @@ None
|
||||
|
||||
### Copy Tool
|
||||
|
||||
![figure.png](figure.png)
|
||||
|
||||
When the copy tool is placed at opposite corners of an area, they select the area as show in the figure. The copy tool uses the location under the placed position. When the copy tool is placed for the first time, a marker entity is placed. To cancel the copy operation, punch the entity marker. When a copy tool is placed a second time, the selected area is copied and the entity marker is removed.
|
||||
When the copy tool is placed at opposite corners of an area, the area is copied. When the copy tool is placed for the first time, a marker entity is placed. To cancel the copy operation, punch the marker. When a copy tool is placed a second time, the selected area is copied and the markers are removed.
|
||||
|
||||
|
||||
### Paste Tool
|
||||
@ -51,14 +51,14 @@ The paste tool is used for pasting the area copied by the copy tool or a schemat
|
||||
|
||||
### Fill Tool
|
||||
|
||||
The fill tool is used to fill a 3D area with a certain item. Start by placing the fill tool two times at opposite corners of the desired area. The selected area includes the positions of the fill markers themselves as shown in the figure.
|
||||
The fill tool is used to fill a 3D area with a certain item. Start by placing the fill tool two times at opposite corners of the desired area.
|
||||
|
||||
Once a second fill marker is placed, a dialog appears listing all items in the players inventory. A search field is also available to search all items. Clicking an item will cause it to be used used for filling the selected area. Clicking on a blank inventory slot will cause the selected area to be filled with air. To cancel the fill, press the "X".
|
||||
|
||||
|
||||
### Replace Tool
|
||||
|
||||
The replace tool is used to replace certain nodes in a 3D area with a selected item. Start by placing the replace tool two times at opposite corners of the desired area. The selected area includes the positions of the replace markers themselves as shown in the figure.
|
||||
The replace tool is used to replace certain nodes in a 3D area with a selected item. Start by placing the replace tool two times at opposite corners of the desired area.
|
||||
|
||||
Once a second replace marker is placed, a dialog appears listing all node types in the selected area. Check the nodes that should be replaced and then press the "OK" button to proceed with the next step. Next a dialog will pop up showing all the items in the players inventory. A search field is also available to search all items. Clicking an item will cause it to be used used to replace the nodes that were checked earlier. Clicking on a blank inventory slot will cause the checked nodes to be replaced with air. To cancel the replace, press the "X".
|
||||
|
||||
@ -100,6 +100,16 @@ This tool is used to mirror the placement or digging of nodes. Place the tool to
|
||||
This tool is used for rotating nodes that support rotation. Right clicking a node with the screwdriver rotates the node around the X or Z axis depending on the player's position. Left clicking a node with the screwdriver rotates the node clockwise around the Y axis. Param2 types `wallmounted`, `facedir`, and `degrotate` are supported. The node is rotated 90 degrees for all param2 types except `degrotate` where the node is rotated by either 1.5 or 15 degrees. If the aux1 key (E) is held while rotating a `degrotate` node, the rotation angle will be increased by 4x.
|
||||
|
||||
|
||||
### Polygon Tool
|
||||
|
||||
This tool is used to create non-concave polygons in 3D space. Place the polygon tool to create markers. Each marker will create a triangle between itself, the last marker placed, and the first marker placed. The first marker placed will be green. To finish the polygon, place a marker on top of the green marker. After doing so, a dialog will appear to select a node or item to fill the polygon.
|
||||
|
||||
|
||||
### Bag
|
||||
|
||||
The bag tool is used to place random items from a list of items. Dig (left click) with the bag to open the bag's inventory. Any item from the player inventory can be moved into the bag. The bag has 16 item slots. When placing the bag an item from the bag is randomly chosen to be placed. If a stack of several items is present in the bag, the item will be more likely to be placed than a single item. The probability of being placed is proportional to the item's count divided by the total count of items in the bag. For example, the probability of getting wood would be 75% for a bag with 3 wood and 1 dirt. Bags can be combined with other edit tools, for example, to fill an area with random kinds of dirt.
|
||||
|
||||
|
||||
## Settings
|
||||
|
||||
### edit_paste_preview_max_entities
|
||||
@ -114,10 +124,12 @@ The maximum volume of any edit operation. Increase to allow larger operations.
|
||||
|
||||
### edit_fast_node_fill_threshold
|
||||
|
||||
When the fill operation has a larger volume then the specified number, fast node fill will be used.
|
||||
To disable fast node placement, set the threshold to be equal to the max operation volume.
|
||||
To disable slow node placement, set the threshold to 0.
|
||||
With fast node placement, callbacks are not called so some nodes might be broken.
|
||||
When the fill operation has a larger volume then the specified number, fast node fill will be used. To disable fast node placement, set the threshold to be equal to the max operation volume. To disable slow node placement, set the threshold to 0. With fast node placement, callbacks are not called so some nodes might be broken.
|
||||
|
||||
|
||||
### edit_polygon_preview_wire_frame_threshold
|
||||
|
||||
If one side of the polygon preview is greater than this setting, a wire frame is used instead of the full preview. The full preview fills the entire polygon with preview entites. If the polygon gets big, the full preview will quickly crash a server. The wire frame preview outlines the triangle componants of the polygon. This setting only affects the preview. The polygon is always completely filled regardless of this setting.
|
||||
|
||||
|
||||
## Privileges
|
||||
|
118
bag.lua
Normal file
118
bag.lua
Normal file
@ -0,0 +1,118 @@
|
||||
local function get_item_list(itemstack)
|
||||
local meta = itemstack:get_meta()
|
||||
local str = meta:get("edit_bag")
|
||||
local item_list = {}
|
||||
if str then item_list = minetest.deserialize(str) end
|
||||
for i, str in pairs(item_list) do
|
||||
item_list[i] = ItemStack(str)
|
||||
end
|
||||
return item_list
|
||||
end
|
||||
|
||||
local function put_item_list(itemstack, item_list)
|
||||
local meta = itemstack:get_meta()
|
||||
local str_list = {}
|
||||
local description = ""
|
||||
local description_len = 0
|
||||
for i, item in pairs(item_list) do
|
||||
str_list[i] = item:to_string()
|
||||
if str_list[i] ~= "" then
|
||||
if description_len < 3 then
|
||||
description = description ..
|
||||
"\n" .. item:get_count() ..
|
||||
" " .. item:get_short_description()
|
||||
description_len = description_len + 1
|
||||
elseif description_len == 3 then
|
||||
description = description .. "\n..."
|
||||
description_len = description_len + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
description = minetest.registered_items["edit:bag"].short_description ..
|
||||
minetest.colorize("yellow", description)
|
||||
local str = minetest.serialize(str_list)
|
||||
meta:set_string("edit_bag", str)
|
||||
meta:set_string("description", description)
|
||||
end
|
||||
|
||||
local function on_inv_change(player)
|
||||
local bag = player:get_wielded_item()
|
||||
if bag:get_name() ~= "edit:bag" then return end
|
||||
if not bag then return end
|
||||
local name = player:get_player_name()
|
||||
local inv_ref = minetest.get_inventory({type = "detached", name = "edit_bag_" .. name})
|
||||
put_item_list(bag, inv_ref:get_list("main"))
|
||||
player:set_wielded_item(bag)
|
||||
end
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local inv_ref = minetest.create_detached_inventory("edit_bag_" .. player:get_player_name(), {
|
||||
on_move = function(inv, from_list, from_index, to_list, to_index, count, player) on_inv_change(player) end,
|
||||
on_put = function(inv, listname, index, stack, player) on_inv_change(player) end,
|
||||
on_take = function(inv, listname, index, stack, player) on_inv_change(player) end,
|
||||
})
|
||||
inv_ref:set_size("main", 16)
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
minetest.remove_detached_inventory("edit_bag_" .. player:get_player_name())
|
||||
end)
|
||||
|
||||
local function on_place(itemstack, player, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then return end
|
||||
local item_list = get_item_list(itemstack)
|
||||
local total_count = 0
|
||||
for i, item_stack in ipairs(item_list) do
|
||||
total_count = total_count + item_stack:get_count()
|
||||
end
|
||||
local selected_index = math.round((total_count - 1) * math.random()) + 1
|
||||
local selected_item
|
||||
|
||||
local current_index = 0
|
||||
for i, item_stack in ipairs(item_list) do
|
||||
local count = item_stack:get_count()
|
||||
if count > 0 then
|
||||
current_index = current_index + count
|
||||
if current_index >= selected_index then
|
||||
selected_item = item_stack
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if selected_item then
|
||||
local pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||
edit.place_item_like_player(player, {name = selected_item}, pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_use(itemstack, user, pointed_thing)
|
||||
local meta = itemstack:get_meta()
|
||||
local str = meta:get("edit_bag")
|
||||
local name = user:get_player_name()
|
||||
local item_list = {}
|
||||
if str then item_list = minetest.deserialize(str) end
|
||||
for i, str in pairs(item_list) do
|
||||
item_list[i] = ItemStack(str)
|
||||
end
|
||||
local inv_ref = minetest.get_inventory({type = "detached", name = "edit_bag_" .. name})
|
||||
inv_ref:set_list("main", item_list)
|
||||
local formspec = "formspec_version[4]size[10.2,10]" ..
|
||||
"label[0.2,0.9;Bag contents:]" ..
|
||||
"button_exit[9,0.2;1,1;quit;X]" ..
|
||||
"list[detached:edit_bag_" .. name .. ";main;0.2,1.4;8,2;]" ..
|
||||
"label[0.2,4.5;Inventory:]" ..
|
||||
"list[current_player;main;0.2,5;8,4;]"
|
||||
minetest.show_formspec(name, "edit:bag", formspec)
|
||||
end
|
||||
|
||||
minetest.register_tool("edit:bag", {
|
||||
description = "Edit Bag",
|
||||
short_description = "Edit Bag",
|
||||
tiles = {"edit_bag.png"},
|
||||
inventory_image = "edit_bag.png",
|
||||
range = 10,
|
||||
on_place = on_place,
|
||||
on_secondary_use = on_place,
|
||||
on_use = on_use,
|
||||
})
|
@ -47,7 +47,7 @@ local function place_circle(player, pos, node)
|
||||
offset1.y * factor.y,
|
||||
offset1.z * factor.z )
|
||||
local pos1 = vector.add(center, offset1)
|
||||
edit.place_node_like_player(player, node, pos1)
|
||||
edit.place_item_like_player(player, node, pos1)
|
||||
|
||||
local offset2 = vector.new(
|
||||
offset1.z,
|
||||
@ -55,7 +55,7 @@ local function place_circle(player, pos, node)
|
||||
offset1.x
|
||||
)
|
||||
local pos2 = vector.add(center, offset2)
|
||||
edit.place_node_like_player(player, node, pos2)
|
||||
edit.place_item_like_player(player, node, pos2)
|
||||
end
|
||||
end
|
||||
|
||||
|
2
copy.lua
2
copy.lua
@ -46,7 +46,7 @@ minetest.register_tool("edit:copy",{
|
||||
tiles = {"edit_copy.png"},
|
||||
inventory_image = "edit_copy.png",
|
||||
range = 10,
|
||||
groups = {edit_place_preview = 1,},
|
||||
groups = {edit_place_preview = 1, edit_box_select_preview = 1},
|
||||
on_place = copy_on_place,
|
||||
on_secondary_use = copy_on_place,
|
||||
_edit_get_selection_points = function(player)
|
||||
|
BIN
figure.png
BIN
figure.png
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
16
fill.lua
16
fill.lua
@ -1,5 +1,5 @@
|
||||
local function player_select_node_formspec(player)
|
||||
edit.player_select_node(player, "Select item to use for fill", function(player, name)
|
||||
local function player_select_item_formspec(player)
|
||||
edit.player_select_item(player, "Select item to use for fill", function(player, item_str)
|
||||
local d = edit.player_data[player]
|
||||
|
||||
if
|
||||
@ -12,7 +12,9 @@ local function player_select_node_formspec(player)
|
||||
|
||||
d.fill1.object:remove()
|
||||
|
||||
if not name then return end
|
||||
local item = ItemStack(item_str)
|
||||
local name = item:get_name()
|
||||
if name == "" then return end
|
||||
|
||||
local def = minetest.registered_items[name]
|
||||
|
||||
@ -63,13 +65,13 @@ local function player_select_node_formspec(player)
|
||||
voxel_manip:write_to_map(true)
|
||||
voxel_manip:update_liquids()
|
||||
else
|
||||
local node = {name = name, param2 = param2}
|
||||
local node = {name = item_str, param2 = param2}
|
||||
-- Work top to bottom so we can remove falling nodes
|
||||
for x = _end.x, start.x, -1 do
|
||||
for y = _end.y, start.y, -1 do
|
||||
for z = _end.z, start.z, -1 do
|
||||
local pos = vector.new(x, y, z)
|
||||
edit.place_node_like_player(player, node, pos)
|
||||
edit.place_item_like_player(player, node, pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -100,7 +102,7 @@ local function fill_on_place(itemstack, player, pointed_thing)
|
||||
return
|
||||
end
|
||||
|
||||
player_select_node_formspec(player)
|
||||
player_select_item_formspec(player)
|
||||
elseif pos then
|
||||
player_data.fill1 = edit.add_marker("edit:fill", pos, player)
|
||||
end
|
||||
@ -111,7 +113,7 @@ minetest.register_tool("edit:fill", {
|
||||
tiles = {"edit_fill.png"},
|
||||
inventory_image = "edit_fill.png",
|
||||
range = 10,
|
||||
groups = {edit_place_preview = 1,},
|
||||
groups = {edit_place_preview = 1, edit_box_select_preview = 1},
|
||||
on_place = fill_on_place,
|
||||
on_secondary_use = fill_on_place,
|
||||
_edit_get_selection_points = function(player)
|
||||
|
57
init.lua
57
init.lua
@ -4,6 +4,7 @@ edit.player_data = {}
|
||||
edit.paste_preview_max_entities = tonumber(minetest.settings:get("edit_paste_preview_max_entities") or 2000)
|
||||
edit.max_operation_volume = tonumber(minetest.settings:get("edit_max_operation_volume") or 20000)
|
||||
edit.fast_node_fill_threshold = tonumber(minetest.settings:get("edit_fast_node_fill_threshold") or 2000)
|
||||
edit.polygon_preview_wire_frame_threshold = tonumber(minetest.settings:get("edit_polygon_preview_wire_frame_threshold") or 40)
|
||||
|
||||
minetest.register_privilege("edit", {
|
||||
description = "Allows usage of edit mod nodes",
|
||||
@ -128,10 +129,13 @@ minetest.register_on_placenode = function(func)
|
||||
return old_register_on_placenode(func)
|
||||
end
|
||||
|
||||
function edit.place_node_like_player(player, node, pos)
|
||||
function edit.place_item_like_player(player, item, pos)
|
||||
local node = table.copy(item)
|
||||
local itemstack = ItemStack(item.name)
|
||||
node.name = itemstack:get_name()
|
||||
local def = minetest.registered_items[node.name]
|
||||
if not def then return end
|
||||
local is_node = minetest.registered_nodes[node.name] ~= nil
|
||||
local itemstack = ItemStack(node.name)
|
||||
local pointed_thing = {
|
||||
type = "node",
|
||||
above = pos,
|
||||
@ -173,9 +177,9 @@ function edit.add_marker(id, pos, player)
|
||||
return luaentity
|
||||
end
|
||||
|
||||
local function player_select_node_formspec(player)
|
||||
local function player_select_item_formspec(player)
|
||||
local d = edit.player_data[player]
|
||||
local search_value = d.player_select_node_search_value
|
||||
local search_value = d.player_select_item_search_value
|
||||
local doing_search = #search_value > 0
|
||||
local inv = minetest.get_inventory({type = "player", name = player:get_player_name()})
|
||||
local size = doing_search and 12 * 8 or inv:get_size("main")
|
||||
@ -223,7 +227,7 @@ local function player_select_node_formspec(player)
|
||||
|
||||
local formspec = "formspec_version[4]size[" .. formspec_width .. "," .. formspec_height .. "]" ..
|
||||
"button[0,0;0,0;minetest_sucks;" .. math.random() .. "]" .. -- Force Minetest to show this formspec
|
||||
"label[0.5,0.7;" .. d.player_select_node_message .. "]" ..
|
||||
"label[0.5,0.7;" .. d.player_select_item_message .. "]" ..
|
||||
"button_exit[" .. formspec_width - 1.2 .. ",0.2;1,1;quit;X]" ..
|
||||
"field_close_on_enter[search_field;false]" ..
|
||||
"label[0.2,1.7;Search all items]" ..
|
||||
@ -237,7 +241,7 @@ local function player_select_node_formspec(player)
|
||||
if doing_search then
|
||||
name = search_results[i]
|
||||
else
|
||||
name = inv:get_stack("main", i):get_name()
|
||||
name = inv:get_stack("main", i):to_string()
|
||||
end
|
||||
|
||||
if not name then break end
|
||||
@ -255,28 +259,28 @@ local function player_select_node_formspec(player)
|
||||
name .. ";" ..
|
||||
name .. ";]"
|
||||
end
|
||||
edit.reliable_show_formspec(player, "edit:player_select_node", formspec)
|
||||
edit.reliable_show_formspec(player, "edit:player_select_item", formspec)
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "edit:player_select_node" then return false end
|
||||
if formname ~= "edit:player_select_item" then return false end
|
||||
local d = edit.player_data[player]
|
||||
|
||||
for key, val in pairs(fields) do
|
||||
if key:find(":") or key == "air" then
|
||||
if d.player_select_node_callback then
|
||||
d.player_select_node_callback(player, key)
|
||||
d.player_select_node_callback = nil
|
||||
minetest.close_formspec(player:get_player_name(), "edit:player_select_node")
|
||||
if d.player_select_item_callback then
|
||||
d.player_select_item_callback(player, key)
|
||||
d.player_select_item_callback = nil
|
||||
minetest.close_formspec(player:get_player_name(), "edit:player_select_item")
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if fields.quit then
|
||||
if d.player_select_node_callback then
|
||||
d.player_select_node_callback(player, nil)
|
||||
d.player_select_node_callback = nil
|
||||
if d.player_select_item_callback then
|
||||
d.player_select_item_callback(player, nil)
|
||||
d.player_select_item_callback = nil
|
||||
end
|
||||
return true
|
||||
elseif fields.cancel_search then
|
||||
@ -285,24 +289,24 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
if
|
||||
fields.search_field and
|
||||
fields.search_field ~= d.player_select_node_search_value
|
||||
fields.search_field ~= d.player_select_item_search_value
|
||||
then
|
||||
d.player_select_node_search_value = fields.search_field
|
||||
player_select_node_formspec(player)
|
||||
d.player_select_item_search_value = fields.search_field
|
||||
player_select_item_formspec(player)
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end)
|
||||
|
||||
function edit.player_select_node(player, message, callback)
|
||||
function edit.player_select_item(player, message, callback)
|
||||
local d = edit.player_data[player]
|
||||
if d.player_select_node_callback then
|
||||
d.player_select_node_callback(player, nil)
|
||||
if d.player_select_item_callback then
|
||||
d.player_select_item_callback(player, nil)
|
||||
end
|
||||
d.player_select_node_callback = callback
|
||||
d.player_select_node_search_value = d.player_select_node_search_value or ""
|
||||
d.player_select_node_message = message
|
||||
player_select_node_formspec(player)
|
||||
d.player_select_item_callback = callback
|
||||
d.player_select_item_search_value = d.player_select_item_search_value or ""
|
||||
d.player_select_item_message = message
|
||||
player_select_item_formspec(player)
|
||||
end
|
||||
|
||||
edit.modpath = minetest.get_modpath("edit")
|
||||
@ -318,3 +322,6 @@ dofile(edit.modpath .. "/circle.lua")
|
||||
dofile(edit.modpath .. "/mirror.lua")
|
||||
dofile(edit.modpath .. "/screwdriver.lua")
|
||||
dofile(edit.modpath .. "/replace.lua")
|
||||
dofile(edit.modpath .. "/polygon.lua")
|
||||
dofile(edit.modpath .. "/line.lua")
|
||||
dofile(edit.modpath .. "/bag.lua")
|
||||
|
190
line.lua
Normal file
190
line.lua
Normal file
@ -0,0 +1,190 @@
|
||||
-- https://www.geeksforgeeks.org/bresenhams-algorithm-for-3-d-line-drawing/
|
||||
-- This Site is affiliated under CCBY-SA https://www.geeksforgeeks.org/legal/copyright-information/
|
||||
-- JS code for generating points on a 3-D line
|
||||
-- using Bresenham's Algorithm
|
||||
-- Converted from the original to Lua
|
||||
function edit.calculate_line_points(p1, p2)
|
||||
p1 = vector.copy(p1)
|
||||
p2 = vector.copy(p2)
|
||||
local output = {vector.copy(p1)}
|
||||
local d = vector.apply(vector.subtract(p1, p2), math.abs)
|
||||
local s = vector.new(
|
||||
p2.x > p1.x and 1 or -1,
|
||||
p2.y > p1.y and 1 or -1,
|
||||
p2.z > p1.z and 1 or -1
|
||||
)
|
||||
|
||||
-- Driving axis is X-axis
|
||||
if d.x >= d.y and d.x >= d.z then
|
||||
local n1 = 2 * d.y - d.x
|
||||
local n2 = 2 * d.z - d.x
|
||||
while p1.x ~= p2.x do
|
||||
p1.x = p1.x + s.x
|
||||
if n1 >= 0 then
|
||||
p1.y = p1.y + s.y
|
||||
n1 = n1 - 2 * d.x
|
||||
end
|
||||
if n2 >= 0 then
|
||||
p1.z = p1.z + s.z
|
||||
n2 = n2 - 2 * d.x
|
||||
end
|
||||
n1 = n1 + 2 * d.y
|
||||
n2 = n2 + 2 * d.z
|
||||
table.insert(output, vector.copy(p1))
|
||||
end
|
||||
|
||||
-- Driving axis is Y-axis
|
||||
elseif d.y >= d.x and d.y >= d.z then
|
||||
local n1 = 2 * d.x - d.y
|
||||
local n2 = 2 * d.z - d.y
|
||||
while p1.y ~= p2.y do
|
||||
p1.y = p1.y + s.y
|
||||
if n1 >= 0 then
|
||||
p1.x = p1.x + s.x
|
||||
n1 = n1 - 2 * d.y
|
||||
end
|
||||
if n2 >= 0 then
|
||||
p1.z = p1.z + s.z
|
||||
n2 = n2 - 2 * d.y
|
||||
end
|
||||
n1 = n1 + 2 * d.x
|
||||
n2 = n2 + 2 * d.z
|
||||
table.insert(output, vector.copy(p1))
|
||||
end
|
||||
|
||||
-- Driving axis is Z-axis
|
||||
else
|
||||
local n1 = 2 * d.y - d.z
|
||||
local n2 = 2 * d.x - d.z
|
||||
while p1.z ~= p2.z do
|
||||
p1.z = p1.z + s.z
|
||||
if n1 >= 0 then
|
||||
p1.y = p1.y + s.y
|
||||
n1 = n1 - 2 * d.z
|
||||
end
|
||||
if n2 >= 0 then
|
||||
p1.x = p1.x + s.x
|
||||
n2 = n2 - 2 * d.z
|
||||
end
|
||||
n1 = n1 + 2 * d.y
|
||||
n2 = n2 + 2 * d.x
|
||||
table.insert(output, vector.copy(p1))
|
||||
end
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
minetest.register_entity("edit:line", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = { x = 1.1, y = 1.1 },
|
||||
physical = false,
|
||||
collide_with_objects = false,
|
||||
static_save = false,
|
||||
use_texture_alpha = true,
|
||||
glow = -1,
|
||||
backface_culling = false,
|
||||
hp_max = 1,
|
||||
textures = {
|
||||
"edit_line.png",
|
||||
"edit_line.png",
|
||||
"edit_line.png",
|
||||
"edit_line.png",
|
||||
"edit_line.png",
|
||||
"edit_line.png",
|
||||
},
|
||||
},
|
||||
on_deactivate = function(self)
|
||||
local player_data = edit.player_data[self._placer]
|
||||
self.remove_called = true
|
||||
if player_data then
|
||||
local line1 = player_data.line1
|
||||
if line1 and not line1.remove_called then
|
||||
line1.object:remove()
|
||||
end
|
||||
player_data.line1 = nil
|
||||
|
||||
local line2 = player_data.line2
|
||||
if line2 and not line2.remove_called then
|
||||
line2.object:remove()
|
||||
end
|
||||
player_data.line2 = nil
|
||||
|
||||
player_data.old_pointed_pos = nil
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local function place_line(player, item_name)
|
||||
local player_data = edit.player_data[player]
|
||||
if not player_data.line1 then return end
|
||||
|
||||
if not item_name then
|
||||
player_data.line1.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
local pos1 = player_data.line1._pos
|
||||
local pos2 = player_data.line2._pos
|
||||
|
||||
local size = vector.add(vector.apply(vector.subtract(pos1, pos2), math.abs), vector.new(1, 1, 1))
|
||||
local pos = vector.new(
|
||||
math.min(pos1.x, pos2.x),
|
||||
math.min(pos1.y, pos2.y),
|
||||
math.min(pos1.z, pos2.z)
|
||||
)
|
||||
player_data.undo_schematic = edit.schematic_from_map(pos, size)
|
||||
|
||||
local line_points = edit.calculate_line_points(pos1, pos2)
|
||||
local item = {name = item_name}
|
||||
for i, pos in pairs(line_points) do
|
||||
edit.place_item_like_player(player, item, pos)
|
||||
end
|
||||
player_data.line1.object:remove()
|
||||
end
|
||||
|
||||
local function line_on_place(itemstack, player, pointed_thing)
|
||||
if not edit.on_place_checks(player) then return end
|
||||
|
||||
if not pointed_thing.above then
|
||||
pointed_thing = edit.get_pointed_thing_node(player)
|
||||
end
|
||||
|
||||
local pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||
if not pos then return end
|
||||
|
||||
local player_data = edit.player_data[player]
|
||||
|
||||
if not player_data.line1 then
|
||||
player_data.line1 = edit.add_marker("edit:line", pos, player)
|
||||
if not player_data.line1 then return end
|
||||
else
|
||||
player_data.line2 = edit.add_marker("edit:line", pos, player)
|
||||
if not player_data.line2 then return end
|
||||
|
||||
local diff = vector.subtract(player_data.line1._pos, pos)
|
||||
local volume = vector.add(vector.apply(diff, math.abs), 1)
|
||||
if volume.x * volume.y * volume.z > edit.max_operation_volume then
|
||||
edit.display_size_error(player)
|
||||
player_data.line1.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
edit.player_select_item(player, "Select item to fill the line", place_line)
|
||||
end
|
||||
edit.old_pointed_pos = nil
|
||||
end
|
||||
|
||||
minetest.register_tool("edit:line", {
|
||||
description = "Edit Line",
|
||||
tiles = {"edit_line.png"},
|
||||
inventory_image = "edit_line.png",
|
||||
range = 10,
|
||||
groups = {edit_place_preview = 1,},
|
||||
on_place = line_on_place,
|
||||
on_secondary_use = line_on_place,
|
||||
_edit_get_selection_points = function(player)
|
||||
local d = edit.player_data[player]
|
||||
return d.line1 and d.line1._pos, d.line2 and d.line2._pos
|
||||
end
|
||||
})
|
@ -21,10 +21,10 @@ local function do_mirror(player, pos, node)
|
||||
|
||||
if d.mirror_mode == "x" then
|
||||
offset.x = -offset.x
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
edit.place_item_like_player(player, node, vector.add(center, offset))
|
||||
elseif d.mirror_mode == "z" then
|
||||
offset.z = -offset.z
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
edit.place_item_like_player(player, node, vector.add(center, offset))
|
||||
elseif d.mirror_mode == "xz" then
|
||||
for i = 1, 4 do
|
||||
local axis = "x"
|
||||
@ -32,7 +32,7 @@ local function do_mirror(player, pos, node)
|
||||
axis = "z"
|
||||
end
|
||||
offset[axis] = -offset[axis]
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
edit.place_item_like_player(player, node, vector.add(center, offset))
|
||||
end
|
||||
elseif d.mirror_mode == "eighths" then
|
||||
for i = 1, 8 do
|
||||
@ -44,7 +44,7 @@ local function do_mirror(player, pos, node)
|
||||
offset = vector.new(offset.z, offset.y, offset.x)
|
||||
end
|
||||
offset[axis] = -offset[axis]
|
||||
edit.place_node_like_player(player, node, vector.add(center, offset))
|
||||
edit.place_item_like_player(player, node, vector.add(center, offset))
|
||||
end
|
||||
end
|
||||
|
||||
|
226
polygon.lua
Normal file
226
polygon.lua
Normal file
@ -0,0 +1,226 @@
|
||||
-- https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm#C++_implementation
|
||||
-- Converted from C++ to Lua
|
||||
-- License: CC BY-SA https://creativecommons.org/licenses/by-sa/4.0/
|
||||
local function ray_intersects_triangle(ray_origin, ray_vector, vertex_a, vertex_b, vertex_c)
|
||||
local epsilon = 0.0000001
|
||||
|
||||
local edge1 = vector.subtract(vertex_b, vertex_a)
|
||||
local edge2 = vector.subtract(vertex_c, vertex_a)
|
||||
local ray_cross_e2 = vector.cross(ray_vector, edge2)
|
||||
local det = vector.dot(edge1, ray_cross_e2)
|
||||
|
||||
if det > -epsilon and det < epsilon then
|
||||
return -- This ray is parallel to this triangle.
|
||||
end
|
||||
local inv_det = 1.0 / det
|
||||
local s = vector.subtract(ray_origin, vertex_a)
|
||||
local u = inv_det * vector.dot(s, ray_cross_e2)
|
||||
|
||||
if u < 0 or u > 1 then return end
|
||||
|
||||
local s_cross_e1 = vector.cross(s, edge1)
|
||||
local v = inv_det * vector.dot(ray_vector, s_cross_e1)
|
||||
|
||||
if v < 0 or u + v > 1 then return end
|
||||
|
||||
-- At this stage we can compute t to find out where the intersection point is on the line.
|
||||
local t = inv_det * vector.dot(edge2, s_cross_e1)
|
||||
|
||||
if t > epsilon then -- ray intersection
|
||||
return vector.add(ray_origin, vector.multiply(ray_vector, t))
|
||||
else -- This means that there is a line intersection but not a ray intersection.
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function edit.calculate_triangle_points(a, b, c)
|
||||
local bounding_box_min = vector.copy(a)
|
||||
local bounding_box_max = vector.copy(a)
|
||||
for index, axis in pairs({"x", "y", "z"}) do
|
||||
bounding_box_min[axis] = math.min(a[axis], b[axis], c[axis])
|
||||
bounding_box_max[axis] = math.max(a[axis], b[axis], c[axis])
|
||||
end
|
||||
|
||||
-- Calculate normal
|
||||
local u = vector.subtract(b, a)
|
||||
local v = vector.subtract(c, a)
|
||||
local normal = vector.new(
|
||||
u.y * v.z - u.z * v.y,
|
||||
u.z * v.x - u.x * v.z,
|
||||
u.x * v.y - u.y * v.x
|
||||
)
|
||||
|
||||
local selected_axis = "y"
|
||||
local longest_length = 0
|
||||
for axis, length in pairs(normal) do
|
||||
length = math.abs(length)
|
||||
if length > longest_length then
|
||||
longest_length = length
|
||||
selected_axis = axis
|
||||
end
|
||||
end
|
||||
|
||||
-- Switch from local to global coordinate system.
|
||||
-- Also works the same to convert local to global coordinate system.
|
||||
local function swap_coord_sys(v)
|
||||
v = vector.copy(v)
|
||||
local old_selected = v[selected_axis]
|
||||
v[selected_axis] = v.y
|
||||
v.y = old_selected
|
||||
return v
|
||||
end
|
||||
|
||||
local bounding_box_min_local = swap_coord_sys(bounding_box_min)
|
||||
local bounding_box_max_local = swap_coord_sys(bounding_box_max)
|
||||
local a_local = swap_coord_sys(a)
|
||||
local b_local = swap_coord_sys(b)
|
||||
local c_local = swap_coord_sys(c)
|
||||
|
||||
local results = {}
|
||||
for x = bounding_box_min_local.x, bounding_box_max_local.x do
|
||||
for z = bounding_box_min_local.z, bounding_box_max_local.z do
|
||||
local intersection = ray_intersects_triangle(vector.new(x, 30928, z), vector.new(0, -1, 0), a_local, b_local, c_local)
|
||||
if intersection then
|
||||
table.insert(results, vector.round(swap_coord_sys(intersection)))
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
local function place_polygon(player, item_name)
|
||||
local player_data = edit.player_data[player]
|
||||
if not player_data then return end
|
||||
if not item_name or #player_data.polygon_markers < 2 then
|
||||
player_data.polygon_markers.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
local markers = player_data.polygon_markers
|
||||
|
||||
local inf = 1 / 0
|
||||
local bounding_box_min = vector.new(inf, inf, inf)
|
||||
local bounding_box_max = vector.new(-inf, -inf, -inf)
|
||||
for index, axis in pairs({"x", "y", "z"}) do
|
||||
for i, marker in ipairs(markers) do
|
||||
bounding_box_min[axis] = math.min(bounding_box_min[axis], marker._pos[axis])
|
||||
bounding_box_max[axis] = math.max(bounding_box_max[axis], marker._pos[axis])
|
||||
end
|
||||
end
|
||||
local volume = vector.add(vector.subtract(bounding_box_max, bounding_box_min), vector.new(1, 1, 1))
|
||||
if volume.x * volume.y * volume.z > edit.max_operation_volume then
|
||||
edit.display_size_error(player)
|
||||
player_data.polygon_markers.object:remove()
|
||||
return
|
||||
end
|
||||
player_data.undo_schematic = edit.schematic_from_map(bounding_box_min, volume)
|
||||
|
||||
local points = {}
|
||||
for i = 3, #markers do
|
||||
table.insert_all(
|
||||
points,
|
||||
edit.calculate_triangle_points(
|
||||
markers[i]._pos,
|
||||
markers[i - 1]._pos,
|
||||
markers[1]._pos
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
local item = {name = item_name}
|
||||
edit.place_item_like_player(player, item, markers[1]._pos)
|
||||
item.param2 = minetest.get_node(markers[1]._pos).param2
|
||||
for i, pos in pairs(points) do
|
||||
edit.place_item_like_player(player, item, pos)
|
||||
end
|
||||
player_data.polygon_markers.object:remove()
|
||||
end
|
||||
|
||||
minetest.register_entity("edit:polygon", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = { x = 1.1, y = 1.1 },
|
||||
physical = false,
|
||||
collide_with_objects = false,
|
||||
static_save = false,
|
||||
use_texture_alpha = true,
|
||||
glow = -1,
|
||||
backface_culling = false,
|
||||
hp_max = 1,
|
||||
textures = {
|
||||
"edit_polygon.png",
|
||||
"edit_polygon.png",
|
||||
"edit_polygon.png",
|
||||
"edit_polygon.png",
|
||||
"edit_polygon.png",
|
||||
"edit_polygon.png",
|
||||
},
|
||||
},
|
||||
on_deactivate = function(self)
|
||||
local player_data = edit.player_data[self._placer]
|
||||
if player_data then
|
||||
local index = table.indexof(player_data.polygon_markers, self)
|
||||
table.remove(player_data.polygon_markers, index)
|
||||
|
||||
local marker = player_data.polygon_markers[1]
|
||||
if index == 1 and marker then
|
||||
local textures = marker.object:get_properties().textures
|
||||
for i, texture in pairs(textures) do
|
||||
textures[i] = texture .. "^[multiply:green"
|
||||
end
|
||||
marker.object:set_properties({textures = textures})
|
||||
end
|
||||
end
|
||||
player_data.old_pointed_pos = nil
|
||||
end,
|
||||
})
|
||||
|
||||
local function polygon_on_place(itemstack, player, pointed_thing)
|
||||
if not edit.on_place_checks(player) then return end
|
||||
|
||||
if not pointed_thing.above then
|
||||
pointed_thing = edit.get_pointed_thing_node(player)
|
||||
end
|
||||
|
||||
local pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||
if not pos then return end
|
||||
|
||||
local player_data = edit.player_data[player]
|
||||
|
||||
if not player_data.polygon_markers then
|
||||
player_data.polygon_markers = {}
|
||||
player_data.polygon_markers.object = player_data.polygon_markers
|
||||
player_data.polygon_markers.object.remove = function(self)
|
||||
for i, luaentity in ipairs(table.copy(self)) do
|
||||
luaentity.object:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if player_data.polygon_markers[1] and vector.equals(player_data.polygon_markers[1]._pos, pos) then
|
||||
edit.player_select_item(player, "Select item to fill the polygon", place_polygon)
|
||||
return
|
||||
end
|
||||
|
||||
local marker = edit.add_marker("edit:polygon", pos, player)
|
||||
if not marker then return end
|
||||
table.insert(player_data.polygon_markers, marker)
|
||||
|
||||
if marker == player_data.polygon_markers[1] then
|
||||
local textures = marker.object:get_properties().textures
|
||||
for i, texture in pairs(textures) do
|
||||
textures[i] = texture .. "^[multiply:green"
|
||||
end
|
||||
marker.object:set_properties({textures = textures})
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_tool("edit:polygon", {
|
||||
description = "Edit Polygon",
|
||||
tiles = {"edit_polygon.png"},
|
||||
inventory_image = "edit_polygon.png",
|
||||
range = 10,
|
||||
groups = {edit_place_preview = 1,},
|
||||
on_place = polygon_on_place,
|
||||
on_secondary_use = polygon_on_place,
|
||||
})
|
291
preview.lua
291
preview.lua
@ -103,6 +103,135 @@ local function create_paste_preview(player)
|
||||
edit.rotate_paste_preview(player)
|
||||
end
|
||||
|
||||
minetest.register_entity("edit:polygon_preview", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
physical = false,
|
||||
pointable = false,
|
||||
collide_with_objects = false,
|
||||
static_save = false,
|
||||
use_texture_alpha = true,
|
||||
glow = -1,
|
||||
backface_culling = false,
|
||||
visual_size = { x = 1.05, y = 1.05 },
|
||||
textures = {
|
||||
"edit_select_preview.png^[sheet:8x8:1,1",
|
||||
"edit_select_preview.png^[sheet:8x8:1,1",
|
||||
"edit_select_preview.png^[sheet:8x8:1,1",
|
||||
"edit_select_preview.png^[sheet:8x8:1,1",
|
||||
"edit_select_preview.png^[sheet:8x8:1,1",
|
||||
"edit_select_preview.png^[sheet:8x8:1,1",
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
local function hide_polygon_preview(player)
|
||||
local player_data = edit.player_data[player]
|
||||
local previews = player_data.polygon_previews
|
||||
for i, obj_ref in ipairs(previews) do
|
||||
obj_ref:set_properties({is_visible = false})
|
||||
end
|
||||
|
||||
if player_data.polygon_preview_hud then
|
||||
player:hud_remove(player_data.polygon_preview_hud)
|
||||
player_data.polygon_preview_hud = nil
|
||||
end
|
||||
|
||||
player_data.polygon_preview_shown = false
|
||||
end
|
||||
|
||||
local function show_polygon_preview(player, show_polygon_hud)
|
||||
local player_data = edit.player_data[player]
|
||||
|
||||
if not player_data.polygon_previews then
|
||||
player_data.polygon_previews = {}
|
||||
player_data.polygon_previews.object = player_data.polygon_previews
|
||||
player_data.polygon_previews.object.remove = function(self)
|
||||
for i, luaentity in ipairs(table.copy(self)) do
|
||||
luaentity:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i, obj_ref in ipairs(player_data.polygon_previews) do
|
||||
obj_ref:set_properties({is_visible = true})
|
||||
end
|
||||
|
||||
player_data.polygon_preview_shown = true
|
||||
end
|
||||
|
||||
local function update_polygon_preview(player, marker_pos_list, show_polygon_hud)
|
||||
local player_pos = player:get_pos()
|
||||
local player_data = edit.player_data[player]
|
||||
|
||||
local show_full_preview = true
|
||||
local bounding_box_min = vector.copy(marker_pos_list[1])
|
||||
local bounding_box_max = vector.copy(marker_pos_list[1])
|
||||
for index, axis in pairs({"x", "y", "z"}) do
|
||||
for i, pos in ipairs(marker_pos_list) do
|
||||
bounding_box_min[axis] = math.min(bounding_box_min[axis], pos[axis])
|
||||
bounding_box_max[axis] = math.max(bounding_box_max[axis], pos[axis])
|
||||
end
|
||||
if
|
||||
bounding_box_max[axis] - bounding_box_min[axis] + 1 >
|
||||
edit.polygon_preview_wire_frame_threshold
|
||||
then
|
||||
show_full_preview = false
|
||||
end
|
||||
end
|
||||
|
||||
local pos_list = {}
|
||||
|
||||
local volume = vector.add(vector.subtract(bounding_box_max, bounding_box_min), vector.new(1, 1, 1))
|
||||
if volume.x * volume.y * volume.z <= edit.max_operation_volume then
|
||||
if #marker_pos_list == 2 or not show_full_preview then
|
||||
table.insert_all(
|
||||
pos_list,
|
||||
edit.calculate_line_points(marker_pos_list[1], marker_pos_list[2])
|
||||
)
|
||||
end
|
||||
|
||||
for i = 3, #marker_pos_list do
|
||||
if show_full_preview then
|
||||
table.insert_all(
|
||||
pos_list,
|
||||
edit.calculate_triangle_points(
|
||||
marker_pos_list[i],
|
||||
marker_pos_list[i - 1],
|
||||
marker_pos_list[1]
|
||||
)
|
||||
)
|
||||
else
|
||||
table.insert_all(
|
||||
pos_list,
|
||||
edit.calculate_line_points(marker_pos_list[i], marker_pos_list[i - 1])
|
||||
)
|
||||
table.insert_all(
|
||||
pos_list,
|
||||
edit.calculate_line_points(marker_pos_list[i], marker_pos_list[1])
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local preview_objs = player_data.polygon_previews
|
||||
if #preview_objs > #pos_list then
|
||||
for i = #pos_list + 1, #preview_objs do
|
||||
preview_objs[#pos_list + 1]:remove()
|
||||
table.remove(preview_objs, #pos_list + 1)
|
||||
end
|
||||
elseif #preview_objs < #pos_list then
|
||||
for i = #preview_objs + 1, #pos_list do
|
||||
local obj_ref = minetest.add_entity(player_pos, "edit:polygon_preview")
|
||||
table.insert(preview_objs, obj_ref)
|
||||
end
|
||||
end
|
||||
|
||||
for i, pos in pairs(pos_list) do
|
||||
preview_objs[i]:set_pos(pos)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("edit:select_preview", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
@ -289,6 +418,36 @@ local function set_schematic_offset(player)
|
||||
d.schematic_offset = offset
|
||||
end
|
||||
|
||||
local function show_place_preview(player, pos, item)
|
||||
local d = edit.player_data[player]
|
||||
|
||||
if not d.place_preview or not d.place_preview.object:get_pos() then
|
||||
local obj_ref = minetest.add_entity(player:get_pos(), "edit:place_preview")
|
||||
if not obj_ref then return end
|
||||
d.place_preview = obj_ref:get_luaentity()
|
||||
d.place_preview_shown = true
|
||||
d.place_preview_item = nil
|
||||
elseif not d.place_preview_shown then
|
||||
d.place_preview.object:set_properties({ is_visible = true })
|
||||
d.place_preview.object:set_detach()
|
||||
d.place_preview_shown = true
|
||||
end
|
||||
|
||||
if not vector.equals(d.place_preview.object:get_pos(), pos) then
|
||||
d.place_preview.object:set_pos(pos)
|
||||
end
|
||||
|
||||
if d.place_preview_item ~= item then
|
||||
local tex = minetest.registered_items[item].tiles[1] ..
|
||||
"^[opacity:150"
|
||||
|
||||
d.place_preview_item = item
|
||||
d.place_preview.object:set_properties({
|
||||
textures = { tex, tex, tex, tex, tex, tex }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
local item = player:get_wielded_item():get_name()
|
||||
@ -314,70 +473,96 @@ minetest.register_globalstep(function(dtime)
|
||||
elseif d.paste_preview_hud then hide_paste_preview(player) end
|
||||
elseif d.paste_preview_hud then hide_paste_preview(player) end
|
||||
|
||||
-- Select preview
|
||||
local node1_pos
|
||||
local node2_pos
|
||||
local pointed_pos
|
||||
local tool_def = minetest.registered_items[item] or minetest.registered_items["air"]
|
||||
-- Stuff for Place preview and box select preview
|
||||
local marker1_pos
|
||||
local marker2_pos
|
||||
local should_show_place_preview = minetest.get_item_group(item, "edit_place_preview") ~= 0
|
||||
local should_use_box_select_preview = minetest.get_item_group(item, "edit_box_select_preview") ~= 0
|
||||
|
||||
if tool_def._edit_get_selection_points then
|
||||
node1_pos, node2_pos = tool_def._edit_get_selection_points(player)
|
||||
if should_show_place_preview or should_use_box_select_preview then
|
||||
local tool_def = minetest.registered_items[item]
|
||||
if tool_def._edit_get_selection_points then
|
||||
marker1_pos, marker2_pos = tool_def._edit_get_selection_points(player)
|
||||
end
|
||||
if not marker2_pos then
|
||||
if tool_def._edit_get_pointed_pos then
|
||||
marker2_pos = tool_def._edit_get_pointed_pos(player)
|
||||
else
|
||||
local pointed_thing = edit.get_pointed_thing_node(player)
|
||||
marker2_pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||
end
|
||||
else should_show_place_preview = false end
|
||||
end
|
||||
|
||||
if not node2_pos or not node1_pos then
|
||||
if tool_def._edit_get_pointed_pos then
|
||||
pointed_pos = tool_def._edit_get_pointed_pos(player)
|
||||
else
|
||||
local pointed_thing = edit.get_pointed_thing_node(player)
|
||||
pointed_pos = edit.pointed_thing_to_pos(pointed_thing)
|
||||
end
|
||||
end
|
||||
-- Box select preview
|
||||
if should_use_box_select_preview and marker1_pos and marker2_pos then
|
||||
local diff = vector.subtract(marker1_pos, marker2_pos)
|
||||
local size = vector.apply(diff, math.abs)
|
||||
size = vector.add(size, vector.new(1, 1, 1))
|
||||
local size_too_big = size.x * size.y * size.z > edit.max_operation_volume
|
||||
if not size_too_big then
|
||||
local preview_pos = vector.add(marker2_pos, vector.multiply(diff, 0.5))
|
||||
update_select_preview(player, preview_pos, size)
|
||||
elseif d.select_preview_shown then hide_select_preview(player) end
|
||||
elseif d.select_preview_shown then hide_select_preview(player) end
|
||||
|
||||
if minetest.get_item_group(item, "edit_place_preview") ~= 0 and not node2_pos and pointed_pos then
|
||||
if not d.place_preview or not d.place_preview.object:get_pos() then
|
||||
local obj_ref = minetest.add_entity(player:get_pos(), "edit:place_preview")
|
||||
if not obj_ref then return end
|
||||
d.place_preview = obj_ref:get_luaentity()
|
||||
d.place_preview_shown = true
|
||||
d.place_preview_item = nil
|
||||
elseif not d.place_preview_shown then
|
||||
d.place_preview.object:set_properties({ is_visible = true })
|
||||
d.place_preview.object:set_detach()
|
||||
d.place_preview_shown = true
|
||||
end
|
||||
|
||||
if not vector.equals(d.place_preview.object:get_pos(), pointed_pos) then
|
||||
d.place_preview.object:set_pos(pointed_pos)
|
||||
end
|
||||
|
||||
if d.place_preview_item ~= item then
|
||||
local tex = minetest.registered_items[item].tiles[1] ..
|
||||
"^[opacity:150"
|
||||
|
||||
d.place_preview_item = item
|
||||
d.place_preview.object:set_properties({
|
||||
textures = { tex, tex, tex, tex, tex, tex }
|
||||
})
|
||||
end
|
||||
-- Place preview
|
||||
if should_show_place_preview and marker2_pos then
|
||||
show_place_preview(player, marker2_pos, item)
|
||||
elseif d.place_preview_shown then
|
||||
d.place_preview.object:set_properties({ is_visible = false })
|
||||
d.place_preview.object:set_attach(player)
|
||||
d.place_preview_shown = false
|
||||
end
|
||||
|
||||
if not node2_pos then
|
||||
node2_pos = pointed_pos
|
||||
-- Polygon preview
|
||||
if item == "edit:polygon" or item == "edit:line" then
|
||||
if marker2_pos then
|
||||
if not d.polygon_preview_shown then
|
||||
show_polygon_preview(player)
|
||||
end
|
||||
|
||||
if not d.old_pointed_pos then d.old_pointed_pos = vector.new(0.5, 0.5, 0.5) end
|
||||
|
||||
if
|
||||
d.old_polygon_item ~= item or
|
||||
not vector.equals(d.old_pointed_pos, marker2_pos)
|
||||
then
|
||||
if item == "edit:polygon" then
|
||||
local markers = d.polygon_markers or {}
|
||||
local marker_pos_list = {}
|
||||
for i, marker in ipairs(markers) do
|
||||
table.insert(marker_pos_list, marker._pos)
|
||||
end
|
||||
table.insert(marker_pos_list, marker2_pos)
|
||||
update_polygon_preview(player, marker_pos_list)
|
||||
else
|
||||
local marker_pos_list = {}
|
||||
if marker1_pos then table.insert(marker_pos_list, marker1_pos) end
|
||||
table.insert(marker_pos_list, marker2_pos)
|
||||
update_polygon_preview(player, marker_pos_list)
|
||||
end
|
||||
d.old_pointed_pos = marker2_pos
|
||||
d.old_polygon_item = item
|
||||
end
|
||||
end
|
||||
elseif d.polygon_preview_shown then
|
||||
hide_polygon_preview(player)
|
||||
end
|
||||
|
||||
if node1_pos and node2_pos then
|
||||
local diff = vector.subtract(node1_pos, node2_pos)
|
||||
local size = vector.apply(diff, math.abs)
|
||||
size = vector.add(size, vector.new(1, 1, 1))
|
||||
local size_too_big = size.x * size.y * size.z > edit.max_operation_volume
|
||||
if not size_too_big then
|
||||
local preview_pos = vector.add(node2_pos, vector.multiply(diff, 0.5))
|
||||
update_select_preview(player, preview_pos, size)
|
||||
elseif d.select_preview_shown then hide_select_preview(player) end
|
||||
elseif d.select_preview_shown then hide_select_preview(player) end
|
||||
if item == "edit:polygon" then
|
||||
if not d.polygon_preview_hud then
|
||||
d.polygon_preview_hud = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
text = "Finish the polygon by placing a marker on the green marker",
|
||||
position = {x = 0.5, y = 0.8},
|
||||
z_index = 100,
|
||||
number = 0xffffff
|
||||
})
|
||||
end
|
||||
elseif d.polygon_preview_hud then
|
||||
player:hud_remove(d.polygon_preview_hud)
|
||||
d.polygon_preview_hud = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
@ -102,7 +102,7 @@ minetest.register_tool("edit:replace", {
|
||||
tiles = {"edit_replace.png"},
|
||||
inventory_image = "edit_replace.png",
|
||||
range = 10,
|
||||
groups = {edit_place_preview = 1,},
|
||||
groups = {edit_place_preview = 1, edit_box_select_preview = 1},
|
||||
on_place = replace_on_place,
|
||||
on_secondary_use = replace_on_place,
|
||||
_edit_get_selection_points = function(player)
|
||||
@ -187,7 +187,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
if not fields.continue then return true end
|
||||
|
||||
edit.player_select_node(player, "Select item to replace nodes", function(player, name)
|
||||
edit.player_select_item(player, "Select item to replace nodes", function(player, name)
|
||||
if
|
||||
not d.replace1 or not d.replace2 or
|
||||
not d.replace_source_nodes or
|
||||
@ -233,7 +233,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if is_node then
|
||||
minetest.swap_node(pos, node)
|
||||
else
|
||||
edit.place_node_like_player(player, node, pos)
|
||||
edit.place_item_like_player(player, node, pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -10,3 +10,7 @@ edit_max_operation_volume (Max edit operation volume) int 20000
|
||||
# To disable slow node placement, set the threshold to 0.
|
||||
# With fast node placement, callbacks are not called so some nodes might be broken.
|
||||
edit_fast_node_fill_threshold (Fast node fill threshold volume) int 2000
|
||||
|
||||
# If one side of the polygon preview is greater than this setting,
|
||||
# a wire frame is used instead of the full preview.
|
||||
edit_polygon_preview_wire_frame_threshold (Polygon preview wire frame threshold) int 40
|
||||
|
BIN
textures/edit_bag.png
Normal file
BIN
textures/edit_bag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 166 B |
BIN
textures/edit_line.png
Normal file
BIN
textures/edit_line.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 B |
BIN
textures/edit_polygon.png
Normal file
BIN
textures/edit_polygon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 175 B |
Loading…
x
Reference in New Issue
Block a user