aboutsummaryrefslogtreecommitdiff
path: root/assets/addons/voxel-core/classes/voxel_set.gd
blob: d0bccacaf5a2bc9244bb97a232edfd67778ca180 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
tool
class_name VoxelSet, "res://addons/voxel-core/assets/classes/voxel_set.png"
extends Resource
# Library of Voxels used by VoxelObjects.



## Signals
# Emitted on request_refresh
signal requested_refresh



## Exported Variables
# Size of each tile in tiles in pixels
export var tile_size := Vector2(32.0, 32.0) setget set_tile_size

# Texture used for tiles / uv mapping
export var tiles : Texture = null setget set_tiles

# Materials used by voxels
export(Array, Material) var materials := [
	SpatialMaterial.new(),
] setget set_materials



## Private Variables
# Voxels stored by their id
var _voxels := []

# Flag indicating whether _uv_scale, tile_size and tiles texture is set
var _uv_ready := false

# World UV Scale, calculated on request_refresh
var _uv_scale := Vector2.ONE



## Built-In Virtual Methods
func _get(property : String):
	if property == "VOXELS":
		return _voxels


func _set(property : String, value) -> bool:
	if property == "VOXELS":
		if typeof(value) == TYPE_DICTIONARY:
			for key in value:
				var voxel : Dictionary = value[key]
				if voxel.has("vsn"):
					Voxel.set_name(voxel, voxel["vsn"])
					voxel.erase("vsn")
				_voxels.append(value[key])
		else:
			_voxels = value
		return true
	return false


func _get_property_list():
	var properties = []
	
	properties.append({
		"name": "VOXELS",
		"type": TYPE_ARRAY,
		"hint": PROPERTY_HINT_NONE,
		"usage": PROPERTY_USAGE_STORAGE,
	})
	
	return properties



## Public Methods
# Sets tile_size, calls on request_refresh by default
func set_tile_size(value : Vector2, refresh := true) -> void:
	tile_size = Vector2(
			floor(clamp(value.x, 1, 256)),
			floor(clamp(value.y, 1, 256)))
	
	if refresh:
		request_refresh()


# Sets tiles, calls on request_refresh by default
func set_tiles(value : Texture, refresh := true) -> void:
	tiles = value
	
	if refresh:
		request_refresh()


# Sets materials used by voxels in VoxelSet
func set_materials(values : Array, refresh := true) -> void:
	for index in range(values.size()):
		var material = values[index]
		if is_instance_valid(material) and not (material is SpatialMaterial or material is ShaderMaterial):
			printerr("VoxelSet : Expected Spatial or Shader material got " + str(material))
			values[index] = null
	
	if values.empty():
		materials.resize(1)
	else:
		materials = values
	property_list_changed_notify()
	
	if refresh:
		request_refresh()


# Returns VoxelSet material with id if present, otherwise returns null
func get_material(id : int) -> Material:
	return materials[id] if id > -1 and id < materials.size() else null


# Returns number of voxels in VoxelSet
func size() -> int:
	return _voxels.size()


# Returns true if VoxelSet has no voxels
func empty() -> bool:
	return _voxels.empty()


# Returns true if VoxelSet has voxel with given id
func has_id(id : int) -> bool:
	return id > -1 and id < _voxels.size()


# Returns true if VoxelSet has everything necessary for uv mapping
func uv_ready() -> bool:
	return _uv_ready


# Returns the uv scale
func uv_scale() -> Vector2:
	return _uv_scale


# Returns true if given id is valid
static func is_valid_id(id : int) -> bool:
	return id > -1


# Returns a list of all the voxel ids
# returns   :   Array<int>   :   contained voxel ids
func get_ids() -> Array:
	return range(_voxels.size())


# Returns name associated with the given id, returns a empty string if id isn't found
func id_to_name(id : int) -> String:
	return Voxel.get_name(get_voxel(id))


# Returns the id of the voxel with the given name, returns -1 if not found
func name_to_id(name : String) -> int:
	name = name.to_lower()
	for id in get_ids():
		if id_to_name(id) == name:
			return id
	return -1


# Appends voxel to end of VoxelSet
func add_voxel(voxel : Dictionary) -> void:
	_voxels.append(voxel)


# Sets the voxel at given id
func set_voxel(id : int, voxel : Dictionary) -> void:
	if not has_id(id):
		printerr("VoxelSet : given id `" + str(id) + "` is out of range")
		return
	
	_voxels[id] = voxel


# Insert voxel with given id
func insert_voxel(id : int, voxel : Dictionary) -> void:
	if id < -1 and id > _voxels.size():
		printerr("VoxelSet : given id `" + str(id) + "` is out of range")
		return
	
	_voxels.insert(id, voxel)


# Replaces all _voxels
func set_voxels(voxels : Array) -> void:
	_voxels = voxels


# Gets voxel Dictionary by their id, returns an empty Dictionary if not found
func get_voxel(id : int) -> Dictionary:
	return _voxels[id] if has_id(id) else {}


# Erase voxel from VoxelSet
func erase_voxel(id : int) -> void:
	_voxels.remove(id)


# Erases all voxels in VoxelSet
func erase_voxels() -> void:
	_voxels.clear()


# Should be called when noticable changes have been committed to voxels.
# Emits requested_refresh, calculates _uv_scale and updates _uv_ready
func request_refresh() -> void:
	_uv_ready = is_instance_valid(tiles)
	if _uv_ready:
		_uv_scale = Vector2.ONE / (tiles.get_size() / tile_size)
	else:
		_uv_scale = Vector2.ONE
	emit_signal("requested_refresh")


# Loads file's content as voxels
# NOTE: Reference Reader.gd for valid file imports
# source_file   :   String   :   Path to file to be loaded
# return int    :   int      :   Error code
func load_file(source_file : String, append := false) -> int:
	var read := Reader.read_file(source_file)
	var error : int = read.get("error", FAILED)
	if error == OK:
		if append:
			for voxel in read["palette"]:
				add_voxel(voxel)
		else:
			set_voxels(read["palette"])
	return error