mcl_meshnode/api.lua

436 lines
12 KiB
Lua

meshnode = {}
meshnode.config = {
max_speed = 2,
max_lift = 1,
yaw_amount = 0.017,
max_radius = 8,
show_in_creative = false,
enable_crafting = false,
disable_privilege = false,
fake_shading = false,
autoconf = false,
}
meshnode.blacklist = {}
local face_rotation = {
{x=0, y=0, z=0}, {x=0, y=90, z=0}, {x=0, y=180, z=0},
{x=0, y=-90, z=0}, {x=90, y=0, z=0}, {x=90, y=0, z=90},
{x=90, y=0, z=180}, {x=90, y=0, z=-90}, {x=-90, y=0, z=0},
{x=-90, y=0, z=-90}, {x=-90, y=0, z=180}, {x=-90, y=0, z=90},
{x=0, y=0, z=-90}, {x=90, y=90, z=0}, {x=180, y=0, z=90},
{x=0, y=-90, z=-90}, {x=0, y=0, z=90}, {x=0, y=90, z=90},
{x=180, y=0, z=-90}, {x=0, y=-90, z=90}, {x=180, y=180, z=0},
{x=180, y=90, z=0}, {x=180, y=0, z=0}, {x=180, y=-90, z=0},
}
local meshnode_id = 0
local function connects_to_group(pos, groups)
local node = minetest.get_node(pos)
for _, group in pairs(groups) do
if minetest.get_item_group(node.name, group) > 0 then
return true
end
end
end
local function get_tile_textures(tiles)
local textures = table.copy(tiles or {})
for i, v in pairs(textures) do
if type(v) == "table" then
textures[i] = v.name or "blank.png"
if v.name and v.animation then
local w = v.animation.aspect_w or 16
local h = v.animation.aspect_h or w
textures[i] = "[combine:"..w.."x"..h..":0,0="..v.name
end
end
end
return textures
end
local function get_tile_shading(t, facedir)
local tiles = {t[1], t[1], t[1], t[1], t[1], t[1]}
if #t == 3 then
tiles = {t[1], t[2], t[3], t[3], t[3], t[3]}
elseif #t == 6 then
tiles = table.copy(t)
end
if facedir and meshnode.config.fake_shading == true then
local tile_opposite = {2, 1, 4, 3, 6, 5}
local tile_rotation = {[0] =
{1, 6}, {1, 3}, {1, 5}, {1, 4},
{6, 2}, {3, 2}, {5, 2}, {4, 2},
{5, 1}, {4, 1}, {6, 1}, {3, 1},
{4, 6}, {6, 3}, {3, 5}, {5, 4},
{3, 6}, {5, 3}, {4, 5}, {6, 4},
{2, 6}, {2, 3}, {2, 5}, {2, 4},
}
local modifiers = {}
local top = tile_rotation[facedir][1]
local front = tile_rotation[facedir][2]
local bottom = tile_opposite[top]
local back = tile_opposite[front]
modifiers[top] = "^[colorize:#000000:16"
modifiers[bottom] = "^[colorize:#000000:128"
modifiers[front] = "^[colorize:#000000:64"
modifiers[back] = "^[colorize:#000000:64"
for i = 1, 6 do
local modifier = modifiers[i] or "^[colorize:#000000:32"
tiles[i] = tiles[i]..modifier
end
end
return tiles
end
local function get_facecon_textures(facecons, texture)
local textures = {
"meshnode_trans.png",
"meshnode_trans.png",
"meshnode_trans.png",
"meshnode_trans.png",
}
for i = 1, 4 do
if facecons[i] == true then
textures[i] = texture
end
end
return textures
end
local function restore_facedir(node, delta, yaw)
local facedir = node.param2 or 0
local def = minetest.registered_items[node.name] or {}
if def.paramtype2 == "facedir" then
local rot = (meshnode.yaw_to_facedir(yaw) + delta) % 4
node.param2 = meshnode.rotate_facedir(rot, facedir)
end
end
meshnode.new_id = function()
meshnode_id = meshnode_id + 1
return tostring(meshnode_id)
end
meshnode.get_luaentity = function(id)
for _, entity in pairs(minetest.luaentities) do
if entity.mesh_id == id then
return entity
end
end
end
meshnode.get_map_pos = function(ref, parent)
local pos = parent.object:getpos()
local yaw = parent.object:getyaw()
local rot = meshnode.rotate_offset(yaw, ref.offset)
local vec = vector.add(pos, rot)
return vector.round(vec)
end
meshnode.facedir_to_yaw = function(facedir)
local yaw = 0
local rot = facedir % 4
if rot == 1 then
yaw = 3 * math.pi / 2
elseif rot == 2 then
yaw = math.pi
elseif rot == 3 then
yaw = math.pi / 2
end
return yaw
end
meshnode.yaw_to_facedir = function(yaw)
local deg = math.floor(math.deg(yaw) + 0.5) % 360
if deg < 90 then
return 0
end
return 4 - math.floor(deg / 90)
end
meshnode.facedir_to_rotation = function(facedir)
return face_rotation[facedir + 1] or face_rotation[1]
end
meshnode.rotation_to_facedir = function(rotation)
for i, v in ipairs(face_rotation) do
if vector.equals(v, rotation) then
return i - 1
end
end
return 0
end
meshnode.rotate_facedir = function(rot, facedir)
if rot == 0 then
return facedir
end
local rotation = facedir % 32
local axis = math.floor(rotation / 4)
if axis == 0 then
rotation = rotation + rot
elseif axis == 5 then
rotation = rotation - rot
else
local axis_rotation = {
{3, 2, 4},
{4, 1, 3},
{2, 4, 1},
{1, 3, 2},
}
local next_axis = axis_rotation[axis]
axis = next_axis[rot]
rotation = rotation + rot
end
return axis * 4 + (rotation % 4)
end
meshnode.rotate_offset = function(yaw, offset)
local cos = math.cos(yaw)
local sin = math.sin(yaw)
local x = offset.x * cos - offset.z * sin
local z = offset.x * sin + offset.z * cos
return vector.round({x=x, y=offset.y, z=z})
end
meshnode.add_entity = function(ref, parent)
local pos = meshnode.get_map_pos(ref, parent)
local object = minetest.add_entity(pos, "meshnode:mesh")
if object then
local properties = {textures={ref.node.name}}
local def = minetest.registered_items[ref.node.name] or {}
local tiles = get_tile_textures(def.tiles)
if ref.meshtype == "stair" or
ref.meshtype == "cube" or
ref.meshtype == "slab" then
local param2 = ref.node.param2 or 0
properties.visual = "mesh"
properties.visual_size = {x=1, y=1}
properties.mesh = "meshnode_"..ref.meshtype..".obj"
properties.textures = get_tile_shading(tiles, param2)
elseif ref.meshtype == "mesh" then
properties.visual = "mesh"
properties.visual_size = {x=10, y=10}
properties.mesh = def.mesh
properties.textures = tiles
elseif ref.meshtype == "plant" then
properties.visual = "mesh"
properties.visual_size = {x=1, y=1}
properties.mesh = "meshnode_plant.obj"
properties.textures = {tiles[1]}
elseif ref.meshtype == "fence" then
local textures = get_facecon_textures(ref.facecons, tiles[1])
table.insert(textures, 1, tiles[1])
properties.visual = "mesh"
properties.visual_size = {x=1, y=1}
properties.mesh = "meshnode_fence.obj"
properties.textures = textures
elseif ref.meshtype == "wall" then
local textures = get_facecon_textures(ref.facecons, tiles[1])
table.insert(textures, 1, tiles[1])
properties.visual = "mesh"
properties.visual_size = {x=1, y=1}
properties.mesh = "meshnode_wall.obj"
properties.textures = textures
elseif ref.meshtype == "pane" then
local textures = get_facecon_textures(ref.facecons, tiles[3])
properties.visual = "mesh"
properties.visual_size = {x=1, y=1}
properties.mesh = "meshnode_pane.obj"
properties.textures = textures
end
object:set_properties(properties)
if parent then
local entity = object:get_luaentity()
if entity then
entity.mesh_id = ref.id
entity.parent_id = parent.mesh_id
else
object:remove()
return
end
local yaw = parent.object:getyaw()
local offset = vector.multiply(ref.offset, 10)
object:set_attach(parent.object, "", offset, ref.rotation)
end
end
return object
end
meshnode.create = function(pos, parent)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local def = minetest.registered_items[node.name] or {}
local meshtype = "wielditem"
local scaffold = "meshnode:scaffold"
local facecons = {}
local faces = {
[1] = {x=0, y=0, z=-1},
[2] = {x=-1, y=0, z=0},
[3] = {x=0, y=0, z=1},
[4] = {x=1, y=0, z=0},
}
if not parent or
meshnode.blacklist[node.name] or
node.name == "meshnode:controller" or
node.name == "air" or
node.name == "ignore" or
def.paramtype2 == "wallmounted" or
def.paramtype2 == "flowingliquid" then
return
elseif def.drawtype == nil or
def.drawtype == "normal" or
def.drawtype == "liquid" then
meshtype = "cube"
elseif def.drawtype == "mesh" then
if string.find(node.name, "^stairs:stair_") then
meshtype = "stair"
else
meshtype = "mesh"
end
elseif string.find(node.name, "^stairs:slab_") then
meshtype = "slab"
elseif def.drawtype == "plantlike" then
meshtype = "plant"
elseif minetest.get_item_group(node.name, "fence") > 0 then
scaffold = "meshnode:scaffold_fence"
if def.drawtype ~= "mesh" then
meshtype = "fence"
for i, face in pairs(faces) do
local p = vector.add(pos, face)
facecons[i] = connects_to_group(p, {"fence", "wood", "tree"})
end
end
elseif minetest.get_item_group(node.name, "wall") > 0 then
scaffold = "meshnode:scaffold_wall"
if def.drawtype ~= "mesh" then
meshtype = "wall"
for i, face in pairs(faces) do
local p = vector.add(pos, face)
facecons[i] = connects_to_group(p, {"wall", "stone"})
end
end
elseif minetest.get_item_group(node.name, "pane") > 0 then
meshtype = "pane"
scaffold = "meshnode:scaffold_pane"
if string.find(node.name, "_flat$") then
facecons = {[2]=true, [4]=true}
else
for i, face in pairs(faces) do
local p = vector.add(pos, face)
facecons[i] = connects_to_group(p, {"pane"})
end
end
elseif def.wield_image ~= "" or def.inventory_image ~="" then
return
end
local param2 = node.param2 or 0
local offset = vector.subtract(pos, parent.object:getpos())
local yaw = math.pi * 2 - parent.object:getyaw()
local delta = meshnode.yaw_to_facedir(yaw)
local facedir = meshnode.rotate_facedir(delta, param2)
local meta_str = nil
local meta_tab = meta:to_table() or {}
if meta_tab.inventory then
for _, list in pairs(meta_tab.inventory) do
for i, stack in ipairs(list) do
local str = ItemStack(stack):to_string()
if str:len() > 0xffff then
minetest.log("error", "String too long for serialization!")
str = ""
end
list[i] = str
end
end
end
local ref = {
id = meshnode.new_id(),
node = node,
meta = meta_tab,
delta = delta,
meshtype = meshtype,
facecons = facecons,
offset = meshnode.rotate_offset(yaw, offset),
rotation = meshnode.facedir_to_rotation(facedir),
}
local object = meshnode.add_entity(ref, parent)
if object then
minetest.set_node(pos, {name=scaffold})
table.insert(parent.nodes, ref)
end
return object
end
meshnode.restore = function(ref, parent)
local entity = meshnode.get_luaentity(ref.id)
local pos = meshnode.get_map_pos(ref, parent)
local yaw = parent.object:getyaw()
restore_facedir(ref.node, ref.delta, yaw)
minetest.add_node(pos, ref.node)
if entity then
entity.object:setpos(pos)
entity.object:set_detach()
entity.object:remove()
end
if ref.meta then
local meta = minetest.get_meta(pos)
local meta_tab = ref.meta or {}
meta:from_table(meta_tab)
end
end
meshnode.restore_all = function(parent, name)
local positions = {}
if #parent.nodes == 0 then
return positions
end
local minp = {x=32000, y=32000, z=32000}
local maxp = vector.multiply(minp, -1)
local yaw = parent.object:getyaw()
local nodedata = {}
for _, ref in pairs(parent.nodes) do
local pos = meshnode.get_map_pos(ref, parent)
if not minetest.get_node_or_nil(pos) then
return
end
if name and minetest.is_protected(pos, name) then
return
end
table.insert(nodedata, {pos=pos, ref=ref})
for axis, val in pairs(pos) do
if val < minp[axis] then
minp[axis] = val
end
if val > maxp[axis] then
maxp[axis] = val
end
end
end
local vm = minetest.get_voxel_manip(minp, maxp)
for _, data in pairs(nodedata) do
restore_facedir(data.ref.node, data.ref.delta, yaw)
vm:set_node_at(data.pos, data.ref.node)
end
vm:write_to_map()
vm:update_liquids()
vm:update_map()
for _, data in pairs(nodedata) do
local entity = meshnode.get_luaentity(data.ref.id)
if entity then
entity.object:setpos(data.pos)
entity.object:set_detach()
entity.object:remove()
end
if data.ref.meta then
local meta = minetest.get_meta(data.pos)
local meta_tab = data.ref.meta or {}
meta:from_table(meta_tab)
end
table.insert(positions, data.pos)
end
return positions
end