Skip to content

Commit 7385683

Browse files
committed
Add OMI_vehicle_* implementation and test files
1 parent 06b74ed commit 7385683

File tree

84 files changed

+6568
-27
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+6568
-27
lines changed

addons/omi_extensions/omi_extensions_plugin.gd

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ func _enter_tree() -> void:
1515
GLTFDocument.register_gltf_document_extension(ext)
1616
ext = GLTFDocumentExtensionOMIPhysicsJoint.new()
1717
GLTFDocument.register_gltf_document_extension(ext)
18+
ext = GLTFDocumentExtensionOMIVehicle.new()
19+
GLTFDocument.register_gltf_document_extension(ext, true)
1820
add_node_3d_gizmo_plugin(seat_gizmo_plugin)
1921

2022

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class_name GlobalGravitySetter
2+
extends Node
3+
4+
5+
@export var gravity: float = 9.80665
6+
@export var direction: Vector3 = Vector3.DOWN
7+
8+
9+
func _ready() -> void:
10+
var world_space_rid: RID = get_viewport().find_world_3d().space
11+
PhysicsServer3D.area_set_param(world_space_rid, PhysicsServer3D.AREA_PARAM_GRAVITY, gravity)
12+
PhysicsServer3D.area_set_param(world_space_rid, PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, direction)
13+
queue_free()

addons/omi_extensions/seat/omi_seat_doc_ext.gd

