preview generation

This commit is contained in:
BuckarooBanzay 2024-10-18 10:22:54 +02:00
parent 8e46bb1fc3
commit 1722e75ee7
7 changed files with 219 additions and 176 deletions

View File

@ -95,13 +95,13 @@ minetest.register_tool("building_lib:autoplace", {
local success, building_name, rotation = building_lib.can_autoplace(mapblock_pos1, playername, autoplacer_name) local success, building_name, rotation = building_lib.can_autoplace(mapblock_pos1, playername, autoplacer_name)
if not success then if not success then
building_lib.clear_preview(playername) building_lib.clear_display(playername)
return return
end end
local building_def = building_lib.get_building(building_name) local building_def = building_lib.get_building(building_name)
if not building_def then if not building_def then
building_lib.clear_preview(playername) building_lib.clear_display(playername)
return return
end end
@ -114,7 +114,7 @@ minetest.register_tool("building_lib:autoplace", {
color = "#ffff00" color = "#ffff00"
end end
building_lib.show_preview( building_lib.show_display(
playername, playername,
"building_lib_autoplace.png", "building_lib_autoplace.png",
color, color,
@ -126,6 +126,6 @@ minetest.register_tool("building_lib:autoplace", {
end, end,
on_deselect = function(_, player) on_deselect = function(_, player)
local playername = player:get_player_name() local playername = player:get_player_name()
building_lib.clear_preview(playername) building_lib.clear_display(playername)
end end
}) })

View File

@ -114,7 +114,7 @@ minetest.register_tool("building_lib:place", {
local building_def, mb_pos1, mb_pos2, rotation = building_lib.get_next_buildable_position(player, buildingname) local building_def, mb_pos1, mb_pos2, rotation = building_lib.get_next_buildable_position(player, buildingname)
if building_def then if building_def then
building_lib.show_preview( building_lib.show_display(
playername, playername,
"building_lib_place.png", "building_lib_place.png",
"#00ff00", "#00ff00",
@ -124,11 +124,11 @@ minetest.register_tool("building_lib:place", {
rotation rotation
) )
else else
building_lib.clear_preview(playername) building_lib.clear_display(playername)
end end
end, end,
on_deselect = function(_, player) on_deselect = function(_, player)
local playername = player:get_player_name() local playername = player:get_player_name()
building_lib.clear_preview(playername) building_lib.clear_display(playername)
end end
}) })

89
display.lua Normal file
View File

@ -0,0 +1,89 @@
-- playername => key
local active_display = {}
function building_lib.show_display(playername, texture, color, building_def, mapblock_pos1, mapblock_pos2, rotation)
texture = texture .. "^[colorize:" .. color
mapblock_pos2 = mapblock_pos2 or mapblock_pos1
local key =
minetest.pos_to_string(mapblock_pos1) .. "/" ..
minetest.pos_to_string(mapblock_pos2) .. "/" ..
texture .. "/" ..
rotation
if active_display[playername] == key then
-- already active on the same region
return
end
-- clear previous entities
building_lib.clear_display(playername)
active_display[playername] = key
local min, _ = mapblock_lib.get_mapblock_bounds_from_mapblock(mapblock_pos1)
local size_mapblocks = vector.subtract(vector.add(mapblock_pos2, 1), mapblock_pos1) -- 1 .. n
local size = vector.multiply(size_mapblocks, 16) -- 16 .. n
local half_size = vector.divide(size, 2) -- 8 .. n
local origin = vector.add(min, half_size)
origin = vector.subtract(origin, 0.5)
local ent = building_lib.add_cube_entity(origin, key)
ent:set_properties({
visual_size = size,
textures = {
texture,
texture,
texture,
texture,
texture,
texture
}
})
if building_def and building_def.markers then
-- add markers
local texture_modifier = "^[colorize:" .. color
local unrotated_size = building_lib.get_building_size(building_def, 360 - rotation)
for _, marker_opts in ipairs(building_def.markers) do
local marker = building_lib.create_marker(marker_opts)
local center_rel_pos = vector.add(marker.position, 0.5)
local rotated_position = mapblock_lib.rotate_pos(center_rel_pos, unrotated_size, rotation)
local node_pos = vector.multiply(vector.add(mapblock_pos1, rotated_position), 16)
node_pos = vector.subtract(node_pos, 0.5)
local z_rotation = marker.rotation.z
if rotation == 90 then
z_rotation = z_rotation - math.pi/2
elseif rotation == 180 then
z_rotation = z_rotation + math.pi
elseif rotation == 270 then
z_rotation = z_rotation + math.pi/2
end
ent = building_lib.add_entity(node_pos, key)
ent:set_properties({
visual_size = marker.size,
textures = {marker.texture .. texture_modifier}
})
ent:set_rotation({
x=marker.rotation.x,
y=marker.rotation.y,
z=z_rotation
})
end
end
end
function building_lib.clear_display(playername)
if active_display[playername] then
building_lib.remove_entities(active_display[playername])
active_display[playername] = nil
end
end
minetest.register_on_leaveplayer(function(player)
building_lib.clear_display(player:get_player_name())
end)

View File

@ -20,12 +20,13 @@ building_lib = {
local MP = minetest.get_modpath("building_lib") local MP = minetest.get_modpath("building_lib")
dofile(MP .. "/memoize.lua") dofile(MP .. "/memoize.lua")
dofile(MP .. "/entity.lua") dofile(MP .. "/entity.lua")
dofile(MP .. "/preview.lua") dofile(MP .. "/display.lua")
dofile(MP .. "/api.lua") dofile(MP .. "/api.lua")
dofile(MP .. "/common.lua") dofile(MP .. "/common.lua")
dofile(MP .. "/markers.lua") dofile(MP .. "/markers.lua")
dofile(MP .. "/placements/mapblock_lib.lua") dofile(MP .. "/placements/mapblock_lib.lua")
dofile(MP .. "/placements/dummy.lua") dofile(MP .. "/placements/dummy.lua")
dofile(MP .. "/preview.lua")
dofile(MP .. "/conditions.lua") dofile(MP .. "/conditions.lua")
dofile(MP .. "/build.lua") dofile(MP .. "/build.lua")
dofile(MP .. "/build_tool.lua") dofile(MP .. "/build_tool.lua")
@ -39,11 +40,6 @@ dofile(MP .. "/events.lua")
dofile(MP .. "/hacks.lua") dofile(MP .. "/hacks.lua")
dofile(MP .. "/mapgen.lua") dofile(MP .. "/mapgen.lua")
local ie = minetest.request_insecure_environment()
if ie and minetest.get_modpath("isogen") then
loadfile(MP .. "/previewgen.lua")(ie)
end
if minetest.get_modpath("mtt") and mtt.enabled then if minetest.get_modpath("mtt") and mtt.enabled then
dofile(MP .. "/events.spec.lua") dofile(MP .. "/events.spec.lua")
dofile(MP .. "/conditions.spec.lua") dofile(MP .. "/conditions.spec.lua")

View File

@ -1,89 +1,123 @@
local cube_len = 8
-- playername => key -- name -> { png, width, height, timestamp }
local active_preview = {} local previews = {}
function building_lib.show_preview(playername, texture, color, building_def, mapblock_pos1, mapblock_pos2, rotation) function building_lib.generate_building_preview(building_def)
texture = texture .. "^[colorize:" .. color local catalog
local offset = {x=0, y=0, z=0}
local size
mapblock_pos2 = mapblock_pos2 or mapblock_pos1 if type(building_def.catalog) == "table" then
local key = catalog = mapblock_lib.get_catalog(building_def.catalog.filename)
minetest.pos_to_string(mapblock_pos1) .. "/" .. offset = building_def.catalog.offset or {x=0, y=0, z=0}
minetest.pos_to_string(mapblock_pos2) .. "/" .. size = building_def.catalog.size or {x=1, y=1, z=1}
texture .. "/" .. else
rotation catalog = mapblock_lib.get_catalog(building_def.catalog)
size = catalog:get_size()
end
if active_preview[playername] == key then local mb_pos2 = vector.add(offset, vector.subtract(size, 1))
-- already active on the same region
return
end
-- clear previous entities
building_lib.clear_preview(playername)
active_preview[playername] = key
local min, _ = mapblock_lib.get_mapblock_bounds_from_mapblock(mapblock_pos1) local min = mapblock_lib.get_mapblock_bounds_from_mapblock(offset)
local _, max = mapblock_lib.get_mapblock_bounds_from_mapblock(mb_pos2)
local size_mapblocks = vector.subtract(vector.add(mapblock_pos2, 1), mapblock_pos1) -- 1 .. n local png = isogen.draw(min, max, {
local size = vector.multiply(size_mapblocks, 16) -- 16 .. n cube_len = cube_len,
local half_size = vector.divide(size, 2) -- 8 .. n get_node = function(pos)
return catalog:get_node(pos)
end
})
local origin = vector.add(min, half_size) local node_size = vector.add(vector.subtract(max, min), 1)
origin = vector.subtract(origin, 0.5) local width, height = isogen.calculate_image_size(node_size, cube_len)
local ent = building_lib.add_cube_entity(origin, key) return {
ent:set_properties({ png = minetest.encode_base64(png),
visual_size = size, width = width,
textures = { height = height,
texture, timestamp = os.time()
texture, }
texture,
texture,
texture,
texture
}
})
if building_def and building_def.markers then
-- add markers
local texture_modifier = "^[colorize:" .. color
local unrotated_size = building_lib.get_building_size(building_def, 360 - rotation)
for _, marker_opts in ipairs(building_def.markers) do
local marker = building_lib.create_marker(marker_opts)
local center_rel_pos = vector.add(marker.position, 0.5)
local rotated_position = mapblock_lib.rotate_pos(center_rel_pos, unrotated_size, rotation)
local node_pos = vector.multiply(vector.add(mapblock_pos1, rotated_position), 16)
node_pos = vector.subtract(node_pos, 0.5)
local z_rotation = marker.rotation.z
if rotation == 90 then
z_rotation = z_rotation - math.pi/2
elseif rotation == 180 then
z_rotation = z_rotation + math.pi
elseif rotation == 270 then
z_rotation = z_rotation + math.pi/2
end
ent = building_lib.add_entity(node_pos, key)
ent:set_properties({
visual_size = marker.size,
textures = {marker.texture .. texture_modifier}
})
ent:set_rotation({
x=marker.rotation.x,
y=marker.rotation.y,
z=z_rotation
})
end
end
end end
function building_lib.clear_preview(playername) -- preview file in the world folder
if active_preview[playername] then local preview_filename = minetest.get_worldpath() .. "/building_preview.json"
building_lib.remove_entities(active_preview[playername])
active_preview[playername] = nil minetest.register_chatcommand("building_previewgen", {
end params = "[modname]",
privs = {
mapblock_lib = true
},
func = function(name, modname)
local world_previews = {}
local f = io.open(preview_filename, "rb")
if f then
-- read previous previews
local json = f:read("*all")
f:close()
world_previews = minetest.parse_json(json)
end
local buildings = building_lib.get_buildings()
local list = {}
for _, building in pairs(buildings) do
if building.modname == modname and building.placement == "mapblock_lib" then
table.insert(list, building)
end
end
if #list == 0 then
return false, "no buildings found with given modname"
end
local count = 0
local worker
worker = function()
local building = table.remove(list)
if not building then
minetest.chat_send_player(name, "Done generating " .. count .. " previews")
minetest.safe_file_write(preview_filename, minetest.write_json(world_previews))
return
end
minetest.chat_send_player(name, "Generating preview for '" .. building.name .. "'")
count = count + 1
local data, err = building_lib.generate_building_preview(building)
if not data then
minetest.chat_send_player(
name,
"Preview generation failed for building: '" .. building.name .. "', error: " .. err
)
else
world_previews[building.name] = data
minetest.after(0, worker)
end
end
minetest.after(0, worker)
return true, "Scheduled " .. #list .. " buildings for preview-generation"
end
})
-- returns a cached or ad-hoc generated preview table
function building_lib.get_building_preview(building_name)
if previews[building_name] then
return previews[building_name]
end
local building_def = building_lib.get_building(building_name)
if not building_def then
return false, "building not found: '" .. building_name .. "'"
end
local preview, err = building_lib.generate_building_preview(building_def)
if err then
return false, "generate preview error: " .. err
end
previews[building_name] = preview
return preview
end end
minetest.register_on_leaveplayer(function(player) -- TODO: generate all previews on startup
building_lib.clear_preview(player:get_player_name())
end)

View File

@ -1,82 +0,0 @@
local ie = ...
local function generate_preview(building_def)
local catalog
local offset = {x=0, y=0, z=0}
local size
if type(building_def.catalog) == "table" then
catalog = mapblock_lib.get_catalog(building_def.catalog.filename)
offset = building_def.catalog.offset or {x=0, y=0, z=0}
size = building_def.catalog.size or {x=1, y=1, z=1}
else
catalog = mapblock_lib.get_catalog(building_def.catalog)
size = catalog:get_size()
end
local mb_pos2 = vector.add(offset, vector.subtract(size, 1))
local min = mapblock_lib.get_mapblock_bounds_from_mapblock(offset)
local _, max = mapblock_lib.get_mapblock_bounds_from_mapblock(mb_pos2)
local png = isogen.draw(min, max, {
cube_len = 8,
get_node = function(pos)
return catalog:get_node(pos)
end
})
local filename = building_lib.get_preview_filename(building_def)
local f = ie.io.open(filename, "wb")
if not f then
return false, "could not open file: '" .. filename .. "'"
end
f:write(png)
f:close()
return true
end
minetest.register_chatcommand("building_previewgen", {
params = "[modname]",
privs = {
mapblock_lib = true
},
func = function(name, modname)
local buildings = building_lib.get_buildings()
local list = {}
for _, building in pairs(buildings) do
if building.modname == modname and building.placement == "mapblock_lib" then
table.insert(list, building)
end
end
if #list == 0 then
return false, "no buildings found with given modname"
end
local worker
worker = function()
local building = table.remove(list)
if not building then
minetest.chat_send_player(name, "Done generating previews")
return
end
minetest.chat_send_player(name, "Generating preview for '" .. building.name .. "'")
local success, err = generate_preview(building)
if not success then
minetest.chat_send_player(
name,
"Preview generation failed for building: '" .. building.name .. "', error: " .. err
)
else
minetest.after(0, worker)
end
end
minetest.after(0, worker)
return true, "Scheduled " .. #list .. " buildings for preview-generation"
end
})

View File

@ -5,18 +5,24 @@ minetest.register_tool("building_lib:remove", {
stack_max = 1, stack_max = 1,
range = 0, range = 0,
on_use = function(_, player) on_use = function(_, player)
local playername = player:get_player_name()
local _, mb_pos1 = building_lib.get_next_removable_position(player) local _, mb_pos1 = building_lib.get_next_removable_position(player)
if not mb_pos1 then
minetest.chat_send_player(playername, "nothing to remove found")
return
end
local success, err = building_lib.remove(mb_pos1) local success, err = building_lib.remove(mb_pos1)
if not success then if not success then
minetest.chat_send_player(player:get_player_name(), err) minetest.chat_send_player(playername, err)
end end
end, end,
on_step = function(_, player) on_step = function(_, player)
local playername = player:get_player_name() local playername = player:get_player_name()
local building_def, mb_pos1, mb_pos2, rotation = building_lib.get_next_removable_position(player) local building_def, mb_pos1, mb_pos2, rotation = building_lib.get_next_removable_position(player)
if building_def then if building_def then
building_lib.show_preview( building_lib.show_display(
playername, playername,
"building_lib_remove.png", "building_lib_remove.png",
"#ff0000", "#ff0000",
@ -26,12 +32,12 @@ minetest.register_tool("building_lib:remove", {
rotation rotation
) )
else else
building_lib.clear_preview(playername) building_lib.clear_display(playername)
end end
end, end,
on_deselect = function(_, player) on_deselect = function(_, player)
local playername = player:get_player_name() local playername = player:get_player_name()
building_lib.clear_preview(playername) building_lib.clear_display(playername)
end end
}) })