176 lines
5.9 KiB
Lua
176 lines
5.9 KiB
Lua
meshport.nodebox_cache = {}
|
|
meshport.mesh_cache = {}
|
|
|
|
meshport.cube_face_priority = {
|
|
allfaces = 1,
|
|
glasslike_framed = 2,
|
|
glasslike = 3,
|
|
normal = 4,
|
|
}
|
|
|
|
function meshport.create_cube_node(nodeDrawtype, nodeTiles, pos, facedir, neighbors)
|
|
local sideFaces = {
|
|
{{x = 0.5, y = 0.5, z =-0.5}, {x = 0.5, y = 0.5, z = 0.5}, {x =-0.5, y = 0.5, z = 0.5}, {x =-0.5, y = 0.5, z =-0.5}}, -- Y+
|
|
{{x = 0.5, y =-0.5, z = 0.5}, {x = 0.5, y =-0.5, z =-0.5}, {x =-0.5, y =-0.5, z =-0.5}, {x =-0.5, y =-0.5, z = 0.5}}, -- Y-
|
|
{{x = 0.5, y =-0.5, z = 0.5}, {x = 0.5, y = 0.5, z = 0.5}, {x = 0.5, y = 0.5, z =-0.5}, {x = 0.5, y =-0.5, z =-0.5}}, -- X+
|
|
{{x =-0.5, y =-0.5, z =-0.5}, {x =-0.5, y = 0.5, z =-0.5}, {x =-0.5, y = 0.5, z = 0.5}, {x =-0.5, y =-0.5, z = 0.5}}, -- X-
|
|
{{x =-0.5, y =-0.5, z = 0.5}, {x =-0.5, y = 0.5, z = 0.5}, {x = 0.5, y = 0.5, z = 0.5}, {x = 0.5, y =-0.5, z = 0.5}}, -- Z+
|
|
{{x = 0.5, y =-0.5, z =-0.5}, {x = 0.5, y = 0.5, z =-0.5}, {x =-0.5, y = 0.5, z =-0.5}, {x =-0.5, y =-0.5, z =-0.5}}, -- Z-
|
|
}
|
|
|
|
local texCoords = {{x = 1, y = 0}, {x = 1, y = 1}, {x = 0, y = 1}, {x = 0, y = 0}}
|
|
|
|
local faces = meshport.Faces:new()
|
|
-- For glasslike_framed nodes, only the first tile is used.
|
|
local tileIdx = nodeDrawtype == "glasslike_framed" and 1 or nil
|
|
local neighborDrawtype, vertNorm
|
|
|
|
for i = 1, 6 do
|
|
neighborDrawtype = meshport.get_aliased_drawtype(meshport.get_def_from_id(neighbors[i]).drawtype)
|
|
|
|
if meshport.cube_face_priority[nodeDrawtype] > (meshport.cube_face_priority[neighborDrawtype] or 0)
|
|
-- For allfaces nodes (such are leaves), interior faces are drawn only when facing X+, Y+, or Z+.
|
|
or (nodeDrawtype == "allfaces" and neighborDrawtype == "allfaces" and i % 2 == 1) then
|
|
vertNorm = meshport.neighbor_dirs[i]
|
|
|
|
faces:insert_face(meshport.prepare_cuboid_face({
|
|
verts = sideFaces[i],
|
|
tex_coords = texCoords,
|
|
vert_norms = {vertNorm, vertNorm, vertNorm, vertNorm},
|
|
tile_idx = tileIdx,
|
|
}, nodeTiles, pos, facedir, i))
|
|
end
|
|
end
|
|
|
|
return faces
|
|
end
|
|
|
|
function meshport.create_nodebox_node(nodeName, pos, facedir, param2, neighbors)
|
|
local nodeDef = minetest.registered_nodes[nodeName]
|
|
|
|
if not meshport.nodebox_cache[nodeName] then
|
|
meshport.nodebox_cache[nodeName] = meshport.prepare_nodebox(nodeDef.node_box)
|
|
end
|
|
|
|
local boxes = meshport.collect_boxes(meshport.nodebox_cache[nodeName], nodeDef, facedir, param2, neighbors)
|
|
|
|
if meshport.nodebox_cache[nodeName].type ~= "connected" then
|
|
boxes:rotate_by_facedir(facedir)
|
|
end
|
|
|
|
return boxes:to_faces(nodeDef.tiles, pos, facedir)
|
|
end
|
|
|
|
function meshport.create_mesh_node(nodeDef, playerName)
|
|
local meshName = nodeDef.mesh
|
|
|
|
if not meshName then
|
|
return
|
|
end
|
|
|
|
if not meshport.mesh_cache[meshName] then
|
|
-- Get the paths of all .obj meshes.
|
|
if not meshport.obj_paths then
|
|
meshport.obj_paths = meshport.get_asset_paths("models", ".obj")
|
|
end
|
|
|
|
if not meshport.obj_paths[meshName] then
|
|
if string.lower(string.sub(meshName, -4)) ~= ".obj" then
|
|
meshport.print(playerName, "warning", string.format("Mesh %q is not supported.", meshName))
|
|
else
|
|
meshport.print(playerName, "warning", string.format("Mesh %q could not be found.", meshName))
|
|
end
|
|
|
|
-- Cache a blank faces object so the player isn't warned again.
|
|
meshport.mesh_cache[meshName] = meshport.Faces:new()
|
|
else
|
|
meshport.mesh_cache[meshName] = meshport.parse_obj(meshport.obj_paths[meshName])
|
|
end
|
|
end
|
|
|
|
return meshport.mesh_cache[meshName]:copy()
|
|
end
|
|
|
|
function meshport.create_node(idx, area, content, param2, playerName)
|
|
if content[idx] == minetest.CONTENT_AIR or content[idx] == minetest.CONTENT_IGNORE then
|
|
return
|
|
end
|
|
|
|
local nodeDef = meshport.get_def_from_id(content[idx])
|
|
|
|
if not nodeDef.drawtype or nodeDef.drawtype == "airlike" then
|
|
return
|
|
end
|
|
|
|
local nodeDrawtype = meshport.get_aliased_drawtype(nodeDef.drawtype)
|
|
local facedir = meshport.get_facedir(param2[idx], nodeDef.paramtype2)
|
|
local isCubicType = meshport.cube_face_priority[nodeDrawtype] or nodeDrawtype == "nodebox"
|
|
local neighbors, faces
|
|
|
|
if isCubicType then
|
|
neighbors = meshport.get_node_neighbors(content, area, idx)
|
|
end
|
|
|
|
if meshport.cube_face_priority[nodeDrawtype] then
|
|
faces = meshport.create_cube_node(nodeDrawtype, nodeDef.tiles, area:position(idx), facedir, neighbors)
|
|
elseif nodeDrawtype == "nodebox" then
|
|
faces = meshport.create_nodebox_node(
|
|
minetest.get_name_from_content_id(content[idx]), area:position(idx), facedir, param2[idx], neighbors)
|
|
elseif nodeDrawtype == "mesh" then
|
|
faces = meshport.create_mesh_node(nodeDef, playerName)
|
|
end
|
|
|
|
if not faces then
|
|
return
|
|
end
|
|
|
|
if not isCubicType then
|
|
faces:rotate_by_facedir(facedir)
|
|
end
|
|
|
|
faces:apply_tiles(nodeDef)
|
|
return faces
|
|
end
|
|
|
|
function meshport.create_mesh(playerName, p1, p2)
|
|
meshport.print(playerName, "info", "Generating mesh...")
|
|
p1, p2 = vector.sort(p1, p2)
|
|
local vm = minetest.get_voxel_manip()
|
|
|
|
-- Add one node of padding to area so we can read neighbor blocks.
|
|
local vp1, vp2 = vm:read_from_map(vector.subtract(p1, 1), vector.add(p2, 1))
|
|
local content = vm:get_data()
|
|
local param2 = vm:get_param2_data()
|
|
|
|
-- Create a VoxelArea for converting from flat array indices to position vectors.
|
|
local vArea = VoxelArea:new{MinEdge = vp1, MaxEdge = vp2}
|
|
local mesh = meshport.Mesh:new()
|
|
local faces
|
|
|
|
-- Loop through all positions in the desired area.
|
|
for idx in vArea:iterp(p1, p2) do
|
|
-- Generate a mesh for the node.
|
|
faces = meshport.create_node(idx, vArea, content, param2, playerName)
|
|
|
|
if faces then
|
|
-- Move the node to its proper position in the mesh.
|
|
faces:translate(vector.add(vector.subtract(vArea:position(idx), p1), 0.5))
|
|
|
|
for _, face in ipairs(faces.faces) do
|
|
-- Add each face to our final mesh.
|
|
mesh:insert_face(face)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Create path for exported mesh.
|
|
local path = string.format("%s%smeshport%s%s_%s",
|
|
minetest.get_worldpath(), DIR_DELIM, DIR_DELIM, playerName, os.date("%Y-%m-%d_%H-%M-%S"))
|
|
minetest.mkdir(path)
|
|
|
|
mesh:write_obj(path)
|
|
mesh:write_mtl(path, playerName)
|
|
|
|
meshport.print(playerName, "info", "Finished.")
|
|
end
|