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…
Reference in New Issue