EinsDreiDreiSieben/mods/.cozylights/wield_light.lua

262 lines
9.8 KiB
Lua

local c_air = minetest.get_content_id("air")
local c_light1 = minetest.get_content_id("cozylights:light1")
local c_lights = { c_light1, c_light1 + 1, c_light1 + 2, c_light1 + 3, c_light1 + 4, c_light1 + 5, c_light1 + 6,
c_light1 + 7, c_light1 + 8, c_light1 + 9, c_light1 + 10, c_light1 + 11, c_light1 + 12, c_light1 + 13 }
local gent_total = 0
local gent_count = 0
local mf = math.floor
--local cozycids_sunlight_propagates = cozylights.cozycids_sunlight_propagates
local function destroy_stale_wielded_light(data,param2data,a,cozyplayer)
local c_light1 = c_lights[1]
local c_light14 = c_lights[14]
for j,p in ipairs(cozyplayer.prev_wielded_lights) do
if a and a:containsp(p) then
local idx = a:indexp(p)
local cid = data[idx]
if cid >= c_light1 and cid <= c_light14 then
if param2data[idx] > 0 and param2data[idx] <= 14 then
data[idx] = c_light1 + param2data[idx] - 1
else
data[idx] = c_air
end
end
else
local node = minetest.get_node(p)
if string.find(node.name, "cozylights:light") then
if node.param2 == 0 then
minetest.set_node(p,{name="air"})
else
minetest.set_node(p,{name="cozylights:light"..node.param2})
end
end
end
end
cozyplayer.prev_wielded_lights = {}
end
--- Like normal raycast but only covers surfaces, faster for large distances, somewhat less accurate
local function lightcast_lite(pos, dir, dirs, radius,data, param2data, a,dim_levels,cozyplayer)
local px, py, pz, dx, dy, dz = pos.x, pos.y, pos.z, dir.x, dir.y, dir.z
local c_light14 = c_lights[14]
local light_nerf = 0
for i = 1, radius do
local x = mf(dx*i+0.5) + px
local y = mf(dy*i+0.5) + py
local z = mf(dz*i+0.5) + pz
local idx = a:index(x,y,z)
local cid = data[idx]
if cid and (cid == c_air or (cid >= c_light1 and cid <= c_light14)) then
for n = 1, 6 do
local adj_idx = idx+dirs[n]
local adj_cid = data[adj_idx]
if adj_cid and ((adj_cid < c_light1 and adj_cid ~= c_air)or adj_cid > c_light14) then
local dim = (dim_levels[i] - light_nerf) > 0 and (dim_levels[i] - light_nerf) or 1
local light = c_lights[dim]
if light > cid then
data[idx] = light
table.insert(cozyplayer.prev_wielded_lights, {x=x,y=y,z=z})
if cid == c_air and param2data[idx] > 0 then
param2data[idx] = 0
end
end
break
end
end
else
break
end
end
end
function cozylights:wielded_light_cleanup(player,cozyplayer,radius)
local pos = vector.round(player:getpos())
local vm = minetest.get_voxel_manip()
local emin, emax
local last_pos = cozyplayer.last_pos
local distance = vector.distance(pos,last_pos)
if distance < 20 then
local pos1 = {
x=pos.x < last_pos.x and pos.x or last_pos.x,
y=pos.y < last_pos.y and pos.y or last_pos.y,
z=pos.z < last_pos.z and pos.z or last_pos.z,
}
local pos2 = {
x=pos.x > last_pos.x and pos.x or last_pos.x,
y=pos.y > last_pos.y and pos.y or last_pos.y,
z=pos.z > last_pos.z and pos.z or last_pos.z,
}
emin, emax = vm:read_from_map(vector.subtract(pos1, radius+1), vector.add(pos2, radius+1))
else
emin, emax = vm:read_from_map(vector.subtract(pos, radius+1), vector.add(pos, radius+1))
end
local data = vm:get_data()
local a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
local param2data = vm:get_param2_data()
destroy_stale_wielded_light(data,param2data,a,cozyplayer)
cozylights:setVoxelManipData(vm,data,nil,true)
end
local max_wield_light_radius = cozylights.max_wield_light_radius
function cozylights:set_wielded_light_radius(_radius)
max_wield_light_radius = _radius
minetest.settings:set("cozylights_wielded_light_radius",_radius)
cozylights.max_wield_light_radius = _radius
end
--ffi.cdef([[
--typedef struct {float x, y, z;} v3float;
--typedef struct {int16_t x, y, z;} v3;
--typedef struct {uint16_t* data; uint8_t* param2data;} vm_data;
--vm_data l_ttt(
-- v3* sphere_surface, int sphere_surface_length, v3 pos, v3 minp, v3 maxp, uint16_t radius, uint16_t* data, uint8_t* param2data,
-- uint8_t* dim_levels, bool* cozycids_sunlight, int c_air, uint16_t* c_lights
--);
--]])
--local ctest = ffi.load(cozylights.modpath.."/liblight.so")
function cozylights:draw_wielded_light(pos, last_pos, cozy_item,vel,cozyplayer,vm,a,data,param2data,emin,emax)
local t = os.clock()
local update_needed = 0
local radius, dim_levels = cozylights:calc_dims(cozy_item)
radius = radius > max_wield_light_radius and max_wield_light_radius or radius
if radius == 0 then
destroy_stale_wielded_light(data,param2data,a,cozyplayer)
local node = minetest.get_node(pos)
if node.name == "air" or string.match(node.name,"cozylights:") then
local brightness_mod = cozy_item.modifiers ~= nil and cozylights.coziest_table[cozy_item.modifiers].brightness or 0
local max_light = mf(cozy_item.light_source + cozylights.brightness_factor + brightness_mod) > 0 and mf(cozy_item.light_source + cozylights.brightness_factor + brightness_mod) or 0
max_light = max_light > 14 and 14 or max_light
local cid = minetest.get_content_id("cozylights:light"..max_light)
if cid > minetest.get_content_id(node.name) then
minetest.set_node(pos,{name="cozylights:light"..max_light,param2=node.param2})
cozyplayer.prev_wielded_lights[#cozyplayer.prev_wielded_lights+1] = pos
end
else
pos.y = pos.y - 1
local n_name = minetest.get_node(pos).name
if n_name == "air" or string.match(n_name,"cozylights:") then
local brightness_mod = cozy_item.modifiers ~= nil and cozylights.coziest_table[cozy_item.modifiers].brightness or 0
local max_light = mf(cozy_item.light_source + cozylights.brightness_factor + brightness_mod) > 0 and mf(cozy_item.light_source + cozylights.brightness_factor + brightness_mod) or 0
max_light = max_light > 14 and 14 or max_light
local cid = minetest.get_content_id("cozylights:light"..max_light)
if cid > minetest.get_content_id(node.name) then
minetest.set_node(pos,{name="cozylights:light"..max_light,param2=node.param2})
cozyplayer.prev_wielded_lights[#cozyplayer.prev_wielded_lights+1] = pos
end
end
end
return
end
local possible_pos = vector.add(pos,vel)
local node = minetest.get_node(possible_pos)
if node.name == "air" or string.match(node.name, "cozylights:light") then
pos = possible_pos
end
if vm == nil then
vm = minetest.get_voxel_manip()
local distance = vector.distance(pos,last_pos)
if distance < 20 then
local pos1 = {
x=pos.x < last_pos.x and pos.x or last_pos.x,
y=pos.y < last_pos.y and pos.y or last_pos.y,
z=pos.z < last_pos.z and pos.z or last_pos.z,
}
local pos2 = {
x=pos.x > last_pos.x and pos.x or last_pos.x,
y=pos.y > last_pos.y and pos.y or last_pos.y,
z=pos.z > last_pos.z and pos.z or last_pos.z,
}
emin, emax = vm:read_from_map(vector.subtract(pos1, radius+1), vector.add(pos2, radius+1))
else
emin, emax = vm:read_from_map(vector.subtract(pos, radius+1), vector.add(pos, radius+1))
end
data = vm:get_data()
param2data = vm:get_param2_data()
a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
update_needed = 1
end
destroy_stale_wielded_light(data,param2data,a,cozyplayer)
local c_light14 = c_lights[14]
local sphere_surface = cozylights:get_sphere_surface(radius)
local px = pos.x
local py = pos.y
local pz = pos.z
local y_below = py - 1
local y_above = py + 1
local cidb = data[a:index(px,y_below,pz)]
local cida = data[a:index(px,y_above,pz)]
if cidb and cida then
if (cidb == c_air or (cidb >= c_light1 and cidb <= c_light14))
and cida ~= c_air and (cida < c_light1 or cida > c_light14)
then
py = py - 1
end
else
return
end
local zstride, ystride = a.zstride, a.ystride
local dirs = { -1*ystride, 1*ystride,-1,1,-1*zstride,1*zstride}
--[[--cdata experiments, so if we offload heavy lifting on c, it will actually be slower by 20%
--not even a bit faster, so i d rather not continue on this
--because vm:set_data works with lua state and expects lua table,
--and interpreting c types back to lua table seems to be ridiculously expensive to bother
--basically lua is useless and helpless without lua state
minetest.chat_send_all("jit.status() "..cozylights:dump(jit.status()))
local csphere_surface = ffi.new("v3struct["..(#sphere_surface+1).."]", sphere_surface)
local cpos = ffi.new("v3struct", pos)
local cemin = ffi.new("v3struct",emin)
local cemax = ffi.new("v3struct",emax)
local cradius = ffi.new("int",radius)
local testcdata = ffi.new("uint16_t["..(#data).."]")
local cdim_levels = ffi.new("uint16_t["..(#dim_levels+1).."]", dim_levels)
local cc_air = ffi.new("int",c_air)
local cc_lights = ffi.new("uint16_t["..(#c_lights+1).."]", c_lights)
for i = 1, #data do
testcdata[i-1] = ffi.new("uint16_t",data[i])
end
local cparam2data = ffi.new("uint16_t["..#param2data.."]")
for i = 1, #param2data do
cparam2data[i-1] = ffi.new("uint16_t",param2data[i])
end
local ccozycids = ffi.new("bool["..#cozycids_sunlight_propagates.."]",cozycids_sunlight_propagates)
local length = ffi.new("int",#sphere_surface)
local idk = (ctest.l_ttt(csphere_surface,length, cpos,cemin,cemax,cradius,testcdata,cparam2data,cdim_levels,ccozycids,cc_air,cc_lights))
idk = idk.data
if idk ~= nil then
for i=0,#data do
local incoming = tonumber(idk[i])
if data[i+1] ~= incoming then
data[i+1] = incoming
table.insert(cozyplayer.prev_wielded_lights, a:position(i+1))
end
end
end
for i = 1, #param2data do
param2data[i] = tonumber(cparam2data[i-1])
end]]
for i,pos2 in ipairs(sphere_surface) do
lightcast_lite(pos, vector.direction(pos,{x=px+pos2.x,y=py+pos2.y,z=pz+pos2.z}),dirs,radius,data,param2data,a,dim_levels,cozyplayer)
end
if update_needed == 1 then
cozylights:setVoxelManipData(vm,data,param2data,true)
end
cozyplayer.last_wield_radius = radius
gent_total = gent_total + mf((os.clock() - t) * 1000)
gent_count = gent_count + 1
--print("Av wield illum time " .. mf(gent_total/gent_count) .. " ms. Sample of: "..gent_count)
end