diff --git a/map_editor.scn b/map_editor.scn index 118300a..e465cd9 100644 --- a/map_editor.scn +++ b/map_editor.scn @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f38a006102be993f6ba2186510f9fd83fcd207ba6f20dc4cc8fac509e48a7485 -size 8229 +oid sha256:3ee83ca95ebcaa6d909fa20fb86c6365695672563c505ac21c0a19267e4c169f +size 7572 diff --git a/map_editor/camera_boundary_mesh.res b/map_editor/camera_boundary_mesh.res index dcbce3c..673c51f 100644 --- a/map_editor/camera_boundary_mesh.res +++ b/map_editor/camera_boundary_mesh.res @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bfd8ad50f3db5aafadc5fb8278b2b7d597fad0d8f0fb2e2fcb4834db07c8336 -size 647 +oid sha256:ce40e96135058eaaf87b5188862dc31e71c6e11a53f5afce3cffccf4320208d6 +size 645 diff --git a/map_editor/edit_mode_button_group.res b/map_editor/edit_mode_button_group.res index 9e6a97d..eb99a58 100644 --- a/map_editor/edit_mode_button_group.res +++ b/map_editor/edit_mode_button_group.res @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcc7954349a7ed68391dfbfd0e8369169da156218b5b856dda3005ef0708ad16 -size 202 +oid sha256:aa858f41def84ccb776a1cfdf2ae3f9517ca2f75b042998c5118e88ff6c591a0 +size 205 diff --git a/map_editor/map_editor_controller.gd b/map_editor/map_editor_controller.gd deleted file mode 100644 index 2162752..0000000 --- a/map_editor/map_editor_controller.gd +++ /dev/null @@ -1,143 +0,0 @@ -class_name MapEditorController extends Node - -## -## Default elevation height for terrain. -## -const DEFAULT_ELEVATION := 0.5 - -var _brush_selected := 0 - -@export -var _registered_brushes: Array[Image] = [] - -@export -var _registered_paints: Array[TerrainPaint] = [] - -var _paint_selected_erase := 0 - -var _paint_selected_blue := 0 - -var _paint_selected_green := 0 - -var _paint_selected_red := 0 - -## -## -## -var smooth_elevation := DEFAULT_ELEVATION - -## -## -## -func disable_controls() -> void: - set_process(false) - -## -## -## -func enable_controls() -> void: - set_process(true) - -## -## -## -func get_registered_brushes() -> Array[Image]: - return _registered_brushes - -## -## -## -func get_registered_paints() -> Array[TerrainPaint]: - return _registered_paints - -## -## -## -func get_selected_brush() -> int: - return _brush_selected - -## -## -## -func get_selected_paint_blue() -> int: - return _paint_selected_blue - -## -## -## -func get_selected_paint_erase() -> int: - return _paint_selected_erase - -## -## -## -func get_selected_paint_green() -> int: - return _paint_selected_green - -## -## -## -func get_selected_paint_red() -> int: - return _paint_selected_red - -## -## Registers [code]brush_mask[/code] as a usable brush option in the editor. -## -func register_brush(brush_mask: Image) -> void: - _registered_brushes.append(brush_mask) - -## -## Registers [code]terrain_paint[/code] as a usable paint option in the editor. -## -func register_paint(terrain_paint: TerrainPaint) -> void: - _registered_paints.append(terrain_paint) - -## -## Resets the editor settings to their initial values. -## -func reset() -> void: - select_brush(0) - select_paint_erase(0) - select_paint_red(0) - select_paint_green(0) - select_paint_blue(0) - -## -## -## -func select_brush(selected_index: int) -> void: - assert((selected_index >= 0) or (selected_index < _registered_brushes.size())) - - _brush_selected = selected_index - -## -## -## -func select_paint_blue(selected_index: int) -> void: - assert((selected_index >= 0) or (selected_index < _registered_paints.size())) - - _paint_selected_blue = selected_index - -## -## -## -func select_paint_erase(selected_index: int) -> void: - assert((selected_index >= 0) or (selected_index < _registered_paints.size())) - - _paint_selected_erase = selected_index - -## -## -## -func select_paint_green(selected_index: int) -> void: - assert((selected_index >= 0) or (selected_index < _registered_paints.size())) - - _paint_selected_green = selected_index - -## -## -## -func select_paint_red(selected_index: int) -> void: - assert((selected_index >= 0) or (selected_index < _registered_paints.size())) - - _paint_selected_red = selected_index diff --git a/terrain/dynamic_terrain_instance_3d.gd b/map_editor/map_editor_terrain_canvas.gd similarity index 72% rename from terrain/dynamic_terrain_instance_3d.gd rename to map_editor/map_editor_terrain_canvas.gd index 07f5d75..69e3ca8 100644 --- a/terrain/dynamic_terrain_instance_3d.gd +++ b/map_editor/map_editor_terrain_canvas.gd @@ -1,27 +1,33 @@ -class_name DynamicTerrainInstance3D extends TerrainInstance3D +class_name MapEditorTerrainCanvas extends Node ## ## Blank sample value. ## const BLANK := Color(0.0, 0.0, 0.0, 0.0) -var _editable_image: Image +var _editable_image := Image.create(1, 1, false, Image.FORMAT_RGBAF) -var _editable_texture: ImageTexture +var _editable_texture := ImageTexture.create_from_image(_editable_image) + +## +## +## +var size := Vector2i.ONE: + set(value): + size = Vector2(maxi(size.x, value.x), maxi(size.y, value.y)) + + _editable_image.resize(size.x, size.y) + _editable_texture.set_image(_editable_image) func _get_brush_area(point: Vector2i, mask_size: Vector2i, mask_scale: float) -> Rect2i: # Convert worldspace point to image-space coordinates for mask. var scaled_mask_size := Vector2i(mask_size * mask_scale) - return Rect2i((point + Vector2i(get_size() * 0.5)) - Vector2i(scaled_mask_size * 0.5), scaled_mask_size) + return Rect2i((point + Vector2i(size * 0.5)) - + Vector2i(scaled_mask_size * 0.5), scaled_mask_size) -func _init() -> void: - super._init() - - _editable_image = Image.create(1, 1, false, Image.FORMAT_RGBAF) - _editable_texture = ImageTexture.create_from_image(_editable_image) - - super.resize(Vector2i.ONE) +func _get_editable_area() -> Rect2i: + return Rect2i(Vector2i.ZERO, size).grow(-1) ## ## Clears the terrain map to the value of [code]clear_elevation[/code]. @@ -30,17 +36,23 @@ func clear(clear_elevation: float) -> void: _editable_image.fill(Color(0.0, 0.0, 0.0, clampf(clear_elevation, 0.0, 1.0))) _editable_texture.update(_editable_image) +## +## +## +func get_texture() -> void: + return _editable_texture + ## ## Oscillates the height of terrain toward a value of [member height_scale] (multiplied by ## [code]level[/code]) around the area of [code]point[/code] determined by [code]mask[/code] ## multiplied by [code]mask_scale[/code] at a rate of [code]intensity[/code]. ## -func oscillate(point: Vector2i, level: float, mask: Image, mask_scale: float, intensity: float) -> void: +func oscillate(level: float, point: Vector2i, mask: Image, mask_scale: float, intensity: float) -> void: var mask_size := mask.get_size() var brush_source := _get_brush_area(point, mask_size, mask_scale) if brush_source.has_area(): - var brush_target := Rect2i(Vector2i.ZERO, get_size()).intersection(brush_source) + var brush_target := _get_editable_area().intersection(brush_source) if brush_target.has_area(): var mask_ratio := mask_size / brush_source.size @@ -64,12 +76,12 @@ func oscillate(point: Vector2i, level: float, mask: Image, mask_scale: float, in ## [code]intensity[/code] around the area of [code]point[/code] determined by [code]mask[/code] ## multiplied by [code]mask_scale[/code] at a rate of [code]intensity[/code]. ## -func paint(point: Vector2i, color: Color, mask: Image, mask_scale: float, intensity: float) -> void: +func paint(color: Color, point: Vector2i, mask: Image, mask_scale: float, intensity: float) -> void: var mask_size := mask.get_size() var brush_source := _get_brush_area(point, mask_size, mask_scale) if brush_source.has_area(): - var brush_target := Rect2i(Vector2i.ZERO, get_size()).intersection(brush_source) + var brush_target := _get_editable_area().intersection(brush_source) if brush_target.has_area(): var mask_ratio := mask_size / brush_source.size @@ -92,23 +104,6 @@ func paint(point: Vector2i, color: Color, mask: Image, mask_scale: float, intens _editable_texture.update(_editable_image) -## -## Attempts to resize the terrain geometry and map to be equal to [code]size[/code] (in-engine -## units), with [code]Vector2i.ONE[/code] as the minimum size. -## -## In the process of growing the terrain map, existing edge data will be copied to fill it out. -## -func resize(size: Vector2i) -> void: - super.resize(size) - - var actual_size := get_size() - - _editable_image.resize(actual_size.x, actual_size.y) - - _editable_texture = ImageTexture.create_from_image(_editable_image) - - update_terrain(_editable_texture) - ## ## Samples the color value in the editable terrain at the worldspace [code]point[/code], returning ## its respective [Color] value. @@ -117,7 +112,6 @@ func resize(size: Vector2i) -> void: ## instead. ## func sample(point: Vector2i) -> Color: - var size := get_size() var image_point := point + Vector2i(size * 0.5) if Rect2i(Vector2i.ZERO, size).has_point(image_point): diff --git a/map_editor/menus_theme.res b/map_editor/menus_theme.res index a643e43..d240659 100644 --- a/map_editor/menus_theme.res +++ b/map_editor/menus_theme.res @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14d3249a6f33398063da6cb0d061b5c602cca68946b607ff5b06e428d3a24d72 -size 842 +oid sha256:16a64c4d989cb97515aeb631c435db2c29388fb701e9e513d26692d3bba10f65 +size 847 diff --git a/project.godot b/project.godot index 96efcf4..4bbe569 100644 --- a/project.godot +++ b/project.godot @@ -9,20 +9,15 @@ config_version=5 _global_script_classes=[{ -"base": "TerrainInstance3D", -"class": &"DynamicTerrainInstance3D", -"language": &"GDScript", -"path": "res://terrain/dynamic_terrain_instance_3d.gd" -}, { "base": "HFlowContainer", "class": &"ItemSelection", "language": &"GDScript", "path": "res://user_interface/button_selection.gd" }, { "base": "Node", -"class": &"MapEditorController", +"class": &"MapEditorTerrainCanvas", "language": &"GDScript", -"path": "res://map_editor/map_editor_controller.gd" +"path": "res://map_editor/map_editor_terrain_canvas.gd" }, { "base": "Node3D", "class": &"PlayerController", @@ -45,9 +40,8 @@ _global_script_classes=[{ "path": "res://terrain/paints/terrain_paint.gd" }] _global_script_class_icons={ -"DynamicTerrainInstance3D": "", "ItemSelection": "", -"MapEditorController": "", +"MapEditorTerrainCanvas": "", "PlayerController": "", "Settings": "", "TerrainInstance3D": "", diff --git a/terrain/terrain.gdshader b/terrain/terrain.gdshader index 1985d01..13b8827 100644 --- a/terrain/terrain.gdshader +++ b/terrain/terrain.gdshader @@ -4,7 +4,7 @@ const int MAP_COUNT = 4; uniform sampler2D[MAP_COUNT] ALBEDO_MAPS; -uniform float MAX_HEIGHT = 100.0; +uniform float HEIGHT_SCALE = 100.0; uniform sampler2D[MAP_COUNT] NORMAL_MAPS; @@ -39,7 +39,7 @@ void fragment() { } float height(vec2 uv) { - return MAX_HEIGHT * texture(TERRAIN_MAP, uv).a; + return HEIGHT_SCALE * texture(TERRAIN_MAP, uv).a; } void vertex() { diff --git a/terrain/terrain_instance_3d.gd b/terrain/terrain_instance_3d.gd index 6112823..544f395 100644 --- a/terrain/terrain_instance_3d.gd +++ b/terrain/terrain_instance_3d.gd @@ -1,87 +1,79 @@ +@tool class_name TerrainInstance3D extends GeometryInstance3D -## -## Identifier constant for a paint slot channel. -## +const _DETAIL := 2 + +const _SHADER := preload("res://terrain/terrain.gdshader") + enum PaintSlot { ERASE, RED, GREEN, BLUE, + ALPHA, } -const _DETAIL := 2 - -var _albedo_map_textures: Array[Texture2D] = [null, null, null, null] +var _albedo_maps: Array[Texture2D] = [null, null, null, null] var _material := ShaderMaterial.new() -var _normal_map_textures: Array[Texture2D] = [null, null, null, null] - var _mesh := PlaneMesh.new() +var _normal_maps: Array[Texture2D] = [null, null, null, null] + +## ## -## The height scale range that is used to determine the minimums and maximums of the terrain map. ## @export var height_scale := 100.0: - get: - return height_scale - set(value): - _material.set_shader_parameter("MAX_HEIGHT", value) + value = maxf(value, 1.0) - height_scale = max(value, 0.0) + if not(is_equal_approx(value, height_scale)): + _material.set_shader_parameter("HEIGHT_SCALE", value) -var _size := Vector2i.ZERO + height_scale = value + +## +## Size of the terrain geometry (in engine units). +## +@export +var size := Vector2i.ONE: + set(value): + value = Vector2i(maxi(value.x, 1), maxi(value.y, 1)) + + if (value.x != size.x) or (value.y != size.y): + _mesh.subdivide_width = value.x * _DETAIL + _mesh.subdivide_depth = value.y * _DETAIL + _mesh.size = value + + _material.set_shader_parameter("SIZE", value) + + size = value func _init() -> void: - _material.shader = preload("res://terrain/terrain.gdshader") + _material.shader = _SHADER - RenderingServer.instance_set_base(get_instance(), _mesh) _mesh.surface_set_material(0, _material) + RenderingServer.instance_set_base(get_instance(), _mesh) ## -## Returns the size of the terrain mesh (as in-engine units). +## Updates the terrain map being used by the terrain to be [code]terrain_map[/code]. ## -func get_size() -> Vector2i: - return _size +func update_map(terrain_map: Texture2D) -> void: + _material.set_shader_parameter("TERRAIN_MAP", terrain_map) ## -## Attempts to resize the terrain to be equal to [code]size[/code] (in-engine units), with -## [code]Vector2i.ONE[/code] as the minimum size. -## -func resize(size: Vector2i) -> void: - var width := maxi(size.x, 1) - var height := maxi(size.y, 1) - - if (width != _size.x) or (height != _size.y): - _mesh.subdivide_width = width * _DETAIL - _mesh.subdivide_depth = height * _DETAIL - _mesh.size = size - - _material.set_shader_parameter("SIZE", Vector2(size)) - - _size = size - -## -## Updates the paint used by the paint channel identified by [code]paint_slot[/code] to +## Updates the [TerrainPaint] being used by the terrain in [code]paint_slot[/code] to be ## [code]terrain_paint[/code]. ## -func update_paint(paint_slot: PaintSlot, paint: TerrainPaint) -> void: - if paint == null: - _albedo_map_textures[paint_slot] = null - _normal_map_textures[paint_slot] = null +func update_paint(paint_slot: PaintSlot, terrain_paint: TerrainPaint) -> void: + if _albedo_maps[paint_slot] != terrain_paint.albedo_map: + _albedo_maps[paint_slot] = terrain_paint.albedo_map - else: - _albedo_map_textures[paint_slot] = paint.albedo_map - _normal_map_textures[paint_slot] = paint.normal_map + _material.set_shader_parameter("ALBEDO_MAPS", _albedo_maps) - _material.set_shader_parameter("ALBEDO_MAPS", _albedo_map_textures) - _material.set_shader_parameter("NORMAL_MAPS", _normal_map_textures) + if _normal_maps[paint_slot] != terrain_paint.normal_map: + _normal_maps[paint_slot] = terrain_paint.normal_map -## -## Updates the terrain map to [code]terrain_map[/code]. -## -func update_terrain(terrain_map: Texture2D) -> void: - _material.set_shader_parameter("TERRAIN_MAP", terrain_map) + _material.set_shader_parameter("NORMAL_MAPS", _normal_maps)