Interior Maps #13
							
								
								
									
										
											BIN
										
									
								
								map_editor.scn
									 (Stored with Git LFS)
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								map_editor.scn
									 (Stored with Git LFS)
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								map_editor/camera_boundary_mesh.res
									 (Stored with Git LFS)
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								map_editor/camera_boundary_mesh.res
									 (Stored with Git LFS)
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										189
									
								
								mesh_grid.gd
									
									
									
									
									
								
							
							
						
						
									
										189
									
								
								mesh_grid.gd
									
									
									
									
									
								
							| @ -1,25 +1,78 @@ | ||||
| class_name MeshGrid extends Node3D | ||||
| 
 | ||||
| const _CELL_COUNT := 8 | ||||
| const _CHUNK_SIZE := 32 | ||||
| 
 | ||||
| class _Chunk: | ||||
| 	var multimesh_instances: Array[_MultimeshInstance] = [] | ||||
| const _GRID_ORIGIN := Vector2(0.5, 0.5) | ||||
| 
 | ||||
| ## | ||||
| ## | ||||
| ## | ||||
| class Chunk: | ||||
| 	var multimesh_instances: Array[MultiMeshInstance] = [] | ||||
| 
 | ||||
| 	var meshes: Array[Mesh] = [] | ||||
| 
 | ||||
| 	func _init() -> void: | ||||
| 		self.meshes.resize(_CELL_COUNT * _CELL_COUNT) | ||||
| 		meshes.resize(_CHUNK_SIZE * _CHUNK_SIZE) | ||||
| 
 | ||||
| class _MultimeshInstance: | ||||
| 	## | ||||
| 	## | ||||
| 	## | ||||
| 	func invalidate(mesh_grid: MeshGrid, coordinate: Vector2i) -> void: | ||||
| 		# TODO: Once this is all lowered into native code, look for ways to parallelize the loops. | ||||
| 		for multimesh_instance in multimesh_instances: | ||||
| 			RenderingServer.free_rid(multimesh_instance._instance_rid) | ||||
| 			RenderingServer.free_rid(multimesh_instance._multimesh_rid) | ||||
| 
 | ||||
| 		multimesh_instances.clear() | ||||
| 		 | ||||
| 		# Normalize mesh instance data for the chunk. | ||||
| 		var transforms_by_mesh := {} | ||||
| 		var grid_size := mesh_grid.size | ||||
| 
 | ||||
| 		for i in meshes.size(): | ||||
| 			var mesh := meshes[i] | ||||
| 
 | ||||
| 			if mesh != null: | ||||
| 				if not(mesh in transforms_by_mesh): | ||||
| 					transforms_by_mesh[mesh] = [] | ||||
| 
 | ||||
| 				transforms_by_mesh[mesh].append(Transform3D(Basis.IDENTITY, Vector3( | ||||
| 					(float(coordinate.x * _CHUNK_SIZE) + (i % _CHUNK_SIZE)) - | ||||
| 						(float(grid_size.x) * _GRID_ORIGIN.x), 0.0, | ||||
| 							(float(coordinate.y * _CHUNK_SIZE) + (i / _CHUNK_SIZE)) -  | ||||
| 								(float(grid_size.y) * _GRID_ORIGIN.y)))) | ||||
| 
 | ||||
| 		# Bake into multimesh instances for the chunk. | ||||
| 		var scenario_rid := mesh_grid.get_world_3d().scenario | ||||
| 		var global_transform := mesh_grid.global_transform | ||||
| 
 | ||||
| 		for chunk_mesh in transforms_by_mesh: | ||||
| 			var multimesh_instance := MultiMeshInstance.new( | ||||
| 				scenario_rid, chunk_mesh.get_rid(), transforms_by_mesh[chunk_mesh]) | ||||
| 
 | ||||
| 			multimesh_instance.set_offset(global_transform) | ||||
| 			multimesh_instances.append(multimesh_instance) | ||||
| 
 | ||||
| 	## | ||||
| 	## | ||||
| 	## | ||||
| 	func set_mesh(coordinate: Vector2i, mesh: Mesh) -> void: | ||||
| 		meshes[(_CHUNK_SIZE * coordinate.y) + coordinate.x] = mesh | ||||
| 
 | ||||
| ## | ||||
| ## | ||||
| ## | ||||
| class MultiMeshInstance: | ||||
| 	var _instance_rid := RID() | ||||
| 
 | ||||
| 	var _multimesh_rid := RID() | ||||
| 
 | ||||
| 	func _init(scenario_rid: RID, mesh: Mesh, transforms: Array) -> void: | ||||
| 	func _init(scenario_rid: RID, mesh_rid: RID, transforms: Array) -> void: | ||||
| 		_multimesh_rid = RenderingServer.multimesh_create() | ||||
| 		_instance_rid = RenderingServer.instance_create2(_multimesh_rid, scenario_rid) | ||||
| 
 | ||||
| 		RenderingServer.multimesh_set_mesh(_multimesh_rid, mesh.get_rid()) | ||||
| 		RenderingServer.multimesh_set_mesh(_multimesh_rid, mesh_rid) | ||||
| 
 | ||||
