Compare commits

..

No commits in common. "4963f7b355f377ae0b184b370b97c6c43edfc975" and "f87cc4f996b6b27da928ade680e02e78a2025e7d" have entirely different histories.

8 changed files with 70 additions and 172 deletions

BIN
local_player.scn (Stored with Git LFS)

Binary file not shown.

BIN
map_editor.scn (Stored with Git LFS)

Binary file not shown.

BIN
map_editor/camera_boundary_mesh.res (Stored with Git LFS)

Binary file not shown.

View File

@ -1,19 +1,18 @@
class_name MapEditorMenu extends VBoxContainer class_name MapEditorMenu extends VBoxContainer
## ##
## [code]edit_action[/code] has been activated for use when the player interacts with the map ##
## editor.
## ##
signal edit_activated(edit_action: Callable) signal edit_activated(edit_action: Callable)
## ##
## Emits [signal edit_activated]. ##
## ##
func activate_editor(edit_action: Callable) -> void: func activate_editor(edit_action: Callable) -> void:
edit_activated.emit(edit_action) edit_activated.emit(edit_action)
## ##
## Resets the state of the menu. ##
## ##
func reset() -> void: func reset() -> void:
pass pass

BIN
map_editor/menus_theme.res (Stored with Git LFS)

Binary file not shown.

BIN
map_editor/tile_selector_button_group.res (Stored with Git LFS)

Binary file not shown.

View File

