aboutsummaryrefslogtreecommitdiff
path: root/game/addons/zylann.hterrain/tools/brush/decal.gd
diff options
context:
space:
mode:
Diffstat (limited to 'game/addons/zylann.hterrain/tools/brush/decal.gd')
-rw-r--r--game/addons/zylann.hterrain/tools/brush/decal.gd121
1 files changed, 121 insertions, 0 deletions
diff --git a/game/addons/zylann.hterrain/tools/brush/decal.gd b/game/addons/zylann.hterrain/tools/brush/decal.gd
new file mode 100644
index 0000000..13433ed
--- /dev/null
+++ b/game/addons/zylann.hterrain/tools/brush/decal.gd
@@ -0,0 +1,121 @@
+@tool
+# Shows a cursor on top of the terrain to preview where the brush will paint
+
+# TODO Use an actual decal node, it wasn't available in Godot 3
+
+const HT_DirectMeshInstance = preload("../../util/direct_mesh_instance.gd")
+const HTerrain = preload("../../hterrain.gd")
+const HTerrainData = preload("../../hterrain_data.gd")
+const HT_Util = preload("../../util/util.gd")
+
+var _mesh_instance : HT_DirectMeshInstance
+var _mesh : PlaneMesh
+var _material = ShaderMaterial.new()
+#var _debug_mesh = CubeMesh.new()
+#var _debug_mesh_instance = null
+
+var _terrain : HTerrain = null
+
+
+func _init():
+ _material.shader = load("res://addons/zylann.hterrain/tools/brush/decal.gdshader")
+ _mesh_instance = HT_DirectMeshInstance.new()
+ _mesh_instance.set_material(_material)
+
+ _mesh = PlaneMesh.new()
+ _mesh_instance.set_mesh(_mesh)
+
+ #_debug_mesh_instance = DirectMeshInstance.new()
+ #_debug_mesh_instance.set_mesh(_debug_mesh)
+
+
+func set_size(size: float):
+ _mesh.size = Vector2(size, size)
+ # Must line up to terrain vertex policy, so must apply an off-by-one.
+ # If I don't do that, the brush will appear to wobble above the ground
+ var ss := size - 1
+ # Don't subdivide too much
+ while ss > 50:
+ ss /= 2
+ _mesh.subdivide_width = ss
+ _mesh.subdivide_depth = ss
+
+
+#func set_shape(shape_image):
+# set_size(shape_image.get_width())
+
+
+func _on_terrain_transform_changed(terrain_global_trans: Transform3D):
+ var inv = terrain_global_trans.affine_inverse()
+ _material.set_shader_parameter("u_terrain_inverse_transform", inv)
+
+ var normal_basis = terrain_global_trans.basis.inverse().transposed()
+ _material.set_shader_parameter("u_terrain_normal_basis", normal_basis)
+
+
+func set_terrain(terrain: HTerrain):
+ if _terrain == terrain:
+ return
+
+ if _terrain != null:
+ _terrain.transform_changed.disconnect(_on_terrain_transform_changed)
+ _mesh_instance.exit_world()
+ #_debug_mesh_instance.exit_world()
+
+ _terrain = terrain
+
+ if _terrain != null:
+ _terrain.transform_changed.connect(_on_terrain_transform_changed)
+ _on_terrain_transform_changed(_terrain.get_internal_transform())
+ _mesh_instance.enter_world(terrain.get_world_3d())
+ #_debug_mesh_instance.enter_world(terrain.get_world())
+
+ update_visibility()
+
+
+func set_position(p_local_pos: Vector3):
+ assert(_terrain != null)
+ assert(typeof(p_local_pos) == TYPE_VECTOR3)
+
+ # Set custom AABB (in local cells) because the decal is displaced by shader
+ var data = _terrain.get_data()
+ if data != null:
+ var r = _mesh.size / 2
+ var aabb = data.get_region_aabb( \
+ int(p_local_pos.x - r.x), \
+ int(p_local_pos.z - r.y), \
+ int(2 * r.x), \
+ int(2 * r.y))
+ aabb.position = Vector3(-r.x, aabb.position.y, -r.y)
+ _mesh.custom_aabb = aabb
+ #_debug_mesh.size = aabb.size
+
+ var trans = Transform3D(Basis(), p_local_pos)
+ var terrain_gt = _terrain.get_internal_transform()
+ trans = terrain_gt * trans
+ _mesh_instance.set_transform(trans)
+ #_debug_mesh_instance.set_transform(trans)
+
+
+# This is called very often so it should be cheap
+func update_visibility():
+ var heightmap = _get_heightmap(_terrain)
+ if heightmap == null:
+ # I do this for refcounting because heightmaps are large resources
+ _material.set_shader_parameter("u_terrain_heightmap", null)
+ _mesh_instance.set_visible(false)
+ #_debug_mesh_instance.set_visible(false)
+ else:
+ _material.set_shader_parameter("u_terrain_heightmap", heightmap)
+ _mesh_instance.set_visible(true)
+ #_debug_mesh_instance.set_visible(true)
+
+
+func _get_heightmap(terrain):
+ if terrain == null:
+ return null
+ var data = terrain.get_data()
+ if data == null:
+ return null
+ return data.get_texture(HTerrainData.CHANNEL_HEIGHT)
+