+7-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func _generate_scene_node(state: GLTFState, gltf_node: GLTFNode, scene_parent: N
3333
return null
3434
# If this node is both a seat and a glTF trigger, generate the Area3D-derived Seat3D node.
3535
# Else, if this is not any kind of trigger, don't generate a Seat3D/Area3D, just set node metadata later.
36-
var trigger = gltf_node.get_additional_data(&"GLTFPhysicsTrigger")
36+
var trigger = gltf_node.get_additional_data(&"GLTFPhysicsTriggerShape")
3737
if trigger == null:
3838
trigger = gltf_node.get_additional_data(&"GLTFPhysicsCompoundTriggerNodes")
3939
if trigger == null:
@@ -42,7 +42,12 @@ func _generate_scene_node(state: GLTFState, gltf_node: GLTFNode, scene_parent: N
4242
return null
4343
if trigger.body_type != "trigger":
4444
return null
45-
return Seat3D.from_points(seat_dict["back"], seat_dict["foot"], seat_dict["knee"], seat_dict.get("angle", TAU * 0.25))
45+
var seat = Seat3D.from_points(seat_dict["back"], seat_dict["foot"], seat_dict["knee"], seat_dict.get("angle", TAU * 0.25))
46+
if trigger is GLTFPhysicsShape:
47+
var shape: CollisionShape3D = trigger.to_node(true)
48+
shape.name = gltf_node.resource_name + "Shape"
49+
seat.add_child(shape)
50+
return seat
4651

4752

4853
func _import_node(_state: GLTFState, gltf_node: GLTFNode, json: Dictionary, node: Node) -> Error:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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 max_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 max_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.max_speed = vehicle_node.max_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.max_speed = max_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.max_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 max_speed != -1.0:
105+
ret["maxSpeed"] = max_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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@tool
2+
class_name GLTFVehicleHoverThruster
3+
extends Resource
4+
5+
6+
## The ratio of the maximum hover energy the hover thruster is currently using for propulsion.
7+
@export_range(0.0, 1.0, 0.01)
8+
var current_hover_ratio: float = 0.0
9+
## The ratio of the maximum gimbal angles the hover thruster is rotated to. The vector length may not be longer than 1.0.
10+
@export var current_gimbal_ratio := Vector2(0.0, 0.0)
11+
## The maximum hover energy in Newton-meters (N⋅m or kg⋅m²/s²) that the hover thruster can provide.
12+
@export_custom(PROPERTY_HINT_NONE, "suffix:kg\u22C5m\u00B2/s\u00B2 (N\u22C5m)")
13+
var max_hover_energy: float = 0.0
14+
## The maximum angle the hover thruster can gimbal or rotate in radians.
15+
@export_custom(PROPERTY_HINT_NONE, "suffix:rad")
16+
var max_gimbal: float = 0.0
17+
18+
19+
static func from_node(thruster_node: VehicleHoverThruster3D) -> GLTFVehicleHoverThruster:
20+
var ret := GLTFVehicleHoverThruster.new()
21+
ret.current_hover_ratio = thruster_node.current_hover_ratio
22+
ret.current_gimbal_ratio = thruster_node.current_gimbal_ratio
23+
ret.max_hover_energy = thruster_node.max_hover_energy
24+
ret.max_gimbal = thruster_node.max_gimbal
25+
return ret
26+
27+
28+
func to_node() -> VehicleHoverThruster3D:
29+
var thruster_node := VehicleHoverThruster3D.new()
30+
thruster_node.current_hover_ratio = current_hover_ratio
31+
thruster_node.current_gimbal_ratio = current_gimbal_ratio
32+
thruster_node.max_hover_energy = max_hover_energy
33+
thruster_node.max_gimbal = max_gimbal
34+
return thruster_node
35+
36+
37+
static func from_dictionary(dict: Dictionary) -> GLTFVehicleHoverThruster:
38+
var ret := GLTFVehicleHoverThruster.new()
39+
if dict.has("currentHoverRatio"):
40+
ret.current_force_ratio = dict["currentHoverRatio"]
41+
if dict.has("currentGimbalRatio"):
42+
ret.current_gimbal_ratio = dict["currentGimbalRatio"]
43+
if dict.has("maxHoverEnergy"):
44+
ret.max_hover_energy = dict["maxHoverEnergy"]
45+
if dict.has("maxGimbal"):
46+
ret.max_gimbal = dict["maxGimbal"]
47+
return ret
48+
49+
50+
func to_dictionary() -> Dictionary:
51+
var ret: Dictionary = {}
52+
ret["maxHoverEnergy"] = max_hover_energy
53+
if current_hover_ratio != 0.0:
54+
ret["currentHoverRatio"] = current_hover_ratio
55+
if current_gimbal_ratio != Vector2.ZERO:
56+
ret["currentGimbalRatio"] = current_gimbal_ratio
57+
if max_gimbal != 0.0:
58+
ret["maxGimbal"] = max_gimbal
59+
return ret
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@tool
2+
class_name GLTFVehicleThruster
3+
extends Resource
4+
5+
6+
## The ratio of the maximum thrust force the thruster is currently using for propulsion.
7+
@export_range(0.0, 1.0, 0.01)
8+
var current_force_ratio: float = 0.0
9+
## The ratio of the maximum gimbal angles the thruster is rotated to. The vector length may not be longer than 1.0.
10+
@export var current_gimbal_ratio := Vector2(0.0, 0.0)
11+
## The maximum thrust force in Newtons (kg⋅m/s²) that the thruster can provide.
12+
@export_custom(PROPERTY_HINT_NONE, "suffix:kg\u22C5m/s\u00B2 (N)")
13+
var max_force: float = 0.0
14+
## The maximum angle the thruster can gimbal or rotate in radians.
15+
@export_custom(PROPERTY_HINT_NONE, "suffix:rad")
16+
var max_gimbal: float = 0.0
17+
18+
19+
static func from_node(thruster_node: VehicleThruster3D) -> GLTFVehicleThruster:
20+
var ret := GLTFVehicleThruster.new()
21+
ret.current_force_ratio = thruster_node.current_force_ratio
22+
ret.current_gimbal_ratio = thruster_node.current_gimbal_ratio
23+
ret.max_force = thruster_node.max_force
24+
ret.max_gimbal = thruster_node.max_gimbal
25+
return ret
26+
27+
28+
func to_node() -> VehicleThruster3D:
29+
var thruster_node := VehicleThruster3D.new()
30+
thruster_node.current_force_ratio = current_force_ratio
31+
thruster_node.current_gimbal_ratio = current_gimbal_ratio
32+
thruster_node.max_force = max_force
33+
thruster_node.max_gimbal = max_gimbal
34+
return thruster_node
35+
36+
37+
static func from_dictionary(dict: Dictionary) -> GLTFVehicleThruster:
38+
var ret := GLTFVehicleThruster.new()
39+
if dict.has("currentForceRatio"):
40+
ret.current_force_ratio = dict["currentForceRatio"]
41+
if dict.has("currentGimbalRatio"):
42+
ret.current_gimbal_ratio = dict["currentGimbalRatio"]
43+
if dict.has("maxForce"):
44+
ret.max_force = dict["maxForce"]
45+
if dict.has("maxGimbal"):
46+
ret.max_gimbal = dict["maxGimbal"]
47+
return ret
48+
49+
50+
func to_dictionary() -> Dictionary:
51+
var ret: Dictionary = {}
52+
ret["maxForce"] = max_force
53+
if current_force_ratio != 0.0:
54+
ret["currentForceRatio"] = current_force_ratio
55+
if current_gimbal_ratio != Vector2.ZERO:
56+
ret["currentGimbalRatio"] = current_gimbal_ratio
57+
if max_gimbal != 0.0:
58+
ret["maxGimbal"] = max_gimbal
59+
return ret

0 commit comments

Comments
 (0)