diff --git a/extensions/2.0/OMI_vehicle_body/README.md b/extensions/2.0/OMI_vehicle_body/README.md index 33a1e1c..4318a44 100644 --- a/extensions/2.0/OMI_vehicle_body/README.md +++ b/extensions/2.0/OMI_vehicle_body/README.md @@ -170,13 +170,28 @@ The actual control scheme is implementation-defined. It may be W and Aside from `"useThrottle"` determining how input affects activation, there is also the matter of how throttle affects the vehicle's thrust. If `"maxSpeed"` is non-negative, the throttle should be a ratio of that speed, with the thrust adjusting so that the vehicle meets the target speed. Otherwise, the throttle should be a ratio of the thrust power, allowing the throttle to control the ratio of the vehicle's acceleration power. +### glTF Object Model + +The following JSON pointers are defined representing mutable properties defined by this extension, for use with the glTF Object Model including extensions such as `KHR_animation_pointer` and `KHR_interactivity`: + +| JSON Pointer | Object Model Type | +| --------------------------------------------------------- | ----------------- | +| `/nodes/{}/extensions/OMI_vehicle_body/angularActivation` | `float3` | +| `/nodes/{}/extensions/OMI_vehicle_body/linearActivation` | `float3` | +| `/nodes/{}/extensions/OMI_vehicle_body/gyroTorque` | `float3` | +| `/nodes/{}/extensions/OMI_vehicle_body/maxSpeed` | `float` | +| `/nodes/{}/extensions/OMI_vehicle_body/angularDampeners` | `boolean` | +| `/nodes/{}/extensions/OMI_vehicle_body/linearDampeners` | `boolean` | +| `/nodes/{}/extensions/OMI_vehicle_body/useThrottle` | `boolean` | + ### JSON Schema See [schema/node.OMI_vehicle_body.schema.json](schema/node.OMI_vehicle_body.schema.json). ## Known Implementations -- Godot Engine +- Basis VR: https://github.com/BasisVR/Basis/pull/442 +- Godot Engine: https://github.com/omigroup/omi-godot/tree/main/addons/omi_extensions/vehicle ## Resources: diff --git a/extensions/2.0/OMI_vehicle_body/mappings.md b/extensions/2.0/OMI_vehicle_body/mappings.md index 534c1c1..692bce6 100644 --- a/extensions/2.0/OMI_vehicle_body/mappings.md +++ b/extensions/2.0/OMI_vehicle_body/mappings.md @@ -4,7 +4,7 @@ This document describes how the OMI_vehicle extensions map to different games, g The term "import" refers to bringing the OMI_vehicle extension data into a game or game engine. The term "export" refers to taking objects from a game or game engine and converting them into OMI_vehicle extension data. -In general, every property in the OMI_vehicle extensions is listed in the tables below, except that the "current" properties are omitted in most cases, because these are usually determined at runtime and do not have any special notes for import/export. +In general, every property in the OMI_vehicle extensions is listed in the tables below, except that the "target" properties are omitted in most cases, because these are usually determined at runtime and do not have any special notes for import/export. In alphabetical order: @@ -36,7 +36,7 @@ Empyrion also has "Auto Rotate", which is a button that can be held to automatic | **maxForce** | Depends on thruster type | Comes in various sizes from 55 kN to 920 MN. | | **maxGimbal** | Not available | Empyrion thrusters do not gimbal. | -Empyrion allows toggling thrusters on and off, but does not have an override for the current force and current gimbal of a thruster. +Empyrion allows toggling thrusters on and off, but does not have an override for the target force and target gimbal of a thruster. For hover thrusters, Empyrion's hover engines have a force measured in kN, the same as non-hover thrusters, but are handicapped so that they stop working beyond a certain height above the ground. For OMI_vehicle_hover_thruster, there is no artificial height limit, instead they operate with a force that is proportional to the distance from the ground - the closer to the ground, the more force they provide. The OMI_vehicle_hover_thruster `maxHoverEnergy` property is measured in Newton-meters (N⋅m or kg⋅m²/s² or Joules), so dividing this number by the amount of meters above the ground will give the force in Newtons (N). @@ -121,8 +121,8 @@ Jolt is a physics engine which has a built-in vehicle system, designed for cars | OMI_vehicle_wheel | Jolt Physics | Additional Notes | | -------------------------------- | ----------------------------- | --------------------------------------------------------------------------------- | -| **currentForceRatio** | Unknown | | -| **currentSteeringRatio** | Steer Angle | | +| **targetForceRatio** | Unknown | | +| **targetSteeringRatio** | Steer Angle | | | **maxForce** | Unknown | | | **maxSteeringAngle** | WheelSettingsWV MaxSteerAngle | Radians. | | **physicsMaterial** | WheelSettingsWV Friction | Separate LongitudinalFriction and LateralFriction settings in Jolt. | @@ -150,9 +150,9 @@ Kerbal Space Program vehicles can be rockets, planes, rovers, and more. | OMI_vehicle_wheel | KSP Wheels | Additional Notes | | -------------------------------- | --------------------- | --------------------------------------------------------------------------------------------- | -| **currentForceRatio** | Motor settings | Motors can be toggled, and are otherwise controlled by the vehicle-wide linear activation. | -| **currentSteeringRatio** | Steering settings | Steering can be toggled, and are otherwise controlled by the vehicle-wide angular activation. | -| **maxForce** | Depends on wheel type | Kerbal Space Program provides several pre-set wheels. | +| **targetForceRatio** | Motor settings | Motors can be toggled, and are otherwise controlled by the vehicle-wide linear activation. | +| **targetSteeringRatio** | Steering settings | Steering can be toggled, and are otherwise controlled by the vehicle-wide angular activation. | +| **maxPropulsionForce** | Depends on wheel type | Kerbal Space Program provides several pre-set wheels. | | **maxSteeringAngle** | Depends on wheel type | Kerbal Space Program provides several pre-set wheels. | | **physicsMaterial** | Friction Control | Kerbal Space Program only has wheel friction, not other material properties. | | **radius** | Depends on wheel type | Kerbal Space Program provides several pre-set wheels. | @@ -164,8 +164,8 @@ Kerbal Space Program vehicles can be rockets, planes, rovers, and more. | OMI_vehicle_thruster | KSP Engines | Additional Notes | | ---------------------- | ------------------------ | ----------------------------------------------------------------------------- | -| **currentForceRatio** | Throttle settings | Engine thrust can be toggled and limited, up to the throttle amount. | -| **currentGimbalRatio** | Gimbal settings | Engines can gimbal like real rocket engines. Gimbal can be limited or locked. | +| **targetForceRatio** | Throttle settings | Engine thrust can be toggled and limited, up to the throttle amount. | +| **targetGimbalRatio** | Gimbal settings | Engines can gimbal like real rocket engines. Gimbal can be limited or locked. | | **maxForce** | Depends on thruster type | Kerbal Space Program provides many pre-set thrusters. | | **maxGimbal** | Depends on thruster type | Kerbal Space Program provides many pre-set thrusters. | @@ -184,9 +184,9 @@ Space Engineers vehicles are general-purpose. They can be spacecraft, cars, and | OMI_vehicle_wheel | Space Engineers Wheels | Additional Notes | | -------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------- | -| **currentForceRatio** | Propulsion Override | Space Engineers allows overriding force per-wheel directly by the player. | -| **currentSteeringRatio** | Steer Override | Space Engineers allows overriding steering per-wheel directly by the player. | -| **maxForce** | Power & Propulsion | Each wheel type can go up to a certain force, and the Power setting is a multiplier (0-1). | +| **targetForceRatio** | Propulsion Override | Space Engineers allows overriding force per-wheel directly by the player. | +| **targetSteeringRatio** | Steer Override | Space Engineers allows overriding steering per-wheel directly by the player. | +| **maxPropulsionForce** | Power & Propulsion | Each wheel type can go up to a certain force, and the Power setting is a multiplier (0-1). | | **maxSteeringAngle** | Steering Angle | This is the maximum. | | **physicsMaterial** | Friction | Space Engineers only specifies friction, not other material properties. | | **radius** | Depends on wheel type | Space Engineers provides several pre-set wheel sizes. | @@ -200,8 +200,8 @@ Space Engineers vehicles are general-purpose. They can be spacecraft, cars, and | OMI_vehicle_thruster | Space Engineers Thrusters | Additional Notes | | ---------------------- | ------------------------- | -------------------------------------------------------------------------------------------------- | -| **currentForceRatio** | Thrust Override | Space Engineers allows overriding per-thruster thrust directly by the player. | -| **currentGimbalRatio** | Not available | Space Engineers thrusters do not gimbal. | +| **targetForceRatio** | Thrust Override | Space Engineers allows overriding per-thruster thrust directly by the player. | +| **targetGimbalRatio** | Not available | Space Engineers thrusters do not gimbal. | | **maxForce** | Depends on thruster type | Space Engineers provides several pre-set thrusters (ion: 14.4 kN, 172.8 kN, 345.6 kN, and 4.3 MN). | | **maxGimbal** | Not available | Space Engineers thrusters do not gimbal. | @@ -220,9 +220,9 @@ VRChat does not have built-in vehicles, but there are third-party solutions buil | OMI_vehicle_wheel | Sacc Wheels | Additional Notes | | -------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------ | -| **currentForceRatio** | Throttle controls | Sacc allows controlling throttle in many ways. | -| **currentSteeringRatio** | Steering controls | Sacc allows controlling steering in many ways including grabbing a steering wheel in VR. | -| **maxForce** | Engine Influence | Sacc also requires a wheel to be a drive wheel for it to apply force. | +| **targetForceRatio** | Throttle controls | Sacc allows controlling throttle in many ways. | +| **targetSteeringRatio** | Steering controls | Sacc allows controlling steering in many ways including grabbing a steering wheel in VR. | +| **maxPropulsionForce** | Engine Influence | Sacc also requires a wheel to be a drive wheel for it to apply force. | | **maxSteeringAngle** | Steering Wheel Degrees | Sacc defines this per-body using degrees, OMI_vehicle_wheel uses radians and is per-wheel. | | **physicsMaterial** | Wheel Slow Down & Grip | Sacc defines "CurrentGrip" and "CurrentWheelSlowDown" instead of friction. | | **radius** | Wheel Radius | | diff --git a/extensions/2.0/OMI_vehicle_body/schema/node.OMI_vehicle_body.schema.json b/extensions/2.0/OMI_vehicle_body/schema/node.OMI_vehicle_body.schema.json index 7b55d82..7757a2e 100644 --- a/extensions/2.0/OMI_vehicle_body/schema/node.OMI_vehicle_body.schema.json +++ b/extensions/2.0/OMI_vehicle_body/schema/node.OMI_vehicle_body.schema.json @@ -36,7 +36,7 @@ }, "maxSpeed": { "type": "number", - "description": "If non-negative, the speed in meters per second at which the vehicle should stop driving acceleration further.", + "description": "If non-negative, the speed in meters per second at which the vehicle should stop driving acceleration further. If throttle is used, activation is a ratio of this speed if positive, or a ratio of thrust power if negative.", "default": -1.0 }, "pilotSeat": { @@ -56,7 +56,7 @@ }, "useThrottle": { "type": "boolean", - "description": "If true, the vehicle should use a throttle for forward movement. If maxSpeed is non-negative, the throttle should be a ratio of that speed, otherwise it should be a ratio of thrust power.", + "description": "If true, the vehicle should use a throttle for linear movement. If maxSpeed is non-negative, the throttle should be a ratio of that speed, otherwise it should be a ratio of thrust power.", "default": false }, "extensions": { }, diff --git a/extensions/2.0/OMI_vehicle_hover_thruster/README.md b/extensions/2.0/OMI_vehicle_hover_thruster/README.md index 3e3c02e..06895b0 100644 --- a/extensions/2.0/OMI_vehicle_hover_thruster/README.md +++ b/extensions/2.0/OMI_vehicle_hover_thruster/README.md @@ -16,7 +16,7 @@ Designed to be used together with the `OMI_vehicle_body` spec. ## Overview -This extension allows specifying generic thrusters in glTF scenes. +This extension allows specifying hover thrusters in glTF scenes. Hover thrusters as defined by `OMI_vehicle_hover_thruster` use hover energy for propulsion and can optionally gimbal. The experienced force is proportional to both the hover energy and the distance from the ground - the closer to the ground, the more force they provide. The `"maxHoverEnergy"` property is measured in Newton-meters (N⋅m or kg⋅m²/s²), so dividing this number by the amount of meters above the ground will give the force in Newtons (N). This extension does not define fuel, power, or any other resource that the hover thruster may consume. @@ -32,7 +32,7 @@ Hover thrusters are invisible by default. To give a thruster a visual appearance The file [hovercraft.gltf](examples/hovercraft.gltf) defines a chrome-colored hovercraft with 4 hover thrusters at the bottom corners of the hovercraft, each a child node of the hovercraft's vehicle body. The hovercraft can rotate itself by gimballing its hover thrusters. The hovercraft also has a pilot seat, allowing a player to sit in it to control the vehicle. This can be imported into a game that supports OMI_vehicle_thruster to have a controllable hovercraft. -For this example, the only source of thrust of any kind is the hover thrusters, so it will struggle to do things like moving up a steep hill. To enhance the handling of the hovercraft, one could add some gyroscope torque and/or non-hover thrusters. However, a proper implementation of OMI_vehicle_hover_thruster should be able to handle a hovercraft with only hover thrusters, including stable hovering over flat ground, controlled angular movement such as pitch, yaw, and roll, preventing the hovercraft from flipping over, and controlled linear movement forward/back/left/right with a bit of up/down movement. +For this example, the only source of thrust of any kind is the hover thrusters, so it will struggle to do things like moving up a steep hill. To enhance the handling of the hovercraft, one could add some gyroscope torque and/or non-hover thrusters. However, a proper implementation of OMI_vehicle_hover_thruster should be able to handle a hovercraft with only hover thrusters, therefore this serves as a useful test model. A good implementation should include stable hovering over flat ground, controlled angular movement such as pitch, yaw, and roll, dampeners working to prevent the hovercraft from flipping over, and controlled linear forward/back/left/right movement with a bit of up/down movement. The hover thruster settings are defined at the document level: @@ -47,7 +47,7 @@ The hover thruster settings are defined at the document level: } ] } - }, + } } ``` @@ -55,7 +55,7 @@ This is then referenced by index on hover thruster glTF nodes. For example, the ```json { - "name": "HoverThrusterFR", + "name": "HoverThrusterFrontRight", "rotation": [-0.17670360207558, 0.81949108839035, 0.426600247621536, 0.33944433927536], "translation": [-1.2, -0.25, 2.1], "extensions": { @@ -76,24 +76,65 @@ The extension must also be added to the glTF's `extensionsUsed` array and becaus The rest of the document, including this summary, defines the properties for the main data structure. -| | Type | Description | Default value | -| ---------------------- | ----------- | --------------------------------------------------------------------------- | -------------------- | -| **currentHoverRatio** | `number` | The ratio of the maximum hover energy the thruster is using for propulsion. | 0.0 | -| **currentGimbalRatio** | `number[2]` | The ratios of the maximum gimbal angle the hover thruster is rotated to. | [0.0, 0.0] | -| **maxHoverEnergy** | `number` | The maximum hover energy in Newton-meters (kg⋅m²/s²) of this thruster. | Required, no default | -| **maxGimbal** | `number` | The maximum angle the hover thruster can rotate in radians. | 0.0 | +| | Type | Description | Default value | +| --------------------------- | ----------- | ----------------------------------------------------------------------------------- | -------------------- | +| **gimbalChangeRate** | `number` | The rate at which the hover thruster can change its gimbal angles. | 1.0 | +| **hoverEnergyChangeRate** | `number` | The rate at which the hover thruster can change its applied hover energy. | -1.0 | +| **linearGimbalAdjustRatio** | `number` | The ratio of how a vehicle's linear activation should adjust the thruster's gimbal. | 0.5 | +| **maxGimbal** | `number` | The maximum angle the hover thruster can rotate in radians. | 0.0 | +| **maxHoverEnergy** | `number` | The maximum hover energy in Newton-meters (kg⋅m²/s²) of this thruster. | Required, no default | +| **targetHoverRatio** | `number` | The ratio of the maximum hover energy the thruster is using for propulsion. | 0.0 | +| **targetGimbalRatio** | `number[2]` | The ratios of the maximum gimbal angle the hover thruster is rotated to. | [0.0, 0.0] | + +#### Gimbal Change Rate + +The `"gimbalChangeRate"` property is a number that defines the rate at which the hover thruster can change its gimbal angles, measured in radians per second (rad/s). If not specified, the default value is 1.0 rad/s. + +All finite numbers are valid values. A value of 0.0 means the hover thruster cannot change its gimbal angle at all after initialization, meaning that it would lock the gimbal in place. Any negative value means the hover thruster MUST instantly change to the desired gimbal angle. Positive values define the maximum rate of change of the gimbal angles in radians per second. With the default value of 1.0 rad/s, a hover thruster can rotate from 0.0 to 0.5 radians in 0.5 seconds. + +For more detail on applying gimbal as rotation, see the `"targetGimbalRatio"` property description below. + +#### Max Gimbal + +The `"maxGimbal"` property is a number that defines the maximum angle in radians that the hover thruster can rotate. If not specified, the default value is 0.0 radians in both axes, which means the hover thruster cannot rotate. + +Valid values are between 0.0 and τ/2 radians (π or 3.14159265... radians, or 180 degrees). Sane values are between 0.0 and 1.0 radians, while realistic values are between 0.0 and 0.2 radians. + +The active gimbal of the hover thruster is defined by a combination of this property and the `"targetGimbalRatio"` property. In general, hover thrusters should gimbal when there is angular input in the same direction the hover thruster gimbals in, such as a rear hover thruster gimballing its nozzle up when up input is given, causing the thrust direction to angle down, causing the vehicle to pitch up. + +For more detail on applying gimbal as rotation, see the `"targetGimbalRatio"` property description below. + +#### Max Hover Energy + +The `"maxHoverEnergy"` property is a number that defines the maximum hover energy that the hover thruster can provide, measured in Newton-meters (N⋅m), or kg⋅m²/s² in SI base units, or simply Joules. This property is required and has no default value. + +Valid values are positive numbers. Hover energy is a sci-fi concept, so there is no real-world equivalent. The desired hover energy of a thruster varies a lot depending on the mass of the vehicle, the desired distance above the ground to hover at, and the desired acceleration and handling characteristics. This wide variety means that there is no sane choice for a default value, so there is no default value. A hover thruster with zero hover energy is useless. -#### Current Hover Ratio +As an example, if a vehicle has a mass of 1000 kg, and is designed for hovering a maximum 4 meters between the thrusters and the ground in 10 m/s² gravity, then the minimum total hover energy required is 1000 kg × 4m × 10 m/s² = 40000 N⋅m = 40000 J. The [hovercraft.gltf](examples/hovercraft.gltf) example has a mass of 2000 kg and 4 hover thrusters with 30000 N⋅m of hover energy each, which can sustain a hover height of (4 × 30000 N⋅m) / (2000 kg × 10 m/s²) = 6 meters above the ground. Note that the hovercraft may hover lower than this if the hover thrusters are not firing at full power or if the thrusters are not pointing directly down, in practice the example hovercraft hovers at around 4 to 5 meters above the ground. -The `"currentHoverRatio"` property is a number that defines the ratio of `"maxHoverEnergy"` the hover thruster is using for propulsion. If not specified, the default value is 0.0, which means the hover thruster is not firing. +The target hover energy of the hover thruster is defined by a combination of this property, the distance between the hover thruster and the ground, and the `"targetHoverRatio"` property. The actual hover energy tends towards the target hover energy at a rate defined by the `"hoverEnergyChangeRate"` property. The actual force applied by the hover thruster is equal to the actual hover energy divided by the distance between the hover thruster and the ground, where closer distances result in more force. + +In general, hover thrusters should fire when there is input in the same direction the thruster exerts force in, such as rear hover thrusters providing forward hover energy when forward input is given. + +#### Linear Gimbal Adjust Ratio + +The `"linearGimbalAdjustRatio"` property is a number that defines the ratio of how a vehicle's linear activation should adjust the hover thruster's gimbal. If not specified, the default value is 0.5, which means the vehicle's linear activation and dampeners can contribute up to half as much influence on the hover thruster's gimbal as the vehicle's angular activation and dampeners. + +Setting this property to a positive value allows the vehicle's linear activation and dampeners to influence the hover thruster's gimbal to help achieve the desired linear movement of the vehicle. For example, if the vehicle wants to go forward, and the hover thruster points downward, and the hover thruster can gimbal, then it is possible for the hover thruster to gimbal to shoot its propellant slightly backward to provide some forward thrust to the vehicle. + +The default value of 0.5 means that the vehicle's linear activation and dampeners can contribute up to half as much influence on the hover thruster's gimbal as the vehicle's angular activation and dampeners. Hover thrusters have a different default value than non-hover thrusters because hovercraft critically need gimbal to control their linear movement, while spacecraft using non-hover thrusters usually expect linear inputs to not affect gimbal rotation. A value of 0.0 would mean that the vehicle's linear activation and dampeners do not affect the hover thruster's gimbal at all, and the hover thruster only gimballs based on the vehicle's angular activation and dampeners. + +#### Target Hover Ratio + +The `"targetHoverRatio"` property is a number that defines the ratio of `"maxHoverEnergy"` the hover thruster is targeting for propulsion. If not specified, the default value is 0.0, which means the hover thruster is not firing. This value is expected to be between 0.0 and 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of turbo boost or overdrive. Negative values should not be used since the hover thrust strength may be different in the opposite direction, so for reverse thrust, a separate hover thruster should be used facing the opposite direction. For example, a hovercraft may have some hover thrusters on the front and some on the rear, with the rear hover thrusters propelling the vehicle forward, and the front hover thrusters propelling the vehicle backward. This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The thruster may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the hover thruster's force ratio is calculated using the vehicle's `"angularActivation"` and `"linearActivation"` properties, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. -#### Current Gimbal Ratio +#### Target Gimbal Ratio -The `"currentGimbalRatio"` property is an array of two numbers that defines the ratio of `"maxGimbal"` the thruster is rotated to. If not specified, the default value is [0.0, 0.0], which means the thruster is not gimballing. +The `"targetGimbalRatio"` property is an array of two numbers that defines the ratio of `"maxGimbal"` the thruster is targeting to be rotated to. If not specified, the default value is [0.0, 0.0], which means the thruster is not gimballing. Each number is expected to be between -1.0 and 1.0, and both numbers together should form a vector of length no longer than 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of over-rotation or over-gimbal. @@ -101,40 +142,47 @@ For a hover thruster in the default orientation (rear hover thruster providing f This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The gimbal may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the thruster's gimbal ratio is calculating using the vehicle's `"angularActivation"` and `"linearActivation"` properties, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. -The thruster's gimbal values can be converted into a rotation Quaternion using the following formula: +The hover thruster's gimbal values can be converted into a rotation Quaternion using the following pseudocode formula: ```javascript function getGimbalRotationQuaternion() { - if (isZeroApprox(currentGimbalRatio) || isZeroApprox(maxGimbal)) { - return Quaternion.IDENTITY; - } - let rotAngles = limitLength(currentGimbalRatio).multiply(maxGimbal); - let angleMag = rotAngles.length(); - let sinNormAngle = Math.sin(angleMag / 2.0) / angleMag; - let cosHalfAngle = Math.cos(angleMag / 2.0); - return new Quaternion(rotAngles.x * sinNormAngle, rotAngles.y * sinNormAngle, 0.0, cosHalfAngle); + if (isZeroApprox(_currentGimbalRadians)) { + return Quaternion.IDENTITY; + } + let angleMag = _currentGimbalRadians.length(); + let sinNormAngle = Math.sin(angleMag / 2.0) / angleMag; + let cosHalfAngle = Math.cos(angleMag / 2.0); + return new Quaternion( + _currentGimbalRadians.x * sinNormAngle, + _currentGimbalRadians.y * sinNormAngle, + 0.0, + cosHalfAngle + ); } ``` -Where `isZeroApprox` returns true if the vector or number is approximately zero, and `limitLength` returns the same vector when its length is less than or equal to 1.0, or a normalized version of the vector when its length is greater than 1.0. The variable `rotAngles` is the rotation in radians, and `angleMag` is the total angle in radians. - -#### Max Hover Energy +Where `isZeroApprox` returns true if the vector or number is approximately zero, and `limitLength` returns the same vector when its length is less than or equal to 1.0, or a normalized version of the vector when its length is greater than 1.0. The variable `rotAngles` is the rotation in radians, and `angleMag` is the total angle in radians. The `_currentGimbalRadians` is a private variable that tends towards `targetGimbalRatio` over time by a rate of `"gimbalChangeRate"` if positive, or instantly if negative. -The `"maxHoverEnergy"` property is a number that defines the maximum hover energy that the hover thruster can provide, measured in Newton-meters (N⋅m), or kg⋅m²/s² in SI base units, or simply Joules. This property is required and has no default value. +### glTF Object Model -Valid values are positive numbers. Hover energy is a sci-fi concept, so there is no real-world equivalent. The desired hover energy of a thruster varies a lot depending on the mass of the vehicle, the desired distance above the ground to hover at, and the desired acceleration and handling characteristics. This wide variety means that there is no sane choice for a default value, so there is no default value. A hover thruster with zero hover energy is useless. +The following JSON pointers are defined representing mutable properties defined by this extension, for use with the glTF Object Model including extensions such as `KHR_animation_pointer` and `KHR_interactivity`: -As an example, if a vehicle has a mass of 1000 kg, and is designed for hovering a maximum 4 meters between the thrusters and the ground in 10 m/s² gravity, then the minimum total hover energy required is 1000 kg * 4 m * 10 m/s² = 40000 N⋅m = 40000 J. The [hovercraft.gltf](examples/hovercraft.gltf) example has a mass of 2000 kg and 4 hover thrusters with 30000 N⋅m of hover energy each, which can sustain a hover height of (4 * 30000 N⋅m) / (2000 kg * 10 m/s²) = 6 meters above the ground. Note that the hovercraft may hover lower than this if the hover thrusters are not firing at full power or if the thrusters are not pointing directly down, in practice the example hovercraft hovers at around 4 to 5 meters above the ground. +| JSON Pointer | Object Model Type | +| ---------------------------------------------------------------------------------- | ----------------- | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters/{}/gimbalChangeRate` | `float` | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters/{}/hoverEnergyChangeRate` | `float` | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters/{}/linearGimbalAdjustRatio` | `float` | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters/{}/maxGimbal` | `float` | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters/{}/maxHoverEnergy` | `float` | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters/{}/targetHoverRatio` | `float` | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters/{}/targetGimbalRatio` | `float2` | -The active force of the hover thruster is defined by a combination of this property, the distance between the hover thruster and the ground, and the `"currentForceRatio"` property. In general, hover thrusters should fire when there is input in the same direction the thruster exerts force in, such as rear hover thrusters providing forward hover energy when forward input is given. - -#### Max Gimbal - -The `"maxGimbal"` property is a number that defines the maximum angle in radians that the hover thruster can rotate. If not specified, the default value is 0.0 radians in both axes, which means the hover thruster cannot rotate. - -Valid values are between 0.0 and τ/2 radians (π or 3.14159265... radians, or 180 degrees). Sane values are between 0.0 and 1.0 radians, while realistic values are between 0.0 and 0.2 radians. +Additionally, the following JSON pointers are defined for read-only properties: -The active gimbal of the hover thruster is defined by a combination of this property and the `"currentGimbalRatio"` property. In general, hover thrusters should gimbal when there is angular input in the same direction the hover thruster gimbals in, such as a rear hover thruster gimballing its nozzle up when up input is given, causing the thrust direction to angle down, causing the vehicle to pitch up. +| JSON Pointer | Object Model Type | +| --------------------------------------------------------------- | ----------------- | +| `/extensions/OMI_vehicle_hover_thruster/hoverThrusters.length` | `int` | +| `/nodes/{}/extensions/OMI_vehicle_hover_thruster/hoverThruster` | `int` | ### JSON Schema @@ -142,8 +190,9 @@ See [glTF.OMI_vehicle_hover_thruster.thruster.schema.json](schema/glTF.OMI_vehic ## Known Implementations -- Godot Engine +- Basis VR: https://github.com/BasisVR/Basis/pull/442 +- Godot Engine: https://github.com/omigroup/omi-godot/tree/main/addons/omi_extensions/vehicle -## Resources: +## Resources - None diff --git a/extensions/2.0/OMI_vehicle_hover_thruster/examples/hovercraft.gltf b/extensions/2.0/OMI_vehicle_hover_thruster/examples/hovercraft.gltf index 0960a31..9c9ad2d 100644 --- a/extensions/2.0/OMI_vehicle_hover_thruster/examples/hovercraft.gltf +++ b/extensions/2.0/OMI_vehicle_hover_thruster/examples/hovercraft.gltf @@ -128,7 +128,6 @@ ], "materials": [ { - "extensions": {}, "name": "HovercraftMaterial", "pbrMetallicRoughness": { "baseColorFactor": [1, 1, 1, 1], @@ -177,7 +176,7 @@ "extensions": { "OMI_vehicle_hover_thruster": { "hoverThruster": 0 } }, - "name": "HoverThrusterBL", + "name": "HoverThrusterRearLeft", "rotation": [-0.42660024762154, -0.33944433927536, -0.17670360207558, 0.81949108839035], "translation": [1.2, -0.25, -2.1] }, @@ -185,7 +184,7 @@ "extensions": { "OMI_vehicle_hover_thruster": { "hoverThruster": 0 } }, - "name": "HoverThrusterBR", + "name": "HoverThrusterRearRight", "rotation": [-0.42660024762154, 0.33944433927536, 0.176703602075577, 0.81949108839035], "translation": [-1.2, -0.25, -2.1] }, @@ -193,7 +192,7 @@ "extensions": { "OMI_vehicle_hover_thruster": { "hoverThruster": 0 } }, - "name": "HoverThrusterFL", + "name": "HoverThrusterFrontLeft", "rotation": [0.176703602075577, 0.81949108839035, 0.426600247621536, -0.33944433927536], "translation": [1.2, -0.25, 2.1] }, @@ -201,7 +200,7 @@ "extensions": { "OMI_vehicle_hover_thruster": { "hoverThruster": 0 } }, - "name": "HoverThrusterFR", + "name": "HoverThrusterFrontRight", "rotation": [-0.17670360207558, 0.81949108839035, 0.426600247621536, 0.33944433927536], "translation": [-1.2, -0.25, 2.1] }, diff --git a/extensions/2.0/OMI_vehicle_hover_thruster/schema/glTF.OMI_vehicle_hover_thruster.thruster.schema.json b/extensions/2.0/OMI_vehicle_hover_thruster/schema/glTF.OMI_vehicle_hover_thruster.thruster.schema.json index f99ffca..cf22583 100644 --- a/extensions/2.0/OMI_vehicle_hover_thruster/schema/glTF.OMI_vehicle_hover_thruster.thruster.schema.json +++ b/extensions/2.0/OMI_vehicle_hover_thruster/schema/glTF.OMI_vehicle_hover_thruster.thruster.schema.json @@ -4,30 +4,53 @@ "title": "OMI_vehicle_hover_thruster Hover Thruster Parameters", "type": "object", "properties": { - "currentForceRatio": { + "gimbalChangeRate": { "type": "number", - "description": "The ratio of the maximum hover energy the hover thruster is using for propulsion.", - "default": 0.0 + "description": "The rate at which the hover thruster can change its gimbal angles, measured in radians per second (rad/s). If negative, the hover thruster instantly changes to the desired gimbal.", + "default": 1.0 }, - "currentGimbalRatio": { + "hoverEnergyChangeRate": { + "type": "number", + "description": "The rate at which the hover thruster can change its applied hover energy, measured in Newton-meters per second (N⋅m/s) or kilograms meters squared per second cubed (kg⋅m²/s³) in SI base units. If negative or not specified, the hover thruster instantly changes to the desired hover energy.", + "default": -1.0 + }, + "linearGimbalAdjustRatio": { + "type": "number", + "description": "The maximum ratio of how a vehicle's linear activation should adjust the hover thruster's gimbal.", + "default": 0.5, + "maximum": 1.0, + "minimum": 0.0 + }, + "maxGimbal": { + "type": "number", + "description": "The maximum angle the hover thruster can gimbal or rotate in radians.", + "default": 0.0, + "maximum": 3.1415925, + "minimum": 0.0 + }, + "maxHoverEnergy": { + "type": "number", + "description": "The maximum hover energy force in Newton-meters (N⋅m or kg⋅m²/s²) that the hover thruster can provide. Required.", + "minimum": 0.0 + }, + "targetHoverRatio": { + "type": "number", + "description": "The ratio of the maximum hover energy the hover thruster is targeting for propulsion. MUST NOT be negative. Values greater than 1.0 have implementation-defined behavior.", + "default": 0.0, + "minimum": 0.0 + }, + "targetGimbalRatio": { "type": "array", - "description": "The ratio of the maximum gimbal angles the hover thruster is rotated to.", + "description": "The ratio of the maximum gimbal angles the hover thruster is targeting to be rotated to. MUST NOT have a length greater than 1.0.", "items": { - "type": "number" + "type": "number", + "maximum": 1.0, + "minimum": -1.0 }, "minItems": 2, "maxItems": 2, "default": [0.0, 0.0] }, - "maxHoverEnergy": { - "type": "number", - "description": "The maximum hover energy force in Newton-meters (N⋅m or kg⋅m²/s²) that the hover thruster can provide. Required." - }, - "maxGimbal": { - "type": "array", - "description": "The maximum angle the hover thruster can gimbal or rotate in radians.", - "default": 0.0 - }, "extensions": { }, "extras": { } }, diff --git a/extensions/2.0/OMI_vehicle_thruster/README.md b/extensions/2.0/OMI_vehicle_thruster/README.md index 3f898cb..1c8ee94 100644 --- a/extensions/2.0/OMI_vehicle_thruster/README.md +++ b/extensions/2.0/OMI_vehicle_thruster/README.md @@ -83,24 +83,61 @@ The extension must also be added to the glTF's `extensionsUsed` array and becaus The rest of the document, including this summary, defines the properties for the main data structure. -| | Type | Description | Default value | -| ---------------------- | ----------- | -------------------------------------------------------------------- | -------------------- | -| **currentForceRatio** | `number` | The ratio of the maximum force the thruster is using for propulsion. | 0.0 | -| **currentGimbalRatio** | `number[2]` | The ratios of the maximum gimbal angle the thruster is rotated to. | [0.0, 0.0] | -| **maxForce** | `number` | The maximum thrust force in Newtons (kg⋅m/s²) of this thruster. | Required, no default | -| **maxGimbal** | `number` | The maximum angle the thruster can rotate in radians. | 0.0 | +| | Type | Description | Default value | +| --------------------------- | ----------- | ----------------------------------------------------------------------------------- | -------------------- | +| **gimbalChangeRate** | `number` | The rate at which the thruster can change its gimbal angles. | 1.0 | +| **forceChangeRate** | `number` | The rate at which the thruster can change its applied force. | -1.0 | +| **linearGimbalAdjustRatio** | `number` | The ratio of how a vehicle's linear activation should adjust the thruster's gimbal. | 0.0 | +| **maxForce** | `number` | The maximum thrust force in Newtons (kg⋅m/s²) of this thruster. | Required, no default | +| **maxGimbal** | `number` | The maximum angle the thruster can rotate in radians. | 0.0 | +| **targetForceRatio** | `number` | The ratio of the maximum force the thruster is using for propulsion. | 0.0 | +| **targetGimbalRatio** | `number[2]` | The ratios of the maximum gimbal angle the thruster is rotated to. | [0.0, 0.0] | -#### Current Force Ratio +#### Gimbal Change Rate -The `"currentForceRatio"` property is a number that defines the ratio of `"maxForce"` the thruster is using for propulsion. If not specified, the default value is 0.0, which means the thruster is not firing. +The `"gimbalChangeRate"` property is a number that defines the rate at which the thruster can change its gimbal angles, measured in radians per second (rad/s). If not specified, the default value is 1.0 rad/s. + +All finite numbers are valid values. A value of 0.0 means the thruster cannot change its gimbal angle at all after initialization, meaning that it would lock the gimbal in place. Any negative value means the thruster MUST instantly change to the desired gimbal angle. Positive values define the maximum rate of change of the gimbal angles in radians per second. With the default value of 1.0 rad/s, a thruster can rotate from 0.0 to 0.5 radians in 0.5 seconds. + +For more detail on applying gimbal as rotation, see the `"targetGimbalRatio"` property description below. + +#### Max Force + +The `"maxForce"` property is a number that defines the maximum thrust force in Newtons (kg⋅m/s²) that the thruster can provide. This property is required and has no default value. + +Valid values are positive numbers. Rocket engines in real life have a wide variety of force amounts. Typical rocket engines are usually between one thousand and one million Newtons, but may be much less in the case of small control thrusters, or much more in the case of large main engines. A thruster with zero force is useless. The desired force of a thruster also varies a lot depending on the mass of the vehicle and the desired acceleration and control over it. This wide variety means that there is no sane choice for a default value, so there is no default value. + +The target force of the thruster is defined by a combination of this property and the `"targetForceRatio"` property. The actual force applied by the thruster tends towards this target force over time by a rate of `"forceChangeRate"` if specified, or instantly if not specified. In general, thrusters should fire when there is linear input in the same direction the thruster exerts force in, such as rear thrusters providing a forward force when forward input is given. + +#### Max Gimbal + +The `"maxGimbal"` property is a number that defines the maximum angle in radians that the thruster can rotate. If not specified, the default value is 0.0 radians in both axes, which means the thruster cannot rotate. + +Valid values are between 0.0 and τ/2 radians (π or 3.14159265... radians, or 180 degrees). Sane values are between 0.0 and 1.0 radians, while realistic values are between 0.0 and 0.2 radians. + +The active gimbal of the thruster is defined by a combination of this property and the `"targetGimbalRatio"` property. In general, thrusters should gimbal when there is angular input in the same direction the thruster gimbals in, such as a rear thruster gimballing its nozzle up when up input is given, causing the thrust direction to angle down, causing the vehicle to pitch up. + +For more detail on applying gimbal as rotation, see the `"targetGimbalRatio"` property description below. + +#### Linear Gimbal Adjust Ratio + +The `"linearGimbalAdjustRatio"` property is a number that defines the maximum ratio of how a vehicle's linear activation should adjust the thruster's gimbal. If not specified, the default value is 0.0, which means the vehicle's linear activation does not affect the thruster's gimbal. + +Setting this property to a positive value allows the vehicle's linear activation and dampeners to influence the thruster's gimbal to help achieve the desired linear movement of the vehicle. For example, if the vehicle wants to go forward, and the thruster points downward, and the thruster can gimbal, then it is possible for the thruster to gimbal to shoot its propellant slightly backward to provide some forward thrust to the vehicle. + +The default value of 0.0 means that the vehicle's linear activation and dampeners do not affect the thruster's gimbal at all, and the thruster only gimballs based on the vehicle's angular activation and dampeners. Non-hover thrusters have a different default value than hover thrusters because spacecraft using non-hover thrusters usually expect linear inputs to not affect gimbal rotation, while hovercraft critically need gimbal to control their linear movement. + +#### Target Force Ratio + +The `"targetForceRatio"` property is a number that defines the ratio of `"maxForce"` the thruster is targeting for propulsion. If not specified, the default value is 0.0, which means the thruster is not firing. This value is expected to be between 0.0 and 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of turbo boost or overdrive. Negative values should not be used since the thrust strength may be different in the opposite direction, so for reverse thrust, a separate thruster should be used facing the opposite direction. For example, a rocket engine will only use one thruster, while a jet engine may have two thrusters, one for forward thrust and one for reverse thrust, with the reverse thruster having lower thrust. This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The thruster may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the thruster's force ratio is set by the vehicle's `"linearActivation"` property in the thrust direction, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. -#### Current Gimbal Ratio +#### Target Gimbal Ratio -The `"currentGimbalRatio"` property is an array of two numbers that defines the ratio of `"maxGimbal"` the thruster is rotated to. If not specified, the default value is [0.0, 0.0], which means the thruster is not gimballing. +The `"targetGimbalRatio"` property is an array of two numbers that defines the ratio of `"maxGimbal"` the thruster is targeting to be rotated to. If not specified, the default value is [0.0, 0.0], which means the thruster is not gimballing. Each number is expected to be between -1.0 and 1.0, and both numbers together should form a vector of length no longer than 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of over-rotation or over-gimbal. @@ -108,38 +145,47 @@ For a thruster in the default orientation (rear thruster providing forward +Z th This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The gimbal may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the thruster's gimbal ratio is set by the vehicle's `"angularActivation"` property in the gimbal direction, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. -The thruster's gimbal values can be converted into a rotation Quaternion using the following formula: +The thruster's gimbal values can be converted into a rotation Quaternion using the following pseudocode formula: ```javascript function getGimbalRotationQuaternion() { - if (isZeroApprox(currentGimbalRatio) || isZeroApprox(maxGimbal)) { + if (isZeroApprox(_currentGimbalRadians)) { return Quaternion.IDENTITY; } - let rotAngles = limitLength(currentGimbalRatio).multiply(maxGimbal); - let angleMag = rotAngles.length(); + let angleMag = _currentGimbalRadians.length(); let sinNormAngle = Math.sin(angleMag / 2.0) / angleMag; let cosHalfAngle = Math.cos(angleMag / 2.0); - return new Quaternion(rotAngles.x * sinNormAngle, rotAngles.y * sinNormAngle, 0.0, cosHalfAngle); + return new Quaternion( + _currentGimbalRadians.x * sinNormAngle, + _currentGimbalRadians.y * sinNormAngle, + 0.0, + cosHalfAngle + ); } ``` -Where `isZeroApprox` returns true if the vector or number is approximately zero, and `limitLength` returns the same vector when its length is less than or equal to 1.0, or a normalized version of the vector when its length is greater than 1.0. The variable `rotAngles` is the rotation in radians, and `angleMag` is the total angle in radians. - -#### Max Force - -The `"maxForce"` property is a number that defines the maximum thrust force in Newtons (kg⋅m/s²) that the thruster can provide. This property is required and has no default value. - -Valid values are positive numbers. Rocket engines in real life have a wide variety of force amounts. Typical rocket engines are usually between one thousand and one million Newtons, but may be much less in the case of small control thrusters, or much more in the case of large main engines. A thruster with zero force is useless. The desired force of a thruster also varies a lot depending on the mass of the vehicle and the desired acceleration and control over it. This wide variety means that there is no sane choice for a default value, so there is no default value. +Where `isZeroApprox` returns true if the vector or number is approximately zero, and `limitLength` returns the same vector when its length is less than or equal to 1.0, or a normalized version of the vector when its length is greater than 1.0. The variable `rotAngles` is the rotation in radians, and `angleMag` is the total angle in radians. The `_currentGimbalRadians` is a private variable that tends towards `targetGimbalRatio` over time by a rate of `"gimbalChangeRate"` if positive, or instantly if negative. -The active force of the thruster is defined by a combination of this property and the `"currentForceRatio"` property. In general, thrusters should fire when there is linear input in the same direction the thruster exerts force in, such as rear thrusters providing a forward force when forward input is given. +### glTF Object Model -#### Max Gimbal +The following JSON pointers are defined representing mutable properties defined by this extension, for use with the glTF Object Model including extensions such as `KHR_animation_pointer` and `KHR_interactivity`: -The `"maxGimbal"` property is a number that defines the maximum angle in radians that the thruster can rotate. If not specified, the default value is 0.0 radians in both axes, which means the thruster cannot rotate. +| JSON Pointer | Object Model Type | +| ----------------------------------------------------------------------- | ----------------- | +| `/extensions/OMI_vehicle_thruster/thrusters/{}/gimbalChangeRate` | `float` | +| `/extensions/OMI_vehicle_thruster/thrusters/{}/forceChangeRate` | `float` | +| `/extensions/OMI_vehicle_thruster/thrusters/{}/linearGimbalAdjustRatio` | `float` | +| `/extensions/OMI_vehicle_thruster/thrusters/{}/maxForce` | `float` | +| `/extensions/OMI_vehicle_thruster/thrusters/{}/maxGimbal` | `float` | +| `/extensions/OMI_vehicle_thruster/thrusters/{}/targetForceRatio` | `float` | +| `/extensions/OMI_vehicle_thruster/thrusters/{}/targetGimbalRatio` | `float2` | -Valid values are between 0.0 and τ/2 radians (π or 3.14159265... radians, or 180 degrees). Sane values are between 0.0 and 1.0 radians, while realistic values are between 0.0 and 0.2 radians. +Additionally, the following JSON pointers are defined for read-only properties: -The active gimbal of the thruster is defined by a combination of this property and the `"currentGimbalRatio"` property. In general, thrusters should gimbal when there is angular input in the same direction the thruster gimbals in, such as a rear thruster gimballing its nozzle up when up input is given, causing the thrust direction to angle down, causing the vehicle to pitch up. +| JSON Pointer | Object Model Type | +| ---------------------------------------------------- | ----------------- | +| `/extensions/OMI_vehicle_thruster/thrusters.length` | `int` | +| `/nodes/{}/extensions/OMI_vehicle_thruster/thruster` | `int` | ### JSON Schema @@ -147,9 +193,10 @@ See [glTF.OMI_vehicle_thruster.thruster.schema.json](schema/glTF.OMI_vehicle_thr ## Known Implementations -- +- Basis VR: https://github.com/BasisVR/Basis/pull/442 +- Godot Engine: https://github.com/omigroup/omi-godot/tree/main/addons/omi_extensions/vehicle -## Resources: +## Resources - Kerbal Space Program wiki on engines: https://wiki.kerbalspaceprogram.com/wiki/Parts#Engines - Space Engineers wiki on thrusters: https://spaceengineers.fandom.com/wiki/Thruster diff --git a/extensions/2.0/OMI_vehicle_thruster/schema/glTF.OMI_vehicle_thruster.thruster.schema.json b/extensions/2.0/OMI_vehicle_thruster/schema/glTF.OMI_vehicle_thruster.thruster.schema.json index f00ca5d..bd9965f 100644 --- a/extensions/2.0/OMI_vehicle_thruster/schema/glTF.OMI_vehicle_thruster.thruster.schema.json +++ b/extensions/2.0/OMI_vehicle_thruster/schema/glTF.OMI_vehicle_thruster.thruster.schema.json @@ -4,29 +4,52 @@ "title": "OMI_vehicle_thruster Thruster Parameters", "type": "object", "properties": { - "currentForceRatio": { + "forceChangeRate": { "type": "number", - "description": "The ratio of the maximum force the thruster is using for propulsion.", - "default": 0.0 + "description": "The rate at which the thruster can change its applied force, measured in Newtons per second (N/s) or kilograms meters per second cubed (kg⋅m/s³) in SI base units. If negative or not specified, the thruster instantly changes to the desired force.", + "default": -1.0 }, - "currentGimbalRatio": { - "type": "array", - "description": "The ratio of the maximum gimbal angles the thruster is rotated to.", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 2, - "default": [0.0, 0.0] + "gimbalChangeRate": { + "type": "number", + "description": "The rate at which the thruster can change its gimbal angles, measured in radians per second (rad/s). If negative, the thruster instantly changes to the desired gimbal.", + "default": 1.0 + }, + "linearGimbalAdjustRatio": { + "type": "number", + "description": "The ratio of how a vehicle's linear activation should adjust the thruster's gimbal.", + "default": 0.0, + "maximum": 1.0, + "minimum": 0.0 }, "maxForce": { "type": "number", - "description": "The maximum thrust force in Newtons (kg⋅m/s²) that the thruster can provide. Required." + "description": "The maximum thrust force in Newtons (kg⋅m/s²) that the thruster can provide. Required.", + "minimum": 0.0 }, "maxGimbal": { - "type": "array", + "type": "number", "description": "The maximum angle the thruster can gimbal or rotate in radians.", - "default": 0.0 + "default": 0.0, + "maximum": 3.1415925, + "minimum": 0.0 + }, + "targetForceRatio": { + "type": "number", + "description": "The ratio of the maximum force the thruster is targeting for propulsion. MUST NOT be negative. Values greater than 1.0 have implementation-defined behavior.", + "default": 0.0, + "minimum": 0.0 + }, + "targetGimbalRatio": { + "type": "array", + "description": "The ratio of the maximum gimbal angles the thruster is targeting to be rotated to. MUST NOT have a length greater than 1.0.", + "items": { + "type": "number", + "maximum": 1.0, + "minimum": -1.0 + }, + "minItems": 2, + "maxItems": 2, + "default": [0.0, 0.0] }, "extensions": { }, "extras": { } diff --git a/extensions/2.0/OMI_vehicle_wheel/README.md b/extensions/2.0/OMI_vehicle_wheel/README.md index 8462617..927ce9d 100644 --- a/extensions/2.0/OMI_vehicle_wheel/README.md +++ b/extensions/2.0/OMI_vehicle_wheel/README.md @@ -39,7 +39,7 @@ The wheel settings are defined at the document level: }, { "radius": 0.35, - "maxForce": 2000 + "maxPropulsionForce": 2000 } ] } @@ -76,37 +76,30 @@ The rest of the document, including this summary, defines the properties for the | | Type | Description | Default value | | -------------------------------- | -------- | -------------------------------------------------------------------------- | ---------------- | -| **currentForceRatio** | `number` | The ratio of the maximum force the wheel is using for propulsion. | 0.0 | -| **currentSteeringRatio** | `number` | The ratio of the maximum steering angle the wheel is rotated to. | 0.0 | -| **maxForce** | `number` | The maximum thrust force in Newtons (kg⋅m/s²) of this wheel. | 0.0 | +| **brakingForce** | `number` | The braking force in Newtons (kg⋅m/s²) that the wheel can provide. | -1.0 | +| **maxPropulsionForce** | `number` | The maximum thrust force in Newtons (kg⋅m/s²) of this wheel. | 0.0 | | **maxSteeringAngle** | `number` | The maximum angle in radians that the wheel can steer or rotate. | 0.0 | | **physicsMaterial** | `number` | The index of the physics material in the top level physicsMaterials array. | -1 (no material) | +| **propulsionForceChangeRate** | `number` | The rate at which the wheel can change its propulsion force in N/s. | -1.0 (instant) | | **radius** | `number` | The radius of the wheel in meters. | 0.25 | +| **steeringChangeRate** | `number` | The rate at which the wheel can change its steering angle in rad/s. | 1.0 | | **suspensionDampingCompression** | `number` | The resistance to the velocity of the suspension in kg/s when compressing. | 2000.0 | | **suspensionDampingRebound** | `number` | The resistance to the velocity of the suspension in kg/s when extending. | 2000.0 | | **suspensionStiffness** | `number` | The resistance to traveling away from the start point in kg/s². | 20000.0 | | **suspensionTravel** | `number` | The distance the suspension can move up or down in meters. | 0.25 | +| **targetPropulsionForceRatio** | `number` | The ratio of the maximum force the wheel is using for propulsion. | 0.0 | +| **targetSteeringRatio** | `number` | The ratio of the maximum steering angle the wheel is rotated to. | 0.0 | | **width** | `number` | The width of the wheel in meters. | 0.125 | -#### Current Force Ratio +#### Braking Force -The `"currentForceRatio"` property is a number that defines the ratio of the `"maximumForce"` the wheel is using for propulsion. If not specified, the default value is 0.0, which means the wheel is not providing any force. +The `"brakingForce"` property is a number that defines the braking force in Newtons (kg⋅m/s²) that the wheel can provide. If not specified, the default value is -1.0, which means the wheel uses its propulsion force for braking. -This value is expected to be between -1.0 and 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of turbo boost or overdrive. - -This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The wheel may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the wheel's force ratio is set by the vehicle's `"linearActivation"` property in the direction the wheel is facing, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. - -#### Current Steering Ratio +If the value is negative, then the wheel uses its propulsion force for braking. If the value is non-negative, then this value is used for braking instead of propulsion force. When specified, this is usually larger than `"maxPropulsionForce"`, but may be any value. If the value is zero, then the wheel cannot provide any braking force. What causes the wheel to brake is suggested to be based on the vehicle's dampeners being enabled and the activation not being the same as the direction of travel, but the specific details, such as how to combine these, and the ability to brake an individual wheel or not, are implementation-defined. -The `"currentSteeringRatio"` property is a number that defines the ratio of the `"maximumSteeringAngle"` the wheel is rotated to. If not specified, the default value is 0.0, which means the wheel is not rotated. - -This value is expected to be between -1.0 and 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of oversteer. Due to glTF's right-handed coordinate system, positive values should rotate the wheel to the left, and negative values should rotate the wheel to the right. - -This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The wheel may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the wheel's steering ratio is set by the vehicle's `"angularActivation"` property in the direction the wheel is facing, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. The exact calculation is determined by the implementation, it may be as simple as using the distance on the vehicle's local Z axis, or as complex as Ackermann steering geometry. For example, a car with all-wheel steering would have the rear wheels rotate in the opposite direction of the front wheels. +#### Max Propulsion Force -#### Max Force - -The `"maxForce"` property is a number that defines the maximum thrust force in Newtons (kg⋅m/s²) that the wheel can transfer to the ground. If not specified, the default value is 0.0 Newtons, which means the wheel cannot provide any force. +The `"maxPropulsionForce"` property is a number that defines the maximum propulsion or thrust force in Newtons (kg⋅m/s²) that the wheel can transfer to the ground. If not specified, the default value is 0.0 Newtons, which means the wheel cannot provide any force. For a realistic normal car with rear-wheel drive and two rear wheels, the rear wheels should be given a maximum force value that is half the maximum force of the car's engine. The ideal value varies depending on the mass of the vehicle, the desired acceleration, and the amount of wheels. @@ -140,15 +133,66 @@ The `"suspensionStiffness"` property is a number that defines the stiffness of t The `"suspensionTravel"` property is a number that defines the distance in meters the wheel can move up or down. If not specified, the default travel is 0.25 meters, which is a sane default that reflects real-world cars. +#### Target Propulsion Force Ratio + +The `"targetPropulsionForceRatio"` property is a number that defines the ratio of the `"maxPropulsionForce"` the wheel is targeting for propulsion. If not specified, the default value is 0.0, which means the wheel is not providing any force. + +This value is expected to be between -1.0 and 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of turbo boost or overdrive. + +This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The wheel may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the wheel's force ratio is set by the vehicle's `"linearActivation"` property in the direction the wheel is facing, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. + +#### Target Steering Ratio + +The `"targetSteeringRatio"` property is a number that defines the ratio of the `"maxSteeringAngle"` the wheel is targeting to be rotated to. If not specified, the default value is 0.0, which means the wheel is not rotated. + +This value is expected to be between -1.0 and 1.0. The behavior of values outside of this range is implementation-defined, it may be clamped to this range, or be allowed to go beyond this range for some kind of oversteer. Due to glTF's right-handed coordinate system, positive values should rotate the wheel to the left, and negative values should rotate the wheel to the right. + +This value is expected to dynamically change at runtime. This is not usually saved in the glTF file, but it is allowed to be. The input used to set this value is implementation-defined. The wheel may be used on its own without a vehicle, but usually it is used for child nodes of a vehicle. In the common case of a vehicle, the wheel's steering ratio is set by the vehicle's `"angularActivation"` property in the direction the wheel is facing, as defined by `OMI_vehicle_body` extension. However, this value may be set by other means, such as a `KHR_interactivity` script. The exact calculation is determined by the implementation, it may be as simple as using the distance on the vehicle's local Z axis, or as complex as Ackermann steering geometry. For example, a car with all-wheel steering would have the rear wheels rotate in the opposite direction of the front wheels. + +#### Width + +The `"width"` property is a number that defines the width of the wheel in meters. If not specified, the default width is 0.125 meters. + +A wheel is treated as a cylinder with this width, centered on the glTF node's origin, aligned on the local YZ plane. Implementations which do not support wheel width may ignore this property and treat the wheel as an infinitely thin disc or circle. + +### glTF Object Model + +The following JSON pointers are defined representing mutable properties defined by this extension, for use with the glTF Object Model including extensions such as `KHR_animation_pointer` and `KHR_interactivity`: + +| JSON Pointer | Object Model Type | +| ---------------------------------------------------------------------- | ----------------- | +| `/extensions/OMI_vehicle_wheel/wheels/{}/brakingForce` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/maxPropulsionForce` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/maxSteeringAngle` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/physicsMaterial` | `int` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/propulsionForceChangeRate` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/radius` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/steeringChangeRate` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/suspensionDampingCompression` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/suspensionDampingRebound` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/suspensionStiffness` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/suspensionTravel` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/width` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/targetPropulsionForceRatio` | `float` | +| `/extensions/OMI_vehicle_wheel/wheels/{}/targetSteeringRatio` | `float` | + +Additionally, the following JSON pointers are defined for read-only properties: + +| JSON Pointer | Object Model Type | +| ---------------------------------------------- | ----------------- | +| `/extensions/OMI_vehicle_wheel/wheels.length` | `int` | +| `/nodes/{}/extensions/OMI_vehicle_wheel/wheel` | `int` | + ### JSON Schema See [glTF.OMI_vehicle_wheel.wheel.schema.json](schema/glTF.OMI_vehicle_wheel.wheel.schema.json) for the main wheel parameter schema, [glTF.OMI_vehicle_wheel.schema.json](schema/glTF.OMI_vehicle_wheel.schema.json) for the document-level list of wheel parameters, and [node.OMI_vehicle_wheel.schema.json](schema/node.OMI_vehicle_wheel.schema.json) for the node-level collider selection. ## Known Implementations -- Godot Engine: +- Basis VR: https://github.com/BasisVR/Basis/pull/442 +- Godot Engine: https://github.com/omigroup/omi-godot/tree/main/addons/omi_extensions/vehicle -## Resources: +## Resources - Godot VehicleWheel3D: https://docs.godotengine.org/en/latest/classes/class_vehiclewheel3d.html - Hyperfy Car: https://madjin.github.io/hyperfy-docs/docs/worlds/apps/objects/#car diff --git a/extensions/2.0/OMI_vehicle_wheel/examples/simple_car.gltf b/extensions/2.0/OMI_vehicle_wheel/examples/simple_car.gltf index 19696c8..515c66f 100644 --- a/extensions/2.0/OMI_vehicle_wheel/examples/simple_car.gltf +++ b/extensions/2.0/OMI_vehicle_wheel/examples/simple_car.gltf @@ -37,7 +37,7 @@ }, { "radius": 0.35, - "maxForce": 2000 + "maxPropulsionForce": 2000 } ] } diff --git a/extensions/2.0/OMI_vehicle_wheel/schema/glTF.OMI_vehicle_wheel.wheel.schema.json b/extensions/2.0/OMI_vehicle_wheel/schema/glTF.OMI_vehicle_wheel.wheel.schema.json index 45922ea..42e5efe 100644 --- a/extensions/2.0/OMI_vehicle_wheel/schema/glTF.OMI_vehicle_wheel.wheel.schema.json +++ b/extensions/2.0/OMI_vehicle_wheel/schema/glTF.OMI_vehicle_wheel.wheel.schema.json @@ -4,60 +4,86 @@ "title": "OMI_vehicle_wheel Wheel Parameters", "type": "object", "properties": { - "currentForceRatio": { + "brakingForce": { "type": "number", - "description": "The ratio of the maximum force the wheel is using for propulsion.", - "default": 0.0 - }, - "currentSteeringRatio": { - "type": "number", - "description": "The ratio of the maximum steering angle the wheel is rotated to.", - "default": 0.0 + "description": "The braking force in Newtons (kg⋅m/s²) that the wheel can provide. If negative or not specified, use propulsion force for braking.", + "default": -1.0 }, - "maxForce": { + "maxPropulsionForce": { "type": "number", "description": "The maximum force in Newtons (kg⋅m/s²) that the wheel can provide.", - "default": 0.0 + "default": 0.0, + "minimum": 0.0 }, "maxSteeringAngle": { "type": "number", "description": "The maximum angle in radians that the wheel can steer.", - "default": 0.0 + "default": 0.0, + "maximum": 3.1415925, + "minimum": 0.0 }, "physicsMaterial": { "allOf": [ { "$ref": "glTFid.schema.json" } ], "description": "The index of the physics material in the top level physicsMaterials array.", "default": -1 }, + "propulsionForceChangeRate": { + "type": "number", + "description": "The rate at which the wheel can change its applied force, measured in Newtons per second (N/s) or kilograms meters per second cubed (kg⋅m/s³) in SI base units. If negative or not specified, the wheel instantly changes to the desired propulsion force.", + "default": -1.0 + }, "radius": { "type": "number", "description": "The radius of the wheel in meters. This is the radius of a circle in the local YZ plane.", - "default": 0.25 + "default": 0.25, + "exclusiveMinimum": 0.0 + }, + "steeringChangeRate": { + "type": "number", + "description": "The rate at which the wheel can change its steering angle, measured in radians per second (rad/s). If negative, the wheel instantly changes to the desired steering angle.", + "default": 1.0 }, "suspensionDampingCompression": { "type": "number", "description": "The damping of the suspension during compression, the resistance to the velocity of the suspension. It is measured in Newton-seconds per meter (N⋅s/m), or kilograms per second (kg/s) in SI base units.", - "default": 2000.0 + "default": 2000.0, + "exclusiveMinimum": 0.0 }, "suspensionDampingRebound": { "type": "number", "description": "The damping of the suspension during rebound/relaxation, the resistance to the velocity of the suspension. It is measured in Newton-seconds per meter (N⋅s/m), or kilograms per second (kg/s) in SI base units.", - "default": 2000.0 + "default": 2000.0, + "exclusiveMinimum": 0.0 }, "suspensionStiffness": { "type": "number", "description": "The stiffness of the suspension, the resistance to traveling away from the start point. It is measured in Newtons per meter (N/m), or kilograms per second squared (kg/s²) in SI base units.", - "default": 20000.0 + "default": 20000.0, + "exclusiveMinimum": 0.0 }, "suspensionTravel": { "type": "number", "description": "The maximum distance the suspension can move up or down in meters.", - "default": 0.25 + "default": 0.25, + "minimum": 0.0 + }, + "targetPropulsionForceRatio": { + "type": "number", + "description": "The ratio of the maximum force the wheel is using for propulsion. May be negative for reverse force. Values outside the range [-1.0, 1.0] have implementation-defined behavior.", + "default": 0.0 + }, + "targetSteeringRatio": { + "type": "number", + "description": "The ratio of the maximum steering angle the wheel is rotated to. May be negative for steering in the opposite direction.", + "default": 0.0, + "minimum": -1.0, + "maximum": 1.0 }, "width": { "type": "number", "description": "The width of the wheel in meters. This is the width of the wheel in the local X axis.", - "default": 0.125 + "default": 0.125, + "exclusiveMinimum": 0.0 }, "extensions": { }, "extras": { }