meshport/export.lua
2019-06-27 21:56:35 -07:00

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