280 lines
8.3 KiB
Lua
280 lines
8.3 KiB
Lua
--[===================================================================[--
|
|
ffi_accel - Mod for Minetest to accelerate handling of LuaVoxelManip
|
|
and PerlinNoiseMap through the foreign function interface
|
|
|
|
Copyright © 2019 Pedro Gimeno Fortea. All rights reserved.
|
|
|
|
Permission is hereby granted to everyone to copy, modify, distribute
|
|
and use this file, for any purpose, in whole or in part, free of
|
|
charge, under the sole condition that the above copyright notice,
|
|
together with this permission grant and the disclaimer below, are
|
|
included in all copies of this software or of a substantial portion
|
|
of it.
|
|
|
|
THIS SOFTWARE COMES WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED.
|
|
--]===================================================================]--
|
|
|
|
-- Based on an idea of EvidenceBKidsCode.
|
|
-- This mod needs to apply a patch to the engine in order to work; see
|
|
--
|
|
|
|
local modname = "Mod ffi_accel"
|
|
|
|
local err_prefix = modname .. " won't have any effect because "
|
|
|
|
local ie = minetest.request_insecure_environment()
|
|
if not ie then
|
|
minetest.log("warning", err_prefix .. "it's not declared in secure.trusted_mods")
|
|
return
|
|
end
|
|
|
|
local ok, ffi = pcall(ie.require, 'ffi')
|
|
if not ok or not ffi then
|
|
minetest.log("warning", err_prefix .. "FFI is not available in this system")
|
|
return
|
|
end
|
|
|
|
ffi.cdef[[
|
|
typedef uint8_t u8;
|
|
typedef uint16_t u16;
|
|
typedef int32_t s32;
|
|
typedef struct {
|
|
u16 nodeid;
|
|
u8 light;
|
|
u8 param2;
|
|
} MapNode;
|
|
|
|
int get_mapnode_version(void);
|
|
void VoxelManip_get_pointer(void **lvmp, void **ptr);
|
|
s32 VoxelManip_get_volume(void **lvmp);
|
|
void PerlinNoiseMap_get_pointer(void **pnmp, void **ptr);
|
|
size_t PerlinNoiseMap_get_area(void **pnmp);
|
|
size_t PerlinNoiseMap_get_volume(void **pnmp);
|
|
]]
|
|
|
|
local ffiC = ffi.C
|
|
local ffinew = ffi.new
|
|
local VoidPtrPtr = ffi.typeof('void*[1]')
|
|
local MapNodePtr = ffi.typeof('MapNode *')
|
|
|
|
local mt_set_data, mt_set_light_data, mt_set_param2_data
|
|
|
|
if not pcall(function ()
|
|
assert(ffiC.get_mapnode_version and ffiC.VoxelManip_get_pointer
|
|
and ffiC.VoxelManip_get_volume and ffiC.PerlinNoiseMap_get_pointer)
|
|
end)
|
|
then
|
|
minetest.log("warning", err_prefix .. "your Minetest executable has not been patched to support FFI")
|
|
return
|
|
end
|
|
|
|
if ffiC.get_mapnode_version() ~= 1 then
|
|
minetest.log("warning", err_prefix .. "of incompatible map node structure")
|
|
return
|
|
end
|
|
|
|
|
|
-- Table of pointers (keys are weak refs)
|
|
local ptrtable = setmetatable({}, {__mode = 'k'})
|
|
-- Table of sizes (keys are weak refs)
|
|
local sizetable = setmetatable({}, {__mode = 'k'})
|
|
|
|
|
|
-- Metamethod: get a value
|
|
local function mm_get(t, idx)
|
|
return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1] or rawget(t, idx)
|
|
end
|
|
|
|
|
|
-- Metamethod: set a value
|
|
local function mm_set(t, idx, value)
|
|
if idx >= 1 and idx <= sizetable[t] then
|
|
ptrtable[t][idx - 1] = value
|
|
else
|
|
rawset(t, idx, value)
|
|
end
|
|
end
|
|
|
|
-- Metamethod: get the nodeid of an index
|
|
local function mm_get_nodeid(t, idx)
|
|
return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].nodeid or rawget(t, idx)
|
|
end
|
|
|
|
-- Metamethod: set the nodeid of an index to a value
|
|
local function mm_set_nodeid(t, idx, value)
|
|
if idx >= 1 and idx <= sizetable[t] then
|
|
ptrtable[t][idx - 1].nodeid = value
|
|
else
|
|
rawset(t, idx, value)
|
|
end
|
|
end
|
|
|
|
-- Metamethod: get the light of an index
|
|
local function mm_get_light(t, idx)
|
|
return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].light or rawget(t, idx)
|
|
end
|
|
|
|
-- Metamethod: set the light of an index to a value
|
|
local function mm_set_light(t, idx, value)
|
|
if idx >= 1 and idx <= sizetable[t] then
|
|
ptrtable[t][idx - 1].light = value
|
|
else
|
|
rawset(t, idx, value)
|
|
end
|
|
end
|
|
|
|
-- Metamethod: get the param2 of an index
|
|
local function mm_get_param2(t, idx)
|
|
return idx >= 1 and idx <= sizetable[t] and ptrtable[t][idx - 1].param2 or rawget(t, idx)
|
|
end
|
|
|
|
-- Metamethod: set the param2 of an index to a value
|
|
local function mm_set_param2(t, idx, value)
|
|
if idx >= 1 and idx <= sizetable[t] then
|
|
ptrtable[t][idx - 1].param2 = value
|
|
else
|
|
rawset(t, idx, value)
|
|
end
|
|
end
|
|
|
|
|
|
-- Metatables for VoxelManip nodeid, light and param2
|
|
local VoxelManip_nodeid_mt = { __index = mm_get_nodeid, __newindex = mm_set_nodeid }
|
|
local VoxelManip_light_mt = { __index = mm_get_light, __newindex = mm_set_light }
|
|
local VoxelManip_param2_mt = { __index = mm_get_param2, __newindex = mm_set_param2 }
|
|
|
|
|
|
-- Replacement VoxelManip.get_data and VoxelManip.set_data methods
|
|
local function VoxelManip_get_data(self, buffer)
|
|
-- Set up the metatable and pointer
|
|
buffer = setmetatable(buffer or {}, VoxelManip_nodeid_mt)
|
|
do
|
|
local ptr = VoidPtrPtr()
|
|
ffiC.VoxelManip_get_pointer(self, ptr)
|
|
ptrtable[buffer] = MapNodePtr(ptr[0])
|
|
end
|
|
sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
|
|
return buffer
|
|
end
|
|
|
|
local function VoxelManip_set_data(self, buffer)
|
|
-- Call the original set_data method if the metatable is not
|
|
-- the one we've set
|
|
if getmetatable(buffer) ~= VoxelManip_nodeid_mt then
|
|
mt_set_data(self, buffer)
|
|
end
|
|
end
|
|
|
|
|
|
-- Replacement VoxelManip.get/set_light_data methods
|
|
local function VoxelManip_get_light_data(self, buffer)
|
|
-- Set up the metatable and pointer
|
|
buffer = setmetatable(buffer or {}, VoxelManip_light_mt)
|
|
do
|
|
local ptr = VoidPtrPtr()
|
|
ffiC.VoxelManip_get_pointer(self, ptr)
|
|
ptrtable[buffer] = MapNodePtr(ptr[0])
|
|
end
|
|
sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
|
|
return buffer
|
|
end
|
|
|
|
local function VoxelManip_set_light_data(self, buffer)
|
|
-- Call the original set_light_data method if the metatable is not
|
|
-- the one we've set
|
|
if getmetatable(buffer) ~= VoxelManip_light_mt then
|
|
mt_set_light_data(self, buffer)
|
|
end
|
|
end
|
|
|
|
|
|
-- Replacement VoxelManip.get/set_param2_data methods
|
|
local function VoxelManip_get_param2_data(self, buffer)
|
|
-- Set up the metatable and pointer
|
|
buffer = setmetatable(buffer or {}, VoxelManip_param2_mt)
|
|
do
|
|
local ptr = VoidPtrPtr()
|
|
ffiC.VoxelManip_get_pointer(self, ptr)
|
|
ptrtable[buffer] = MapNodePtr(ptr[0])
|
|
end
|
|
sizetable[buffer] = ffiC.VoxelManip_get_volume(self)
|
|
return buffer
|
|
end
|
|
|
|
local function VoxelManip_set_param2_data(self, buffer)
|
|
-- Call the original set_param2_data method if the metatable is not
|
|
-- the one we've set
|
|
if getmetatable(buffer) ~= VoxelManip_param2_mt then
|
|
mt_set_param2_data(self, buffer)
|
|
end
|
|
end
|
|
|
|
|
|
-- Metatable for PerlinNoiseMap
|
|
local PerlinNoiseMap_mt = { __index = mm_get }
|
|
|
|
-- Replacement methods for PerlinNoiseMap.*
|
|
local function PerlinNoiseMap_get_2d_map_flat(self, pos, buffer)
|
|
-- Calculate the noise
|
|
self:calc_2d_map(pos)
|
|
|
|
-- Prepare the metatable for direct access to the data
|
|
buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
|
|
do
|
|
local ptr = VoidPtrPtr()
|
|
ffiC.PerlinNoiseMap_get_pointer(self, ptr)
|
|
ptrtable[buffer] = MapNodePtr(ptr[0])
|
|
end
|
|
sizetable[buffer] = ffiC.PerlinNoiseMap_get_area(self)
|
|
return buffer
|
|
end
|
|
|
|
local function PerlinNoiseMap_get_3d_map_flat(self, pos, buffer)
|
|
-- Calculate the noise
|
|
self:calc_3d_map(pos)
|
|
|
|
-- Prepare the metatable for direct access to the data
|
|
buffer = setmetatable(buffer or {}, PerlinNoiseMap_mt)
|
|
do
|
|
local ptr = VoidPtrPtr()
|
|
ffiC.PerlinNoiseMap_get_pointer(self, ptr)
|
|
ptrtable[buffer] = MapNodePtr(ptr[0])
|
|
end
|
|
sizetable[buffer] = ffiC.PerlinNoiseMap_get_volume(self)
|
|
return buffer
|
|
end
|
|
|
|
--[[
|
|
local function PerlinNoiseMap_get_map_slice(slice_offset, slice_size, buffer)
|
|
-- TODO
|
|
end
|
|
--]]
|
|
|
|
-- Monkey-patch VoxelManip and PerlinNoiseMap
|
|
minetest.after(0, function()
|
|
-- VoxelManip buffer-intensive methods
|
|
local mt = getmetatable(minetest.get_voxel_manip())
|
|
mt.get_data = VoxelManip_get_data
|
|
mt_set_data = mt.set_data
|
|
mt.set_data = VoxelManip_set_data
|
|
mt.get_light_data = VoxelManip_get_light_data
|
|
mt_set_light_data = mt.set_light_data
|
|
mt.set_light_data = VoxelManip_set_light_data
|
|
mt.get_param2_data = VoxelManip_get_param2_data
|
|
mt_set_param2_data = mt.set_param2_data
|
|
mt.set_param2_data = VoxelManip_set_param2_data
|
|
|
|
-- PerlinNoiseMap buffer-intensive methods
|
|
mt = getmetatable(minetest.get_perlin_map({spread = {x=100, y=100, z=100}}, {x=1, y=1, z=1}))
|
|
mt.get_2d_map_flat = PerlinNoiseMap_get_2d_map_flat
|
|
mt.get_3d_map_flat = PerlinNoiseMap_get_3d_map_flat
|
|
-- mt.get_map_slice = PerlinNoiseMap_get_map_slice
|
|
|
|
-- Aliases
|
|
mt.get2DMap_flat = mt.get_2d_map_flat
|
|
mt.get3DMap_flat = mt.get_3d_map_flat
|
|
-- mt.getMapSlice = mt.get_map_slice
|
|
end)
|
|
|
|
minetest.log("action", modname .. " activated!")
|