diff options
Diffstat (limited to 'addons/voxel-core/classes/voxel_object.gd')
-rw-r--r-- | addons/voxel-core/classes/voxel_object.gd | 501 |
1 files changed, 0 insertions, 501 deletions
diff --git a/addons/voxel-core/classes/voxel_object.gd b/addons/voxel-core/classes/voxel_object.gd deleted file mode 100644 index 8d12633..0000000 --- a/addons/voxel-core/classes/voxel_object.gd +++ /dev/null @@ -1,501 +0,0 @@ -tool -extends MeshInstance -# Makeshift interface class inhereted by all voxel visualization objects. - - - -## Signals -# Emitted when VoxelSet is changed -signal set_voxel_set(voxel_set) - - - -## Enums -# Defines the modes in which Mesh can be constructed -enum MeshModes { - # Naive meshing, simple culling of voxel faces; http://web.archive.org/web/20200428085802/https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/ - NAIVE, - # Greedy meshing, culls and merges similar voxel faces; http://web.archive.org/web/20201112011204/https://www.gedge.ca/dev/2014/08/17/greedy-voxel-meshing - GREEDY, - # Marching Cubes meshing, https://en.wikipedia.org/wiki/Marching_cubes - #MARCHING_CUBES, - # Transvoxel meshing, http://web.archive.org/web/20201112033736/http://transvoxel.org/ - #TRANSVOXEL, -} - - - -## Exported Variables -# The meshing mode by which Mesh is generated -export(MeshModes) var mesh_mode := MeshModes.NAIVE setget set_mesh_mode - -# Flag indicating that UV Mapping should be applied when generating meshes if applicable -export var uv_map := false setget set_uv_map - -# Flag indicating the persitant attachment and maintenance of a StaticBody -export var static_body := false setget set_static_body - -# The VoxelSet for this VoxelObject -export(Resource) var voxel_set = null setget set_voxel_set - - - -## Public Variables -# Flag indicating that edits to voxel data will be frequent -# NOTE: When true will only allow naive meshing -var edit_hint := 0 setget set_edit_hint - - - -# Public Methods -# Sets the EditHint flag, calls update_mesh if needed and not told otherwise -func set_edit_hint(value : int, update := is_inside_tree()) -> void: - edit_hint = value - - if update: - update_mesh() - - -# Sets the mesh_mode, calls update_mesh if needed and not told otherwise -func set_mesh_mode(value : int, update := is_inside_tree()) -> void: - mesh_mode = value - - if update: - update_mesh() - - -# Sets the uv_map, calls update_mesh if needed and not told otherwise -func set_uv_map(value : bool, update := is_inside_tree()) -> void: - uv_map = value - - if update: - update_mesh() - - -# Sets static_body, calls update_static_body if needed and not told otherwise -func set_static_body(value : bool, update := is_inside_tree()) -> void: - static_body = value - - if update: - update_static_body() - - -# Sets voxel_set, calls update_mesh if needed and not told otherwise -func set_voxel_set(value : Resource, update := is_inside_tree()) -> void: - if not (typeof(value) == TYPE_NIL or value is VoxelSet): - printerr("Invalid Resource given expected VoxelSet") - return - - if is_instance_valid(voxel_set): - if voxel_set.is_connected("requested_refresh", self, "update_mesh"): - voxel_set.disconnect("requested_refresh", self, "update_mesh") - - voxel_set = value - if is_instance_valid(voxel_set): - if not voxel_set.is_connected("requested_refresh", self, "update_mesh"): - voxel_set.connect("requested_refresh", self, "update_mesh") - - if update: - update_mesh() - emit_signal("set_voxel_set", voxel_set) - - -# Return true if no voxels are present -func empty() -> bool: - return true - - -# Sets given voxel id at the given grid position -func set_voxel(grid : Vector3, voxel_id : int) -> void: - pass - - -# Replace current voxel data with given voxel data -# voxels : Dictionary<Vector3, int> : voxels to set -func set_voxels(voxels : Dictionary) -> void: - erase_voxels() - for grid in voxels: - set_voxel(grid, voxels[grid]) - - -# Returns voxel id at given grid position if present; otherwise returns -1 -func get_voxel_id(grid : Vector3) -> int: - return -1 - - -# Returns voxel Dictionary representing voxel id at given grid position -func get_voxel(grid : Vector3) -> Dictionary: - return voxel_set.get_voxel(get_voxel_id(grid)) - - -# Returns Array of all voxel grid positions -# return : Array<Vector3> : Array of Vector3 each represents a grid position of a voxel -func get_voxels() -> Array: - return [] - - -# Erase voxel id at given grid position -func erase_voxel(grid : Vector3) -> void: - pass - - -# Erase all voxels -func erase_voxels() -> void: - for grid in get_voxels(): - erase_voxel(grid) - - -# Returns 3D axis-aligned bounding box -# volume : Array<Vector3> : Array of grid positions from which to calculate bounds -# return : Dictionary : bounding box, contains: { position : Vector3, size: Vector3 } -func get_box(volume := get_voxels()) -> Dictionary: - var box := { "position": Vector3.ZERO, "size": Vector3.ZERO } - - if not volume.empty(): - box["position"] = Vector3.INF - box["size"] = -Vector3.INF - - for voxel_grid in volume: - if voxel_grid.x < box["position"].x: - box["position"].x = voxel_grid.x - if voxel_grid.y < box["position"].y: - box["position"].y = voxel_grid.y - if voxel_grid.z < box["position"].z: - box["position"].z = voxel_grid.z - - if voxel_grid.x > box["size"].x: - box["size"].x = voxel_grid.x - if voxel_grid.y > box["size"].y: - box["size"].y = voxel_grid.y - if voxel_grid.z > box["size"].z: - box["size"].z = voxel_grid.z - - box["size"] = (box["size"] - box["position"]).abs() + Vector3.ONE - - return box - - -# Moves voxels in given volume by given translation -# translation : Vector3 : translation to move voxels by -# volume : Array<Vector3> : Array of grid positions representing voxels to move -func move(translation := Vector3(), volume := get_voxels()) -> void: - var translated := {} - for voxel_grid in volume: - translated[voxel_grid + translation] = get_voxel_id(voxel_grid) - erase_voxel(voxel_grid) - for voxel_grid in translated: - set_voxel(voxel_grid, translated[voxel_grid]) - - -# Centers voxels in given volume with respect to axis origin with the given alignment -# alignment : Vector3 : Alignment to center voxels by -# volume : Array<Vector3> : Array of grid positions representing voxels to center -func center(alignment := Vector3(0.5, 0.5, 0.5), volume := get_voxels()) -> void: - move(vec_to_center(alignment, volume), volume) - - -# Flips voxels in given volume over set axis -func flip(x : bool, y : bool, z : bool, volume := get_voxels()) -> void: - var flipped := {} - for voxel_grid in volume: - flipped[Vector3( - (voxel_grid.x + (1 if z else 0)) * (-1 if z else 1), - (voxel_grid.y + (1 if y else 0)) * (-1 if y else 1), - (voxel_grid.z + (1 if x else 0)) * (-1 if x else 1))] = get_voxel_id(voxel_grid) - erase_voxel(voxel_grid) - for voxel_grid in flipped: - set_voxel(voxel_grid, flipped[voxel_grid]) - - -# Returns the translation necessary to center given volume by -# alignment : Vector3 : Alignment to center voxels by -# volume : Array<Vector3> : Array of grid positions representing voxels to center -# return : Vector3 : Translation necessary to center -func vec_to_center(alignment := Vector3(0.5, 0.5, 0.5), volume := get_voxels()) -> Vector3: - var box := get_box(volume) - alignment = Vector3.ONE - Vector3( - clamp(alignment.x, 0.0, 1.0), - clamp(alignment.y, 0.0, 1.0), - clamp(alignment.z, 0.0, 1.0)) - return -box["position"] - (box["size"] * alignment).floor() - -# A Fast Voxel Traversal Algorithm for Ray Tracing, by John Amanatides -# Algorithm paper: https://web.archive.org/web/20201108160724/http://www.cse.chalmers.se/edu/year/2010/course/TDA361/grid.pdf -# from : Vector3 : World position from which to start raycast -# direction : Vector3 : Direction of raycast -# max_distance : int : Maximum distance of ray cast -# stop : FuncRef : Calls on function, that receives "hit" and returns bool, as raycast is projected, if it returns true raycast is returned -# return : Dictionary<String, Vector3> : If voxel is "hit", returns Dictionary with grid position and face normal; else empty -func intersect_ray( - from : Vector3, - direction : Vector3, - max_distance := 64, - stop : FuncRef = null) -> Dictionary: - var hit := { - "normal": Vector3(), - } - var grid := Voxel.world_to_grid(from) - var step := Vector3( - 1 if direction.x > 0 else -1, - 1 if direction.y > 0 else -1, - 1 if direction.z > 0 else -1) - var t_delta := direction.inverse().abs() - var dist := from.distance_to(Voxel.world_to_snapped(from)) - var t_max := t_delta * dist - var step_index := -1 - - var t = 0.0 - var valid := false - while t < max_distance: - hit["position"] = grid - hit["normal"].x = -step.x if step_index == 0 else 0 - hit["normal"].y = -step.y if step_index == 1 else 0 - hit["normal"].z = -step.z if step_index == 2 else 0 - if get_voxel_id(grid) > -1 or (is_instance_valid(stop) and stop.call_func(hit)): - valid = true - break - - match t_max.min_axis(): - Vector3.AXIS_X: - grid.x += step.x - t = t_max.x - t_max.x += t_delta.x - step_index = 0 - Vector3.AXIS_Y: - grid.y += step.y - t = t_max.y - t_max.y += t_delta.y - step_index = 1 - Vector3.AXIS_Z: - grid.z += step.z - t = t_max.z - t_max.z += t_delta.z - step_index = 2 - if not valid: - hit.clear() - return hit - - -# Returns Array of all voxel grid positions connected to given target -# target : Vector3 : Grid position at which to start flood select -# selected : Array : Array to add selected voxel grid positions to -# return : Array<Vector3> : Array of all voxel grid positions connected to given target -func select_flood(target : Vector3, selected := []) -> Array: - selected.append(get_voxel_id(target)) - - for direction in Voxel.Faces: - var next = target + direction - if get_voxel_id(next) == get_voxel_id(selected[0]): - if not selected.has(next): - select_flood(next, selected) - - return selected - - -# Returns Array of all voxel grid positions connected to given target that aren't obstructed at the given face normal -# target : Vector3 : Grid position at which to start flood select -# face_normal : Vector3 : Normal of face to check for obstruction -# selected : Array : Array to add selected voxel grid positions to -# return : Array<Vector3> : Array of all voxel grid positions connected to given target -func select_face(target : Vector3, face_normal : Vector3, selected := []) -> Array: - selected.append(target) - - for direction in Voxel.Faces[face_normal]: - var next = target + direction - if get_voxel_id(next) > -1: - if get_voxel_id(next + face_normal) == -1: - if not selected.has(next): - select_face(next, face_normal, selected) - - return selected - - -# Returns Array of all voxel grid positions connected to given target that are similar and aren't obstructed at the given face normal -# target : Vector3 : Grid position at which to start flood select -# face_normal : Vector3 : Normal of face to check for obstruction -# selected : Array : Array to add selected voxel grid positions to -# return : Array<Vector3> : Array of all voxel grid positions connected to given target -func select_face_similar(target : Vector3, face_normal : Vector3, selected := []) -> Array: - selected.append(target) - - for direction in Voxel.Faces[face_normal]: - var next = target + direction - if get_voxel_id(next) == get_voxel_id(selected[0]): - if get_voxel_id(next + face_normal) == -1: - if not selected.has(next): - select_face_similar(next, face_normal, selected) - - return selected - - -# Loads and sets voxels and replaces VoxelSet with given file -# NOTE: Reference Reader.gd for valid file imports -# source_file : String : Path to file to be loaded -# new_voxel_set : bool : If true new VoxelSet is created, else overwrite current one -# return int : int : Error code -func load_file(source_file : String, new_voxel_set := true) -> int: - var read := Reader.read_file(source_file) - var error : int = read.get("error", FAILED) - if error == OK: - if new_voxel_set or not is_instance_valid(voxel_set): - set_voxel_set(VoxelSet.new(), false) - voxel_set.set_voxels(read["palette"]) - - set_voxels(read["voxels"]) - return error - - -# Makes a naive mesh out of volume of voxels given -# volume : Array<Vector3> : Array of grid positions representing volume of voxels from which to buid ArrayMesh -# vt : VoxelTool : VoxelTool with which ArrayMesh will be built -# return : ArrayMesh : Naive voxel mesh -func naive_volume(volume : Array, vt := VoxelTool.new()) -> ArrayMesh: - if not is_instance_valid(voxel_set): - return null - - vt.begin(voxel_set, uv_map) - - for position in volume: - for direction in Voxel.Faces: - if get_voxel_id(position + direction) == -1: - vt.add_face(get_voxel(position), direction, position) - - return vt.commit() - - -# Greedy meshing -# volume : Array<Vector3> : Array of grid positions representing volume of voxels from which to buid ArrayMesh -# vt : VoxelTool : VoxelTool with which ArrayMesh will be built -# return : ArrayMesh : Greedy voxel mesh -func greed_volume(volume : Array, vt := VoxelTool.new()) -> ArrayMesh: - if not is_instance_valid(voxel_set): - return null - - vt.begin(voxel_set, uv_map) - - var faces = Voxel.Faces.duplicate() - for face in faces: - faces[face] = [] - for position in volume: - if get_voxel_id(position + face) == -1: - faces[face].append(position) - - for face in faces: - while not faces[face].empty(): - var bottom_right : Vector3 = faces[face].pop_front() - var bottom_left : Vector3 = bottom_right - var top_right : Vector3 = bottom_right - var top_left : Vector3 = bottom_right - var voxel : Dictionary = get_voxel(bottom_right) - - - if not uv_map or Voxel.get_face_uv(voxel, face) == -Vector2.ONE: - var width := 1 - - while true: - var index = faces[face].find(top_right + Voxel.Faces[face][1]) - if index > -1: - var _voxel = get_voxel(faces[face][index]) - if Voxel.get_face_color(_voxel, face) == Voxel.get_face_color(voxel, face) and (not uv_map or Voxel.get_face_uv(_voxel, face) == -Vector2.ONE): - width += 1 - faces[face].remove(index) - top_right += Voxel.Faces[face][1] - bottom_right += Voxel.Faces[face][1] - else: - break - else: - break - - while true: - var index = faces[face].find(top_left + Voxel.Faces[face][0]) - if index > -1: - var _voxel = get_voxel(faces[face][index]) - if Voxel.get_face_color(_voxel, face) == Voxel.get_face_color(voxel, face) and (not uv_map or Voxel.get_face_uv(_voxel, face) == -Vector2.ONE): - width += 1 - faces[face].remove(index) - top_left += Voxel.Faces[face][0] - bottom_left += Voxel.Faces[face][0] - else: - break - else: - break - - while true: - var used := [] - var current := top_right - var index = faces[face].find(current + Voxel.Faces[face][3]) - if index > -1: - var _voxel = get_voxel(faces[face][index]) - if Voxel.get_face_color(_voxel, face) == Voxel.get_face_color(voxel, face) and (not uv_map or Voxel.get_face_uv(_voxel, face) == -Vector2.ONE): - current += Voxel.Faces[face][3] - used.append(current) - while true: - index = faces[face].find(current + Voxel.Faces[face][0]) - if index > -1: - _voxel = get_voxel(faces[face][index]) - if Voxel.get_face_color(_voxel, face) == Voxel.get_face_color(voxel, face) and (not uv_map or Voxel.get_face_uv(_voxel, face) == -Vector2.ONE): - current += Voxel.Faces[face][0] - used.append(current) - else: - break - else: - break - if used.size() == width: - top_right += Voxel.Faces[face][3] - top_left += Voxel.Faces[face][3] - for use in used: - faces[face].erase(use) - else: - break - else: - break - else: - break - - while true: - var used := [] - var current := bottom_right - var index = faces[face].find(current + Voxel.Faces[face][2]) - if index > -1: - var _voxel = get_voxel(faces[face][index]) - if Voxel.get_face_color(_voxel, face) == Voxel.get_face_color(voxel, face) and (not uv_map or Voxel.get_face_uv(_voxel, face) == -Vector2.ONE): - current += Voxel.Faces[face][2] - used.append(current) - while true: - index = faces[face].find(current + Voxel.Faces[face][0]) - if index > -1: - _voxel = get_voxel(faces[face][index]) - if Voxel.get_face_color(_voxel, face) == Voxel.get_face_color(voxel, face) and (not uv_map or Voxel.get_face_uv(_voxel, face) == -Vector2.ONE): - current += Voxel.Faces[face][0] - used.append(current) - else: - break - else: - break - if used.size() == width: - bottom_right += Voxel.Faces[face][2] - bottom_left += Voxel.Faces[face][2] - for use in used: - faces[face].erase(use) - else: - break - else: - break - else: - break - - vt.add_face(voxel,face, - bottom_right, bottom_left, top_right, top_left) - - return vt.commit() - - -# Updates Mesh and calls on save and update_static_body if needed -# save : bool : Save voxels on update -func update_mesh() -> void: - update_static_body() - - -# Sets and updates StaticMesh if demanded -func update_static_body() -> void: - pass |