122 lines
3.5 KiB
GDScript3
122 lines
3.5 KiB
GDScript3
|
@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)
|
||
|
|