Updates via shellscript

master
Lars Mueller 2019-08-14 13:24:04 +02:00
commit 7a3b62cefa
42 changed files with 2233 additions and 0 deletions

119
Readme.md Normal file
View File

@ -0,0 +1,119 @@
![Logo](/home/lars/.minetest/mods/voxelizer/logo.png)
# Voxelizer (`voxelizer`)
Turns 3D models into astonishing voxel builds. Sort of the opposite of [`wesh`](https://github.com/entuland/wesh) and [`meshport`](https://github.com/random-geek/meshport).
Another mighty world manipulation tool like [`worldedit`](https://github.com/Uberi/Minetest-WorldEdit). Blazing fast.
## About
**Note : Voxelizer needs to be added to the "trusted mods" if "mod security" is enabled.**
Depends on [`modlib`](https://github.com/appgurueu/modlib) and [`cmdlib`](https://github.com/appgurueu/cmdlib). IntelliJ IDEA with EmmyLua plugin project.
Code licensed under the [NPOSLv3 (Non-Profit Open Software License version 3.0)](https://opensource.org/licenses/NPOSL-3.0) for now.
Written by Lars Mueller alias LMD or appguru(eu).
Media licenses (files in the `media` folder) :
* `character.obj` - [CC BY-SA 3.0](https://github.com/minetest/minetest_game/tree/master/mods/player_api/README.txt), by MirceaKitsune & stujones11
* `character.png` - [CC BY-SA 3.0](https://github.com/minetest/minetest_game/tree/master/mods/player_api/README.txt), by Jordach
* `colors.txt` - [BSD 2-Clause "Simplified" License](https://github.com/minetest/minetestmapper/blob/master/COPYING), by sfan5
* `wool.txt` - derived from `colors.txt`, same license
Logo license (`logo.png`) : derived from `character.png` by Jordach (see above), same license (CC BY-SA 3.0), rendering & modifications by me (LMD)
## Screenshots
![Screenshot 1](/home/lars/.minetest/mods/voxelizer/screenshot.png)
Some Sams, using reduced palettes.
![Screenshot 2](/home/lars/.minetest/mods/voxelizer/screenshot_2.png)
Another Sam, using the full `colors.txt` palette from Minetestmapper.
![Screenshot 3](/home/lars/.minetest/mods/voxelizer/screenshot_3.png)
Same Sam, rear view.
![Screenshot 4](/home/lars/.minetest/mods/voxelizer/screenshot_4.png)
2 mages & Ironmen (thanks to [Jordach](http://minetest.fensta.bplaced.net/#author=Jordach) and [Ginsu23](http://minetest.fensta.bplaced.net/#author=Ginsu23) for the skins)
The used texture pack was [MTUOTP](https://content.minetest.net/packages/GamingAssociation39/mtuotp/) by Aurailus and GamingAssociation39.
Other textures seen are from [Minimal Development Test](https://github.com/minetest/minetest/tree/master/games/minimal) or the [Wool mod](https://github.com/minetest/minetest_game/tree/master/mods/wool) (wool textures by Cisoun).
## Usage
All commands are executed with `/vox <command> {params}`. If in need for help, just do `/help vox`.
You need the `voxelizer` priv to use any of the Voxelizer commands. Some commands require extra privs.
Media - models, textures and nodemaps (color lookups) - is stored in `<worldpath>/media`.
If you are unsure about which settings to use, either do some research or *try it and see*.
Editing the placed models is recommended; Voxelizer might place a few blocks undesirably, which needs to be fixed by hand.
### Configuration
Per-player configuration commands. Configuration remains after shutdown (is persistent).
* `texture [path]` - set/get the current texture (see [Supported File Formats](#supported-file-formats))
* `nodemap [path]` - set/get the current nodemap (see [Supported File Formats](#supported-file-formats))
* `dithering [id]` - set/get the current error diffusion dithering algorithms (specify algorithm ID)
* `color_choosing [id]` - set/get current color choosing mode (best/average)
* `filtering [id]` - set/get current filtering mode (nearest/bilinear)
* `placement [id]` - set/get merge modes (specify mode ID)
* `model [path]` - set/get the current 3D model (see [Supported File Formats](#supported-file-formats))
* `alpha_weighing [enable/disable]` - get/enable/disable weighing colors (see `color_choosing`) by their alpha
* `protection_bypass [enable/disable]` - get/enable/disable protection bypass (you need the priv `protection_bypass` to enable it)
* `precision [number]` - set/get the current rasterization accuracy (integer). Note that this increases computation time quadratically. Values higher than `10` are not recommended.
#### Supported file formats
##### Textures
All file formats supported by `ImageIO` on your Java setup. You can find them out using the following commands :
```bash
cd ~/.minetest/mods/voxelizer/production/voxelizer
java SupportedTextureFormats
```
On my system (Java 11), the output was :
```
The supported image file formats are : JPG, jpg, tiff, bmp, BMP, gif, GIF, WBMP, png, PNG, JPEG, tif, TIF, TIFF, wbmp, jpeg
```
Internally, the `SIF` (`.sif`, "Simple Image File") file format (just gave it some name) is used :
* 4 byte header : 2 times a 2 byte unsigned short, first is width, second is height
* Followed by uncompressed image data : array of 4 byte tuples, consisting of ARGB unsigned bytes, positions in array are calculated as `x + y * width`
##### Node Map
Any valid `minetestmapper-colors.txt` will be accepted by this mod. The format is :
Multiple lines like `[(<content_id:hex>|<nodename>) <red> <green> <blue>][#<comment>]`
##### 3D Models
Only the `.obj` file format is (with certain restrictions) supported. It is recommended to export your models from Blender.
Restrictions :
* No free form geometry (`vp`s)
* No complex texture coordinates (`vt`s with more than 2 coordinates given), use simple ones
* No polygonal faces (`f`s with more than 3 indexes), use triangles
* No line (`l`) elements
* No material (`.mtl`) file usage, only a single texture
* No smooth shading (`s`)
* No normals (`vn`)
All of the above will be ignored whenever possible.
Export your .obj files with Blender properly by ticking the right options, as seen here :
![Ticked checkboxes in Blender's "Export OBJ" dialog](http://www.opengl-tutorial.org/assets/images/tuto-7-model-loading/Blender.png)
So summarized, the following boxes should be ticked :
- [x] Apply modifiers
- [x] Write normals (not required)
- [x] Write UVs
- [x] Triangulate faces
- [x] Objects as OBJ objects
Everything else should not be ticked.
### Actions
* `1`/`2` - set first and second edge position, model will be placed thereafter and positions will be deleted
* `place [scale]` - place model with given scale (defaults to `1`)
* `download <url> [filename]` - download a file from the internet using a GET request, requires `voxelizer:download` priv additionally.
File will be downloaded to `<worldpath>/media/filename`. The URL filename will be taken if `filename` is not specified.
**Likely not safe due to the usage of HTTP Requests.**

349
chatcommands.lua Normal file
View File

@ -0,0 +1,349 @@
conf = {max_precision = 15, download = true}
local mediapath = minetest.get_modpath("voxelizer").."/media/"
defaults = {texture = mediapath.."character.png",
model = mediapath.."character.obj",
nodemap = mediapath.."colors.txt",
placement = 1,
dithering = 10,
precision = 4,
min_density = 0.1}
minetest.register_privilege("protection_bypass", {
description = "Can bypass protection",
give_to_admin = true,
give_to_singleplayer = true
})
minetest.register_privilege("voxelizer", {
description = "Can use the voxelizer mod",
give_to_admin = true,
give_to_singleplayer = true
})
cmd_ext.register_chatcommand("vox", {
description = "Voxelizer",
privs = {voxelizer = true},
func = function(sendername, params)
return false, "Use the commands of the voxelizer mod using /vox <command> {params} - for a list of commands do /help vox."
end
})
function get_setting_int(playername, settingname)
local setting = minetest.get_player_by_name(playername):get_meta():get_int("voxelizer_"..settingname)
return setting ~= 0 and setting
end
function set_setting_int(playername, settingname, value)
return minetest.get_player_by_name(playername):get_meta():set_int("voxelizer_"..settingname, value)
end
function get_setting_float(playername, settingname)
local setting = minetest.get_player_by_name(playername):get_meta():get_float("voxelizer_"..settingname)
return setting ~= 0 and setting
end
function set_setting_float(playername, settingname, value)
return minetest.get_player_by_name(playername):get_meta():set_float("voxelizer_"..settingname, value)
end
function get_setting(playername, settingname)
local setting = minetest.get_player_by_name(playername):get_meta():get_string("voxelizer_"..settingname)
return setting ~= "" and setting
end
function set_setting(playername, settingname, value)
return minetest.get_player_by_name(playername):get_meta():set_string("voxelizer_"..settingname, value)
end
function register_setting_command(commandname, values, settingname)
settingname = settingname or commandname
local root = number_ext.round(math.sqrt(#values))
local display_vals = {}
for index, val in ipairs(values) do
table.insert(display_vals, index..". "..val)
end
cmd_ext.register_chatcommand("vox "..commandname, {
description = "Get and list or set player-specific setting "..settingname,
params = "[index]",
func = function(sendername, params)
if params.index then
params.index = tonumber(params.index)
if not params.index or params.index < 1 or params.index > #values or params.index % 1 > 0 then
return false, "Index out of range : Needs to be an integer between 1 and "..#values
end
set_setting_int(sendername, settingname, params.index)
return true, string.format('Setting "%s" was set to %d - "%s"', settingname, params.index, values[params.index])
end
local display_vals = {unpack(display_vals)}
local setting = get_setting_int(sendername, settingname, params.index) or defaults[settingname]
if display_vals[setting] then
display_vals[setting] = minetest.get_color_escape_sequence("#FFFF66")..display_vals[setting].." (active)"..minetest.get_color_escape_sequence("#FFFFFF")
end
local options = {}
for i=1, #values, root do
local row = {unpack(display_vals, i, i+root-1)}
table.insert(options, table.concat(row, ", "))
end
return nil, table.concat(options, "\n")
end
})
end
function register_file_command(commandname, settingname)
settingname = settingname or commandname
cmd_ext.register_chatcommand("vox "..commandname, {
description = "Get or set player-specific file "..settingname,
params = "[filename]",
func = function(sendername, params)
if params.filename then
local path = get_media(params.filename)
if not file_ext.exists(path) then return false, "File doesn't exist" end
set_setting(sendername, settingname, params.filename)
return true, string.format('File "%s" was set to "%s"', settingname, params.filename)
end
local value = get_setting(sendername, settingname)
if value then
return true, string.format('File "%s" is currently set to "%s"', settingname, value)
end
return true, string.format('File "%s" is currently not set, using default', settingname)
end
})
end
local algorithm_names = {}
for index, matrix in ipairs(dithering_matrices) do
algorithm_names[index] = matrix.name
end
table.insert(algorithm_names, "No preprocessing")
register_setting_command("dithering", algorithm_names)
local color_choosing = {"best", "average"}
register_setting_command("color_choosing", {"Best", "Average"})
local merge_mode = {"overwrite", "add", "intersection"}
register_setting_command("placement", {"Overwrite", "Add", "Intersection"})
local filtering = {"nearest", "bilinear"}
register_setting_command("filtering", {"Nearest neighbor", "Bilinear"})
register_file_command("model")
register_file_command("texture")
register_file_command("nodemap")
function register_bool_setting_command(name)
cmd_ext.register_chatcommand("vox "..name, {
description = "Check whether "..name.." is enabled",
func = function(sendername)
local enabled = get_setting_int(sendername, name) == 1
return true, string.format("Setting %s is %s", name, (enabled and "enabled") or "disabled")
end
})
cmd_ext.register_chatcommand("vox "..name.." enable", {
description = "Enable "..name,
privs = {protection_bypass = true},
func = function(sendername)
set_setting_int(sendername, name, 1)
return true, name.." was enabled."
end
})
cmd_ext.register_chatcommand("vox "..name.." disable", {
description = "Disable "..name,
privs = {protection_bypass = true},
func = function(sendername)
set_setting_int(sendername, name, 2)
return true, name.." was disabled."
end
})
end
register_bool_setting_command("protection_bypass")
register_bool_setting_command("alpha_weighing")
local max_precision = conf.max_precision
cmd_ext.register_chatcommand("vox precision", {
description = "Set/get current precision",
params = "[number]",
func = function(sendername, params)
if params.number then
params.number = tonumber(params.number)
if params.number % 1 ~= 0 or params.number < 1 or params.number > max_precision then
return false, "Number needs to be an integer between 1 and "..max_precision
end
set_setting_int(sendername, "precision", params.number)
return true, "Precision was set to "..params.number
end
return true, "Precision currently is "..(get_setting_int(sendername, "precision") or defaults.precision)
end
})
cmd_ext.register_chatcommand("vox min_density", {
description = "Set/get minimum density",
params = "[number]",
func = function(sendername, params)
if params.number then
params.number = tonumber(params.number)
if params.number < 0 or params.number > 1 then
return false, "Number needs to be a floating-point number between 0 and 1"
end
set_setting_float(sendername, "min_density", params.number)
return true, "Precision was set to "..params.number
end
return true, "Precision currently is "..(get_setting_int(sendername, "min_density") or defaults.min_density)
end
})
local function substitute_coords(pos, playername)
local player_pos = minetest.get_player_by_name(playername):get_pos()
local x, y, z = unpack(pos)
local pos = {x or player_pos.x, y or player_pos.y, z or player_pos.z}
return pos
end
local function place(sendername, additional)
local function setting(name) return get_setting(sendername, name) end
local function setting_int(name) return get_setting_int(sendername, name) end
local function setting_float(name) return get_setting_float(sendername, name) end
local model, texture, nodemap = setting("model"), setting("texture"), setting("nodemap")
local params = {
playername = sendername,
model = (model and get_media(model)) or defaults.model,
texture = (texture and get_media(texture)) or defaults.texture,
nodemap = (nodemap and get_media(nodemap)) or defaults.nodemap,
min_density = setting_float("min_density") or defaults.min_density,
precision = setting_int("precision") or defaults.precision,
dithering = dithering_matrices[setting_int("dithering")],
protection_bypass = setting_int("protection_bypass") == 1,
weighed = setting_int("alpha_weighing") == 1,
filtering = filtering[setting_int("filtering")],
color_choosing = color_choosing[setting_int("color_choosing")],
merge_mode = merge_mode[setting_int("placement")],
}
table_ext.add_all(params, additional)
return place_obj(params)
end
cmd_ext.register_chatcommand("vox place", {
description = "Place model at position with given size. Missing coordinates will be replaced by player coordinates.",
params = "[scale] {position}",
func = function(sendername, params)
if params.scale then
params.scale = tonumber(params.scale)
if not params.scale then return false, "Scale needs to be a valid number" end
if params.position and #params.position > 3 then return false, "Only 3 coordinates (x, y, z) are allowed" end
end
local pos = substitute_coords(params.position or {}, sendername)
local error = place(sendername, { pos1 = pos, scale = params.scale })
if error then return false, "Failed to place model : "..error end
return true, "Placed model at "..vector.to_string(pos).." with the scale "..(params.scale or 1)
end
})
cmd_ext.register_chatcommand("vox 1", {
description = "Set first corner position of model to be placed. Missing coordinates will be replaced by player coordinates.",
params = "{position}",
func = function(sendername, params)
if params.position and #params.position > 3 then return false, "Only 3 coordinates (x, y, z) are allowed" end
local pos = substitute_coords(params.position or {}, sendername)
set_setting(sendername, "1", minetest.write_json(pos))
local old_id = get_setting_int(sendername, "pos1_waypoint")
if old_id then minetest.get_player_by_name(sendername):hud_remove(old_id) end
local id = minetest.get_player_by_name(sendername):hud_add({hud_elem_type="waypoint",
name="Voxelizer Position 1",
number=0xFFFF00,
world_pos = vector.to_minetest(pos)})
minetest.add_particle({
pos = vector.to_minetest(pos),
velocity = {x=0, y=0, z=0},
acceleration = {x=0, y=0, z=0},
expirationtime = 6,
size = 16,
collisiondetection = false,
collision_removal = false,
object_collision = false,
vertical = false,
texture = "voxelizer_marker.png",
playername = sendername,
animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2.0 },
glow = 14
})
set_setting_int(sendername, "pos1_waypoint", id)
end
})
cmd_ext.register_chatcommand("vox 2", {
description = "Set second corner position of model & place it. Missing coordinates will be replaced by player coordinates.",
params = "{position}",
func = function(sendername, params)
if params.position and #params.position > 3 then return false, "Only 3 coordinates (x, y, z) are allowed" end
local pos1, pos2 = minetest.parse_json(get_setting(sendername, "1")), substitute_coords(params.position or {}, sendername)
if not pos1 then
return false, "First corner position not set. Do \"/vox 1\" first to set it."
end
local old_id = get_setting_int(sendername, "pos1_waypoint")
if old_id then minetest.get_player_by_name(sendername):hud_remove(old_id) end
local error = place(sendername, { pos1 = pos1, pos2 = pos2 })
if error then return false, "Failed to place model : "..error end
return true, "Placed model from "..vector.to_string(pos1).." to "..vector.to_string(pos2)
end
})
cmd_ext.register_chatcommand("vox reset", {
description = "Reset settings",
func = function(sendername)
for setting, _ in pairs(defaults) do
set_setting(sendername, setting, "")
end
return true, "Settings resetted."
end
})
if conf.download then
minetest.register_privilege("voxelizer:download", {
description = "Can download files from the internet using the voxelizer mod",
give_to_admin = true,
give_to_singleplayer = true
})
local errors = {
"URL and output path need to be given",
"Couldn't create file",
"Output file doesn't exist or can't be written",
"Couldn't download file",
"Couldn't write to file",
"Malformed URL"
}
cmd_ext.register_chatcommand("vox download", {
description = "Download a file from the internet",
params = "<url> [filename]",
privs = {["voxelizer:download"]=true},
func = function(sendername, params)
if not params.filename then
local last_slash
for i = params.url:len(), 1, -1 do
if params.url:sub(i, i) == "/" then
last_slash = i
break
end
end
params.filename = params.url:sub(last_slash+1)
end
local path = voxelizer.get_media(params.filename)
local call = string.format('java -classpath "%s/production" FileDownloader "%s" "%s"', minetest.get_modpath("voxelizer"), params.url, path)
print(call)
local response_code = os.execute(call)
if response_code ~= 0 then
local error = errors[response_code]
if error then return false, "Download failed : "..error end
return false, "Download failed"
end
return true, "Download successful"
end
})
end

84
closest_color.lua Normal file
View File

@ -0,0 +1,84 @@
local max_linear=10 -- Maximum number of colors for using linear search
-- Finds the closest color using a binary search like thing - point cloud is split into two (ideally equally sized) clouds multiple times which gives a searchable tree
-- Builds a k-d tree for 3d points
function kd_tree_builder(dim)
return function(points, axis)
if #points == 1 then
return points[1]
end
axis=(axis or 0)+1
table.sort(points, function(a,b) return a[axis] > b[axis] end)
local median=math.floor(#points/2)
local next_axis=(axis+1)%dim
return {
axis=axis,
pivot=points[median],
left=build_kd_tree({unpack(points, 1, median)}, next_axis),
right=build_kd_tree({unpack(points, median+1)}, next_axis)
}
end
end
build_kd_tree = kd_tree_builder(3)
distance = function(c1, c2) return math.sqrt(math.pow((c1[1]-c2[1]), 2)+math.pow((c1[2]-c2[2]), 2)+math.pow((c1[3]-c2[3]), 2)) end
-- Returns a function which gives you the closest color, based on k-d trees
function kd_closest_color_finder(colors)
local tree = build_kd_tree(colors)
return function(color)
local min_distance=math.huge
local closest_color
f=function(tree)
local axis=tree.axis
if #tree > 0 then -- Subtree is leaf
local distance = distance(tree, color)
if distance < min_distance then
min_distance = distance
closest_color = tree
end
return
else
local new_tree, other_tree = tree.right, tree.left
if color[axis] < tree.pivot[axis] then
new_tree, other_tree = tree.left, tree.right
end
f(other_tree)
if tree.pivot then
local dist = math.abs(tree.pivot[axis]-color[axis])
if dist <= min_distance then
f(new_tree)
end
end
end
end
f(tree)
return closest_color, min_distance
end
end
-- returns a function which returns closest color, based on linear search
function linear_closest_color_finder(colors)
return function(c1)
local min=math.huge
local clos
for _, c2 in pairs(colors) do
local dis=distance(c1, c2)
if dis < min then
min=dis
clos=c2
end
end
return clos, min
end
end
-- returns a function for finding the closest color, based on length of colors uses either k-d tree or linear search
function closest_color_finder(colors)
if #colors <= max_linear then
return linear_closest_color_finder(colors)
end
return kd_closest_color_finder(colors)
end

58
conf.lua Normal file
View File

@ -0,0 +1,58 @@
local int = function(value) if value % 1 ~= 0 then return "Integer instead of float expected." end end
local conf_spec = {
type = "table",
children = {
max_precision = {
type = "number",
func = function(value) if value % 1 ~= 0 then return "Integer instead of float expected." end end
},
download = {
type = "boolean"
},
defaults = {
type = "table",
possible_keys = {
model = {type = "string"},
texture = {type = "string"},
nodemap = {type = "string"}
},
required_keys = {
min_density = {type = "number", range = {0, 1}},
precision = {
type = "number",
range = {1, 100},
func = int
},
dithering = {
type = "number",
range = {1, 10},
func = int
},
placement = {
type = "number",
range = {1, 3},
func = int
},
color_choosing = {
type = "number",
range = {1, 2},
func = int
},
filtering = {
type = "number",
range = {1, 2},
func = int
}
}
}
},
}
config=conf.import("voxelizer", conf_spec)
local mediapath = minetest.get_modpath("voxelizer").."/media/"
local fallback_defaults = {texture = mediapath.."character.png", model = mediapath.."character.obj", nodemap = mediapath.."colors.txt"}
for key, alt in pairs(fallback_defaults) do
if config.defaults[key] == nil then
config.defaults[key] = alt
end
end

47
config_help.md Normal file
View File

@ -0,0 +1,47 @@
# Voxelizer - Configuration
JSON Configuration : `<worldpath>/config/voxelizer.json`
Text Logs : `<worldpath>/logs/voxelizer/<date>.txt`
Explaining document(this, Markdown) : `<modpath/gamepath>/voxelizer/config_help.md`
Readme : `<modpath/gamepath>/voxelizer/Readme.md`
Default Configuration : `<modpath/gamepath>/voxelizer/default_config.json`
```json
{
"max_precision" : 15,
"download" : true,
"defaults" : {
"precision" : 4,
"min_density" : 0.1,
"dithering" : 10,
"placement" : 1,
"color_choosing" : 1,
"filtering" : 1
}
}
```
#### `max_precision`
Integer, maximum settable precision.
#### `download`
Boolean, whether to enable the `/vox download` chatcommand.
#### `defaults`
Dictionairy / table, default names assigned to corresponding values. Possible names below.
##### `min_density`
Float between 0 and 1. Minimum density default.
##### `precision`
Integer > 1 and < 100. Precision default.
##### `dithering`
Default dithering algorithm ID (see `/vox dithering`).
##### `placement`
Default placement mode ID (see `/vox placement`).
##### `color_choosing`
Default color choosing algorithm ID (see `/vox color_choosing`).
##### `filtering`
Default filtering algorithm ID (see `/vox filtering`).
##### `model` / `texture` / `nodemap`
Optional default filenames. Files will be searched in world's media folder.
If not given, Voxelizer falls back to default files from mod's media folder.

12
default_config.json Normal file
View File

@ -0,0 +1,12 @@
{
"max_precision" : 15,
"download" : true,
"defaults" : {
"precision" : 4,
"min_density" : 0.1,
"dithering" : 10,
"placement" : 1,
"color_choosing" : 1,
"filtering" : 1
}
}

84
dithering.lua Normal file
View File

@ -0,0 +1,84 @@
dithering_matrices = {
{
name = "Floyd-Steinberg",
{x_off=1, 7/16},
{x_off=-1, 3/16, 5/16, 1/16}
},
{
name = "Jarvis, Judice & Ninke",
{x_off = 1, 7/48, 5/48},
{x_off = -2, 3/48, 5/48, 7/48, 5/48, 3/48},
{x_off = -2, 1/48, 3/48, 5/48, 3/48, 1/48}
},
{
name = "Stucke",
{x_off = 1, 8/42, 4/42},
{x_off = -2, 2/42, 4/42, 8/42, 4/42, 2/42},
{x_off = -2, 1/42, 2/42, 4/42, 2/42, 1/42}
},
{
name = "Atkinson",
{x_off=1, 1/8, 1/8},
{x_off = -1, 1/8, 1/8, 1/8},
{x_off = 0, 1/8}
},
{
name = "Burkes",
{x_off = 1, 8/42, 4/42},
{x_off = -2, 2/42, 4/42, 8/42, 4/42, 2/42}
},
{
name = "Sierra",
{x_off = 1, 4/16, 3/16},
{x_off = -2, 1/16, 2/16, 3/16, 2/16, 1/16}
},
{
name = "Sierra Lite",
{x_off = 1, 2/4},
{x_off = -1, 1/4, 1/4}
},
{
name = "Two row Sierra",
{x_off = 1, 5/32, 3/32},
{x_off = -2, 2/32, 4/32, 5/32, 4/32, 2/32},
{x_off = -1, 2/32, 3/32, 2/32}
},
{
name = "No dithering"
}
}
function dither(texture, closest_color_finder, matrix)
matrix = matrix or dithering_matrices[1]
local newtexture = {width = texture.width, height = texture.height}
for x=0, texture.width-1 do
for y=0, texture.height-1 do
local color = rgba_number_to_table(get_texture_color_at(texture, x, y))
local alpha = color[1]
table.remove(color, 1)
local closest_color = closest_color_finder(color)
set_texture_color_at(newtexture, x, y, rgba_tuple_to_number(alpha, unpack(closest_color)))
local error = vector.subtract(closest_color, color) -- Difference between closest color - actual color
table.insert(error, 1, 0)
-- now push the error to unprocessed pixels
for ydif, line in ipairs(matrix) do
for index, diff in ipairs(line) do
local nx, ny = x + index + line.x_off-1, y+ydif-1
if nx >= 0 and ny >= 0 and nx <= texture.width-1 and ny <= texture.height-1 then
if not rgba_number_to_table(get_texture_color_at(texture, x, y)) then error("AAAA") end
local target_color = vector.floor(
vector.clamp(
vector.add(
rgba_number_to_table(get_texture_color_at(texture, x, y)),
vector.multiply(error, diff)
),
0, 255
)
)
set_texture_color_at(texture, x, y, rgba_table_to_number(target_color))
end
end
end
end
end
return newtexture
end

17
init.lua Normal file
View File

@ -0,0 +1,17 @@
voxelizer={}
minetest.mkdir(minetest.get_worldpath().."/media") -- Create media dir
--cmd_ext.register_chatcommand()
extend_mod("voxelizer", "conf") -- Load JSON configuration stored in worldpath
extend_mod("voxelizer", "vector") -- Own vector lib, operating on lists
extend_mod("voxelizer", "closest_color") -- Closest color finder, uses linear search / k-d tree depending on number of colors
extend_mod("voxelizer", "texture_reader") -- Texture reader, reads textures, uses Java program
extend_mod("voxelizer", "dithering") -- Error diffusion dithering
extend_mod("voxelizer", "obj_reader") -- OBJ reader, reads simple OBJ models
extend_mod("voxelizer", "node_map_reader") -- Node map reader, reads minetestmapper-colors.txt like files
extend_mod("voxelizer", "main") -- Main : Actual API for placing of models using VoxelManip
extend_mod("voxelizer", "chatcommands") -- Chatcommands for making use of the API
-- Tests : Only uncomment if you actually want to test something !
--[[minetest.register_on_mods_loaded(function()
extend_mod("voxelizer", "test")
end)]]

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

214
main.lua Normal file
View File

@ -0,0 +1,214 @@
function get_media(name)
return minetest.get_worldpath().."/media/"..name
end
function get_obj_bounding_box(vertexes)
local min={math.huge, math.huge, math.huge}
local max={-math.huge, -math.huge, -math.huge}
for _, vertex in pairs(vertexes) do
for i=1, 3 do
if vertex[i] < min[i] then
min[i]=vertex[i]
elseif vertex[i] > max[i] then
max[i]=vertex[i]
end
end
end
return min, max
end
function get_voxel_area(min, max, vm)
local vox_min, vox_max = {}, {}
for i=1, 3 do
if min[i] < 0 then vox_min[i]=math.floor(min[i]) else vox_min[i]=math.ceil(min[i]) end
if max[i] < 0 then vox_max[i]=math.floor(max[i]) else vox_max[i]=math.ceil(max[i]) end
end -- Floor/ceil min/max for VoxelArea
vox_min, vox_max = vector.convert(vector.subtract(vox_min, {16,16,16})), vector.convert(vector.add(vox_max, {16,16,16}))
local c1, c2 = vm:read_from_map(vox_min, vox_max)
local area = VoxelArea:new{MinEdge=c1, MaxEdge=c2}
return area
end
local steps = 6
local min_amount = 0.1
function place_obj(params)
local path_to_obj, path_to_texture, path_to_nodemap, pos1, pos2, scale = params.model, params.texture, params.nodemap, params.pos1, params.pos2, params.scale
local steps = params.precision or steps
local min_amount = (params.min_amount or min_amount) * 255 * steps * steps
local obj_content = file_ext.read(path_to_obj)
if not obj_content then
return ("OBJ doesn't exist."):format(path_to_obj)
end
if not file_ext.exists(path_to_texture) then
return ("Texture doesn't exist."):format(path_to_texture)
end
local nodemap_content=file_ext.read(path_to_nodemap)
if not nodemap_content then
return ("Nodemap doesn't exist."):format(path_to_nodemap)
end
local nodemap = read_node_map(nodemap_content)
local colors = table_ext.keys(nodemap)
table_ext.map(colors, rgb_number_to_table)
local closest_color_finder = closest_color_finder(colors)
local texture = read_texture(path_to_texture)
if params.dithering then
texture = dither(texture, closest_color_finder, params.dithering)
end
local filtering
if params.filtering == "bilinear" then
filtering = function(pos_uv) return bilinear_filtering(texture, pos_uv) end
else -- default : nearest
filtering = function(pos_uv) return nearest_filtering(texture, pos_uv) end
end
local triangle_consumer_factory, transform, min, max, area, nodes
nodes={} -- VoxelArea index -> colors
local vm = minetest.get_voxel_manip()
local data
triangle_consumer_factory=function(vertexes)
min, max = get_obj_bounding_box(vertexes)
if pos2 then -- do something with vertexes
-- transforms a vector : OBJ space -> MT space from pos1 to pos2
-- steps :
-- 0. translate to 0
-- 1. normalize it (squash/stretch it to a 1x1x1 cube)
-- 2. stretch it to pos1 - pos2 thingy
-- 3. translate to pos1
local mt_space = vector.subtract(pos2, pos1)
local obj_space = vector.subtract(max, min)
local transform_vec = vector.divide(mt_space, obj_space)
function transform(v)
local vec = vector.subtract(v, min) -- translate to 0
local res = vector.multiply_vector(vec, transform_vec)
res = vector.add(res, pos1)
return res
end
elseif scale then
function transform(v)
local res = vector.add(vector.multiply(v, scale), pos1)
return res
end
else
function transform(v)
local res = vector.add(v, pos1)
return res
end
end
table_ext.map(vertexes, transform) -- Transforming the vertices to MT space
area = get_voxel_area(transform(min), transform(max), vm)
data = vm:get_data()
local is_protected = function(pos) return true end
if params.playername and not params.protection_bypass then
--is_protected = function(pos) return minetest.is_protected(pos, params.playername) end
end
local is_mergeable = function(index) return true end
if params.merge_mode == "add" then
is_mergeable = function(index)
local d = data[index]
if d == minetest.CONTENT_AIR or d == minetest.CONTENT_IGNORE or d == minetest.CONTENT_UNKNOWN then
return true
end
return false
end
elseif params.merge_mode == "intersection" then
is_mergeable = function(index)
local d = data[index]
if d == minetest.CONTENT_AIR or d == minetest.CONTENT_IGNORE then
return false
end
return true
end
end
local merge_at = function(pos, index) return is_protected(pos) and is_mergeable(index) end
return function(points, uvs)
local b1=vector.subtract(points[2], points[1]) -- First basis vector, vertices
local len1=vector.length(b1)
local b2=vector.subtract(points[3], points[1]) -- Second basis vector, vertices
local len2=vector.length(b2)
local u1=vector.subtract(uvs[2], uvs[1]) -- First basis vector, UVs
local u2=vector.subtract(uvs[3], uvs[1]) -- Second basis vector, UVs
for l1=0,1,1/(len1*steps) do -- Lambda 1 - scalar of first basis
for l2=0,1,1/(len2*steps) do -- Lambda 2 - 2nd one
if l1+l2 <= 1 then -- On triangle
local res = vector.add(vector.multiply(b1, l1), vector.multiply(b2, l2))
local pos = vector.add(points[1], res)
local floor_pos = vector.convert(vector.floor(pos))
local index = area:indexp(floor_pos)
if merge_at(floor_pos, index) then
nodes[index] = nodes[index] or {amount=0}
nodes[index].amount = nodes[index].amount + 1
-- Now finding the same coord on texture
local pos_uv = vector.add(uvs[1], vector.add(vector.multiply(u1, l1), vector.multiply(u2, l2)))
local color_uv = get_texture_color_at(texture, math.floor(pos_uv[1]*(texture.width-0.0000001)), math.floor((1-pos_uv[2])*(texture.height-0.0000001)))
nodes[index][color_uv] = ((nodes[index][color_uv]) and (nodes[index][color_uv]+1)) or 1
end
end
end
end
end
end
local get_color
if params.color_choosing == "best" then
function get_color(node)
local best_color = -math.huge
local best_amount = -math.huge
for color, amount in pairs(node) do
local color = rgba_number_to_table(color)
if amount >= best_amount then
best_color = color
best_amount = amount
end
end
return best_color, best_amount
end
else -- average
function get_color(node)
local average_color = {0, 0, 0, 0}
local sumcount = 0
for color, count in pairs(node) do
sumcount = sumcount + count
local color = rgba_number_to_table(color)
average_color = vector.add(average_color, vector.multiply(color, count))
end
average_color = vector.multiply(average_color, 1/sumcount)
return average_color
end
end
read_obj(obj_content, triangle_consumer_factory)
for index, node in pairs(nodes) do
local amount = node.amount
node.amount = nil
if params.weighed or true then
for c, v in pairs(node) do
local k = rgba_number_to_table(c)
node[c] = v * k[1]
end
end
local best_color = get_color(node)
if best_color then
if best_color[1]*amount >= min_amount then
table.remove(best_color, 1)
local closest_color = closest_color_finder(best_color)
closest_color = rgb_table_to_number(closest_color)
data[index] = nodemap[closest_color]
end
end
end
vm:set_data(data)
--vm:calc_lighting()
--vm:update_liquids()
vm:write_to_map()
end

314
media/character.obj Normal file
View File

@ -0,0 +1,314 @@
# Blender v2.76 (sub 0) OBJ File: 'character.blend'
# www.blender.org
o Player_Cube
v -2.100000 6.299495 1.049999
v -2.100000 6.300505 -1.050001
v -2.100000 12.599493 1.053028
v -2.100000 12.600502 -1.046972
v -4.200159 12.600022 -1.047291
v -4.199840 12.599971 1.052709
v -2.099840 12.599971 1.052389
v -2.100159 12.600022 -1.047610
v -4.200159 6.300025 -1.047446
v -4.199840 6.299973 1.052554
v -2.099839 6.299973 1.052234
v -2.100159 6.300025 -1.047766
v -2.100000 0.000002 1.049999
v -2.100000 0.000002 -1.050001
v -2.100000 6.300002 1.049999
v -2.100000 6.300002 -1.050001
v -2.100504 12.600028 2.102523
v -2.099495 12.600011 -2.097476
v -2.100460 16.800030 2.102506
v -2.099451 16.800011 -2.097494
v 2.100000 6.300505 -1.050001
v 2.100000 6.299495 1.049999
v 2.100000 12.600502 -1.046972
v 2.100000 12.599493 1.053028
v 2.100159 6.300025 -1.047766
v 2.099839 6.299973 1.052234
v 4.199840 6.299973 1.052554
v 4.200159 6.300025 -1.047446
v 2.100159 12.600022 -1.047610
v 2.099840 12.599971 1.052389
v 4.199840 12.599971 1.052709
v 4.200159 12.600022 -1.047291
v 0.000000 -0.000000 -1.050001
v -0.000000 0.000000 1.049999
v 0.000000 6.300000 1.049999
v 0.000000 6.300000 -1.050001
v 2.100504 12.599967 -2.096467
v 2.099495 12.599984 2.103532
v 2.100549 16.799969 -2.096485
v 2.099540 16.799984 2.103515
v 2.100000 6.300002 -1.050001
v 2.100000 6.300002 1.049999
v 2.100000 0.000002 -1.050001
v 2.100000 0.000002 1.049999
v 0.000000 0.000000 -1.050001
v -0.000000 -0.000000 1.049999
v -0.000000 6.300000 1.049999
v -0.000000 6.300000 -1.050001
v -2.300554 12.400033 2.302476
v -2.299449 12.400013 -2.297523
v -2.300506 17.000034 2.302457
v -2.299401 17.000015 -2.297543
v 2.300550 12.399964 -2.296418
v 2.299445 12.399984 2.303581
v 2.300599 16.999966 -2.296438
v 2.299494 16.999985 2.303562
v 2.100000 12.600502 -1.046972
v 2.100000 12.600502 -1.046972
v 2.100000 12.599493 1.053028
v 2.100000 12.599493 1.053028
v 2.100000 6.299495 1.049999
v 2.100000 6.299495 1.049999
v 2.100000 6.300505 -1.050001
v 2.100000 6.300505 -1.050001
v -2.100000 12.599493 1.053028
v -2.100000 12.599493 1.053028
v -2.100000 6.299495 1.049999
v -2.100000 6.299495 1.049999
v -2.100000 12.600502 -1.046972
v -2.100000 12.600502 -1.046972
v -2.100000 6.300505 -1.050001
v -2.100000 6.300505 -1.050001
v -4.200159 12.600022 -1.047291
v -4.200159 12.600022 -1.047291
v -4.200159 6.300025 -1.047446
v -4.200159 6.300025 -1.047446
v -2.099840 12.599971 1.052389
v -2.099840 12.599971 1.052389
v -2.099839 6.299973 1.052234
v -2.099839 6.299973 1.052234
v -4.199840 12.599971 1.052709
v -4.199840 12.599971 1.052709
v -4.199840 6.299973 1.052554
v -4.199840 6.299973 1.052554
v -2.100159 12.600022 -1.047610
v -2.100159 12.600022 -1.047610
v -2.100159 6.300025 -1.047766
v -2.100159 6.300025 -1.047766
v -0.000000 0.000000 1.049999
v -0.000000 0.000000 1.049999
v 0.000000 -0.000000 -1.050001
v 0.000000 -0.000000 -1.050001
v -2.100000 0.000002 1.049999
v -2.100000 0.000002 1.049999
v -2.100000 6.300002 1.049999
v -2.100000 6.300002 1.049999
v -2.100000 6.300002 -1.050001
v -2.100000 6.300002 -1.050001
v -2.100000 0.000002 -1.050001
v -2.100000 0.000002 -1.050001
v 2.100549 16.799969 -2.096485
v 2.100549 16.799969 -2.096485
v 2.099540 16.799984 2.103515
v 2.099540 16.799984 2.103515
v 2.099495 12.599984 2.103532
v 2.099495 12.599984 2.103532
v 2.100504 12.599967 -2.096467
v 2.100504 12.599967 -2.096467
v -2.100460 16.800030 2.102506
v -2.100460 16.800030 2.102506
v -2.100504 12.600028 2.102523
v -2.100504 12.600028 2.102523
v -2.099451 16.800011 -2.097494
v -2.099451 16.800011 -2.097494
v -2.099495 12.600011 -2.097476
v -2.099495 12.600011 -2.097476
v 2.100159 12.600022 -1.047610
v 2.100159 12.600022 -1.047610
v 2.099840 12.599971 1.052389
v 2.099840 12.599971 1.052389
v 2.099839 6.299973 1.052234
v 2.099839 6.299973 1.052234
v 2.100159 6.300025 -1.047766
v 2.100159 6.300025 -1.047766
v 4.199840 12.599971 1.052709
v 4.199840 12.599971 1.052709
v 4.199840 6.299973 1.052554
v 4.199840 6.299973 1.052554
v 4.200159 12.600022 -1.047291
v 4.200159 12.600022 -1.047291
v 4.200159 6.300025 -1.047446
v 4.200159 6.300025 -1.047446
v 0.000000 6.300000 1.049999
v 0.000000 6.300000 1.049999
v -0.000000 6.300000 1.049999
v -0.000000 6.300000 1.049999
v 2.100000 6.300002 1.049999
v 2.100000 6.300002 1.049999
v -0.000000 -0.000000 1.049999
v -0.000000 -0.000000 1.049999
v 0.000000 6.300000 -1.050001
v 0.000000 6.300000 -1.050001
v 2.100000 0.000002 1.049999
v 2.100000 0.000002 1.049999
v 0.000000 0.000000 -1.050001
v 0.000000 0.000000 -1.050001
v 2.100000 0.000002 -1.050001
v 2.100000 0.000002 -1.050001
v 2.100000 6.300002 -1.050001
v 2.100000 6.300002 -1.050001
v -0.000000 6.300000 -1.050001
v -0.000000 6.300000 -1.050001
v 2.300599 16.999966 -2.296438
v 2.300599 16.999966 -2.296438
v 2.299494 16.999985 2.303562
v 2.299494 16.999985 2.303562
v 2.299445 12.399984 2.303581
v 2.299445 12.399984 2.303581
v 2.300550 12.399964 -2.296418
v 2.300550 12.399964 -2.296418
v -2.300506 17.000034 2.302457
v -2.300506 17.000034 2.302457
v -2.300554 12.400033 2.302476
v -2.300554 12.400033 2.302476
v -2.299401 17.000015 -2.297543
v -2.299401 17.000015 -2.297543
v -2.299449 12.400013 -2.297523
v -2.299449 12.400013 -2.297523
vt 0.500000 0.375000
vt 0.500000 0.000000
vt 0.625000 0.000000
vt 0.437500 0.375000
vt 0.437500 0.000000
vt 0.312500 0.375000
vt 0.312500 0.000000
vt 0.562500 0.375000
vt 0.562500 0.500000
vt 0.437500 0.500000
vt 0.312500 0.500000
vt 0.125000 0.000000
vt 0.187500 0.000000
vt 0.187500 0.375000
vt 0.875000 0.375000
vt 0.875000 0.000000
vt 0.812500 0.000000
vt 0.750000 0.375000
vt 0.812500 0.375000
vt 0.187500 0.500000
vt 0.125000 0.500000
vt 0.125000 0.375000
vt 0.062500 0.375000
vt 0.062500 0.000000
vt 0.000000 0.000000
vt 0.375000 0.750000
vt 0.375000 0.500000
vt 0.500000 0.500000
vt 0.250000 0.750000
vt 0.250000 0.500000
vt 0.125000 0.750000
vt 0.375000 1.000000
vt 0.250000 1.000000
vt 0.125000 1.000000
vt 0.250000 0.375000
vt 0.250000 0.000000
vt 0.812500 0.500000
vt 0.750000 0.500000
vt 0.687500 0.375000
vt 0.000000 0.750000
vt 0.000000 0.500000
vt 0.062500 0.500000
vt 0.750000 0.000000
vt 0.687500 0.000000
vt 0.687500 0.500000
vt 0.875000 0.750000
vt 0.875000 0.500000
vt 1.000000 0.500000
vt 0.750000 0.750000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.875000 1.000000
vt 0.750000 1.000000
vt 0.625000 1.000000
vt 0.625000 0.375000
vt 0.000000 0.375000
vt 0.500000 0.750000
vt 1.000000 0.750000
s off
f 3/1 1/2 22/3
f 4/4 2/5 67/2
f 57/6 63/7 71/5
f 21/8 61/9 68/10
f 66/10 24/11 23/6
f 145/12 46/13 135/14
f 6/15 10/16 11/17
f 8/18 77/19 79/17
f 34/20 13/21 14/22
f 16/23 99/24 93/25
f 19/26 17/27 38/28
f 20/29 18/30 111/27
f 113/29 101/31 107/21
f 105/32 112/33 116/29
f 110/33 40/34 39/31
f 35/14 95/35 94/36
f 43/24 45/12 151/22
f 141/22 91/12 100/24
f 28/19 27/37 26/38
f 32/39 29/18 30/38
f 47/14 139/13 44/36
f 133/14 90/13 92/12
f 143/25 147/24 41/23
f 62/36 64/7 58/6
f 104/40 106/41 108/21
f 96/21 134/42 142/23
f 85/18 87/43 9/44
f 73/39 75/44 83/3
f 127/3 131/44 129/39
f 74/39 82/45 78/38
f 132/44 123/43 117/18
f 148/22 144/21 140/20
f 118/18 124/43 121/17
f 80/38 84/37 76/19
f 122/17 128/16 126/15
f 152/23 136/42 138/21
f 51/46 49/47 54/48
f 52/49 50/38 163/47
f 153/50 159/51 167/38
f 157/52 164/53 168/49
f 162/53 56/54 55/50
f 158/28 160/51 154/50
f 59/55 3/1 22/3
f 65/1 4/4 67/2
f 69/4 57/6 71/5
f 72/4 21/8 68/10
f 70/4 66/10 23/6
f 48/22 145/12 135/14
f 7/19 6/15 11/17
f 12/43 8/18 79/17
f 33/14 34/20 14/22
f 15/56 16/23 93/25
f 103/57 19/26 38/28
f 109/26 20/29 111/27
f 115/30 113/29 107/21
f 37/26 105/32 116/29
f 114/29 110/33 39/31
f 89/13 35/14 94/36
f 149/23 43/24 151/22
f 97/23 141/22 100/24
f 25/18 28/19 26/38
f 31/45 32/39 30/38
f 42/35 47/14 44/36
f 36/22 133/14 92/12
f 137/56 143/25 41/23
f 60/35 62/36 58/6
f 102/31 104/40 108/21
f 98/22 96/21 142/23
f 5/39 85/18 9/44
f 81/55 73/39 83/3
f 125/55 127/3 129/39
f 86/18 74/39 78/38
f 130/39 132/44 117/18
f 146/14 148/22 140/20
f 119/19 118/18 121/17
f 88/18 80/38 76/19
f 120/19 122/17 126/15
f 150/22 152/23 138/21
f 155/58 51/46 54/48
f 161/46 52/49 163/47
f 165/49 153/50 167/38
f 53/46 157/52 168/49
f 166/49 162/53 55/50
f 156/57 158/28 154/50

BIN
media/character.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
media/character.sif Normal file

Binary file not shown.

234
media/character_bs.obj Normal file
View File

@ -0,0 +1,234 @@
v -20.000000 -32.500000 9.999993
v -20.000000 -32.500000 -10.000005
v -20.000000 35.000000 9.999993
v -20.000000 35.000000 -10.000005
v -40.000000 35.000000 -10.000000
v -40.000000 35.000000 9.999999
v -20.000000 34.999985 9.999995
v -20.000000 34.999985 -10.000001
v -39.999996 -32.500008 -10.000000
v -39.999996 -32.500008 9.999999
v -20.000000 -32.500015 9.999995
v -20.000000 -32.500015 -10.000001
v -20.000000 -100.000000 9.999996
v -20.000000 -100.000000 -10.000005
v -20.000000 -32.500000 9.999996
v -20.000000 -32.500000 -10.000005
v -20.000000 35.000000 19.999996
v -20.000000 35.000000 -20.000004
v -20.000000 75.000000 19.999996
v -20.000000 75.000000 -20.000004
v 20.000000 -32.500000 -10.000002
v 20.000000 -32.500000 9.999996
v 20.000000 35.000000 -10.000002
v 20.000000 35.000000 9.999996
v 20.000000 -32.500015 -10.000001
v 20.000000 -32.500015 9.999995
v 39.999996 -32.500008 9.999999
v 39.999996 -32.500008 -10.000000
v 20.000000 34.999985 -10.000001
v 20.000000 34.999985 9.999995
v 40.000000 35.000000 9.999999
v 40.000000 35.000000 -10.000000
v 0.000001 -100.000000 -10.000005
v -0.000001 -100.000000 9.999996
v 0.000000 -32.500000 9.999996
v 0.000000 -32.500000 -10.000005
v 20.000000 35.000000 -20.000004
v 20.000000 35.000000 19.999996
v 20.000000 75.000000 -20.000004
v 20.000000 75.000000 19.999996
v 20.000000 -32.500000 -10.000005
v 20.000000 -32.500000 9.999996
v 20.000000 -100.000000 -10.000005
v 20.000000 -100.000000 9.999996
v 0.000000 -100.000000 -10.000005
v 0.000000 -100.000000 9.999996
v 0.000000 -32.500000 9.999996
v 0.000000 -32.500000 -10.000005
v -22.000000 33.029953 21.999996
v -22.000000 33.029953 -22.000006
v -22.000000 77.029892 21.999996
v -22.000000 77.029892 -22.000006
v 22.000000 33.029953 -22.000006
v 22.000000 33.029953 21.999996
v 22.000000 77.029892 -22.000006
v 22.000000 77.029892 21.999996
v -20.000000 -32.500000 13.644027
v -20.000000 35.000000 13.644027
v 20.000000 -32.500000 13.644027
v 20.000000 35.000000 13.644027
v 20.000000 35.000000 9.999999
v 20.000000 -32.500000 9.999999
v -20.000000 -32.500000 9.999999
v -20.000000 35.000000 9.999999
vt 0.500000 0.375000
vt 0.500000 0.000000
vt 0.625000 0.000000
vt 0.437500 0.375000
vt 0.437500 0.000000
vt 0.312500 0.375000
vt 0.312500 0.000000
vt 0.562500 0.375000
vt 0.562500 0.500000
vt 0.437500 0.500000
vt 0.312500 0.500000
vt 0.187500 0.000000
vt 0.125000 0.000000
vt 0.125000 0.375000
vt 0.812500 0.375000
vt 0.875000 0.375000
vt 0.875000 0.000000
vt 0.812500 0.000000
vt 0.750000 0.000000
vt 0.187500 0.500000
vt 0.125000 0.500000
vt 0.062500 0.375000
vt 0.062500 0.000000
vt 0.000000 0.000000
vt 0.375000 0.750000
vt 0.375000 0.500000
vt 0.500000 0.500000
vt 0.250000 0.750000
vt 0.250000 0.500000
vt 0.125000 0.750000
vt 0.375000 1.000000
vt 0.250000 1.000000
vt 0.125000 1.000000
vt 0.187500 0.375000
vt 0.250000 0.000000
vt 0.812500 0.500000
vt 0.750000 0.500000
vt 0.687500 0.375000
vt 0.750000 0.375000
vt 0.000000 0.500000
vt 0.062500 0.500000
vt 0.687500 0.000000
vt 0.625000 0.375000
vt 0.687500 0.500000
vt 0.875000 0.750000
vt 0.875000 0.500000
vt 1.000000 0.500000
vt 0.750000 0.750000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.875000 1.000000
vt 0.750000 1.000000
vt 0.625000 1.000000
vt 1.000000 0.000000
vt 1.000000 0.375000
vt 1.000000 0.343750
vt 0.875000 0.343750
vt 0.984375 0.000000
vt 0.984375 0.375000
vt 0.890625 0.375000
vt 0.890625 0.000000
vt 0.875000 0.031250
vt 1.000000 0.031250
vt 0.000000 0.375000
vt 0.500000 0.750000
vt 0.250000 0.375000
vt 0.000000 0.750000
vt 1.000000 0.750000
vn -0.000000 0.000000 1.000000
vn -1.000000 0.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 1.000000 0.000000 0.000000
f 3/1/1 1/2/1 22/3/1
f 4/4/2 2/5/2 1/2/2
f 23/6/3 21/7/3 2/5/3
f 21/8/4 22/9/4 1/10/4
f 3/10/5 24/11/5 23/6/5
f 45/12/2 46/13/2 47/14/2
f 7/15/1 6/16/1 10/17/1
f 7/15/6 11/18/6 12/19/6
f 34/20/4 13/21/4 14/14/4
f 16/22/2 14/23/2 13/24/2
f 19/25/1 17/26/1 38/27/1
f 20/28/2 18/29/2 17/26/2
f 39/30/3 37/21/3 18/29/3
f 38/31/4 17/32/4 18/28/4
f 19/32/5 40/33/5 39/30/5
f 15/34/1 13/12/1 34/35/1
f 43/13/3 45/23/3 48/22/3
f 36/22/3 33/23/3 14/13/3
f 28/15/4 27/36/4 26/37/4
f 32/38/5 29/39/5 30/37/5
f 46/35/1 44/12/1 42/34/1
f 35/14/6 34/13/6 33/12/6
f 44/24/6 43/23/6 41/22/6
f 22/35/6 21/7/6 23/6/6
f 38/40/6 37/21/6 39/30/6
f 15/21/5 35/41/5 36/22/5
f 8/39/3 12/19/3 9/42/3
f 5/43/2 9/3/2 10/42/2
f 27/42/6 28/3/6 32/43/6
f 5/38/5 6/44/5 7/37/5
f 28/42/3 25/19/3 29/39/3
f 43/14/4 44/21/4 46/20/4
f 25/19/2 26/18/2 30/15/2
f 11/37/4 10/36/4 9/15/4
f 30/15/1 26/18/1 27/17/1
f 48/22/5 47/41/5 42/21/5
f 51/45/1 49/46/1 54/47/1
f 52/48/2 50/37/2 49/46/2
f 55/49/3 53/50/3 50/37/3
f 54/51/4 49/52/4 50/48/4
f 51/52/5 56/53/5 55/49/5
f 54/27/6 53/50/6 55/49/6
f 58/16/1 57/17/1 59/54/1
f 62/54/3 63/17/3 64/16/3
f 60/55/5 61/56/5 64/57/5
f 59/54/6 62/58/6 61/59/6
f 58/16/2 64/60/2 63/61/2
f 57/17/4 63/62/4 62/63/4
f 24/43/1 3/1/1 22/3/1
f 3/1/2 4/4/2 1/2/2
f 4/4/3 23/6/3 2/5/3
f 2/4/4 21/8/4 1/10/4
f 4/4/5 3/10/5 23/6/5
f 48/34/2 45/12/2 47/14/2
f 11/18/1 7/15/1 10/17/1
f 8/39/6 7/15/6 12/19/6
f 33/34/4 34/20/4 14/14/4
f 15/64/2 16/22/2 13/24/2
f 40/65/1 19/25/1 38/27/1
f 19/25/2 20/28/2 17/26/2
f 20/28/3 39/30/3 18/29/3
f 37/25/4 38/31/4 18/28/4
f 20/28/5 19/32/5 39/30/5
f 35/66/1 15/34/1 34/35/1
f 41/14/3 43/13/3 48/22/3
f 16/14/3 36/22/3 14/13/3
f 25/39/4 28/15/4 26/37/4
f 31/44/5 32/38/5 30/37/5
f 47/66/1 46/35/1 42/34/1
f 36/34/6 35/14/6 33/12/6
f 42/64/6 44/24/6 41/22/6
f 24/66/6 22/35/6 23/6/6
f 40/67/6 38/40/6 39/30/6
f 16/14/5 15/21/5 36/22/5
f 5/38/3 8/39/3 9/42/3
f 6/38/2 5/43/2 10/42/2
f 31/38/6 27/42/6 32/43/6
f 8/39/5 5/38/5 7/37/5
f 32/38/3 28/42/3 29/39/3
f 45/34/4 43/14/4 46/20/4
f 29/39/2 25/19/2 30/15/2
f 12/39/4 11/37/4 9/15/4
f 31/16/1 30/15/1 27/17/1
f 41/14/5 48/22/5 42/21/5
f 56/68/1 51/45/1 54/47/1
f 51/45/2 52/48/2 49/46/2
f 52/48/3 55/49/3 50/37/3
f 53/45/4 54/51/4 50/48/4
f 52/48/5 51/52/5 55/49/5
f 56/65/6 54/27/6 55/49/6
f 60/55/1 58/16/1 59/54/1
f 61/55/3 62/54/3 64/16/3
f 58/16/5 60/55/5 64/57/5
f 60/55/6 59/54/6 61/59/6
f 57/17/2 58/16/2 63/61/2
f 59/54/4 57/17/4 62/63/4

76
media/colors.txt Normal file
View File

@ -0,0 +1,76 @@
0 128 128 128 # CONTENT_STONE
2 39 66 106 # CONTENT_WATER
3 255 255 0 # CONTENT_TORCH
9 39 66 106 # CONTENT_WATERSOURCE
e 117 86 41 # CONTENT_SIGN_WALL
f 128 79 0 # CONTENT_CHEST
15 103 78 42 # CONTENT_FENCE
1e 162 119 53 # CONTENT_RAIL
1f 154 110 40 # CONTENT_LADDER
20 255 100 0 # CONTENT_LAVA
21 255 100 0 # CONTENT_LAVASOURCE
800 107 134 51 # CONTENT_GRASS
801 86 58 31 # CONTENT_TREE
802 48 95 8 # CONTENT_LEAVES
803 102 129 38 # CONTENT_GRASS_FOOTSTEPS
804 178 178 0 # CONTENT_MESE
805 101 84 36 # CONTENT_MUD
808 104 78 42 # CONTENT_WOOD
809 210 194 156 # CONTENT_SAND
80a 123 123 123 # CONTENT_COBBLE
80b 199 199 199 # CONTENT_STEEL
80c 183 183 222 # CONTENT_GLASS
80d 219 202 178 # CONTENT_MOSSYCOBBLE
80e 70 70 70 # CONTENT_GRAVEL
80f 204 0 0 # CONTENT_SANDSTONE
810 0 215 0 # CONTENT_CACTUS
811 170 50 25 # CONTENT_BRICK
812 104 78 42 # CONTENT_CLAY
813 58 105 18 # CONTENT_PAPYRUS
814 196 160 0 # CONTENT_BOOKSHELF
815 205 190 121 # CONTENT_JUNGLETREE
816 62 101 25 # CONTENT_JUNGLEGRASS
817 255 153 255 # CONTENT_NC
818 102 50 255 # CONTENT_NC_RB
819 200 0 0 # CONTENT_APPLE
default:stone 128 128 128
default:stone_with_coal 50 50 50
default:water_flowing 39 66 106
default:torch 255 255 0
default:water_source 39 66 106
default:sign_wall 117 86 41
default:chest 128 79 0
default:fence_wood 103 78 42
default:rail 162 119 53
default:ladder 154 110 40
default:lava_flowing 255 100 0
default:lava_source 255 100 0
default:dirt_with_grass 107 134 51
default:tree 86 58 31
default:leaves 48 95 8
default:dirt_with_grass_and_footsteps 102 129 38
default:mese 178 178 0
default:dirt 101 84 36
default:wood 104 78 42
default:sand 210 194 156
default:cobble 123 123 123
default:steelblock 199 199 199
default:glass 183 183 222
default:mossycobble 219 202 178
default:gravel 70 70 70
default:sandstone 204 0 0
default:cactus 0 215 0
default:brick 170 50 25
default:clay 104 78 42
default:papyrus 58 105 18
default:bookshelf 196 160 0
default:jungletree 205 190 121
default:junglegrass 62 101 25
default:nyancat 255 153 255
default:nyancat_rainbow 102 50 255
default:apple 200 0 0
default:desert_sand 210 180 50
default:desert_stone 150 100 30
default:dry_shrub 100 80 40

BIN
media/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

BIN
media/ironman.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
media/mage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
media/mage2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,76 @@
0 128 128 128 # CONTENT_STONE
2 39 66 106 # CONTENT_WATER
3 255 255 0 # CONTENT_TORCH
9 39 66 106 # CONTENT_WATERSOURCE
e 117 86 41 # CONTENT_SIGN_WALL
f 128 79 0 # CONTENT_CHEST
15 103 78 42 # CONTENT_FENCE
1e 162 119 53 # CONTENT_RAIL
1f 154 110 40 # CONTENT_LADDER
20 255 100 0 # CONTENT_LAVA
21 255 100 0 # CONTENT_LAVASOURCE
800 107 134 51 # CONTENT_GRASS
801 86 58 31 # CONTENT_TREE
802 48 95 8 # CONTENT_LEAVES
803 102 129 38 # CONTENT_GRASS_FOOTSTEPS
804 178 178 0 # CONTENT_MESE
805 101 84 36 # CONTENT_MUD
808 104 78 42 # CONTENT_WOOD
809 210 194 156 # CONTENT_SAND
80a 123 123 123 # CONTENT_COBBLE
80b 199 199 199 # CONTENT_STEEL
80c 183 183 222 # CONTENT_GLASS
80d 219 202 178 # CONTENT_MOSSYCOBBLE
80e 70 70 70 # CONTENT_GRAVEL
80f 204 0 0 # CONTENT_SANDSTONE
810 0 215 0 # CONTENT_CACTUS
811 170 50 25 # CONTENT_BRICK
812 104 78 42 # CONTENT_CLAY
813 58 105 18 # CONTENT_PAPYRUS
814 196 160 0 # CONTENT_BOOKSHELF
815 205 190 121 # CONTENT_JUNGLETREE
816 62 101 25 # CONTENT_JUNGLEGRASS
817 255 153 255 # CONTENT_NC
818 102 50 255 # CONTENT_NC_RB
819 200 0 0 # CONTENT_APPLE
default:stone 128 128 128
default:stone_with_coal 50 50 50
default:water_flowing 39 66 106
default:torch 255 255 0
default:water_source 39 66 106
default:sign_wall 117 86 41
default:chest 128 79 0
default:fence_wood 103 78 42
default:rail 162 119 53
default:ladder 154 110 40
default:lava_flowing 255 100 0
default:lava_source 255 100 0
default:dirt_with_grass 107 134 51
default:tree 86 58 31
default:leaves 48 95 8
default:dirt_with_grass_and_footsteps 102 129 38
default:mese 178 178 0
default:dirt 101 84 36
default:wood 104 78 42
default:sand 210 194 156
default:cobble 123 123 123
default:steelblock 199 199 199
default:glass 183 183 222
default:mossycobble 219 202 178
default:gravel 70 70 70
default:sandstone 204 0 0
default:cactus 0 215 0
default:brick 170 50 25
default:clay 104 78 42
default:papyrus 58 105 18
default:bookshelf 196 160 0
default:jungletree 205 190 121
default:junglegrass 62 101 25
default:nyancat 255 153 255
default:nyancat_rainbow 102 50 255
default:apple 200 0 0
default:desert_sand 210 180 50
default:desert_stone 150 100 30
default:dry_shrub 100 80 40

4
mod.conf Normal file
View File

@ -0,0 +1,4 @@
name=voxelizer
title=Voxelizer
description=Turns 3D models into astonishing voxel builds.
depends=modlib, cmdlib

21
node_map_reader.lua Normal file
View File

@ -0,0 +1,21 @@
-- Reads a node map in a similar format as minetestmapper.txt
function read_node_map(minetestmapper_content)
local lines=string_ext.split_without_limit(minetestmapper_content, "\n")
local iterator, _, index=ipairs(lines)
local color_to_cid={}
--Process lines
index, line=iterator(lines, index)
while line do
parts=string_ext.split(line, " ",5)
if #parts >= 4 then
local c_id=tonumber(parts[1], 16)
if not c_id then c_id=minetest.get_content_id(parts[1]) end
local r, g, b=tonumber(parts[2]), tonumber(parts[3]), tonumber(parts[4])
if c_id and r and g and b and minetest.get_name_from_content_id(c_id) ~= "unknown" and minetest.get_name_from_content_id(c_id) ~= "ignore" then color_to_cid[r*256*256+g*256+b]=c_id end
end
index, line=iterator(lines, index)
end
return color_to_cid
end

61
obj_reader.lua Normal file
View File

@ -0,0 +1,61 @@
--obj_content: string, OBJ file content; triangle_consumer_factory: function(vertexes), returns triangle_consumer: function(vertexes, uvs)
-- TODO add support for colors
function read_obj(obj_content, triangle_consumer_factory)
print(obj_content)
local lines=string_ext.split_without_limit(obj_content, "\n")
local iterator, _, index=ipairs(lines)
::next_object::
-- Vertices
local vertices = {}
local counter=1
index, line=iterator(lines, index)
repeat
if string_ext.starts_with(line, "v ") then
local parts=string_ext.split(line:sub(3), " ", 4) -- x, y, z, unneeded
local x, y, z = tonumber(parts[1]), tonumber(parts[2]), tonumber(parts[3])
if x and y and z then
vertices[counter] = {x,y,z}
end
counter = counter + 1
end
index, line=iterator(lines, index)
until string_ext.starts_with(line, "vt ")
--UVs
local uvs={}
counter=1
repeat
if string_ext.starts_with(line, "vt ") then
local parts=string_ext.split(line:sub(4), " ", 3)
local x, y = tonumber(parts[1]), tonumber(parts[2])
if x and y then
uvs[counter] = {x, y}
end
counter = counter + 1
end
index, line=iterator(lines, index)
until string_ext.starts_with(line, "f ")
local triangle_consumer=triangle_consumer_factory(vertices)
--Faces (need to be triangles), polygons are ignored
repeat
if string_ext.starts_with(line, "f ") then --Face
local parts=string_ext.split(line:sub(3), " ", 4)
local verts={}
local texs={}
for i=1, 3 do
local indices = string_ext.split(parts[i], "/", 3)
local vert, uv = tonumber(indices[1]), tonumber(indices[2])
verts[i] = vertices[vert]
texs[i] = uvs[uv]
if not verts[i] or not texs[i] then goto invalid end
end
triangle_consumer(verts, texs)
::invalid::
elseif string_ext.starts_with(line, "o ") then
goto next_object
end
index, line=iterator(lines, index)
until not line
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
screenshot_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

BIN
screenshot_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
screenshot_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

51
src/FileDownloader.java Normal file
View File

@ -0,0 +1,51 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
public class FileDownloader {
// Takes URL to file and downloads it to given destination
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("URL and output path need to be given");
System.exit(1);
} else {
try {
URL in = new URL(args[0]);
File out = new File(args[1]);
if (!out.exists()) {
try {
out.createNewFile();
} catch (IOException e) {
System.out.println("Couldn't create file");
System.exit(2);
}
}
if (!out.canWrite()) {
System.out.println("Output file doesn't exist or can't be written");
System.exit(3);
}
try {
FileOutputStream outStream = new FileOutputStream(out);
try {
ReadableByteChannel download = Channels.newChannel(in.openStream());
outStream.getChannel().transferFrom(download, 0, Long.MAX_VALUE);
} catch (IOException e) {
System.out.println("Couldn't download file");
System.exit(4);
}
} catch (FileNotFoundException e) {
System.out.println("Couldn't write to file");
System.exit(5);
}
} catch (MalformedURLException e) {
System.out.println("Malformed URL");
System.exit(6);
}
}
}
}

47
src/LogoCreator.java Normal file
View File

@ -0,0 +1,47 @@
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
public class LogoCreator {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Error : Output and input path need to be given.");
System.exit(1);
} else {
File in = new File(args[0]);
File out = new File(args[1]);
int scale = args.length >=3 ? Integer.parseInt(args[2]) : 3;
if (!in.exists() || !in.canRead()) {
System.out.println("Error : Input file doesn't exist or can't be read.");
System.exit(1);
}
try {
BufferedImage input = ImageIO.read(in);
BufferedImage output = new BufferedImage(input.getWidth() * scale, input.getHeight() * scale, BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < input.getWidth(); x++) {
for (int y = 0; y < input.getHeight(); y++) {
int c = input.getRGB(x, y);
for (int x_2 = x * scale + 1; x_2 < x * scale + scale - 1; x_2++) {
for (int y_2 = y * scale + 1; y_2 < y * scale + scale - 1; y_2++) {
output.setRGB(x_2, y_2, c);
}
}
int b = new Color(c, true).brighter().getRGB();
for (int x_2 = 0; x_2 < scale; x_2 += scale-1) {
for (int y_2 = 0; y_2 < scale; y_2++) {
output.setRGB(x_2 + x*scale, y_2 + y*scale, b);
output.setRGB(y_2 + x*scale, x_2 + y*scale, b);
}
}
}
}
ImageIO.write(output, "PNG", out);
System.out.println("Texture created successfully.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,7 @@
import javax.imageio.ImageIO;
public class SupportedTextureFormats {
public static void main(String... args) {
System.out.println("The supported image file formats are : "+String.join(", ", ImageIO.getReaderFormatNames()));
}
}

62
src/TextureLoader.java Normal file
View File

@ -0,0 +1,62 @@
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class TextureLoader {
/* Takes two file paths as inputs : Input and output. Input file needs to be a supported image, and output needs to be writable. Image will be stored in output using SIF file format.*/
public static void main(String... args) {
if (args.length < 2) {
System.out.println("Output and input path need to be given");
System.exit(1);
} else {
File in = new File(args[0]);
File out = new File(args[1]);
if (!out.exists()) {
try {
out.createNewFile();
} catch (IOException e) {
System.out.println("Couldn't create output file");
System.exit(2);
}
}
if (!in.exists() || !in.canRead() || !out.canWrite()) {
System.out.println("Output or input file doesn't exist or can't be read/written");
System.exit(3);
}
try {
BufferedImage img = ImageIO.read(in);
FileOutputStream outputStream = new FileOutputStream(out);
/* Write headers */
outputStream.write(img.getWidth()/256);
outputStream.write(img.getWidth()%256);
outputStream.write(img.getHeight()/256);
outputStream.write(img.getHeight()%256);
/* Write data */
for (int y = 0; y < img.getHeight(); y++) {
for (int x = 0; x < img.getWidth(); x++) {
int color = img.getRGB(x, y);
int alpha = color >>> 24;
int red = (color & 0x00FF0000) >> 16;
int green = (color & 0x0000FF00) >> 8;
int blue = color & 0x000000FF;
outputStream.write(alpha);
outputStream.write(red);
outputStream.write(green);
outputStream.write(blue);
}
}
outputStream.close();
} catch (IOException e) {
System.out.println("File couldn't be written");
System.exit(4);
}
}
}
}

68
test.lua Normal file
View File

@ -0,0 +1,68 @@
math.randomseed(os.time())
local function random_color()
color={}
for i=1, 3 do color[i]=math.random(0, 255) end
return color
end
local function random_colors(k)
local random_colors={}
for i=1, k or 10000 do
table.insert(random_colors, random_color())
end
return random_colors
end
local function test_correctness()
local k=0
for i = 1, 1000 do
local colors=random_colors(1000)
local tree=kd_closest_color_finder(colors)
local color=random_color() --colors[math.random(1, 20)]
local linear, lin_distance=linear_closest_color_finder(colors)(color)
local kd, kd_distance=tree(color)
if lin_distance == kd_distance then
k=k+1
end
end
print(tostring(k).." of 1000 samples")
end
local function test_performance()
local color=random_color() --colors[math.random(1, 20)]
local colors=random_colors(10000)
local tree=kd_closest_color_finder(colors)
local linear=linear_closest_color_finder(colors)
for _, tree in ipairs({tree, linear}) do
local x = os.clock()
local s = 0
for i = 1, 1000 do kd, kd_distance=tree(color) end
print(string.format("elapsed time: %.2f", os.clock() - x))
end
end
print("Closest Color Finder Test : ")
test_correctness()
test_performance()
local function test_texture_reader()
image = read_texture(get_resource("voxelizer", "test/image.png"))
print("Texture Reader Test : ")
print(color_to_number(color_to_table(get_texture_color_at(image, 0, 0))) == 0x00000000)
print(color_to_number(color_to_table(get_texture_color_at(image, 1, 0))) == 0xFFFF0000)
print(color_to_number(color_to_table(get_texture_color_at(image, 0, 1))) == 0xFF00FF00)
print(color_to_number(color_to_table(get_texture_color_at(image, 1, 1))) == 0xFF0000FF)
end
test_texture_reader()
local function test_nodemap_reader()
print("Nodemap Reader Test : ")
local color_to_cid = read_node_map(file_ext.read("/usr/share/games/minetest/minetestmapper-colors.txt"))
for c, cid in pairs(color_to_cid) do
print(string.format("%x", c).." -> "..minetest.get_name_from_content_id(cid))
end
end
test_nodemap_reader()

BIN
test/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

BIN
test/image.sif Normal file

Binary file not shown.

127
texture_reader.lua Normal file
View File

@ -0,0 +1,127 @@
function rgba_number_to_table(number)
local b = number % 256
local g = math.floor(number / 256) % 256
local r = math.floor(number / 256 / 256) % 256
local a = math.floor(number / 256 / 256 / 256) % 256
return {a, r, g, b}
end
function rgb_number_to_table(number)
local b = number % 256
local g = math.floor(number / 256) % 256
local r = math.floor(number / 256 / 256) % 256
return {r, g, b}
end
function rgba_tuple_to_number(a, r, g, b)
return a*256*256*256+r*256*256+g*256+b
end
function rgba_table_to_number(table)
return rgba_tuple_to_number(unpack(table))
end
function rgb_tuple_to_number(r, g, b)
return r*256*256+g*256+b
end
function rgb_table_to_number(table)
return rgb_tuple_to_number(unpack(table))
end
function get_texture_color_at(texture, x, y)
return texture[x+y*texture.width+1]
end
function set_texture_color_at(texture, x, y, color)
texture[x+y*texture.width+1] = color
end
function in_bounds(texture, x, y)
return x >= 0 and y >= 0 and x < texture.width and x < texture.height
end
function nearest_filtering(texture, pos_uv)
local x = math.min(math.floor(pos_uv[1]*texture.width), texture.width-1)
local y = math.min(math.floor((1-pos_uv[2])*texture.height), texture.height-1)
return get_texture_color_at(texture, x, y)
end
function bilinear_filtering(texture, pos_uv)
local x = pos_uv[1]*texture.width
local x_line = number_ext.round(x)
local y = (1-pos_uv[2])*texture.height
local y_line = number_ext.round(y)
local affected, affected_alpha = 0, 0
local avg_alpha = 0
local avg = {0, 0, 0}
for xf = -1, 1, 2 do
local px = x+xf*0.5
local f1 = math.max(0, xf*x_line-px)
for xf = -1, 1, 2 do
local py = y+xf*0.5
local f2 = math.max(0, xf*y_line-py)
local factor = f1 * f2
if factor > 0 then
local a, r, g, b = unpack(get_texture_color_at(texture, math.floor(px), math.floor(py)))
affected = affected + factor * a
avg = vector.add(avg, vector.multiply({r, g, b}, factor * a))
affected_alpha = affected_alpha + factor
avg_alpha = avg_alpha + factor * a
end
end
end
avg_alpha = avg_alpha / affected_alpha
avg = vector.multiply(avg, 1 / affected)
local color = {avg_alpha, unpack(avg)}
return color
end
local errors = {
"Output and input path need to be given",
"Couldn't create output file",
"Output or input file doesn't exist or can't be read/written",
"File couldn't be written"
}
function read_texture(path_to_texture)
local last_dot
for i = path_to_texture:len(), 1, -1 do
if path_to_texture:sub(i, i) == "." then
last_dot = i
break
elseif path_to_texture:sub(i, i) == "/" then
break
end
end
local path_to_output
if path_to_texture:sub(last_dot+1):lower() == "sif" then -- we can assume it's already .sif
path_to_output = path_to_texture
else -- else, convert
if not last_dot then
path_to_output = path_to_texture..".sif"
else
path_to_output = path_to_texture:sub(1, last_dot-1)..".sif"
end
local response_code = os.execute('java -classpath "'..minetest.get_modpath("voxelizer")..'/production" TextureLoader "'..path_to_texture..'" "'..path_to_output..'"')
if response_code ~= 0 then
return errors[response_code] or "Texture couldn't be converted"
end
end
local texture_content = io.open(path_to_output, "rb")
-- Image ending : .sif (Simple Image Format)
-- 4 bytes image header (2 bytes width, 2 bytes height)
-- Content : 4 byte ARGB colors
local image={}
image.width = texture_content:read(1):byte()*255+texture_content:read(1):byte()
image.height = texture_content:read(1):byte()*255+texture_content:read(1):byte()
local bytes = texture_content:read("*all")
for i=1, bytes:len(), 4 do
table.insert(image, rgba_tuple_to_number(bytes:byte(i), bytes:byte(i+1), bytes:byte(i+2), bytes:byte(i+3)))
end
texture_content:close()
return image
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

88
vector.lua Normal file
View File

@ -0,0 +1,88 @@
vector={}
vector.subtract=function(v1, v2)
local res={}
for i=1, #v1 do
res[i] = v1[i]-v2[i]
end
return res
end
vector.add=function(v1, v2)
local res={}
for i=1, #v1 do
res[i] = v1[i]+v2[i]
end
return res
end
vector.multiply_vector=function(v1, v2)
local res={}
for i=1, #v1 do
res[i] = v1[i]*v2[i]
end
return res
end
-- Scalar multiplication
vector.multiply=function(v1, s2)
local res={}
for i=1, #v1 do
res[i] = v1[i]*s2
end
return res
end
vector.divide=function(v1, v2)
local res={}
for i=1, #v1 do
if v2[i] ~= 0 then
res[i] = v1[i]/v2[i]
else
res[i] = 0
end
end
return res
end
vector.length=function(v)
local res=0
for i=1, #v do
res = res + (v[i] * v[i])
end
return math.sqrt(res)
end
vector.floor=function(v)
local res={}
for i=1, #v do
res[i] = math.floor(v[i])
end
return res
end
vector.convert=function(v)
return {x=v[1], y=v[2], z=v[3]}
end
vector.to_minetest=vector.convert
vector.from_minetest = function(v)
return {v.x, v.y, v.z}
end
vector.clamp=function(v, min, max)
local res={}
for i=1, #v do
res[i] = math.max(min, math.min(v[i], max))
end
return res
end
vector.to_string = function(v)
local c = {}
for i=1, #v do
c[i] = number_ext.round(v[i], 100)
end
return table.concat(c, ", ")
end

13
voxelizer.iml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/production" />
<output-test url="file://$MODULE_DIR$/test/voxelizer" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>