diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..970c92f --- /dev/null +++ b/404.html @@ -0,0 +1,688 @@ + + + +
+ + + + + + + + + + + + + + +Open Metaverse Interoperability Group Stage 2 Proposal
+Written against the glTF 2.0 spec.
+Can be optionally used together with the OMI_physics_body
spec.
Colliders can be added to glTF nodes along with information about the "type" of collider it is representing.
+This extension allows specifying shapes to be used in glTF scenes for collision or triggers. Shapes should be specified on their own glTF nodes.
+This extension does not describe what colliders should do within a scene, aside from whether they are a trigger or not. For a more thorough physics body specification, you need to also use the OMI_physics_body
extension, and add OMI_collider
nodes as children of the physics body. Multiple OMI_collider
siblings with the same isTrigger
setting may be treated as a single "compound collider" in game engines that support them.
This example defines a single box collider with a size of 1 meter in all dimensions:
+{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensions": {
+ "OMI_collider": {
+ "colliders": [
+ {
+ "size": [1, 1, 1],
+ "type": "box"
+ }
+ ]
+ }
+ },
+ "extensionsUsed": [
+ "OMI_collider"
+ ],
+ "nodes": [
+ {
+ "extensions": {
+ "OMI_collider": {
+ "collider": 0
+ }
+ },
+ "name": "BoxCollider"
+ }
+ ],
+ "scene": 0,
+ "scenes": [
+ {
+ "nodes": [0]
+ }
+ ]
+}
+
+More example assets can be found in the examples/ folder. All of these examples use OMI_collider
and not OMI_physics_body
.
This extension consists of three new data structures for defining colliders on the root glTF document and referencing them on a glTF node. The main data structure defines a collider shape and is what most of this document describes. The second data structure uses the key "OMI_collider"
in the document-level "extensions"
which contains a list of the main collider shape data structures. The third data structure uses the key "OMI_collider"
in the node-level "extensions"
which contains an index of the collider shape to use from the list document-level collider list.
The extension must also be added to the glTF's extensionsUsed
array and because it is optional, it does not need to be added to the extensionsRequired
array.
The rest of the document, including this summary, defines the properties for the main data structure.
++ | Type | +Description | +Default value | +Valid on | +
---|---|---|---|---|
type | +string |
+The type of the collider shape as a string. | +Required, no default | +Always valid | +
isTrigger | +boolean |
+If true, the shape is a trigger and is not solid to other objects. | +false | +Always valid | +
size | +number[3] |
+The size of the box shape in meters. | +[1.0, 1.0, 1.0] | +Box | +
radius | +number |
+The radius of the shape in meters. | +0.5 | +Sphere, capsule, cylinder | +
height | +number |
+The height of the shape in meters. | +2.0 | +Capsule, cylinder | +
mesh | +number |
+The index of the glTF mesh in the document to use as a trimesh shape. | +-1 | +Trimesh, hull | +
The "type"
property is a lowercase string that defines what type of shape this collider is.
The selection of shapes was carefully chosen with a balance of compatibility between major game engines and containing the most commonly used shapes for easy asset creation. Colliders inherit the transform of the glTF node they are attached to. This includes rotation and translation, however it is discouraged to scale collider nodes since this can cause problems in some physics engines.
+Here is a table listing the mapping between the OMI_collider
type and the equivalent types in major game engines.
Shape | +Unity | +Unreal | +Godot | +Blender | +Bullet (Ammo, Panda3D, etc) | +
---|---|---|---|---|---|
Box | +Box | +Box | +BoxShape3D | +Box | +Box Shape | +
Sphere | +Sphere | +Sphere | +SphereShape3D | +Sphere | +Sphere Shape | +
Capsule | +Capsule | +Capsule | +CapsuleShape3D | +Capsule | +Capsule Shape | +
Cylinder | +Approximation | +Approximation | +CylinderShape3D | +Cylinder | +Cylinder Shape | +
Hull | +Mesh (Convex) | +Convex | +ConvexPolygonShape3D | +Convex Hull | +Convex Shape | +
Trimesh | +Mesh | +Mesh | +ConcavePolygonShape3D | +Mesh | +Mesh Shape | +
Box shapes describe a cube or cuboid shape. They have size
property which is an array of 3 numbers that describes the width, height, and depth. If the size
property is omitted, the default size is [1, 1, 1]
, representing a cube with a volume of one cubic meter, edges/diameters one meter long, and extents/radius of half a meter. The position of the glTF node is the center of the box shape.
Sphere shapes describe a uniform "ball" shape. They have a radius
property which is a single number. If the radius
property is omitted, the default radius is 0.5
, representing a sphere with a radius of half a meter, a diameter of one meter. The position of the glTF node is the center of the sphere shape.
Capsule shapes describe a "pill" shape. They have a radius
and height
property. The height is aligned with the node's local vertical axis. If you wish to align it along a different axis, rotate the glTF node. If the radius
property is omitted, the default radius is 0.5
, and if the height
property is omitted, the default height is 2
. The height describes the total height from bottom to top. The height of the capsule must be at least twice as much as the radius. The "mid-height" between the centers of each spherical cap end can be found with height - radius * 2
.
Cylinder shapes describe a "tall circle" shape. They are similar in structure to capsules, they have a radius
and height
property. The height is aligned with the node's local vertical axis. If you wish to align it along a different axis, rotate the glTF node. If the radius
property is omitted, the default radius is 0.5
, and if the height
property is omitted, the default height is 2
.
The use of cylinder is discouraged if another shape would work well in its place. Cylinders are harder to calculate than boxes, spheres, and capsules. Not all game engines support cylinder shapes. Engines that do not support cylinder shapes should use an approximation, such as a convex hull roughly shaped like a cylinder. Cylinders over twice as tall as they are wide can use another approximation: a convex hull combined with an embedded capsule (to allow for smooth rolling), by copying the cylinder's values into a new capsule shape.
+Hull shapes represent a convex hull. Being "convex" means that the shape cannot have any holes or divots. Hulls are defined with a mesh
property with an index of a mesh in the glTF meshes
array. The glTF mesh in the array MUST be a trimesh
to work, and should be made of only one glTF mesh primitive (one surface). Valid hulls must contain at least one triangle, which becomes three points on the convex hull. Hulls are recommended to have at least four points so that they have 3D volume. The final hull shape should have no more than 255 points in total.
Hulls can be used to represent complex convex shapes that are not easy to represent with other primitives. If a shape can be represented with a few primitives, prefer using those primitives instead of convex hulls. Hulls are much faster than trimesh shapes.
+Trimesh shapes represent a concave triangle mesh. They are defined with a mesh
property with an index of a mesh in the glTF meshes
array. The glTF mesh in the array MUST be a trimesh
to work, and should be made of only one glTF mesh primitive (one surface). Valid trimesh shapes must contain at least one triangle.
Avoid using a trimesh shape for most objects, they are the slowest shapes to calculate and have several limitations. Most physics engines do not support moving trimesh shapes or calculating collisions between multiple trimesh shapes. Trimesh shapes will not work reliably with isTrigger
or with pushing objects out due to not having an "interior" space, they only have a surface. Trimesh shapes are typically used for complex level geometry (for example, things that you can go inside of). If your shape can be represented with a combination of simpler primitives, or a convex hull, or multiple convex hulls, prefer that instead.
The "isTrigger"
property defines if this shape is a trigger or collider. It is a boolean, either true
or false
. If omitted, the default value is false
.
Trigger shapes are not solid to other objects and will not collide with them. Triggers can generate events when another physics body "enters" them. For example, a "goal" area which triggers whenever a ball gets thrown into it. Which event gets triggered by a trigger shape is game-specific and not defined by OMI_collider
(if nothing happens, that is also valid).
See schema/glTF.OMI_collider.collider.schema.json for the main collider schema, schema/glTF.OMI_collider.schema.json for the document-level list of colliders, and schema/node.OMI_collider.schema.json for the node-level collider selection.
+Draft
+Written against the glTF 2.0 spec.
+This extension allows for the addition of spatialized and non-spatialized audio to glTF scenes.
+Audio emitter objects may be added to 3D nodes for positional audio or to the scene for environmental or ambient audio such as background music.
+{
+ "extensions": {
+ "KHR_audio_emitter": {
+ "emitters": [
+ {
+ "name": "Positional Emitter",
+ "type": "positional",
+ "gain": 0.8,
+ "sources": [0, 1],
+ "positional": {
+ "shapeType": "omnidirectional",
+ "distanceModel": "inverse",
+ "maxDistance": 10.0,
+ "refDistance": 1.0,
+ "rolloffFactor": 0.8
+ }
+ },
+ {
+ "name": "Global Emitter",
+ "type": "global",
+ "gain": 0.8,
+ "sources": [1]
+ }
+ ],
+ "sources": [
+ {
+ "name": "Clip 1",
+ "gain": 0.6,
+ "autoPlay": true,
+ "loop": true,
+ "audio": 0
+ },
+ {
+ "name": "Clip 2",
+ "gain": 0.6,
+ "autoPlay": true,
+ "loop": true,
+ "audio": 1
+ }
+ ],
+ "audio": [
+ {
+ "uri": "audio1.mp3"
+ },
+ {
+ "bufferView": 0,
+ "mimeType": "audio/mpeg"
+ }
+ ]
+ }
+ },
+ "scenes": [
+ {
+ "name": "Default Scene",
+ "extensions": {
+ "KHR_audio_emitter": {
+ "emitters": [1]
+ }
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "Duck",
+ "translation": [1.0, 2.0, 3.0],
+ "extensions": {
+ "KHR_audio_emitter": {
+ "emitter": 0
+ }
+ }
+ }
+ ]
+}
+
+This extension consists of three primary data structures: Audio Data, Audio Sources, and Audio Emitters. Data, sources and emitters are defined on an KHR_audio_emitter
object added to the extensions
object on the document root.
The extension must be added to the file's extensionsUsed
array and because it is optional, it does not need to be added to the extensionsRequired
array.
{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensionsUsed": [
+ "KHR_audio_emitter"
+ ],
+ "scenes": [...],
+ "nodes": [...],
+ "extensions": {
+ "KHR_audio_emitter": {
+ "audio": [...],
+ "sources": [...],
+ "emitters": [...]
+ }
+ }
+}
+
+Audio data objects define where audio data is located and what format the data is in. The data is either accessed via a bufferView or uri.
+When storing audio data in a buffer view, the mimeType
field must be specified. The base specification supports audio/mpeg
and audio/wav
MIME types. These were chosen with consideration for the wide support for these types acrosss 3D engines and common use cases. Other supported audio formats may be added via extensions.
Note that in tools that process glTF files, but do not implement the KHR_audio_emitter
extension, external files referenced via the uri
field may not be properly copied to their final destination or baked into the final binary glTF file. In these cases, using the bufferView
property may be a better choice assuming the referenced bufferView
index is not changed by the tool. The uri
field might be a better choice when you want to be able to quickly change the referenced audio asset.
The "bufferView"
property is the integer index of the bufferView that contains the audio data. If present, use this instead of the audio source's uri property.
The "mimeType"
property is a string that specifies the audio's MIME type. Required if bufferView
is defined. Unless specified by another extension, the only supported mimeTypes are audio/mpeg
and audio/wav
.
The "uri"
property is a string for the uri of the audio file. Relative paths are relative to the .gltf file.
Audio sources reference audio data and define playback properties for it. Audio sources may be used by zero to many audio emitters.
++ | Type | +Description | +Default value | +
---|---|---|---|
gain | +number |
+Unitless linear multiplier against original audio file volume used for determining audio source loudness. | +1.0 | +
loop | +boolean |
+Whether or not to loop the specified audio when finished. | +false | +
autoPlay | +boolean |
+Whether or not to play the specified audio when the glTF is loaded. | +false | +
audio | +number |
+The index of the audio data assigned to this clip. | +Required, no default | +
The "gain"
property is a number that is a unitless linear multiplier against original audio file volume used for determining audio source loudness. If not specified, the audio source volume gain is 1.0
.
This value is linear, a value of 0.0
is no volume, 0.5
is half volume, 1.0
is the original volume, 2.0
is double the volume, etc. The final volume of the audio is a combination of this value, the audio emitter's gain, and if the audio emitter is positional, the relative positions of the emitter and listener.
The "loop"
property is a boolean that specifies whether or not to loop the specified audio when finished. If false
or not specified, the audio source does not loop.
The "autoPlay"
property is a boolean that specifies whether or not to play the specified audio when the glTF is loaded. If false
or not specified, the audio source does not play automatically.
The "audio"
property is an integer index of the audio data in the "audio" array assigned to this audio source. This property is required.
Audio emitters define how audio sources are played back. Emitter properties are defined at the document level and are references by nodes. Audio may be played globally or positionally. Positional audio has further properties that define how audio volume scales with distance and angle.
++ | Type | +Description | +Default value | +
---|---|---|---|
type | +string |
+The emitter type of this audio emitter: global or positional . |
+Required, no default | +
gain | +number |
+Unitless linear multiplier against audio source volume used for determining audio emitter loudness. | +1.0 | +
sources | +number[] |
+An array of audio source indices used by the audio emitter. This array may be empty. | +[] | +
positional | +object |
+A sub-JSON containing the positional audio emitter properties. | +{} | +
The "type"
property is a string that specifies the audio emitter type. This property is required.
global
Global audio emitters are not affected by the position of audio listeners. All positional
properties may not be defined on global audio emitters.positional
Positional audio emitters play audio at a position in the scene. The properties are defined in the positional
object. Using sound cones, the orientation is -Z
having the same emission direction as KHR_lights_punctual
and glTF cameras.The "gain"
property is a number that is a unitless linear multiplier against audio source volume used for determining audio emitter loudness. If not specified, the audio emitter volume gain is 1.0
.
This value is linear, a value of 0.0
is no volume, 0.5
is half volume, 1.0
is the original volume, 2.0
is double the volume, etc. The final volume of the audio is a combination of this value, the audio source's gain, and if the audio emitter is positional, the relative positions of the emitter and listener.
The "sources"
property is an array of integer indices of audio sources in the "sources" array used by the audio emitter. This array may be empty. If empty or not specified, this emitter can be used to define how audio should emit from a node, but not which audio source to play.
The "positional"
property is a sub-JSON object containing the positional audio emitter properties. This may only be defined if type
is set to positional
.
When the audio emitter type is set to positional
, additional properties may be defined on the positional
object.
+ | Type | +Description | +Default value | +
---|---|---|---|
shapeType | +string |
+The shape of the audio emitter. May be omnidirectional , cone , or a value specified by another extension. |
+"omnidirectional" |
+
coneInnerAngle | +number |
+The anglular diameter of a cone inside of which there will be no angular volume reduction. | +6.2831853... (τ or 2π rad, 360 deg) | +
coneOuterAngle | +number |
+The anglular diameter of a cone outside of which the volume will be reduced to a constant value of coneOuterGain . |
+6.2831853... (τ or 2π rad, 360 deg) | +
coneOuterGain | +number |
+The linear volume gain of the audio emitter set when outside the cone defined by the coneOuterAngle property. |
+0.0 | +
distanceModel | +string |
+Specifies the distance model for the audio emitter. | +"inverse" |
+
maxDistance | +number |
+The maximum distance between the emitter and listener, beyond which the audio cannot be heard. | +0.0 | +
refDistance | +number |
+A reference distance for reducing volume as the emitter moves further from the listener. | +1.0 | +
rolloffFactor | +number |
+Describes how quickly the volume is reduced as the emitter moves away from listener. | +1.0 | +
The "shapeType"
property is a string that defines shape type of the audio emitter. May be "omnidirectional"
, "cone"
, or a value specified by another extension. If not specified, the default shape is omnidirectional
.
If the shape is "omnidirectional"
, the "coneInnerAngle"
, "coneOuterAngle"
, and "coneOuterGain"
properties are ignored.
The "coneInnerAngle"
property is a number that defines the angle, in radians, of a cone inside of which there will be no volume reduction. This angle represents the angular "diameter" of the cone, from side to side. If not specified, the angle of Tau radians (6.283185307179586476925286766559
or 360 degrees) is used, which means the audio emits in all directions (not in a cone).
The "coneOuterAngle"
property is a number that defines the angle, in radians, of a cone outside of which the volume will be reduced to a constant value of coneOuterGain
. This angle represents the angular "diameter" of the cone, from side to side. If not specified, the angle of Tau radians (6.283185307179586476925286766559
or 360 degrees) is used, which means some audio will emit in all directions.
The "coneOuterGain"
property is a number that defines the linear volume gain of the audio emitter set when outside the cone defined by the coneOuterAngle
property. It is a linear value (not dB). If not specified, the cone outer gain is 0.0
, meaning the audio will be silent outside of the cone.
The "distanceModel"
property is a string that specifies the distance model for the audio emitter.
linear
A linear distance model calculating the gain induced by the distance according to:
+ 1.0 - rolloffFactor * (distance - refDistance) / (maxDistance - refDistance)
inverse
(default) An inverse distance model calculating the gain induced by the distance according to:
+ refDistance / (refDistance + rolloffFactor * (Math.max(distance, refDistance) - refDistance))
exponential
An exponential distance model calculating the gain induced by the distance according to:
+ pow((Math.max(distance, refDistance) / refDistance, -rolloffFactor))
The "maxDistance"
property is a number that defines the maximum distance between the emitter and listener, after which the volume will not be reduced any further. If zero or not specified, the audio emitter does not have a maximum distance, and it can be heard from any distance.
For the linear distance model, the max distance must be greater than the ref distance. For all distance models, max distance cannot be a negative number.
+The "refDistance"
property is a number that defines a reference distance for reducing volume as the emitter moves further from the listener. For distances less than this, the volume is not reduced. This value cannot be zero or a negative number. If not specified, the default value is 1.0
.
The "rolloffFactor"
property is a number that describes how quickly the volume is reduced as the emitter moves away from listener. When distanceModel is set to linear, the maximum value is 1.0
. Otherwise, there is no upper limit to the rolloff factor. If not specified, the default value is 1.0
.
Audio emitters of type global
may be added to scenes using the following syntax:
{
+ "scenes": [
+ {
+ "extensions": {
+ "KHR_audio_emitter": {
+ "emitters": [1, 3]
+ }
+ }
+ }
+ ]
+}
+
+Audio emitters of type global
may be added to nodes using the following syntax:
{
+ "nodes": [
+ {
+ "extensions": {
+ "KHR_audio_emitter": {
+ "emitter": 1
+ }
+ }
+ }
+ ]
+}
+
+Audio emitters of type positional
may be added to nodes using the following syntax:
{
+ "nodes": [
+ {
+ "extensions": {
+ "KHR_audio_emitter": {
+ "emitter": 0
+ }
+ }
+ }
+ ]
+}
+
+Note that multiple global audio emitters are allowed on the scene, but only a single audio emitter may be added to a node.
+The Audio Rolloff range is (0.0, +∞)
. The default is 1.0
.
The rolloff formula is dependant on the distance model defined. The available distance models are linear
, inverse
, and exponential
.
1.0 - rolloffFactor * (distance - refDistance) / (maxDistance - refDistance)
refDistance / (refDistance + rolloffFactor * (Math.max(distance, refDistance) - refDistance))
pow((Math.max(distance, refDistance) / refDistance, -rolloffFactor))
The gain unit range is (0.0, +∞)
. The default is 1.0
.
originalVolume * gain
Figure 1. A modified graphic based on the W3C Web Audio API Audio cone Figure
+The cone properties relate to the PannerNode
interface and determine the amount of volume relative to a listeners position within the defined cone area.
The gain relative to cone properties is determined in a similar way as described in the web audio api with the difference that this audio emitter extension uses radians in place of degrees. Cone Gain Algorithm Example
+Radians are used for angles matching glTF2.
+See glTF.KHR_audio_emitter.schema.json for the main document-level schema.
+Prior Art:
+ + + + + + + + + + + + + + +Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+This extension allows glTF models to use Ogg Vorbis as a valid audio data format. A client that does not implement this extension can ignore the provided Ogg Vorbis audio data and continue to rely on the MP3 audio available in the base KHR_audio_emitter specification, if any. Defining fallback audio data is optional. The best practices section describes the intended use case of this extension and the expected behavior when using it without fallback audio data.
+Ogg Vorbis is a free and open-source lossy audio compression format. It is widely used in video games and other applications that require high-quality audio with a small file size. It has similar use cases to MP3, but is more efficient, with a higher quality for the same file size, or a smaller file size for the same quality.
+The OMI_audio_ogg_vorbis
extension can be added to the extensions of an object in the "sources"
array inside of KHR_audio_emitter
. and specifies an audio
property that points to the index of an object in the "audio"
array which in turn points to the Ogg Vorbis audio data.
The following glTF will load music.ogg
in clients that support this extension, and otherwise fall back to music.mp3
. Note that this example excludes other properties typically found on an item in "sources"
, such as the name or gain.
"KHR_audio_emitter": {
+ "sources": [
+ {
+ "audio": 0,
+ "extensions": {
+ "OMI_audio_ogg_vorbis": {
+ "audio": 1
+ }
+ }
+ }
+ ],
+ "audio": [
+ {
+ "uri": "music.mp3"
+ },
+ {
+ "uri": "music.ogg"
+ }
+ ]
+}
+
+When defined with a uri
and no mimeType
, the file extension of the audio data MUST be .ogg
. The .oga
file extension MUST NOT be used for Vorbis audio data. Although most audio in the Ogg container shares the same MIME type, Vorbis and Speex have been grandfathered in to use .ogg
as the file extension, while other audio codecs in the Ogg container may use .oga
or another file extension.
Audio data can also be stored in a buffer, such as in the glTF Binary (.glb) format. When stored here, the item in the "audio" MUST have a mimeType
set to audio/ogg
.
"KHR_audio_emitter": {
+ "sources": [
+ {
+ "audio": 0,
+ "extensions": {
+ "OMI_audio_ogg_vorbis": {
+ "audio": 1
+ }
+ }
+ }
+ ],
+ "audio": [
+ {
+ "mimeType": "audio/mpeg",
+ "bufferView": 1
+ },
+ {
+ "mimeType": "audio/ogg",
+ "bufferView": 2
+ }
+ ]
+}
+
+KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json
+Ogg Vorbis has similar use cases to MP3. Both are lossy audio compression formats that are widely supported. Ogg Vorbis should be preferred to MP3 when delivering audio data to clients that support loading Ogg Vorbis audio data from OMI_audio_ogg_vorbis
.
Since Ogg Vorbis is not a part of the base KHR_audio_emitter spec, it is recommended to provide an MP3 fallback for clients that do not support this extension. The fallback audio data would typically be of lower quality than the corresponding Ogg Vorbis audio data, such that the total file size of both files is lower than the full quality audio saved as MP3, otherwise it would defeat the purpose of using Ogg Vorbis to reduce file size.
+When fallback audio data is defined, this extension should not be present in extensionsRequired
. This will allow clients that support KHR_audio_emitter
but not OMI_audio_ogg_vorbis
to play the fallback audio data.
To use Ogg Vorbis audio data without a fallback, define OMI_audio_ogg_vorbis
in extensionsUsed
, and if KHR_audio_emitter
is a required extension, define OMI_audio_ogg_vorbis
in extensionsRequired
as well. The item in the "sources"
array will then not have an audio
property, and instead have an extensions
property to define the audio data as shown below.
"KHR_audio_emitter": {
+ "sources": [
+ {
+ "extensions": {
+ "OMI_audio_ogg_vorbis": {
+ "audio": 0
+ }
+ }
+ }
+ ],
+ "audio": [
+ {
+ "uri": "music.ogg"
+ }
+ ]
+}
+
+If a glTF contains an audio source using Ogg Vorbis audio with no fallback audio data, and KHR_audio_emitter
is a required extension, then OMI_audio_ogg_vorbis
should be added to the extensionsRequired
array, such that clients that do not support Ogg Vorbis will not attempt to load the file. This ensures that glTF files which require audio playback will also require the ability to load Ogg Vorbis audio data to be played.
If a glTF contains an audio source using Ogg Vorbis audio with no fallback audio data, and KHR_audio_emitter
is not a required extension, then do not add OMI_audio_ogg_vorbis
to extensionsRequired
. Clients supporting KHR_audio_emitter
but not OMI_audio_ogg_vorbis
will consider the lack of "audio"
in a KHR_audio_emitter
source to be an error, and either will not be able to play audio from that source, or will not load the KHR_audio_emitter
extension.
See schema/glTF.KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json for the schema.
+None
+Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+This extension allows glTF models to use Opus as a valid audio codec. A client that does not implement this extension can ignore the provided Opus audio data and continue to rely on the MP3 audio available in the base KHR_audio_emitter specification, if any. Defining fallback audio data is optional. The best practices section describes the intended use case of this extension and the expected behavior when using it without fallback audio data.
+Opus codec audio is a free and open source lossy audio codec. It is widely used in modern websites and other Internet applications, but does not have widespread support in popular game engines. It is proposed as an IETF standard as defined by IETF RFC 6716. Opus superior format to MP3 and Ogg Vorbis, and it is suitable for many use cases traditionally suitable to WAV audio. Opus is a lossy audio codec that is more efficient than MP3 and Ogg Vorbis, with a higher quality for the same file size, or a smaller file size for the same quality.
+The OMI_audio_opus
extension allows the Opus audio data to be stored in either the Ogg container format or the WebM container format. This is done to minimize friction depending on the desired target platform. The Ogg container format is the de facto file format for Opus, it uses the .opus
file extension and the audio/opus
MIME type. The WebM container format is more commonly supported in web browsers such as Safari, it uses the .webm
file extension and the audio/webm
MIME type (or video/webm
if the file contains video data, which is expected to be ignored by audio emitters). Implementations should be able to handle both formats, but may more efficiently support one format over the other. For example, in a web game targeting Safari, its own assets should be in the WebM container format, while if that same game encounters a user-provided glTF asset using Ogg Opus audio, the game may need to convert the audio to WebM Opus before playing it in Safari. The same may happen in reverse, if an app only supports Ogg Opus, it should prefer Ogg Opus for its own assets, and may need to convert WebM Opus to Ogg Opus before playing it. Additionally, WebM allows for videos, which allows the audio from a video file to be used in an audio emitter, while the video data may be used by another extension.
The OMI_audio_opus
extension can be added to the extensions of an object in the "sources"
array inside of KHR_audio_emitter
. and specifies an audio
property that points to the index of an object in the "audio"
array which in turn points to the Opus audio data.
The following glTF will load music.opus
in clients that support this extension, and otherwise fall back to music.mp3
. Note that this example excludes other properties typically found on an item in "sources"
, such as the name or gain.
"KHR_audio_emitter": {
+ "sources": [
+ {
+ "audio": 0,
+ "extensions": {
+ "OMI_audio_opus": {
+ "audio": 1
+ }
+ }
+ }
+ ],
+ "audio": [
+ {
+ "uri": "music.mp3"
+ },
+ {
+ "uri": "music.opus"
+ }
+ ]
+}
+
+When defined with a uri
and no mimeType
, the file extension of the audio data MUST be .opus
when using the Ogg container format, or MUST be .webm
when using the WebM container format. The .oga
or .ogg
file extensions MUST NOT be used for Opus codec audio data. Although most audio in the Ogg container shares the same MIME type, Opus uses .opus
as the file extension, while other audio codecs in the Ogg container may use .oga
or .ogg
or another file extension.
Audio data can also be stored in a buffer, such as in the glTF Binary (.glb) format. When stored here, the item in the "audio" MUST have a mimeType
set to audio/opus
when using the Ogg container format (see proposed IETF RFC 7587 for IANA), or audio/webm
or video/webm
when using the WebM container format. If a WebM contains only audio data, then audio/webm
should be used. If a WebM contains video data, then video/webm
should be used, and the video data is expected to be ignored when playing the audio from a KHR_audio_emitter
source extended with OMI_audio_opus
with this data.
"KHR_audio_emitter": {
+ "sources": [
+ {
+ "audio": 0,
+ "extensions": {
+ "OMI_audio_opus": {
+ "audio": 1
+ }
+ }
+ }
+ ],
+ "audio": [
+ {
+ "mimeType": "audio/mpeg",
+ "bufferView": 1
+ },
+ {
+ "mimeType": "audio/opus",
+ "bufferView": 2
+ }
+ ]
+}
+
+KHR_audio_emitter.source.OMI_audio_opus.schema.json
+Opus codec audio is a superior format to MP3 and Ogg Vorbis for their intended use cases, and it is suitable for many use cases traditionally suitable to WAV audio. Opus is a lossy audio codec that is more efficient than MP3 and Ogg Vorbis, with a higher quality for the same file size, or a smaller file size for the same quality.
+Opus is designed to be used either for long-term audio storage or streaming audio over the internet. Opus has a low decode time, with a low latency, making it suitable for time-sensitive audio playback such as short sound effects in video games, therefore it can be used in place of WAV audio when lossy audio is acceptable. See the Wikipedia chart comparing Opus to other audio codecs.
+If making a comparison to image formats, Opus is to audio as WebP is to images. It is a modern, efficient, and high-quality format that is superior to older formats, but has less support in popular software.
+Since Opus is not a part of the base KHR_audio_emitter spec, it is recommended to provide an MP3 or WAV fallback for clients that do not support this extension. Prefer using an MP3 fallback for music or a WAV fallback for short sound effects. The fallback audio data would typically be of lower quality than the corresponding Opus audio data, such that the total file size of both files is lower than the full quality audio saved as MP3 or WAV, otherwise it would defeat the purpose of using Opus to reduce file size.
+When fallback audio data is defined, this extension should not be present in extensionsRequired
. This will allow clients that support KHR_audio_emitter
but not OMI_audio_opus
to play the fallback audio data.
To use Opus audio data without a fallback, define OMI_audio_opus
in extensionsUsed
, and if KHR_audio_emitter
is a required extension, define OMI_audio_opus
in extensionsRequired
as well. The item in the "sources"
array will then not have an audio
property, and instead have an extensions
property to define the audio data as shown below.
"KHR_audio_emitter": {
+ "sources": [
+ {
+ "extensions": {
+ "OMI_audio_opus": {
+ "audio": 0
+ }
+ }
+ }
+ ],
+ "audio": [
+ {
+ "uri": "music.opus"
+ }
+ ]
+}
+
+If a glTF contains an audio source using Opus audio with no fallback audio data, and KHR_audio_emitter
is a required extension, then OMI_audio_opus
should be added to the extensionsRequired
array, such that clients that do not support Opus will not attempt to load the file. This ensures that glTF files which require audio playback will also require the ability to load Opus audio data to be played.
If a glTF contains an audio source using Opus audio with no fallback audio data, and KHR_audio_emitter
is not a required extension, then do not add OMI_audio_opus
to extensionsRequired
. Clients supporting KHR_audio_emitter
but not OMI_audio_opus
will consider the lack of "audio"
in a KHR_audio_emitter
source to be an error, and either will not be able to play audio from that source, or will not load the KHR_audio_emitter
extension.
See schema/glTF.KHR_audio_emitter.source.OMI_audio_opus.schema.json for the schema.
+None
+Draft Specification
+Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+Optionally depends on the OMI_physics_body
spec for triggers.
This extension allows for objects to add world traversal behavior in the form of links. This specification does not contain any definitions in how the link data is used and it is up to the client builder to decide how the link is activated.
+In addition to being defined independently, the OMI_link
extension can be defined on an OMI_physics_body
node with the type set to trigger. When OMI_link
is on a trigger, the trigger should be treated as an activator for the link, such that a player activating that trigger should cause that player to activate the link.
Clients may choose to have application-specific behaviors for URIs otherwise the default platform / browser URI handler should be used. Applications should perform any necessary URI security checks and may decide to present a dialog to inform the user of how the URI is handled.
+{
+ "scenes": [
+ {
+ "name": "Default Scene",
+ "nodes": [0]
+ }
+ ],
+ "nodes": [
+ {
+ "name": "OMI Group meetup space",
+ "translation": [1.0, 2.0, 3.0],
+ "extensions": {
+ "OMI_link": {
+ "title": "Meetup World",
+ "uri": "https://omigroup.org/worlds/meetup"
+ },
+ "OMI_physics_body": {
+ "trigger": {}
+ }
+ }
+ }
+ ]
+}
+
+uri
Must be a valid uri, relative path, or fragment.
+Valid uri
field values:
https://omigroup.org/worlds/meetup
+https://omigroup.org/worlds/meetup#portal1
+https://omigroup.org/worlds/meetup?autojoin=true#portal1
+vrchat://launch?id=wrld_hjdksahgklfshjfgjklsd
+./breakoutroom1
+#portal1
+
+title
Optional field that describes the destination of the uri. Implementations can show this title if it exists or fallback to showing the uri.
+TODO
+Interested Implementations: +* Third Room - https://github.com/thirdroom/thirdroom +* Three Object Viewer (WordPress Plugin) - https://wordpress.org/plugins/three-object-viewer/
+TODO
+Anthony Burchell, Individual Contributor
+Draft Specification
+Open Metaverse interoperability Group Stage 1 proposal
+Written against the glTF 2.0 spec.
+The OMI_personality extension allows you to specify a personality for a glTF node representing a entity. This extension can be used in virtual worlds, where characters can have unique personalities, and interactions with them can be enhanced by providing additional information about their behavior and dialogue.
+To use the "OMI_personality" extension, you must first specify it in the extensionsUsed property of your glTF file.
+{
+ "extensionsUsed": ["OMI_personality"]
+}
+
+Next, apply the extension to a child node of the glTF file. The node's position and rotation data can be used to determine the location of the spawn point in the scene.
+{
+ "nodes": [
+ {
+ "name": "cat",
+ "extensions": {
+ "OMI_personality": {
+ "agent": "cat",
+ "personality": "#agent has a cheerful personality.",
+ "defaultMessage": "nya nya!"
+ }
+ }
+ }
+ ]
+}
+
+In the example above, the "OMI_personality" extension is applied to a node named "cat". The agent property is used to specify the type of agent associated with the node, in this case, it's a cat. The personality property describes the agent's personality, and the defaultMessage property is the message that the agent will send as a default.
+The defaultMessage
parameter is optional. The agent
and personality
options are required and provide context about the avatars name and default personality description.
+ | Type | +Description | +
---|---|---|
agent | +string |
+The name of the agent or NPC. | +
personality | +string |
+A default description of the personality of the agent allowing clients to inject that context into language model logic. | +
defaultMessage | +string |
+A default message for this agent to initialize with. | +
The OMI_personality extension is defined by the following JSON schema:
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "OMI_personality",
+ "description": "An extension for the glTF format that defines a personality for a node and an endpoint where additional information can be queried.",
+ "type": "object",
+ "properties": {
+ "agent": {
+ "type": "string",
+ "description": "The name of the agent associated with the node.",
+ "maxLength": 128
+ },
+ "personality": {
+ "type": "string",
+ "description": "A description of the agent's personality."
+ },
+ "defaultMessage": {
+ "type": "string",
+ "description": "The default message that the agent will send on initialization."
+ }
+ },
+ "required": ["agent", "personality"]
+}
+
+The OMI_personality extension allows users to inject a unique personality into their virtual representations and adheres to a simple set of properties that aim to be compatible with lots of AI software to come. In the below example implementation, the data for personality is used to combine with the input from the user talking to the NPC. The final prompt is being sent to the OpenAI Davinci model to allow for completion of the agent's response.
+ // Request coming from three.js frontend that is querying this endpoint making a call to the GPT-3 model. Contains the Personality data embeded in the NPC file.
+ const data = await request.json();
+ let prompt = data.inputs.personality;
+ let prompt = data.Input.personality;
+
+ let finalPrompt = prompt
+ .replaceAll('#speaker', data.Input.Speaker)
+ .replaceAll('#input', data.Input.Input)
+ .replaceAll('#agent', data.Input.Agent)
+ .replaceAll('#conversation', data.Input.Conversation)
+ .replaceAll('undefined\n','' ).replaceAll('undefined','')
+ .slice(-5000)
+
+ const token = authorization.split(' ')[1];
+ const postData = {
+ prompt: finalPrompt,
+ max_tokens: 500,
+ stop : ["###"],
+ temperature: 0.7,
+ };
+
+ // Make the first request to the Davinci model
+ const davinciResponse = await fetch('https://api.openai.com/v1/engines/text-davinci-003/completions', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
+ },
+ body: JSON.stringify(postData)
+ });
+
+
+The final prompt in this examples follows a structure of:
+Agent is cheerful and always willing to help with programming tasks. They are an entity that lives in a virtual world always looking for new and intersting ways to help humans create content in the metaverse.
+Speaker: Hello agent! Tell me about yourself
+Agent:
+
+The above example is a final prompt that is sent to OpenAI where the model completes what the "Agent:" would write to complete this conversation factoring in the personality data above the chat log.
+In this example, the text is being passed back to the Three.js scene and rendered in the scene using a Text component.
+ + + + + + + + + + + + + +If a node has the "collider"
property defined, it is a solid collider node that objects can collide with.
+ | Type | +Description | +Default value | +
---|---|---|---|
shape | +integer |
+The index of the shape to use as the collision shape. | +-1 | +
The "shape"
property is an integer index that references a shape in the document-level shapes array as defined by the OMI_physics_shape
extension. If not specified or -1, this node has no collider shape, but may be the parent of other nodes that do have collider shapes, and can combine those nodes into one collider (this may be a body or compound collider depending on the engine).
See schema/node.OMI_physics_body.collider.schema.json for the collider properties JSON schema.
+ + + + + + + + + + + + + +If a node has the "motion"
property defined, its transform is driven by the physics engine.
+ | Type | +Description | +Default value | +
---|---|---|---|
type | +string |
+The type of the physics body as a string. | +Required, no default | +
mass | +number |
+The mass of the physics body in kilograms. | +1.0 | +
linearVelocity | +number[3] |
+The initial linear velocity of the body in meters per second. | +[0.0, 0.0, 0.0] | +
angularVelocity | +number[3] |
+The initial angular velocity of the body in radians per second. | +[0.0, 0.0, 0.0] | +
centerOfMass | +number[3] |
+The center of mass offset from the origin in meters. | +[0.0, 0.0, 0.0] | +
inertiaDiagonal | +number[3] |
+The inertia around principle axes in kilogram meter squared (kg⋅m²). | +[0.0, 0.0, 0.0] | +
inertiaOrientation | +number[4] |
+The inertia orientation as a Quaternion. | +[0.0, 0.0, 0.0, 1.0] | +
The "type"
property is a lowercase string that defines what type of physics body this is. Different types of physics bodies have different interactions with physics systems and other bodies within a scene.
Here is a table listing the mapping between the OMI_physics_body
type and the equivalent types in major game engines.
Body Type | +Unity | +Godot 3 | +Godot 4 | +Unreal | +
---|---|---|---|---|
Static | +Collider | +StaticBody | +StaticBody3D | +WorldStatic, Simulate Physics = false | +
Kinematic | +Rigidbody.isKinematic | +KinematicBody | +AnimatableBody3D | +WorldDynamic, Simulate Physics = false | +
Dynamic | +Rigidbody | +RigidBody | +RigidBody3D | +PhysicsBody, Simulate Physics = true | +
Static bodies can be collided with, but do not have simulated movement. They are usually used for level geometry. Specifying a static body is optional, as nodes with collider properties are assumed to be static without itself or a parent node having the motion property.
+Kinematic bodies can be collided with, and can be moved using scripts or animations. They can be used for moving platforms.
+Dynamic bodies are bodies simulated with rigid body dynamics. They collide with other bodies, and move around on their own in the physics simulation. They are affected by gravity. They can be used for props that move around in the world.
+The "mass"
property is a number that defines how much mass this physics body has in kilograms. Not all body types can make use of mass, such as triggers or non-moving bodies, in which case the mass can be ignored. If not specified, the default value is 1 kilogram.
The "linearVelocity"
property is an array of three numbers that defines how much linear velocity this physics body starts with in meters per second. Not all body types can make use of linear velocity, such as non-moving bodies, in which case the linear velocity can be ignored. If not specified, the default value is zero.
The "angularVelocity"
property is an array of three numbers that defines how much angular velocity this physics body starts with in radians per second. Not all body types can make use of angular velocity, such as non-moving bodies, in which case the angular velocity can be ignored. If not specified, the default value is zero.
The "centerOfMass"
property is an array of three numbers that defines the position offset in meters of the center of mass in the body's local space. If not specified, the default value is zero.
This property is useful when converting assets with a center of mass, but when creating new assets it is recommended to leave the center of mass at the body's origin. Some physics engines support the center of mass being offset from the origin, but not all of them do. Implementations without support for a center of mass offset would have to adjust the node positions to make this work, which may be undesired.
+The "inertiaDiagonal"
property is an array of 3 numbers that defines the inertia around the principle axes of the body in kilogram meter squared (kg⋅m²). We specify "tensor" in the name because this defines inertia in multiple directions and is different from linear momentum inertia. Only the "dynamic" motion type can make use of inertia. If zero or not specified, the inertia should be automatically calculated by the physics engine.
The "inertiaOrientation"
property is an array of 4 numbers that defines a Quaternion for orientation of the inertia's principle axes relative to the body's local space. If not specified or set to the default value of [0.0, 0.0, 0.0, 1.0]
, no rotation is applied, the inertia's principle axes are aligned with the body's local space axes.
See schema/node.OMI_physics_body.motion.schema.json for the motion properties JSON schema.
+ + + + + + + + + + + + + +A useful construct in a physics engine is a non-solid volume of space which does not generate impulses when overlapping with other volumes. These objects are typically called "triggers", "sensors", "phantoms", or "overlap volumes" in physics simulation engines. Triggers allow specifying such volumes either as a single shape or combination of nodes with shapes.
+A trigger is added to a glTF node by specifying the "trigger"
property inside of a node's "OMI_physics_body"
extension.
A "trigger"
may specify a "shape"
property which references a geometric shape defined by the OMI_physics_shape
extension. Alternatively, a "trigger"
may have a "nodes"
property, which is an array of glTF nodes which make up a compound trigger on this glTF node. The nodes in this array must be descendent nodes which must have "trigger"
properties. At least one of "shape"
or "nodes"
must be set to a valid value.
As the name "trigger" suggests, implementations may use these shapes as sensors that generate overlap events, which can be used to trigger things. What behavior gets triggered is outside the scope of this extension, but may be defined in other glTF extensions or application-specific logic.
++ | Type | +Description | +Default value | +
---|---|---|---|
shape | +integer |
+The index of the shape to use as the trigger shape. | +-1 | +
nodes | +integer[] |
+For compound triggers, the set of descendant glTF nodes with a trigger property that make up this compound trigger. | +[] | +
The "shape"
property is an integer index that references a shape in the document-level shapes array as defined by the OMI_physics_shape
extension. If not specified or -1, this node has no trigger shape, but may have "nodes"
defined to create a compound trigger.
The "nodes"
property is an array of integer indices that reference descendant glTF nodes with a trigger property, which make up a compound trigger on this glTF node. If not specified or empty, this node is not a compound trigger.
When this property is set and contains valid items, this indicates that this glTF node is a compound trigger. Each item is the index of a glTF node that must have its own OMI_physics_body trigger property, and must be a descendant of this node.
+See schema/node.OMI_physics_body.trigger.schema.json for the trigger properties JSON schema.
+ + + + + + + + + + + + + +Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+Depends on the OMI_physics_shape
spec to be useful.
This extension allows for specifying physics bodies in glTF scenes.
+Nodes with the OMI_physics_body
extension may define motion, collider, and trigger properties.
If a node with a collider shape does not have a motion property on itself or an ancestor node, it should be a static solid object that does not move.
+This example defines a static body node which has a single box collider as a child:
+{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensions": {
+ "OMI_physics_shape": {
+ "shapes": [
+ {
+ "type": "box",
+ "box": {
+ "size": [1, 2, 3]
+ }
+ }
+ ]
+ }
+ },
+ "extensionsUsed": [
+ "OMI_physics_body",
+ "OMI_physics_shape"
+ ],
+ "nodes": [
+ {
+ "extensions": {
+ "OMI_physics_body": {
+ "motion": {
+ "type": "dynamic"
+ },
+ "collider": {
+ "shape": 0
+ }
+ }
+ },
+ "name": "DynamicBox"
+ }
+ ],
+ "scene": 0,
+ "scenes": [{ "nodes": [0] }]
+}
+
+The above example shows dynamic motion and collision shape specified on one node. A nearly identical example using 2 nodes can be found in examples/basic/dynamic_box.gltf.
+More example assets can be found in the examples/ folder. All of these examples use both OMI_physics_shape
and OMI_physics_body
.
This extension consists of a new OMI_physics_body
data structure which can be added to a glTF node.
The extension must also be added to the glTF's extensionsUsed
array and because it is optional, it does not need to be added to the extensionsRequired
array.
The extension is intended to be used together with OMI_physics_shape
, which defines the shapes used by the "shape"
properties inside of the "collider"
and "trigger"
sub-JSON properties.
+ | Type | +Description | +Default value | +
---|---|---|---|
motion | +JSON | +If present, this node has its motion controlled by physics. | +null |
+
collider | +JSON | +If present, this node is solid and can be collided with. | +null |
+
trigger | +JSON | +If present, this node is non-solid and can act as a trigger. | +null |
+
Each of these properties are recommended to be defined on separate nodes. This results in a very clear, simple, and portable document structure, and ensures that each behavior has its own transform. However, they may also be all defined on the same node. Implementations must support all of these cases in order to be compliant.
+If a node has the "motion"
property defined, its transform is driven by the physics engine.
The list of motion properties and their details can be found in the README.motion.md file.
+If a node has the "collider"
property defined, it is a solid collider node that objects can collide with.
The list of collider properties and their details can be found in the README.collider.md file.
+If a node has the "trigger"
property defined, it is a non-solid trigger that can detect when objects enter it.
The list of trigger properties and their details can be found in the README.trigger.md file.
+See node.OMI_physics_body.schema.json for the main node schema, and these for the sub-JSON property schemas: +* Motion: node.OMI_physics_body.motion.schema.json +* Collider: node.OMI_physics_body.collider.schema.json +* Trigger: node.OMI_physics_body.trigger.schema.json
+Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+Depends on the OMI_physics_body
and OMI_physics_shape
specs for per-node gravity volumes.
This extension allows specifying gravity volumes on trigger glTF nodes and specifying the global world gravity at the document level.
+Global world gravity is always directional. A document-level extension may define the magnitude and direction of the global world gravity. Only one or zero instanced glTF files should have the document-level extension. The intention is to use the document-level extension only on the main "map" glTF. If multiple glTF files have the document-level gravity extension, use the most recently instanced glTF model's gravity (when instancing, override the previous gravity).
+For a per-node gravity volume, the OMI_physics_gravity
extension MUST be defined on an OMI_physics_body
node that is a base trigger node. Meaning, the glTF node has a trigger property, does not have motion or collider properties, and must not be a child shape of a compound trigger, as only one gravity definition is allowed per compound trigger. The shape of where the gravity influence is experienced is defined by the trigger shapes. The details of which way the gravity points can be specified in a variety of types, which are described in more detail below.
Whether or not a rigid body is influenced by a gravity volume is determined by the priority/replace/stop settings of each gravity volume the body is inside of. The priority integer determines the order in which gravity volumes are calculated, with higher values being calculated first, and the global world gravity is always calculated last. The replace boolean controls whether or not to keep gravity already calculated so far from higher priority nodes. The stop boolean controls whether or not to continue calculating gravity from any lower priority nodes and the global world gravity. If only the default settings are used, where replace and stop are both false, the gravity is combined from all gravity volumes and the global world gravity.
+This example defines a scene with a millionth-scale Earth planet in space. This is the same as earth_millionth_scale.gltf except slimmed down to only a single glTF node, the one related to gravity. It is a fully complete file that can be loaded into any app that supports this extension.
+In this example, the global world gravity is set to zero to mimic space. There is an Earth object with point gravity and a radius of 6.37814. The gravity strength is 9.80665 at the unit distance of 6.37814. The area of effect is a sphere with a radius of 40 units, which means the gravity cuts off to zero at a distance of 40 units from the center. Dynamic objects overlapping this sphere will be pulled towards the center by an amount determined by their distance and the field's gravity and unit distance, following the inverse square law.
+{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensions": {
+ "OMI_physics_gravity": {
+ "gravity": 0.0
+ },
+ "OMI_physics_shape": {
+ "shapes": [
+ {
+ "type": "sphere",
+ "radius": 40.0
+ }
+ ]
+ }
+ },
+ "extensionsUsed": [
+ "OMI_physics_body",
+ "OMI_physics_gravity",
+ "OMI_physics_shape"
+ ],
+ "nodes": [
+ {
+ "name": "EarthGravity",
+ "extensions": {
+ "OMI_physics_body": {
+ "trigger": {
+ "shape": 0
+ }
+ },
+ "OMI_physics_gravity": {
+ "gravity": 9.80665,
+ "point": {
+ "unitDistance": 6.37814
+ },
+ "type": "point"
+ }
+ }
+ }
+ ],
+ "scene": 0,
+ "scenes": [{ "nodes": [0] }]
+}
+
+More example assets can be found in the examples/ folder.
+This extension consists of two new data structures for defining gravity.
+For the root glTF document, the key "OMI_physics_gravity"
in the document-level "extensions"
defines global gravity for the whole world, including the entire glTF scene and any other instanced scenes. The global gravity is always directional.
For a glTF node, the key "OMI_physics_gravity"
in the node-level "extensions"
defines gravity for a trigger volume as defined by OMI_physics_body
. The area of effect is determined by the trigger volume's shapes. Objects in this volume may be affected by its gravity, depending on the priority, replace, and stop settings of gravity volumes affecting this part of space. The type of gravity determines which way the gravity points within the gravity field, and how strongly.
The extension must also be added to the glTF's extensionsUsed
array and because it is optional, it does not need to be added to the extensionsRequired
array.
The following properties apply to the document-level extension defining the world gravity:
++ | Type | +Description | +Default value | +Valid on | +
---|---|---|---|---|
gravity | +number |
+The gravity amount in meters per second squared. Can be zero or negative. | +Required, no default | +Always valid | +
direction | +number[3] |
+The normalized direction of the gravity. World gravity is always directional. | +[0.0, -1.0, 0.0] | +Always valid | +
Note: Only one or zero instanced glTF files should have the document-level gravity extension. The intention is to only use the document-level extension on the main "map" glTF. If multiple glTF files have the document-level gravity extension, use the most recently instanced glTF file's gravity.
+The following properties apply to the gravity of each node, and is a superset of the world gravity properties:
++ | Type | +Description | +Default value | +Valid on | +
---|---|---|---|---|
type | +string |
+The type of the gravity as a string. | +Required, no default | +Always valid | +
gravity | +number |
+The gravity amount in meters per second squared. Can be zero or negative. | +Required, no default | +Always valid | +
priority | +number |
+The process priority of this gravity node. Must be an integer. | +0 | +Always valid | +
replace | +boolean |
+If true, replace the current gravity instead of adding to it. | +false | +Always valid | +
stop | +boolean |
+If true, stop checking more nodes for gravity. | +false | +Always valid | +
In addition to the type, a key with the same name as the type can be used to define a sub-JSON with the details of the shape. Which sub-properties are allowed depend on which shape type is being used. The possible properties are described in the following table.
++ | Type | +Description | +Default value | +Valid on | +
---|---|---|---|---|
direction | +number[3] |
+The normalized direction of the gravity relative to this node's transform. | +[0.0, -1.0, 0.0] | +Directional | +
unitDistance | +number |
+The distance from the nearest point at which the experienced gravity equals the gravity property. | +0.0 | +Non-directional | +
radius | +number |
+The radius of the circle to attract gravity towards. | +1.0 | +Disc, torus | +
points | +number[] |
+The points that make up the line segments of a line type gravity field. | +[] | +Line | +
shape | +number |
+The integer ID of a physics shape used to define the gravity direction. | +-1 | +Shaped | +
The "type"
property is a string that defines the type of the gravity. This property is only valid on node gravity, for world gravity the type is always directional.
The type value may be set to "directional"
, "point"
, "disc"
, "torus"
, "line"
, or "shaped"
.
On a node, the type property must be set to one of the below values, otherwise the behavior is undefined. Further details of the gravity type are defined in a sub-JSON with the same name as the type. The possible properties in this sub-JSON are defined in the above table. If the sub-JSON is not present or is missing a property, the default value should be used.
+The type property is not to be confused with the shape of the gravity volume. The trigger body's shape nodes define the area in which the gravity will affect objects. The type property defines which way the gravity points and how it scales with distance.
+The directional and point gravity types are the easiest to implement and fastest to calculate. Preference should be given to these types when they are suitable for the desired use case.
+"directional"
gravity defines uniform parallel gravity with a given direction at the given strength. The direction is defined relative to the node's transform with the "direction"
property.
"point"
gravity defines gravity that points towards a single point, the trigger volume's origin. The "unitDistance"
property defines the distance from the center at which the actual gravity equals the "gravity"
property, unless "unitDistance"
is zero, in which case the gravity is constant regardless of distance.
The vector to the trigger body's origin determines the direction of gravity. The gravity strength is determined by that vector and the "unitDistance"
property, unless "unitDistance"
is zero, in which case the gravity is constant regardless of distance.
The point type can be used to define gravity for a spherical planet, with the unit distance property being set to the radius of the planet.
+"disc"
gravity defines gravity that points toward the nearest point in a filled circle. The "radius"
property defines the radius of the circle. The orientation of the circle is flat on the local XZ plane of the node.
The vector to the nearest point in the filled circle determines the direction of gravity. The gravity strength is determined by that vector and the "unitDistance"
property, unless "unitDistance"
is zero, in which case the gravity is constant regardless of distance.
The disc type can be conceptually thought of as two directional gravity fields on either side of the circle, connected by a smooth semicircle transition on the edge.
+"torus"
gravity defines gravity that points toward the nearest point on the edge of a hollow circle. The "radius"
property defines the radius of the circle. The orientation of the circle is flat on the local XZ plane of the node.
The vector to the nearest point on the edge of the hollow circle determines the direction of gravity. The gravity strength is determined by that vector and the "unitDistance"
property, unless "unitDistance"
is zero, in which case the gravity is constant regardless of distance.
The torus type can be used to define gravity for a toroidal planet, with the major radius of the torus being the radius property, and the minor radius of the torus being the unit distance property (or in the case of constant gravity, the minor radius does not need to be set to any parameter).
+"line"
gravity defines gravity using series of line segments defined by a series of points. The "points"
property is an array of numbers that defines these points relative to the node's transform. The size of "points"
must be a multiple of 3 numbers and must contain at least 6 numbers.
The vector to the nearest point on any of the line segments determines the direction of gravity. The gravity strength is determined by that vector and the "unitDistance"
property, unless "unitDistance"
is zero, in which case the gravity is constant regardless of distance.
The line type is a flexible type that defines gravity towards arbitrary lines. It can be used with only 2 points to define simple shapes like gravity for cylinder or capsule planets, or with many points to define a complex snake-like gravity field.
+"shaped"
gravity defines gravity towards the nearest point on a physics shape. This is not to be confused with the trigger shape that defines the area of effect. The "shape"
property references the ID of a shape as defined by OMI_physics_shape
.
The vector to the nearest point on the shape determines the direction of gravity. The gravity strength is determined by that vector and the "unitDistance"
property, unless "unitDistance"
is zero, in which case the gravity is constant regardless of distance.
The shaped type is a flexible type that can be used to create things like cube-shaped planets or giving gravity to arbitrary surfaces with minimal effort.
+The "gravity"
property is a number that defines the gravity strength in meters per second squared. Required. Can be zero or negative. If negative, the gravity points in the opposite direction of the direction vector in the case of directional gravity, or away from the nearest point of the other gravity types.
The "direction"
property is a vector as a three-number array. It is only valid for directional gravity.
It defines the direction of the gravity relative to this node's transform. Should be normalized. If not specified, the default value is a unit vector pointing downwards, [0.0, -1.0, 0.0]
.
The "unitDistance"
property is a number that defines how gravity scales with distance. It is only valid for non-directional gravity.
It defines how the gravity strength should scale based on the distance to the nearest point. If zero, the gravity is constant regardless of distance. This value should not be negative. If not specified, the default value is 0.0.
+If positive, it defines the distance from the center point in meters at which the actual gravity equals the gravity amount. The gravity should have squared falloff following the inverse square law. For example, if the unit distance is 10 meters and the physics body is 20 meters away, then the experienced gravity magnitude is one quarter of the gravity property. Similarly, if the unit distance is 10 meters and the physics body is 5 meters away, then the experienced gravity magnitude is four times the gravity property.
+The "radius"
property is a number that defines the radius of the circle to attract gravity towards. It is only valid for disc and torus gravity.
The orientation of the circle is flat on the local XZ plane of the node. If not specified, the default value is 1.0.
+The "points"
property is an array of vectors, each made of 3 numbers. It is only valid for line gravity.
The size of "points"
must be a multiple of 3 numbers and must contain at least 6 numbers. If not specified, the default value is an empty array, which is an invalid value and will have no effect.
The "shape"
property is an integer that defines the ID of a physics shape as defined by OMI_physics_shape
. It is only valid for shaped gravity.
Defines the shape which is used to determine the direction of gravity. If not specified, the default value is -1, meaning the shape is invalid and will have no effect.
+The "priority"
property is an integer that defines the process order of this gravity node. Must be an integer.
Higher priority gravity nodes are processed first. The world gravity is always processed last, after all gravity nodes. If not specified, the default value is 0.
+Asset creators are highly encouraged to use priority numbers with large gaps, such as thousands or hundreds. This allows for adding new priority values in-between later. For example, a glTF file could have gravity nodes with priorities 1000 and 2000, then another gravity node can later be inserted in-between with a priority of 1500 without changing the existing gravity nodes. This can be repeated for adding a gravity node between 1000 and 1500, etc. If there are small volumes expected to be processed first, they should have very high priority values (positive thousands), while maps and other large volumes that are expected to be processed last should have very low negative priority (negative thousands).
+The "replace"
property is a boolean. If not specified, the default value is false.
If true, the gravity of this gravity volume replaces the calculated gravity instead of adding to it. This discards all previously applied gravity of higher priority nodes.
+The "stop"
property is a boolean. If not specified, the default value is false.
If true, stop checking more gravity volume nodes for gravity. This means that lower priority gravity nodes will be ignored. This also ignores the world gravity, which is always calculated last.
+The gravity types described above can be used in a variety of ways to create a wide variety of gravity types. Here are some examples of how to use the above gravity types to define other gravity shapes.
+Example name | +Description | +
---|---|
Capsule | +Line gravity with 2 points where the line spans the mid-height and is fully inside the trigger volume. | +
Cylinder | +Line gravity with 2 points where the line touches or surpasses the ends of the trigger volume. | +
Wedge | +Line gravity with 2 points where the line is defined at the corner edge of a trigger volume. | +
Cone | +Line gravity with 2 points, plus directional gravity pointing towards the large end of the cone. | +
See schema/glTF.OMI_physics_gravity.schema.json for document-level gravity, schema/node.OMI_physics_gravity.schema.json for node-level gravity, and the schema/node.OMI_physics_gravity.*.schema.json
files for the sub-JSON schemas for gravity types.
Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+Depends on the OMI_physics_body
spec, which depends on the OMI_physics_shape
spec.
This extension allows defining a glTF node as a physics joint for constraining dynamic bodies.
+Each physics joint node is a separate node that references the two bodies it joints together. At least one of the bodies must be a dynamic body (OMI_physics_body
motion type "dynamic") for the joint to function. One or zero of the connected nodes may be a solid body that joints cannot move (OMI_physics_body
motion type "static" or "kinematic"). A joint cannot be connected to a trigger body, and cannot be connected to a non-OMI_physics_body
glTF node.
Each physics joint node must reference one or more joint constraints defined in the document-level joint constraints array.
+A joint should be on its own glTF node, it should not be on the same node as a mesh, camera, light, physics body, physics shape, etc.
+This example defines 2 dynamic bodies that are connected with a joint that constrains linearly on a fixed point, also known as a "pin" joint. This example JSON is a subset of the examples/simple_joint.gltf file.
+{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensionsUsed": [
+ "OMI_physics_body",
+ "OMI_physics_joint",
+ "OMI_physics_shape"
+ ],
+ "extensions": {
+ "OMI_physics_joint": {
+ "constraints": [
+ {
+ "linearAxes": [0, 1, 2]
+ }
+ ]
+ }
+ },
+ "nodes": [
+ {
+ "extensions": {
+ "OMI_physics_joint": {
+ "constraints": [0],
+ "nodeA": 1,
+ "nodeB": 2
+ }
+ },
+ "name": "PinJoint",
+ "translation": [-0.23, 0.6, 0.0]
+ },
+ {
+ "extensions": {
+ "OMI_physics_body": {
+ "motion": {
+ "type": "dynamic"
+ }
+ }
+ },
+ "name": "BodyA",
+ "rotation": [0.0, 0.0, -0.17364804446697, 0.984807789325714],
+ "translation": [-0.45, 0.68, 0.0]
+ },
+ {
+ "extensions": {
+ "OMI_physics_body": {
+ "motion": {
+ "type": "dynamic"
+ }
+ }
+ },
+ "name": "BodyB",
+ "translation": [0.0, 0.6, 0.0]
+ }
+ ]
+}
+
+More example assets can be found in the examples/ folder.
+This extension consists of three new data structures for defining joints. A glTF file with joints should have a document-level "OMI_physics_joint"
object added to the document "extensions"
which contains an array of joint constraints. Each joint constraint is an object defined as the below spec. Then, the key "OMI_physics_joint"
can be added to the node-level "extensions"
of a glTF node to define that node as a physics joint. Each joint node has an array of constraints that references the document-level constraints array by index, and each joint node also references 2 physics body nodes.
The extension must also be added to the glTF's extensionsUsed
array and because it is optional, it does not need to be added to the extensionsRequired
array.
+ | Type | +Description | +Default value | +
---|---|---|---|
linearAxes | +number[0..3] |
+The axes to constrain. Can only contain 0 (X), 1 (Y), or 2 (Z). | +[] (empty array) | +
angularAxes | +number[0..3] |
+The axes to constrain. Can only contain 0 (X), 1 (Y), or 2 (Z). | +[] (empty array) | +
lowerLimit | +number |
+The lower limit of the constraint, in meters or radians. | +0.0 | +
upperLimit | +number |
+The lower limit of the constraint, in meters or radians. | +0.0 | +
stiffness | +number |
+The stiffness of the limits, how hard to spring back when beyond. | +Infinity | +
damping | +number |
+When beyond the limits, the damping of the springing back motion. | +1.0 | +
A joint constraint should define at least one linear axis or one angular axis, otherwise the joint constraint will not perform any constraints.
+The reason that joints constraints are defined at the document-level and referenced on nodes is because it's very common to have many joints with the same constraints. For example, a long rope using many bodies each with pin joints (linear axes constrained) would have the same joint constraints repeated many times, but with different nodes attached.
+The "linearAxes"
property defines a list of linear axes to constrain. Can only contain 3 possible values, 0 (X), 1 (Y), or 2 (Z). If empty or not specified, this joint constraint does not constrain linearly.
The "angularAxes"
property defines a list of angular axes to constrain. Can only contain 3 possible values, 0 (X), 1 (Y), or 2 (Z). If empty or not specified, this joint constraint does not constrain angularly.
The "lowerLimit"
property defines the lower limit of the constraint in meters or radians, depending on whether the constraint is linear or angular. If not specified, the default value is 0.0.
The lower limit must be less than or equal to the upper limit, otherwise the constraint is invalid. If the lower and upper limits are equal, the constraint is fixed on that value.
+The "upperLimit"
property defines the upper limit of the constraint in meters or radians, depending on whether the constraint is linear or angular. If not specified, the default value is 0.0.
The upper limit must be greater than or equal to the lower limit, otherwise the constraint is invalid. If the lower and upper limits are equal, the constraint is fixed on that value.
+The "stiffness"
property defines how soft or hard the limits are, and how much force should be applied to the bodies to spring back once they surpasses the limits. Must be positive. If not specified, the default value is infinity.
A lower value indicates a softer limit, while a higher value indicades a harder limit. A value of infinity means the limits should be completely hard and may not be surpassed. Implementations are expected to clamp this to within the limits of the physics engine. For example, if a physics engine cannot handle an infinite stiffness, it should be set to a high value, since an extremely stiff joint is close enough to an infinitely stiff joint. For more information, see the Wikipedia article on stiffness.
+The "damping"
property defines the amount of damping the bodies should experience while the bodies surpasses the limits and are springing back to be within them. Damping must not be applied when within the limits. Must be non-negative. If not specified, the default value is 1.0.
+ | Type | +Description | +Default value | +
---|---|---|---|
constraints | +number[] |
+Array of indices in the document-level constraint array. Must be integers. | +Required, no default | +
nodeA | +number |
+Node index of one physics body used by the joint. Must be an integer. | +Required, no default | +
nodeB | +number |
+Node index of one physics body used by the joint. Must be an integer. | +Required, no default | +
The "constraints"
property defines the joint constraints to use for this joint. Must be an array of integers which are the index of the constraint in the document-level array.
If a joint node has multiple joint constraints that overlap each other, later constraints should override the previous constraints.
+Each of these properties defines one of two bodies used by the joint. Must be an integer which is the index of the glTF node, and that node must be a physics body defined using the OMI_physics_body
spec.
At least one of the connected nodes must be a physics body using dynamic rigid body physics, meaning OMI_physics_body
motion type set to "dynamic". If neither node is using dynamic rigid body physics, the joints will not work. When physics body nodes are joined, they should not collide with each other. One or zero of the nodes can be a solid body that cannot be moved by joints, meaning OMI_physics_body
motion type set to "static" or "kinematic". A connected node cannot be null, cannot be a non-body, and cannot be a trigger body.
The position on the body on which the joint operates is determined by the position of the joint relative to each body node. As such, the initial position of the joint node matters.
++ | Type | +Description | +Default value | +
---|---|---|---|
constraints | +object[] |
+Array of joint constraint objects. | +Required, no default | +
While a generic joint type is very useful and extensible, it's also worth defining when a generic joint is equivalent to a special-purpose joint. These mappings can be used to convert to more optimized joint types if supported in a physics engine, or as simply a friendly name to display to a user.
+In this chart, "fixed" means "constrained with lower and upper limits set to zero", and "free" means "not constrained".
+Special case name | +Description | +
---|---|
Fixed / Weld Joint | +All axes fixed. | +
Pin Joint | +All linear axes fixed, all angular axes free. | +
Hinge Joint | +One angular axis constrained with non-equal lower and upper limits, the rest are fixed. | +
Slider Joint | +One linear axis and optionally the angular axis around it constrained with non-equal lower and upper limits, the rest are fixed. | +
See glTF.OMI_physics_joint.joint_constraint.schema.json, node.OMI_physics_joint.schema.json, and glTF.OMI_physics_joint.schema.json for the schemas.
+Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+Does nothing on its own. Designed to be used together with the OMI_physics_body
spec.
This extension allows specifying physics shapes to be used in glTF scenes.
+All types of physics shapes defined in this extension are intended to be used for physics simulations and to be portable between many engines. Without another extension using it, this extension does not mandate any particular behavior for the shapes aside from their geometric properties. The precise usage of these physics shape primitives SHOULD be specified by the extenions which utilize them. In general, these physics shapes are used to specify geometry which can be used for collision detection.
+The OMI_physics_shape
extension is intended to be used together with the OMI_physics_body
extension, which allows attaching shapes to glTF nodes and specifying their behavior, including static, kinematic, dynamic, and non-solid triggers. The OMI_physics_body
extension refers to a shape using an index of a shape in the OMI_physics_shape
document-level shapes array.
This example defines a single box shape with a size of 1 meter in all dimensions:
+{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensions": {
+ "OMI_physics_shape": {
+ "shapes": [
+ {
+ "type": "box",
+ "box": {
+ "size": [1, 1, 1]
+ }
+ }
+ ]
+ }
+ },
+ "extensionsUsed": [
+ "OMI_physics_shape"
+ ]
+}
+
+More example assets can be found in the examples/ folder.
+This extension consists of many new data structures for defining physics shapes on the root glTF document and referencing them on a glTF node. The main data structure glTF.OMI_physics_shape.shape.schema.json
uses several smaller data structures to define a physics shape. The glTF.OMI_physics_shape.schema.json
data structure uses the key "OMI_physics_shape"
in the document-level "extensions"
which contains a list of the main physics shape data structures. The node.OMI_physics_shape.schema.json
data structure uses the key "OMI_physics_shape"
in the node-level "extensions"
which contains an index of the physics shape to use from the list document-level physics shape list.
The extension must also be added to the glTF's extensionsUsed
array and because it is optional, it does not need to be added to the extensionsRequired
array.
The main data structure glTF.OMI_physics_shape.shape.schema.json
defines a type property.
+ | Type | +Description | +Default value | +
---|---|---|---|
type | +string |
+The type of the physics shape as a string. | +Required, no default | +
In addition to the type, a key with the same name as the type can be used to define a sub-JSON with the details of the shape. Which sub-properties are allowed depend on which shape type is being used. The possible properties are described in the following table.
++ | Type | +Description | +Default value | +Valid on | +
---|---|---|---|---|
size | +number[3] |
+The size of the box shape in meters. | +[1.0, 1.0, 1.0] | +Box | +
radius | +number |
+The radius of the shape in meters. | +0.5 | +Sphere, capsule, cylinder | +
height | +number |
+The height of the shape in meters. | +2.0 | +Capsule, cylinder | +
mesh | +number |
+The index of the glTF mesh in the document to use as a mesh shape. | +-1 | +Trimesh, convex | +
If a key for the type is missing, or a sub-JSON key is missing, implementations should use the default value. A mesh index of -1 means invalid.
+The "type"
property is a lowercase string that defines what type of shape this physics shape is.
The selection of shapes was carefully chosen with a balance of compatibility between major game engines and containing the most commonly used shapes for easy asset creation. Physics shapes inherit the transform of the glTF node they are attached to. This includes rotation and translation, however it is discouraged to scale physics shape nodes since this can cause problems in some physics engines.
+Here is a table listing the mapping between the OMI_physics_shape
type and the equivalent types in major game engines.
Shape | +Unity | +Unreal | +Godot | +Blender | +Bullet (Ammo, Panda3D, etc) | +
---|---|---|---|---|---|
Box | +Box | +Box | +BoxShape3D | +Box | +Box Shape | +
Sphere | +Sphere | +Sphere | +SphereShape3D | +Sphere | +Sphere Shape | +
Capsule | +Capsule | +Capsule | +CapsuleShape3D | +Capsule | +Capsule Shape | +
Cylinder | +Approximation | +Approximation | +CylinderShape3D | +Cylinder | +Cylinder Shape | +
Convex | +Mesh (Convex) | +Convex | +ConvexPolygonShape3D | +Convex Hull | +Convex Shape | +
Trimesh | +Mesh | +Mesh | +ConcavePolygonShape3D | +Mesh | +Mesh Shape | +
Box shapes describe a cube or cuboid shape. They have size
property which is an array of 3 numbers that describes the width, height, and depth. If the size
property is omitted, the default size is [1, 1, 1]
, representing a cube with a volume of one cubic meter, edges/diameters one meter long, and extents/radius of half a meter. The position of the glTF node is the center of the box shape.
Sphere shapes describe a uniform "ball" shape. They have a radius
property which is a single number. If the radius
property is omitted, the default radius is 0.5
, representing a sphere with a radius of half a meter, a diameter of one meter. The position of the glTF node is the center of the sphere shape.
Capsule shapes describe a "pill" shape. They have a radius
and height
property. The height is aligned with the node's local vertical axis. If it's desired to align it along a different axis, rotate the glTF node. If the radius
property is omitted, the default radius is 0.5
, and if the height
property is omitted, the default height is 2.0
. The height describes the total height from bottom to top. The height of the capsule must be at least twice as much as the radius. The "mid-height" between the centers of each spherical cap end can be found with height - radius * 2.0
.
Cylinder shapes describe a "tall circle" shape. They are similar in structure to capsules, they have a radius
and height
property. The height is aligned with the node's local vertical axis. If it's desired to align it along a different axis, rotate the glTF node. If the radius
property is omitted, the default radius is 0.5
, and if the height
property is omitted, the default height is 2.0
.
The use of cylinder is discouraged if another shape would work well in its place. Cylinders are harder to calculate than boxes, spheres, and capsules. Not all game engines support cylinder shapes. Engines that do not support cylinder shapes should use an approximation, such as a convex hull roughly shaped like a cylinder. Cylinders over twice as tall as they are wide can use another approximation: a convex hull combined with an embedded capsule (to allow for smooth rolling), by copying the cylinder's values into a new capsule shape.
+Convex shapes represent a convex hull. Being "convex" means that the shape cannot have any holes or divots, and that all line segments connecting points on the surface stay within the shape. Convex hulls are defined with a mesh
property with an index of a mesh in the glTF meshes
array. The glTF mesh in the array MUST be a trimesh
to work, and should be made of only one glTF mesh primitive (one surface). Valid convex hulls must contain at least one triangle, which becomes three points on the convex hull. Convex hulls are recommended to have at least four points so that they have 3D volume.
Convex hulls can be used to represent complex convex shapes that are not easy to represent with other primitives. If a shape can be represented with a few primitives, prefer using those primitives instead of convex hulls. Convex hulls are much faster than trimesh shapes.
+When convex hulls are used, asset creators should try to limit the number of vertices in the shape for improved performance and compatibility. Some game engines limit the amount of vertices that can be in a convex hull, which may caused the actual imported shape to be simplified if the shape in the glTF file is too complex.
+Trimesh shapes represent a concave triangle mesh. They are defined with a mesh
property with an index of a mesh in the glTF meshes
array. The glTF mesh in the array MUST be a trimesh
to work, and should be made of only one glTF mesh primitive (one surface). Valid trimesh shapes must contain at least one triangle.
Avoid using a trimesh shape for most objects, they are the slowest shapes to calculate and have several limitations. Most physics engines do not support moving trimesh shapes or calculating collisions between multiple trimesh shapes. Trimesh shapes will not work reliably with trigger bodies or with pushing objects out due to not having an "interior" space, they only have a surface. Trimesh shapes are typically used for complex level geometry (for example, things that objects can go inside of). If a shape can be represented with a combination of simpler primitives, or a convex hull, or multiple convex hulls, prefer that instead.
+See glTF.OMI_physics_shape.schema.json for the document-level list of shapes, glTF.OMI_physics_shape.shape.schema.json for the shape resource schema, and the glTF.OMI_physics_shape.shape.*.schema.json
files for the individual shape types.
Open Metaverse Interoperability Group Stage 2 Proposal
+Written against the glTF 2.0 spec.
+Optionally depends on the OMI_physics_body
spec for triggers.
This extension allows defining a glTF node as a seat which a humanoid character can sit on.
+A seat is defined by specifying the back, foot, and knee positions on the seat from which can be derived the IK positions and directions for placing a humanoid character on that seat. Optionally, the angle between the upper leg and spine can also be defined. This definition supports seats placed in any orientation and supports characters of any size sitting on them.
+In addition to being defined independently, the OMI_seat
extension can be defined on an OMI_physics_body
node with the type set to trigger. When OMI_seat
is on a trigger, the trigger should be treated as an entry for the seat, such that a player activating that trigger should cause that player to enter the seat.
Alternate seated positions, such as a tiny character with straight legs on top of the seat, or a character with their legs crossed on top of the seat, are not the recommended use cases of the seat definition, but are not forbidden if an app wants a character to sit in an unusual position. The primary purpose of this spec is to define the seat itself; how characters sit on that seat is secondary, and the fine details are up to the implementation.
+This extension does not define any animations for entering or exiting the seat. It also does not define anything that a character's arms, head, tail, or chest should be doing, it only defines information about the seat itself intended to be used to place the legs on the seat.
+This example defines a seat using a simple chair facing positive Z. This is the same as the simple_chair_pos_z.gltf
example, but without the mesh data.
{
+ "asset": {
+ "version": "2.0"
+ },
+ "extensionsUsed": [
+ "OMI_seat"
+ ],
+ "nodes": [
+ {
+ "mesh": 0,
+ "name": "SimpleChairMesh"
+ },
+ {
+ "children": [
+ 0
+ ],
+ "name": "SimpleChairPosZ",
+ "extensions": {
+ "OMI_seat": {
+ "back": [0, 0.33, -0.24],
+ "foot": [0, -0.25, 0.3],
+ "knee": [0, 0.33, 0.3]
+ }
+ }
+ }
+ ],
+ "scene": 0,
+ "scenes": [{ "nodes": [1] }],
+}
+
+More example assets can be found in the examples/ folder.
+This extension consists of one new data structure for defining the seat limits, "OMI_seat"
. The key "OMI_seat"
can be added to the node-level "extensions"
of a glTF node to define a seat.
The extension must also be added to the glTF's extensionsUsed
array and because it is optional, it does not need to be added to the extensionsRequired
array.
+ | Type | +Description | +Default value | +
---|---|---|---|
back | +number[3] |
+The position limit on the seat corresponding to the character's back in local space. | +Required, no default | +
foot | +number[3] |
+The position limit on the seat corresponding to the character's feet in local space. | +Required, no default | +
knee | +number[3] |
+The base position on the seat corresponding to the character's knees in local space. | +Required, no default | +
angle | +number |
+The angle between the spine and back-knee line in radians. | +1.570796... (τ/4 or π/2 rad, 90 deg) | +
To avoid confusion with different standards of which direction is the forward direction, we define the forward direction by the direction from the back position to the knee position. The up direction is a vector perpendicular to the forward direction such that the direction from the knee to the foot points down. The back, foot, and knee positions should not be colinear or nearly colinear.
+The "back"
property is a vector as a three-number array that defines the position limit of the character's back and hips in local space.
The character's upper leg (thigh) bones are typically placed on lines parallel to the line between the knee and back points, with the lines offset upward based on the thickness of the upper leg and offset to the side based on the desired distance between each leg. If the upper leg is smaller than this line, then the knees are aligned with the knee point, and the hips are moved forward in-between the knee and back points. If the upper leg is larger than this line, then the hips are aligned with the back point, the knees are moved forward farther than the knee point. The knees may also be moved up depending on the foot-knee constraint.
+The "foot"
property is a vector as a three-number array that defines the position limit of the character's feet and lower legs in local space.
The character's lower leg (calf) bones are typically placed on lines parallel to the line between the knee and foot points, with the lines offset forward based on the thickness of the lower leg and offset to the side based on the desired distance between each leg. If the lower leg is smaller than this line, then the knees are aligned with the knee point, and the feet are moved upward in-between the knee and back points. If the lower leg is larger than this line, then the feet are aligned with the foot point, and the knee is moved upward farther than the knee point. The knees may also be moved forward depending on the back-knee constraint.
+The "knee"
property is a vector as a three-number array that defines the base position of the character's knees in local space.
The "angle"
property is a number that defines the angle between the spine and the line connecting the back and knee positions. If not specified, the default value is 1.57079632679489662
or τ/4 or π/2, which is 90 degrees represented in radians.
This is an angle instead of a position because: +* We want to support cases where the character in the seat is smaller than the seat, in which case the hips may be far from the back position in order to place the knees at the knee vector, and the angle between the hips and an upper position would change. Instead we just want to define which direction the spine points. +* We do not want to define the upper bound of a seat. A seat should support arbitrarily large characters (if an app wants to limit character size, that's an application-specific constraint beyond the scope of seats), and there is no way to IK a large character within such a constraint without breaking their back or violating other constraints, unlike with the foot/knee/back position. +* We want to make this optional for simple use cases. Defining an angle between the spine and back-knee line is unnecessary information for most office chairs, dinner table chairs, and seats without a solid back.
+This property only needs to be defined when the desired angle between the spine and back-knee line is not 90 degrees. For example, a reclining chair that has a back tilted so the character could lie back should have this defined.
+The positions defined above can be used to uniquely find other vectors used to place a character on the seat.
+These are the directions of the seat itself, the actual directions a character ends up at may be different. The specifics of this depend on the IK system used. However, one common feature is the need to offset from the seat based on the thickness of the legs in order to determine the points to use for IK, so that we avoid the character's legs being inside of the seat itself.
+In a nutshell, the legs sit as close to the seat as possible while not clipping though it, therefore we offset by the leg thickness, otherwise the leg bones would be exactly on the seat's surface. We want to keep the seat's points exactly on the seat and have the character legs offset from the seat's points so that we don't play favorites for any particular leg size.
+See schema/node.OMI_seat.schema.json for the schema.
+Draft Specification
+Open Metaverse Interoperability Group Stage 1 Proposal
+Written against the glTF 2.0 spec.
+The OMI_spawn_point extension allows you to specify a spawn point in a glTF scene, which is a place where spawnable objects can be created, such as characters. This extension can be used on game maps to mark a room or any other location as a spawn point by placing an OMI_spawn_point glTF node at the desired location. This can be useful for VR or AR experiences, where the viewer's starting position and orientation can have a significant impact on the overall experience.
+To use the "OMI_spawn_point" extension, you must first specify it in the extensionsUsed property of your glTF file.
+{
+ "extensionsUsed": ["OMI_spawn_point"]
+}
+
+Next, apply the extension to a child node of the glTF file. The node's position and rotation data can be used to determine the location of the spawn point in the scene.
+{
+ "nodes": [
+ {
+ "name": "spawn_point_node",
+ "translation": [0, 0, 1],
+ "rotation": [0, 0, 0, 1],
+ "extensions": {
+ "OMI_spawn_point": {
+ "title": "Hill East",
+ "team": "Red",
+ "group": "Hill"
+ }
+ }
+ }
+ ]
+}
+
+In the example above, the "OMI_spawn_point" extension is applied to a node named "spawn_point_node". The node's position and rotation data can be used to determine the location of the spawn point in the scene. The title, team, and group properties are optional and can be used to provide additional information about the spawn point.
+All of the properties are optional. An empty JSON object is a valid spawn point. All properties have a default value of null.
++ | Type | +Description | +
---|---|---|
title | +string |
+The title of the spawn point. This can be used as a user-facing name for the spawn point. | +
team | +string |
+The team that the spawn point is associated with. Ex: "Red", "Blue", "Humans", "Aliens", "Axis", "Allies". | +
group | +string |
+The group that the spawn point is associated with. This can be used to combine together related spawn points. | +
How each application decides to use the properties, or if they are used at all, is up to that specific implementation's needs.
+See schema/node.OMI_spawn_point.schema.json for the schema.
+Example Three.js implementation - probably not to be included in final proposal
+// Create a new Three.js scene
+const scene = new THREE.Scene();
+
+// Set up a perspective camera
+const camera = new THREE.PerspectiveCamera(
+ 75,
+ window.innerWidth / window.innerHeight,
+ 0.1,
+ 1000
+);
+
+// Load the glTF file
+const gltfLoader = new THREE.GLTFLoader();
+gltfLoader.load('my-omi-spawn-file.gltf', (gltf) => {
+ // Add the scene from the glTF file to the Three.js scene
+ scene.add(gltf.scene);
+
+ // Find the "OMI_spawn_point" node
+ let spawnPointNode = null;
+ scene.traverse((node) => {
+ if (node.isObject3D && node.userData.OMI_spawn_point) {
+ spawnPointNode = node;
+ }
+ });
+
+ // Set the position of the camera to the spawn point position from the source node data.
+ if (spawnPointNode) {
+ camera.position.copy(spawnPointNode.position);
+ }
+});
+
+Interested Implementations: +* INTERESTED AND PENDING - Third Room - https://github.com/thirdroom/thirdroom +* INTERESTED AND PENDING - Three Object Viewer (WordPress Plugin) - https://wordpress.org/plugins/three-object-viewer/ +* NOT YET PUBLIC - The Mirror https://www.themirror.space/
+ + + + + + + + + + + + + +