262 lines
9.8 KiB
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
|
|
|