Clean up MeshGrid logic
This commit is contained in:
parent
42c3a53b24
commit
499cfe84ef
BIN
map_editor.scn (Stored with Git LFS)
BIN
map_editor.scn (Stored with Git LFS)
Binary file not shown.
76
mesh_grid.gd
76
mesh_grid.gd
|
@ -5,33 +5,33 @@ const _CHUNK_SIZE := 32
|
||||||
const _GRID_ORIGIN := Vector2(0.5, 0.5)
|
const _GRID_ORIGIN := Vector2(0.5, 0.5)
|
||||||
|
|
||||||
##
|
##
|
||||||
##
|
## Baked block of meshes making up a segment of the grid.
|
||||||
##
|
##
|
||||||
class Chunk:
|
class Chunk:
|
||||||
var multimesh_instances: Array[MultiMeshInstance] = []
|
var _multimesh_instances: Array[MultiMeshInstance] = []
|
||||||
|
|
||||||
var meshes: Array[Mesh] = []
|
var _meshes: Array[Mesh] = []
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
meshes.resize(_CHUNK_SIZE * _CHUNK_SIZE)
|
_meshes.resize(_CHUNK_SIZE * _CHUNK_SIZE)
|
||||||
|
|
||||||
##
|
##
|
||||||
##
|
## Invalidates the mesh block, re-baking its contents from the current mesh data set.
|
||||||
##
|
##
|
||||||
func invalidate(mesh_grid: MeshGrid, coordinate: Vector2i) -> void:
|
func invalidate(mesh_grid: MeshGrid, coordinate: Vector2i) -> void:
|
||||||
# TODO: Once this is all lowered into native code, look for ways to parallelize the loops.
|
# TODO: Once this is all lowered into native code, look for ways to parallelize the loops.
|
||||||
for multimesh_instance in multimesh_instances:
|
for multimesh_instance in _multimesh_instances:
|
||||||
RenderingServer.free_rid(multimesh_instance._instance_rid)
|
RenderingServer.free_rid(multimesh_instance._instance_rid)
|
||||||
RenderingServer.free_rid(multimesh_instance._multimesh_rid)
|
RenderingServer.free_rid(multimesh_instance._multimesh_rid)
|
||||||
|
|
||||||
multimesh_instances.clear()
|
_multimesh_instances.clear()
|
||||||
|
|
||||||
# Normalize mesh instance data for the chunk.
|
# Normalize mesh instance data for the chunk.
|
||||||
var transforms_by_mesh := {}
|
var transforms_by_mesh := {}
|
||||||
var grid_size := mesh_grid.size
|
var grid_size := mesh_grid.size
|
||||||
|
|
||||||
for i in meshes.size():
|
for i in _meshes.size():
|
||||||
var mesh := meshes[i]
|
var mesh := _meshes[i]
|
||||||
|
|
||||||
if mesh != null:
|
if mesh != null:
|
||||||
if not(mesh in transforms_by_mesh):
|
if not(mesh in transforms_by_mesh):
|
||||||
|
@ -43,7 +43,7 @@ class Chunk:
|
||||||
(float(coordinate.y * _CHUNK_SIZE) + (i / _CHUNK_SIZE)) -
|
(float(coordinate.y * _CHUNK_SIZE) + (i / _CHUNK_SIZE)) -
|
||||||
(float(grid_size.y) * _GRID_ORIGIN.y))))
|
(float(grid_size.y) * _GRID_ORIGIN.y))))
|
||||||
|
|
||||||
# Bake into multimesh instances for the chunk.
|
# (Re)-bake into multimesh instances for the chunk.
|
||||||
var scenario_rid := mesh_grid.get_world_3d().scenario
|
var scenario_rid := mesh_grid.get_world_3d().scenario
|
||||||
var global_transform := mesh_grid.global_transform
|
var global_transform := mesh_grid.global_transform
|
||||||
|
|
||||||
|
@ -52,16 +52,20 @@ class Chunk:
|
||||||
scenario_rid, chunk_mesh.get_rid(), transforms_by_mesh[chunk_mesh])
|
scenario_rid, chunk_mesh.get_rid(), transforms_by_mesh[chunk_mesh])
|
||||||
|
|
||||||
multimesh_instance.set_offset(global_transform)
|
multimesh_instance.set_offset(global_transform)
|
||||||
multimesh_instances.append(multimesh_instance)
|
_multimesh_instances.append(multimesh_instance)
|
||||||
|
|
||||||
##
|
##
|
||||||
|
## Sets the mesh location in the chunk at the relative [code]coordinatess[/code] to
|
||||||
|
## [code]mesh[/code].
|
||||||
##
|
##
|
||||||
|
## *Note* that [method Chunk.invalidate] must be called at some point after to visually update
|
||||||
|
## the chunk.
|
||||||
##
|
##
|
||||||
func set_mesh(coordinate: Vector2i, mesh: Mesh) -> void:
|
func set_mesh(coordinates: Vector2i, mesh: Mesh) -> void:
|
||||||
meshes[(_CHUNK_SIZE * coordinate.y) + coordinate.x] = mesh
|
_meshes[(_CHUNK_SIZE * coordinates.y) + coordinates.x] = mesh
|
||||||
|
|
||||||
##
|
##
|
||||||
##
|
## Specialized multi-mesh instance convenience for use within the mesh grid and its chunks.
|
||||||
##
|
##
|
||||||
class MultiMeshInstance:
|
class MultiMeshInstance:
|
||||||
var _instance_rid := RID()
|
var _instance_rid := RID()
|
||||||
|
@ -83,7 +87,7 @@ class MultiMeshInstance:
|
||||||
RenderingServer.multimesh_instance_set_transform(_multimesh_rid, i, transforms[i])
|
RenderingServer.multimesh_instance_set_transform(_multimesh_rid, i, transforms[i])
|
||||||
|
|
||||||
##
|
##
|
||||||
##
|
## Sets the parent transform of all mesh instances under it to [code]offset[/code].
|
||||||
##
|
##
|
||||||
func set_offset(offset: Transform3D) -> void:
|
func set_offset(offset: Transform3D) -> void:
|
||||||
RenderingServer.instance_set_transform(_instance_rid, offset)
|
RenderingServer.instance_set_transform(_instance_rid, offset)
|
||||||
|
@ -93,7 +97,7 @@ var _chunks: Array[Chunk] = []
|
||||||
var _chunks_size := Vector2i.ZERO
|
var _chunks_size := Vector2i.ZERO
|
||||||
|
|
||||||
##
|
##
|
||||||
##
|
## Size of the mesh grid (in engine units).
|
||||||
##
|
##
|
||||||
@export
|
@export
|
||||||
var size: Vector2i:
|
var size: Vector2i:
|
||||||
|
@ -108,6 +112,9 @@ var size: Vector2i:
|
||||||
_chunks_size = Vector2i((value / float(_CHUNK_SIZE)).ceil())
|
_chunks_size = Vector2i((value / float(_CHUNK_SIZE)).ceil())
|
||||||
size = value
|
size = value
|
||||||
|
|
||||||
|
func _get_area() -> Rect2i:
|
||||||
|
return Rect2i(Vector2i.ZERO, size)
|
||||||
|
|
||||||
func _get_chunk(chunk_coordinate: Vector2i) -> Chunk:
|
func _get_chunk(chunk_coordinate: Vector2i) -> Chunk:
|
||||||
return _chunks[(_chunks_size.x * chunk_coordinate.y) + chunk_coordinate.x]
|
return _chunks[(_chunks_size.x * chunk_coordinate.y) + chunk_coordinate.x]
|
||||||
|
|
||||||
|
@ -115,11 +122,15 @@ func _notification(what: int) -> void:
|
||||||
match what:
|
match what:
|
||||||
NOTIFICATION_TRANSFORM_CHANGED:
|
NOTIFICATION_TRANSFORM_CHANGED:
|
||||||
for chunk in _chunks:
|
for chunk in _chunks:
|
||||||
for multimesh_instance in chunk.multimesh_instances:
|
for multimesh_instance in chunk._multimesh_instances:
|
||||||
multimesh_instance.set_offset_transform(global_transform)
|
multimesh_instance.set_offset_transform(global_transform)
|
||||||
|
|
||||||
##
|
##
|
||||||
|
## Clears the entire mesh grid to only contain [code]mesh[/code].
|
||||||
##
|
##
|
||||||
|
## [code]null[/code] may be past to [code]mesh[/code] for clearing the mesh grid to nothing.
|
||||||
|
##
|
||||||
|
## For clearing a specific region of the mesh grid, see [method fill_mesh].
|
||||||
##
|
##
|
||||||
func clear_mesh(mesh: Mesh) -> void:
|
func clear_mesh(mesh: Mesh) -> void:
|
||||||
for y in size.y:
|
for y in size.y:
|
||||||
|
@ -135,10 +146,18 @@ func clear_mesh(mesh: Mesh) -> void:
|
||||||
_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate)
|
_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate)
|
||||||
|
|
||||||
##
|
##
|
||||||
|
## Clears the region of the mesh grid at [code]area[/code] to only contain [code]mesh[/code].
|
||||||
##
|
##
|
||||||
|
## [code]null[/code] may be past to [code]mesh[/code] for filling the region to nothing.
|
||||||
|
##
|
||||||
|
## *Note* that [code]area[/code] *must* be within the area of the of the mesh grid, where
|
||||||
|
## [code]Vector2i.ZERO[/code] is the top-left and [member size] [code]- 1[/code] is the bottom-
|
||||||
|
## right.
|
||||||
|
##
|
||||||
|
## For clearing the whole of the mesh grid, see [method clear_mesh].
|
||||||
##
|
##
|
||||||
func fill_mesh(area: Rect2i, mesh: Mesh) -> void:
|
func fill_mesh(area: Rect2i, mesh: Mesh) -> void:
|
||||||
assert(Rect2i(Vector2i.ZERO, size).encloses(area), "area must be within grid")
|
assert(_get_area().encloses(area), "area must be within grid")
|
||||||
|
|
||||||
var filled_chunks := BitMap.new()
|
var filled_chunks := BitMap.new()
|
||||||
|
|
||||||
|
@ -160,16 +179,25 @@ func fill_mesh(area: Rect2i, mesh: Mesh) -> void:
|
||||||
_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate)
|
_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate)
|
||||||
|
|
||||||
##
|
##
|
||||||
|
## Plots a single mesh at [code]coordinates[/code] to be [code]mesh[/code], overwriting whatever it
|
||||||
|
## previously contained.
|
||||||
##
|
##
|
||||||
|
## [code]null[/code] may be past to [code]mesh[/code] for clearing the mesh grid to nothing.
|
||||||
##
|
##
|
||||||
func plot_mesh(coordinate: Vector2i, mesh: Mesh) -> void:
|
## *Note* that [code]coordinates[/code] *must* be within the area of the of the mesh grid, where
|
||||||
assert(Rect2i(Vector2i.ZERO, size).has_point(coordinate), "coordinate must be within grid")
|
## [code]Vector2i.ZERO[/code] is the top-left and [member size] [code]- 1[/code] is the bottom-
|
||||||
|
## right.
|
||||||
|
##
|
||||||
|
## For bulk-setting many meshes at once, see [method fill_mesh] and [method clear_mesh].
|
||||||
|
##
|
||||||
|
func plot_mesh(coordinates: Vector2i, mesh: Mesh) -> void:
|
||||||
|
assert(_get_area().has_point(coordinates), "coordinate must be within grid")
|
||||||
|
|
||||||
var chunk_coordinate := coordinate / _CHUNK_SIZE
|
var chunk_coordinates := coordinates / _CHUNK_SIZE
|
||||||
var chunk := _get_chunk(chunk_coordinate)
|
var chunk := _get_chunk(chunk_coordinates)
|
||||||
|
|
||||||
chunk.set_mesh(coordinate % _CHUNK_SIZE, mesh)
|
chunk.set_mesh(coordinates % _CHUNK_SIZE, mesh)
|
||||||
chunk.invalidate(self, chunk_coordinate)
|
chunk.invalidate(self, chunk_coordinates)
|
||||||
|
|
||||||
##
|
##
|
||||||
## Returns [code]world_position[/code] converted into a coordinate aligned with the [MeshGrid].
|
## Returns [code]world_position[/code] converted into a coordinate aligned with the [MeshGrid].
|
||||||
|
|
Loading…
Reference in New Issue