@ -1,82 +1,25 @@
class_name MeshGrid extends Node3D class_name MeshGrid extends Node3D
const _CHUNK_SIZE := 32 const _CELL_COUNT := 8
const _GRID_ORIGIN := Vector2(0.5, 0.5) class _Chunk:
var multimesh_instances: Array[_MultimeshInstance] = []
## var meshes: Array[Mesh] = []
## Baked block of meshes making up a segment of the grid.
##
class Chunk:
var _multimesh_instances: Array[MultiMeshInstance] = []
var _meshes: Array[Mesh] = []
func _init() -> void: func _init() -> void:
_meshes.resize(_CHUNK_SIZE * _CHUNK_SIZE) self.meshes.resize(_CELL_COUNT * _CELL_COUNT)
## class _MultimeshInstance:
## Invalidates the mesh block, re-baking its contents from the current mesh data set.
##
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))))
# (Re)-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)
##
## 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(coordinates: Vector2i, mesh: Mesh) -> void:
_meshes[(_CHUNK_SIZE * coordinates.y) + coordinates.x] = mesh
##
## Specialized multi-mesh instance convenience for use within the mesh grid and its chunks.
##
class MultiMeshInstance:
var _instance_rid := RID() var _instance_rid := RID()
var _multimesh_rid := RID() var _multimesh_rid := RID()
func _init(scenario_rid: RID, mesh_rid: RID, transforms: Array) -> void: func _init(scenario_rid: RID, mesh: Mesh, transforms: Array) -> void:
_multimesh_rid = RenderingServer.multimesh_create() _multimesh_rid = RenderingServer.multimesh_create()
_instance_rid = RenderingServer.instance_create2(_multimesh_rid, scenario_rid) _instance_rid = RenderingServer.instance_create2(_multimesh_rid, scenario_rid)
RenderingServer.multimesh_set_mesh(_multimesh_rid, mesh_rid) RenderingServer.multimesh_set_mesh(_multimesh_rid, mesh.get_rid())
var transform_count := transforms.size() var transform_count := transforms.size()
@ -86,118 +29,88 @@ class MultiMeshInstance:
for i in transform_count: for i in transform_count:
RenderingServer.multimesh_instance_set_transform(_multimesh_rid, i, transforms[i]) RenderingServer.multimesh_instance_set_transform(_multimesh_rid, i, transforms[i])
## var _chunks: Array[_Chunk] = []
## Sets the parent transform of all mesh instances under it to [code]offset[/code].
##
func set_offset(offset: Transform3D) -> void:
RenderingServer.instance_set_transform(_instance_rid, offset)
var _chunks: Array[Chunk] = [] var _grid_origin := Vector2(0.5, 0.5)
var _chunks_size := Vector2i.ZERO
## ##
## Size of the mesh grid (in engine units). ## 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 @export
var size: Vector2i: var size: Vector2i:
set(value): set(value):
var chunk_size_factor := float(_CHUNK_SIZE) self._chunks.resize(int(ceil((value.x * value.y) / float(_CELL_COUNT))))
_chunks.resize(int(ceilf(value.x / chunk_size_factor) * ceilf(value.y / chunk_size_factor))) for i in self._chunks.size():
self._chunks[i] = _Chunk.new()
for i in _chunks.size():
_chunks[i] = Chunk.new()
_chunks_size = Vector2i((value / float(_CHUNK_SIZE)).ceil())
size = value 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: 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) RenderingServer.instance_set_transform(
multimesh_instance._instance_rid, multimesh_instance.global_transform)
## ##
## Clears the entire mesh grid to only contain [code]mesh[/code]. ## Sets the cell at [code]coordinate[/code] to display [code]mesh[/code].
## ##
## [code]null[/code] may be past to [code]mesh[/code] for clearing the mesh grid to nothing. ## Note that this function assumes [code]coordinate[/code] is within the bounds of the grid
## coordinate space.
## ##
## For clearing a specific region of the mesh grid, see [method fill_mesh]. ## 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: func set_mesh(coordinate: Vector2i, mesh: Mesh) -> void:
for y in size.y: assert(Rect2i(Vector2i.ZERO, size).has_point(coordinate), "coordinate must be within grid")
for x in size.x:
var coordinate := Vector2i(x, y)
_get_chunk(coordinate / _CHUNK_SIZE).set_mesh(coordinate % _CHUNK_SIZE, mesh) # 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
for y in _chunks_size.y: chunk_meshes[(_CELL_COUNT * cell_coordinate.y) + cell_coordinate.x] = mesh
for x in _chunks_size.x:
var chunk_coordinate := Vector2i(x, y)
_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate) # 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()
## 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:
assert(get_area().encloses(area), "area must be within grid")
var filled_chunks := BitMap.new() # Normalize mesh instance data for the chunk.
var mesh_transform_sets := {}
filled_chunks.resize(_chunks_size) for i in chunk_meshes.size():
var chunk_mesh := chunk_meshes[i]
for y in range(area.position.y, area.end.y): if chunk_mesh != null:
for x in range(area.position.x, area.end.x): if not(chunk_mesh in mesh_transform_sets):
var coordinate := Vector2i(x, y) mesh_transform_sets[chunk_mesh] = []
var chunk_coordinate := coordinate / _CHUNK_SIZE
_get_chunk(chunk_coordinate).set_mesh(coordinate % _CHUNK_SIZE, mesh) mesh_transform_sets[chunk_mesh].push_back(Transform3D(Basis.IDENTITY, Vector3(
filled_chunks.set_bitv(chunk_coordinate, true) (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))))
for y in _chunks_size.y: # Bake into multimesh instances for the chunk.
for x in _chunks_size.x: var scenario_rid := get_world_3d().scenario
if filled_chunks.get_bit(x, y):
var chunk_coordinate := Vector2i(x, y)
_get_chunk(chunk_coordinate).invalidate(self, chunk_coordinate) for chunk_mesh in mesh_transform_sets:
var multimesh_instance :=\
_MultimeshInstance.new(scenario_rid, chunk_mesh, mesh_transform_sets[chunk_mesh])
func get_area() -> Rect2i: RenderingServer.instance_set_transform(multimesh_instance._instance_rid, global_transform)
return Rect2i(Vector2i.ZERO, size)
## chunk.multimesh_instances.push_back(multimesh_instance)
## 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.
##
## *Note* that [code]coordinates[/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 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_coordinates := coordinates / _CHUNK_SIZE
var chunk := _get_chunk(chunk_coordinates)
chunk.set_mesh(coordinates % _CHUNK_SIZE, mesh)
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].
@ -206,4 +119,4 @@ func plot_mesh(coordinates: Vector2i, mesh: Mesh) -> void:
## coordinates outside of the [MeshGrid] bounds as well. ## coordinates outside of the [MeshGrid] bounds as well.
## ##
func world_to_grid(world_position: Vector2) -> Vector2i: 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())

View File

@ -14,7 +14,6 @@ config/name="Protectorate"
run/main_scene="res://map_editor.scn" run/main_scene="res://map_editor.scn"
config/use_custom_user_dir=true config/use_custom_user_dir=true
config/features=PackedStringArray("4.0", "Forward Plus") config/features=PackedStringArray("4.0", "Forward Plus")
boot_splash/show_image=false
config/icon="res://icon.png" config/icon="res://icon.png"
[autoload] [autoload]
@ -24,7 +23,6 @@ LocalPlayer="*res://local_player.scn"
[debug] [debug]
gdscript/warnings/integer_division=0
gdscript/warnings/assert_always_true=0 gdscript/warnings/assert_always_true=0
gdscript/warnings/assert_always_false=0 gdscript/warnings/assert_always_false=0
@ -82,15 +80,6 @@ editor_paint={
] ]
} }
[layer_names]
3d_render/layer_1="Physical"
3d_render/layer_2="Interface"
[physics]
common/physics_ticks_per_second=30
[rendering] [rendering]
lights_and_shadows/directional_shadow/soft_shadow_filter_quality=3 lights_and_shadows/directional_shadow/soft_shadow_filter_quality=3