| 		var transform_count := transforms.size() | ||||
| 
 | ||||
| @ -29,88 +82,94 @@ class _MultimeshInstance: | ||||
| 		for i in transform_count: | ||||
| 			RenderingServer.multimesh_instance_set_transform(_multimesh_rid, i, transforms[i]) | ||||
| 
 | ||||
| var _chunks: Array[_Chunk] = [] | ||||
| 	## | ||||
| 	## | ||||
| 	## | ||||
| 	func set_offset(offset: Transform3D) -> void: | ||||
| 		RenderingServer.instance_set_transform(_instance_rid, offset) | ||||
| 
 | ||||
| var _grid_origin := Vector2(0.5, 0.5) | ||||
| var _chunks: Array[Chunk] = [] | ||||
| 
 | ||||
| var _chunks_size := Vector2i.ZERO | ||||
| 
 | ||||
| ## | ||||
| ## The number of horizontal and vertical cells in the current grid. | ||||
| ## | ||||
| ## Setting this value will result in the current grid data being completely overwritten to return | ||||
| ## it to a sensible default. | ||||
| ## | ||||
| @export | ||||
| var size: Vector2i: | ||||
| 	set(value): | ||||
| 		self._chunks.resize(int(ceil((value.x * value.y) / float(_CELL_COUNT)))) | ||||
| 		var chunk_size_factor := float(_CHUNK_SIZE) | ||||
| 
 | ||||
| 		for i in self._chunks.size(): | ||||
| 			self._chunks[i] = _Chunk.new() | ||||
| 		_chunks.resize(int(ceilf(value.x / chunk_size_factor) * ceilf(value.y / chunk_size_factor))) | ||||
| 
 | ||||
| 		for i in _chunks.size(): | ||||
| 			_chunks[i] = Chunk.new() | ||||
| 
 | ||||
| 		_chunks_size = Vector2i((value / float(_CHUNK_SIZE)).ceil()) | ||||
| 		size = value | ||||
| 
 | ||||
| func _get_chunk(chunk_coordinate: Vector2i) -> Chunk: | ||||
| 	return _chunks[(_chunks_size.x * chunk_coordinate.y) + chunk_coordinate.x] | ||||
| 
 | ||||
| func _notification(what: int) -> void: | ||||
| 	match what: | ||||
| 		NOTIFICATION_TRANSFORM_CHANGED: | ||||
| 			for chunk in _chunks: | ||||
| 				for multimesh_instance in chunk.multimesh_instances: | ||||
| 					RenderingServer.instance_set_transform( | ||||
| 						multimesh_instance._instance_rid, multimesh_instance.global_transform) | ||||
| 					multimesh_instance.set_offset_transform(global_transform) | ||||
| 
 | ||||
| ## | ||||
| ## Sets the cell at [code]coordinate[/code] to display [code]mesh[/code]. | ||||
| ## | ||||
| ## Note that this function assumes [code]coordinate[/code] is within the bounds of the grid | ||||
| ## coordinate space. | ||||
| ## | ||||
| ## Note that changes to a cell result in the chunk it resides in being completely regenerated as | ||||
| ## part of its modification, and therefore has an implicitly higher overhead to other setter | ||||
| ## operations defined by [MeshGrid]. | ||||
| func clear_mesh(mesh: Mesh) -> void: | ||||
| 	for y in size.y: | ||||
| 		for x in size.x: | ||||
| 			var coordinate := Vector2i(x, y) | ||||
| 
 | ||||
| 			_get_chunk(coordinate / _CHUNK_SIZE).set_mesh(coordinate % _CHUNK_SIZE, mesh) | ||||
| 
 | ||||
| 	for y in _chunks_size.y: | ||||
| 		for x in _chunks_size.x: | ||||
| 			var chunk_coordinate := Vector2i(x, y) | ||||
| 
 | ||||
| 			_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate) | ||||
| 
 | ||||
| ## | ||||
| func set_mesh(coordinate: Vector2i, mesh: Mesh) -> void: | ||||
| ## | ||||
| ## | ||||
| func fill_mesh(area: Rect2i, mesh: Mesh) -> void: | ||||
| 	assert(Rect2i(Vector2i.ZERO, size).encloses(area), "area must be within grid") | ||||
| 
 | ||||
| 	var filled_chunks := BitMap.new() | ||||
| 
 | ||||
| 	filled_chunks.resize(_chunks_size) | ||||
| 
 | ||||
| 	for y in range(area.position.y, area.end.y): | ||||
| 		for x in range(area.position.x, area.end.x): | ||||
| 			var coordinate := Vector2i(x, y) | ||||
| 			var chunk_coordinate := coordinate / _CHUNK_SIZE | ||||
| 
 | ||||
| 			_get_chunk(chunk_coordinate).set_mesh(coordinate % _CHUNK_SIZE, mesh) | ||||
| 			filled_chunks.set_bitv(chunk_coordinate, true) | ||||
| 
 | ||||
