445 lines
13 KiB
Lua
445 lines
13 KiB
Lua
local growwallActive=false
|
|
local growwalkActive=false
|
|
local wallMaxHeight=10
|
|
local wallAutoExtend=false
|
|
local markerNodeType="wool:red"
|
|
local outputNodeType="default:sandstonebrick"
|
|
|
|
-- Import section
|
|
local importScale=1 -- upscaling hasn't been properly implemented- all it will do is create gaps between the blocks, and unless they are multiples of 2 the gaps will be relatively irregular
|
|
local importOffsetX=1500
|
|
local importOffsetY=35
|
|
local importOffsetZ=0
|
|
|
|
local bitmapFileName="/home/david/.minetest/mods/growwall/hogwarts.bmp"
|
|
local binvoxFileName="/home/david/.minetest/mods/binvox/Hogwarts_hollow_256grid.binvox"
|
|
|
|
local binvoxGridSize=256
|
|
|
|
-- binvox can be downloaded from:-
|
|
-- http://www.cs.princeton.edu/~min/binvox/
|
|
-- Instructions for using it are here:-
|
|
-- http://minecraft.gamepedia.com/Programs_and_editors/Binvox
|
|
|
|
-- to import binvox files (ie 3D meshes converted to voxels/nodes which can be directly imported into minetest use the following command line (for linux):-
|
|
--cd '/home/david/.minetest/mods/binvox'
|
|
--./binvox -ri Hogwarts.obj -- this converts a mesh to voxels using default 256x256x256 grid, and removes internal voxels (ie keeps objects hollow)
|
|
--./binvox -pb -d 2048 -ri Hogwarts.obj -- this converts a mesh to voxels using use offscreen pbuffer which enables 4096x4096x4096 grid, and keeps it hollow
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Don't edit these
|
|
local voxManip -- handle to a voxelmanipulator
|
|
local oldPos = {x=0,y=0,z=0} -- players most recent position
|
|
|
|
|
|
minetest.register_chatcommand("/growwall", {
|
|
params = "",
|
|
description = "Activate growwall",
|
|
func = function(name, param)
|
|
growwallActive = not growwallActive
|
|
if growwallActive == true then
|
|
minetest.chat_send_all("growwallActive=true")
|
|
else
|
|
minetest.chat_send_all("growwallActive=false")
|
|
end
|
|
|
|
-- possibly add params
|
|
return true
|
|
end,
|
|
})
|
|
|
|
minetest.register_on_punchnode(
|
|
function(pos, node, puncher)
|
|
if growwallActive == true then
|
|
voxManip = minetest.get_voxel_manip()
|
|
if node.name ~= markerNodeType then
|
|
growblock(pos)
|
|
else
|
|
checkSurround(pos)
|
|
end
|
|
voxManip = nil
|
|
end
|
|
end
|
|
)
|
|
|
|
|
|
minetest.register_chatcommand("/growbmp", {
|
|
params = "",
|
|
description = "Load a bmp and place it in the minetest world",
|
|
func = function(name, param)
|
|
|
|
local f = assert(io.open(bitmapFileName, "rb"));
|
|
local bmp = f:read("*a");
|
|
f:close();
|
|
|
|
voxManip = minetest.get_voxel_manip()
|
|
DrawBitmap(bmp);
|
|
voxManip = nil
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_chatcommand("/growbnv", {
|
|
params = "",
|
|
description = "Load a binvox file and place it in the minetest world",
|
|
func = function(name, param)
|
|
voxManip = minetest.get_voxel_manip()
|
|
readBinvox()
|
|
voxManip = nil
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_chatcommand("/growwalk", {
|
|
params = "",
|
|
description = "Activate growwalk",
|
|
func = function(name, param)
|
|
growwalkActive = not growwalkActive
|
|
if growwalkActive == true then
|
|
local player = minetest.get_player_by_name("singleplayer")
|
|
local oldPos = player:getpos()
|
|
oldPos = {x=(math.floor(oldPos.x+0.5)), y=(math.floor(oldPos.y+0.5)), z=(math.floor(oldPos.z+0.5))}
|
|
|
|
minetest.chat_send_all("growwalkActive=true")
|
|
voxManip = minetest.get_voxel_manip()
|
|
else
|
|
minetest.chat_send_all("growwalkActive=false")
|
|
voxManip = nil
|
|
end
|
|
return true
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
if growwalkActive == true then
|
|
local player = minetest.get_player_by_name("singleplayer")
|
|
local pos = player:getpos()
|
|
pos = {x=(math.floor(pos.x+0.5)), y=(math.floor(pos.y+0.5)), z=(math.floor(pos.z+0.5))}
|
|
|
|
if pos.x~=oldPos.x or pos.z~=oldPos.z then
|
|
growblock(oldPos)
|
|
oldPos = pos
|
|
end
|
|
end
|
|
end)
|
|
|
|
|
|
|
|
------------------------------
|
|
-- Simple growth functions
|
|
------------------------------
|
|
|
|
|
|
function checkSurround(pos)
|
|
for dy=-3,3 do
|
|
for dx=-1,1 do
|
|
for dz=-1,1 do
|
|
local p = {x=pos.x+dx, y=pos.y+dy, z=pos.z+dz}
|
|
voxManip:read_from_map(p, p)
|
|
if (minetest.get_node(p).name == markerNodeType) then
|
|
-- local p1 = {x=pos.x+dx, y=pos.y+1, z=pos.z+dz}
|
|
-- voxManip:read_from_map(p1, p1)
|
|
-- if (minetest.get_node(p1).name == "air") then
|
|
-- print(p.x..",".. p.y..",".. p.z)
|
|
growblock(p)
|
|
checkSurround(p)
|
|
-- end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function growblock(pos)
|
|
print(pos.x..",".. pos.y..",".. pos.z)
|
|
for dy=0,wallMaxHeight do
|
|
local p = {x=pos.x, y=math.floor(pos.y+dy), z=pos.z}
|
|
voxManip:read_from_map(p, p)
|
|
|
|
-- place only replaces air and water
|
|
--minetest.place_node(p, {name=outputNodeType})
|
|
minetest.set_node(p, {name=outputNodeType})
|
|
end
|
|
end
|
|
|
|
|
|
|
|
------------------------------
|
|
-- BITMAP management functions
|
|
------------------------------
|
|
|
|
function error(err)
|
|
-- Replace with your own error output method:
|
|
minetest.chat_send_all(err)
|
|
print("ERROR growwall:".. err)
|
|
end
|
|
|
|
-- Helper function: Parse a 16-bit WORD from the binary string
|
|
function ReadWORD(str, offset)
|
|
local loByte = str:byte(offset);
|
|
local hiByte = str:byte(offset+1);
|
|
return hiByte*256 + loByte;
|
|
end
|
|
|
|
-- Helper function: Parse a 32-bit DWORD from the binary string
|
|
function ReadDWORD(str, offset)
|
|
local loWord = ReadWORD(str, offset);
|
|
local hiWord = ReadWORD(str, offset+2);
|
|
return hiWord*65536 + loWord;
|
|
end
|
|
|
|
-- Process a bitmap file in a string, and call DrawPoint for each pixel
|
|
function DrawBitmap(bytecode)
|
|
|
|
if 1==0 then
|
|
-- This code could easily be used to modify the placement of the image
|
|
local player = minetest.get_player_by_name(name)
|
|
local pos = player:getpos()
|
|
pos.x, pos.y, pos.z = math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)
|
|
|
|
|
|
|
|
for x = pos.x-1, pos.x+1 do
|
|
for z = pos.z-1, pos.z+1 do
|
|
minetest.place_node({x=x, y=pos.y, z=z}, {name="displayallnodes:Background"})
|
|
minetest.place_node({x=x, y=pos.y+1, z=z}, {name="displayallnodes:Background"})
|
|
end
|
|
end
|
|
minetest.remove_node(pos)
|
|
minetest.place_node(pos, {name="displayallnodes:White"})
|
|
meta = minetest.get_meta(pos)
|
|
meta:set_string("nodeCount", 0);
|
|
|
|
player:setpos({x=pos.x, y=pos.y+1, z=pos.z})
|
|
player:set_look_yaw(0)
|
|
player:get_look_pitch(0)
|
|
|
|
local nodeMax = 0
|
|
-- key is node name, eg default:stone value is definition table
|
|
for key, value in pairs(minetest.registered_nodes) do
|
|
nodeMax = nodeMax + 1
|
|
end
|
|
minetest.chat_send_all("placing".. nodeMax.. " nodes")
|
|
|
|
meta:set_string("nodeMax", nodeMax);
|
|
end
|
|
|
|
-------------------------
|
|
-- Parse BITMAPFILEHEADER
|
|
-------------------------
|
|
minetest.chat_send_all("Parse BITMAPFILEHEADER")
|
|
|
|
local offset = 1;
|
|
local bfType = ReadWORD(bytecode, offset); -- 2 bytes the header field used to identify the BMP & DIB file
|
|
if(bfType ~= 0x4D42) then
|
|
error("Not a bitmap file (Invalid BMP magic value)");
|
|
return;
|
|
end
|
|
local bfOffBits = ReadDWORD(bytecode, offset+10); -- 4 bytes the offset, i.e. starting address, of the byte where the bitmap image data (pixel array) can be found.
|
|
|
|
-------------------------
|
|
-- Parse BITMAPINFOHEADER
|
|
-------------------------
|
|
minetest.chat_send_all("Parse BITMAPINFOHEADER")
|
|
|
|
offset = 15; -- BITMAPFILEHEADER is 14 bytes long
|
|
local biWidth = ReadDWORD(bytecode, offset+4); -- 4 bytes the bitmap width in pixels (signed integer)
|
|
local biHeight = ReadDWORD(bytecode, offset+8); -- 4 bytes the bitmap height in pixels (signed integer)
|
|
local biBitCount = ReadWORD(bytecode, offset+14); -- 2 bytes the number of bits per pixel, which is the color depth of the image.
|
|
local biCompression = ReadDWORD(bytecode, offset+16); -- 4 bytes the compression method being used.
|
|
if(biBitCount ~= 24) then
|
|
error("Only 24-bit bitmaps supported (Is " .. biBitCount .. "bpp)");
|
|
return;
|
|
end
|
|
if(biCompression ~= 0) then
|
|
error("Only uncompressed bitmaps supported (Compression type is " .. biCompression .. ")");
|
|
return;
|
|
end
|
|
|
|
---------------------
|
|
-- Parse bitmap image
|
|
---------------------
|
|
|
|
local player = minetest.get_player_by_name("singleplayer")
|
|
nodeCount = 0
|
|
|
|
--The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding, which
|
|
-- must be appended to the end of the rows
|
|
biRowLength = biWidth*biBitCount/8
|
|
biRowLengthPadded = (math.floor((biRowLength/4)-0.00000001)+1)*4
|
|
|
|
--Normally pixels are stored "upside-down" with respect to normal image raster scan order,
|
|
-- starting in the lower left corner, going from left to right, and then row by row from the bottom to the top of the image
|
|
for y = biHeight-1, 0, -1 do
|
|
offset = bfOffBits + 1 + biRowLengthPadded*y;
|
|
for x = 0, biWidth-1 do
|
|
local b = bytecode:byte(offset);
|
|
local g = bytecode:byte(offset+1);
|
|
local r = bytecode:byte(offset+2);
|
|
offset = offset + 3;
|
|
|
|
local wallHeight = math.floor((255 - r)/10) -- use r channel of RGB for wall height
|
|
if wallHeight>0 then
|
|
if (nodeCount % 1000) == 0 then
|
|
minetest.chat_send_all("placed ".. nodeCount.. " pixels/nodes")
|
|
end
|
|
nodeCount = nodeCount+1
|
|
for height = 0, wallHeight do
|
|
local p = {x=((x*importScale)+importOffsetX), y=((height*importScale)+importOffsetY), z=((y*importScale)+importOffsetZ)}
|
|
voxManip:read_from_map(p, p)
|
|
|
|
minetest.set_node(p, {name=outputNodeType})
|
|
-- end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
minetest.chat_send_all("completed placing nodes")
|
|
end
|
|
|
|
|
|
-- Process a binvox file, and create nodes
|
|
-- chatcommand("/growbnv"
|
|
function readBinvox()
|
|
|
|
-- local voxels
|
|
local dimX, dimY, dimZ = 0,0,0
|
|
local size
|
|
local tx, ty, tz
|
|
local scale
|
|
|
|
io.input(io.open(binvoxFileName, "rb"))
|
|
|
|
|
|
--
|
|
-- read header
|
|
--
|
|
line = io.read("*line")
|
|
if line == nil then
|
|
minetest.chat_send_all("Error: File ".. binvoxFileName.. " not found")
|
|
return false;
|
|
end
|
|
if line:find("#binvox") == nil then
|
|
minetest.chat_send_all("Error: first line reads [".. line.. "] instead of [#binvox]")
|
|
return false;
|
|
end
|
|
print(line)
|
|
|
|
-- version_string = line.substring(8);
|
|
-- version = Integer.parseInt(version_string);
|
|
-- print("reading binvox version " + version);
|
|
|
|
local done = false
|
|
while (done==false) do
|
|
|
|
line = io.read("*line")
|
|
print(line)
|
|
|
|
if line:find("data") ~= nil then
|
|
done = true;
|
|
else
|
|
if line:find("dim") ~= nil then
|
|
-- local dimensions = {}
|
|
-- for dimension in line:gmatch("%S+") do table.insert(dimensions, dimension) end
|
|
dimX = binvoxGridSize --tonumber(dimensions[1])
|
|
dimY = binvoxGridSize --tonumber(dimensions[2])
|
|
dimZ = binvoxGridSize --tonumber(dimensions[3])
|
|
print("binvox dimensions: X:".. tostring(dimX).. ", Y:".. tostring(dimY).. ", Z:".. tostring(dimZ));
|
|
minetest.chat_send_all("binvox dimensions: X:".. tostring(dimX).. ", Y:".. tostring(dimY).. ", Z:".. tostring(dimZ));
|
|
else
|
|
if line:find("translate") ~= nil then
|
|
-- tx = binvox_data.read(2);
|
|
-- ty = binvox_data.read(2);
|
|
-- tz = binvox_data.read(2);
|
|
else
|
|
if line:find("scale") ~= nil then
|
|
-- scale = binvox_data.read(2);
|
|
else
|
|
minetest.chat_send_all(" unrecognized keyword [".. line.. "], skipping");
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end -- while
|
|
|
|
if (done == false) then
|
|
minetest.chat_send_all("binvox: error reading header");
|
|
return false;
|
|
end
|
|
if (dimX == 0) then
|
|
minetest.chat_send_all("binvox: missing dimensions in header");
|
|
return false;
|
|
end
|
|
|
|
local size = dimX * dimY * dimZ
|
|
-- local voxels[size];
|
|
|
|
--
|
|
-- read voxel data
|
|
--
|
|
local value, count, index, end_index, nodeCount, x, y, z, zwpy = 0,0,0,0,0,0,0,0,0
|
|
|
|
print("**********")
|
|
minetest.chat_send_all("Reading binvox data")
|
|
while (end_index < size) do
|
|
value=0
|
|
while (value==0) do
|
|
-- print("V:".. tostring(value).. " C:".. tostring(count).. " I:".. tostring(index))
|
|
index = index + count
|
|
if (index > size) then
|
|
io.close()
|
|
return false
|
|
end
|
|
value = io.read(1) -- this will be a 1 if voxel is present, else 0/nil if not
|
|
count = io.read(1) -- this is the number of times value will be repeated along the axis
|
|
if ((value == nil) or (count == nil)) then
|
|
minetest.chat_send_all("Completed placement of ".. nodeCount.. " nodes/voxels")
|
|
minetest.chat_send_all("Explore and consider reimporting if cavegen has punched holes in it")
|
|
print("growwall binvox imported ".. nodeCount.. " nodes/voxels")
|
|
io.close()
|
|
return true
|
|
end
|
|
value = string.byte(value) -- this will be a 1 if voxel is present, else 0/nil if not
|
|
count = string.byte(count) -- this is the number of times value will be repeated along the axis
|
|
end
|
|
|
|
x = math.floor(index / (dimY*dimZ))
|
|
zwpy = index % (dimY*dimZ) -- z*w + y
|
|
z = math.floor(zwpy / dimX)
|
|
y = zwpy % dimX
|
|
|
|
end_index = index + count
|
|
for i = 0, count do
|
|
-- voxels[i] = value
|
|
print("binvox voxel: X:".. x.. ", Y:".. y.. ", Z:".. z)
|
|
-- applying no scaling means the entire model will take up a 256x256x256 node cube
|
|
-- scaling is set in the locals (visible to the entire mod) in the first section
|
|
local p = {x=((-x*importScale)+importOffsetX), y=((y*importScale)+importOffsetY), z=((z*importScale)+importOffsetZ)} -- -ve x corrects mirroring from blender obj output - not sure if this is universal
|
|
voxManip:read_from_map(p, p)
|
|
minetest.set_node(p, {name=outputNodeType})
|
|
y = y + 1
|
|
nodeCount = nodeCount + 1
|
|
if (nodeCount % 1000) == 0 then
|
|
minetest.chat_send_all("placed ".. nodeCount.. " nodes/voxels")
|
|
end
|
|
end
|
|
|
|
end -- while
|
|
|
|
minetest.chat_send_all("Completed placement of ".. nodeCount.. " nodes/voxels")
|
|
minetest.chat_send_all("Explore and consider reimporting if cavegen has punched holes in it")
|
|
print("growwall binvox imported ".. nodeCount.. " nodes/voxels")
|
|
io.close()
|
|
return true
|
|
|
|
end -- read_binvox
|
|
|
|
|
|
|