|
| 1 | +@tool |
| 2 | +class_name GLTFVehicleBody |
| 3 | +extends Resource |
| 4 | + |
| 5 | + |
| 6 | +## The input value controlling the ratio of the vehicle's angular forces. |
| 7 | +@export var angular_activation := Vector3.ZERO |
| 8 | +## The input value controlling the ratio of the vehicle's linear forces. |
| 9 | +@export var linear_activation := Vector3.ZERO |
| 10 | +## The gyroscope torque intrinsic to the vehicle, excluding torque from parts, measured in Newton-meters per radian (kg⋅m²/s²/rad). |
| 11 | +@export_custom(PROPERTY_HINT_NONE, "suffix:kg\u22C5m\u00B2/s\u00B2/rad (N\u22C5m/rad)") |
| 12 | +var gyroscope_torque := Vector3.ZERO |
| 13 | +## If non-negative, the speed in meters per second at which the vehicle should stop driving acceleration further. |
| 14 | +@export var maximum_speed: float = -1.0 |
| 15 | +## The index of the `OMI_seat` glTF node to use as the pilot / driver seat. |
| 16 | +@export var pilot_seat_index: int = -1 |
| 17 | +## The Godot node to use as the pilot seat / driver seat. |
| 18 | +var pilot_seat_node: Node3D = null |
| 19 | +## If true, the vehicle should slow its rotation down when not given angular activation input for a specific rotation. |
| 20 | +@export var angular_dampeners: bool = true |
| 21 | +## If true, the vehicle should slow itself down when not given linear activation input for a specific direction. |
| 22 | +@export var linear_dampeners: bool = false |
| 23 | +## If true, the vehicle should use a throttle for linear movement. If maximum_speed is non-negative, the throttle should be a ratio of that speed, otherwise it should be a ratio of thrust power. |
| 24 | +@export var use_throttle: bool = false |
| 25 | + |
| 26 | + |
| 27 | +static func from_node(vehicle_node: VehicleBody3D) -> GLTFVehicleBody: |
| 28 | + var ret := GLTFVehicleBody.new() |
| 29 | + if vehicle_node is PilotedVehicleBody3D: |
| 30 | + ret.angular_activation = vehicle_node.angular_activation |
| 31 | + ret.linear_activation = vehicle_node.linear_activation |
| 32 | + ret.gyroscope_torque = vehicle_node.gyroscope_torque |
| 33 | + ret.maximum_speed = vehicle_node.maximum_speed |
| 34 | + ret.pilot_seat_node = vehicle_node.pilot_seat_node |
| 35 | + ret.angular_dampeners = vehicle_node.angular_dampeners |
| 36 | + ret.linear_dampeners = vehicle_node.linear_dampeners |
| 37 | + ret.use_throttle = vehicle_node.use_throttle |
| 38 | + return ret |
| 39 | + |
| 40 | + |
| 41 | +func to_node(gltf_state: GLTFState, gltf_node: GLTFNode) -> PilotedVehicleBody3D: |
| 42 | + # Set up the body node. |
| 43 | + var vehicle_node := PilotedVehicleBody3D.new() |
| 44 | + var gltf_physics_body: GLTFPhysicsBody = gltf_node.get_additional_data(&"GLTFPhysicsBody") |
| 45 | + if gltf_physics_body == null: |
| 46 | + printerr("GLTF vehicle body: Expected the vehicle body to also be a physics body. Continuing anyway.") |
| 47 | + else: |
| 48 | + vehicle_node.mass = gltf_physics_body.mass |
| 49 | + vehicle_node.linear_velocity = gltf_physics_body.linear_velocity |
| 50 | + vehicle_node.angular_velocity = gltf_physics_body.angular_velocity |
| 51 | + vehicle_node.inertia = gltf_physics_body.inertia_diagonal |
| 52 | + vehicle_node.center_of_mass = gltf_physics_body.center_of_mass |
| 53 | + vehicle_node.center_of_mass_mode = RigidBody3D.CENTER_OF_MASS_MODE_CUSTOM |
| 54 | + # If there is a collider shape, set it up. |
| 55 | + var gltf_collider_shape: GLTFPhysicsShape = gltf_node.get_additional_data(&"GLTFPhysicsColliderShape") |
| 56 | + if gltf_collider_shape != null: |
| 57 | + _setup_shape_mesh_resource_from_index_if_needed(gltf_state, gltf_collider_shape) |
| 58 | + var col_shape: CollisionShape3D = gltf_collider_shape.to_node(true) |
| 59 | + col_shape.name = gltf_node.resource_name + "Collider" |
| 60 | + vehicle_node.add_child(col_shape) |
| 61 | + # Set up the vehicle properties. |
| 62 | + vehicle_node.angular_activation = angular_activation |
| 63 | + vehicle_node.linear_activation = linear_activation |
| 64 | + vehicle_node.gyroscope_torque = gyroscope_torque |
| 65 | + vehicle_node.maximum_speed = maximum_speed |
| 66 | + vehicle_node.angular_dampeners = angular_dampeners |
| 67 | + vehicle_node.linear_dampeners = linear_dampeners |
| 68 | + vehicle_node.use_throttle = use_throttle |
| 69 | + return vehicle_node |
| 70 | + |
| 71 | + |
| 72 | +static func from_dictionary(dict: Dictionary) -> GLTFVehicleBody: |
| 73 | + var ret := GLTFVehicleBody.new() |
| 74 | + if dict.has("angularActivation"): |
| 75 | + var ang_arr: Array = dict["angularActivation"] |
| 76 | + ret.angular_activation = Vector3(ang_arr[0], ang_arr[1], ang_arr[2]) |
| 77 | + if dict.has("linearActivation"): |
| 78 | + var lin_arr: Array = dict["linearActivation"] |
| 79 | + ret.linear_activation = Vector3(lin_arr[0], lin_arr[1], lin_arr[2]) |
| 80 | + if dict.has("gyroTorque"): |
| 81 | + var gyro_arr: Array = dict["gyroTorque"] |
| 82 | + ret.gyroscope_torque = Vector3(gyro_arr[0], gyro_arr[1], gyro_arr[2]) |
| 83 | + if dict.has("maxSpeed"): |
| 84 | + ret.maximum_speed = dict["maxSpeed"] |
| 85 | + if dict.has("pilotSeat"): |
| 86 | + ret.pilot_seat_index = dict["pilotSeat"] |
| 87 | + if dict.has("angularDampeners"): |
| 88 | + ret.angular_dampeners = dict["angularDampeners"] |
| 89 | + if dict.has("linearDampeners"): |
| 90 | + ret.linear_dampeners = dict["linearDampeners"] |
| 91 | + if dict.has("useThrottle"): |
| 92 | + ret.use_throttle = dict["useThrottle"] |
| 93 | + return ret |
| 94 | + |
| 95 | + |
| 96 | +func to_dictionary() -> Dictionary: |
| 97 | + var ret: Dictionary = {} |
| 98 | + if angular_activation != Vector3.ZERO: |
| 99 | + ret["angularActivation"] = [angular_activation.x, angular_activation.y, angular_activation.z] |
| 100 | + if linear_activation != Vector3.ZERO: |
| 101 | + ret["linearActivation"] = [linear_activation.x, linear_activation.y, linear_activation.z] |
| 102 | + if gyroscope_torque != Vector3.ZERO: |
| 103 | + ret["gyroTorque"] = [gyroscope_torque.x, gyroscope_torque.y, gyroscope_torque.z] |
| 104 | + if maximum_speed != -1.0: |
| 105 | + ret["maxSpeed"] = maximum_speed |
| 106 | + if pilot_seat_index != -1: |
| 107 | + ret["pilotSeat"] = pilot_seat_index |
| 108 | + if not angular_dampeners: # Default is true. |
| 109 | + ret["angularDampeners"] = angular_dampeners |
| 110 | + if linear_dampeners: |
| 111 | + ret["linearDampeners"] = linear_dampeners |
| 112 | + if use_throttle: |
| 113 | + ret["useThrottle"] = use_throttle |
| 114 | + return ret |
| 115 | + |
| 116 | + |
| 117 | +func _setup_shape_mesh_resource_from_index_if_needed(gltf_state: GLTFState, gltf_shape: GLTFPhysicsShape) -> void: |
| 118 | + var shape_mesh_index: int = gltf_shape.mesh_index |
| 119 | + if shape_mesh_index == -1: |
| 120 | + return # No mesh for this shape. |
| 121 | + var importer_mesh: ImporterMesh = gltf_shape.importer_mesh |
| 122 | + if importer_mesh != null: |
| 123 | + return # The mesh resource is already set up. |
| 124 | + var state_meshes: Array[GLTFMesh] = gltf_state.meshes |
| 125 | + var gltf_mesh: GLTFMesh = state_meshes[shape_mesh_index] |
| 126 | + importer_mesh = gltf_mesh.mesh |
| 127 | + gltf_shape.importer_mesh = importer_mesh |
0 commit comments