| 	for y in _chunks_size.y: | ||||
| 		for x in _chunks_size.x: | ||||
| 			if filled_chunks.get_bit(x, y): | ||||
| 				var chunk_coordinate := Vector2i(x, y) | ||||
| 
 | ||||
| 				_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate) | ||||
| 
 | ||||
| ## | ||||
| ## | ||||
| ## | ||||
| func plot_mesh(coordinate: Vector2i, mesh: Mesh) -> void: | ||||
| 	assert(Rect2i(Vector2i.ZERO, size).has_point(coordinate), "coordinate must be within grid") | ||||
| 
 | ||||
| 	# TODO: Once this is all lowered into native code, look for ways to parallelize the loops. | ||||
| 	var chunk_coordinate := coordinate / _CELL_COUNT | ||||
| 	var cell_coordinate := coordinate % _CELL_COUNT | ||||
| 	var chunk := _chunks[(size.x * chunk_coordinate.y) + chunk_coordinate.x] | ||||
| 	var chunk_meshes := chunk.meshes | ||||
| 	var chunk_coordinate := coordinate / _CHUNK_SIZE | ||||
| 	var chunk := _get_chunk(chunk_coordinate) | ||||
| 
 | ||||
| 	chunk_meshes[(_CELL_COUNT * cell_coordinate.y) + cell_coordinate.x] = mesh | ||||
| 
 | ||||
| 	# Invalide any existing baked multimeshes in the chunk. | ||||
| 	for multimesh_instance in chunk.multimesh_instances: | ||||
| 		RenderingServer.free_rid(multimesh_instance._instance_rid) | ||||
| 		RenderingServer.free_rid(multimesh_instance._multimesh_rid) | ||||
| 
 | ||||
| 	chunk.multimesh_instances.clear() | ||||
| 
 | ||||
| 	# Normalize mesh instance data for the chunk. | ||||
| 	var mesh_transform_sets := {} | ||||
| 
 | ||||
| 	for i in chunk_meshes.size(): | ||||
| 		var chunk_mesh := chunk_meshes[i] | ||||
| 
 | ||||
| 		if chunk_mesh != null: | ||||
| 			if not(chunk_mesh in mesh_transform_sets): | ||||
| 				mesh_transform_sets[chunk_mesh] = [] | ||||
| 
 | ||||
| 			mesh_transform_sets[chunk_mesh].push_back(Transform3D(Basis.IDENTITY, Vector3( | ||||
| 				(float(chunk_coordinate.x * _CELL_COUNT) + (i % _CELL_COUNT)) - | ||||
| 					(float(size.x) * _grid_origin.x), 0.0, | ||||
| 						(float(chunk_coordinate.y * _CELL_COUNT) + (i / _CELL_COUNT)) -  | ||||
| 							(float(size.y) * _grid_origin.y)))) | ||||
| 
 | ||||
| 	# Bake into multimesh instances for the chunk. | ||||
| 	var scenario_rid := get_world_3d().scenario | ||||
| 
 | ||||
| 	for chunk_mesh in mesh_transform_sets: | ||||
| 		var multimesh_instance :=\ | ||||
| 			_MultimeshInstance.new(scenario_rid, chunk_mesh, mesh_transform_sets[chunk_mesh]) | ||||
| 
 | ||||
| 		RenderingServer.instance_set_transform(multimesh_instance._instance_rid, global_transform) | ||||
| 
 | ||||
| 		chunk.multimesh_instances.push_back(multimesh_instance) | ||||
| 	chunk.set_mesh(coordinate % _CHUNK_SIZE, mesh) | ||||
| 	chunk.invalidate(self, chunk_coordinate) | ||||
| 
 | ||||
| ## | ||||
| ## Returns [code]world_position[/code] converted into a coordinate aligned with the [MeshGrid]. | ||||
| @ -119,4 +178,4 @@ func set_mesh(coordinate: Vector2i, mesh: Mesh) -> void: | ||||
| ## coordinates outside of the [MeshGrid] bounds as well. | ||||
| ## | ||||
| func world_to_grid(world_position: Vector2) -> Vector2i: | ||||
| 	return Vector2i((world_position + (Vector2(size) * _grid_origin)).floor()) | ||||
| 	return Vector2i((world_position + (Vector2(size) * _GRID_ORIGIN)).floor()) | ||||
|  | ||||
| @ -23,6 +23,7 @@ LocalPlayer="*res://local_player.scn" | ||||
| 
 | ||||
| [debug] | ||||
| 
 | ||||
| gdscript/warnings/integer_division=0 | ||||
| gdscript/warnings/assert_always_true=0 | ||||
| gdscript/warnings/assert_always_false=0 | ||||
| 
 | ||||
| @ -80,6 +81,10 @@ editor_paint={ | ||||
| ] | ||||
| } | ||||
| 
 | ||||
| [physics] | ||||
| 
 | ||||
| common/physics_ticks_per_second=30 | ||||
| 
 | ||||
| [rendering] | ||||
| 
 | ||||
| lights_and_shadows/directional_shadow/soft_shadow_filter_quality=3 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user