aboutsummaryrefslogtreecommitdiff
path: root/game/addons/zylann.hterrain/tools/terrain_preview.gd
blob: 1c64b6d3562d3144ed11e4f956b2f4a97fdfaa1a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
@tool
extends SubViewportContainer

const PREVIEW_MESH_LOD = 2

const HTerrainMesher = preload("../hterrain_mesher.gd")
const HT_Util = preload("../util/util.gd")

signal dragged(relative, button_mask)

@onready var _viewport : SubViewport = $Viewport
@onready var _mesh_instance : MeshInstance3D = $Viewport/MeshInstance
@onready var _camera : Camera3D = $Viewport/Camera
@onready var _light : DirectionalLight3D = $Viewport/DirectionalLight

# Use the simplest shader
var _shader : Shader = load("res://addons/zylann.hterrain/shaders/simple4_lite.gdshader")
var _yaw := 0.0
var _pitch := -PI / 6.0
var _distance := 0.0
var _default_distance := 0.0
var _sea_outline : MeshInstance3D = null
var _sea_plane : MeshInstance3D = null
var _mesh_resolution := 0


func _ready():
   if _sea_outline == null:
      var mesh := HT_Util.create_wirecube_mesh()
      var mat2 := StandardMaterial3D.new()
      mat2.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
      mat2.albedo_color = Color(0, 0.5, 1)
      mesh.surface_set_material(0, mat2)
      _sea_outline = MeshInstance3D.new()
      _sea_outline.mesh = mesh
      _viewport.add_child(_sea_outline)
   
   if _sea_plane == null:
      var mesh := PlaneMesh.new()
      mesh.size = Vector2(1, 1)
      var mat2 := StandardMaterial3D.new()
      mat2.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
      mat2.albedo_color = Color(0, 0.5, 1, 0.5)
      mat2.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
      mesh.material = mat2
      _sea_plane = MeshInstance3D.new()
      _sea_plane.mesh = mesh
      _sea_plane.hide()
      _viewport.add_child(_sea_plane)


func setup(heights_texture: Texture2D, normals_texture: Texture2D):
   var terrain_size := heights_texture.get_width()
   var mesh_resolution := terrain_size / PREVIEW_MESH_LOD
   
   if _mesh_resolution != mesh_resolution or not (_mesh_instance.mesh is ArrayMesh):
      _mesh_resolution = mesh_resolution
      var mesh := HTerrainMesher.make_flat_chunk(
         _mesh_resolution, _mesh_resolution, PREVIEW_MESH_LOD, 0)
      _mesh_instance.mesh = mesh
      _default_distance = _mesh_instance.get_aabb().size.x
      _distance = _default_distance
      #_mesh_instance.translation -= 0.5 * Vector3(terrain_size, 0, terrain_size)
      _update_camera()

   var mat : ShaderMaterial = _mesh_instance.mesh.surface_get_material(0)
   
   if mat == null:
      mat = ShaderMaterial.new()
      mat.shader = _shader
      _mesh_instance.mesh.surface_set_material(0, mat)
         
   mat.set_shader_parameter("u_terrain_heightmap", heights_texture)
   mat.set_shader_parameter("u_terrain_normalmap", normals_texture)
   mat.set_shader_parameter("u_terrain_inverse_transform", Transform3D())
   mat.set_shader_parameter("u_terrain_normal_basis", Basis())

   var aabb := _mesh_instance.get_aabb()
   _sea_outline.scale = aabb.size

   aabb = _mesh_instance.get_aabb()
   _sea_plane.scale = Vector3(aabb.size.x, 1, aabb.size.z)
   _sea_plane.position = Vector3(aabb.size.x, 0, aabb.size.z) / 2.0


func set_sea_visible(visible: bool):
   _sea_plane.visible = visible


func set_shadows_enabled(enabled: bool):
   _light.shadow_enabled = enabled


func _update_camera():
   var aabb := _mesh_instance.get_aabb()
   var target := aabb.position + 0.5 * aabb.size
   var trans := Transform3D()
   trans.basis = Basis(Quaternion(Vector3(0, 1, 0), _yaw) * Quaternion(Vector3(1, 0, 0), _pitch))
   var back := trans.basis.z
   trans.origin = target + back * _distance
   _camera.transform = trans


func cleanup():
   if _mesh_instance != null:
      var mat : ShaderMaterial = _mesh_instance.mesh.surface_get_material(0)
      assert(mat != null)
      mat.set_shader_parameter("u_terrain_heightmap", null)
      mat.set_shader_parameter("u_terrain_normalmap", null)


func _gui_input(event: InputEvent):
   if HT_Util.is_in_edited_scene(self):
      return
   
   if event is InputEventMouseMotion:
      if event.button_mask & MOUSE_BUTTON_MASK_MIDDLE:
         var d : Vector2 = 0.01 * event.relative
         _yaw -= d.x
         _pitch -= d.y
         _update_camera()
      else:
         var rel : Vector2 = 0.01 * event.relative
         # Align dragging to view rotation
         rel = -rel.rotated(-_yaw)
         dragged.emit(rel, event.button_mask)
   
   elif event is InputEventMouseButton:
      if event.pressed:
         
         var factor := 1.2
         var max_factor := 10.0
         var min_distance := _default_distance / max_factor
         var max_distance := _default_distance
         
         # Zoom in/out
         if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
            _distance = clampf(_distance * factor, min_distance, max_distance)
            _update_camera()

         elif event.button_index == MOUSE_BUTTON_WHEEL_UP:
            _distance = clampf(_distance / factor, min_distance, max_distance)
            _update_camera()