Compare commits

..

No commits in common. "c7a0d6bce2ac4dbb6c569570978f4c45425ee8b7" and "129c4f16d8915ed3a940a33184666cb0a9bee879" have entirely different histories.

53 changed files with 323 additions and 358 deletions

BIN
black.png (Stored with Git LFS)

Binary file not shown.

View File

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cj72ibo3c6x8e"
path="res://.godot/imported/black.png-19a8df0b1a4edb74ea2eecb66d46d03d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://black.png"
dest_files=["res://.godot/imported/black.png-19a8df0b1a4edb74ea2eecb66d46d03d.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
editor.scn (Stored with Git LFS)

Binary file not shown.

BIN
editor/brush_hard_circle_mask.png (Stored with Git LFS)

Binary file not shown.

View File

@ -1,14 +0,0 @@
[remap]
importer="image"
type="Image"
uid="uid://drok88h02tdre"
path="res://.godot/imported/brush_hard_circle_mask.png-e647df7b970f00cdf2f3f2ac38ad8257.image"
[deps]
source_file="res://editor/brush_hard_circle_mask.png"
dest_files=["res://.godot/imported/brush_hard_circle_mask.png-e647df7b970f00cdf2f3f2ac38ad8257.image"]
[params]

BIN
editor/edit_mode_button_group.res (Stored with Git LFS)

Binary file not shown.

View File

@ -1,46 +1,70 @@
class_name DynamicTerrainInstance3D extends TerrainInstance3D
class_name EditableTerrain extends Node
##
## Blank sample value.
##
const BLANK := Color(0.0, 0.0, 0.0, 0.0)
var _editable_image: Image
var _image := Image.create(1, 1, false, Image.FORMAT_RGBAF)
var _editable_texture: ImageTexture
##
## Tracked [TerrainInstance3D] to echo all terrain editing changes to.
##
var instance: TerrainInstance3D = null:
get:
return instance
func _get_brush_area(point: Vector2i, mask_size: Vector2i, mask_scale: float) -> Rect2i:
set(value):
if (value != null) and (value != instance):
value.terrain_map = ImageTexture.create_from_image(_image)
instance = value
##
## Width and height of the editable terrain in units.
##
var size := Vector2i.ZERO:
get:
return size
set(value):
_image.resize(value.x, value.y)
if instance != null:
instance.size = value
instance.terrain_map = ImageTexture.create_from_image(_image)
size = value
func _get_brush_area(point: Vector2i, mask_size: Vector2i, scale: float) -> Rect2i:
# Convert worldspace point to image-space coordinates for mask.
var scaled_mask_size := Vector2i(mask_size * mask_scale)
var scaled_mask_size := Vector2i(mask_size * 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)
clear(BLANK)
##
## Clears the terrain map to the value of [code]clear_elevation[/code].
## Clears the image to the value of [code]clear_color[/code].
##
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 clear(clear_color: Color) -> void:
_image.fill(clear_color)
if instance != null:
instance.terrain_map.update(_image)
##
##
##
func oscillate(point: Vector2i, level: float,
mask: Image, mask_scale: float, intensity: float, delta: float) -> void:
mask: Image, scale: float, intensity: float, delta: float) -> void:
var mask_size := mask.get_size()
var brush_source := _get_brush_area(point, mask_size, mask_scale)
var brush_source := _get_brush_area(point, mask_size, scale)
if brush_source.has_area():
var brush_target := Rect2i(Vector2i.ZERO, get_size()).intersection(brush_source)
var brush_target := Rect2i(Vector2i.ZERO, size).intersection(brush_source)
if brush_target.has_area():
var mask_ratio := mask_size / brush_source.size
@ -51,25 +75,26 @@ func oscillate(point: Vector2i, level: float,
for x in brush_target.size.x:
var coord := brush_target.position + Vector2i(x, y)
var pixel := _editable_image.get_pixelv(coord)
var pixel := _image.get_pixelv(coord)
_editable_image.set_pixelv(coord, Color(pixel.r, pixel.g, pixel.b,
_image.set_pixelv(coord, Color(pixel.r, pixel.g, pixel.b,
lerpf(pixel.a, level, intensity * delta *\
mask.get_pixel(int((offset.x + x) * mask_ratio.x), mask_y).a)).clamp())
_editable_texture.update(_editable_image)
if instance != null:
instance.terrain_map.update(_image)
##
##
##
func paint(point: Vector2i, color: Color,
mask: Image, mask_scale: float, intensity: float, delta: float) -> void:
mask: Image, scale: float, intensity: float, delta: float) -> void:
var mask_size := mask.get_size()
var brush_source := _get_brush_area(point, mask_size, mask_scale)
var brush_source := _get_brush_area(point, mask_size, scale)
if brush_source.has_area():
var brush_target := Rect2i(Vector2i.ZERO, get_size()).intersection(brush_source)
var brush_target := Rect2i(Vector2i.ZERO, size).intersection(brush_source)
if brush_target.has_area():
var mask_ratio := mask_size / brush_source.size
@ -80,31 +105,18 @@ func paint(point: Vector2i, color: Color,
for x in brush_target.size.x:
var coord := brush_target.position + Vector2i(x, y)
var pixel := _editable_image.get_pixelv(coord)
var pixel := _image.get_pixelv(coord)
var mask_intensity_delta := intensity * delta *\
mask.get_pixel(int((offset.x + x) * mask_ratio.x), mask_y).a
_editable_image.set_pixelv(coord, Color(
_image.set_pixelv(coord, Color(
lerpf(pixel.r, color.r, mask_intensity_delta),
lerpf(pixel.g, color.g, mask_intensity_delta),
lerpf(pixel.b, color.b, mask_intensity_delta), pixel.a).clamp())
_editable_texture.update(_editable_image)
##
##
##
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)
if instance != null:
instance.terrain_map.update(_image)
##
## Samples the color value in the editable terrain at the worldspace [code]point[/code], returning
@ -114,10 +126,9 @@ 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):
return _editable_image.get_pixelv(image_point)
return _image.get_pixelv(image_point)
return BLANK

View File

@ -9,10 +9,10 @@
config_version=5
_global_script_classes=[{
"base": "TerrainInstance3D",
"class": &"DynamicTerrainInstance3D",
"base": "Node",
"class": &"EditableTerrain",
"language": &"GDScript",
"path": "res://terrain/dynamic_terrain_instance_3d.gd"
"path": "res://editor/editable_terrain.gd"
}, {
"base": "Node3D",
"class": &"PlayerController",
@ -28,25 +28,18 @@ _global_script_classes=[{
"class": &"TerrainInstance3D",
"language": &"GDScript",
"path": "res://terrain/terrain_instance_3d.gd"
}, {
"base": "Resource",
"class": &"TerrainPaint",
"language": &"GDScript",
"path": "res://terrain/paints/terrain_paint.gd"
}]
_global_script_class_icons={
"DynamicTerrainInstance3D": "",
"EditableTerrain": "",
"PlayerController": "",
"Settings": "",
"TerrainInstance3D": "",
"TerrainPaint": ""
"TerrainInstance3D": ""
}
[application]
config/name="Protectorate"
run/main_scene="res://editor.scn"
config/use_custom_user_dir=true
config/features=PackedStringArray("4.0", "Forward Plus")
config/icon="res://icon.png"

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://jcmf7j3qo6ea"
path="res://.godot/imported/arid_grass_albedo.png-532bc188063dc524e2c08228bed13fa7.ctex"
path="res://.godot/imported/arid_grass_albedo.png-cac7ceff15cf65e54d8ee36a70e05853.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/arid_grass_albedo.png"
dest_files=["res://.godot/imported/arid_grass_albedo.png-532bc188063dc524e2c08228bed13fa7.ctex"]
source_file="res://terrain/arid_grass_albedo.png"
dest_files=["res://.godot/imported/arid_grass_albedo.png-cac7ceff15cf65e54d8ee36a70e05853.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://co0jompch208v"
path="res://.godot/imported/arid_grass_displacement.png-cad938b62195c0b0f41099d596594b8b.ctex"
path="res://.godot/imported/arid_grass_displacement.png-66c8c71a36e11168aed23cd0531ba7bd.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/arid_grass_displacement.png"
dest_files=["res://.godot/imported/arid_grass_displacement.png-cad938b62195c0b0f41099d596594b8b.ctex"]
source_file="res://terrain/arid_grass_displacement.png"
dest_files=["res://.godot/imported/arid_grass_displacement.png-66c8c71a36e11168aed23cd0531ba7bd.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bh141jkgbmya8"
path="res://.godot/imported/arid_grass_normal.png-7bf9fdf5f927fea47a2f86a9ffab326e.ctex"
path="res://.godot/imported/arid_grass_normal.png-6cfb96e0a0b1eac890998accab9c7109.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/arid_grass_normal.png"
dest_files=["res://.godot/imported/arid_grass_normal.png-7bf9fdf5f927fea47a2f86a9ffab326e.ctex"]
source_file="res://terrain/arid_grass_normal.png"
dest_files=["res://.godot/imported/arid_grass_normal.png-6cfb96e0a0b1eac890998accab9c7109.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://jdq8kwjo3uap"
path="res://.godot/imported/arid_grass_roughness.png-40299cbb0e24b392d013d45af79f5444.ctex"
path="res://.godot/imported/arid_grass_roughness.png-8ff0f34a3348350d0edc5e43b43bcd0e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/arid_grass_roughness.png"
dest_files=["res://.godot/imported/arid_grass_roughness.png-40299cbb0e24b392d013d45af79f5444.ctex"]
source_file="res://terrain/arid_grass_roughness.png"
dest_files=["res://.godot/imported/arid_grass_roughness.png-8ff0f34a3348350d0edc5e43b43bcd0e.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cs06oiwrord6t"
path="res://.godot/imported/cobblestone_albedo.png-f0ce6b9d25b0c6c4130e70ff660bcbd6.ctex"
path="res://.godot/imported/cobbled_limestone_albedo.png-cbafb40d3c6fc43ce00d68cb02456536.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/cobblestone_albedo.png"
dest_files=["res://.godot/imported/cobblestone_albedo.png-f0ce6b9d25b0c6c4130e70ff660bcbd6.ctex"]
source_file="res://terrain/cobbled_limestone_albedo.png"
dest_files=["res://.godot/imported/cobbled_limestone_albedo.png-cbafb40d3c6fc43ce00d68cb02456536.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://mkxbjfwdwkaq"
path="res://.godot/imported/cobblestone_displacement.png-bcf2e2194862b85cb002d9559d57903b.ctex"
path="res://.godot/imported/cobbled_limestone_displacement.png-8e8b27438ff815a9d58d4a490e04203c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/cobblestone_displacement.png"
dest_files=["res://.godot/imported/cobblestone_displacement.png-bcf2e2194862b85cb002d9559d57903b.ctex"]
source_file="res://terrain/cobbled_limestone_displacement.png"
dest_files=["res://.godot/imported/cobbled_limestone_displacement.png-8e8b27438ff815a9d58d4a490e04203c.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ukk0161x3eg2"
path="res://.godot/imported/cobblestone_normal.png-5130e667fa81f7452d15808908f4cb99.ctex"
path="res://.godot/imported/cobbled_limestone_normal.png-4d7b2eb9c070e06256eb42d408ff4a14.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/cobblestone_normal.png"
dest_files=["res://.godot/imported/cobblestone_normal.png-5130e667fa81f7452d15808908f4cb99.ctex"]
source_file="res://terrain/cobbled_limestone_normal.png"
dest_files=["res://.godot/imported/cobbled_limestone_normal.png-4d7b2eb9c070e06256eb42d408ff4a14.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bcybapft8tl2x"
path="res://.godot/imported/cobblestone_roughness.png-2f09da1d7000b898b80d7b177062e509.ctex"
path="res://.godot/imported/cobbled_limestone_roughness.png-f78cac7004d15ddc2a00fd10f74b8c10.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/cobblestone_roughness.png"
dest_files=["res://.godot/imported/cobblestone_roughness.png-2f09da1d7000b898b80d7b177062e509.ctex"]
source_file="res://terrain/cobbled_limestone_roughness.png"
dest_files=["res://.godot/imported/cobbled_limestone_roughness.png-f78cac7004d15ddc2a00fd10f74b8c10.ctex"]
[params]

View File

@ -3,8 +3,8 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bgpk15qrqxly8"
path.s3tc="res://.godot/imported/default_albedo.png-ec7b9bee606a4c8482c5f21d50eaafb2.s3tc.ctex"
path.etc2="res://.godot/imported/default_albedo.png-ec7b9bee606a4c8482c5f21d50eaafb2.etc2.ctex"
path.s3tc="res://.godot/imported/default_albedo.png-92a6d62c2ead547c4c35766bdeaf9065.s3tc.ctex"
path.etc2="res://.godot/imported/default_albedo.png-92a6d62c2ead547c4c35766bdeaf9065.etc2.ctex"
metadata={
"imported_formats": ["s3tc", "etc2"],
"vram_texture": true
@ -12,8 +12,8 @@ metadata={
[deps]
source_file="res://terrain/paints/default_albedo.png"
dest_files=["res://.godot/imported/default_albedo.png-ec7b9bee606a4c8482c5f21d50eaafb2.s3tc.ctex", "res://.godot/imported/default_albedo.png-ec7b9bee606a4c8482c5f21d50eaafb2.etc2.ctex"]
source_file="res://terrain/default_albedo.png"
dest_files=["res://.godot/imported/default_albedo.png-92a6d62c2ead547c4c35766bdeaf9065.s3tc.ctex", "res://.godot/imported/default_albedo.png-92a6d62c2ead547c4c35766bdeaf9065.etc2.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b8cv4q43fsglu"
path="res://.godot/imported/desert_sand_albedo.png-1f8394c37b6df0b83ddc4964049fe1fb.ctex"
path="res://.godot/imported/desert_sand_albedo.png-5307ce3455129127b5c6f3a135f5cbc7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/desert_sand_albedo.png"
dest_files=["res://.godot/imported/desert_sand_albedo.png-1f8394c37b6df0b83ddc4964049fe1fb.ctex"]
source_file="res://terrain/desert_sand_albedo.png"
dest_files=["res://.godot/imported/desert_sand_albedo.png-5307ce3455129127b5c6f3a135f5cbc7.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bdacir1igfinb"
path="res://.godot/imported/desert_sand_displacement.png-de6822961a581feed0d45f5f34fbb048.ctex"
path="res://.godot/imported/desert_sand_displacement.png-3674eb6c274d21b1ddd14b8c6ce982fe.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/desert_sand_displacement.png"
dest_files=["res://.godot/imported/desert_sand_displacement.png-de6822961a581feed0d45f5f34fbb048.ctex"]
source_file="res://terrain/desert_sand_displacement.png"
dest_files=["res://.godot/imported/desert_sand_displacement.png-3674eb6c274d21b1ddd14b8c6ce982fe.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://c40cc5b5sq4yu"
path="res://.godot/imported/desert_sand_normal.png-4882d77c1942a502a8d07cd8761a392c.ctex"
path="res://.godot/imported/desert_sand_normal.png-9b37ed7fc3712ce013db97500f604934.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/desert_sand_normal.png"
dest_files=["res://.godot/imported/desert_sand_normal.png-4882d77c1942a502a8d07cd8761a392c.ctex"]
source_file="res://terrain/desert_sand_normal.png"
dest_files=["res://.godot/imported/desert_sand_normal.png-9b37ed7fc3712ce013db97500f604934.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bwkcsnjetn5wr"
path="res://.godot/imported/desert_sand_roughness.png-0c32daf878e9b29d36631441877f21ac.ctex"
path="res://.godot/imported/desert_sand_roughness.png-c85e29a6965b06eac0c712e74b047821.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/desert_sand_roughness.png"
dest_files=["res://.godot/imported/desert_sand_roughness.png-0c32daf878e9b29d36631441877f21ac.ctex"]
source_file="res://terrain/desert_sand_roughness.png"
dest_files=["res://.godot/imported/desert_sand_roughness.png-c85e29a6965b06eac0c712e74b047821.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://eqbk8dx3px02"
path="res://.godot/imported/dry_mud_albedo.png-6844f23cea910070ff2cf0d2e02e4e06.ctex"
path="res://.godot/imported/dry_mud_albedo.png-0fe12d446fc4e93e2a42ccc9303ac693.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/dry_mud_albedo.png"
dest_files=["res://.godot/imported/dry_mud_albedo.png-6844f23cea910070ff2cf0d2e02e4e06.ctex"]
source_file="res://terrain/dry_mud_albedo.png"
dest_files=["res://.godot/imported/dry_mud_albedo.png-0fe12d446fc4e93e2a42ccc9303ac693.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bwqtubithbfsy"
path="res://.godot/imported/dry_mud_displacement.png-84ac0ae4e00848577ccfe89d2c4787e8.ctex"
path="res://.godot/imported/dry_mud_displacement.png-459305492d2f8cba90344ad67808cc02.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/dry_mud_displacement.png"
dest_files=["res://.godot/imported/dry_mud_displacement.png-84ac0ae4e00848577ccfe89d2c4787e8.ctex"]
source_file="res://terrain/dry_mud_displacement.png"
dest_files=["res://.godot/imported/dry_mud_displacement.png-459305492d2f8cba90344ad67808cc02.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://c86r5niid1hp1"
path="res://.godot/imported/dry_mud_normal.png-6211d1992b291d2e0174ec9de201218d.ctex"
path="res://.godot/imported/dry_mud_normal.png-bbedc7a55f837a837bfdab39fcbc7272.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/dry_mud_normal.png"
dest_files=["res://.godot/imported/dry_mud_normal.png-6211d1992b291d2e0174ec9de201218d.ctex"]
source_file="res://terrain/dry_mud_normal.png"
dest_files=["res://.godot/imported/dry_mud_normal.png-bbedc7a55f837a837bfdab39fcbc7272.ctex"]
[params]

View File

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://7xnkstxa6q65"
path="res://.godot/imported/dry_mud_roughness.png-35dfe15111e09484a06efd2f6e55b73b.ctex"
path="res://.godot/imported/dry_mud_roughness.png-0d5a4572411f8d454ef69ed8b252672b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://terrain/paints/dry_mud_roughness.png"
dest_files=["res://.godot/imported/dry_mud_roughness.png-35dfe15111e09484a06efd2f6e55b73b.ctex"]
source_file="res://terrain/dry_mud_roughness.png"
dest_files=["res://.godot/imported/dry_mud_roughness.png-0d5a4572411f8d454ef69ed8b252672b.ctex"]
[params]

Binary file not shown.

Binary file not shown.

BIN
terrain/paints/default_terrain_paint.res (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
terrain/paints/dry_mud_terrain_paint.res (Stored with Git LFS)

Binary file not shown.

View File

@ -1,13 +0,0 @@
class_name TerrainPaint extends Resource
##
##
##
@export
var albedo_map: Texture2D = null
##
##
##
@export
var normal_map: Texture2D = null

View File

@ -1,57 +0,0 @@
shader_type spatial;
const int MAP_COUNT = 4;
uniform sampler2D[MAP_COUNT] ALBEDO_MAPS;
uniform float MAX_HEIGHT = 100.0;
uniform sampler2D[MAP_COUNT] NORMAL_MAPS;
uniform vec2 SIZE;
uniform sampler2D TERRAIN_MAP : hint_default_transparent, repeat_disable;
void fragment() {
vec2 uv = UV * (SIZE / 2.0);
vec2 uv_alt = uv * -0.5;
vec3 splat_map = texture(TERRAIN_MAP, UV).rgb;
float blank = clamp(1.0 - splat_map.r - splat_map.g - splat_map.b, 0.0, 1.0);
ALBEDO = blank * 0.5 * (texture(ALBEDO_MAPS[0], uv).rgb + texture(ALBEDO_MAPS[0], uv_alt).rgb);
for (int i = 0; i < (MAP_COUNT - 1); i += 1) {
int map_index = i + 1;
ALBEDO += splat_map[i] * 0.5 * (texture(ALBEDO_MAPS[map_index], uv).rgb +
texture(ALBEDO_MAPS[map_index], uv_alt).rgb);
}
NORMAL_MAP = blank * 0.5 *
(texture(NORMAL_MAPS[0], uv).rgb + texture(NORMAL_MAPS[0], uv_alt).rgb);
for (int i = 0; i < (MAP_COUNT - 1); i += 1) {
int map_index = i + 1;
NORMAL_MAP += splat_map[i] * 0.5 * (texture(NORMAL_MAPS[map_index], uv).rgb +
texture(NORMAL_MAPS[map_index], uv_alt).rgb);
}
}
float height(vec2 uv) {
return MAX_HEIGHT * texture(TERRAIN_MAP, uv).a;
}
void vertex() {
VERTEX.y = height(UV);
vec2 texel_size = UV / SIZE;
vec4 h = vec4(
height(UV + (texel_size * vec2(0, -1))),
height(UV + (texel_size * vec2(-1, 0))),
height(UV + (texel_size * vec2( 1, 0))),
height(UV + (texel_size * vec2( 0, 1))));
NORMAL = normalize(vec3(h.y - h.z, 2.0, h.x - h.w));
}

View File

@ -1,87 +1,169 @@
@tool
class_name TerrainInstance3D extends GeometryInstance3D
##
## Identifier constant for a paint slot channel.
##
enum PaintSlot {
ERASE,
RED,
GREEN,
BLUE,
}
const _DETAIL := 2
var _albedo_map_textures: Array[Texture2D] = [null, null, null, null]
var _mesh := PlaneMesh.new()
var _material := ShaderMaterial.new()
var _normal_map_textures: Array[Texture2D] = [null, null, null, null]
var _mesh := PlaneMesh.new()
##
## The height scale range that is used to determine the minimums and maximums of the terrain map.
##
##
@export
var height_scale := 100.0:
var size: Vector2i = Vector2i.ZERO:
get:
return height_scale
return size
set(value):
_material.set_shader_parameter("MAX_HEIGHT", value)
var width := maxi(value.x, 0)
var height := maxi(value.y, 0)
height_scale = max(value, 0.0)
if (width != size.x) or (height != size.y):
if (width == 0) and (height == 0):
RenderingServer.instance_set_base(self.get_instance(), RID())
var _size := Vector2i.ZERO
else:
self._mesh.subdivide_width = width * _DETAIL
self._mesh.subdivide_depth = height * _DETAIL
self._mesh.size = value
self._material.set_shader_parameter("SIZE", Vector2(value))
RenderingServer.instance_set_base(self.get_instance(), self._mesh)
size = value
##
##
##
@export
var albedo_map: Texture2D = null:
get:
return albedo_map
set(value):
self._material.set_shader_parameter("ALBEDO_MAP_0", value)
albedo_map = value
##
##
##
@export
var albedo_map_b: Texture2D = null:
get:
return albedo_map_b
set(value):
self._material.set_shader_parameter("ALBEDO_MAP_B", value)
albedo_map_b = value
##
##
##
@export
var albedo_map_g: Texture2D = null:
get:
return albedo_map_g
set(value):
self._material.set_shader_parameter("ALBEDO_MAP_G", value)
albedo_map_g = value
##
##
##
@export
var albedo_map_r: Texture2D = null:
get:
return albedo_map_r
set(value):
self._material.set_shader_parameter("ALBEDO_MAP_R", value)
albedo_map_r = value
##
##
##
@export
var normal_map: Texture2D = null:
get:
return normal_map
set(value):
self._material.set_shader_parameter("NORMAL_MAP_0", value)
normal_map = value
##
##
##
@export
var normal_map_b: Texture2D = null:
get:
return normal_map_b
set(value):
self._material.set_shader_parameter("NORMAL_MAP_B", value)
normal_map_b = value
##
##
##
@export
var normal_map_g: Texture2D = null:
get:
return normal_map_g
set(value):
self._material.set_shader_parameter("NORMAL_MAP_G", value)
normal_map_g = value
##
##
##
@export
var normal_map_r: Texture2D = null:
get:
return normal_map_r
set(value):
self._material.set_shader_parameter("NORMAL_MAP_R", value)
normal_map_r = value
##
##
##
@export
var terrain_map: Texture2D = null:
get:
return terrain_map
set(value):
self._material.set_shader_parameter("TERRAIN_MAP", value)
terrain_map = value
##
##
##
@export
var max_height := 100.0:
get:
return max_height
set(value):
self._material.set_shader_parameter("MAX_HEIGHT", value)
max_height = max(value, 0.0)
func _init() -> void:
_material.shader = preload("res://terrain/terrain.gdshader")
self._material.shader = preload("res://terrain/terrain_shader.gdshader")
RenderingServer.instance_set_base(get_instance(), _mesh)
_mesh.surface_set_material(0, _material)
##
## Returns the size of the terrain mesh (as in-engine units).
##
func get_size() -> Vector2i:
return _size
##
## 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
## [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
else:
_albedo_map_textures[paint_slot] = paint.albedo_map
_normal_map_textures[paint_slot] = paint.normal_map
_material.set_shader_parameter("ALBEDO_MAPS", _albedo_map_textures)
_material.set_shader_parameter("NORMAL_MAPS", _normal_map_textures)
##
## Updates the terrain map to [code]terrain_map[/code].
##
func update_terrain(terrain_map: Texture2D) -> void:
_material.set_shader_parameter("TERRAIN_MAP", terrain_map)
self._mesh.surface_set_material(0, self._material)

View File

@ -0,0 +1,58 @@
shader_type spatial;
uniform sampler2D ALBEDO_MAP_0 : hint_default_black;
uniform sampler2D ALBEDO_MAP_R : hint_default_black;
uniform sampler2D ALBEDO_MAP_G : hint_default_black;
uniform sampler2D ALBEDO_MAP_B : hint_default_black;
uniform float MAX_HEIGHT = 100.0;
uniform sampler2D NORMAL_MAP_0 : hint_default_black;
uniform sampler2D NORMAL_MAP_R : hint_default_black;
uniform sampler2D NORMAL_MAP_G : hint_default_black;
uniform sampler2D NORMAL_MAP_B : hint_default_black;
uniform vec2 SIZE;
uniform sampler2D TERRAIN_MAP : hint_default_transparent, repeat_disable;
void fragment() {
vec2 uv = UV * (SIZE / 2.0);
vec2 uv_alt = uv * -0.5;
vec3 splat_map = texture(TERRAIN_MAP, UV).rgb;
float blank = clamp(1.0 - splat_map.r - splat_map.g - splat_map.b, 0.0, 1.0);
ALBEDO = blank * 0.5 * (texture(ALBEDO_MAP_0, uv).rgb + texture(ALBEDO_MAP_0, uv_alt).rgb);
ALBEDO += splat_map.r * 0.5 * (texture(ALBEDO_MAP_R, uv).rgb + texture(ALBEDO_MAP_R, uv_alt).rgb);
ALBEDO += splat_map.g * 0.5 * (texture(ALBEDO_MAP_G, uv).rgb + texture(ALBEDO_MAP_G, uv_alt).rgb);
ALBEDO += splat_map.b * 0.5 * (texture(ALBEDO_MAP_B, uv).rgb + texture(ALBEDO_MAP_B, uv_alt).rgb);
NORMAL_MAP = blank * 0.5 * (texture(NORMAL_MAP_0, uv).rgb + texture(NORMAL_MAP_0, uv_alt).rgb);
NORMAL_MAP += splat_map.r * 0.5 * (texture(NORMAL_MAP_R, uv).rgb + texture(NORMAL_MAP_R, uv_alt).rgb);
NORMAL_MAP += splat_map.g * 0.5 * (texture(NORMAL_MAP_G, uv).rgb + texture(NORMAL_MAP_G, uv_alt).rgb);
NORMAL_MAP += splat_map.b * 0.5 * (texture(NORMAL_MAP_B, uv).rgb + texture(NORMAL_MAP_B, uv_alt).rgb);
}
float height(vec2 uv) {
return MAX_HEIGHT * texture(TERRAIN_MAP, uv).a;
}
void vertex() {
VERTEX.y = height(UV);
vec2 texel_size = UV / SIZE;
vec4 h = vec4(
height(UV + (texel_size * vec2(0, -1))),
height(UV + (texel_size * vec2(-1, 0))),
height(UV + (texel_size * vec2( 1, 0))),
height(UV + (texel_size * vec2( 0, 1))));
NORMAL = normalize(vec3(h.y - h.z, 2.0, h.x - h.w));
}

BIN
user_interface/screen_cursor.png (Stored with Git LFS)

Binary file not shown.

View File

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://crt8tmxbpu1hl"
path="res://.godot/imported/screen_cursor.png-46ebd33cd7dfb4c0211a698e6d39e483.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://user_interface/screen_cursor.png"
dest_files=["res://.godot/imported/screen_cursor.png-46ebd33cd7dfb4c0211a698e6d39e483.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1