123 lines
3.7 KiB
GDScript
123 lines
3.7 KiB
GDScript
tool
|
|
|
|
const Logger = preload("./util/logger.gd")
|
|
|
|
var _shape_rid = RID()
|
|
var _body_rid = RID()
|
|
var _terrain_transform = Transform()
|
|
var _terrain_data = null
|
|
var _logger = Logger.get_for(self)
|
|
|
|
|
|
func _init(attached_node: Node, initial_layer: int, initial_mask: int):
|
|
_logger.debug("HTerrainCollider: creating body")
|
|
assert(attached_node != null)
|
|
_shape_rid = PhysicsServer.shape_create(PhysicsServer.SHAPE_HEIGHTMAP)
|
|
_body_rid = PhysicsServer.body_create(PhysicsServer.BODY_MODE_STATIC)
|
|
|
|
PhysicsServer.body_set_collision_layer(_body_rid, initial_layer)
|
|
PhysicsServer.body_set_collision_mask(_body_rid, initial_mask)
|
|
|
|
# TODO This is an attempt to workaround https://github.com/godotengine/godot/issues/24390
|
|
PhysicsServer.body_set_ray_pickable(_body_rid, false)
|
|
|
|
# TODO This is a workaround to https://github.com/godotengine/godot/issues/25304
|
|
PhysicsServer.shape_set_data(_shape_rid, {
|
|
"width": 2,
|
|
"depth": 2,
|
|
"heights": PoolRealArray([0, 0, 0, 0]),
|
|
"min_height": -1,
|
|
"max_height": 1
|
|
})
|
|
|
|
PhysicsServer.body_add_shape(_body_rid, _shape_rid)
|
|
|
|
# This makes collision hits report the provided object as `collider`
|
|
PhysicsServer.body_attach_object_instance_id(_body_rid, attached_node.get_instance_id())
|
|
|
|
|
|
func set_collision_layer(layer: int):
|
|
PhysicsServer.body_set_collision_layer(_body_rid, layer)
|
|
|
|
|
|
func set_collision_mask(mask: int):
|
|
PhysicsServer.body_set_collision_mask(_body_rid, mask)
|
|
|
|
|
|
func _notification(what):
|
|
if what == NOTIFICATION_PREDELETE:
|
|
_logger.debug("Destroy HTerrainCollider")
|
|
PhysicsServer.free_rid(_body_rid)
|
|
# The shape needs to be freed after the body, otherwise the engine crashes
|
|
PhysicsServer.free_rid(_shape_rid)
|
|
|
|
|
|
func set_transform(transform):
|
|
assert(_body_rid != RID())
|
|
_terrain_transform = transform
|
|
_update_transform()
|
|
|
|
|
|
func set_world(world):
|
|
assert(_body_rid != RID())
|
|
PhysicsServer.body_set_space(_body_rid, world.get_space() if world != null else RID())
|
|
|
|
|
|
func create_from_terrain_data(terrain_data):
|
|
assert(terrain_data != null)
|
|
assert(not terrain_data.is_locked())
|
|
_logger.debug("HTerrainCollider: setting up heightmap")
|
|
|
|
_terrain_data = terrain_data
|
|
|
|
var aabb = terrain_data.get_aabb()
|
|
|
|
var width = terrain_data.get_resolution()
|
|
var depth = terrain_data.get_resolution()
|
|
var height = aabb.size.y
|
|
|
|
var shape_data = {
|
|
"width": terrain_data.get_resolution(),
|
|
"depth": terrain_data.get_resolution(),
|
|
"heights": terrain_data.get_all_heights(),
|
|
"min_height": aabb.position.y,
|
|
"max_height": aabb.end.y
|
|
}
|
|
|
|
PhysicsServer.shape_set_data(_shape_rid, shape_data)
|
|
|
|
_update_transform(aabb)
|
|
|
|
|
|
func _update_transform(aabb=null):
|
|
if _terrain_data == null:
|
|
_logger.debug("HTerrainCollider: terrain data not set yet")
|
|
return
|
|
|
|
if aabb == null:
|
|
aabb = _terrain_data.get_aabb()
|
|
|
|
var width = _terrain_data.get_resolution()
|
|
var depth = _terrain_data.get_resolution()
|
|
var height = aabb.size.y
|
|
|
|
#_terrain_transform
|
|
|
|
var trans
|
|
var v = Engine.get_version_info()
|
|
if v.major == 3 and v.minor <= 1:
|
|
# Bullet centers the shape to its overall AABB so we need to move it to match the visuals
|
|
trans = Transform(Basis(), 0.5 * Vector3(width, height, depth) + Vector3(0, aabb.position.y, 0))
|
|
else:
|
|
# In 3.2, vertical centering changed.
|
|
# https://github.com/godotengine/godot/pull/28326
|
|
trans = Transform(Basis(), 0.5 * Vector3(width - 1, 0, depth - 1))
|
|
|
|
# And then apply the terrain transform
|
|
trans = _terrain_transform * trans
|
|
|
|
PhysicsServer.body_set_state(_body_rid, PhysicsServer.BODY_STATE_TRANSFORM, trans)
|
|
# Cannot use shape transform when scaling is involved,
|
|
# because Godot is undoing that scale for some reason.
|
|
# See https://github.com/Zylann/godot_heightmap_plugin/issues/70
|
|
#PhysicsServer.body_set_shape_transform(_body_rid, 0, trans)
|