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 @@ + + + + + + + + + + + + + + + + + + + OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/Archived/OMI_collider/examples/box_collider.gltf b/Archived/OMI_collider/examples/box_collider.gltf new file mode 100644 index 0000000..8672a72 --- /dev/null +++ b/Archived/OMI_collider/examples/box_collider.gltf @@ -0,0 +1,40 @@ +{ + "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 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/capsule_collider.gltf b/Archived/OMI_collider/examples/capsule_collider.gltf new file mode 100644 index 0000000..2eb928f --- /dev/null +++ b/Archived/OMI_collider/examples/capsule_collider.gltf @@ -0,0 +1,37 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_collider": { + "colliders": [ + { + "height": 2.0, + "radius": 0.5, + "type": "capsule" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "nodes": [ + { + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "CapsuleCollider" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/cylinder_collider.gltf b/Archived/OMI_collider/examples/cylinder_collider.gltf new file mode 100644 index 0000000..f0e9eb2 --- /dev/null +++ b/Archived/OMI_collider/examples/cylinder_collider.gltf @@ -0,0 +1,37 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_collider": { + "colliders": [ + { + "height": 2.0, + "radius": 0.5, + "type": "cylinder" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "nodes": [ + { + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "CylinderCollider" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/hull/convex_hull.gltf b/Archived/OMI_collider/examples/hull/convex_hull.gltf new file mode 100644 index 0000000..dae833e --- /dev/null +++ b/Archived/OMI_collider/examples/hull/convex_hull.gltf @@ -0,0 +1,240 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5125, + "count": 24, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 0.999969, + 0, + 0.999969, + 1 + ], + "min": [ + -0.999969, + -0.007936, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 0.894377, + 0.447188, + 1 + ], + "min": [ + -0.894377, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5125, + "count": 24, + "max": [ + 19 + ], + "min": [ + 2 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 96, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 240, + "byteOffset": 384 + }, + { + "buffer": 0, + "byteLength": 320, + "byteOffset": 624 + }, + { + "buffer": 0, + "byteLength": 240, + "byteOffset": 944 + }, + { + "buffer": 0, + "byteLength": 160, + "byteOffset": 1184 + }, + { + "buffer": 0, + "byteLength": 96, + "byteOffset": 1344 + } + ], + "buffers": [ + { + "byteLength": 1440, + "uri": "convex_hull0.bin" + } + ], + "extensions": { + "OMI_collider": { + "colliders": [ + { + "mesh": 0, + "type": "hull" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 4, + "POSITION": 2, + "TANGENT": 3, + "TEXCOORD_0": 5 + }, + "indices": 6, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "ConvexHull" + }, + { + "extensions": {}, + "mesh": 1, + "name": "ConvexMesh" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/hull/convex_hull0.bin b/Archived/OMI_collider/examples/hull/convex_hull0.bin new file mode 100644 index 0000000..6dd4d73 Binary files /dev/null and b/Archived/OMI_collider/examples/hull/convex_hull0.bin differ diff --git a/Archived/OMI_collider/examples/hull/convex_hull_only.gltf b/Archived/OMI_collider/examples/hull/convex_hull_only.gltf new file mode 100644 index 0000000..b58e2a5 --- /dev/null +++ b/Archived/OMI_collider/examples/hull/convex_hull_only.gltf @@ -0,0 +1,104 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5125, + "count": 24, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 96, + "byteOffset": 288 + } + ], + "buffers": [ + { + "byteLength": 384, + "uri": "convex_hull_only0.bin" + } + ], + "extensions": { + "OMI_collider": { + "colliders": [ + { + "mesh": 0, + "type": "hull" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "ConvexHull" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/hull/convex_hull_only0.bin b/Archived/OMI_collider/examples/hull/convex_hull_only0.bin new file mode 100644 index 0000000..fc706fe Binary files /dev/null and b/Archived/OMI_collider/examples/hull/convex_hull_only0.bin differ diff --git a/Archived/OMI_collider/examples/sphere_collider.gltf b/Archived/OMI_collider/examples/sphere_collider.gltf new file mode 100644 index 0000000..157c4a6 --- /dev/null +++ b/Archived/OMI_collider/examples/sphere_collider.gltf @@ -0,0 +1,36 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_collider": { + "colliders": [ + { + "radius": 0.5, + "type": "sphere" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "nodes": [ + { + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "SphereCollider" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/trigger_box.gltf b/Archived/OMI_collider/examples/trigger_box.gltf new file mode 100644 index 0000000..1dbbf18 --- /dev/null +++ b/Archived/OMI_collider/examples/trigger_box.gltf @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_collider": { + "colliders": [ + { + "isTrigger": true, + "size": [ + 1, + 1, + 1 + ], + "type": "box" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "nodes": [ + { + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "TriggerBox" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/trimesh/concave_trimesh.gltf b/Archived/OMI_collider/examples/trimesh/concave_trimesh.gltf new file mode 100644 index 0000000..36b11f4 --- /dev/null +++ b/Archived/OMI_collider/examples/trimesh/concave_trimesh.gltf @@ -0,0 +1,203 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + } + ], + "buffers": [ + { + "byteLength": 1296, + "uri": "concave_trimesh0.bin" + } + ], + "extensions": { + "OMI_collider": { + "colliders": [ + { + "mesh": 0, + "type": "trimesh" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1, + 2 + ], + "name": "ConcaveTrimesh" + }, + { + "extensions": {}, + "mesh": 0, + "name": "BoxMeshInstance" + }, + { + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "ConcaveTrimeshCollider" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/trimesh/concave_trimesh0.bin b/Archived/OMI_collider/examples/trimesh/concave_trimesh0.bin new file mode 100644 index 0000000..969920a Binary files /dev/null and b/Archived/OMI_collider/examples/trimesh/concave_trimesh0.bin differ diff --git a/Archived/OMI_collider/examples/trimesh/concave_trimesh_only.gltf b/Archived/OMI_collider/examples/trimesh/concave_trimesh_only.gltf new file mode 100644 index 0000000..3b6ab2f --- /dev/null +++ b/Archived/OMI_collider/examples/trimesh/concave_trimesh_only.gltf @@ -0,0 +1,110 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 36, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 35 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 432, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 432 + } + ], + "buffers": [ + { + "byteLength": 576, + "uri": "concave_trimesh_only0.bin" + } + ], + "extensions": { + "OMI_collider": { + "colliders": [ + { + "mesh": 0, + "type": "trimesh" + } + ] + } + }, + "extensionsUsed": [ + "OMI_collider" + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "name": "ConcaveTrimeshOnly" + }, + { + "extensions": { + "OMI_collider": { + "collider": 0 + } + }, + "name": "ConcaveTrimeshCollider" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/Archived/OMI_collider/examples/trimesh/concave_trimesh_only0.bin b/Archived/OMI_collider/examples/trimesh/concave_trimesh_only0.bin new file mode 100644 index 0000000..a8baead Binary files /dev/null and b/Archived/OMI_collider/examples/trimesh/concave_trimesh_only0.bin differ diff --git a/Archived/OMI_collider/index.html b/Archived/OMI_collider/index.html new file mode 100644 index 0000000..1ed9d35 --- /dev/null +++ b/Archived/OMI_collider/index.html @@ -0,0 +1,928 @@ + + + + + + + + + + + + + + + + + + + + + OMI_collider - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_collider

+

Contributors

+
    +
  • Aaron Franke, The Mirror Megaverse Inc.
  • +
  • Robert Long, The Matrix.org Foundation
  • +
  • Mauve Signweaver, Mauve Software Inc.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 2 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Can be optionally used together with the OMI_physics_body spec.

+

Overview

+

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.

+

Example:

+

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.

+

glTF Schema Updates

+

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.

+

Property Summary

+

The rest of the document, including this summary, defines the properties for the main data structure.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault valueValid on
typestringThe type of the collider shape as a string.Required, no defaultAlways valid
isTriggerbooleanIf true, the shape is a trigger and is not solid to other objects.falseAlways valid
sizenumber[3]The size of the box shape in meters.[1.0, 1.0, 1.0]Box
radiusnumberThe radius of the shape in meters.0.5Sphere, capsule, cylinder
heightnumberThe height of the shape in meters.2.0Capsule, cylinder
meshnumberThe index of the glTF mesh in the document to use as a trimesh shape.-1Trimesh, hull
+

Collider Types

+

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.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ShapeUnityUnrealGodotBlenderBullet (Ammo, Panda3D, etc)
BoxBoxBoxBoxShape3DBoxBox Shape
SphereSphereSphereSphereShape3DSphereSphere Shape
CapsuleCapsuleCapsuleCapsuleShape3DCapsuleCapsule Shape
CylinderApproximationApproximationCylinderShape3DCylinderCylinder Shape
HullMesh (Convex)ConvexConvexPolygonShape3DConvex HullConvex Shape
TrimeshMeshMeshConcavePolygonShape3DMeshMesh Shape
+

Box

+

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

+

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

+

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

+

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

+

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

+

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.

+

Trigger

+

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).

+

JSON Schema

+

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.

+

Known Implementations

+
    +
  • Godot Engine: https://github.com/godotengine/godot/pull/69266
  • +
  • Third Room Unity Exporter: https://github.com/matrix-org/thirdroom-unity-exporter/blob/main/Runtime/Scripts/OMI_collider/OMI_ColliderExtension.cs
  • +
  • Third Room glTF Transform: https://github.com/matrix-org/thirdroom/blob/main/src/asset-pipeline/extensions/OMIColliderExtension.ts
  • +
  • Third Room glTF Loader: https://github.com/matrix-org/thirdroom/blob/main/src/engine/gltf/OMI_collider.ts
  • +
  • Three Object Viewer WordPress Plugin: https://github.com/antpb/three-object-viewer/
  • +
+

Resources:

+
    +
  • Godot Shapes: https://docs.godotengine.org/en/latest/classes/class_shape3d.html
  • +
  • Unity Colliders: https://docs.unity3d.com/Manual/CollidersOverview.html
  • +
  • Unreal Engine Collision Shapes: https://docs.unrealengine.com/4.27/en-US/API/Runtime/PhysicsCore/FCollisionShape/
  • +
  • Unreal Engine Mesh Collisions: https://docs.unrealengine.com/4.27/en-US/WorkingWithContent/Types/StaticMeshes/HowTo/SettingCollision/
  • +
  • Blender Collisions: https://docs.blender.org/manual/en/latest/physics/rigid_body/properties/collisions.html
  • +
  • Mozilla Hubs ammo-shape: https://github.com/MozillaReality/hubs-blender-exporter/blob/bb28096159e1049b6b80da00b1ae1534a6ca0855/default-config.json#L608
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/Archived/OMI_collider/schema/glTF.OMI_collider.collider.schema.json b/Archived/OMI_collider/schema/glTF.OMI_collider.collider.schema.json new file mode 100644 index 0000000..e535c2a --- /dev/null +++ b/Archived/OMI_collider/schema/glTF.OMI_collider.collider.schema.json @@ -0,0 +1,143 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_collider.collider.schema.json", + "title": "OMI_collider Collider", + "type": "object", + "description": "Parameters describing a collider physics shape.", + "allOf": [ { "$ref": "glTFChildOfRootProperty.schema.json" } ], + "properties": { + "type": { + "type": "string", + "description": "The type of collider this node represents.", + "enum": [ + "box", + "sphere", + "capsule", + "cylinder", + "hull", + "trimesh" + ] + }, + "isTrigger": { + "type": "boolean", + "description": "When true this collider will not be solid to physics bodies in the scene, and instead the shape will act as a trigger detection area.", + "default": false + }, + "name": { }, + "extensions": { }, + "extras": { } + }, + "required": [ + "type" + ], + "oneOf": [ + { + "required": [ + "type", + "size" + ], + "properties": { + "type": { + "const": "box" + }, + "size": { + "type": "array", + "description": "Three number array representing the width, height, and depth of the box (x, y, z)." + } + } + }, + { + "required": [ + "type", + "radius" + ], + "properties": { + "type": { + "const": "sphere" + }, + "radius": { + "type": "number", + "description": "The radius to use for the sphere shape." + } + } + }, + { + "required": [ + "type", + "radius", + "height" + ], + "properties": { + "type": { + "const": "capsule" + }, + "radius": { + "type": "number", + "description": "The radius to use for the capsule shape." + }, + "height": { + "type": "number", + "description": "The full height of the capsule shape." + } + } + }, + { + "required": [ + "type", + "radius", + "height" + ], + "properties": { + "type": { + "const": "cylinder" + }, + "radius": { + "type": "number", + "description": "The radius to use for the cylinder shape." + }, + "height": { + "type": "number", + "description": "The height of the cylinder shape." + } + } + }, + { + "required": [ + "type", + "mesh" + ], + "properties": { + "type": { + "const": "trimesh" + }, + "mesh": { + "description": "A reference to the mesh in the glTF meshes array to use for this concave trimesh shape. The mesh MUST be a trimesh and have at least one triangle.", + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ] + } + } + }, + { + "required": [ + "type", + "mesh" + ], + "properties": { + "type": { + "const": "hull" + }, + "mesh": { + "description": "A reference to the mesh in the glTF meshes array to use for this concave trimesh shape. The mesh MUST be a trimesh and have at least one triangle. The hull computed from this mesh should have no more than 255 points in total.", + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ] + } + } + } + ] +} diff --git a/Archived/OMI_collider/schema/glTF.OMI_collider.schema.json b/Archived/OMI_collider/schema/glTF.OMI_collider.schema.json new file mode 100644 index 0000000..e62f634 --- /dev/null +++ b/Archived/OMI_collider/schema/glTF.OMI_collider.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_collider.schema.json", + "title": "OMI_collider glTF Document Extension", + "type": "object", + "description": "Top level collider resources.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "colliders": { + "description": "An array of colliders that can be referenced by nodes.", + "type": "array", + "items": { + "type": "object", + "$ref": "glTF.OMI_collider.collider.schema.json" + }, + "minItems": 1 + }, + "extensions": {}, + "extras": {} + }, + "required": [ + "colliders" + ] +} diff --git a/Archived/OMI_collider/schema/node.OMI_collider.schema.json b/Archived/OMI_collider/schema/node.OMI_collider.schema.json new file mode 100644 index 0000000..01937df --- /dev/null +++ b/Archived/OMI_collider/schema/node.OMI_collider.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_collider.schema.json", + "title": "OMI_collider glTF Node Extension", + "type": "object", + "description": "Node extension that references a collider.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "collider": { + "allOf": [{"$ref": "glTFid.schema.json"}], + "description": "The id of the collider referenced by this node." + }, + "extensions": {}, + "extras": {} + }, + "required": [ + "collider" + ] +} diff --git a/KHR_audio_emitter/examples/boom_box/boom_box.glb b/KHR_audio_emitter/examples/boom_box/boom_box.glb new file mode 100644 index 0000000..67860b9 Binary files /dev/null and b/KHR_audio_emitter/examples/boom_box/boom_box.glb differ diff --git a/KHR_audio_emitter/examples/boom_box/boom_box.gltf b/KHR_audio_emitter/examples/boom_box/boom_box.gltf new file mode 100644 index 0000000..fc2de83 --- /dev/null +++ b/KHR_audio_emitter/examples/boom_box/boom_box.gltf @@ -0,0 +1,310 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 5546, + "max": [0.158739, 0.218841, 0.16122], + "min": [-0.158738, -0.093846, -0.16122], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 5546, + "max": [1, 0.999997, 1, 1], + "min": [-0.999999, -0.99991, -1, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 5546, + "max": [1, 1, 0.999977], + "min": [-1, -1, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 5546, + "max": [0.999893, 0.977859], + "min": [0.000656, 0.00322], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 18108, + "max": [3586], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 1512, + "max": [0.158767, 0.147275, 0.161401], + "min": [-0.158767, -0.093846, -0.160383], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5125, + "count": 1512, + "max": [1511], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 486, + "max": [0.147321, 0.194401, 0.128095], + "min": [-0.147379, 0.075066, 0.019262], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5125, + "count": 486, + "max": [485], + "min": [0], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "copyright": "2023 The Khronos Group", + "extensions": { "KHR_xmp_json_ld": { "packet": 0 } }, + "generator": "Godot Engine v4.3.dev.custom_build", + "version": "2.0" + }, + "bufferViews": [ + { "buffer": 0, "byteLength": 66552, "byteOffset": 0 }, + { "buffer": 0, "byteLength": 88736, "byteOffset": 66552 }, + { "buffer": 0, "byteLength": 66552, "byteOffset": 155288 }, + { "buffer": 0, "byteLength": 44368, "byteOffset": 221840 }, + { "buffer": 0, "byteLength": 72432, "byteOffset": 266208 }, + { "buffer": 0, "byteLength": 18144, "byteOffset": 338640 }, + { "buffer": 0, "byteLength": 6048, "byteOffset": 356784 }, + { "buffer": 0, "byteLength": 5832, "byteOffset": 362832 }, + { "buffer": 0, "byteLength": 1944, "byteOffset": 368664 } + ], + "buffers": [{ "byteLength": 370608, "uri": "boom_box0.bin" }], + "extensions": { + "KHR_audio_emitter": { + "audio": [ + { + "extensions": { "KHR_xmp_json_ld": { "packet": 0 } }, + "mimeType": "audio/mpeg", + "uri": "kevin_macleod_bassa_island_loop.mp3" + } + ], + "emitters": [ + { + "gain": 0.3, + "name": "BoomBoxAudioEmitter", + "positional": { "refDistance": 1 }, + "sources": [0], + "type": "positional" + } + ], + "sources": [ + { + "audio": 0, + "autoPlay": true, + "loop": true, + "name": "kevin_macleod_bassa_island_loop" + } + ] + }, + "KHR_xmp_json_ld": { + "packets": [ + { + "@context": { "dc": "http://purl.org/dc/elements/1.1/" }, + "@id": "", + "dc:creator": { "@list": ["The Khronos Group"] }, + "dc:description": "Showcase of the KHR_audio_emitter and OMI_physics_body extensions", + "dc:format": "model/gltf+json", + "dc:publisher": { "@set": ["The Khronos Group"] }, + "dc:rights": "CC0 1.0 Universal", + "dc:title": "Boom Box with audio and physics" + }, + { + "@context": { "dc": "http://purl.org/dc/elements/1.1/" }, + "@id": "", + "dc:creator": { "@list": ["Kevin MacLeod"] }, + "dc:date": "2011", + "dc:description": "Bassa Island Game Loop by Kevin MacLeod", + "dc:format": "audio/mpeg", + "dc:identifier": "ISRC: USUAN1100840", + "dc:publisher": { "@set": ["Incompetech"] }, + "dc:rights": "CC BY 3.0: Attribution", + "dc:source": "https://incompetech.com/music/royalty-free/mp3-royaltyfree/Bassa%20Island%20Game%20Loop.mp3", + "dc:title": "Bassa Island Game Loop" + } + ] + }, + "OMI_physics_shape": { + "shapes": [ + { + "convex": { "mesh": 1 }, + "type": "convex" + }, + { + "convex": { "mesh": 2 }, + "type": "convex" + }, + { + "capsule": { "height": 0.04, "radius": 0.007 }, + "type": "capsule" + }, + { + "capsule": { "height": 0.199, "radius": 0.005 }, + "type": "capsule" + } + ] + } + }, + "extensionsUsed": [ + "GODOT_single_root", + "KHR_audio_emitter", + "KHR_xmp_json_ld", + "OMI_physics_body", + "OMI_physics_shape" + ], + "images": [ + { + "mimeType": "image/png", + "name": "boom_box_base_color", + "uri": "textures/boom_box_base_color.png" + }, + { + "mimeType": "image/png", + "name": "boom_box_orm", + "uri": "textures/boom_box_orm.png" + }, + { + "mimeType": "image/png", + "name": "boom_box_normal", + "uri": "textures/boom_box_normal.png" + }, + { + "mimeType": "image/png", + "name": "boom_box_emission", + "uri": "textures/boom_box_emission.png" + } + ], + "materials": [ + { + "emissiveFactor": [0, 0, 0], + "emissiveTexture": { "index": 3 }, + "extensions": {}, + "name": "BoomBox_Mat", + "normalTexture": { "index": 2, "scale": 1 }, + "occlusionTexture": { "index": 1 }, + "pbrMetallicRoughness": { + "baseColorFactor": [1, 1, 1, 1], + "baseColorTexture": { "index": 0 }, + "metallicFactor": 1, + "metallicRoughnessTexture": { "index": 1 }, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 2, "POSITION": 0, "TANGENT": 1, "TEXCOORD_0": 3 }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 5 }, "indices": 6, "mode": 4 }] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 7 }, "indices": 8, "mode": 4 }] + } + ], + "nodes": [ + { + "children": [1, 2, 3, 4, 5, 6], + "extensions": { "OMI_physics_body": { "motion": { "mass": 5, "type": "dynamic" } } }, + "name": "BoomBox" + }, + { + "mesh": 0, + "name": "BoomBoxMesh", + "rotation": [0, 1, 0, 0] + }, + { + "extensions": { + "KHR_audio_emitter": { "emitter": 0 } + }, + "name": "BoomBoxAudioEmitter" + }, + { + "extensions": { + "OMI_physics_body": { "collider": { "shape": 0 } } + }, + "name": "MainColliderShape", + "rotation": [0, 1, 0, 0] + }, + { + "extensions": { + "OMI_physics_body": { "collider": { "shape": 1 } } + }, + "name": "HandleColliderShape", + "rotation": [0, 1, 0, 0] + }, + { + "extensions": { + "OMI_physics_body": { "collider": { "shape": 2 } } + }, + "name": "AntennaBaseColliderShape", + "translation": [0.145, 0.085, 0.004] + }, + { + "extensions": { + "OMI_physics_body": { "collider": { "shape": 3 } } + }, + "name": "AntennaShaftColliderShape", + "rotation": [-0.4547680914402, 0, 0, 0.890609860420227], + "translation": [0.1445, 0.159, -0.0775] + } + ], + "samplers": [{ "magFilter": 9729, "minFilter": 9987, "wrapS": 10497, "wrapT": 10497 }], + "scene": 0, + "scenes": [{ "nodes": [0] }], + "textures": [ + { "sampler": 0, "source": 0 }, + { "sampler": 0, "source": 1 }, + { "sampler": 0, "source": 2 }, + { "sampler": 0, "source": 3 } + ] +} diff --git a/KHR_audio_emitter/examples/boom_box/boom_box0.bin b/KHR_audio_emitter/examples/boom_box/boom_box0.bin new file mode 100644 index 0000000..f7ffc26 Binary files /dev/null and b/KHR_audio_emitter/examples/boom_box/boom_box0.bin differ diff --git a/KHR_audio_emitter/examples/boom_box/kevin_macleod_bassa_island_loop.mp3 b/KHR_audio_emitter/examples/boom_box/kevin_macleod_bassa_island_loop.mp3 new file mode 100644 index 0000000..563c00c Binary files /dev/null and b/KHR_audio_emitter/examples/boom_box/kevin_macleod_bassa_island_loop.mp3 differ diff --git a/KHR_audio_emitter/examples/boom_box/textures/boom_box_base_color.png b/KHR_audio_emitter/examples/boom_box/textures/boom_box_base_color.png new file mode 100644 index 0000000..20737b7 Binary files /dev/null and b/KHR_audio_emitter/examples/boom_box/textures/boom_box_base_color.png differ diff --git a/KHR_audio_emitter/examples/boom_box/textures/boom_box_emission.png b/KHR_audio_emitter/examples/boom_box/textures/boom_box_emission.png new file mode 100644 index 0000000..23371ae Binary files /dev/null and b/KHR_audio_emitter/examples/boom_box/textures/boom_box_emission.png differ diff --git a/KHR_audio_emitter/examples/boom_box/textures/boom_box_normal.png b/KHR_audio_emitter/examples/boom_box/textures/boom_box_normal.png new file mode 100644 index 0000000..7963042 Binary files /dev/null and b/KHR_audio_emitter/examples/boom_box/textures/boom_box_normal.png differ diff --git a/KHR_audio_emitter/examples/boom_box/textures/boom_box_orm.png b/KHR_audio_emitter/examples/boom_box/textures/boom_box_orm.png new file mode 100644 index 0000000..6ce1fa7 Binary files /dev/null and b/KHR_audio_emitter/examples/boom_box/textures/boom_box_orm.png differ diff --git a/KHR_audio_emitter/figures/cone-diagram.svg b/KHR_audio_emitter/figures/cone-diagram.svg new file mode 100644 index 0000000..92cc7fe --- /dev/null +++ b/KHR_audio_emitter/figures/cone-diagram.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + Listener + + + Example Values + coneInnerAngle + : 0.872665 ( 50° ) + coneOuterAngle + : 2.0944 ( 120° ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + orientation + + + forwar + + + d + + + coneInnerAngle + + + coneOuterAngle + + + gain = 1 + + + gain = coneOuterGain + + + V + + + ariable gain + + + + + + diff --git a/KHR_audio_emitter/index.html b/KHR_audio_emitter/index.html new file mode 100644 index 0000000..c219f36 --- /dev/null +++ b/KHR_audio_emitter/index.html @@ -0,0 +1,1546 @@ + + + + + + + + + + + + + + + + + + + + + + + KHR_audio_emitter - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

KHR_audio_emitter

+

Contributors

+
    +
  • Robert Long, Element Inc.
  • +
  • Anthony Burchell, Individual Contributor
  • +
  • K. S. Ernest (iFire) Lee, Individual Contributor
  • +
  • Michael Nisbet, Individual Contributor
  • +
  • humbletim, Individual Contributor
  • +
  • Norbert Nopper, UX3D @UX3DGpuSoftware
  • +
  • Aaron Franke, The Mirror Megaverse Inc.
  • +
+

Status

+

Draft

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Overview

+

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.

+

Example:

+
{
+    "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
+                }
+            }
+        }
+    ]
+}
+
+

glTF Schema Updates

+

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.

+

Example:

+
{
+    "asset": {
+        "version": "2.0"
+    },
+    "extensionsUsed": [
+        "KHR_audio_emitter"
+    ],
+    "scenes": [...],
+    "nodes": [...],
+    "extensions": {
+        "KHR_audio_emitter": {
+            "audio": [...],
+            "sources": [...],
+            "emitters": [...]
+        }
+    }
+}
+
+

Audio Data

+

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.

+

Buffer View

+

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.

+

MIME Type

+

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.

+

URI

+

The "uri" property is a string for the uri of the audio file. Relative paths are relative to the .gltf file.

+

Audio Sources

+

Audio sources reference audio data and define playback properties for it. Audio sources may be used by zero to many audio emitters.

+

Property Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
gainnumberUnitless linear multiplier against original audio file volume used for determining audio source loudness.1.0
loopbooleanWhether or not to loop the specified audio when finished.false
autoPlaybooleanWhether or not to play the specified audio when the glTF is loaded.false
audionumberThe index of the audio data assigned to this clip.Required, no default
+

Gain

+

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.

+

Loop

+

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.

+

Auto Play

+

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.

+

Audio

+

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 Emitter

+

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.

+

Property Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
typestringThe emitter type of this audio emitter: global or positional.Required, no default
gainnumberUnitless linear multiplier against audio source volume used for determining audio emitter loudness.1.0
sourcesnumber[]An array of audio source indices used by the audio emitter. This array may be empty.[]
positionalobjectA sub-JSON containing the positional audio emitter properties.{}
+

Type

+

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.
  • +
+

Gain

+

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.

+

Sources

+

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.

+

Positional

+

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.

+

Positional Audio Emitter Properties

+

When the audio emitter type is set to positional, additional properties may be defined on the positional object.

+

Property Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
shapeTypestringThe shape of the audio emitter. May be omnidirectional, cone, or a value specified by another extension."omnidirectional"
coneInnerAnglenumberThe anglular diameter of a cone inside of which there will be no angular volume reduction.6.2831853... (τ or 2π rad, 360 deg)
coneOuterAnglenumberThe 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)
coneOuterGainnumberThe linear volume gain of the audio emitter set when outside the cone defined by the coneOuterAngle property.0.0
distanceModelstringSpecifies the distance model for the audio emitter."inverse"
maxDistancenumberThe maximum distance between the emitter and listener, beyond which the audio cannot be heard.0.0
refDistancenumberA reference distance for reducing volume as the emitter moves further from the listener.1.0
rolloffFactornumberDescribes how quickly the volume is reduced as the emitter moves away from listener.1.0
+

Shape Type

+

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.

+

Cone Inner Angle

+

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).

+

Cone Outer Angle

+

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.

+

Cone Outer Gain

+

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.

+

Distance Model

+

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))
  • +
+

Max Distance

+

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.

+

Ref Distance

+

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.

+

Rolloff Factor

+

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.

+

Using Audio Emitters

+

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.

+

Audio Rolloff Formula

+

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.

+
    +
  • linear formula: 1.0 - rolloffFactor * (distance - refDistance) / (maxDistance - refDistance)
  • +
  • inverse formula: refDistance / (refDistance + rolloffFactor * (Math.max(distance, refDistance) - refDistance))
  • +
  • exponential formula: pow((Math.max(distance, refDistance) / refDistance, -rolloffFactor))
  • +
+

Audio Gain Units

+

The gain unit range is (0.0, +∞). The default is 1.0.

+
    +
  • gain formula: originalVolume * gain
  • +
+

Audio Cone Vizualized

+

Audio cone showing how cone parameters impact volume based on relative distance to the source.

+

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

+

Unit for Angles

+

Radians are used for angles matching glTF2.

+

JSON Schema

+

See glTF.KHR_audio_emitter.schema.json for the main document-level schema.

+

Known Implementations

+
    +
  • Third Room - https://github.com/thirdroom/thirdroom
  • +
  • Three Object Viewer (WordPress Plugin) - https://wordpress.org/plugins/three-object-viewer/
  • +
  • UX3D Experimental C++ implementation - https://github.com/ux3d/OMI
  • +
+

Resources

+

Prior Art:

+ + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.audio.schema.json b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.audio.schema.json new file mode 100644 index 0000000..bb4b45d --- /dev/null +++ b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.audio.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.KHR_audio_emitter.audio.schema.json", + "title": "KHR_audio_emitter Audio Data", + "type": "object", + "description": "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. `mimeType` is required when `bufferView` is used.", + "allOf": [{ "$ref": "glTFChildOfRootProperty.schema.json" }], + "properties": { + "uri": { + "type": "string", + "description": "The uri of the audio file.", + "format": "uriref", + "gltf_detailedDescription": "The uri of the audio file. Relative paths are relative to the .gltf file.", + "gltf_uriType": "audio" + }, + "mimeType": { + "type": "string", + "description": "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`.", + "anyOf": [ + { + "const": "audio/mpeg" + }, + { + "const": "audio/wav" + }, + { + "type": "string" + } + ] + }, + "bufferView": { + "allOf": [{ "$ref": "glTFid.schema.json" }], + "description": "The index of the bufferView that contains the audio data. Use this instead of the audio source's uri property." + }, + "name": {}, + "extensions": {}, + "extras": {} + }, + "dependentRequired": { + "bufferView": ["mimeType"] + }, + "oneOf": [{ "required": ["uri"] }, { "required": ["bufferView"] }] +} diff --git a/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.emitter.positional.schema.json b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.emitter.positional.schema.json new file mode 100644 index 0000000..a0f4fc9 --- /dev/null +++ b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.emitter.positional.schema.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.KHR_audio_emitter.emitter.positional.schema.json", + "title": "KHR_audio_emitter Positional Audio Emitter", + "type": "object", + "description": "Positional audio emitter properties.", + "allOf": [{ "$ref": "glTFProperty.schema.json" }], + "properties": { + "shapeType": { + "type": "string", + "description": "Shape in which emitter emits audio (omnidirectional, cone, custom).", + "anyOf": [ + { + "const": "omnidirectional", + "description": "Emitter emits audio in all directions." + }, + { + "const": "cone", + "description": "Emitter emits audio in a cone defined by coneInnerAngle, coneOuterAngle, and coneOuterGain." + }, + { + "type": "string" + } + ], + "default": "omnidirectional" + }, + "coneInnerAngle": { + "type": "number", + "description": "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 (360 degrees) is used, which means the audio emits in all directions (not in a cone).", + "minimum": 0.0, + "maximum": 6.2831853071795864, + "default": 6.2831853071795864 + }, + "coneOuterAngle": { + "type": "number", + "description": "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 (360 degrees) is used, which means some audio will emit in all directions.", + "minimum": 0.0, + "maximum": 6.2831853071795864, + "default": 6.2831853071795864 + }, + "coneOuterGain": { + "type": "number", + "description": "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.", + "minimum": 0.0, + "default": 0.0 + }, + "distanceModel": { + "type": "string", + "description": "Specifies the distance model for the audio emitter.", + "anyOf": [ + { + "const": "linear", + "description": "A linear distance model calculating the gain induced by the distance according to: 1.0 - rolloffFactor * (distance - refDistance) / (maxDistance - refDistance)" + }, + { + "const": "inverse", + "description": "An inverse distance model calculating the gain induced by the distance according to: refDistance / (refDistance + rolloffFactor * (Math.max(distance, refDistance) - refDistance))" + }, + { + "const": "exponential", + "description": "An exponential distance model calculating the gain induced by the distance according to: pow((Math.max(distance, refDistance) / refDistance, -rolloffFactor))" + } + ], + "default": "inverse" + }, + "maxDistance": { + "type": "number", + "description": "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.", + "minimum": 0.0, + "default": 0.0 + }, + "refDistance": { + "type": "number", + "description": "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`.", + "exclusiveMinimum": 0.0, + "default": 1.0 + }, + "rolloffFactor": { + "type": "number", + "description": "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. Otherwise, there is no upper limit to the rolloff factor. If not specified, the default value is `1.0`.", + "minimum": 0.0, + "default": 1.0 + }, + "extensions": {}, + "extras": {} + } +} diff --git a/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.emitter.schema.json b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.emitter.schema.json new file mode 100644 index 0000000..362ab69 --- /dev/null +++ b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.emitter.schema.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.KHR_audio_emitter.emitter.schema.json", + "title": "KHR_audio_emitter Audio Emitter", + "type": "object", + "description": "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.", + "allOf": [{ "$ref": "glTFChildOfRootProperty.schema.json" }], + "properties": { + "type": { + "type": "string", + "description": "Specifies the audio emitter type.", + "anyOf": [ + { + "const": "global", + "description": "Global audio emitters are not affected by the position of audio listeners. All `positional` properties may not be defined on global audio emitters." + }, + { + "const": "positional", + "description": "Positional audio emitters play audio at a position in the scene. The properties are defined in the `positional` object." + }, + { + "type": "string" + } + ] + }, + "gain": { + "type": "number", + "description": "Unitless linear multiplier against original source volume for determining emitter loudness.", + "minimum": 0.0, + "default": 1.0 + }, + "sources": { + "type": "array", + "description": "An array of audio source indices used by the audio emitter. This array may be empty.", + "items": { + "allOf": [ { "$ref": "glTFid.schema.json" } ] + } + }, + "positional": { + "type": "object", + "$ref": "glTF.KHR_audio_emitter.emitter.positional.schema.json" + }, + "name": {}, + "extensions": {}, + "extras": {} + }, + "required": ["type"] +} diff --git a/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.schema.json b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.schema.json new file mode 100644 index 0000000..31bba14 --- /dev/null +++ b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.KHR_audio_emitter.schema.json", + "title": "KHR_audio_emitter glTF Document Extension", + "type": "object", + "description": "Top level audio properties and resources.", + "allOf": [{ "$ref": "glTFProperty.schema.json" }], + "properties": { + "sources": { + "type": "array", + "description": "An array of audio sources to be used in audio emitters.", + "items": { + "type": "object", + "$ref": "glTF.KHR_audio_emitter.source.schema.json" + }, + "minItems": 1 + }, + "emitters": { + "type": "array", + "description": "An array of positional or global audio emitters that can be referenced by nodes or scenes.", + "items": { + "type": "object", + "$ref": "glTF.KHR_audio_emitter.emitter.schema.json" + }, + "minItems": 1 + }, + "audio": { + "type": "array", + "description": "An array of audio that can be referenced by nodes.", + "items": { + "type": "object", + "$ref": "glTF.KHR_audio_emitter.audio.schema.json" + }, + "minItems": 1 + }, + "extensions": {}, + "extras": {} + }, + "required": ["sources", "emitters", "audio"] +} diff --git a/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.source.schema.json b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.source.schema.json new file mode 100644 index 0000000..97b95bc --- /dev/null +++ b/KHR_audio_emitter/schema/glTF.KHR_audio_emitter.source.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.KHR_audio_emitter.source.schema.json", + "title": "KHR_audio_emitter Audio Source", + "type": "object", + "description": "Audio sources reference audio data and define playback properties for it. Audio sources may be used by zero to many audio emitters.", + "allOf": [{ "$ref": "glTFChildOfRootProperty.schema.json" }], + "properties": { + "autoPlay": { + "type": "boolean", + "description": "Whether or not to play the specified audio when the glTF is loaded.", + "default": false + }, + "gain": { + "type": "number", + "description": "Unitless multiplier against original audio file volume for determining audio source loudness.", + "minimum": 0.0, + "default": 1.0 + }, + "loop": { + "type": "boolean", + "description": "Whether or not to loop the specified audio when finished.", + "default": false + }, + "audio": { + "allOf": [ { "$ref": "glTFid.schema.json" } ], + "description": "The index of the audio data assigned to this clip." + }, + "name": {}, + "extensions": {}, + "extras": {} + } +} diff --git a/KHR_audio_emitter/schema/node.KHR_audio_emitter.schema.json b/KHR_audio_emitter/schema/node.KHR_audio_emitter.schema.json new file mode 100644 index 0000000..757162c --- /dev/null +++ b/KHR_audio_emitter/schema/node.KHR_audio_emitter.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.KHR_audio_emitter.schema.json", + "title": "KHR_audio_emitter glTF Node Extension", + "type": "object", + "description": "Node extension that references an audio emitter.", + "allOf": [{ "$ref": "glTFProperty.schema.json" }], + "properties": { + "emitter": { + "allOf": [ { "$ref": "glTFid.schema.json" } ], + "description": "The id of the audio emitter referenced by this node." + }, + "extensions": {}, + "extras": {} + }, + "required": ["emitter"] +} diff --git a/KHR_audio_emitter/schema/scene.KHR_audio_emitter.schema.json b/KHR_audio_emitter/schema/scene.KHR_audio_emitter.schema.json new file mode 100644 index 0000000..1148f63 --- /dev/null +++ b/KHR_audio_emitter/schema/scene.KHR_audio_emitter.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "scene.KHR_audio_emitter.schema.json", + "title": "KHR_audio_emitter glTF Scene Extension", + "type": "object", + "description": "Scene extension that references global audio emitters.", + "allOf": [{ "$ref": "glTFProperty.schema.json" }], + "properties": { + "emitters": { + "type": "array", + "description": "The indices of each global audio emitter. Positional audio emitters may not be added to the scene node.", + "items": { + "allOf": [ { "$ref": "glTFid.schema.json" } ] + }, + "uniqueItems": true, + "minItems": 1 + }, + "extensions": {}, + "extras": {} + }, + "required": ["emitters"] +} diff --git a/OMI_audio_ogg_vorbis/index.html b/OMI_audio_ogg_vorbis/index.html new file mode 100644 index 0000000..72f30a7 --- /dev/null +++ b/OMI_audio_ogg_vorbis/index.html @@ -0,0 +1,959 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_audio_ogg_vorbis - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_audio_ogg_vorbis

+

Contributors

+
    +
  • Aaron Franke, Godot Engine.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Overview

+

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.

+

glTF Schema Updates

+

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
+        }
+    ]
+}
+
+

JSON Schema

+

KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json

+

Best Practices

+

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.

+

Using Without a Fallback

+

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.

+

JSON Schema

+

See schema/glTF.KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json for the schema.

+

Known Implementations

+

None

+

Resources

+
    +
  • https://wiki.xiph.org/Vorbis
  • +
  • https://wiki.xiph.org/MIME_Types_and_File_Extensions
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_audio_ogg_vorbis/schema/glTF.KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json b/OMI_audio_ogg_vorbis/schema/glTF.KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json new file mode 100644 index 0000000..e947cd2 --- /dev/null +++ b/OMI_audio_ogg_vorbis/schema/glTF.KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.KHR_audio_emitter.source.OMI_audio_ogg_vorbis.schema.json", + "title": "OMI_audio_ogg_vorbis glTF KHR_audio_emitter Source Extension", + "type": "object", + "description": "glTF extension to specify audio data on audio sources using the Ogg Vorbis audio data format.", + "allOf": [{ "$ref": "glTFProperty.schema.json" }], + "properties": { + "audio": { + "allOf": [{ "$ref": "glTFid.schema.json" }], + "description": "The index of the item in the audio array with audio data in the Ogg Vorbis format." + }, + "extensions": {}, + "extras": {} + } +} diff --git a/OMI_audio_opus/index.html b/OMI_audio_opus/index.html new file mode 100644 index 0000000..0eb2ca6 --- /dev/null +++ b/OMI_audio_opus/index.html @@ -0,0 +1,965 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_audio_opus - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_audio_opus

+

Contributors

+
    +
  • Aaron Franke, Godot Engine.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Overview

+

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.

+

glTF Schema Updates

+

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
+        }
+    ]
+}
+
+

JSON Schema

+

KHR_audio_emitter.source.OMI_audio_opus.schema.json

+

Best Practices

+

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.

+

Using Without a Fallback

+

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.

+

JSON Schema

+

See schema/glTF.KHR_audio_emitter.source.OMI_audio_opus.schema.json for the schema.

+

Known Implementations

+

None

+

Resources

+
    +
  • https://wiki.xiph.org/OpusFAQ
  • +
  • https://datatracker.ietf.org/doc/html/rfc6716
  • +
  • https://datatracker.ietf.org/doc/html/rfc7587#section-6.1
  • +
  • https://commons.wikimedia.org/wiki/File:Opus_bitrate%2Blatency_comparison.png
  • +
  • https://www.webmproject.org/docs/container/
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_audio_opus/schema/glTF.KHR_audio_emitter.source.OMI_audio_opus.schema.json b/OMI_audio_opus/schema/glTF.KHR_audio_emitter.source.OMI_audio_opus.schema.json new file mode 100644 index 0000000..ec51825 --- /dev/null +++ b/OMI_audio_opus/schema/glTF.KHR_audio_emitter.source.OMI_audio_opus.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.KHR_audio_emitter.source.OMI_audio_opus.schema.json", + "title": "OMI_audio_opus glTF KHR_audio_emitter Source Extension", + "type": "object", + "description": "glTF extension to specify audio data on audio sources using the Opus audio codec in either the Ogg or WebM container formats.", + "allOf": [{ "$ref": "glTFProperty.schema.json" }], + "properties": { + "audio": { + "allOf": [{ "$ref": "glTFid.schema.json" }], + "description": "The index of the item in the audio array with audio data in the Opus audio codec." + }, + "extensions": {}, + "extras": {} + } +} diff --git a/OMI_link/index.html b/OMI_link/index.html new file mode 100644 index 0000000..f1018f5 --- /dev/null +++ b/OMI_link/index.html @@ -0,0 +1,938 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_link - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_link

+

Contributors

+
    +
  • OMI glTF Extensions Group
  • +
  • Robert Long, The Matrix.org Foundation
  • +
  • Anthony Burchell, Individual Contributor
  • +
+

Status

+

Draft Specification

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Optionally depends on the OMI_physics_body spec for triggers.

+

Overview

+

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.

+

Example:

+
{
+    "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": {}
+                }
+            }
+        }
+    ]
+}
+
+

glTF Schema Updates

+

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.

+

JSON Schema

+

TODO

+

Known Implementations

+

Interested Implementations: +* Third Room - https://github.com/thirdroom/thirdroom +* Three Object Viewer (WordPress Plugin) - https://wordpress.org/plugins/three-object-viewer/

+

Resources

+

TODO

+

Prior Art

+
    +
  • Third Room - https://github.com/matrix-org/thirdroom-unity-exporter/tree/main/Runtime/Scripts/MX_portal
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_personality/index.html b/OMI_personality/index.html new file mode 100644 index 0000000..61fe8d9 --- /dev/null +++ b/OMI_personality/index.html @@ -0,0 +1,950 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_personality - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_personality

+

Contributors

+

Anthony Burchell, Individual Contributor

+

Status

+

Draft Specification

+

Open Metaverse interoperability Group Stage 1 proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Overview

+

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.

+

Example

+

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.

+

Properties

+

The defaultMessage parameter is optional. The agent and personality options are required and provide context about the avatars name and default personality description.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescription
agentstringThe name of the agent or NPC.
personalitystringA default description of the personality of the agent allowing clients to inject that context into language model logic.
defaultMessagestringA default message for this agent to initialize with.
+

JSON Schema

+

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"]
+}
+
+

Implementation Details

+

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.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_body/README.collider/index.html b/OMI_physics_body/README.collider/index.html new file mode 100644 index 0000000..dfea323 --- /dev/null +++ b/OMI_physics_body/README.collider/index.html @@ -0,0 +1,811 @@ + + + + + + + + + + + + + + + + + + + + + + + Collider - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_physics_body Collider Property

+

If a node has the "collider" property defined, it is a solid collider node that objects can collide with.

+

Collider Properties

+ + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
shapeintegerThe index of the shape to use as the collision shape.-1
+

Shape

+

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).

+

JSON Schema

+

See schema/node.OMI_physics_body.collider.schema.json for the collider properties JSON schema.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_body/README.motion/index.html b/OMI_physics_body/README.motion/index.html new file mode 100644 index 0000000..576bf7c --- /dev/null +++ b/OMI_physics_body/README.motion/index.html @@ -0,0 +1,993 @@ + + + + + + + + + + + + + + + + + + + + + + + Motion - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_physics_body Motion Property

+

If a node has the "motion" property defined, its transform is driven by the physics engine.

+
    +
  • Descendant nodes should move with that node. The physics engine should treat them as part of a single body.
  • +
  • If a descendant node has its own motion property, that node should be treated as an independent body during simulation. There is no implicit requirement that it follows its "parent" rigid body.
  • +
  • If a node's transform is animated by animations in the file, those animations should take priority over the physics simulation.
  • +
+

Motion Properties

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
typestringThe type of the physics body as a string.Required, no default
massnumberThe mass of the physics body in kilograms.1.0
linearVelocitynumber[3]The initial linear velocity of the body in meters per second.[0.0, 0.0, 0.0]
angularVelocitynumber[3]The initial angular velocity of the body in radians per second.[0.0, 0.0, 0.0]
centerOfMassnumber[3]The center of mass offset from the origin in meters.[0.0, 0.0, 0.0]
inertiaDiagonalnumber[3]The inertia around principle axes in kilogram meter squared (kg⋅m²).[0.0, 0.0, 0.0]
inertiaOrientationnumber[4]The inertia orientation as a Quaternion.[0.0, 0.0, 0.0, 1.0]
+

Motion Types

+

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 TypeUnityGodot 3Godot 4Unreal
StaticColliderStaticBodyStaticBody3DWorldStatic, Simulate Physics = false
KinematicRigidbody.isKinematicKinematicBodyAnimatableBody3DWorldDynamic, Simulate Physics = false
DynamicRigidbodyRigidBodyRigidBody3DPhysicsBody, Simulate Physics = true
+

Static

+

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

+

Kinematic bodies can be collided with, and can be moved using scripts or animations. They can be used for moving platforms.

+

Dynamic

+

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.

+

Mass

+

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.

+

Linear Velocity

+

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.

+

Angular Velocity

+

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.

+

Center of Mass

+

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.

+

Inertia Diagonal

+

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.

+

Inertia Orientation

+

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.

+

JSON Schema

+

See schema/node.OMI_physics_body.motion.schema.json for the motion properties JSON schema.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_body/README.trigger/index.html b/OMI_physics_body/README.trigger/index.html new file mode 100644 index 0000000..291ecf1 --- /dev/null +++ b/OMI_physics_body/README.trigger/index.html @@ -0,0 +1,832 @@ + + + + + + + + + + + + + + + + + + + + + + + Trigger - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_physics_body Trigger Property

+

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.

+

Trigger Properties

+ + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
shapeintegerThe index of the shape to use as the trigger shape.-1
nodesinteger[]For compound triggers, the set of descendant glTF nodes with a trigger property that make up this compound trigger.[]
+

Shape

+

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.

+

Nodes

+

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.

+

JSON Schema

+

See schema/node.OMI_physics_body.trigger.schema.json for the trigger properties JSON schema.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_body/examples/basic/compound_trigger.gltf b/OMI_physics_body/examples/basic/compound_trigger.gltf new file mode 100644 index 0000000..589e503 --- /dev/null +++ b/OMI_physics_body/examples/basic/compound_trigger.gltf @@ -0,0 +1,72 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": ["OMI_physics_body", "OMI_physics_shape"], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [3, 1, 1] + } + }, + { + "type": "box", + "box": { + "size": [1, 3, 1] + } + } + ] + } + }, + "nodes": [ + { + "children": [1, 2, 3], + "extensions": { + "OMI_physics_body": { + "trigger": { + "nodes": [1, 2] + } + } + }, + "name": "CompoundTriggerL" + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 0 + } + } + }, + "name": "BottomOfL", + "translation": [1, 0, 0] + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 1 + } + } + }, + "name": "TopOfL", + "translation": [0, 2, 0] + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 0 + } + } + }, + "name": "SeparateTrigger", + "translation": [0, 0, 4] + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/basic/dynamic_box.gltf b/OMI_physics_body/examples/basic/dynamic_box.gltf new file mode 100644 index 0000000..db7e9c6 --- /dev/null +++ b/OMI_physics_body/examples/basic/dynamic_box.gltf @@ -0,0 +1,46 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 2, 3] + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "children": [1], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "DynamicBox" + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "BoxShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/basic/trigger_box.gltf b/OMI_physics_body/examples/basic/trigger_box.gltf new file mode 100644 index 0000000..104ac70 --- /dev/null +++ b/OMI_physics_body/examples/basic/trigger_box.gltf @@ -0,0 +1,35 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 1, 1] + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 0 + } + } + }, + "name": "TriggerBox" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/complex/ball_pit.gltf b/OMI_physics_body/examples/complex/ball_pit.gltf new file mode 100644 index 0000000..f3bc727 --- /dev/null +++ b/OMI_physics_body/examples/complex/ball_pit.gltf @@ -0,0 +1,1653 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 4, + 0.05, + 4 + ], + "min": [ + -4, + -0.05, + -4 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.05, + 0.5, + 4 + ], + "min": [ + -0.05, + -0.5, + -4 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.05, + 0.5, + 4 + ], + "min": [ + -0.05, + -0.5, + -4 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 15, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 4, + 0.5, + 0.05 + ], + "min": [ + -4, + -0.5, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 16, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 17, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 18, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 19, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 20, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.499434, + 0.5, + 0.499434 + ], + "min": [ + -0.499434, + -0.5, + -0.499434 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 21, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 22, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 23, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 24, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 25, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.499434, + 0.5, + 0.499434 + ], + "min": [ + -0.499434, + -0.5, + -0.499434 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 26, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 27, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 28, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 29, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 30, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.499434, + 0.5, + 0.499434 + ], + "min": [ + -0.499434, + -0.5, + -0.499434 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 31, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 32, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 33, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 34, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 35, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.499434, + 0.5, + 0.499434 + ], + "min": [ + -0.499434, + -0.5, + -0.499434 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 36, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 37, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 38, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 39, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 40, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.499434, + 0.5, + 0.499434 + ], + "min": [ + -0.499434, + -0.5, + -0.499434 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 41, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 42, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 43, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 44, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 1296 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 1584 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 1968 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 2256 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 2448 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 2592 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 2880 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 3264 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 3552 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 3744 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 3888 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 4176 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 4560 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 4848 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 5040 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 5184 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 31704 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 67064 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 93584 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 111264 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 161952 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 188472 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 223832 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 250352 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 268032 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 318720 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 345240 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 380600 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 407120 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 424800 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 475488 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 502008 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 537368 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 563888 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 581568 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 632256 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 658776 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 694136 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 720656 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 738336 + } + ], + "buffers": [ + { + "byteLength": 789024, + "uri": "ball_pit0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.0500000007450581 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [8, 0.1, 8] + } + }, + { + "type": "box", + "box": { + "size": [0.1, 1, 8] + } + }, + { + "type": "box", + "box": { + "size": [8, 1, 0.1] + } + }, + { + "type": "sphere", + "sphere": { + "radius": 0.5 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 12, + "POSITION": 10, + "TANGENT": 11, + "TEXCOORD_0": 13 + }, + "indices": 14, + "material": 2, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 17, + "POSITION": 15, + "TANGENT": 16, + "TEXCOORD_0": 18 + }, + "indices": 19, + "material": 3, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 22, + "POSITION": 20, + "TANGENT": 21, + "TEXCOORD_0": 23 + }, + "indices": 24, + "material": 4, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 27, + "POSITION": 25, + "TANGENT": 26, + "TEXCOORD_0": 28 + }, + "indices": 29, + "material": 5, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 32, + "POSITION": 30, + "TANGENT": 31, + "TEXCOORD_0": 33 + }, + "indices": 34, + "material": 6, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 37, + "POSITION": 35, + "TANGENT": 36, + "TEXCOORD_0": 38 + }, + "indices": 39, + "material": 7, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 42, + "POSITION": 40, + "TANGENT": 41, + "TEXCOORD_0": 43 + }, + "indices": 44, + "material": 8, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1, + 10, + 13, + 16, + 19, + 22, + 25 + ], + "extensions": {}, + "name": "BallPit" + }, + { + "children": [ + 2, + 4, + 6, + 8 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "Pit" + }, + { + "children": [ + 3 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "Floor" + }, + { + "extensions": {}, + "mesh": 0, + "name": "Mesh1" + }, + { + "children": [ + 5 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "WallEast", + "translation": [ + 4, + 0.5, + 0 + ] + }, + { + "extensions": {}, + "mesh": 1, + "name": "Mesh2" + }, + { + "children": [ + 7 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "WallWest", + "translation": [ + -4, + 0.5, + 0 + ] + }, + { + "extensions": {}, + "mesh": 2, + "name": "Mesh3" + }, + { + "children": [ + 9 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 2 + } + } + }, + "name": "WallNorth", + "translation": [ + 0, + 0.5, + -4 + ] + }, + { + "extensions": {}, + "mesh": 3, + "name": "Mesh4" + }, + { + "children": [ + 11 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "mass": 1, + "type": "dynamic" + } + } + }, + "name": "Ball1", + "translation": [ + 0, + 1, + 0 + ] + }, + { + "children": [ + 12 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 3 + } + } + }, + "name": "SphereShape1" + }, + { + "extensions": {}, + "mesh": 4, + "name": "BallMesh1" + }, + { + "children": [ + 14 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "mass": 1, + "type": "dynamic" + } + } + }, + "name": "Ball2", + "translation": [ + 0.5, + 2, + 0.5 + ] + }, + { + "children": [ + 15 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 3 + } + } + }, + "name": "SphereShape2" + }, + { + "extensions": {}, + "mesh": 5, + "name": "BallMesh2" + }, + { + "children": [ + 17 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "mass": 1, + "type": "dynamic" + } + } + }, + "name": "Ball3", + "translation": [ + -0.5, + 2.5, + 0 + ] + }, + { + "children": [ + 18 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 3 + } + } + }, + "name": "SphereShape3" + }, + { + "extensions": {}, + "mesh": 6, + "name": "BallMesh3" + }, + { + "children": [ + 20 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "mass": 1, + "type": "dynamic" + } + } + }, + "name": "Ball4", + "translation": [ + 0.25, + 3.5, + 0 + ] + }, + { + "children": [ + 21 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 3 + } + } + }, + "name": "SphereShape4" + }, + { + "extensions": {}, + "mesh": 7, + "name": "BallMesh4" + }, + { + "children": [ + 23 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "mass": 1, + "type": "dynamic" + } + } + }, + "name": "Ball5", + "translation": [ + -0.5, + 4, + -0.5 + ] + }, + { + "children": [ + 24 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 3 + } + } + }, + "name": "SphereShape5" + }, + { + "extensions": {}, + "mesh": 8, + "name": "BallMesh5" + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "rotation": [ + -0.25881916284561, + 0, + 0, + 0.965925812721252 + ], + "translation": [ + 0, + 3, + 6 + ] + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/complex/ball_pit0.bin b/OMI_physics_body/examples/complex/ball_pit0.bin new file mode 100644 index 0000000..371e754 Binary files /dev/null and b/OMI_physics_body/examples/complex/ball_pit0.bin differ diff --git a/OMI_physics_body/examples/complex/dynamic_with_velocity.gltf b/OMI_physics_body/examples/complex/dynamic_with_velocity.gltf new file mode 100644 index 0000000..7ccf18c --- /dev/null +++ b/OMI_physics_body/examples/complex/dynamic_with_velocity.gltf @@ -0,0 +1,50 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "sphere", + "sphere": { + "radius": 0.5 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "children": [ + 1 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "angularVelocity": [4, 5, 6], + "linearVelocity": [1, 2, 3], + "type": "dynamic" + } + } + }, + "name": "DynamicWithVelocity" + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "SphereShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/complex/indirect_children.gltf b/OMI_physics_body/examples/complex/indirect_children.gltf new file mode 100644 index 0000000..18bc05e --- /dev/null +++ b/OMI_physics_body/examples/complex/indirect_children.gltf @@ -0,0 +1,125 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 1, 1] + } + } + ] + } + }, + "extensionsUsed": ["OMI_physics_shape", "OMI_physics_body"], + "nodes": [ + { + "children": [1, 3, 5, 8, 11, 12], + "name": "IndirectColliders" + }, + { + "children": [2], + "extensions": { + "OMI_physics_body": { + "motion": { "type": "kinematic" } + } + }, + "name": "KinematicDirect", + "translation": [0, 0, 0] + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { "shape": 0 } + } + }, + "name": "BoxShapeKinematicDirect" + }, + { + "children": [4], + "extensions": { + "OMI_physics_body": { + "trigger": {} + } + }, + "name": "TriggerDirect", + "translation": [0, 0, -2] + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { "shape": 0 } + } + }, + "name": "BoxShapeTriggerDirect" + }, + { + "children": [6], + "extensions": { + "OMI_physics_body": { + "motion": { "type": "kinematic" } + } + }, + "name": "KinematicIndirect", + "translation": [-2, 0, 0] + }, + { + "children": [7], + "name": "IntermediaryNodeKinematic" + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { "shape": 0 } + } + }, + "name": "BoxShapeKinematicIndirect" + }, + { + "children": [9], + "extensions": { + "OMI_physics_body": { + "trigger": {} + } + }, + "name": "TriggerIndirect", + "translation": [-2, 0, -2] + }, + { + "children": [10], + "name": "IntermediaryNodeTrigger" + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { "shape": 0 } + } + }, + "name": "BoxShapeTriggerIndirect" + }, + { + "extensions": { + "OMI_physics_body": { + "motion": { "type": "kinematic" }, + "collider": { "shape": 0 } + } + }, + "name": "KinematicSameNode", + "translation": [2, 0, 0] + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { "shape": 0 } + } + }, + "name": "TriggerSameNode", + "translation": [2, 0, -2] + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/complex/static_body_motion.gltf b/OMI_physics_body/examples/complex/static_body_motion.gltf new file mode 100644 index 0000000..65e0d7f --- /dev/null +++ b/OMI_physics_body/examples/complex/static_body_motion.gltf @@ -0,0 +1,46 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 2, 3] + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "children": [1], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "StaticBodyMotion" + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "BoxShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/complex/static_compound_collider.gltf b/OMI_physics_body/examples/complex/static_compound_collider.gltf new file mode 100644 index 0000000..5169119 --- /dev/null +++ b/OMI_physics_body/examples/complex/static_compound_collider.gltf @@ -0,0 +1,44 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 2, 3] + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "children": [1], + "extensions": { + "OMI_physics_body": { + "collider": {} + } + }, + "name": "StaticCompoundCollider" + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "BoxShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/complex/static_with_trigger.gltf b/OMI_physics_body/examples/complex/static_with_trigger.gltf new file mode 100644 index 0000000..cebc6e7 --- /dev/null +++ b/OMI_physics_body/examples/complex/static_with_trigger.gltf @@ -0,0 +1,62 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 1, 1] + } + }, + { + "type": "sphere", + "sphere": { + "radius": 2 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "children": [1, 2], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "StaticBox" + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "StaticShape" + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 1 + } + } + }, + "name": "TriggerShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/complex/two_boxes.gltf b/OMI_physics_body/examples/complex/two_boxes.gltf new file mode 100644 index 0000000..e696db7 --- /dev/null +++ b/OMI_physics_body/examples/complex/two_boxes.gltf @@ -0,0 +1,72 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 1, 1] + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_shape", + "OMI_physics_body" + ], + "nodes": [ + { + "children": [1, 3], + "extensions": {}, + "name": "TwoBoxes" + }, + { + "children": [2], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "StaticBody", + "translation": [-1, 0, 0] + }, + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "StaticBoxShape" + }, + { + "children": [4], + "extensions": { + "OMI_physics_body": { + "trigger": {} + } + }, + "name": "TriggerBody", + "translation": [1, 0, 0] + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 0 + } + } + }, + "name": "TriggerBoxShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_body/examples/triggers/sloped_floor_color.png b/OMI_physics_body/examples/triggers/sloped_floor_color.png new file mode 100644 index 0000000..3c3107a Binary files /dev/null and b/OMI_physics_body/examples/triggers/sloped_floor_color.png differ diff --git a/OMI_physics_body/examples/triggers/triggers.bin b/OMI_physics_body/examples/triggers/triggers.bin new file mode 100644 index 0000000..de92c26 Binary files /dev/null and b/OMI_physics_body/examples/triggers/triggers.bin differ diff --git a/OMI_physics_body/examples/triggers/triggers.gltf b/OMI_physics_body/examples/triggers/triggers.gltf new file mode 100644 index 0000000..6315195 --- /dev/null +++ b/OMI_physics_body/examples/triggers/triggers.gltf @@ -0,0 +1,535 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v4.0.44", + "version": "2.0" + }, + "extensionsUsed": ["KHR_lights_punctual", "OMI_physics_shape", "OMI_physics_body"], + "extensionsRequired": ["KHR_lights_punctual"], + "extensions": { + "KHR_lights_punctual": { + "lights": [ + { + "color": [1, 1, 1], + "intensity": 54.35141306588226, + "type": "point", + "name": "Light" + } + ] + }, + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [10.376665115356445, 0.3404197692871094, 10.376665115356445] + } + }, + { + "type": "convex", + "convex": { + "mesh": 1 + } + }, + { + "type": "convex", + "convex": { + "mesh": 2 + } + }, + { + "type": "convex", + "convex": { + "mesh": 3 + } + }, + { + "type": "convex", + "convex": { + "mesh": 4 + } + } + ] + } + }, + "scene": 0, + "scenes": [ + { + "name": "Scene", + "nodes": [0, 1, 2, 4, 12] + } + ], + "nodes": [ + { + "extensions": { + "KHR_lights_punctual": { + "light": 0 + } + }, + "name": "Light", + "rotation": [-0.28416627645492554, 0.7269423007965088, 0.34203392267227173, 0.5232754945755005], + "translation": [4.076245307922363, 5.903861999511719, -1.0054539442062378] + }, + { + "camera": 0, + "name": "Camera", + "rotation": [-0.14519648253917694, 0, 0, 0.9894028902053833], + "translation": [-0.021041998639702797, 2.5271661281585693, 7.237770080566406] + }, + { + "children": [3], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "Floor", + "scale": [0.16844403743743896, 0.16844403743743896, 0.16844403743743896] + }, + { + "mesh": 0, + "name": "FloorMesh" + }, + { + "children": [5], + "extensions": { + "OMI_physics_body": { + "motion": { + "mass": 1 + }, + "collider": { + "shape": 1 + } + } + }, + "name": "Cube", + "translation": [-0.0003802180290222168, 1.8371120691299438, 0.00196520215831697] + }, + { + "mesh": 1, + "name": "CubeMesh" + }, + { + "children": [7], + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 2 + } + } + }, + "name": "ChildA", + "rotation": [0, 0, -0.7071068286895752, 0.7071068286895752], + "translation": [-0.2780584394931793, 0.6075316667556763, 0.28256291151046753] + }, + { + "mesh": 2, + "name": "ChildAMesh" + }, + { + "children": [9], + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 3 + } + } + }, + "name": "ChildB", + "rotation": [-0.5000000596046448, 0.5, -0.5, 0.5], + "translation": [-0.2780584394931793, 0.6075316667556763, 0.28256291151046753] + }, + { + "mesh": 3, + "name": "ChildBMesh" + }, + { + "children": [11], + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 4 + } + } + }, + "name": "Standalone", + "rotation": [-0.5000000596046448, 0.5, -0.5, 0.5], + "translation": [0.2828197777271271, 0.6075316667556763, -0.2794848382472992] + }, + { + "mesh": 4, + "name": "StandaloneMesh" + }, + { + "extensions": { + "OMI_physics_body": { + "trigger": { + "nodes": [6, 8] + } + } + }, + "children": [6, 8, 10], + "name": "Triggers" + } + ], + "cameras": [ + { + "name": "Camera", + "perspective": { + "aspectRatio": 1.7777777777777777, + "yfov": 0.39959652046304894, + "zfar": 100, + "znear": 0.10000000149011612 + }, + "type": "perspective" + } + ], + "materials": [ + { + "doubleSided": true, + "name": "FloorBaked", + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "Yellowish", + "pbrMetallicRoughness": { + "baseColorFactor": [0.8000000715255737, 0.7555356621742249, 0.0002703802892938256, 1], + "metallicFactor": 0.5, + "roughnessFactor": 0.800000011920929 + } + }, + { + "alphaMode": "BLEND", + "doubleSided": true, + "name": "TransparentGreen", + "pbrMetallicRoughness": { + "baseColorFactor": [0.11157119274139404, 0.8000000715255737, 0, 0.32684826850891113], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + } + ], + "meshes": [ + { + "name": "Cube.001", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "NORMAL": 1, + "TEXCOORD_0": 2 + }, + "indices": 3, + "material": 0 + } + ] + }, + { + "name": "Cube", + "primitives": [ + { + "attributes": { + "POSITION": 4, + "NORMAL": 5, + "TEXCOORD_0": 6 + }, + "indices": 7, + "material": 1 + } + ] + }, + { + "name": "Cube.002", + "primitives": [ + { + "attributes": { + "POSITION": 8, + "NORMAL": 9, + "TEXCOORD_0": 10 + }, + "indices": 7, + "material": 2 + } + ] + }, + { + "name": "Cube.003", + "primitives": [ + { + "attributes": { + "POSITION": 11, + "NORMAL": 12, + "TEXCOORD_0": 13 + }, + "indices": 7, + "material": 2 + } + ] + }, + { + "name": "Cube.004", + "primitives": [ + { + "attributes": { + "POSITION": 14, + "NORMAL": 15, + "TEXCOORD_0": 16 + }, + "indices": 7, + "material": 2 + } + ] + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "mimeType": "image/png", + "name": "sloped_floor_color", + "uri": "sloped_floor_color.png" + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 24, + "max": [5.188332557678223, 0.1702098846435547, 5.188332557678223], + "min": [-5.188332557678223, -0.1702098846435547, -5.188332557678223], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 24, + "type": "VEC2" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 4, + "componentType": 5126, + "count": 24, + "max": [0.10373210906982422, 0.10373210906982422, 0.10373210906982422], + "min": [-0.10373210906982422, -0.10373210906982422, -0.10373210906982422], + "type": "VEC3" + }, + { + "bufferView": 5, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 6, + "componentType": 5126, + "count": 24, + "type": "VEC2" + }, + { + "bufferView": 7, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 8, + "componentType": 5126, + "count": 24, + "max": [0.277789443731308, 0.8446961641311646, 0.277789443731308], + "min": [-0.277789443731308, -0.277789443731308, -0.277789443731308], + "type": "VEC3" + }, + { + "bufferView": 9, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 10, + "componentType": 5126, + "count": 24, + "type": "VEC2" + }, + { + "bufferView": 11, + "componentType": 5126, + "count": 24, + "max": [0.277789443731308, 0.8446961641311646, 0.277789443731308], + "min": [-0.277789443731308, -0.277789443731308, -0.277789443731308], + "type": "VEC3" + }, + { + "bufferView": 12, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 13, + "componentType": 5126, + "count": 24, + "type": "VEC2" + }, + { + "bufferView": 14, + "componentType": 5126, + "count": 24, + "max": [0.277789443731308, 0.276752769947052, 0.277789443731308], + "min": [-0.277789443731308, -0.277789443731308, -0.277789443731308], + "type": "VEC3" + }, + { + "bufferView": 15, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 16, + "componentType": 5126, + "count": 24, + "type": "VEC2" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 288, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 576, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 72, + "byteOffset": 768, + "target": 34963 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 840, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 1128, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 1416, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 72, + "byteOffset": 1608, + "target": 34963 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 1680, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 1968, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 2256, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 2448, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 2736, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 3024, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 3216, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 3504, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 3792, + "target": 34962 + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987 + } + ], + "buffers": [ + { + "byteLength": 3984, + "uri": "triggers.bin" + } + ] +} diff --git a/OMI_physics_body/index.html b/OMI_physics_body/index.html new file mode 100644 index 0000000..158cce4 --- /dev/null +++ b/OMI_physics_body/index.html @@ -0,0 +1,835 @@ + + + + + + + + + + + + + + + + + + + + + + + Overview - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_physics_body

+

Contributors

+
    +
  • Aaron Franke, The Mirror Megaverse Inc.
  • +
  • Mauve Signweaver, Mauve Software Inc.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Depends on the OMI_physics_shape spec to be useful.

+

Overview

+

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.

+

Example:

+

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.

+

glTF Schema Updates

+

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.

+

Property Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
motionJSONIf present, this node has its motion controlled by physics.null
colliderJSONIf present, this node is solid and can be collided with.null
triggerJSONIf 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.

+

Motion

+

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.

+

Collider

+

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.

+

Trigger

+

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.

+

JSON Schema

+

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

+

Known Implementations

+
    +
  • Godot Engine: https://github.com/godotengine/godot/pull/78967
  • +
+

Resources:

+
    +
  • Unity colliders: https://docs.unity3d.com/Manual/CollidersOverview.html
  • +
  • Unreal Engine Physics: https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Physics/Collision/Overview/
  • +
  • Godot Physics Body: https://docs.godotengine.org/en/stable/classes/class_physicsbody.html
  • +
  • Godot Area: https://docs.godotengine.org/en/stable/classes/class_area.html
  • +
  • Godot RigidBody3D: https://docs.godotengine.org/en/latest/classes/class_rigidbody3d.html
  • +
  • Wikipedia Moment of Inertia: https://en.wikipedia.org/wiki/Moment_of_inertia
  • +
  • Wikipedia Rigid Body Dynamics: https://en.wikipedia.org/wiki/Rigid_body_dynamics
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_body/schema/node.OMI_physics_body.collider.schema.json b/OMI_physics_body/schema/node.OMI_physics_body.collider.schema.json new file mode 100644 index 0000000..6bde046 --- /dev/null +++ b/OMI_physics_body/schema/node.OMI_physics_body.collider.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_body.collider.schema.json", + "title": "OMI_physics_body Collider Property", + "type": "object", + "description": "Parameters describing the geometric shape used for collisions.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "shape": { + "allOf": [ { "$ref": "glTFid.schema.json" } ], + "description": "The id of the shape referenced by this node.", + "default": -1 + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_body/schema/node.OMI_physics_body.motion.schema.json b/OMI_physics_body/schema/node.OMI_physics_body.motion.schema.json new file mode 100644 index 0000000..2d2841b --- /dev/null +++ b/OMI_physics_body/schema/node.OMI_physics_body.motion.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_body.motion.schema.json", + "title": "OMI_physics_body Motion Property", + "type": "object", + "description": "Parameter describing the motion of a physics body glTF node.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "type": { + "type": "string", + "description": "The motion type of this physics body as a string.", + "enum": [ + "static", + "kinematic", + "dynamic" + ] + }, + "mass": { + "type": "number", + "description": "The mass of this physics body in kilograms.", + "default": 1.0 + }, + "linearVelocity": { + "type": "array", + "description": "The initial linear velocity of the body in meters per second.", + "default": [0.0, 0.0, 0.0] + }, + "angularVelocity": { + "type": "array", + "description": "The initial angular velocity of the body in radians per second.", + "default": [0.0, 0.0, 0.0] + }, + "inertiaDiagonal": { + "type": "array", + "description": "The inertia around principle axes in kilogram meter squared. If zero or not specified, the inertia should be calculated automatically.", + "default": [0.0, 0.0, 0.0] + }, + "inertiaOrientation": { + "type": "array", + "description": "The inertia orientation as a Quaternion. If not specified, the inertia is not rotated.", + "default": [0.0, 0.0, 0.0, 1.0] + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "type" + ] +} diff --git a/OMI_physics_body/schema/node.OMI_physics_body.schema.json b/OMI_physics_body/schema/node.OMI_physics_body.schema.json new file mode 100644 index 0000000..8dfd735 --- /dev/null +++ b/OMI_physics_body/schema/node.OMI_physics_body.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_body.schema.json", + "title": "OMI_physics_body glTF Node Extension", + "type": "object", + "description": "Defines physics information for a node.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "motion": { + "type": "object", + "description": "If present, this node has its motion controlled by physics, specified by these motion parameters.", + "$ref": "glTF.OMI_physics_body.motion.schema.json" + }, + "collider": { + "type": "object", + "description": "If present, this node is solid and can be collided with, specified by these collider parameters.", + "$ref": "glTF.OMI_physics_body.collider.schema.json" + }, + "trigger": { + "type": "object", + "description": "If present, this node is non-solid and can act as a trigger, specified by these trigger parameters.", + "$ref": "glTF.OMI_physics_body.trigger.schema.json" + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_body/schema/node.OMI_physics_body.trigger.schema.json b/OMI_physics_body/schema/node.OMI_physics_body.trigger.schema.json new file mode 100644 index 0000000..5ca2e8e --- /dev/null +++ b/OMI_physics_body/schema/node.OMI_physics_body.trigger.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_body.trigger.schema.json", + "title": "OMI_physics_body Trigger Property", + "type": "object", + "description": "Parameters describing the geometric shape used for trigger volumes.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "shape": { + "description": "The id of the shape referenced by this node.", + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ], + "default": -1 + }, + "nodes": { + "type": "array", + "description": "For compound triggers, the set of descendant glTF nodes with a trigger property that make up this compound trigger.", + "items": { + "$ref": "glTFid.schema.json" + }, + "uniqueItems": true, + "minItems": 1 + }, + "extensions": { }, + "extras": { } + }, + "anyOf": [ + { + "allOf": [ + { "required": ["shape"] }, + { "not": { "required": ["nodes"] } } + ] + }, + { + "allOf": [ + { "required": ["nodes"] }, + { "not": { "required": ["shape"] } }, + { "not": { "required": ["collisionFilter"] } } + ] + } + ] +} diff --git a/OMI_physics_gravity/examples/earth_millionth_scale/earth_color_4096.jpg b/OMI_physics_gravity/examples/earth_millionth_scale/earth_color_4096.jpg new file mode 100644 index 0000000..04320f3 Binary files /dev/null and b/OMI_physics_gravity/examples/earth_millionth_scale/earth_color_4096.jpg differ diff --git a/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.bin b/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.bin new file mode 100644 index 0000000..c8b0d80 Binary files /dev/null and b/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.bin differ diff --git a/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.glb b/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.glb new file mode 100644 index 0000000..e8f1b9c Binary files /dev/null and b/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.glb differ diff --git a/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.gltf b/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.gltf new file mode 100644 index 0000000..ccb690f --- /dev/null +++ b/OMI_physics_gravity/examples/earth_millionth_scale/earth_millionth_scale.gltf @@ -0,0 +1,216 @@ +{ + "asset": { + "extensions": { "KHR_xmp_json_ld": { "packet": 0 } }, + "generator": "Godot Engine v4.3.dev.custom_build", + "version": "2.0" + }, + "extensionsUsed": [ + "GODOT_single_root", + "KHR_xmp_json_ld", + "OMI_physics_body", + "OMI_physics_gravity", + "OMI_physics_shape" + ], + "extensions": { + "KHR_xmp_json_ld": { + "packets": [ + { + "@context": { "dc": "http://purl.org/dc/elements/1.1/" }, + "@id": "", + "dc:coverage": "Earth", + "dc:creator": { "@list": ["NASA", "aaronfranke"] }, + "dc:description": "This spectacular 'blue marble' image is the most detailed true-color image of the entire Earth to date. Using a collection of satellite-based observations, scientists and visualizers stitched together months of observations of the land surface, oceans, sea ice, and clouds into a seamless, true-color mosaic. This model is at a one-millionth scale.", + "dc:format": "model/gltf+json", + "dc:rights": "NASA Image Use Policy https://visibleearth.nasa.gov/image-use-policy", + "dc:source": "https://visibleearth.nasa.gov/images/57752/blue-marble-land-surface-shallow-water-and-shaded-topography/57754l", + "dc:subject": { "@set": ["Earth"] }, + "dc:title": "Blue Marble: Land Surface, Shallow Water, and Shaded Topography", + "dc:type": { "@set": ["Planet", "Demo", "Test"] } + } + ] + }, + "OMI_physics_gravity": { + "gravity": 0.0 + }, + "OMI_physics_shape": { + "shapes": [ + { + "type": "sphere", + "sphere": { + "radius": 40.0 + } + }, + { + "type": "sphere", + "sphere": { + "radius": 6.37814 + } + } + ] + } + }, + "nodes": [ + { + "name": "EarthMillionthScale", + "children": [1, 2, 3], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static", + "mass": 5972200.0 + } + } + } + }, + { + "name": "EarthCollider", + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + } + }, + { + "name": "EarthMesh", + "mesh": 0 + }, + { + "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] }], + "materials": [ + { + "doubleSided": true, + "name": "EarthMaterial", + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0, + "metallicRoughnessTexture": { + "index": 1 + } + } + } + ], + "meshes": [ + { + "name": "EarthMeshData", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "NORMAL": 1, + "TEXCOORD_0": 2 + }, + "indices": 3, + "material": 0 + } + ] + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + }, + { + "sampler": 0, + "source": 1 + } + ], + "images": [ + { + "mimeType": "image/jpeg", + "name": "earth_color_4096", + "uri": "earth_color_4096.jpg" + }, + { + "mimeType": "image/png", + "name": "earth_roughness", + "uri": "earth_roughness.png" + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 2447, + "max": [6.378139495849609, 6.378159999847412, 6.378159999847412], + "min": [-6.378159999847412, -6.378159999847412, -6.378159999847412], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 2447, + "type": "VEC3" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 2447, + "type": "VEC2" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 14400, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 29364, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 29364, + "byteOffset": 29364, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 19576, + "byteOffset": 58728, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 28800, + "byteOffset": 78304, + "target": 34963 + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987 + } + ], + "buffers": [ + { + "byteLength": 107104, + "uri": "earth_millionth_scale.bin" + } + ] +} diff --git a/OMI_physics_gravity/examples/earth_millionth_scale/earth_roughness.png b/OMI_physics_gravity/examples/earth_millionth_scale/earth_roughness.png new file mode 100644 index 0000000..13a76dc Binary files /dev/null and b/OMI_physics_gravity/examples/earth_millionth_scale/earth_roughness.png differ diff --git a/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater.glb b/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater.glb new file mode 100644 index 0000000..cdb14ad Binary files /dev/null and b/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater.glb differ diff --git a/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater.gltf b/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater.gltf new file mode 100644 index 0000000..9fd281c --- /dev/null +++ b/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater.gltf @@ -0,0 +1,1369 @@ +{ + "asset": { + "extensions": { "KHR_xmp_json_ld": { "packet": 0 } }, + "generator": "Godot Engine v4.3.dev.custom_build", + "version": "2.0" + }, + "extensionsUsed": ["KHR_xmp_json_ld", "OMI_physics_body", "OMI_physics_gravity", "OMI_physics_shape"], + "extensions": { + "KHR_xmp_json_ld": { + "packets": [ + { + "@context": { "dc": "http://purl.org/dc/elements/1.1/" }, + "@id": "", + "dc:coverage": "Petavius Crater, Earth's Moon", + "dc:creator": { "@list": ["SebastianSosnowski", "aaronfranke"] }, + "dc:description": "Moon Petavius Crater with OMI_physics_gravity playground as a test file for OMI_physics_gravity. Petavius is a large lunar impact crater located to the southeast of the Mare Fecunditatis, near the southeastern lunar limb. This model is at a ten-thousandth scale.", + "dc:format": "model/gltf+json", + "dc:rights": "CC-BY 4.0 Attribution", + "dc:source": "https://sketchfab.com/3d-models/moon-petavius-crater-ce9c009b517b421eab8c8429b536382f", + "dc:subject": { "@set": ["Space", "Moon", "Gravity"] }, + "dc:title": "Moon Petavius Crater with gravity", + "dc:type": { "@set": ["Map", "Demo", "Test"] } + } + ] + }, + "OMI_physics_gravity": { "gravity": 1.62 }, + "OMI_physics_shape": { + "shapes": [ + { "box": { "size": [3, 3, 3] }, "type": "box" }, + { "trimesh": { "mesh": 0 }, "type": "trimesh" }, + { "box": { "size": [2, 6, 2] }, "type": "box" }, + { "box": { "size": [2, 2, 2] }, "type": "box" }, + { "box": { "size": [16, 10, 10] }, "type": "box" }, + { "convex": { "mesh": 7 }, "type": "convex" }, + { "box": { "size": [40, 32, 44] }, "type": "box" }, + { "trimesh": { "mesh": 9 }, "type": "trimesh" }, + { "box": { "size": [20, 16, 16] }, "type": "box" }, + { "box": { "size": [20, 8, 10] }, "type": "box" }, + { "trimesh": { "mesh": 11 }, "type": "trimesh" }, + { "box": { "size": [8, 16, 8] }, "type": "box" }, + { "convex": { "mesh": 13 }, "type": "convex" }, + { "sphere": { "radius": 6 }, "type": "sphere" }, + { "sphere": { "radius": 2.5 }, "type": "sphere" }, + { "box": { "size": [10, 16, 16] }, "type": "box" }, + { "box": { "size": [10, 9, 16] }, "type": "box" }, + { "convex": { "mesh": 16 }, "type": "convex" } + ] + } + }, + "images": [ + { "uri": "textures/moon_petavius_crater_material_albedo000.jpg" }, + { "uri": "textures/moon_petavius_crater_material_orm000.jpg" }, + { "uri": "textures/moon_petavius_crater_material_normal000.jpg" } + ], + "materials": [ + { + "name": "MoonPetaviusCraterMaterial", + "normalTexture": { "index": 2, "scale": 1 }, + "occlusionTexture": { "index": 1 }, + "pbrMetallicRoughness": { + "baseColorFactor": [0.99999988079071, 0.99999988079071, 0.99999988079071, 1], + "baseColorTexture": { "index": 0 }, + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "doubleSided": true, + "name": "ZeroGravitySetZeroPedestalMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0, 0.600000500679016, 0.800000548362732, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "ZeroGravityAddUpMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0, 0, 0.800000548362732, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "SidewaysGravityLocalDownMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0.800000548362732, 0, 0, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "SidewaysGravityLocalWestMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0.800000548362732, 0.800000548362732, 0, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "DiscGravityMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0, 0.800000548362732, 0, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "TorusGravityMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0.800000548362732, 0.199999749660492, 0, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "LineGravityMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0.800000548362732, 0, 0.480000972747803, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [0.214041143655777, 0.522521495819092, 0.99999988079071, 1], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "doubleSided": true, + "name": "PointGravityMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0.39999982714653, 0, 0.800000548362732, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "ShapedGravityCubeMaterial", + "pbrMetallicRoughness": { + "baseColorFactor": [0, 0.800000548362732, 0.299999475479126, 1], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + } + ], + "meshes": [ + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 0 }, "indices": 1, "mode": 4 }] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 4, "POSITION": 2, "TANGENT": 3, "TEXCOORD_0": 5 }, + "indices": 6, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 9, "POSITION": 7, "TANGENT": 8, "TEXCOORD_0": 10 }, + "indices": 11, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 14, "POSITION": 12, "TANGENT": 13, "TEXCOORD_0": 15 }, + "indices": 16, + "material": 2, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 19, "POSITION": 17, "TANGENT": 18, "TEXCOORD_0": 20 }, + "indices": 21, + "material": 3, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 24, "POSITION": 22, "TANGENT": 23, "TEXCOORD_0": 25 }, + "indices": 26, + "material": 4, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 29, "POSITION": 27, "TANGENT": 28, "TEXCOORD_0": 30 }, + "indices": 31, + "material": 5, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 32 }, "indices": 33, "mode": 4 }] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 36, "POSITION": 34, "TANGENT": 35, "TEXCOORD_0": 37 }, + "indices": 38, + "material": 6, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 39 }, "indices": 40, "mode": 4 }] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 43, "POSITION": 41, "TANGENT": 42, "TEXCOORD_0": 44 }, + "indices": 45, + "material": 7, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 46 }, "indices": 47, "mode": 4 }] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 50, "POSITION": 48, "TANGENT": 49, "TEXCOORD_0": 51 }, + "indices": 52, + "material": 8, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 53 }, "indices": 54, "mode": 4 }] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 57, "POSITION": 55, "TANGENT": 56, "TEXCOORD_0": 58 }, + "indices": 59, + "material": 9, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 62, "POSITION": 60, "TANGENT": 61, "TEXCOORD_0": 63 }, + "indices": 64, + "material": 10, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 65 }, "indices": 66, "mode": 4 }] + } + ], + "nodes": [ + { + "children": [1, 2], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "MoonPetaviusCrater" + }, + { "extensions": { "OMI_physics_body": { "collider": { "shape": 1 } } }, "name": "MoonPetaviusCraterShape" }, + { "mesh": 1, "name": "MoonPetaviusCraterMesh" }, + { + "children": [4, 5, 6], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [4] } }, + "OMI_physics_gravity": { + "gravity": 0, + "replace": true, + "stop": true, + "type": "directional" + } + }, + "name": "ZeroGravityReplaceZero", + "translation": [10, 0, 3] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 2 } } }, + "name": "ZeroGravityReplaceZeroShape", + "translation": [0, 2, 0] + }, + { "mesh": 2, "name": "ZeroGravityReplaceZeroPedestalMesh" }, + { + "children": [7], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "ZeroGravityReplaceZeroPedestalCollider" + }, + { + "extensions": { "OMI_physics_body": { "collider": { "shape": 3 } } }, + "name": "ZeroGravityReplaceZeroPedestalShape", + "translation": [0, -1, 0] + }, + { + "children": [9, 10, 11], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [9] } }, + "OMI_physics_gravity": { + "directional": { "direction": [0, 1, 0] }, + "gravity": 1.62, + "type": "directional" + } + }, + "name": "ZeroGravityAddUp", + "translation": [10, 0, -0.5] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 2 } } }, + "name": "ZeroGravityAddUpShape", + "translation": [0, 2, 0] + }, + { "mesh": 3, "name": "ZeroGravityAddUpPedestalMesh" }, + { + "children": [12], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "ZeroGravityAddUpPedestalCollider" + }, + { + "extensions": { "OMI_physics_body": { "collider": { "shape": 3 } } }, + "name": "ZeroGravityAddUpPedestalShape", + "translation": [0, -1, 0] + }, + { + "children": [14, 15, 16], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [14] } }, + "OMI_physics_gravity": { "gravity": 1.62, "type": "directional" } + }, + "name": "SidewaysGravityLocalDown", + "rotation": [0.0695864856243134, -0.1205275952816, -0.49513384699821, 0.857597410678864], + "translation": [-9, 0.3, -0.5] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 2 } } }, + "name": "SidewaysGravityLocalDownShape", + "rotation": [0, 0, 0.258819341659546, 0.965925753116608] + }, + { + "mesh": 4, + "name": "SidewaysGravityLocalDownPedestalMesh", + "rotation": [0, 0, 0.258819162845612, 0.965925812721252] + }, + { + "children": [17], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "SidewaysGravityLocalDownPedestalCollider", + "rotation": [0, 0, 0.258819162845612, 0.965925812721252] + }, + { + "extensions": { "OMI_physics_body": { "collider": { "shape": 3 } } }, + "name": "SidewaysGravityLocalDownPedestalShape", + "translation": [0, -1, 0] + }, + { + "children": [19, 20, 21], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [19] } }, + "OMI_physics_gravity": { + "directional": { "direction": [-1, 0, 0] }, + "gravity": 0.935, + "type": "directional" + } + }, + "name": "SidewaysGravityLocalWest", + "rotation": [0, 0.139172896742821, 0, 0.990268111228943], + "translation": [-8.5, 0, 4] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 2 } } }, + "name": "SidewaysGravityLocalWestShape", + "rotation": [0, 0, -0.25881916284561, 0.965925812721252] + }, + { + "mesh": 5, + "name": "SidewaysGravityLocalWestPedestalMesh", + "rotation": [0, 0, -0.25881916284561, 0.965925812721252] + }, + { + "children": [22], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "SidewaysGravityLocalWestPedestalCollider", + "rotation": [0, 0, -0.25881916284561, 0.965925812721252] + }, + { + "extensions": { "OMI_physics_body": { "collider": { "shape": 3 } } }, + "name": "SidewaysGravityLocalWestPedestalShape", + "translation": [0, -1, 0] + }, + { + "children": [24, 25, 26], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [24] } }, + "OMI_physics_gravity": { + "disc": { "radius": 3 }, + "gravity": 2, + "replace": true, + "stop": true, + "type": "disc" + } + }, + "name": "DiscGravity", + "translation": [-6, 0, 22] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 4 } } }, + "name": "DiscGravityShape", + "translation": [0, 0, 2] + }, + { "mesh": 6, "name": "DiscGravityMesh" }, + { + "children": [27], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "DiscGravityCollider" + }, + { "extensions": { "OMI_physics_body": { "collider": { "shape": 5 } } }, "name": "DiscGravityColliderShape" }, + { + "children": [29, 30, 31], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [29] } }, + "OMI_physics_gravity": { + "gravity": 2, + "replace": true, + "stop": true, + "torus": { "radius": 10 }, + "type": "torus" + } + }, + "name": "TorusGravity", + "translation": [-34, 0, 2] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 6 } } }, + "name": "TorusGravityShape1", + "translation": [-5, 0, 0] + }, + { "mesh": 8, "name": "TorusGravityMesh" }, + { + "children": [32], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "TorusGravityCollider" + }, + { "extensions": { "OMI_physics_body": { "collider": { "shape": 7 } } }, "name": "TorusGravityColliderShape" }, + { + "children": [34, 35, 36, 37], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [34, 35] } }, + "OMI_physics_gravity": { + "gravity": 2, + "line": { + "points": [0, 0, 3, 0, 0, -3, -5.196, 0, -6] + }, + "replace": true, + "stop": true, + "type": "line" + } + }, + "name": "LineGravityCapsule", + "translation": [-10, -1, -22] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 8 } } }, + "name": "LineGravityCapsuleShape1", + "translation": [-3, 0, -5] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 9 } } }, + "name": "LineGravityCapsuleShape2", + "translation": [-3, -4, 8] + }, + { "mesh": 10, "name": "LineGravityCapsuleMesh" }, + { + "children": [38], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "LineGravityCapsuleCollider" + }, + { + "extensions": { "OMI_physics_body": { "collider": { "shape": 10 } } }, + "name": "LineGravityCapsuleColliderShape" + }, + { + "children": [40, 41, 42], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [40] } }, + "OMI_physics_gravity": { + "gravity": 2.8, + "line": { + "points": [0, -10, 0, 0, 10, 0] + }, + "type": "line" + } + }, + "name": "LineGravityCone", + "translation": [20, 0, -7] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 11 } } }, + "name": "LineGravityConeShape", + "translation": [0, 6, 0] + }, + { + "mesh": 12, + "name": "LineGravityConeMesh", + "translation": [0, 2, 0] + }, + { + "children": [43], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "LineGravityConeCollider" + }, + { + "extensions": { "OMI_physics_body": { "collider": { "shape": 12 } } }, + "name": "LineGravityConeColliderShape", + "translation": [0, 2, 0] + }, + { + "children": [45, 46, 47], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [45] } }, + "OMI_physics_gravity": { "gravity": 2, "replace": true, "stop": true, "type": "point" } + }, + "name": "PointGravity", + "translation": [7, 5, -15] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 13 } } }, + "name": "PointGravityShape", + "translation": [0, 2.5, 0] + }, + { "mesh": 14, "name": "PointGravitySphereMesh" }, + { + "children": [48], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "PointGravityCollider" + }, + { "extensions": { "OMI_physics_body": { "collider": { "shape": 14 } } }, "name": "PointGravityColliderShape" }, + { + "children": [50, 51, 52, 53], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [50, 51] } }, + "OMI_physics_gravity": { + "gravity": 3, + "replace": true, + "shaped": { "shape": 0 }, + "stop": true, + "type": "shaped" + } + }, + "name": "ShapedGravityCube", + "translation": [20, -2, 12] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 15 } } }, + "name": "ShapedGravityCubeShape1", + "translation": [4, 0, 0] + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 16 } } }, + "name": "ShapedGravityCubeShape2", + "translation": [-6, -3.5, 0] + }, + { "mesh": 15, "name": "ShapedGravityCubeMesh" }, + { + "children": [54], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "ShapedGravityCubeCollider" + }, + { + "extensions": { "OMI_physics_body": { "collider": { "shape": 17 } } }, + "name": "ShapedGravityCubeColliderShape" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 29259, + "max": [19, 1.30579996109009, 19.0009002685547], + "min": [-19, -1.38950002193451, -19.00020027160645], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5123, + "count": 29259, + "max": [29258], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 5106, + "max": [19, 1.30579996109009, 19.0008983612061], + "min": [-19, -1.38953995704651, -19.00020027160645], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 5106, + "max": [1, 0.983123898506165, 0.269449949264526, -1], + "min": [0.0377098880708218, -0.76375699043274, -0.88739860057831, -1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5126, + "count": 5106, + "max": [0.750136017799377, 0.999999046325684, 0.819615840911865], + "min": [-0.98061925172806, 0.182534545660019, -0.75767570734024], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 5106, + "max": [0.974990487098694, 0.975005745887756], + "min": [0.0249942783266306, 0.0249790195375681], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5123, + "count": 29259, + "max": [5043], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 0, 0.999979496002197], + "min": [-1, -2, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 0.0000736713409423828, 1], + "min": [-1, -0.00003045622725, -0.00003057986032, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 1], + "min": [-1, -1, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [0.874998092651367, 1], + "min": [0.12498664855957, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [23], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 0, 0.999979496002197], + "min": [-1, -2, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 0.0000736713409423828, 1], + "min": [-1, -0.00003045622725, -0.00003057986032, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 1], + "min": [-1, -1, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 15, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [0.874998092651367, 1], + "min": [0.12498664855957, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 16, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [23], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 17, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 0, 0.999979496002197], + "min": [-1, -2, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 18, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 0.0000736713409423828, 1], + "min": [-1, -0.00003045622725, -0.00003057986032, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 19, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 1], + "min": [-1, -1, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 20, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [0.874998092651367, 1], + "min": [0.12498664855957, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 21, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [23], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 22, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 0, 0.999979496002197], + "min": [-1, -2, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 23, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 0.0000736713409423828, 1], + "min": [-1, -0.00003045622725, -0.00003057986032, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 24, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [1, 1, 1], + "min": [-1, -1, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 25, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [0.874998092651367, 1], + "min": [0.12498664855957, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 26, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [23], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 27, + "byteOffset": 0, + "componentType": 5126, + "count": 1250, + "max": [3.49990320205688, 0.5, 3.5], + "min": [-3.5, -0.5, -3.5], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 28, + "byteOffset": 0, + "componentType": 5126, + "count": 1250, + "max": [1, 0.277228564023972, 1, 1], + "min": [-1, -0.09738609194756, -1.00000011920929, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 29, + "byteOffset": 0, + "componentType": 5126, + "count": 1250, + "max": [1, 0.999985158443451, 1], + "min": [-1, -0.99987101554871, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 30, + "byteOffset": 0, + "componentType": 5126, + "count": 1250, + "max": [1, 0.749996185302734], + "min": [0, 0.249988555908203], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 31, + "byteOffset": 0, + "componentType": 5123, + "count": 6516, + "max": [1104], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 32, + "byteOffset": 0, + "componentType": 5126, + "count": 6516, + "max": [3.49990010261536, 0.5, 3.5], + "min": [-3.5, -0.5, -3.5], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 33, + "byteOffset": 0, + "componentType": 5123, + "count": 6516, + "max": [6515], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 34, + "byteOffset": 0, + "componentType": 5126, + "count": 8593, + "max": [13.1995973587036, 3.20000004768372, 13.1999998092651], + "min": [-13.19999980926514, -3.20000004768372, -13.19999980926514], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 35, + "byteOffset": 0, + "componentType": 5126, + "count": 8593, + "max": [1, 0.658829808235168, 1, 1], + "min": [-1, -0.74816966056824, -1.00000011920929, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 36, + "byteOffset": 0, + "componentType": 5126, + "count": 8593, + "max": [1, 0.999999821186066, 1], + "min": [-1, -0.99999982118607, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 37, + "byteOffset": 0, + "componentType": 5126, + "count": 8593, + "max": [1, 1], + "min": [0, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 38, + "byteOffset": 0, + "componentType": 5123, + "count": 49152, + "max": [8384], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 39, + "byteOffset": 0, + "componentType": 5126, + "count": 49152, + "max": [13.1996002197266, 3.20000004768372, 13.1999998092651], + "min": [-13.19999980926514, -3.20000004768372, -13.19999980926514], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 40, + "byteOffset": 0, + "componentType": 5123, + "count": 49152, + "max": [49151], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 41, + "byteOffset": 0, + "componentType": 5126, + "count": 4618, + "max": [1.99985980987549, 1.99994897842407, 4.99997043609619], + "min": [-7.1950798034668, -2, -7.99892997741699], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 42, + "byteOffset": 0, + "componentType": 5126, + "count": 4618, + "max": [1, 1.00000023841858, 0.944491326808929, 1], + "min": [-1, -1.00000011920929, -0.86602449417114, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 43, + "byteOffset": 0, + "componentType": 5126, + "count": 4618, + "max": [0.999702334403992, 0.999702036380768, 1], + "min": [-0.99970126152039, -0.99970179796219, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 44, + "byteOffset": 0, + "componentType": 5126, + "count": 4618, + "max": [1, 0.999984741210938], + "min": [0, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 45, + "byteOffset": 0, + "componentType": 5123, + "count": 24576, + "max": [4415], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 46, + "byteOffset": 0, + "componentType": 5126, + "count": 24576, + "max": [1.99989998340607, 1.99989998340607, 5], + "min": [-7.19509983062744, -2, -7.99889993667603], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 47, + "byteOffset": 0, + "componentType": 5123, + "count": 24576, + "max": [24575], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 48, + "byteOffset": 0, + "componentType": 5126, + "count": 456, + "max": [3.46399998664856, 3, 3.46399998664856], + "min": [-3.46399998664856, -3, -3.46399998664856], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 49, + "byteOffset": 0, + "componentType": 5126, + "count": 456, + "max": [1, 0, 1, 1], + "min": [-1, -0.00002157951531, -1, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 50, + "byteOffset": 0, + "componentType": 5126, + "count": 456, + "max": [0.866026937961578, 0.500042736530304, 0.866026937961578], + "min": [-0.86604118347168, -1, -0.86601793766022], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 51, + "byteOffset": 0, + "componentType": 5126, + "count": 456, + "max": [1, 1], + "min": [0, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 52, + "byteOffset": 0, + "componentType": 5123, + "count": 2112, + "max": [455], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 53, + "byteOffset": 0, + "componentType": 5126, + "count": 522, + "max": [3.46399998664856, 3, 3.46399998664856], + "min": [-3.44772005081177, -3, -3.46399998664856], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 54, + "byteOffset": 0, + "componentType": 5123, + "count": 522, + "max": [521], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 55, + "byteOffset": 0, + "componentType": 5126, + "count": 755, + "max": [2.5, 2.5, 2.5], + "min": [-2.5, -2.5, -2.5], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 56, + "byteOffset": 0, + "componentType": 5126, + "count": 755, + "max": [0.999899983406067, 0.311829507350922, 1, 1], + "min": [-0.9998996257782, -0.31187635660172, -1, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 57, + "byteOffset": 0, + "componentType": 5126, + "count": 755, + "max": [0.999997138977051, 1, 1], + "min": [-0.99999707937241, -1, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 58, + "byteOffset": 0, + "componentType": 5126, + "count": 755, + "max": [1, 1], + "min": [0, 0.527611196041107], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 59, + "byteOffset": 0, + "componentType": 5123, + "count": 3840, + "max": [728], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 60, + "byteOffset": 0, + "componentType": 5126, + "count": 2006, + "max": [2.5, 2.5, 2.5], + "min": [-2.5, -2.5, -2.5], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 61, + "byteOffset": 0, + "componentType": 5126, + "count": 2006, + "max": [1, 1.00000011920929, 0.737568855285645, 1], + "min": [-1, -0.76894599199295, -0.86604261398315, 1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 62, + "byteOffset": 0, + "componentType": 5126, + "count": 2006, + "max": [1, 1, 1], + "min": [-1, -1, -1], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 63, + "byteOffset": 0, + "componentType": 5126, + "count": 2006, + "max": [0.874998092651367, 1], + "min": [0.12498664855957, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 64, + "byteOffset": 0, + "componentType": 5123, + "count": 2916, + "max": [1943], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 65, + "byteOffset": 0, + "componentType": 5126, + "count": 2916, + "max": [2.5, 2.5, 2.5], + "min": [-2.5, -2.5, -2.5], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 66, + "byteOffset": 0, + "componentType": 5123, + "count": 2916, + "max": [2915], + "min": [0], + "normalized": false, + "type": "SCALAR" + } + ], + "bufferViews": [ + { "buffer": 0, "byteLength": 351108, "byteOffset": 0, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 58518, "byteOffset": 351108, "target": 34963 }, + { "buffer": 0, "byteLength": 61272, "byteOffset": 409628, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 81696, "byteOffset": 470900, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 61272, "byteOffset": 552596, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 40848, "byteOffset": 613868, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 58518, "byteOffset": 654716, "target": 34963 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 713236, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 384, "byteOffset": 713524, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 713908, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 192, "byteOffset": 714196, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 72, "byteOffset": 714388, "target": 34963 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 714460, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 384, "byteOffset": 714748, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 715132, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 192, "byteOffset": 715420, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 72, "byteOffset": 715612, "target": 34963 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 715684, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 384, "byteOffset": 715972, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 716356, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 192, "byteOffset": 716644, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 72, "byteOffset": 716836, "target": 34963 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 716908, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 384, "byteOffset": 717196, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 288, "byteOffset": 717580, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 192, "byteOffset": 717868, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 72, "byteOffset": 718060, "target": 34963 }, + { "buffer": 0, "byteLength": 15000, "byteOffset": 718132, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 20000, "byteOffset": 733132, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 15000, "byteOffset": 753132, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 10000, "byteOffset": 768132, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 13032, "byteOffset": 778132, "target": 34963 }, + { "buffer": 0, "byteLength": 78192, "byteOffset": 791164, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 13032, "byteOffset": 869356, "target": 34963 }, + { "buffer": 0, "byteLength": 103116, "byteOffset": 882388, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 137488, "byteOffset": 985504, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 103116, "byteOffset": 1122992, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 68744, "byteOffset": 1226108, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 98304, "byteOffset": 1294852, "target": 34963 }, + { "buffer": 0, "byteLength": 589824, "byteOffset": 1393156, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 98304, "byteOffset": 1982980, "target": 34963 }, + { "buffer": 0, "byteLength": 55416, "byteOffset": 2081284, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 73888, "byteOffset": 2136700, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 55416, "byteOffset": 2210588, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 36944, "byteOffset": 2266004, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 49152, "byteOffset": 2302948, "target": 34963 }, + { "buffer": 0, "byteLength": 294912, "byteOffset": 2352100, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 49152, "byteOffset": 2647012, "target": 34963 }, + { "buffer": 0, "byteLength": 5472, "byteOffset": 2696164, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 7296, "byteOffset": 2701636, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 5472, "byteOffset": 2708932, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 3648, "byteOffset": 2714404, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 4224, "byteOffset": 2718052, "target": 34963 }, + { "buffer": 0, "byteLength": 6264, "byteOffset": 2722276, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 1044, "byteOffset": 2728540, "target": 34963 }, + { "buffer": 0, "byteLength": 9060, "byteOffset": 2729584, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 12080, "byteOffset": 2738644, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 9060, "byteOffset": 2750724, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 6040, "byteOffset": 2759784, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 7680, "byteOffset": 2765824, "target": 34963 }, + { "buffer": 0, "byteLength": 24072, "byteOffset": 2773504, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 32096, "byteOffset": 2797576, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 24072, "byteOffset": 2829672, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 16048, "byteOffset": 2853744, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 5832, "byteOffset": 2869792, "target": 34963 }, + { "buffer": 0, "byteLength": 34992, "byteOffset": 2875624, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 5832, "byteOffset": 2910616, "target": 34963 } + ], + "buffers": [{ "byteLength": 2916448, "uri": "moon_petavius_crater0.bin" }], + "samplers": [{ "magFilter": 9729, "minFilter": 9987, "wrapS": 10497, "wrapT": 10497 }], + "scene": 0, + "scenes": [{ "name": "MoonPetaviusCraterScene", "nodes": [0, 3, 8, 13, 18, 23, 28, 33, 39, 44, 49] }], + "textures": [ + { "sampler": 0, "source": 0 }, + { "sampler": 0, "source": 1 }, + { "sampler": 0, "source": 2 } + ] +} diff --git a/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater0.bin b/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater0.bin new file mode 100644 index 0000000..e4610ea Binary files /dev/null and b/OMI_physics_gravity/examples/moon_petavius_crater/moon_petavius_crater0.bin differ diff --git a/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_albedo000.jpg b/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_albedo000.jpg new file mode 100644 index 0000000..dc39b65 Binary files /dev/null and b/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_albedo000.jpg differ diff --git a/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_normal000.jpg b/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_normal000.jpg new file mode 100644 index 0000000..160df5e Binary files /dev/null and b/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_normal000.jpg differ diff --git a/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_orm000.jpg b/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_orm000.jpg new file mode 100644 index 0000000..7d08d7b Binary files /dev/null and b/OMI_physics_gravity/examples/moon_petavius_crater/textures/moon_petavius_crater_material_orm000.jpg differ diff --git a/OMI_physics_gravity/examples/ramp/ramp_gravity.glb b/OMI_physics_gravity/examples/ramp/ramp_gravity.glb new file mode 100644 index 0000000..d2f80e4 Binary files /dev/null and b/OMI_physics_gravity/examples/ramp/ramp_gravity.glb differ diff --git a/OMI_physics_gravity/examples/ramp/ramp_gravity.gltf b/OMI_physics_gravity/examples/ramp/ramp_gravity.gltf new file mode 100644 index 0000000..3f0bb6a --- /dev/null +++ b/OMI_physics_gravity/examples/ramp/ramp_gravity.gltf @@ -0,0 +1,158 @@ +{ + "asset": { + "generator": "Godot Engine v4.3.dev.custom_build", + "version": "2.0" + }, + "extensionsUsed": ["GODOT_single_root", "OMI_physics_body", "OMI_physics_gravity", "OMI_physics_shape"], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { "box": { "size": [6, 4, 3] }, "type": "box" }, + { "box": { "size": [2, 7, 5] }, "type": "box" }, + { "trimesh": { "mesh": 1 }, "type": "trimesh" } + ] + } + }, + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [1, 1, 1, 1], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { "targetNames": [] }, + "primitives": [ + { + "attributes": { "NORMAL": 2, "POSITION": 0, "TANGENT": 1, "TEXCOORD_0": 3 }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { "targetNames": [] }, + "primitives": [{ "attributes": { "POSITION": 5 }, "indices": 6, "mode": 4 }] + } + ], + "nodes": [ + { + "children": [1, 2, 3], + "extensions": { + "OMI_physics_body": { "trigger": { "nodes": [1] } }, + "OMI_physics_gravity": { + "gravity": -9.8, + "shaped": { "shape": 0 }, + "stop": true, + "type": "shaped" + } + }, + "name": "RampGravity" + }, + { + "extensions": { "OMI_physics_body": { "trigger": { "shape": 1 } } }, + "name": "RampGravityShape", + "translation": [0, -1.5, -2] + }, + { + "mesh": 0, + "name": "RampGravityMesh", + "translation": [0, -5, 0.5] + }, + { + "children": [4], + "extensions": { "OMI_physics_body": { "motion": { "type": "static" } } }, + "name": "RampGravityCollider", + "translation": [0, -5, 0.5] + }, + { "extensions": { "OMI_physics_body": { "collider": { "shape": 2 } } }, "name": "RampGravityColliderShape" } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 136, + "max": [0.999979496002197, 7, -0.00006628036499], + "min": [-1, 0, -5], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 136, + "max": [1, 0.0000152590218931437, 0.999698579311371, 1], + "min": [-0.00013981414668, -0.99970042705536, -0.00001525865719, -1], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 136, + "max": [0.0000841021610540338, 1, 1], + "min": [-0.00014381069923, 0, 0.0000239175878959941], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 136, + "max": [0.749996185302734, 0.499992370605469], + "min": [0.499992370605469, 0], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5123, + "count": 204, + "max": [135], + "min": [0], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 204, + "max": [1, 7, -0.00009999999747], + "min": [-1, 0, -5], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5123, + "count": 204, + "max": [203], + "min": [0], + "normalized": false, + "type": "SCALAR" + } + ], + "bufferViews": [ + { "buffer": 0, "byteLength": 1632, "byteOffset": 0, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 2176, "byteOffset": 1632, "byteStride": 16, "target": 34962 }, + { "buffer": 0, "byteLength": 1632, "byteOffset": 3808, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 1088, "byteOffset": 5440, "byteStride": 8, "target": 34962 }, + { "buffer": 0, "byteLength": 408, "byteOffset": 6528, "target": 34963 }, + { "buffer": 0, "byteLength": 2448, "byteOffset": 6936, "byteStride": 12, "target": 34962 }, + { "buffer": 0, "byteLength": 408, "byteOffset": 9384, "target": 34963 } + ], + "buffers": [{ "byteLength": 9792, "uri": "ramp_gravity0.bin" }] +} diff --git a/OMI_physics_gravity/examples/ramp/ramp_gravity0.bin b/OMI_physics_gravity/examples/ramp/ramp_gravity0.bin new file mode 100644 index 0000000..6dd93a9 Binary files /dev/null and b/OMI_physics_gravity/examples/ramp/ramp_gravity0.bin differ diff --git a/OMI_physics_gravity/examples/rounded_cube/rounded_cube.bin b/OMI_physics_gravity/examples/rounded_cube/rounded_cube.bin new file mode 100644 index 0000000..f01cc76 Binary files /dev/null and b/OMI_physics_gravity/examples/rounded_cube/rounded_cube.bin differ diff --git a/OMI_physics_gravity/examples/rounded_cube/rounded_cube.glb b/OMI_physics_gravity/examples/rounded_cube/rounded_cube.glb new file mode 100644 index 0000000..5dc2acb Binary files /dev/null and b/OMI_physics_gravity/examples/rounded_cube/rounded_cube.glb differ diff --git a/OMI_physics_gravity/examples/rounded_cube/rounded_cube.gltf b/OMI_physics_gravity/examples/rounded_cube/rounded_cube.gltf new file mode 100644 index 0000000..7234946 --- /dev/null +++ b/OMI_physics_gravity/examples/rounded_cube/rounded_cube.gltf @@ -0,0 +1,142 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_gravity", + "OMI_physics_shape" + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [6, 6, 6] + } + }, + { + "type": "box", + "box": { + "size": [1, 1, 1] + } + }, + { + "type": "convex", + "convex": { + "mesh": 0 + } + } + ] + } + }, + "nodes": [ + { + "name": "RoundedCube", + "children": [1, 2, 3], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + } + }, + { + "name": "RoundedCubeGravity", + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 0 + } + }, + "OMI_physics_gravity": { + "type": "shaped", + "gravity": 9.80665, + "stop": true, + "shaped": { + "shape": 1 + } + } + } + }, + { + "name": "RoundedCubeMesh", + "mesh": 0 + }, + { + "name": "RoundedCubeShape", + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 2 + } + } + } + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }], + "accessors": [ + { + "type": "VEC3", + "componentType": 5126, + "count": 1844, + "max": [0.75, 0.75, 0.75], + "min": [-0.75, -0.75, -0.75], + "bufferView": 0, + "byteOffset": 0 + }, + { + "type": "VEC3", + "componentType": 5126, + "count": 1844, + "bufferView": 0, + "byteOffset": 12 + }, + { + "type": "SCALAR", + "componentType": 5123, + "count": 10344, + "bufferView": 1, + "byteOffset": 0 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 44256, + "byteStride": 24, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 44256, + "byteLength": 20688, + "target": 34963 + } + ], + "buffers": [ + { + "uri": "rounded_cube.bin", + "byteLength": 64944 + } + ], + "meshes": [ + { + "name": "RoundedCubeMeshData", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "NORMAL": 1 + }, + "mode": 4, + "indices": 2 + } + ] + } + ] +} diff --git a/OMI_physics_gravity/index.html b/OMI_physics_gravity/index.html new file mode 100644 index 0000000..69fa6b0 --- /dev/null +++ b/OMI_physics_gravity/index.html @@ -0,0 +1,1310 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_physics_gravity - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_physics_gravity

+

Contributors

+
    +
  • Aaron Franke, Godot Engine.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Depends on the OMI_physics_body and OMI_physics_shape specs for per-node gravity volumes.

+

Overview

+

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.

+

Example:

+

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.

+

glTF Schema Updates

+

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.

+

Property Summary

+

The following properties apply to the document-level extension defining the world gravity:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault valueValid on
gravitynumberThe gravity amount in meters per second squared. Can be zero or negative.Required, no defaultAlways valid
directionnumber[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:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault valueValid on
typestringThe type of the gravity as a string.Required, no defaultAlways valid
gravitynumberThe gravity amount in meters per second squared. Can be zero or negative.Required, no defaultAlways valid
prioritynumberThe process priority of this gravity node. Must be an integer.0Always valid
replacebooleanIf true, replace the current gravity instead of adding to it.falseAlways valid
stopbooleanIf true, stop checking more nodes for gravity.falseAlways 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.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault valueValid on
directionnumber[3]The normalized direction of the gravity relative to this node's transform.[0.0, -1.0, 0.0]Directional
unitDistancenumberThe distance from the nearest point at which the experienced gravity equals the gravity property.0.0Non-directional
radiusnumberThe radius of the circle to attract gravity towards.1.0Disc, torus
pointsnumber[]The points that make up the line segments of a line type gravity field.[]Line
shapenumberThe integer ID of a physics shape used to define the gravity direction.-1Shaped
+

Type

+

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

+

"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

+

"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

+

"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

+

"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

+

"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

+

"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.

+

Gravity

+

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.

+

Direction

+

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].

+

Unit Distance

+

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.

+

Radius

+

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.

+

Points

+

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.

+

Shape

+

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.

+

Priority

+

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).

+

Replace

+

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.

+

Stop

+

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.

+

Additional Examples

+

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 nameDescription
CapsuleLine gravity with 2 points where the line spans the mid-height and is fully inside the trigger volume.
CylinderLine gravity with 2 points where the line touches or surpasses the ends of the trigger volume.
WedgeLine gravity with 2 points where the line is defined at the corner edge of a trigger volume.
ConeLine gravity with 2 points, plus directional gravity pointing towards the large end of the cone.
+

JSON Schema

+

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.

+

Known Implementations

+
    +
  • Godot Engine add-on
  • +
+

Resources:

+
    +
  • Godot Area3D: https://docs.godotengine.org/en/latest/classes/class_area3d.html
  • +
  • Gravity in Super Mario Galaxy: https://www.youtube.com/watch?v=QLH_0T_xv3I
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_gravity/schema/glTF.OMI_physics_gravity.schema.json b/OMI_physics_gravity/schema/glTF.OMI_physics_gravity.schema.json new file mode 100644 index 0000000..23630a4 --- /dev/null +++ b/OMI_physics_gravity/schema/glTF.OMI_physics_gravity.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_gravity.schema.json", + "title": "OMI_physics_gravity glTF Document Extension", + "type": "object", + "description": "Top level global gravity parameters.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "gravity": { + "type": "number", + "description": "The gravity amount in meters per second squared. Can be zero or negative. Required." + }, + "direction": { + "type": "array", + "description": "The direction of the global gravity. Should be normalized.", + "default": [0.0, -1.0, 0.0], + "minItems": 3, + "maxItems": 3 + } + }, + "required": [ + "gravity" + ] +} diff --git a/OMI_physics_gravity/schema/node.OMI_physics_gravity.directional.schema.json b/OMI_physics_gravity/schema/node.OMI_physics_gravity.directional.schema.json new file mode 100644 index 0000000..d01c385 --- /dev/null +++ b/OMI_physics_gravity/schema/node.OMI_physics_gravity.directional.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_gravity.directional.schema.json", + "title": "OMI_physics_gravity Directional Gravity", + "type": "object", + "description": "Parameters describing a directional gravity field.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "direction": { + "type": "array", + "description": "The normalized direction of gravity relative to this node's transform.", + "default": [0.0, -1.0, 0.0], + "minItems": 3, + "maxItems": 3 + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_gravity/schema/node.OMI_physics_gravity.disc.schema.json b/OMI_physics_gravity/schema/node.OMI_physics_gravity.disc.schema.json new file mode 100644 index 0000000..ee7f93d --- /dev/null +++ b/OMI_physics_gravity/schema/node.OMI_physics_gravity.disc.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_gravity.disc.schema.json", + "title": "OMI_physics_gravity Disc Gravity", + "type": "object", + "description": "Parameters describing a disc gravity field.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "radius": { + "type": "number", + "description": "The radius to use for the circle used in the disc gravity type.", + "default": 1.0 + }, + "unitDistance": { + "type": "number", + "description": "The distance from the nearest point at which the gravity equals the gravity property. If zero or not specified, the gravity strength is constant.", + "default": 0.0 + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_gravity/schema/node.OMI_physics_gravity.line.schema.json b/OMI_physics_gravity/schema/node.OMI_physics_gravity.line.schema.json new file mode 100644 index 0000000..1016f8b --- /dev/null +++ b/OMI_physics_gravity/schema/node.OMI_physics_gravity.line.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_gravity.line.schema.json", + "title": "OMI_physics_gravity Line Gravity", + "type": "object", + "description": "Parameters describing a line type gravity field.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "points": { + "type": "array", + "description": "The points that make up the line segments of a line type gravity field. Each set of 3 numbers is a vector. Must have a size be at least 6 and be a multiple of 3.", + "default": [], + "minItems": 6 + }, + "unitDistance": { + "type": "number", + "description": "The distance from the nearest point at which the gravity equals the gravity property. If zero or not specified, the gravity strength is constant.", + "default": 0.0 + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "points" + ] +} diff --git a/OMI_physics_gravity/schema/node.OMI_physics_gravity.point.schema.json b/OMI_physics_gravity/schema/node.OMI_physics_gravity.point.schema.json new file mode 100644 index 0000000..1563dcd --- /dev/null +++ b/OMI_physics_gravity/schema/node.OMI_physics_gravity.point.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_gravity.point.schema.json", + "title": "OMI_physics_gravity Point Gravity", + "type": "object", + "description": "Parameters describing a point gravity field.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "unitDistance": { + "type": "number", + "description": "The distance from the origin point at which the gravity equals the gravity property. If zero or not specified, the gravity strength is constant.", + "default": 0.0 + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_gravity/schema/node.OMI_physics_gravity.schema.json b/OMI_physics_gravity/schema/node.OMI_physics_gravity.schema.json new file mode 100644 index 0000000..f23f1ff --- /dev/null +++ b/OMI_physics_gravity/schema/node.OMI_physics_gravity.schema.json @@ -0,0 +1,139 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_gravity.schema.json", + "title": "OMI_physics_gravity glTF Node Extension", + "type": "object", + "description": "Defines gravity information for a node.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "type": { + "type": "string", + "description": "The type of gravity of this node as a string. Required.", + "enum": [ + "directional", + "disc", + "line", + "point", + "shaped", + "torus" + ] + }, + "gravity": { + "type": "number", + "description": "The gravity amount in meters per second squared. Can be zero or negative. Required." + }, + "priority": { + "type": "number", + "description": "The process priority of this gravity node. Must be an integer. Higher priority gravity nodes are processed first. The world gravity is processed last.", + "default": 0 + }, + "replace": { + "type": "boolean", + "description": "If true, the gravity amount replaces the current gravity amount instead of adding to it.", + "default": false + }, + "stop": { + "type": "boolean", + "description": "If true, stop checking more nodes for gravity. This means that lower priority gravity nodes will be ignored. If priority is non-negative, this also means the world gravity is ignored.", + "default": false + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "type", + "gravity" + ], + "oneOf": [ + { + "required": [ + "type", + "directional" + ], + "properties": { + "type": { + "const": "directional" + }, + "directional": { + "type": "object", + "$ref": "node.OMI_physics_gravity.directional.schema.json" + } + } + }, + { + "required": [ + "type", + "disc" + ], + "properties": { + "type": { + "const": "disc" + }, + "disc": { + "type": "object", + "$ref": "node.OMI_physics_gravity.disc.schema.json" + } + } + }, + { + "required": [ + "type", + "line" + ], + "properties": { + "type": { + "const": "line" + }, + "line": { + "type": "object", + "$ref": "node.OMI_physics_gravity.line.schema.json" + } + } + }, + { + "required": [ + "type", + "point" + ], + "properties": { + "type": { + "const": "point" + }, + "point": { + "type": "object", + "$ref": "node.OMI_physics_gravity.point.schema.json" + } + } + }, + { + "required": [ + "type", + "shaped" + ], + "properties": { + "type": { + "const": "shaped" + }, + "shaped": { + "type": "object", + "$ref": "node.OMI_physics_gravity.shaped.schema.json" + } + } + }, + { + "required": [ + "type", + "torus" + ], + "properties": { + "type": { + "const": "torus" + }, + "torus": { + "type": "object", + "$ref": "node.OMI_physics_gravity.torus.schema.json" + } + } + } + ] +} diff --git a/OMI_physics_gravity/schema/node.OMI_physics_gravity.shaped.schema.json b/OMI_physics_gravity/schema/node.OMI_physics_gravity.shaped.schema.json new file mode 100644 index 0000000..6a91869 --- /dev/null +++ b/OMI_physics_gravity/schema/node.OMI_physics_gravity.shaped.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_gravity.shaped.schema.json", + "title": "OMI_physics_gravity Shaped Gravity", + "type": "object", + "description": "Parameters describing a gravity field that points towards a shape.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "shape": { + "$ref": "glTFid.schema.json", + "description": "The ID of the shape defined by OMI_physics_shape which gravity points towards." + }, + "unitDistance": { + "type": "number", + "description": "The distance from the nearest point at which the gravity equals the gravity property. If zero or not specified, the gravity strength is constant.", + "default": 0.0 + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "shape" + ] +} diff --git a/OMI_physics_gravity/schema/node.OMI_physics_gravity.torus.schema.json b/OMI_physics_gravity/schema/node.OMI_physics_gravity.torus.schema.json new file mode 100644 index 0000000..cdac571 --- /dev/null +++ b/OMI_physics_gravity/schema/node.OMI_physics_gravity.torus.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_gravity.torus.schema.json", + "title": "OMI_physics_gravity Torus Gravity", + "type": "object", + "description": "Parameters describing a torus gravity field.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "radius": { + "type": "number", + "description": "The radius to use for the circle used in the torus gravity type.", + "default": 1.0 + }, + "unitDistance": { + "type": "number", + "description": "The distance from the nearest point at which the gravity equals the gravity property. If zero or not specified, the gravity strength is constant.", + "default": 0.0 + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_joint/examples/hanging_rope.gltf b/OMI_physics_joint/examples/hanging_rope.gltf new file mode 100644 index 0000000..72940f7 --- /dev/null +++ b/OMI_physics_joint/examples/hanging_rope.gltf @@ -0,0 +1,893 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.25, + 0.25, + 0.25 + ], + "min": [ + -0.25, + -0.25, + -0.25 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 15, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 16, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 17, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 18, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 19, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "generator": "Godot Engine v4.1.dev.custom_build@9f12e7b52d944281a39b7d3a33de6700c76cc23a", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 1296 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 24696 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 55896 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 79296 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 94896 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 136368 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 159768 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 190968 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 214368 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 229968 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 271440 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 294840 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 326040 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 349440 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 365040 + } + ], + "buffers": [ + { + "byteLength": 406512, + "uri": "hanging_rope0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.05 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [0.5, 0.5, 0.5] + } + }, + { + "type": "capsule", + "capsule": { + "height": 0.5, + "radius": 0.05 + } + } + ] + }, + "OMI_physics_joint": { + "constraints": [ + { + "linearAxes": [ + 0, + 1, + 2 + ], + "stiffness": 0.300000011920929 + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_joint", + "OMI_physics_shape" + ], + "materials": [ + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 12, + "POSITION": 10, + "TANGENT": 11, + "TEXCOORD_0": 13 + }, + "indices": 14, + "material": 2, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 17, + "POSITION": 15, + "TANGENT": 16, + "TEXCOORD_0": 18 + }, + "indices": 19, + "material": 3, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1, + 4, + 5, + 8, + 9, + 12, + 13, + 16 + ], + "extensions": {}, + "name": "HangingRope" + }, + { + "children": [ + 2 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "Ceiling", + "translation": [ + 0.00000000000208164995657567, + 1.5, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 3 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "CeilingShape" + }, + { + "extensions": {}, + "mesh": 0, + "name": "CeilingMesh" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 5, + "nodeB": 1 + } + }, + "name": "PinJoint1", + "translation": [ + -0.0000000279397, + 1.25, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 6 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentTop", + "rotation": [ + 0.000000000000000213587655926077, + 0.000000000000000123314960753772, + 0.258819162845612, + 0.965925812721252 + ], + "translation": [ + -0.17000000178814, + 1.14999997615814, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 7 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeTop", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 1, + "name": "RopeMeshTop" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 9, + "nodeB": 5 + } + }, + "name": "PinJoint2", + "translation": [ + -0.37000000476837, + 1.02999997138977, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 10 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentMiddle", + "rotation": [ + 0.000000000000000202027284087501, + 0.000000000000000141460709354785, + 0.173648044466972, + 0.984807789325714 + ], + "translation": [ + -0.5799999833107, + 0.949999988079071, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 11 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeMiddle", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 2, + "name": "RopeMeshMiddle" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 13, + "nodeB": 9 + } + }, + "name": "PinJoint3", + "translation": [ + -0.80000001192093, + 0.870000004768372, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 14 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentBottom", + "translation": [ + -1.02999997138977, + 0.870000004768372, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 15 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeBottom", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 3, + "name": "RopeMeshBottom" + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "translation": [ + 0.00000000000208164995657567, + 0.600000023841858, + 1.5 + ] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/OMI_physics_joint/examples/hanging_rope0.bin b/OMI_physics_joint/examples/hanging_rope0.bin new file mode 100644 index 0000000..4c654f4 Binary files /dev/null and b/OMI_physics_joint/examples/hanging_rope0.bin differ diff --git a/OMI_physics_joint/examples/pendulum_balls.gltf b/OMI_physics_joint/examples/pendulum_balls.gltf new file mode 100644 index 0000000..2c831d7 --- /dev/null +++ b/OMI_physics_joint/examples/pendulum_balls.gltf @@ -0,0 +1,1349 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0.1, + 0.1 + ], + "min": [ + -1, + -0.1, + -0.1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.249717, + 0.25, + 0.249717 + ], + "min": [ + -0.249717, + -0.25, + -0.249717 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.5, + 0.05 + ], + "min": [ + -0.05, + -0.5, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 15, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.249717, + 0.25, + 0.249717 + ], + "min": [ + -0.249717, + -0.25, + -0.249717 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 16, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 17, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 18, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 19, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 20, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.5, + 0.05 + ], + "min": [ + -0.05, + -0.5, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 21, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 22, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 23, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 24, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 25, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.249717, + 0.25, + 0.249717 + ], + "min": [ + -0.249717, + -0.25, + -0.249717 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 26, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 27, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 28, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 29, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 30, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.5, + 0.05 + ], + "min": [ + -0.05, + -0.5, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 31, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 32, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 33, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 34, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "generator": "Godot Engine v4.1.dev.custom_build@9f12e7b52d944281a39b7d3a33de6700c76cc23a", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 1296 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 27816 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 63176 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 89696 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 107376 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 158064 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 181464 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 212664 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 236064 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 251664 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 293136 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 319656 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 355016 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 381536 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 399216 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 449904 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 473304 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 504504 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 527904 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 543504 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 584976 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 611496 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 646856 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 673376 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 691056 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 741744 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 765144 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 796344 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 819744 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 835344 + } + ], + "buffers": [ + { + "byteLength": 876816, + "uri": "pendulum_balls0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.0500000007450581 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [2, 0.2, 0.2] + } + }, + { + "type": "sphere", + "sphere": { + "radius": 0.25 + } + } + ] + }, + "OMI_physics_joint": { + "constraints": [ + { + "linearAxes": [ + 0, + 1, + 2 + ], + "stiffness": 0.300000011920929 + }, + { + "angularAxes": [ + 0, + 1 + ], + "stiffness": 0.300000011920929 + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_joint", + "OMI_physics_shape" + ], + "materials": [ + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 12, + "POSITION": 10, + "TANGENT": 11, + "TEXCOORD_0": 13 + }, + "indices": 14, + "material": 2, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 17, + "POSITION": 15, + "TANGENT": 16, + "TEXCOORD_0": 18 + }, + "indices": 19, + "material": 3, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 22, + "POSITION": 20, + "TANGENT": 21, + "TEXCOORD_0": 23 + }, + "indices": 24, + "material": 4, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 27, + "POSITION": 25, + "TANGENT": 26, + "TEXCOORD_0": 28 + }, + "indices": 29, + "material": 5, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 32, + "POSITION": 30, + "TANGENT": 31, + "TEXCOORD_0": 33 + }, + "indices": 34, + "material": 6, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1, + 4, + 5, + 9, + 10, + 14, + 15, + 19 + ], + "extensions": {}, + "name": "PendulumBalls" + }, + { + "children": [ + 2 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "TopBody", + "translation": [ + 0.00000000000208164995657567, + 1, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 3 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "TopBodyShape" + }, + { + "extensions": {}, + "mesh": 0, + "name": "TopBodyMesh" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0, + 1 + ], + "nodeA": 1, + "nodeB": 5 + } + }, + "name": "HingeJoint1", + "translation": [ + 0.00000000000208164995657567, + 0.899999976158142, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 6, + 8 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "Ball1", + "translation": [ + 0.00000000000208164995657567, + -0.25, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 7 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "BallShape1" + }, + { + "extensions": {}, + "mesh": 1, + "name": "BallMesh1" + }, + { + "extensions": {}, + "mesh": 2, + "name": "BallStringMesh1", + "translation": [ + 0.00000000000208164995657567, + 0.699999988079071, + 0.00000000000208164995657567 + ] + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0, + 1 + ], + "nodeA": 1, + "nodeB": 10 + } + }, + "name": "HingeJoint2", + "translation": [ + -0.44999998807907, + 0.899999976158142, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 11, + 13 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "Ball2", + "rotation": [ + 0.000000000000000094381003489835, + 0.000000000000000227855912687686, + -0.38268345594406, + 0.923879563808441 + ], + "translation": [ + -1.26317000389099, + 0.0868272036314011, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 12 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "BallShape2" + }, + { + "extensions": {}, + "mesh": 3, + "name": "BallMesh2" + }, + { + "extensions": {}, + "mesh": 4, + "name": "BallStringMesh2", + "translation": [ + 0.00000000000208164995657567, + 0.699999988079071, + 0.00000000000208164995657567 + ] + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0, + 1 + ], + "nodeA": 1, + "nodeB": 15 + } + }, + "name": "HingeJoint3", + "rotation": [ + 0.000000000000000213587655926077, + 0.000000000000000123314960753772, + 0.258819162845612, + 0.965925812721252 + ], + "translation": [ + 0.449999988079071, + 0.899999976158142, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 16, + 18 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "Ball3", + "rotation": [ + 0.000000000000000440183937976967, + -0, + 0.991444885730743, + 0.130526155233383 + ], + "translation": [ + 0.747641980648041, + 2.01080989837646, + 0.00000000000208192989757661 + ] + }, + { + "children": [ + 17 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "BallShape22" + }, + { + "extensions": {}, + "mesh": 5, + "name": "BallMesh22" + }, + { + "extensions": {}, + "mesh": 6, + "name": "BallStringMesh22", + "translation": [ + 0.00000000000208164995657567, + 0.699999988079071, + 0.00000000000208164995657567 + ] + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "translation": [ + 0.00000000000208164995657567, + 0.00000000000208164995657567, + 1.5 + ] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/OMI_physics_joint/examples/pendulum_balls0.bin b/OMI_physics_joint/examples/pendulum_balls0.bin new file mode 100644 index 0000000..fc06e65 Binary files /dev/null and b/OMI_physics_joint/examples/pendulum_balls0.bin differ diff --git a/OMI_physics_joint/examples/rope_railing.gltf b/OMI_physics_joint/examples/rope_railing.gltf new file mode 100644 index 0000000..0bf001f --- /dev/null +++ b/OMI_physics_joint/examples/rope_railing.gltf @@ -0,0 +1,1508 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.075, + 0.55, + 0.075 + ], + "min": [ + -0.075, + -0.55, + -0.075 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 15, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 16, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 17, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 18, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 19, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 20, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 21, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 22, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 23, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 24, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 25, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 26, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 27, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 28, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 29, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 30, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.075, + 0.55, + 0.075 + ], + "min": [ + -0.075, + -0.55, + -0.075 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 31, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 32, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 33, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 34, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "generator": "Godot Engine v4.1.dev.custom_build@9f12e7b52d944281a39b7d3a33de6700c76cc23a", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 1296 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 24696 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 55896 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 79296 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 94896 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 136368 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 159768 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 190968 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 214368 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 229968 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 271440 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 294840 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 326040 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 349440 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 365040 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 406512 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 429912 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 461112 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 484512 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 500112 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 541584 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 564984 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 596184 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 619584 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 635184 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 676656 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 676944 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 677328 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 677616 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 677808 + } + ], + "buffers": [ + { + "byteLength": 677952, + "uri": "rope_railing0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.0500000007450581 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [0.15, 1.1, 0.15] + } + }, + { + "type": "capsule", + "capsule": { + "height": 0.5, + "radius": 0.05 + } + } + ] + }, + "OMI_physics_joint": { + "constraints": [ + { + "linearAxes": [ + 0, + 1, + 2 + ], + "stiffness": 0.300000011920929 + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_joint", + "OMI_physics_shape" + ], + "materials": [ + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 12, + "POSITION": 10, + "TANGENT": 11, + "TEXCOORD_0": 13 + }, + "indices": 14, + "material": 2, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 17, + "POSITION": 15, + "TANGENT": 16, + "TEXCOORD_0": 18 + }, + "indices": 19, + "material": 3, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 22, + "POSITION": 20, + "TANGENT": 21, + "TEXCOORD_0": 23 + }, + "indices": 24, + "material": 4, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 27, + "POSITION": 25, + "TANGENT": 26, + "TEXCOORD_0": 28 + }, + "indices": 29, + "material": 5, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 32, + "POSITION": 30, + "TANGENT": 31, + "TEXCOORD_0": 33 + }, + "indices": 34, + "material": 6, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1, + 4, + 5, + 8, + 9, + 12, + 13, + 16, + 17, + 20, + 21, + 24, + 25, + 28 + ], + "extensions": {}, + "name": "RopeRailing" + }, + { + "children": [ + 2 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "PoleLeft", + "translation": [ + -1.10000002384186, + 0.550000011920929, + 0.00000000000208164735449046 + ] + }, + { + "children": [ + 3 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "PoleShapeLeft" + }, + { + "extensions": {}, + "mesh": 0, + "name": "PoleMeshLeft" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 1, + "nodeB": 5 + } + }, + "name": "PinJoint1", + "translation": [ + -1.02999997138977, + 0.980000019073486, + 0.00000000000208164735449046 + ] + }, + { + "children": [ + 6 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentLeft", + "rotation": [ + 0.000000000000000123314947518882, + 0.000000000000000213587655926077, + -0.25881916284561, + 0.965925812721252 + ], + "translation": [ + -0.86000001430511, + 0.879999995231628, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 7 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeLeft", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 1, + "name": "RopeMeshLeft" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 5, + "nodeB": 9 + } + }, + "name": "PinJoint2", + "translation": [ + -0.66000002622604, + 0.759999990463257, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 10 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentLeftMiddle", + "rotation": [ + 0.000000000000000141460722589675, + 0.000000000000000202027284087501, + -0.17364804446697, + 0.984807789325714 + ], + "translation": [ + -0.44999998807907, + 0.680000007152557, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 11 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeLeftMiddle", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 2, + "name": "RopeMeshLeftMiddle" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 9, + "nodeB": 13 + } + }, + "name": "PinJoint3", + "translation": [ + -0.23000000417233, + 0.600000023841858, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 14 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentMiddle", + "translation": [ + 0.00000000000208164995657567, + 0.600000023841858, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 15 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeMiddle", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 3, + "name": "RopeMeshMiddle" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 13, + "nodeB": 17 + } + }, + "name": "PinJoint4", + "translation": [ + 0.230000004172325, + 0.600000023841858, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 18 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentRightMiddle", + "rotation": [ + 0.000000000000000202027284087501, + 0.000000000000000141460709354785, + 0.173648044466972, + 0.984807789325714 + ], + "translation": [ + 0.449999988079071, + 0.680000007152557, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 19 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeRightMiddle", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 4, + "name": "RopeMeshRightMiddle" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 17, + "nodeB": 21 + } + }, + "name": "PinJoint5", + "translation": [ + 0.660000026226044, + 0.759999990463257, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 22 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "RopeSegmentRight", + "rotation": [ + 0.000000000000000213587655926077, + 0.000000000000000123314960753772, + 0.258819162845612, + 0.965925812721252 + ], + "translation": [ + 0.860000014305115, + 0.879999995231628, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 23 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "RopeShapeRight", + "rotation": [ + 0.000000000000000246629630339969, + 0.00000000000000000000000935848017, + 0.70710676908493, + 0.70710676908493 + ] + }, + { + "extensions": {}, + "mesh": 5, + "name": "RopeMeshRight" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0 + ], + "nodeA": 21, + "nodeB": 25 + } + }, + "name": "PinJoint6", + "translation": [ + 1.02999997138977, + 0.980000019073486, + 0.00000000000208164735449046 + ] + }, + { + "children": [ + 26 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "PoleRight", + "translation": [ + 1.10000002384186, + 0.550000011920929, + 0.00000000000208164735449046 + ] + }, + { + "children": [ + 27 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "PoleShapeRight" + }, + { + "extensions": {}, + "mesh": 6, + "name": "PoleMeshRight" + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "translation": [ + 0.00000000000208164995657567, + 0.600000023841858, + 1.5 + ] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/OMI_physics_joint/examples/rope_railing0.bin b/OMI_physics_joint/examples/rope_railing0.bin new file mode 100644 index 0000000..bf45c6c Binary files /dev/null and b/OMI_physics_joint/examples/rope_railing0.bin differ diff --git a/OMI_physics_joint/examples/simple_joint.gltf b/OMI_physics_joint/examples/simple_joint.gltf new file mode 100644 index 0000000..792ab37 --- /dev/null +++ b/OMI_physics_joint/examples/simple_joint.gltf @@ -0,0 +1,604 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.125, + 0.5 + ], + "min": [ + -0.5, + -0.125, + -0.5 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "generator": "Godot Engine v4.1.dev.custom_build@9f12e7b52d944281a39b7d3a33de6700c76cc23a", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 23400 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 54600 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 78000 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 93600 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 135072 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 158472 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 189672 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 213072 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 228672 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 270144 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 270432 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 270816 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 271104 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 271296 + } + ], + "buffers": [ + { + "byteLength": 271440, + "uri": "simple_joint0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.0500000007450581 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "capsule", + "capsule": { + "height": 0.5, + "radius": 0.05 + } + }, + { + "type": "box", + "box": { + "size": [1, 0.25, 1] + } + } + ] + }, + "OMI_physics_joint": { + "constraints": [ + { + "linearAxes": [0, 1, 2] + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_joint", + "OMI_physics_shape" + ], + "materials": [ + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 12, + "POSITION": 10, + "TANGENT": 11, + "TEXCOORD_0": 13 + }, + "indices": 14, + "material": 2, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "extensions": { + "OMI_physics_joint": { + "constraints": [0], + "nodeA": 1, + "nodeB": 2 + } + }, + "name": "PinJoint", + "translation": [-0.23, 0.6, 0.0] + }, + { + "children": [4], + "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] + }, + { + "children": [6], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "BodyB", + "translation": [0.0, 0.6, 0.0] + }, + { + "children": [0, 1, 2, 8, 11], + "extensions": {}, + "name": "SimpleJoint" + }, + { + "children": [5], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ShapeA", + "rotation": [0.0, 0.0, 0.70710676908493, 0.70710676908493] + }, + { + "extensions": {}, + "mesh": 0, + "name": "MeshA" + }, + { + "children": [7], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ShapeB", + "rotation": [0.0, 0.0, 0.70710676908493, 0.70710676908493] + }, + { + "extensions": {}, + "mesh": 1, + "name": "MeshB" + }, + { + "children": [9], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "FloorBody", + "translation": [0.1, 0.0, 0.0] + }, + { + "children": [10], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "FloorShape" + }, + { + "extensions": {}, + "mesh": 2, + "name": "FloorMesh" + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "translation": [0.0, 0.5, 1.5] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [3] + } + ] +} diff --git a/OMI_physics_joint/examples/simple_joint0.bin b/OMI_physics_joint/examples/simple_joint0.bin new file mode 100644 index 0000000..e55afe0 Binary files /dev/null and b/OMI_physics_joint/examples/simple_joint0.bin differ diff --git a/OMI_physics_joint/examples/slider_ball.gltf b/OMI_physics_joint/examples/slider_ball.gltf new file mode 100644 index 0000000..040f2de --- /dev/null +++ b/OMI_physics_joint/examples/slider_ball.gltf @@ -0,0 +1,498 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0.05, + 0.05 + ], + "min": [ + -1, + -0.05, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.249717, + 0.25, + 0.249717 + ], + "min": [ + -0.249717, + -0.25, + -0.249717 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "generator": "Godot Engine v4.1.dev.custom_build@9f12e7b52d944281a39b7d3a33de6700c76cc23a", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 1296 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 27816 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 63176 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 89696 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 107376 + } + ], + "buffers": [ + { + "byteLength": 158064, + "uri": "slider_ball0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.0500000007450581 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [2, 0.1, 0.1] + } + }, + { + "type": "sphere", + "sphere": { + "radius": 0.25 + } + } + ] + }, + "OMI_physics_joint": { + "constraints": [ + { + "damping": 0.5, + "linearAxes": [ + 0 + ], + "lowerLimit": -1.75, + "stiffness": 1, + "upperLimit": 0.25 + }, + { + "linearAxes": [ + 1, + 2 + ], + "stiffness": 1 + }, + { + "angularAxes": [ + 0 + ], + "damping": 0, + "stiffness": 1 + }, + { + "angularAxes": [ + 1, + 2 + ], + "stiffness": 1 + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_joint", + "OMI_physics_shape" + ], + "materials": [ + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1, + 4, + 5, + 8 + ], + "extensions": {}, + "name": "PendulumBalls" + }, + { + "children": [ + 2 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "SliderLine" + }, + { + "children": [ + 3 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "SliderLineShape" + }, + { + "extensions": {}, + "mesh": 0, + "name": "SliderLineMesh" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0, + 1, + 2, + 3 + ], + "nodeA": 5, + "nodeB": 1 + } + }, + "name": "SliderJoint", + "translation": [ + -0.75, + 0.00000000000208164995657567, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 6 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "angularVelocity": [ + 0.0174532998353243, + 0.0174532998353243, + 0.0174532998353243 + ], + "linearVelocity": [ + 1, + 0.00000000000208164995657567, + 0.00000000000208164995657567 + ], + "type": "dynamic" + } + } + }, + "name": "Ball", + "translation": [ + -0.75, + 0.00000000000208164995657567, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 7 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "BallShape" + }, + { + "extensions": {}, + "mesh": 1, + "name": "BallMesh" + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "translation": [ + 0.00000000000208164995657567, + 0.00000000000208164995657567, + 1.5 + ] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/OMI_physics_joint/examples/slider_ball0.bin b/OMI_physics_joint/examples/slider_ball0.bin new file mode 100644 index 0000000..c75c0d7 Binary files /dev/null and b/OMI_physics_joint/examples/slider_ball0.bin differ diff --git a/OMI_physics_joint/examples/swing_and_slide.gltf b/OMI_physics_joint/examples/swing_and_slide.gltf new file mode 100644 index 0000000..929547c --- /dev/null +++ b/OMI_physics_joint/examples/swing_and_slide.gltf @@ -0,0 +1,643 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0.05, + 0.05 + ], + "min": [ + -1, + -0.05, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.249717, + 0.25, + 0.249717 + ], + "min": [ + -0.249717, + -0.25, + -0.249717 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 0.998868, + 1, + 0.998868 + ], + "min": [ + -0.998869, + -1, + -0.998867 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 2210, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 12672, + "max": [ + 2209 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.5, + 0.05 + ], + "min": [ + -0.05, + -0.5, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "generator": "Godot Engine v4.1.dev.custom_build@9f12e7b52d944281a39b7d3a33de6700c76cc23a", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 1296 + }, + { + "buffer": 0, + "byteLength": 35360, + "byteOffset": 27816 + }, + { + "buffer": 0, + "byteLength": 26520, + "byteOffset": 63176 + }, + { + "buffer": 0, + "byteLength": 17680, + "byteOffset": 89696 + }, + { + "buffer": 0, + "byteLength": 50688, + "byteOffset": 107376 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 158064 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 181464 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 212664 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 236064 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 251664 + } + ], + "buffers": [ + { + "byteLength": 293136, + "uri": "swing_and_slide0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.0500000007450581 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [2, 0.1, 0.1] + } + }, + { + "type": "sphere", + "sphere": { + "radius": 0.25 + } + } + ] + }, + "OMI_physics_joint": { + "constraints": [ + { + "linearAxes": [ + 0 + ], + "lowerLimit": -0.25, + "stiffness": 0.699999988079071, + "upperLimit": 1.75 + }, + { + "linearAxes": [ + 1, + 2 + ], + "stiffness": 0.699999988079071 + }, + { + "angularAxes": [ + 0, + 1 + ], + "stiffness": 0.5 + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_joint", + "OMI_physics_shape" + ], + "materials": [ + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 12, + "POSITION": 10, + "TANGENT": 11, + "TEXCOORD_0": 13 + }, + "indices": 14, + "material": 2, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1, + 4, + 5, + 9 + ], + "extensions": {}, + "name": "SwingAndSlide" + }, + { + "children": [ + 2 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "TopBody", + "translation": [ + 0.00000000000208164995657567, + 1, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 3 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "TopBodyShape" + }, + { + "extensions": {}, + "mesh": 0, + "name": "TopBodyMesh" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [ + 0, + 1, + 2 + ], + "nodeA": 1, + "nodeB": 5 + } + }, + "name": "CustomJoint", + "translation": [ + -0.75, + 1, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 6, + 8 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "Ball", + "rotation": [ + 0.000000000000000094381003489835, + 0.000000000000000227855912687686, + -0.38268345594406, + 0.923879563808441 + ], + "translation": [ + -1.60000002384186, + 0.150000005960464, + 0.00000000000208164995657567 + ] + }, + { + "children": [ + 7 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "BallShape" + }, + { + "extensions": {}, + "mesh": 1, + "name": "BallMesh" + }, + { + "extensions": {}, + "mesh": 2, + "name": "BallStringMesh", + "translation": [ + 0.00000000000208164995657567, + 0.699999988079071, + 0.00000000000208164995657567 + ] + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "translation": [ + 0.00000000000208164995657567, + 0.00000000000208164995657567, + 1.5 + ] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} diff --git a/OMI_physics_joint/examples/swing_and_slide0.bin b/OMI_physics_joint/examples/swing_and_slide0.bin new file mode 100644 index 0000000..807678c Binary files /dev/null and b/OMI_physics_joint/examples/swing_and_slide0.bin differ diff --git a/OMI_physics_joint/examples/weld_joint.gltf b/OMI_physics_joint/examples/weld_joint.gltf new file mode 100644 index 0000000..175356a --- /dev/null +++ b/OMI_physics_joint/examples/weld_joint.gltf @@ -0,0 +1,611 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 0.05, + 0.25, + 0.05 + ], + "min": [ + -0.05, + -0.25, + -0.05 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000022, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 1950, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5125, + "count": 10368, + "max": [ + 1949 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 10, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.125, + 0.5 + ], + "min": [ + -0.5, + -0.125, + -0.5 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 11, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 12, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 13, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 14, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "generator": "Godot Engine v4.1.dev.custom_build@9f12e7b52d944281a39b7d3a33de6700c76cc23a", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 23400 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 54600 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 78000 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 93600 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 135072 + }, + { + "buffer": 0, + "byteLength": 31200, + "byteOffset": 158472 + }, + { + "buffer": 0, + "byteLength": 23400, + "byteOffset": 189672 + }, + { + "buffer": 0, + "byteLength": 15600, + "byteOffset": 213072 + }, + { + "buffer": 0, + "byteLength": 41472, + "byteOffset": 228672 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 270144 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 270432 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 270816 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 271104 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 271296 + } + ], + "buffers": [ + { + "byteLength": 271440, + "uri": "weld_joint0.bin" + } + ], + "cameras": [ + { + "perspective": { + "yfov": 1.30899691581726, + "zfar": 4000, + "znear": 0.05 + }, + "type": "perspective" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "capsule", + "capsule": { + "height": 0.5, + "radius": 0.05 + } + }, + { + "type": "box", + "box": { + "size": [1, 0.25, 1] + } + } + ] + }, + "OMI_physics_joint": { + "constraints": [ + { + "linearAxes": [0, 1, 2], + "angularAxes": [0, 1, 2] + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_joint", + "OMI_physics_shape" + ], + "materials": [ + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + }, + { + "extensions": {}, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 7, + "POSITION": 5, + "TANGENT": 6, + "TEXCOORD_0": 8 + }, + "indices": 9, + "material": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 12, + "POSITION": 10, + "TANGENT": 11, + "TEXCOORD_0": 13 + }, + "indices": 14, + "material": 2, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [1, 4, 5, 8, 11], + "extensions": {}, + "name": "SimpleJoint" + }, + { + "children": [2], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "BodyA", + "rotation": [0.000859406718518585, 0.000151536631165072, -0.17364810407162, 0.984807372093201], + "translation": [-0.45, 0.68, 0.0] + }, + { + "children": [3], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ShapeA", + "rotation": [0.0, 0.0, 0.70710676908493, 0.70710676908493] + }, + { + "extensions": {}, + "mesh": 0, + "name": "MeshA" + }, + { + "extensions": { + "OMI_physics_joint": { + "constraints": [0], + "nodeA": 1, + "nodeB": 5 + } + }, + "name": "WeldJoint", + "translation": [-0.23, 0.6, 0.0] + }, + { + "children": [ + 6 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "dynamic" + } + } + }, + "name": "BodyB", + "translation": [0.0, 0.6, 0.0] + }, + { + "children": [ + 7 + ], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ShapeB", + "rotation": [0.0, 0.0, 0.70710676908493, 0.70710676908493] + }, + { + "extensions": {}, + "mesh": 1, + "name": "MeshB" + }, + { + "children": [ + 9 + ], + "extensions": { + "OMI_physics_body": { + "motion": { + "type": "static" + } + } + }, + "name": "FloorBody", + "translation": [0.1, 0.0, 0.0] + }, + { + "children": [10], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 1 + } + } + }, + "name": "FloorShape" + }, + { + "extensions": {}, + "mesh": 2, + "name": "FloorMesh" + }, + { + "camera": 0, + "extensions": {}, + "name": "Camera", + "translation": [0.0, 0.5, 1.5] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [0] + } + ] +} diff --git a/OMI_physics_joint/examples/weld_joint0.bin b/OMI_physics_joint/examples/weld_joint0.bin new file mode 100644 index 0000000..e55afe0 Binary files /dev/null and b/OMI_physics_joint/examples/weld_joint0.bin differ diff --git a/OMI_physics_joint/index.html b/OMI_physics_joint/index.html new file mode 100644 index 0000000..a873833 --- /dev/null +++ b/OMI_physics_joint/index.html @@ -0,0 +1,1205 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_physics_joint - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_physics_joint

+

Contributors

+
    +
  • Aaron Franke, The Mirror Megaverse Inc.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Depends on the OMI_physics_body spec, which depends on the OMI_physics_shape spec.

+

Overview

+

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.

+

Example:

+

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.

+

glTF Schema Updates

+

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.

+

Joint Constraint Property Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
linearAxesnumber[0..3]The axes to constrain. Can only contain 0 (X), 1 (Y), or 2 (Z).[] (empty array)
angularAxesnumber[0..3]The axes to constrain. Can only contain 0 (X), 1 (Y), or 2 (Z).[] (empty array)
lowerLimitnumberThe lower limit of the constraint, in meters or radians.0.0
upperLimitnumberThe lower limit of the constraint, in meters or radians.0.0
stiffnessnumberThe stiffness of the limits, how hard to spring back when beyond.Infinity
dampingnumberWhen 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.

+

Linear Axes

+

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.

+

Angular Axes

+

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.

+

Lower Limit

+

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.

+

Upper Limit

+

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.

+

Stiffness

+

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.

+

Damping

+

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.

+

Node Property Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
constraintsnumber[]Array of indices in the document-level constraint array. Must be integers.Required, no default
nodeAnumberNode index of one physics body used by the joint. Must be an integer.Required, no default
nodeBnumberNode index of one physics body used by the joint. Must be an integer.Required, no default
+

Constraints

+

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.

+

Node A and Node B

+

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.

+

Document Property Summary

+ + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
constraintsobject[]Array of joint constraint objects.Required, no default
+

Special Cases

+

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 nameDescription
Fixed / Weld JointAll axes fixed.
Pin JointAll linear axes fixed, all angular axes free.
Hinge JointOne angular axis constrained with non-equal lower and upper limits, the rest are fixed.
Slider JointOne linear axis and optionally the angular axis around it constrained with non-equal lower and upper limits, the rest are fixed.
+

JSON Schema

+

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.

+

Known Implementations

+
    +
  • Godot Engine add-on
  • +
+

Resources:

+
    +
  • Godot Joint3D https://docs.godotengine.org/en/latest/classes/class_generic6dofjoint3d.html
  • +
  • Unity Joints https://docs.unity3d.com/Manual/Joints.html
  • +
  • Wikipedia Stiffness https://en.wikipedia.org/wiki/Stiffness
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_joint/schema/glTF.OMI_physics_joint.joint_constraint.schema.json b/OMI_physics_joint/schema/glTF.OMI_physics_joint.joint_constraint.schema.json new file mode 100644 index 0000000..dc3d8d3 --- /dev/null +++ b/OMI_physics_joint/schema/glTF.OMI_physics_joint.joint_constraint.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_joint.joint_constraint.schema.json", + "title": "OMI_physics_joint Joint Constraint", + "type": "object", + "description": "Parameters describing a physics joint constraint / limit.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "linearAxes": { + "type": "array", + "description": "The linear axes to constrain. Can only contain 0 (X), 1 (Y), or 2 (Z).", + "default": [] + }, + "angularAxes": { + "type": "array", + "description": "The angular axes to constrain. Can only contain 0 (X), 1 (Y), or 2 (Z).", + "default": [] + }, + "lowerLimit": { + "type": "number", + "description": "The lower limit of the constraint, in meters or radians.", + "default": 0.0 + }, + "upperLimit": { + "type": "number", + "description": "The upper limit of the constraint, in meters or radians.", + "default": 0.0 + }, + "stiffness": { + "type": "number", + "description": "When beyond the limits, the stiffness of springing back.", + "default": "Infinity" + }, + "damping": { + "type": "number", + "description": "When beyond the limits, the damping of the springing back motion.", + "default": 1.0 + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_joint/schema/glTF.OMI_physics_joint.schema.json b/OMI_physics_joint/schema/glTF.OMI_physics_joint.schema.json new file mode 100644 index 0000000..50af464 --- /dev/null +++ b/OMI_physics_joint/schema/glTF.OMI_physics_joint.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_joint.schema.json", + "title": "OMI_physics_joint glTF Document Extension", + "type": "object", + "description": "Top level joint resources.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "constraints": { + "description": "An array of physics joint constraints that can be referenced by nodes.", + "type": "array", + "items": { + "type": "object", + "$ref": "joint_constraint.schema.json" + }, + "minItems": 1 + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "constraints" + ] +} diff --git a/OMI_physics_joint/schema/node.OMI_physics_joint.schema.json b/OMI_physics_joint/schema/node.OMI_physics_joint.schema.json new file mode 100644 index 0000000..37ee9d6 --- /dev/null +++ b/OMI_physics_joint/schema/node.OMI_physics_joint.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_physics_joint.schema.json", + "title": "OMI_physics_joint glTF Node Extension", + "type": "object", + "description": "Node extension that references a physics joint.", + "allOf": [ { "$ref": "glTFProperty.schema.json" } ], + "properties": { + "constraints": { + "description": "An array of ids of joint constraints that are applied to the node.", + "type": "array", + "items": { + "type": "number", + "$ref": "glTFid.schema.json" + }, + "minItems": 1 + }, + "nodeA": { + "description": "The id of the first node.", + "type": "number", + "$ref": "glTFid.schema.json" + }, + "nodeB": { + "description": "The id of the second node.", + "type": "number", + "$ref": "glTFid.schema.json" + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "constraints" + ] +} diff --git a/OMI_physics_shape/examples/box_collider.gltf b/OMI_physics_shape/examples/box_collider.gltf new file mode 100644 index 0000000..5939743 --- /dev/null +++ b/OMI_physics_shape/examples/box_collider.gltf @@ -0,0 +1,35 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [1, 1, 1] + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "BoxShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/capsule_collider.gltf b/OMI_physics_shape/examples/capsule_collider.gltf new file mode 100644 index 0000000..2937462 --- /dev/null +++ b/OMI_physics_shape/examples/capsule_collider.gltf @@ -0,0 +1,36 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "capsule", + "capsule": { + "height": 2.0, + "radius": 0.5 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "CapsuleShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/convex/convex_hull.gltf b/OMI_physics_shape/examples/convex/convex_hull.gltf new file mode 100644 index 0000000..9c93cf9 --- /dev/null +++ b/OMI_physics_shape/examples/convex/convex_hull.gltf @@ -0,0 +1,237 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5125, + "count": 24, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 0.999969, + 0, + 0.999969, + 1 + ], + "min": [ + -0.999969, + -0.007936, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 0.894377, + 0.447188, + 1 + ], + "min": [ + -0.894377, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 20, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5125, + "count": 24, + "max": [ + 19 + ], + "min": [ + 2 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 96, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 240, + "byteOffset": 384 + }, + { + "buffer": 0, + "byteLength": 320, + "byteOffset": 624 + }, + { + "buffer": 0, + "byteLength": 240, + "byteOffset": 944 + }, + { + "buffer": 0, + "byteLength": 160, + "byteOffset": 1184 + }, + { + "buffer": 0, + "byteLength": 96, + "byteOffset": 1344 + } + ], + "buffers": [ + { + "byteLength": 1440, + "uri": "convex_hull0.bin" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "convex", + "convex": { + "mesh": 0 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "mode": 4 + } + ] + }, + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 4, + "POSITION": 2, + "TANGENT": 3, + "TEXCOORD_0": 5 + }, + "indices": 6, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [1], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ConvexHullShape" + }, + { + "extensions": {}, + "mesh": 1, + "name": "ConvexMesh" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/convex/convex_hull0.bin b/OMI_physics_shape/examples/convex/convex_hull0.bin new file mode 100644 index 0000000..6dd4d73 Binary files /dev/null and b/OMI_physics_shape/examples/convex/convex_hull0.bin differ diff --git a/OMI_physics_shape/examples/convex/convex_hull_only.gltf b/OMI_physics_shape/examples/convex/convex_hull_only.gltf new file mode 100644 index 0000000..4e05c43 --- /dev/null +++ b/OMI_physics_shape/examples/convex/convex_hull_only.gltf @@ -0,0 +1,103 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5125, + "count": 24, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 96, + "byteOffset": 288 + } + ], + "buffers": [ + { + "byteLength": 384, + "uri": "convex_hull_only0.bin" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "convex", + "convex": { + "mesh": 0 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ConvexHullShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/convex/convex_hull_only0.bin b/OMI_physics_shape/examples/convex/convex_hull_only0.bin new file mode 100644 index 0000000..fc706fe Binary files /dev/null and b/OMI_physics_shape/examples/convex/convex_hull_only0.bin differ diff --git a/OMI_physics_shape/examples/cylinder_collider.gltf b/OMI_physics_shape/examples/cylinder_collider.gltf new file mode 100644 index 0000000..d1a416c --- /dev/null +++ b/OMI_physics_shape/examples/cylinder_collider.gltf @@ -0,0 +1,36 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "cylinder", + "cylinder": { + "height": 2.0, + "radius": 0.5 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "CylinderShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/default_box.gltf b/OMI_physics_shape/examples/default_box.gltf new file mode 100644 index 0000000..f4f7c91 --- /dev/null +++ b/OMI_physics_shape/examples/default_box.gltf @@ -0,0 +1,32 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box" + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "DefaultBoxShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/sphere_collider.gltf b/OMI_physics_shape/examples/sphere_collider.gltf new file mode 100644 index 0000000..b473e11 --- /dev/null +++ b/OMI_physics_shape/examples/sphere_collider.gltf @@ -0,0 +1,35 @@ +{ + "asset": { + "version": "2.0" + }, + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "sphere", + "sphere": { + "radius": 0.5 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "SphereShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/trimesh/concave_trimesh.gltf b/OMI_physics_shape/examples/trimesh/concave_trimesh.gltf new file mode 100644 index 0000000..9eb7754 --- /dev/null +++ b/OMI_physics_shape/examples/trimesh/concave_trimesh.gltf @@ -0,0 +1,196 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 0, + 1, + 1 + ], + "min": [ + -1, + -0.000015, + -1, + 1 + ], + "normalized": false, + "type": "VEC4" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1, + 1 + ], + "min": [ + 0, + 0 + ], + "normalized": false, + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 384, + "byteOffset": 288 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 672 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 960 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 1152 + } + ], + "buffers": [ + { + "byteLength": 1296, + "uri": "concave_trimesh0.bin" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "trimesh", + "trimesh": { + "mesh": 0 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.99999988079071, + 0.99999988079071, + 0.99999988079071, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + } + } + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 0, + "TANGENT": 1, + "TEXCOORD_0": 3 + }, + "indices": 4, + "material": 0, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [1], + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ConcaveTrimeshShape" + }, + { + "extensions": {}, + "mesh": 0, + "name": "BoxMeshInstance" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/trimesh/concave_trimesh0.bin b/OMI_physics_shape/examples/trimesh/concave_trimesh0.bin new file mode 100644 index 0000000..969920a Binary files /dev/null and b/OMI_physics_shape/examples/trimesh/concave_trimesh0.bin differ diff --git a/OMI_physics_shape/examples/trimesh/concave_trimesh_only.gltf b/OMI_physics_shape/examples/trimesh/concave_trimesh_only.gltf new file mode 100644 index 0000000..328a3d1 --- /dev/null +++ b/OMI_physics_shape/examples/trimesh/concave_trimesh_only.gltf @@ -0,0 +1,103 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 36, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "normalized": false, + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5125, + "count": 36, + "max": [ + 35 + ], + "min": [ + 0 + ], + "normalized": false, + "type": "SCALAR" + } + ], + "asset": { + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 432, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 432 + } + ], + "buffers": [ + { + "byteLength": 576, + "uri": "concave_trimesh_only0.bin" + } + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "trimesh", + "trimesh": { + "mesh": 0 + } + } + ] + } + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape" + ], + "meshes": [ + { + "extras": { + "targetNames": [] + }, + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "extensions": { + "OMI_physics_body": { + "collider": { + "shape": 0 + } + } + }, + "name": "ConcaveTrimeshShape" + } + ], + "scene": 0, + "scenes": [{ "nodes": [0] }] +} diff --git a/OMI_physics_shape/examples/trimesh/concave_trimesh_only0.bin b/OMI_physics_shape/examples/trimesh/concave_trimesh_only0.bin new file mode 100644 index 0000000..a8baead Binary files /dev/null and b/OMI_physics_shape/examples/trimesh/concave_trimesh_only0.bin differ diff --git a/OMI_physics_shape/index.html b/OMI_physics_shape/index.html new file mode 100644 index 0000000..4219ce2 --- /dev/null +++ b/OMI_physics_shape/index.html @@ -0,0 +1,1125 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_physics_shape - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_physics_shape

+

Contributors

+
    +
  • Aaron Franke, The Mirror Megaverse Inc.
  • +
  • Robert Long, The Matrix.org Foundation
  • +
  • Mauve Signweaver, Mauve Software Inc.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Does nothing on its own. Designed to be used together with the OMI_physics_body spec.

+

Overview

+

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.

+

Example:

+

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.

+

glTF Schema Updates

+

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.

+

Property Summary

+

The main data structure glTF.OMI_physics_shape.shape.schema.json defines a type property.

+ + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
typestringThe 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.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault valueValid on
sizenumber[3]The size of the box shape in meters.[1.0, 1.0, 1.0]Box
radiusnumberThe radius of the shape in meters.0.5Sphere, capsule, cylinder
heightnumberThe height of the shape in meters.2.0Capsule, cylinder
meshnumberThe index of the glTF mesh in the document to use as a mesh shape.-1Trimesh, 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.

+

Shape Types

+

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.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ShapeUnityUnrealGodotBlenderBullet (Ammo, Panda3D, etc)
BoxBoxBoxBoxShape3DBoxBox Shape
SphereSphereSphereSphereShape3DSphereSphere Shape
CapsuleCapsuleCapsuleCapsuleShape3DCapsuleCapsule Shape
CylinderApproximationApproximationCylinderShape3DCylinderCylinder Shape
ConvexMesh (Convex)ConvexConvexPolygonShape3DConvex HullConvex Shape
TrimeshMeshMeshConcavePolygonShape3DMeshMesh Shape
+

Box

+

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

+

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

+

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

+

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

+

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

+

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.

+

JSON Schema

+

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.

+

Known Implementations

+
    +
  • Godot Engine: https://github.com/godotengine/godot/pull/78967
  • +
+

Resources:

+
    +
  • Godot Shapes: https://docs.godotengine.org/en/latest/classes/class_shape3d.html
  • +
  • Unity Colliders: https://docs.unity3d.com/Manual/CollidersOverview.html
  • +
  • Unreal Engine Collision Shapes: https://docs.unrealengine.com/4.27/en-US/API/Runtime/PhysicsCore/FCollisionShape/
  • +
  • Unreal Engine Mesh Collisions: https://docs.unrealengine.com/4.27/en-US/WorkingWithContent/Types/StaticMeshes/HowTo/SettingCollision/
  • +
  • Blender Collisions: https://docs.blender.org/manual/en/latest/physics/rigid_body/properties/collisions.html
  • +
  • Mozilla Hubs ammo-shape: https://github.com/MozillaReality/hubs-blender-exporter/blob/bb28096159e1049b6b80da00b1ae1534a6ca0855/default-config.json#L608
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.schema.json new file mode 100644 index 0000000..485e4d8 --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.schema.json", + "title": "OMI_physics_shape glTF Document Extension", + "type": "object", + "description": "Top level shape resources.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "shapes": { + "type": "array", + "description": "An array of shapes that can be referenced by nodes.", + "items": { + "type": "object", + "$ref": "shape.schema.json" + }, + "minItems": 1 + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "shapes" + ] +} diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.box.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.box.schema.json new file mode 100644 index 0000000..d46e842 --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.box.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.shape.box.schema.json", + "title": "OMI_physics_shape Box Shape", + "type": "object", + "description": "Parameters describing a box physics shape.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "size": { + "type": "array", + "description": "Three number array representing the width, height, and depth of the box (x, y, z)." + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.capsule.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.capsule.schema.json new file mode 100644 index 0000000..fbbdd5b --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.capsule.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.shape.capsule.schema.json", + "title": "OMI_physics_shape Capsule Shape", + "type": "object", + "description": "Parameters describing a capsule physics shape.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "radius": { + "type": "number", + "description": "The radius to use for the capsule shape." + }, + "height": { + "type": "number", + "description": "The full height of the capsule shape." + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.convex.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.convex.schema.json new file mode 100644 index 0000000..9dcfdf2 --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.convex.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.shape.convex.schema.json", + "title": "OMI_physics_shape Convex Shape", + "type": "object", + "description": "Parameters describing a convex hull physics shape.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "mesh": { + "allOf": [ { "$ref": "glTFid.schema.json" } ], + "description": "A reference to the mesh in the glTF meshes array to use for this convex hull shape. The mesh MUST be a trimesh and have at least one triangle." + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.cylinder.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.cylinder.schema.json new file mode 100644 index 0000000..ee0d28a --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.cylinder.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.shape.cylinder.schema.json", + "title": "OMI_physics_shape Cylinder Shape", + "type": "object", + "description": "Parameters describing a cylinder physics shape.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "radius": { + "type": "number", + "description": "The radius to use for the cylinder shape." + }, + "height": { + "type": "number", + "description": "The height of the cylinder shape." + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.schema.json new file mode 100644 index 0000000..d6e6d84 --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.schema.json @@ -0,0 +1,119 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.shape.schema.json", + "title": "OMI_physics_shape Shape Resource", + "type": "object", + "description": "Defines a physics shape resource.", + "allOf": [ { "$ref" : "glTFChildOfRootProperty.schema.json" } ], + "properties": { + "type": { + "type": "string", + "description": "The type of shape this node represents.", + "enum": [ + "box", + "sphere", + "capsule", + "cylinder", + "convex", + "trimesh" + ] + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "type" + ], + "oneOf": [ + { + "required": [ + "type", + "box" + ], + "properties": { + "type": { + "const": "box" + }, + "box": { + "type": "object", + "$ref": "glTF.OMI_physics_shape.shape.box.schema.json" + } + } + }, + { + "required": [ + "type", + "sphere" + ], + "properties": { + "type": { + "const": "sphere" + }, + "sphere": { + "type": "object", + "$ref": "glTF.OMI_physics_shape.shape.sphere.schema.json" + } + } + }, + { + "required": [ + "type", + "capsule" + ], + "properties": { + "type": { + "const": "capsule" + }, + "capsule": { + "type": "object", + "$ref": "glTF.OMI_physics_shape.shape.capsule.schema.json" + } + } + }, + { + "required": [ + "type", + "cylinder" + ], + "properties": { + "type": { + "const": "cylinder" + }, + "cylinder": { + "type": "object", + "$ref": "glTF.OMI_physics_shape.shape.cylinder.schema.json" + } + } + }, + { + "required": [ + "type", + "convex" + ], + "properties": { + "type": { + "const": "convex" + }, + "convex": { + "type": "object", + "$ref": "glTF.OMI_physics_shape.shape.convex.schema.json" + } + } + }, + { + "required": [ + "type", + "trimesh" + ], + "properties": { + "type": { + "const": "trimesh" + }, + "trimesh": { + "type": "object", + "$ref": "glTF.OMI_physics_shape.shape.trimesh.schema.json" + } + } + } + ] +} diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.sphere.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.sphere.schema.json new file mode 100644 index 0000000..064ac2a --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.sphere.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.shape.sphere.schema.json", + "title": "OMI_physics_shape Sphere Shape", + "type": "object", + "description": "Parameters describing a sphere physics shape.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "radius": { + "type": "number", + "description": "The radius to use for the sphere shape." + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.trimesh.schema.json b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.trimesh.schema.json new file mode 100644 index 0000000..3c67d2e --- /dev/null +++ b/OMI_physics_shape/schema/glTF.OMI_physics_shape.shape.trimesh.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "glTF.OMI_physics_shape.shape.trimesh.schema.json", + "title": "OMI_physics_shape Trimesh Shape", + "type": "object", + "description": "Parameters describing a concave trimesh physics shape.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "mesh": { + "allOf": [ { "$ref": "glTFid.schema.json" } ], + "description": "A reference to the mesh in the glTF meshes array to use for this concave trimesh shape. The mesh MUST be a trimesh and have at least one triangle." + }, + "extensions": { }, + "extras": { } + } +} diff --git a/OMI_seat/examples/recliner_chair_full.gltf b/OMI_seat/examples/recliner_chair_full.gltf new file mode 100644 index 0000000..8c747f7 --- /dev/null +++ b/OMI_seat/examples/recliner_chair_full.gltf @@ -0,0 +1,118 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v3.4.50 (manually modified with seat data)", + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_seat" + ], + "scene": 0, + "scenes": [ + { + "nodes": [1] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "ReclinerChairFullMesh" + }, + { + "children": [ + 0 + ], + "name": "ReclinerChairFull", + "extensions": { + "OMI_seat": { + "back": [0, 0.33, -0.247], + "foot": [0, -0.17, 0.8], + "knee": [0, 0.33, 0.3], + "angle": 2.0943951 + } + } + } + ], + "meshes": [ + { + "name": "ReclinerChairFullMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 184, + "max": [ + 0.30000001192092896, + 0.9351986050605774, + 0.6531219482421875 + ], + "min": [ + -0.30000001192092896, + -0.25, + -0.6483013033866882 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 184, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 184, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 276, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 2208, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1472, + "byteOffset": 2208, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 2208, + "byteOffset": 3680, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 552, + "byteOffset": 5888, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 6440, + "uri": "data:application/octet-stream;base64,mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvnE9ij6ZmZk+mZmZvnE9ij6ZmZk+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvnE9ij6SwnU+mZmZvnE9ij6SwnU+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vnE9ij6SwnU+ksJ1vnE9ij6SwnU+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PnE9ij6SwnU+ksJ1PnE9ij6SwnU+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPnE9ij6ZmZk+mZmZPnE9ij6ZmZk+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPnE9ij6SwnU+mZmZPnE9ij6SwnU+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvnE9ij6SwnW+mZmZvnE9ij6SwnW+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvnE9ij6ZmZm+mZmZvnE9ij6ZmZm+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vnE9ij6SwnW+ksJ1vnE9ij6SwnW+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PnE9ij6SwnW+ksJ1PnE9ij6SwnW+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PnE9ij6ZmZm+ksJ1PnE9ij6ZmZm+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPnE9ij6SwnW+mZmZPnE9ij6SwnW+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPnE9ij6ZmZm+mZmZPnE9ij6ZmZm+AACAvlXH1D7ZuZe+AACAvlXH1D7ZuZe+AACAvlXH1D7ZuZe+AACAvi1pbz+6qRi/AACAvi1pbz+6qRi/AACAvi1pbz+6qRi/AACAvi1rxT6LVLK+AACAvi1rxT6LVLK+AACAvi1rxT6LVLK+AACAvhm7Zz8T9yW/AACAvhm7Zz8T9yW/AACAvhm7Zz8T9yW/AACAPlXH1D7ZuZe+AACAPlXH1D7ZuZe+AACAPlXH1D7ZuZe+AACAPi1pbz+6qRi/AACAPi1pbz+6qRi/AACAPi1pbz+6qRi/AACAPi1rxT6LVLK+AACAPi1rxT6LVLK+AACAPi1rxT6LVLK+AACAPhm7Zz8T9yW/AACAPhm7Zz8T9yW/AACAPhm7Zz8T9yW/AAAAvilwqD59QHy+AAAAvilwqD59QHy+AAAAvlXH1D7ZuZe+AAAAvlXH1D7ZuZe+AAAAvgEUmT7xupi+AAAAvgEUmT7xupi+AAAAvi1rxT6LVLK+AAAAvi1rxT6LVLK+AAAAPilwqD59QHy+AAAAPilwqD59QHy+AAAAPlXH1D7ZuZe+AAAAPlXH1D7ZuZe+AAAAPgEUmT7xupi+AAAAPgEUmT7xupi+AAAAPi1rxT6LVLK+AAAAPi1rxT6LVLK+AACAvigCXj5U3Kc+AACAvigCXj5U3Kc+AACAvigCXj5U3Kc+AACAvsg+h72LVhw/AACAvsg+h72LVhw/AACAvsg+h72LVhw/AACAvvy5hD49lb0+AACAvvy5hD49lb0+AACAvvy5hD49lb0+AACAvoBswbwAMyc/AACAvoBswbwAMyc/AACAvoBswbwAMyc/AACAPigCXj5U3Kc+AACAPigCXj5U3Kc+AACAPigCXj5U3Kc+AACAPsg+h72LVhw/AACAPsg+h72LVhw/AACAPsg+h72LVhw/AACAPvy5hD49lb0+AACAPvy5hD49lb0+AACAPvy5hD49lb0+AACAPoBswbwAMyc/AACAPoBswbwAMyc/AACAPoBswbwAMyc/AAAAvkU1kz4jqIM+AAAAvkU1kz4jqIM+AAAAvigCXj5U3Kc+AAAAvigCXj5U3Kc+AAAAvi7uqD4LYZk+AAAAvi7uqD4LYZk+AAAAvvy5hD49lb0+AAAAvvy5hD49lb0+AAAAPkU1kz4jqIM+AAAAPkU1kz4jqIM+AAAAPigCXj5U3Kc+AAAAPigCXj5U3Kc+AAAAPi7uqD4LYZk+AAAAPi7uqD4LYZk+AAAAPvy5hD49lb0+AAAAPvy5hD49lb0+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAAA+AAAAPwAAwD4AAEA/AADAPgAAQD8AACA/AABAPwAAID8AAEA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA+AACAPgAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAGA/AACAPgAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAA2LNdv////z4AAAAAAAAAP9azXT8AAIC/AAAAAAAAAIAAAAAAAAAAP9azXT8AAIC/AAAAAAAAAIAAAAAA2LNdP////74AAAAA2LNdv////z4AAIC/AAAAAAAAAIAAAAAAAAAAv9azXb8AAIC/AAAAAAAAAIAAAAAAAAAAv9azXb8AAAAA2LNdP////74AAAAA2LNdv////z4AAAAAAAAAP9azXT8AAIA/AAAAAAAAAIAAAAAAAAAAP9azXT8AAAAA2LNdP////74AAIA/AAAAAAAAAIAAAAAA2LNdv////z4AAAAAAAAAv9azXb8AAIA/AAAAAAAAAIAAAAAAAAAAv9azXb8AAAAA2LNdP////74AAIA/AAAAAAAAAIAAAAAAAQAAP9azXT8AAIC/AAAAAAAAAIAAAAAAAQAAP9azXT8AAIC/AAAAAAAAAIAAAIC/AAAAAAAAAIAAAAAA////vtizXb8AAIC/AAAAAAAAAIAAAAAA////vtizXb8AAAAAAQAAP9azXT8AAIA/AAAAAAAAAIAAAAAAAQAAP9azXT8AAIA/AAAAAAAAAIAAAAAA////vtizXb8AAIA/AAAAAAAAAIAAAAAA////vtizXb8AAIA/AAAAAAAAAIAAAAAA9wQ1P+4ENb8AAAAA8AQ1v/UENb8AAIC/AAAAAAAAAIAAAAAA8AQ1v/UENb8AAIC/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA9wQ1P+4ENb8AAIC/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAIC/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAAAA8wQ1v/MENT8AAAAA8AQ1v/UENb8AAAAA9wQ1P+4ENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA8AQ1v/UENb8AAIA/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAAAA9wQ1P+4ENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA8wQ1P/QENT8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIC/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIC/AAAAAAAAAIAAAIC/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIC/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAAAA8wQ1v/MENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIA/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIA/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIA/AAAAAAAAAIACAAQACQACAAkABwAIAAoAFQAIABUAEwAUABcAEQAUABEADgANAA8AAwANAAMAAQAGABIADAAGAAwAAAAWAAsABQAWAAUAEAAaABwAIAAaACAAHgAfACEAKgAfACoAKAApACsAJgApACYAJAAjACUAGwAjABsAGQAdACcAIgAdACIAGABHAEkAUgBHAFIAUAAuADAANAAuADQAMgAzADUAPgAzAD4APAA9AD8AOgA9ADoAOAA3ADkALwA3AC8ALQAxADsANgAxADYALABCAEQASABCAEgARgBRAFMATgBRAE4ATABLAE0AQwBLAEMAQQBFAE8ASgBFAEoAQABWAFgAXABWAFwAWgBbAF0AZgBbAGYAZABlAGcAYgBlAGIAYABfAGEAVwBfAFcAVQBZAGMAXgBZAF4AVABqAGwAcQBqAHEAbwBwAHIAfQBwAH0AewB8AH8AeQB8AHkAdgB1AHcAawB1AGsAaQBuAHoAdABuAHQAaAB+AHMAbQB+AG0AeACBAIMAhgCBAIYAhACFAIcAjgCFAI4AjACNAI8AiwCNAIsAiQCIAIoAggCIAIIAgACSAJQAmQCSAJkAlwCYAJoApgCYAKYAogCkAKcAoQCkAKEAngCcAKAAkwCcAJMAkQCWAKMAnQCWAJ0AkAClAJsAlQClAJUAnwCpAKsArgCpAK4ArACtAK8AtgCtALYAtAC1ALcAswC1ALMAsQCwALIAqgCwAKoAqAA=" + } + ] +} diff --git a/OMI_seat/examples/recliner_chair_legs.gltf b/OMI_seat/examples/recliner_chair_legs.gltf new file mode 100644 index 0000000..465926a --- /dev/null +++ b/OMI_seat/examples/recliner_chair_legs.gltf @@ -0,0 +1,117 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v3.4.50 (manually modified with seat data)", + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_seat" + ], + "scene": 0, + "scenes": [ + { + "nodes": [1] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "ReclinerChairLegsMesh" + }, + { + "children": [ + 0 + ], + "name": "ReclinerChairLegs", + "extensions": { + "OMI_seat": { + "back": [0, 0.33, -0.24], + "foot": [0, -0.17, 0.8], + "knee": [0, 0.33, 0.3] + } + } + } + ], + "meshes": [ + { + "name": "ReclinerChairLegsMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 184, + "max": [ + 0.30000001192092896, + 1.03000009059906, + 0.6531219482421875 + ], + "min": [ + -0.30000001192092896, + -0.25, + -0.30000001192092896 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 184, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 184, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 276, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 2208, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1472, + "byteOffset": 2208, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 2208, + "byteOffset": 3680, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 552, + "byteOffset": 5888, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 6440, + "uri": "data:application/octet-stream;base64,mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvnE9ij6ZmZk+mZmZvnE9ij6ZmZk+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvnE9ij6SwnU+mZmZvnE9ij6SwnU+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vnE9ij6SwnU+ksJ1vnE9ij6SwnU+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PnE9ij6SwnU+ksJ1PnE9ij6SwnU+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPnE9ij6ZmZk+mZmZPnE9ij6ZmZk+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPnE9ij6SwnU+mZmZPnE9ij6SwnU+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvnE9ij6SwnW+mZmZvnE9ij6SwnW+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvnE9ij6ZmZm+mZmZvnE9ij6ZmZm+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vnE9ij6SwnW+ksJ1vnE9ij6SwnW+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PnE9ij6SwnW+ksJ1PnE9ij6SwnW+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PnE9ij6ZmZm+ksJ1PnE9ij6ZmZm+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPnE9ij6SwnW+mZmZPnE9ij6SwnW+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPnE9ij6ZmZm+mZmZPnE9ij6ZmZm+AACAvvYo3D6SwnW+AACAvvYo3D6SwnW+AACAvvYo3D6SwnW+AACAvgvXgz+SwnW+AACAvgvXgz+SwnW+AACAvgvXgz+SwnW+AACAvvYo3D6ZmZm+AACAvvYo3D6ZmZm+AACAvvYo3D6ZmZm+AACAvgvXgz+ZmZm+AACAvgvXgz+ZmZm+AACAvgvXgz+ZmZm+AACAPvYo3D6SwnW+AACAPvYo3D6SwnW+AACAPvYo3D6SwnW+AACAPgvXgz+SwnW+AACAPgvXgz+SwnW+AACAPgvXgz+SwnW+AACAPvYo3D6ZmZm+AACAPvYo3D6ZmZm+AACAPvYo3D6ZmZm+AACAPgvXgz+ZmZm+AACAPgvXgz+ZmZm+AACAPgvXgz+ZmZm+AAAAvsL1qD6SwnW+AAAAvsL1qD6SwnW+AAAAvvYo3D6SwnW+AAAAvvYo3D6SwnW+AAAAvsL1qD6ZmZm+AAAAvsL1qD6ZmZm+AAAAvvYo3D6ZmZm+AAAAvvYo3D6ZmZm+AAAAPsL1qD6SwnW+AAAAPsL1qD6SwnW+AAAAPvYo3D6SwnW+AAAAPvYo3D6SwnW+AAAAPsL1qD6ZmZm+AAAAPsL1qD6ZmZm+AAAAPvYo3D6ZmZm+AAAAPvYo3D6ZmZm+AACAvigCXj5U3Kc+AACAvigCXj5U3Kc+AACAvigCXj5U3Kc+AACAvsg+h72LVhw/AACAvsg+h72LVhw/AACAvsg+h72LVhw/AACAvvy5hD49lb0+AACAvvy5hD49lb0+AACAvvy5hD49lb0+AACAvoBswbwAMyc/AACAvoBswbwAMyc/AACAvoBswbwAMyc/AACAPigCXj5U3Kc+AACAPigCXj5U3Kc+AACAPigCXj5U3Kc+AACAPsg+h72LVhw/AACAPsg+h72LVhw/AACAPsg+h72LVhw/AACAPvy5hD49lb0+AACAPvy5hD49lb0+AACAPvy5hD49lb0+AACAPoBswbwAMyc/AACAPoBswbwAMyc/AACAPoBswbwAMyc/AAAAvkU1kz4jqIM+AAAAvkU1kz4jqIM+AAAAvigCXj5U3Kc+AAAAvigCXj5U3Kc+AAAAvi7uqD4LYZk+AAAAvi7uqD4LYZk+AAAAvvy5hD49lb0+AAAAvvy5hD49lb0+AAAAPkU1kz4jqIM+AAAAPkU1kz4jqIM+AAAAPigCXj5U3Kc+AAAAPigCXj5U3Kc+AAAAPi7uqD4LYZk+AAAAPi7uqD4LYZk+AAAAPvy5hD49lb0+AAAAPvy5hD49lb0+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAAA+AAAAPwAAwD4AAEA/AADAPgAAQD8AACA/AABAPwAAID8AAEA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA+AACAPgAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAGA/AACAPgAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAA9wQ1P+4ENb8AAAAA8AQ1v/UENb8AAIC/AAAAAAAAAIAAAAAA8AQ1v/UENb8AAIC/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA9wQ1P+4ENb8AAIC/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAIC/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAAAA8wQ1v/MENT8AAAAA8AQ1v/UENb8AAAAA9wQ1P+4ENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA8AQ1v/UENb8AAIA/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAAAA9wQ1P+4ENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA8wQ1P/QENT8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIC/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIC/AAAAAAAAAIAAAIC/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIC/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAAAA8wQ1v/MENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIA/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIA/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIA/AAAAAAAAAIACAAQACQACAAkABwAIAAoAFQAIABUAEwAUABcAEQAUABEADgANAA8AAwANAAMAAQAGABIADAAGAAwAAAAWAAsABQAWAAUAEAAaABwAIAAaACAAHgAfACEAKgAfACoAKAApACsAJgApACYAJAAjACUAGwAjABsAGQAdACcAIgAdACIAGABHAEkAUgBHAFIAUAAuADAANAAuADQAMgAzADUAPgAzAD4APAA9AD8AOgA9ADoAOAA3ADkALwA3AC8ALQAxADsANgAxADYALABCAEQASABCAEgARgBRAFMATgBRAE4ATABLAE0AQwBLAEMAQQBFAE8ASgBFAEoAQABWAFgAXABWAFwAWgBbAF0AZgBbAGYAZABlAGcAYgBlAGIAYABfAGEAVwBfAFcAVQBZAGMAXgBZAF4AVABqAGwAcQBqAHEAbwBwAHIAfQBwAH0AewB8AH8AeQB8AHkAdgB1AHcAawB1AGsAaQBuAHoAdABuAHQAaAB+AHMAbQB+AG0AeACBAIMAhgCBAIYAhACFAIcAjgCFAI4AjACNAI8AiwCNAIsAiQCIAIoAggCIAIIAgACSAJQAmQCSAJkAlwCYAJoApgCYAKYAogCkAKcAoQCkAKEAngCcAKAAkwCcAJMAkQCWAKMAnQCWAJ0AkAClAJsAlQClAJUAnwCpAKsArgCpAK4ArACtAK8AtgCtALYAtAC1ALcAswC1ALMAsQCwALIAqgCwAKoAqAA=" + } + ] +} diff --git a/OMI_seat/examples/recliner_chair_with_trigger.gltf b/OMI_seat/examples/recliner_chair_with_trigger.gltf new file mode 100644 index 0000000..1e1a1c8 --- /dev/null +++ b/OMI_seat/examples/recliner_chair_with_trigger.gltf @@ -0,0 +1,147 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v3.4.50 (manually modified with seat data and physics data)", + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_physics_body", + "OMI_physics_shape", + "OMI_seat" + ], + "extensions": { + "OMI_physics_shape": { + "shapes": [ + { + "type": "box", + "box": { + "size": [0.5, 1, 1] + } + } + ] + } + }, + "scene": 0, + "scenes": [ + { + "nodes": [1] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "ReclinerChairFullMesh" + }, + { + "children": [ + 0, + 2 + ], + "name": "ReclinerChairFullWithTrigger", + "extensions": { + "OMI_physics_body": { + "trigger": {} + }, + "OMI_seat": { + "back": [0, 0.33, -0.247], + "foot": [0, -0.17, 0.8], + "knee": [0, 0.33, 0.3], + "angle": 2.0943951 + } + } + }, + { + "name": "ReclinerChairShape", + "translation": [0, 0.5, 0], + "extensions": { + "OMI_physics_body": { + "trigger": { + "shape": 0 + } + } + } + } + ], + "meshes": [ + { + "name": "ReclinerChairFullMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 184, + "max": [ + 0.30000001192092896, + 0.9351986050605774, + 0.6531219482421875 + ], + "min": [ + -0.30000001192092896, + -0.25, + -0.6483013033866882 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 184, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 184, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 276, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 2208, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1472, + "byteOffset": 2208, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 2208, + "byteOffset": 3680, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 552, + "byteOffset": 5888, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 6440, + "uri": "data:application/octet-stream;base64,mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvnE9ij6ZmZk+mZmZvnE9ij6ZmZk+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvnE9ij6SwnU+mZmZvnE9ij6SwnU+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vnE9ij6SwnU+ksJ1vnE9ij6SwnU+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PnE9ij6SwnU+ksJ1PnE9ij6SwnU+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPnE9ij6ZmZk+mZmZPnE9ij6ZmZk+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPnE9ij6SwnU+mZmZPnE9ij6SwnU+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvnE9ij6SwnW+mZmZvnE9ij6SwnW+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvnE9ij6ZmZm+mZmZvnE9ij6ZmZm+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vnE9ij6SwnW+ksJ1vnE9ij6SwnW+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PnE9ij6SwnW+ksJ1PnE9ij6SwnW+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PnE9ij6ZmZm+ksJ1PnE9ij6ZmZm+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPnE9ij6SwnW+mZmZPnE9ij6SwnW+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPnE9ij6ZmZm+mZmZPnE9ij6ZmZm+AACAvlXH1D7ZuZe+AACAvlXH1D7ZuZe+AACAvlXH1D7ZuZe+AACAvi1pbz+6qRi/AACAvi1pbz+6qRi/AACAvi1pbz+6qRi/AACAvi1rxT6LVLK+AACAvi1rxT6LVLK+AACAvi1rxT6LVLK+AACAvhm7Zz8T9yW/AACAvhm7Zz8T9yW/AACAvhm7Zz8T9yW/AACAPlXH1D7ZuZe+AACAPlXH1D7ZuZe+AACAPlXH1D7ZuZe+AACAPi1pbz+6qRi/AACAPi1pbz+6qRi/AACAPi1pbz+6qRi/AACAPi1rxT6LVLK+AACAPi1rxT6LVLK+AACAPi1rxT6LVLK+AACAPhm7Zz8T9yW/AACAPhm7Zz8T9yW/AACAPhm7Zz8T9yW/AAAAvilwqD59QHy+AAAAvilwqD59QHy+AAAAvlXH1D7ZuZe+AAAAvlXH1D7ZuZe+AAAAvgEUmT7xupi+AAAAvgEUmT7xupi+AAAAvi1rxT6LVLK+AAAAvi1rxT6LVLK+AAAAPilwqD59QHy+AAAAPilwqD59QHy+AAAAPlXH1D7ZuZe+AAAAPlXH1D7ZuZe+AAAAPgEUmT7xupi+AAAAPgEUmT7xupi+AAAAPi1rxT6LVLK+AAAAPi1rxT6LVLK+AACAvigCXj5U3Kc+AACAvigCXj5U3Kc+AACAvigCXj5U3Kc+AACAvsg+h72LVhw/AACAvsg+h72LVhw/AACAvsg+h72LVhw/AACAvvy5hD49lb0+AACAvvy5hD49lb0+AACAvvy5hD49lb0+AACAvoBswbwAMyc/AACAvoBswbwAMyc/AACAvoBswbwAMyc/AACAPigCXj5U3Kc+AACAPigCXj5U3Kc+AACAPigCXj5U3Kc+AACAPsg+h72LVhw/AACAPsg+h72LVhw/AACAPsg+h72LVhw/AACAPvy5hD49lb0+AACAPvy5hD49lb0+AACAPvy5hD49lb0+AACAPoBswbwAMyc/AACAPoBswbwAMyc/AACAPoBswbwAMyc/AAAAvkU1kz4jqIM+AAAAvkU1kz4jqIM+AAAAvigCXj5U3Kc+AAAAvigCXj5U3Kc+AAAAvi7uqD4LYZk+AAAAvi7uqD4LYZk+AAAAvvy5hD49lb0+AAAAvvy5hD49lb0+AAAAPkU1kz4jqIM+AAAAPkU1kz4jqIM+AAAAPigCXj5U3Kc+AAAAPigCXj5U3Kc+AAAAPi7uqD4LYZk+AAAAPi7uqD4LYZk+AAAAPvy5hD49lb0+AAAAPvy5hD49lb0+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAAA+AAAAPwAAwD4AAEA/AADAPgAAQD8AACA/AABAPwAAID8AAEA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA+AACAPgAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAGA/AACAPgAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAPwAAAIAAAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIC/AAAAAAAAAIAAAAAAAACAvwAAAIAAAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIC/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAAAAAACAvwAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgD8AAIA/AAAAAAAAAIAAAAAAAACAvwAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAAAAAAAAAAgL8AAIA/AAAAAAAAAIAAAAAA2LNdv////z4AAAAAAAAAP9azXT8AAIC/AAAAAAAAAIAAAAAAAAAAP9azXT8AAIC/AAAAAAAAAIAAAAAA2LNdP////74AAAAA2LNdv////z4AAIC/AAAAAAAAAIAAAAAAAAAAv9azXb8AAIC/AAAAAAAAAIAAAAAAAAAAv9azXb8AAAAA2LNdP////74AAAAA2LNdv////z4AAAAAAAAAP9azXT8AAIA/AAAAAAAAAIAAAAAAAAAAP9azXT8AAAAA2LNdP////74AAIA/AAAAAAAAAIAAAAAA2LNdv////z4AAAAAAAAAv9azXb8AAIA/AAAAAAAAAIAAAAAAAAAAv9azXb8AAAAA2LNdP////74AAIA/AAAAAAAAAIAAAAAAAQAAP9azXT8AAIC/AAAAAAAAAIAAAAAAAQAAP9azXT8AAIC/AAAAAAAAAIAAAIC/AAAAAAAAAIAAAAAA////vtizXb8AAIC/AAAAAAAAAIAAAAAA////vtizXb8AAAAAAQAAP9azXT8AAIA/AAAAAAAAAIAAAAAAAQAAP9azXT8AAIA/AAAAAAAAAIAAAAAA////vtizXb8AAIA/AAAAAAAAAIAAAAAA////vtizXb8AAIA/AAAAAAAAAIAAAAAA9wQ1P+4ENb8AAAAA8AQ1v/UENb8AAIC/AAAAAAAAAIAAAAAA8AQ1v/UENb8AAIC/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA9wQ1P+4ENb8AAIC/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAIC/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAAAA8wQ1v/MENT8AAAAA8AQ1v/UENb8AAAAA9wQ1P+4ENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA8AQ1v/UENb8AAIA/AAAAAAAAAIAAAAAA8wQ1P/QENT8AAAAA9wQ1P+4ENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENT8AAAAA8wQ1P/QENT8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIC/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIC/AAAAAAAAAIAAAIC/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIC/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAAAA8wQ1v/MENb8AAIA/AAAAAAAAAIAAAAAA8wQ1v/MENb8AAIA/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIA/AAAAAAAAAIAAAAAA8wQ1P/MENT8AAIA/AAAAAAAAAIACAAQACQACAAkABwAIAAoAFQAIABUAEwAUABcAEQAUABEADgANAA8AAwANAAMAAQAGABIADAAGAAwAAAAWAAsABQAWAAUAEAAaABwAIAAaACAAHgAfACEAKgAfACoAKAApACsAJgApACYAJAAjACUAGwAjABsAGQAdACcAIgAdACIAGABHAEkAUgBHAFIAUAAuADAANAAuADQAMgAzADUAPgAzAD4APAA9AD8AOgA9ADoAOAA3ADkALwA3AC8ALQAxADsANgAxADYALABCAEQASABCAEgARgBRAFMATgBRAE4ATABLAE0AQwBLAEMAQQBFAE8ASgBFAEoAQABWAFgAXABWAFwAWgBbAF0AZgBbAGYAZABlAGcAYgBlAGIAYABfAGEAVwBfAFcAVQBZAGMAXgBZAF4AVABqAGwAcQBqAHEAbwBwAHIAfQBwAH0AewB8AH8AeQB8AHkAdgB1AHcAawB1AGsAaQBuAHoAdABuAHQAaAB+AHMAbQB+AG0AeACBAIMAhgCBAIYAhACFAIcAjgCFAI4AjACNAI8AiwCNAIsAiQCIAIoAggCIAIIAgACSAJQAmQCSAJkAlwCYAJoApgCYAKYAogCkAKcAoQCkAKEAngCcAKAAkwCcAJMAkQCWAKMAnQCWAJ0AkAClAJsAlQClAJUAnwCpAKsArgCpAK4ArACtAK8AtgCtALYAtAC1ALcAswC1ALMAsQCwALIAqgCwAKoAqAA=" + } + ] +} diff --git a/OMI_seat/examples/simple_chair_down_x.gltf b/OMI_seat/examples/simple_chair_down_x.gltf new file mode 100644 index 0000000..7bc9f40 --- /dev/null +++ b/OMI_seat/examples/simple_chair_down_x.gltf @@ -0,0 +1,117 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v3.4.50 (manually modified with seat data)", + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_seat" + ], + "scene": 0, + "scenes": [ + { + "nodes": [1] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "SimpleChairMesh" + }, + { + "children": [ + 0 + ], + "name": "SimpleChairDownX", + "extensions": { + "OMI_seat": { + "back": [0.33, 0.24, 0], + "foot": [-0.25, -0.3, 0], + "knee": [0.33, -0.3, 0] + } + } + } + ], + "meshes": [ + { + "name": "SimpleChairMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 144, + "max": [ + 1.0299999713897705, + 0.30000001192092896, + 0.29999998211860657 + ], + "min": [ + -0.25, + -0.30000004172325134, + -0.29999998211860657 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 144, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 144, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 216, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1152, + "byteOffset": 1728, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 2880, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 432, + "byteOffset": 4608, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 5040, + "uri": "data:application/octet-stream;base64,cT2KPpuZmb6ZmZk+cT2KPpuZmb6ZmZk+cT2KPpuZmb6ZmZk+wfWoPpuZmb6ZmZk+wfWoPpuZmb6ZmZk+wfWoPpuZmb6ZmZk+cj2KPpqZmT6ZmZk+cj2KPpqZmT6ZmZk+cj2KPpqZmT6ZmZk+wvWoPpqZmT6ZmZk+wvWoPpqZmT6ZmZk+wvWoPpqZmT6ZmZk+cT2KPpuZmb6ZmZm+cT2KPpuZmb6ZmZm+cT2KPpuZmb6ZmZm+wfWoPpuZmb6ZmZm+wfWoPpuZmb6ZmZm+wfWoPpuZmb6ZmZm+cj2KPpqZmT6ZmZm+cj2KPpqZmT6ZmZm+cj2KPpqZmT6ZmZm+wvWoPpqZmT6ZmZm+wvWoPpqZmT6ZmZm+wvWoPpqZmT6ZmZm+/v9/vpmZmb6YmZk+/v9/vpmZmb6YmZk+/v9/vpmZmb6YmZk+cD2KPpiZmb6YmZk+cD2KPpiZmb6YmZk+AACAvpHCdb6YmZk+AACAvpHCdb6YmZk+AACAvpHCdb6YmZk+cD2KPpTCdb6YmZk+cD2KPpTCdb6YmZk+/v9/vpmZmb6QwnU+/v9/vpmZmb6QwnU+/v9/vpmZmb6QwnU+cD2KPpiZmb6QwnU+cD2KPpiZmb6QwnU+AACAvpHCdb6QwnU+AACAvpHCdb6QwnU+AACAvpHCdb6QwnU+cD2KPpTCdb6QwnU+cD2KPpTCdb6QwnU+/v9/vpmZmb6RwnW+/v9/vpmZmb6RwnW+/v9/vpmZmb6RwnW+cD2KPpiZmb6RwnW+cD2KPpiZmb6RwnW+//9/vpDCdb6RwnW+//9/vpDCdb6RwnW+//9/vpDCdb6RwnW+cD2KPpDCdb6RwnW+cD2KPpDCdb6RwnW+/v9/vpmZmb6YmZm+/v9/vpmZmb6YmZm+/v9/vpmZmb6YmZm+cD2KPpiZmb6YmZm+cD2KPpiZmb6YmZm+//9/vpDCdb6YmZm+//9/vpDCdb6YmZm+//9/vpDCdb6YmZm+cD2KPpDCdb6YmZm+cD2KPpDCdb6YmZm+/v9/vpHCdT6YmZk+/v9/vpHCdT6YmZk+/v9/vpHCdT6YmZk+cD2KPpDCdT6YmZk+cD2KPpDCdT6YmZk+/v9/vpiZmT6YmZk+/v9/vpiZmT6YmZk+/v9/vpiZmT6YmZk+bz2KPpmZmT6YmZk+bz2KPpmZmT6YmZk+/v9/vpHCdT6RwnU+/v9/vpHCdT6RwnU+/v9/vpHCdT6RwnU+cD2KPpDCdT6RwnU+cD2KPpDCdT6RwnU+/v9/vpiZmT6RwnU+/v9/vpiZmT6RwnU+/v9/vpiZmT6RwnU+bz2KPpmZmT6RwnU+bz2KPpmZmT6RwnU+/v9/vpPCdT6QwnW+/v9/vpPCdT6QwnW+/v9/vpPCdT6QwnW+bz2KPpHCdT6QwnW+bz2KPpHCdT6QwnW+/v9/vpiZmT6QwnW+/v9/vpiZmT6QwnW+/v9/vpiZmT6QwnW+bz2KPpmZmT6QwnW+bz2KPpmZmT6QwnW+/v9/vpPCdT6YmZm+/v9/vpPCdT6YmZm+/v9/vpPCdT6YmZm+bz2KPpHCdT6YmZm+bz2KPpHCdT6YmZm+/v9/vpiZmT6YmZm+/v9/vpiZmT6YmZm+/v9/vpiZmT6YmZm+bz2KPpmZmT6YmZm+bz2KPpmZmT6YmZm+9ijcPpDCdT7//38+9ijcPpDCdT7//38+9ijcPpDCdT7//38+CteDP5DCdT7//38+CteDP5DCdT7//38+CteDP5DCdT7//38+9SjcPpiZmT79/38+9SjcPpiZmT79/38+9SjcPpiZmT79/38+CteDP5iZmT79/38+CteDP5iZmT79/38+CteDP5iZmT79/38+9ijcPpPCdT7+/3++9ijcPpPCdT7+/3++9ijcPpPCdT7+/3++CteDP5DCdT7+/3++CteDP5DCdT7+/3++CteDP5DCdT7+/3++9CjcPpeZmT7+/3++9CjcPpeZmT7+/3++9CjcPpeZmT7+/3++CteDP5aZmT7+/3++CteDP5aZmT7+/3++CteDP5aZmT7+/3++wfWoPpHCdT4BAAA+wfWoPpHCdT4BAAA+9SjcPpHCdT4BAAA+9SjcPpHCdT4BAAA+wfWoPpmZmT4BAAA+wfWoPpmZmT4BAAA+9SjcPpiZmT4BAAA+9SjcPpiZmT4BAAA+wfWoPpHCdT7+//+9wfWoPpHCdT7+//+99SjcPpHCdT7+//+99SjcPpHCdT7+//+9wfWoPpmZmT79//+9wfWoPpmZmT79//+99SjcPpiZmT79//+99SjcPpiZmT79//+9AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAAA+AAAAPwAAwD4AAEA/AADAPgAAQD8AACA/AABAPwAAID8AAEA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA+AACAPgAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAGA/AACAPgAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AACAv1RVVTMAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAP1RVVbMAAACAAACAv1RVVTMAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAP1RVVbMAAACAAACAv1RVVTMAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAP1RVVbMAAACAAACAv1RVVTMAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAP1RVVbMAAACA//9/v1lVBbUAAACAjDSAMwAAgL8AAACAAAAAAJA0ADUAAIA/jDSAMwAAgL8AAACAAAAAAJA0ADUAAIA///9/v1lVBbUAAACAAAAAAJA0ADUAAIA/0k7AMwAAgD8AAACAAAAAAJA0ADUAAIA/0k7AMwAAgD8AAACA//9/v1lVBbUAAACAAAAAAJA0gLQAAIC/jDSAMwAAgL8AAACAAAAAAJA0gLQAAIC/jDSAMwAAgL8AAACA//9/v1lVBbUAAACAAAAAAJA0gLQAAIC/0k7AMwAAgD8AAACAAAAAAJA0gLQAAIC/0k7AMwAAgD8AAACAAACAv1pVhbQAAACAjjSAMwAAgL8AAACAAAAAAAAAAAAAAIA/jjSAMwAAgL8AAACAAAAAAAAAAAAAAIA/AACAv1pVhbQAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAv1pVhbQAAACAAAAAAAAAAAAAAIC/jjSAMwAAgL8AAACAAAAAAAAAAAAAAIC/jjSAMwAAgL8AAACAAACAv1pVhbQAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAjjQAswAAgL8AAACAAAAAAAAAAAAAAIA/jjQAswAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAjjSAswAAgD8AAACAAAAAAAAAAAAAAIA/jjSAswAAgD8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAjjQAswAAgL8AAACAAAAAAIs0gLQAAIC/jjQAswAAgL8AAACAAAAAAIs0gLQAAIC/AACAvwAAAAAAAACAjjSAswAAgD8AAACAAAAAAIs0gLQAAIC/jjSAswAAgD8AAACAAAAAAIs0gLQAAIC/AACAvwAAAAAAAACAjDSAswAAgL8AAACAAAAAAAAAAAAAAIA/jDSAswAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAjDSAswAAgD8AAACAAAAAAAAAAAAAAIA/jDSAswAAgD8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAjDSAswAAgL8AAACAAAAAAAAAAAAAAIC/jDSAswAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAjDSAswAAgD8AAACAAAAAAAAAAAAAAIC/jDSAswAAgD8AAACAAAAAAAAAAAAAAIC/AACAvwsASLURAOAzWFVVswAAgL8EAECzAAAAAAAAAAAAAIA/WFVVswAAgL8EAECzAAAAAAAAAAAAAIA/AACAPwAAAABeVQW0AACAvwsASLURAOAzAAAAAAAAAAAAAIA/WFVVMwAAgD8DAMCzAAAAAAAAAAAAAIA/WFVVMwAAgD8DAMCzAACAPwAAAABeVQW0AACAvwsASLURAOAzWFVVswAAgL8EAECzAAAAAAAAAAAAAIC/WFVVswAAgL8EAECzAAAAAAAAAAAAAIC/AACAPwAAAABeVQW0AACAvwsASLURAOAzAAAAAAAAAAAAAIC/WFVVMwAAgD8DAMCzAAAAAAAAAAAAAIC/WFVVMwAAgD8DAMCzAACAPwAAAABeVQW0AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA//v+fNAAAgD8AAACAAAAAAAAAAAAAAIA//v+fNAAAgD8AAACAAAAAAAAAgL8AAACAs6omKQAAAAAAAIC/AAAAAAAAgL8AAACAs6omKQAAAAAAAIC/s6omKQAAAAAAAIC//v+fNAAAgD8AAACAs6omKQAAAAAAAIC//v+fNAAAgD8AAACAAgAEAAkAAgAJAAcACAAKABYACAAWABQAEwAVABAAEwAQAA4ADQAPAAMADQADAAEABgASAAwABgAMAAAAFwALAAUAFwAFABEAGgAcACAAGgAgAB4AHwAhACsAHwArACkAKAAqACUAKAAlACMAJAAmABsAJAAbABkAHQAnACIAHQAiABgARgBIAFIARgBSAFAALgAwADQALgA0ADIAMwA1AD8AMwA/AD0APAA+ADkAPAA5ADcAOAA6AC8AOAAvAC0AMQA7ADYAMQA2ACwAQgBEAEkAQgBJAEcAUQBTAE4AUQBOAEwASwBNAEMASwBDAEEARQBPAEoARQBKAEAAVgBYAF0AVgBdAFsAWgBcAGYAWgBmAGQAZQBnAGIAZQBiAGAAXwBhAFcAXwBXAFUAWQBjAF4AWQBeAFQAagBsAHEAagBxAG8AcAByAH4AcAB+AHwAewB9AHgAewB4AHYAdQB3AGsAdQBrAGkAbgB6AHQAbgB0AGgAfwBzAG0AfwBtAHkAgQCDAIYAgQCGAIQAhQCHAI8AhQCPAI0AjACOAIsAjACLAIkAiACKAIIAiACCAIAA" + } + ] +} diff --git a/OMI_seat/examples/simple_chair_neg_z.gltf b/OMI_seat/examples/simple_chair_neg_z.gltf new file mode 100644 index 0000000..de60c21 --- /dev/null +++ b/OMI_seat/examples/simple_chair_neg_z.gltf @@ -0,0 +1,117 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v3.4.50 (manually modified with seat data)", + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_seat" + ], + "scene": 0, + "scenes": [ + { + "nodes": [1] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "SimpleChairMesh" + }, + { + "children": [ + 0 + ], + "name": "SimpleChairNegZ", + "extensions": { + "OMI_seat": { + "back": [0, 0.33, 0.24], + "foot": [0, -0.25, -0.3], + "knee": [0, 0.33, -0.3] + } + } + } + ], + "meshes": [ + { + "name": "SimpleChairMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 144, + "max": [ + 0.30000001192092896, + 1.03000009059906, + 0.30000001192092896 + ], + "min": [ + -0.30000001192092896, + -0.25, + -0.30000001192092896 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 144, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 144, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 216, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1152, + "byteOffset": 1728, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 2880, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 432, + "byteOffset": 4608, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 5040, + "uri": "data:application/octet-stream;base64,mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvnE9ij6ZmZk+mZmZvnE9ij6ZmZk+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvnE9ij6SwnU+mZmZvnE9ij6SwnU+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vnE9ij6SwnU+ksJ1vnE9ij6SwnU+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PnE9ij6SwnU+ksJ1PnE9ij6SwnU+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPnE9ij6ZmZk+mZmZPnE9ij6ZmZk+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPnE9ij6SwnU+mZmZPnE9ij6SwnU+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvnE9ij6SwnW+mZmZvnE9ij6SwnW+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvnE9ij6ZmZm+mZmZvnE9ij6ZmZm+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vnE9ij6SwnW+ksJ1vnE9ij6SwnW+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PnE9ij6SwnW+ksJ1PnE9ij6SwnW+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PnE9ij6ZmZm+ksJ1PnE9ij6ZmZm+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPnE9ij6SwnW+mZmZPnE9ij6SwnW+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPnE9ij6ZmZm+mZmZPnE9ij6ZmZm+AACAvvYo3D6ZmZk+AACAvvYo3D6ZmZk+AACAvvYo3D6ZmZk+AACAvgvXgz+ZmZk+AACAvgvXgz+ZmZk+AACAvgvXgz+ZmZk+AACAvvYo3D6SwnU+AACAvvYo3D6SwnU+AACAvvYo3D6SwnU+AACAvgvXgz+SwnU+AACAvgvXgz+SwnU+AACAvgvXgz+SwnU+AACAPvYo3D6ZmZk+AACAPvYo3D6ZmZk+AACAPvYo3D6ZmZk+AACAPgvXgz+ZmZk+AACAPgvXgz+ZmZk+AACAPgvXgz+ZmZk+AACAPvYo3D6SwnU+AACAPvYo3D6SwnU+AACAPvYo3D6SwnU+AACAPgvXgz+SwnU+AACAPgvXgz+SwnU+AACAPgvXgz+SwnU+AAAAvsL1qD6ZmZk+AAAAvsL1qD6ZmZk+AAAAvvYo3D6ZmZk+AAAAvvYo3D6ZmZk+AAAAvsL1qD6SwnU+AAAAvsL1qD6SwnU+AAAAvvYo3D6SwnU+AAAAvvYo3D6SwnU+AAAAPsL1qD6ZmZk+AAAAPsL1qD6ZmZk+AAAAPvYo3D6ZmZk+AAAAPvYo3D6ZmZk+AAAAPsL1qD6SwnU+AAAAPsL1qD6SwnU+AAAAPvYo3D6SwnU+AAAAPvYo3D6SwnU+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAAA+AAAAPwAAwD4AAEA/AADAPgAAQD8AACA/AABAPwAAID8AAEA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA+AACAPgAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAGA/AACAPgAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAgAEAAkAAgAJAAcACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAMADQADAAEABgASAAwABgAMAAAAFgALAAUAFgAFABAAGgAcACAAGgAgAB4AHwAhACoAHwAqACgAKQArACYAKQAmACQAIwAlABsAIwAbABkAHQAnACIAHQAiABgARwBJAFIARwBSAFAALgAwADQALgA0ADIAMwA1AD4AMwA+ADwAPQA/ADoAPQA6ADgANwA5AC8ANwAvAC0AMQA7ADYAMQA2ACwAQgBEAEgAQgBIAEYAUQBTAE4AUQBOAEwASwBNAEMASwBDAEEARQBPAEoARQBKAEAAVgBYAFwAVgBcAFoAWwBdAGYAWwBmAGQAZQBnAGIAZQBiAGAAXwBhAFcAXwBXAFUAWQBjAF4AWQBeAFQAagBsAHEAagBxAG8AcAByAH0AcAB9AHsAfAB/AHkAfAB5AHYAdQB3AGsAdQBrAGkAbgB6AHQAbgB0AGgAfgBzAG0AfgBtAHgAgQCDAIYAgQCGAIQAhQCHAI4AhQCOAIwAjQCPAIsAjQCLAIkAiACKAIIAiACCAIAA" + } + ] +} diff --git a/OMI_seat/examples/simple_chair_pos_z.gltf b/OMI_seat/examples/simple_chair_pos_z.gltf new file mode 100644 index 0000000..ea8ee66 --- /dev/null +++ b/OMI_seat/examples/simple_chair_pos_z.gltf @@ -0,0 +1,117 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v3.4.50 (manually modified with seat data)", + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_seat" + ], + "scene": 0, + "scenes": [ + { + "nodes": [1] + } + ], + "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] + } + } + } + ], + "meshes": [ + { + "name": "SimpleChairMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 144, + "max": [ + 0.30000001192092896, + 1.03000009059906, + 0.30000001192092896 + ], + "min": [ + -0.30000001192092896, + -0.25, + -0.30000001192092896 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 144, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 144, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 216, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1152, + "byteOffset": 1728, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 2880, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 432, + "byteOffset": 4608, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 5040, + "uri": "data:application/octet-stream;base64,mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvnI9ij6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvsL1qD6amZk+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvnI9ij6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZvsL1qD6amZm+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPnI9ij6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPsL1qD6amZk+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPnI9ij6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mpmZPsL1qD6amZm+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvgAAgL6ZmZk+mZmZvnE9ij6ZmZk+mZmZvnE9ij6ZmZk+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvgAAgL6SwnU+mZmZvnE9ij6SwnU+mZmZvnE9ij6SwnU+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vgAAgL6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vnE9ij6ZmZk+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vgAAgL6SwnU+ksJ1vnE9ij6SwnU+ksJ1vnE9ij6SwnU+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PgAAgL6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PnE9ij6ZmZk+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PgAAgL6SwnU+ksJ1PnE9ij6SwnU+ksJ1PnE9ij6SwnU+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPgAAgL6ZmZk+mZmZPnE9ij6ZmZk+mZmZPnE9ij6ZmZk+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPgAAgL6SwnU+mZmZPnE9ij6SwnU+mZmZPnE9ij6SwnU+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvgAAgL6SwnW+mZmZvnE9ij6SwnW+mZmZvnE9ij6SwnW+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvgAAgL6ZmZm+mZmZvnE9ij6ZmZm+mZmZvnE9ij6ZmZm+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vgAAgL6SwnW+ksJ1vnE9ij6SwnW+ksJ1vnE9ij6SwnW+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vgAAgL6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1vnE9ij6ZmZm+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PgAAgL6SwnW+ksJ1PnE9ij6SwnW+ksJ1PnE9ij6SwnW+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PgAAgL6ZmZm+ksJ1PnE9ij6ZmZm+ksJ1PnE9ij6ZmZm+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPgAAgL6SwnW+mZmZPnE9ij6SwnW+mZmZPnE9ij6SwnW+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPgAAgL6ZmZm+mZmZPnE9ij6ZmZm+mZmZPnE9ij6ZmZm+AACAvvYo3D6SwnW+AACAvvYo3D6SwnW+AACAvvYo3D6SwnW+AACAvgvXgz+SwnW+AACAvgvXgz+SwnW+AACAvgvXgz+SwnW+AACAvvYo3D6ZmZm+AACAvvYo3D6ZmZm+AACAvvYo3D6ZmZm+AACAvgvXgz+ZmZm+AACAvgvXgz+ZmZm+AACAvgvXgz+ZmZm+AACAPvYo3D6SwnW+AACAPvYo3D6SwnW+AACAPvYo3D6SwnW+AACAPgvXgz+SwnW+AACAPgvXgz+SwnW+AACAPgvXgz+SwnW+AACAPvYo3D6ZmZm+AACAPvYo3D6ZmZm+AACAPvYo3D6ZmZm+AACAPgvXgz+ZmZm+AACAPgvXgz+ZmZm+AACAPgvXgz+ZmZm+AAAAvsL1qD6SwnW+AAAAvsL1qD6SwnW+AAAAvvYo3D6SwnW+AAAAvvYo3D6SwnW+AAAAvsL1qD6ZmZm+AAAAvsL1qD6ZmZm+AAAAvvYo3D6ZmZm+AAAAvvYo3D6ZmZm+AAAAPsL1qD6SwnW+AAAAPsL1qD6SwnW+AAAAPvYo3D6SwnW+AAAAPvYo3D6SwnW+AAAAPsL1qD6ZmZm+AAAAPsL1qD6ZmZm+AAAAPvYo3D6ZmZm+AAAAPvYo3D6ZmZm+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAAA+AAAAPwAAwD4AAEA/AADAPgAAQD8AACA/AABAPwAAID8AAEA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA+AACAPgAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAGA/AACAPgAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAgAEAAkAAgAJAAcACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAMADQADAAEABgASAAwABgAMAAAAFgALAAUAFgAFABAAGgAcACAAGgAgAB4AHwAhACoAHwAqACgAKQArACYAKQAmACQAIwAlABsAIwAbABkAHQAnACIAHQAiABgARwBJAFIARwBSAFAALgAwADQALgA0ADIAMwA1AD4AMwA+ADwAPQA/ADoAPQA6ADgANwA5AC8ANwAvAC0AMQA7ADYAMQA2ACwAQgBEAEgAQgBIAEYAUQBTAE4AUQBOAEwASwBNAEMASwBDAEEARQBPAEoARQBKAEAAVgBYAFwAVgBcAFoAWwBdAGYAWwBmAGQAZQBnAGIAZQBiAGAAXwBhAFcAXwBXAFUAWQBjAF4AWQBeAFQAagBsAHEAagBxAG8AcAByAH0AcAB9AHsAfAB/AHkAfAB5AHYAdQB3AGsAdQBrAGkAbgB6AHQAbgB0AGgAfgBzAG0AfgBtAHgAgQCDAIYAgQCGAIQAhQCHAI4AhQCOAIwAjQCPAIsAjQCLAIkAiACKAIIAiACCAIAA" + } + ] +} diff --git a/OMI_seat/examples/simple_chair_what.gltf b/OMI_seat/examples/simple_chair_what.gltf new file mode 100644 index 0000000..c870e2b --- /dev/null +++ b/OMI_seat/examples/simple_chair_what.gltf @@ -0,0 +1,117 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v3.4.50 (manually modified with seat data)", + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_seat" + ], + "scene": 0, + "scenes": [ + { + "nodes": [1] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "SimpleChairMesh" + }, + { + "children": [ + 0 + ], + "name": "SimpleChairWhat", + "extensions": { + "OMI_seat": { + "back": [0.11, 0.368, -0.139], + "foot": [-0.032, -0.338, 0.194], + "knee": [0.344, 0.098, 0.266] + } + } + } + ], + "meshes": [ + { + "name": "SimpleChairMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 144, + "max": [ + 0.7213316559791565, + 1.0307531356811523, + 0.46110567450523376 + ], + "min": [ + -0.4797835350036621, + -0.467403769493103, + -0.45110565423965454 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 144, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 144, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 216, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1152, + "byteOffset": 1728, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 1728, + "byteOffset": 2880, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 432, + "byteOffset": 4608, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 5040, + "uri": "data:application/octet-stream;base64,fDPxPRLIOj4CP+g+fDPxPRLIOj4CP+g+fDPxPRLIOj4CP+g+yIEgPorcaD4LFuw+yIEgPorcaD4LFuw+yIEgPorcaD4LFuw+RHERvqP99j4gTmw7RHERvqP99j4gTmw7RHERvqP99j4gTmw7cxLTvfADBz+w9DU8cxLTvfADBz+w9DU8cxLTvfADBz+w9DU83kz8PuKFnr0A24I93kz8PuKFnr0A24I93kz8PuKFnr0A24I9ciAIP+i5BL0sN5I9ciAIP+i5BL0sN5I9ciAIP+i5BL0sN5I9u45uPkPwYz6mr8W+u45uPkPwYz6mr8W+u45uPkPwYz6mr8W+YzuLPl0CiT6c2MG+YzuLPl0CiT6c2MG+YzuLPl0CiT6c2MG+YEFhvhiUVL5S98Y+YEFhvhiUVL5S98Y+YEFhvhiUVL5S98Y+ejPxPRHIOj4AP+g+ejPxPRHIOj4AP+g+Edx7vsjbNb4W7a8+Edx7vsjbNb4W7a8+Edx7vsjbNb4W7a8+Fv67PWGAWT7ENNE+Fv67PWGAWT7ENNE+/No6vsoub75NA7M+/No6vsoub75NA7M+/No6vsoub75NA7M+IQAfPl8tID77StQ+IQAfPl8tID77StQ+rXVVvnp2UL4R+Zs+rXVVvnp2UL4R+Zs+rXVVvnp2UL4R+Zs+b2UEPq/lPj6/QL0+b2UEPq/lPj6/QL0+crDwPTQC4r66GBs9crDwPTQC4r66GBs9crDwPTQC4r66GBs9qxnpPvygUr0Wq9I9qxnpPvygUr0Wq9I9D3u7PQym0r4gyem7D3u7PQym0r4gyem7D3u7PQym0r4gyem7UszbPnh/r7xMBG09UszbPnh/r7xMBG09nL4ePoxP774A7pC6nL4ePoxP774A7pC6nL4ePoxP774A7pC63Ez8PuCFnr0E24I93Ez8PuCFnr0E24I96yMEPmTz375O2Ty96yMEPmTz375O2Ty96yMEPmTz375O2Ty9g//uPoAqQr1IyJo8g//uPoAqQr1IyJo811joviibfz1gTYa811joviibfz1gTYa811joviibfz1gTYa8Iq3tvXqh5z6+Fkc9Iq3tvXqh5z6+Fkc9MKb1vjQ+vT2QeHu9MKb1vjQ+vT2QeHu9MKb1vjQ+vT2QeHu9QnERvqL99j4ATmw7QnERvqL99j4ATmw7pSXVvmAwFT3cxmK9pSXVvmAwFT3cxmK9pSXVvmAwFT3cxmK9W+CgvSBU2j5Q2h08W+CgvSBU2j5Q2h08/nLivtAIiD1gjM29/nLivtAIiD1gjM29/nLivtAIiD1gjM29vhXWvUiw6T5K2xC9vhXWvUiw6T5K2xC9KjDuvYWJL74Q+bu+KjDuvYWJL74Q+bu+KjDuvYWJL74Q+bu+B8NiPqPSXz5ksZq+B8NiPqPSXz5ksZq+x7IRvjXREL5NA9O+x7IRvjXREL5NA9O+x7IRvjXREL5NA9O+VihIPvOKfj6fu7G+VihIPvOKfj6fu7G+ZGOhvTckSr4W7c++ZGOhvTckSr4W7c++ZGOhvTckSr4W7c++tJSEPvI3RT5opa6+tJSEPvI3RT5opa6+yJjWvedrK75S9+a+yJjWvedrK75S9+a+yJjWvedrK75S9+a+uI5uPkLwYz6kr8W+uI5uPkLwYz6kr8W+26GcPCn+DD/L/BM926GcPCn+DD/L/BM926GcPCn+DD/L/BM9YFLRPq4YgD8AmOM9YFLRPq4YgD8AmOM9YFLRPq4YgD8AmOM91M7guz2sFD9bVBG81M7guz2sFD9bVBG81M7guz2sFD9bVBG8BgXEPrjvgz8Ib4c9BgXEPrjvgz8Ib4c9BgXEPrjvgz8Ib4c9HMqpPmciqz5Ix5O+HMqpPmciqz5Ix5O+HMqpPmciqz5Ix5O+Mak4P2fESD/EwVq+Mak4P2fESD/EwVq+Mak4P2fESD/EwVq+xHycPo9+uj6E0aq+xHycPo9+uj6E0aq+xHycPo9+uj6E0aq+hAIyP3tyUD8ea4S+hAIyP3tyUD8ea4S+hAIyP3tyUD8ea4S+4kUEPXDf1z4vxGu94kUEPXDf1z4vxGu9dCjHPdhF/j76kDi9dCjHPdhF/j76kDi94NjOO5g75z4GC9K94NjOO5g75z4GC9K9EfORPQDRBj9scbi9EfORPQDRBj9scbi9eBFBPnpyoD7uN2G+eBFBPnpyoD7uN2G+HcqBPuLYxj4ga1S+HcqBPuLYxj4ga1S+x3YmPqLOrz4ypoe+x3YmPqLOrz4ypoe+ifloPgo11j7MP4G+ifloPgo11j7MP4G+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAAA+AAAAPwAAwD4AAEA/AADAPgAAQD8AACA/AABAPwAAID8AAEA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA+AACAPgAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD4AAIA+AADAPgAAAAAAAMA+AACAPwAAID8AAAAAAAAgPwAAgD8AAGA/AACAPgAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAwD4AAAAAAADAPgAAgD8AACA/AAAAAAAAID8AAIA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/4UYmvwAAQL8BAAC+1bPdPgEAAL8AAEA//f8fv92z3T7kRiY/1bPdPgEAAL8AAEA//f8fv92z3T7kRiY/40YmP///Pz8DAAA+4UYmvwAAQL8BAAC+/f8fv92z3T7kRiY/7rPdvgAAAD/7/z+//f8fv92z3T7kRiY/7rPdvgAAAD/7/z+/40YmP///Pz8DAAA+4UYmvwAAQL8BAAC+1bPdPgEAAL8AAEA/AAAgP9uz3b7hRia/1bPdPgEAAL8AAEA/AAAgP9uz3b7hRia/40YmP///Pz8DAAA+4UYmvwAAQL8BAAC+7rPdvgAAAD/7/z+/AAAgP9uz3b7hRia/7rPdvgAAAD/7/z+/AAAgP9uz3b7hRia/40YmP///Pz8DAAA+4EYmvwIAQL8NAAC+0bPdPgQAAL///z8/AQAgv96z3T7hRiY/0bPdPgQAAL///z8/AQAgv96z3T7hRiY/4EYmvwIAQL8NAAC+AQAgv96z3T7hRiY/1rPdvv///z4CAEC/AQAgv96z3T7hRiY/1rPdvv///z4CAEC/4EYmvwIAQL8NAAC+0bPdPgQAAL///z8/BAAgP8qz3b7kRia/0bPdPgQAAL///z8/BAAgP8qz3b7kRia/4EYmvwIAQL8NAAC+1rPdvv///z4CAEC/BAAgP8qz3b7kRia/1rPdvv///z4CAEC/BAAgP8qz3b7kRia/2kYmvwYAQL8fAAC+2LPdPv///74BAEA/AAAgv9ez3T7jRiY/2LPdPv///74BAEA/AAAgv9ez3T7jRiY/2kYmvwYAQL8fAAC+AAAgv9ez3T7jRiY/27PdvgEAAD/+/z+/AAAgv9ez3T7jRiY/27PdvgEAAD/+/z+/2kYmvwYAQL8fAAC+2LPdPv///74BAEA///8fP9az3b7kRia/2LPdPv///74BAEA///8fP9az3b7kRia/2kYmvwYAQL8fAAC+27PdvgEAAD/+/z+///8fP9az3b7kRia/27PdvgEAAD/+/z+///8fP9az3b7kRia/4UYmv/7/P7/7//+91rPdPgEAAL8CAEA/AgAgv9qz3T7gRiY/1rPdPgEAAL8CAEA/AgAgv9qz3T7gRiY/4UYmv/7/P7/7//+9AgAgv9qz3T7gRiY/3LPdvgIAAD/9/z+/AgAgv9qz3T7gRiY/3LPdvgIAAD/9/z+/4UYmv/7/P7/7//+91rPdPgEAAL8CAEA/AwAgP9iz3b7gRia/1rPdPgEAAL8CAEA/AwAgP9iz3b7gRia/4UYmv/7/P7/7//+93LPdvgIAAD/9/z+/AwAgP9iz3b7gRia/3LPdvgIAAD/9/z+/AwAgP9iz3b7gRia/5UYmv///P7/l//+937PdPvf//74AAEA/AwAgv9Kz3T7jRiY/37PdPvf//74AAEA/AwAgv9Kz3T7jRiY/5UYmv///P7/l//+9AwAgv9Kz3T7jRiY/1rPdvgcAAD/8/z+/AwAgv9Kz3T7jRiY/1rPdvgcAAD/8/z+/5UYmv///P7/l//+937PdPvf//74AAEA/AAAgP+uz3b7cRia/37PdPvf//74AAEA/AAAgP+uz3b7cRia/5UYmv///P7/l//+91rPdvgcAAD/8/z+/AAAgP+uz3b7cRia/1rPdvgcAAD/8/z+/AAAgP+uz3b7cRia/5kYmv/v/P78IAAC+2rPdPgAAAL/+/z8/AgAgv9uz3T7fRiY/2rPdPgAAAL/+/z8/AgAgv9uz3T7fRiY/1kYmPwoAQD8QAAA+5kYmv/v/P78IAAC+AgAgv9uz3T7fRiY/2LPdvv///z4AAEC/AgAgv9uz3T7fRiY/2LPdvv///z4AAEC/1kYmPwoAQD8QAAA+5kYmv/v/P78IAAC+2rPdPgAAAL/+/z8//P8fP9ez3b7kRia/2rPdPgAAAL/+/z8//P8fP9ez3b7kRia/1kYmPwoAQD8QAAA+5kYmv/v/P78IAAC+2LPdvv///z4AAEC//P8fP9ez3b7kRia/2LPdvv///z4AAEC//P8fP9ez3b7kRia/1kYmPwoAQD8QAAA+5bPdPvv//779/z8/AQAgv9Sz3T7iRiY/5bPdPvv//779/z8/AQAgv9Sz3T7iRiY/AQAgv9Sz3T7iRiY/3LPdvv3//z4BAEC/AQAgv9Sz3T7iRiY/3LPdvv3//z4BAEC/5bPdPvv//779/z8/AQAgP9Kz3b7jRia/5bPdPvv//779/z8/AQAgP9Kz3b7jRia/3LPdvv3//z4BAEC/AQAgP9Kz3b7jRia/3LPdvv3//z4BAEC/AQAgP9Kz3b7jRia/AgAEAAkAAgAJAAcACAAKABUACAAVABMAFAAWABAAFAAQAA4ADQAPAAMADQADAAEABgASAAwABgAMAAAAFwALAAUAFwAFABEAGgAcACAAGgAgAB4AHwAhACoAHwAqACgAKQArACYAKQAmACQAIwAlABsAIwAbABkAHQAnACIAHQAiABgARwBJAFIARwBSAFAALgAwADQALgA0ADIAMwA1AD4AMwA+ADwAPQA/ADoAPQA6ADgANwA5AC8ANwAvAC0AMQA7ADYAMQA2ACwAQgBEAEgAQgBIAEYAUQBTAE4AUQBOAEwASwBNAEMASwBDAEEARQBPAEoARQBKAEAAVgBYAFwAVgBcAFoAWwBdAGYAWwBmAGQAZQBnAGIAZQBiAGAAXwBhAFcAXwBXAFUAWQBjAF4AWQBeAFQAagBsAHEAagBxAG8AcAByAH0AcAB9AHsAfAB+AHgAfAB4AHYAdQB3AGsAdQBrAGkAbgB6AHQAbgB0AGgAfwBzAG0AfwBtAHkAgQCDAIYAgQCGAIQAhQCHAI4AhQCOAIwAjQCPAIsAjQCLAIkAiACKAIIAiACCAIAA" + } + ] +} diff --git a/OMI_seat/index.html b/OMI_seat/index.html new file mode 100644 index 0000000..c4f2772 --- /dev/null +++ b/OMI_seat/index.html @@ -0,0 +1,1030 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_seat - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_seat

+

Contributors

+
    +
  • Aaron Franke, The Mirror Megaverse Inc.
  • +
+

Status

+

Open Metaverse Interoperability Group Stage 2 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Optionally depends on the OMI_physics_body spec for triggers.

+

Overview

+

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.

+

Example:

+

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.

+

glTF Schema Updates

+

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.

+

Property Summary

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionDefault value
backnumber[3]The position limit on the seat corresponding to the character's back in local space.Required, no default
footnumber[3]The position limit on the seat corresponding to the character's feet in local space.Required, no default
kneenumber[3]The base position on the seat corresponding to the character's knees in local space.Required, no default
anglenumberThe 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.

+

Back

+

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.

+

Knee

+

The "knee" property is a vector as a three-number array that defines the base position of the character's knees in local space.

+

Angle

+

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.

+

Property Details

+

The positions defined above can be used to uniquely find other vectors used to place a character on the seat.

+
    +
  • The upper leg main direction is the direction from the back vector to the knee vector. This is also the forward direction.
  • +
  • The lower leg main direction is the direction from the knee vector to the foot vector.
  • +
  • The right direction is the normalized cross product of the lower leg main direction and the upper leg main direction.
  • +
  • The upper leg up direction is the cross product of the right direction and the upper leg main direction.
  • +
  • The lower leg forward direction is the cross product of the right direction and the lower leg main direction.
  • +
  • The spine direction can be found by rotating the upper leg main direction around the right vector by the angle property.
  • +
+

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.

+
    +
  • The IK position for the back/hips/spine is the back position, plus the upper leg thickness times the upper leg up direction, plus the spine thickness times the upper leg main direction.
  • +
  • The IK position for the feet is the foot position, plus the lower leg thickness times the lower leg forward direction, minus the foot thickness times the lower leg main direction.
  • +
  • The IK position for the knees is more complicated. It needs to be adjusted by both the upper leg and lower leg offsets. For perpendicular upper and lower legs, this is trivial, you just add the vectors together. Otherwise some trigonometry is required. The knee offset can be found by adding the upper leg offset with the upper leg direction multiplied by an adjustment scalar. The adjustment scalar can be found with the lower offset length divided by the sin of the angle between the legs, minus the upper offset length divided by the tan of the angle between the legs.
  • +
+

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.

+

JSON Schema

+

See schema/node.OMI_seat.schema.json for the schema.

+

Known Implementations

+
    +
  • Godot Engine add-on
  • +
+

Resources:

+
    +
  • None
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_seat/schema/node.OMI_seat.schema.json b/OMI_seat/schema/node.OMI_seat.schema.json new file mode 100644 index 0000000..13f08d5 --- /dev/null +++ b/OMI_seat/schema/node.OMI_seat.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_seat.schema.json", + "title": "OMI_seat glTF Node Extension", + "type": "object", + "description": "Parameters describing a seat for a character.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "back": { + "type": "array", + "description": "Three number array defining the seat position corresponding to the character's back position limit in local space." + }, + "foot": { + "type": "array", + "description": "Three number array defining the seat position corresponding to the character's feet position limit in local space." + }, + "knee": { + "type": "array", + "description": "Three number array defining the seat position corresponding to the character's knee base position in local space." + }, + "angle": { + "type": "number", + "description": "The angle between the spine and back-knee line in radians.", + "default": 1.57079632679489662 + }, + "extensions": { }, + "extras": { } + }, + "required": [ + "back", + "foot", + "knee" + ] +} diff --git a/OMI_spawn_point/examples/blue_spawn_point.gltf b/OMI_spawn_point/examples/blue_spawn_point.gltf new file mode 100644 index 0000000..ecfdeff --- /dev/null +++ b/OMI_spawn_point/examples/blue_spawn_point.gltf @@ -0,0 +1,130 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_spawn_point" + ], + "scene": 0, + "scenes": [ + { + "nodes": [0] + } + ], + "nodes": [ + { + "children": [1], + "mesh": 0, + "name": "BlueFlatSpawnPoint" + }, + { + "name": "BlueSpawnPoint", + "extensions": { + "OMI_spawn_point": { + "team": "Blue", + "title": "Spawn Point" + } + } + } + ], + "materials": [ + { + "doubleSided": true, + "name": "Material", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.0625, + 0.0625, + 0.5, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + } + ], + "meshes": [ + { + "name": "FlatSpawnPointMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3, + "material": 0 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 24, + "max": [ + 2.5, + -0.125, + 2.5 + ], + "min": [ + -2.5, + -0.375, + -2.5 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 24, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 288, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 480, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 72, + "byteOffset": 768, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "data:application/octet-stream;base64,AAAgwAAAwL4AACBAAAAgwAAAwL4AACBAAAAgwAAAwL4AACBAAAAgwAAAAL4AACBAAAAgwAAAAL4AACBAAAAgwAAAAL4AACBAAAAgwAAAwL4AACDAAAAgwAAAwL4AACDAAAAgwAAAwL4AACDAAAAgwAAAAL4AACDAAAAgwAAAAL4AACDAAAAgwAAAAL4AACDAAAAgQAAAwL4AACBAAAAgQAAAwL4AACBAAAAgQAAAwL4AACBAAAAgQAAAAL4AACBAAAAgQAAAAL4AACBAAAAgQAAAAL4AACBAAAAgQAAAwL4AACDAAAAgQAAAwL4AACDAAAAgQAAAwL4AACDAAAAgQAAAAL4AACDAAAAgQAAAAL4AACDAAAAgQAAAAL4AACDAAAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAgAEAAkAAgAJAAcACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAMADQADAAEABgASAAwABgAMAAAAFgALAAUAFgAFABAA" + } + ] +} diff --git a/OMI_spawn_point/examples/red_spawn_point.gltf b/OMI_spawn_point/examples/red_spawn_point.gltf new file mode 100644 index 0000000..612addb --- /dev/null +++ b/OMI_spawn_point/examples/red_spawn_point.gltf @@ -0,0 +1,130 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_spawn_point" + ], + "scene": 0, + "scenes": [ + { + "nodes": [0] + } + ], + "nodes": [ + { + "children": [1], + "mesh": 0, + "name": "RedFlatSpawnPoint" + }, + { + "name": "RedSpawnPoint", + "extensions": { + "OMI_spawn_point": { + "team": "Red", + "title": "Spawn Point" + } + } + } + ], + "materials": [ + { + "doubleSided": true, + "name": "Material", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.5, + 0.0625, + 0.0625, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 0.5 + } + } + ], + "meshes": [ + { + "name": "FlatSpawnPointMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3, + "material": 0 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 24, + "max": [ + 2.5, + -0.125, + 2.5 + ], + "min": [ + -2.5, + -0.375, + -2.5 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 24, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 288, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 480, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 72, + "byteOffset": 768, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "data:application/octet-stream;base64,AAAgwAAAwL4AACBAAAAgwAAAwL4AACBAAAAgwAAAwL4AACBAAAAgwAAAAL4AACBAAAAgwAAAAL4AACBAAAAgwAAAAL4AACBAAAAgwAAAwL4AACDAAAAgwAAAwL4AACDAAAAgwAAAwL4AACDAAAAgwAAAAL4AACDAAAAgwAAAAL4AACDAAAAgwAAAAL4AACDAAAAgQAAAwL4AACBAAAAgQAAAwL4AACBAAAAgQAAAwL4AACBAAAAgQAAAAL4AACBAAAAgQAAAAL4AACBAAAAgQAAAAL4AACBAAAAgQAAAwL4AACDAAAAgQAAAwL4AACDAAAAgQAAAwL4AACDAAAAgQAAAAL4AACDAAAAgQAAAAL4AACDAAAAgQAAAAL4AACDAAAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAgAEAAkAAgAJAAcACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAMADQADAAEABgASAAwABgAMAAAAFgALAAUAFgAFABAA" + } + ] +} diff --git a/OMI_spawn_point/examples/spawn_point.gltf b/OMI_spawn_point/examples/spawn_point.gltf new file mode 100644 index 0000000..dbc0a38 --- /dev/null +++ b/OMI_spawn_point/examples/spawn_point.gltf @@ -0,0 +1,112 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "OMI_spawn_point" + ], + "scene": 0, + "scenes": [ + { + "nodes": [0] + } + ], + "nodes": [ + { + "children": [1], + "mesh": 0, + "name": "FlatSpawnPoint" + }, + { + "name": "SpawnPoint", + "extensions": { + "OMI_spawn_point": { + "title": "Spawn Point" + } + } + } + ], + "meshes": [ + { + "name": "FlatSpawnPointMesh", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "TEXCOORD_0": 1, + "NORMAL": 2 + }, + "indices": 3 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 24, + "max": [ + 2.5, + -0.125, + 2.5 + ], + "min": [ + -2.5, + -0.375, + -2.5 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 24, + "type": "VEC2" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 24, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 0, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 192, + "byteOffset": 288, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 288, + "byteOffset": 480, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 72, + "byteOffset": 768, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "data:application/octet-stream;base64,AAAgwAAAwL4AACBAAAAgwAAAwL4AACBAAAAgwAAAwL4AACBAAAAgwAAAAL4AACBAAAAgwAAAAL4AACBAAAAgwAAAAL4AACBAAAAgwAAAwL4AACDAAAAgwAAAwL4AACDAAAAgwAAAwL4AACDAAAAgwAAAAL4AACDAAAAgwAAAAL4AACDAAAAgwAAAAL4AACDAAAAgQAAAwL4AACBAAAAgQAAAwL4AACBAAAAgQAAAwL4AACBAAAAgQAAAAL4AACBAAAAgQAAAAL4AACBAAAAgQAAAAL4AACBAAAAgQAAAwL4AACDAAAAgQAAAwL4AACDAAAAgQAAAwL4AACDAAAAgQAAAAL4AACDAAAAgQAAAAL4AACDAAAAgQAAAAL4AACDAAAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAgAEAAkAAgAJAAcACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAMADQADAAEABgASAAwABgAMAAAAFgALAAUAFgAFABAA" + } + ] +} diff --git a/OMI_spawn_point/index.html b/OMI_spawn_point/index.html new file mode 100644 index 0000000..284563b --- /dev/null +++ b/OMI_spawn_point/index.html @@ -0,0 +1,960 @@ + + + + + + + + + + + + + + + + + + + + + + + OMI_spawn_point - OMI glTF Extensions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + +
+
+ + + + +

OMI_spawn_point

+

Contributors

+
    +
  • OMI glTF Extensions Group
  • +
  • Robert Long, The Matrix.org Foundation
  • +
  • Anthony Burchell, Individual Contributor
  • +
  • Aaron Franke, The Mirror Megaverse Inc.
  • +
+

Status

+

Draft Specification

+

Open Metaverse Interoperability Group Stage 1 Proposal

+

Dependencies

+

Written against the glTF 2.0 spec.

+

Overview

+

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.

+

Example

+

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.

+

Properties

+

All of the properties are optional. An empty JSON object is a valid spawn point. All properties have a default value of null.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescription
titlestringThe title of the spawn point. This can be used as a user-facing name for the spawn point.
teamstringThe team that the spawn point is associated with. Ex: "Red", "Blue", "Humans", "Aliens", "Axis", "Allies".
groupstringThe 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.

+
    +
  • A game that does not display a menu for spawning does not have to display the title.
  • +
  • A game that displays a menu for spawning can generate custom text from the team, group, and/or node name if the title is not specified.
  • +
  • A game with no teams does not need to read or use the team property.
  • +
  • A game with teams can consider a spawn point without a team assigned as a fallback for when a player is not on a team.
  • +
  • A game with no reason to group together spawn points can ignore the group property.
  • +
+

JSON Schema

+

See schema/node.OMI_spawn_point.schema.json for the schema.

+

Known Implementations

+

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);
+    }
+});
+
+

Known Implementations

+

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/

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/OMI_spawn_point/schema/node.OMI_spawn_point.schema.json b/OMI_spawn_point/schema/node.OMI_spawn_point.schema.json new file mode 100644 index 0000000..e605040 --- /dev/null +++ b/OMI_spawn_point/schema/node.OMI_spawn_point.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.OMI_spawn_point.schema.json", + "title": "OMI_spawn_point", + "type": "object", + "description": "Parameters that define a spawn point.", + "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], + "properties": { + "title": { + "type": "string", + "description": "The title of the spawn point.", + "maxLength": 128 + }, + "team": { + "type": "string", + "description": "The team that this spawn point belongs to, if any.", + "maxLength": 128 + }, + "group": { + "type": "string", + "description": "The group that this spawn point belongs to, if any.", + "maxLength": 128 + }, + "extensions": { }, + "extras": { } + } +} diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000..1cf13b9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.081f42fc.min.js b/assets/javascripts/bundle.081f42fc.min.js new file mode 100644 index 0000000..32734cd --- /dev/null +++ b/assets/javascripts/bundle.081f42fc.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Fi=Object.create;var gr=Object.defineProperty;var ji=Object.getOwnPropertyDescriptor;var Wi=Object.getOwnPropertyNames,Dt=Object.getOwnPropertySymbols,Ui=Object.getPrototypeOf,xr=Object.prototype.hasOwnProperty,no=Object.prototype.propertyIsEnumerable;var oo=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,R=(e,t)=>{for(var r in t||(t={}))xr.call(t,r)&&oo(e,r,t[r]);if(Dt)for(var r of Dt(t))no.call(t,r)&&oo(e,r,t[r]);return e};var io=(e,t)=>{var r={};for(var o in e)xr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Dt)for(var o of Dt(e))t.indexOf(o)<0&&no.call(e,o)&&(r[o]=e[o]);return r};var yr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Di=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Wi(t))!xr.call(e,n)&&n!==r&&gr(e,n,{get:()=>t[n],enumerable:!(o=ji(t,n))||o.enumerable});return e};var Vt=(e,t,r)=>(r=e!=null?Fi(Ui(e)):{},Di(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var ao=(e,t,r)=>new Promise((o,n)=>{var i=p=>{try{s(r.next(p))}catch(c){n(c)}},a=p=>{try{s(r.throw(p))}catch(c){n(c)}},s=p=>p.done?o(p.value):Promise.resolve(p.value).then(i,a);s((r=r.apply(e,t)).next())});var co=yr((Er,so)=>{(function(e,t){typeof Er=="object"&&typeof so!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(Er,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(H){return!!(H&&H!==document&&H.nodeName!=="HTML"&&H.nodeName!=="BODY"&&"classList"in H&&"contains"in H.classList)}function p(H){var mt=H.type,ze=H.tagName;return!!(ze==="INPUT"&&a[mt]&&!H.readOnly||ze==="TEXTAREA"&&!H.readOnly||H.isContentEditable)}function c(H){H.classList.contains("focus-visible")||(H.classList.add("focus-visible"),H.setAttribute("data-focus-visible-added",""))}function l(H){H.hasAttribute("data-focus-visible-added")&&(H.classList.remove("focus-visible"),H.removeAttribute("data-focus-visible-added"))}function f(H){H.metaKey||H.altKey||H.ctrlKey||(s(r.activeElement)&&c(r.activeElement),o=!0)}function u(H){o=!1}function h(H){s(H.target)&&(o||p(H.target))&&c(H.target)}function w(H){s(H.target)&&(H.target.classList.contains("focus-visible")||H.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(H.target))}function A(H){document.visibilityState==="hidden"&&(n&&(o=!0),te())}function te(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function ie(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(H){H.target.nodeName&&H.target.nodeName.toLowerCase()==="html"||(o=!1,ie())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",A,!0),te(),r.addEventListener("focus",h,!0),r.addEventListener("blur",w,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var Yr=yr((Rt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Rt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Rt=="object"?Rt.ClipboardJS=r():t.ClipboardJS=r()})(Rt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Ii}});var a=i(279),s=i.n(a),p=i(370),c=i.n(p),l=i(817),f=i.n(l);function u(V){try{return document.execCommand(V)}catch(_){return!1}}var h=function(_){var O=f()(_);return u("cut"),O},w=h;function A(V){var _=document.documentElement.getAttribute("dir")==="rtl",O=document.createElement("textarea");O.style.fontSize="12pt",O.style.border="0",O.style.padding="0",O.style.margin="0",O.style.position="absolute",O.style[_?"right":"left"]="-9999px";var j=window.pageYOffset||document.documentElement.scrollTop;return O.style.top="".concat(j,"px"),O.setAttribute("readonly",""),O.value=V,O}var te=function(_,O){var j=A(_);O.container.appendChild(j);var D=f()(j);return u("copy"),j.remove(),D},ie=function(_){var O=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},j="";return typeof _=="string"?j=te(_,O):_ instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(_==null?void 0:_.type)?j=te(_.value,O):(j=f()(_),u("copy")),j},J=ie;function H(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?H=function(O){return typeof O}:H=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},H(V)}var mt=function(){var _=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=_.action,j=O===void 0?"copy":O,D=_.container,Y=_.target,ke=_.text;if(j!=="copy"&&j!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Y!==void 0)if(Y&&H(Y)==="object"&&Y.nodeType===1){if(j==="copy"&&Y.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(j==="cut"&&(Y.hasAttribute("readonly")||Y.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(ke)return J(ke,{container:D});if(Y)return j==="cut"?w(Y):J(Y,{container:D})},ze=mt;function Ie(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(O){return typeof O}:Ie=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},Ie(V)}function _i(V,_){if(!(V instanceof _))throw new TypeError("Cannot call a class as a function")}function ro(V,_){for(var O=0;O<_.length;O++){var j=_[O];j.enumerable=j.enumerable||!1,j.configurable=!0,"value"in j&&(j.writable=!0),Object.defineProperty(V,j.key,j)}}function Ai(V,_,O){return _&&ro(V.prototype,_),O&&ro(V,O),V}function Ci(V,_){if(typeof _!="function"&&_!==null)throw new TypeError("Super expression must either be null or a function");V.prototype=Object.create(_&&_.prototype,{constructor:{value:V,writable:!0,configurable:!0}}),_&&br(V,_)}function br(V,_){return br=Object.setPrototypeOf||function(j,D){return j.__proto__=D,j},br(V,_)}function Hi(V){var _=Pi();return function(){var j=Wt(V),D;if(_){var Y=Wt(this).constructor;D=Reflect.construct(j,arguments,Y)}else D=j.apply(this,arguments);return ki(this,D)}}function ki(V,_){return _&&(Ie(_)==="object"||typeof _=="function")?_:$i(V)}function $i(V){if(V===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return V}function Pi(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(V){return!1}}function Wt(V){return Wt=Object.setPrototypeOf?Object.getPrototypeOf:function(O){return O.__proto__||Object.getPrototypeOf(O)},Wt(V)}function vr(V,_){var O="data-clipboard-".concat(V);if(_.hasAttribute(O))return _.getAttribute(O)}var Ri=function(V){Ci(O,V);var _=Hi(O);function O(j,D){var Y;return _i(this,O),Y=_.call(this),Y.resolveOptions(D),Y.listenClick(j),Y}return Ai(O,[{key:"resolveOptions",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof D.action=="function"?D.action:this.defaultAction,this.target=typeof D.target=="function"?D.target:this.defaultTarget,this.text=typeof D.text=="function"?D.text:this.defaultText,this.container=Ie(D.container)==="object"?D.container:document.body}},{key:"listenClick",value:function(D){var Y=this;this.listener=c()(D,"click",function(ke){return Y.onClick(ke)})}},{key:"onClick",value:function(D){var Y=D.delegateTarget||D.currentTarget,ke=this.action(Y)||"copy",Ut=ze({action:ke,container:this.container,target:this.target(Y),text:this.text(Y)});this.emit(Ut?"success":"error",{action:ke,text:Ut,trigger:Y,clearSelection:function(){Y&&Y.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(D){return vr("action",D)}},{key:"defaultTarget",value:function(D){var Y=vr("target",D);if(Y)return document.querySelector(Y)}},{key:"defaultText",value:function(D){return vr("text",D)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(D){var Y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(D,Y)}},{key:"cut",value:function(D){return w(D)}},{key:"isSupported",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Y=typeof D=="string"?[D]:D,ke=!!document.queryCommandSupported;return Y.forEach(function(Ut){ke=ke&&!!document.queryCommandSupported(Ut)}),ke}}]),O}(s()),Ii=Ri},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,p){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(p))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(l,f,u,h,w){var A=c.apply(this,arguments);return l.addEventListener(u,A,w),{destroy:function(){l.removeEventListener(u,A,w)}}}function p(l,f,u,h,w){return typeof l.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(A){return s(A,f,u,h,w)}))}function c(l,f,u,h){return function(w){w.delegateTarget=a(w.target,f),w.delegateTarget&&h.call(l,w)}}o.exports=p},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function p(u,h,w){if(!u&&!h&&!w)throw new Error("Missing required arguments");if(!a.string(h))throw new TypeError("Second argument must be a String");if(!a.fn(w))throw new TypeError("Third argument must be a Function");if(a.node(u))return c(u,h,w);if(a.nodeList(u))return l(u,h,w);if(a.string(u))return f(u,h,w);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(u,h,w){return u.addEventListener(h,w),{destroy:function(){u.removeEventListener(h,w)}}}function l(u,h,w){return Array.prototype.forEach.call(u,function(A){A.addEventListener(h,w)}),{destroy:function(){Array.prototype.forEach.call(u,function(A){A.removeEventListener(h,w)})}}}function f(u,h,w){return s(document.body,u,h,w)}o.exports=p},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var p=window.getSelection(),c=document.createRange();c.selectNodeContents(i),p.removeAllRanges(),p.addRange(c),a=p.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var p=this.e||(this.e={});return(p[i]||(p[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var p=this;function c(){p.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),p=0,c=s.length;for(p;p{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var ts=/["'&<>]/;ei.exports=rs;function rs(e){var t=""+e,r=ts.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function N(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],a;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(s){a={error:s}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(a)throw a.error}}return i}function q(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||s(u,h)})})}function s(u,h){try{p(o[u](h))}catch(w){f(i[0][3],w)}}function p(u){u.value instanceof nt?Promise.resolve(u.value.v).then(c,l):f(i[0][2],u)}function c(u){s("next",u)}function l(u){s("throw",u)}function f(u,h){u(h),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mo(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof de=="function"?de(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(a){return new Promise(function(s,p){a=e[i](a),n(s,p,a.done,a.value)})}}function n(i,a,s,p){Promise.resolve(p).then(function(c){i({value:c,done:s})},a)}}function k(e){return typeof e=="function"}function ft(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var zt=ft(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function qe(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=de(a),p=s.next();!p.done;p=s.next()){var c=p.value;c.remove(this)}}catch(A){t={error:A}}finally{try{p&&!p.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(A){i=A instanceof zt?A.errors:[A]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=de(f),h=u.next();!h.done;h=u.next()){var w=h.value;try{fo(w)}catch(A){i=i!=null?i:[],A instanceof zt?i=q(q([],N(i)),N(A.errors)):i.push(A)}}}catch(A){o={error:A}}finally{try{h&&!h.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new zt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)fo(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&qe(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&qe(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Tr=Fe.EMPTY;function qt(e){return e instanceof Fe||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function fo(e){k(e)?e():e.unsubscribe()}var $e={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var ut={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,a=n.isStopped,s=n.observers;return i||a?Tr:(this.currentObservers=null,s.push(r),new Fe(function(){o.currentObservers=null,qe(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Eo(r,o)},t}(F);var Eo=function(e){re(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Tr},t}(g);var _r=function(e){re(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(g);var Lt={now:function(){return(Lt.delegate||Date).now()},delegate:void 0};var _t=function(e){re(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Lt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,p=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+p)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),p=0;p0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(vt);var So=function(e){re(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(gt);var Hr=new So(To);var Oo=function(e){re(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=bt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var a=r.actions;o!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==o&&(bt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(vt);var Mo=function(e){re(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(gt);var me=new Mo(Oo);var M=new F(function(e){return e.complete()});function Yt(e){return e&&k(e.schedule)}function kr(e){return e[e.length-1]}function Xe(e){return k(kr(e))?e.pop():void 0}function He(e){return Yt(kr(e))?e.pop():void 0}function Bt(e,t){return typeof kr(e)=="number"?e.pop():t}var xt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Gt(e){return k(e==null?void 0:e.then)}function Jt(e){return k(e[ht])}function Xt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function Zt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Gi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var er=Gi();function tr(e){return k(e==null?void 0:e[er])}function rr(e){return lo(this,arguments,function(){var r,o,n,i;return Nt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,nt(r.read())];case 3:return o=a.sent(),n=o.value,i=o.done,i?[4,nt(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,nt(n)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function or(e){return k(e==null?void 0:e.getReader)}function W(e){if(e instanceof F)return e;if(e!=null){if(Jt(e))return Ji(e);if(xt(e))return Xi(e);if(Gt(e))return Zi(e);if(Xt(e))return Lo(e);if(tr(e))return ea(e);if(or(e))return ta(e)}throw Zt(e)}function Ji(e){return new F(function(t){var r=e[ht]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Xi(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?v(function(n,i){return e(n,i,o)}):le,Te(1),r?Be(t):zo(function(){return new ir}))}}function Fr(e){return e<=0?function(){return M}:y(function(t,r){var o=[];t.subscribe(T(r,function(n){o.push(n),e=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new g}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,p=s===void 0?!0:s;return function(c){var l,f,u,h=0,w=!1,A=!1,te=function(){f==null||f.unsubscribe(),f=void 0},ie=function(){te(),l=u=void 0,w=A=!1},J=function(){var H=l;ie(),H==null||H.unsubscribe()};return y(function(H,mt){h++,!A&&!w&&te();var ze=u=u!=null?u:r();mt.add(function(){h--,h===0&&!A&&!w&&(f=Wr(J,p))}),ze.subscribe(mt),!l&&h>0&&(l=new at({next:function(Ie){return ze.next(Ie)},error:function(Ie){A=!0,te(),f=Wr(ie,n,Ie),ze.error(Ie)},complete:function(){w=!0,te(),f=Wr(ie,a),ze.complete()}}),W(H).subscribe(l))})(c)}}function Wr(e,t){for(var r=[],o=2;oe.next(document)),e}function $(e,t=document){return Array.from(t.querySelectorAll(e))}function P(e,t=document){let r=fe(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function fe(e,t=document){return t.querySelector(e)||void 0}function Re(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var xa=S(d(document.body,"focusin"),d(document.body,"focusout")).pipe(_e(1),Q(void 0),m(()=>Re()||document.body),B(1));function et(e){return xa.pipe(m(t=>e.contains(t)),K())}function kt(e,t){return C(()=>S(d(e,"mouseenter").pipe(m(()=>!0)),d(e,"mouseleave").pipe(m(()=>!1))).pipe(t?Ht(r=>Me(+!r*t)):le,Q(e.matches(":hover"))))}function Bo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Bo(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Bo(o,n);return o}function sr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function wt(e){let t=x("script",{src:e});return C(()=>(document.head.appendChild(t),S(d(t,"load"),d(t,"error").pipe(b(()=>$r(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),L(()=>document.head.removeChild(t)),Te(1))))}var Go=new g,ya=C(()=>typeof ResizeObserver=="undefined"?wt("https://unpkg.com/resize-observer-polyfill"):I(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>Go.next(t)))),b(e=>S(Ke,I(e)).pipe(L(()=>e.disconnect()))),B(1));function ce(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return ya.pipe(E(r=>r.observe(t)),b(r=>Go.pipe(v(o=>o.target===t),L(()=>r.unobserve(t)))),m(()=>ce(e)),Q(ce(e)))}function Tt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function cr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function Jo(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Ue(e){return{x:e.offsetLeft,y:e.offsetTop}}function Xo(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function Zo(e){return S(d(window,"load"),d(window,"resize")).pipe(Le(0,me),m(()=>Ue(e)),Q(Ue(e)))}function pr(e){return{x:e.scrollLeft,y:e.scrollTop}}function De(e){return S(d(e,"scroll"),d(window,"scroll"),d(window,"resize")).pipe(Le(0,me),m(()=>pr(e)),Q(pr(e)))}var en=new g,Ea=C(()=>I(new IntersectionObserver(e=>{for(let t of e)en.next(t)},{threshold:0}))).pipe(b(e=>S(Ke,I(e)).pipe(L(()=>e.disconnect()))),B(1));function tt(e){return Ea.pipe(E(t=>t.observe(e)),b(t=>en.pipe(v(({target:r})=>r===e),L(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function tn(e,t=16){return De(e).pipe(m(({y:r})=>{let o=ce(e),n=Tt(e);return r>=n.height-o.height-t}),K())}var lr={drawer:P("[data-md-toggle=drawer]"),search:P("[data-md-toggle=search]")};function rn(e){return lr[e].checked}function Je(e,t){lr[e].checked!==t&&lr[e].click()}function Ve(e){let t=lr[e];return d(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function wa(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ta(){return S(d(window,"compositionstart").pipe(m(()=>!0)),d(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function on(){let e=d(window,"keydown").pipe(v(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:rn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),v(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!wa(o,r)}return!0}),pe());return Ta().pipe(b(t=>t?M:e))}function xe(){return new URL(location.href)}function pt(e,t=!1){if(G("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function nn(){return new g}function an(){return location.hash.slice(1)}function sn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Sa(e){return S(d(window,"hashchange"),e).pipe(m(an),Q(an()),v(t=>t.length>0),B(1))}function cn(e){return Sa(e).pipe(m(t=>fe(`[id="${t}"]`)),v(t=>typeof t!="undefined"))}function $t(e){let t=matchMedia(e);return ar(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function pn(){let e=matchMedia("print");return S(d(window,"beforeprint").pipe(m(()=>!0)),d(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function Nr(e,t){return e.pipe(b(r=>r?t():M))}function zr(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let a=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+a*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function Ne(e,t){return zr(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),B(1))}function ln(e,t){let r=new DOMParser;return zr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),B(1))}function mn(e,t){let r=new DOMParser;return zr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),B(1))}function fn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function un(){return S(d(window,"scroll",{passive:!0}),d(window,"resize",{passive:!0})).pipe(m(fn),Q(fn()))}function dn(){return{width:innerWidth,height:innerHeight}}function hn(){return d(window,"resize",{passive:!0}).pipe(m(dn),Q(dn()))}function bn(){return z([un(),hn()]).pipe(m(([e,t])=>({offset:e,size:t})),B(1))}function mr(e,{viewport$:t,header$:r}){let o=t.pipe(Z("size")),n=z([o,r]).pipe(m(()=>Ue(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:a,size:s},{x:p,y:c}])=>({offset:{x:a.x-p,y:a.y-c+i},size:s})))}function Oa(e){return d(e,"message",t=>t.data)}function Ma(e){let t=new g;return t.subscribe(r=>e.postMessage(r)),t}function vn(e,t=new Worker(e)){let r=Oa(t),o=Ma(t),n=new g;n.subscribe(o);let i=o.pipe(X(),ne(!0));return n.pipe(X(),Pe(r.pipe(U(i))),pe())}var La=P("#__config"),St=JSON.parse(La.textContent);St.base=`${new URL(St.base,xe())}`;function ye(){return St}function G(e){return St.features.includes(e)}function Ee(e,t){return typeof t!="undefined"?St.translations[e].replace("#",t.toString()):St.translations[e]}function Se(e,t=document){return P(`[data-md-component=${e}]`,t)}function ae(e,t=document){return $(`[data-md-component=${e}]`,t)}function _a(e){let t=P(".md-typeset > :first-child",e);return d(t,"click",{once:!0}).pipe(m(()=>P(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function gn(e){if(!G("announce.dismiss")||!e.childElementCount)return M;if(!e.hidden){let t=P(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return C(()=>{let t=new g;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),_a(e).pipe(E(r=>t.next(r)),L(()=>t.complete()),m(r=>R({ref:e},r)))})}function Aa(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function xn(e,t){let r=new g;return r.subscribe(({hidden:o})=>{e.hidden=o}),Aa(e,t).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))}function Pt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function yn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function En(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Pt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Pt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function wn(e){return x("button",{class:"md-clipboard md-icon",title:Ee("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function qr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(p=>!e.terms[p]).reduce((p,c)=>[...p,x("del",null,c)," "],[]).slice(0,-1),i=ye(),a=new URL(e.location,i.base);G("search.highlight")&&a.searchParams.set("h",Object.entries(e.terms).filter(([,p])=>p).reduce((p,[c])=>`${p} ${c}`.trim(),""));let{tags:s}=ye();return x("a",{href:`${a}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(p=>{let c=s?p in s?`md-tag-icon md-tag--${s[p]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${c}`},p)}),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Ee("search.result.term.missing"),": ",...n)))}function Tn(e){let t=e[0].score,r=[...e],o=ye(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),a=r.findIndex(l=>l.scoreqr(l,1)),...p.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,p.length>0&&p.length===1?Ee("search.result.more.one"):Ee("search.result.more.other",p.length))),...p.map(l=>qr(l,1)))]:[]];return x("li",{class:"md-search-result__item"},c)}function Sn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?sr(r):r)))}function Qr(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function On(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Ca(e){var o;let t=ye(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Mn(e,t){var o;let r=ye();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Ee("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Ca)))}var Ha=0;function ka(e){let t=z([et(e),kt(e)]).pipe(m(([o,n])=>o||n),K()),r=C(()=>Jo(e)).pipe(oe(De),ct(1),m(()=>Xo(e)));return t.pipe(Ae(o=>o),b(()=>z([t,r])),m(([o,n])=>({active:o,offset:n})),pe())}function $a(e,t){let{content$:r,viewport$:o}=t,n=`__tooltip2_${Ha++}`;return C(()=>{let i=new g,a=new _r(!1);i.pipe(X(),ne(!1)).subscribe(a);let s=a.pipe(Ht(c=>Me(+!c*250,Hr)),K(),b(c=>c?r:M),E(c=>c.id=n),pe());z([i.pipe(m(({active:c})=>c)),s.pipe(b(c=>kt(c,250)),Q(!1))]).pipe(m(c=>c.some(l=>l))).subscribe(a);let p=a.pipe(v(c=>c),ee(s,o),m(([c,l,{size:f}])=>{let u=e.getBoundingClientRect(),h=u.width/2;if(l.role==="tooltip")return{x:h,y:8+u.height};if(u.y>=f.height/2){let{height:w}=ce(l);return{x:h,y:-16-w}}else return{x:h,y:16+u.height}}));return z([s,i,p]).subscribe(([c,{offset:l},f])=>{c.style.setProperty("--md-tooltip-host-x",`${l.x}px`),c.style.setProperty("--md-tooltip-host-y",`${l.y}px`),c.style.setProperty("--md-tooltip-x",`${f.x}px`),c.style.setProperty("--md-tooltip-y",`${f.y}px`),c.classList.toggle("md-tooltip2--top",f.y<0),c.classList.toggle("md-tooltip2--bottom",f.y>=0)}),a.pipe(v(c=>c),ee(s,(c,l)=>l),v(c=>c.role==="tooltip")).subscribe(c=>{let l=ce(P(":scope > *",c));c.style.setProperty("--md-tooltip-width",`${l.width}px`),c.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(K(),be(me),ee(s)).subscribe(([c,l])=>{l.classList.toggle("md-tooltip2--active",c)}),z([a.pipe(v(c=>c)),s]).subscribe(([c,l])=>{l.role==="dialog"?(e.setAttribute("aria-controls",n),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",n)}),a.pipe(v(c=>!c)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),ka(e).pipe(E(c=>i.next(c)),L(()=>i.complete()),m(c=>R({ref:e},c)))})}function lt(e,{viewport$:t},r=document.body){return $a(e,{content$:new F(o=>{let n=e.title,i=yn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t})}function Pa(e,t){let r=C(()=>z([Zo(e),De(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:a,height:s}=ce(e);return{x:o-i.x+a/2,y:n-i.y+s/2}}));return et(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Te(+!o||1/0))))}function Ln(e,t,{target$:r}){let[o,n]=Array.from(e.children);return C(()=>{let i=new g,a=i.pipe(X(),ne(!0));return i.subscribe({next({offset:s}){e.style.setProperty("--md-tooltip-x",`${s.x}px`),e.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),tt(e).pipe(U(a)).subscribe(s=>{e.toggleAttribute("data-md-visible",s)}),S(i.pipe(v(({active:s})=>s)),i.pipe(_e(250),v(({active:s})=>!s))).subscribe({next({active:s}){s?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Le(16,me)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(ct(125,me),v(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?e.style.setProperty("--md-tooltip-0",`${-s}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),d(n,"click").pipe(U(a),v(s=>!(s.metaKey||s.ctrlKey))).subscribe(s=>{s.stopPropagation(),s.preventDefault()}),d(n,"mousedown").pipe(U(a),ee(i)).subscribe(([s,{active:p}])=>{var c;if(s.button!==0||s.metaKey||s.ctrlKey)s.preventDefault();else if(p){s.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(c=Re())==null||c.blur()}}),r.pipe(U(a),v(s=>s===o),Ge(125)).subscribe(()=>e.focus()),Pa(e,t).pipe(E(s=>i.next(s)),L(()=>i.complete()),m(s=>R({ref:e},s)))})}function Ra(e){return e.tagName==="CODE"?$(".c, .c1, .cm",e):[e]}function Ia(e){let t=[];for(let r of Ra(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let a;for(;a=/(\(\d+\))(!)?/.exec(i.textContent);){let[,s,p]=a;if(typeof p=="undefined"){let c=i.splitText(a.index);i=c.splitText(s.length),t.push(c)}else{i.textContent=s,t.push(i);break}}}}return t}function _n(e,t){t.append(...Array.from(e.childNodes))}function fr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,a=new Map;for(let s of Ia(t)){let[,p]=s.textContent.match(/\((\d+)\)/);fe(`:scope > li:nth-child(${p})`,e)&&(a.set(p,En(p,i)),s.replaceWith(a.get(p)))}return a.size===0?M:C(()=>{let s=new g,p=s.pipe(X(),ne(!0)),c=[];for(let[l,f]of a)c.push([P(".md-typeset",f),P(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(p)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of c)l?_n(f,u):_n(u,f)}),S(...[...a].map(([,l])=>Ln(l,t,{target$:r}))).pipe(L(()=>s.complete()),pe())})}function An(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return An(t)}}function Cn(e,t){return C(()=>{let r=An(e);return typeof r!="undefined"?fr(r,e,t):M})}var Hn=Vt(Yr());var Fa=0;function kn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return kn(t)}}function ja(e){return ge(e).pipe(m(({width:t})=>({scrollable:Tt(e).width>t})),Z("scrollable"))}function $n(e,t){let{matches:r}=matchMedia("(hover)"),o=C(()=>{let n=new g,i=n.pipe(Fr(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let a=[];if(Hn.default.isSupported()&&(e.closest(".copy")||G("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${Fa++}`;let l=wn(c.id);c.insertBefore(l,e),G("content.tooltips")&&a.push(lt(l,{viewport$}))}let s=e.closest(".highlight");if(s instanceof HTMLElement){let c=kn(s);if(typeof c!="undefined"&&(s.classList.contains("annotate")||G("content.code.annotate"))){let l=fr(c,e,t);a.push(ge(s).pipe(U(i),m(({width:f,height:u})=>f&&u),K(),b(f=>f?l:M)))}}return $(":scope > span[id]",e).length&&e.classList.add("md-code__content"),ja(e).pipe(E(c=>n.next(c)),L(()=>n.complete()),m(c=>R({ref:e},c)),Pe(...a))});return G("content.lazy")?tt(e).pipe(v(n=>n),Te(1),b(()=>o)):o}function Wa(e,{target$:t,print$:r}){let o=!0;return S(t.pipe(m(n=>n.closest("details:not([open])")),v(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(v(n=>n||!o),E(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Pn(e,t){return C(()=>{let r=new g;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Wa(e,t).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))})}var Rn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var Br,Da=0;function Va(){return typeof mermaid=="undefined"||mermaid instanceof Element?wt("https://unpkg.com/mermaid@10/dist/mermaid.min.js"):I(void 0)}function In(e){return e.classList.remove("mermaid"),Br||(Br=Va().pipe(E(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Rn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),B(1))),Br.subscribe(()=>ao(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${Da++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),a=r.attachShadow({mode:"closed"});a.innerHTML=n,e.replaceWith(r),i==null||i(a)})),Br.pipe(m(()=>({ref:e})))}var Fn=x("table");function jn(e){return e.replaceWith(Fn),Fn.replaceWith(On(e)),I({ref:e})}function Na(e){let t=e.find(r=>r.checked)||e[0];return S(...e.map(r=>d(r,"change").pipe(m(()=>P(`label[for="${r.id}"]`))))).pipe(Q(P(`label[for="${t.id}"]`)),m(r=>({active:r})))}function Wn(e,{viewport$:t,target$:r}){let o=P(".tabbed-labels",e),n=$(":scope > input",e),i=Qr("prev");e.append(i);let a=Qr("next");return e.append(a),C(()=>{let s=new g,p=s.pipe(X(),ne(!0));z([s,ge(e)]).pipe(U(p),Le(1,me)).subscribe({next([{active:c},l]){let f=Ue(c),{width:u}=ce(c);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let h=pr(o);(f.xh.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([De(o),ge(o)]).pipe(U(p)).subscribe(([c,l])=>{let f=Tt(o);i.hidden=c.x<16,a.hidden=c.x>f.width-l.width-16}),S(d(i,"click").pipe(m(()=>-1)),d(a,"click").pipe(m(()=>1))).pipe(U(p)).subscribe(c=>{let{width:l}=ce(o);o.scrollBy({left:l*c,behavior:"smooth"})}),r.pipe(U(p),v(c=>n.includes(c))).subscribe(c=>c.click()),o.classList.add("tabbed-labels--linked");for(let c of n){let l=P(`label[for="${c.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),d(l.firstElementChild,"click").pipe(U(p),v(f=>!(f.metaKey||f.ctrlKey)),E(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return G("content.tabs.link")&&s.pipe(Ce(1),ee(t)).subscribe(([{active:c},{offset:l}])=>{let f=c.innerText.trim();if(c.hasAttribute("data-md-switching"))c.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let w of $("[data-tabs]"))for(let A of $(":scope > input",w)){let te=P(`label[for="${A.id}"]`);if(te!==c&&te.innerText.trim()===f){te.setAttribute("data-md-switching",""),A.click();break}}window.scrollTo({top:e.offsetTop-u});let h=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...h])])}}),s.pipe(U(p)).subscribe(()=>{for(let c of $("audio, video",e))c.pause()}),tt(e).pipe(b(()=>Na(n)),E(c=>s.next(c)),L(()=>s.complete()),m(c=>R({ref:e},c)))}).pipe(Qe(se))}function Un(e,{viewport$:t,target$:r,print$:o}){return S(...$(".annotate:not(.highlight)",e).map(n=>Cn(n,{target$:r,print$:o})),...$("pre:not(.mermaid) > code",e).map(n=>$n(n,{target$:r,print$:o})),...$("pre.mermaid",e).map(n=>In(n)),...$("table:not([class])",e).map(n=>jn(n)),...$("details",e).map(n=>Pn(n,{target$:r,print$:o})),...$("[data-tabs]",e).map(n=>Wn(n,{viewport$:t,target$:r})),...$("[title]",e).filter(()=>G("content.tooltips")).map(n=>lt(n,{viewport$:t})))}function za(e,{alert$:t}){return t.pipe(b(r=>S(I(!0),I(!1).pipe(Ge(2e3))).pipe(m(o=>({message:r,active:o})))))}function Dn(e,t){let r=P(".md-typeset",e);return C(()=>{let o=new g;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),za(e,t).pipe(E(n=>o.next(n)),L(()=>o.complete()),m(n=>R({ref:e},n)))})}var qa=0;function Qa(e,t){document.body.append(e);let{width:r}=ce(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=cr(t),n=typeof o!="undefined"?De(o):I({x:0,y:0}),i=S(et(t),kt(t)).pipe(K());return z([i,n]).pipe(m(([a,s])=>{let{x:p,y:c}=Ue(t),l=ce(t),f=t.closest("table");return f&&t.parentElement&&(p+=f.offsetLeft+t.parentElement.offsetLeft,c+=f.offsetTop+t.parentElement.offsetTop),{active:a,offset:{x:p-s.x+l.width/2-r/2,y:c-s.y+l.height+8}}}))}function Vn(e){let t=e.title;if(!t.length)return M;let r=`__tooltip_${qa++}`,o=Pt(r,"inline"),n=P(".md-typeset",o);return n.innerHTML=t,C(()=>{let i=new g;return i.subscribe({next({offset:a}){o.style.setProperty("--md-tooltip-x",`${a.x}px`),o.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),S(i.pipe(v(({active:a})=>a)),i.pipe(_e(250),v(({active:a})=>!a))).subscribe({next({active:a}){a?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Le(16,me)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(ct(125,me),v(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?o.style.setProperty("--md-tooltip-0",`${-a}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Qa(o,e).pipe(E(a=>i.next(a)),L(()=>i.complete()),m(a=>R({ref:e},a)))}).pipe(Qe(se))}function Ka({viewport$:e}){if(!G("header.autohide"))return I(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Ye(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),K()),o=Ve("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),K(),b(n=>n?r:I(!1)),Q(!1))}function Nn(e,t){return C(()=>z([ge(e),Ka(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),K((r,o)=>r.height===o.height&&r.hidden===o.hidden),B(1))}function zn(e,{header$:t,main$:r}){return C(()=>{let o=new g,n=o.pipe(X(),ne(!0));o.pipe(Z("active"),We(t)).subscribe(([{active:a},{hidden:s}])=>{e.classList.toggle("md-header--shadow",a&&!s),e.hidden=s});let i=ue($("[title]",e)).pipe(v(()=>G("content.tooltips")),oe(a=>Vn(a)));return r.subscribe(o),t.pipe(U(n),m(a=>R({ref:e},a)),Pe(i.pipe(U(n))))})}function Ya(e,{viewport$:t,header$:r}){return mr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=ce(e);return{active:o>=n}}),Z("active"))}function qn(e,t){return C(()=>{let r=new g;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=fe(".md-content h1");return typeof o=="undefined"?M:Ya(o,t).pipe(E(n=>r.next(n)),L(()=>r.complete()),m(n=>R({ref:e},n)))})}function Qn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),K()),n=o.pipe(b(()=>ge(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),Z("bottom"))));return z([o,n,t]).pipe(m(([i,{top:a,bottom:s},{offset:{y:p},size:{height:c}}])=>(c=Math.max(0,c-Math.max(0,a-p,i)-Math.max(0,c+p-s)),{offset:a-i,height:c,active:a-i<=p})),K((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function Ba(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return I(...e).pipe(oe(o=>d(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),B(1))}function Kn(e){let t=$("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=$t("(prefers-color-scheme: light)");return C(()=>{let i=new g;return i.subscribe(a=>{if(document.body.setAttribute("data-md-color-switching",""),a.color.media==="(prefers-color-scheme)"){let s=matchMedia("(prefers-color-scheme: light)"),p=document.querySelector(s.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");a.color.scheme=p.getAttribute("data-md-color-scheme"),a.color.primary=p.getAttribute("data-md-color-primary"),a.color.accent=p.getAttribute("data-md-color-accent")}for(let[s,p]of Object.entries(a.color))document.body.setAttribute(`data-md-color-${s}`,p);for(let s=0;sa.key==="Enter"),ee(i,(a,s)=>s)).subscribe(({index:a})=>{a=(a+1)%t.length,t[a].click(),t[a].focus()}),i.pipe(m(()=>{let a=Se("header"),s=window.getComputedStyle(a);return o.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(p=>(+p).toString(16).padStart(2,"0")).join("")})).subscribe(a=>r.content=`#${a}`),i.pipe(be(se)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Ba(t).pipe(U(n.pipe(Ce(1))),st(),E(a=>i.next(a)),L(()=>i.complete()),m(a=>R({ref:e},a)))})}function Yn(e,{progress$:t}){return C(()=>{let r=new g;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(E(o=>r.next({value:o})),L(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Gr=Vt(Yr());function Ga(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Bn({alert$:e}){Gr.default.isSupported()&&new F(t=>{new Gr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||Ga(P(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(E(t=>{t.trigger.focus()}),m(()=>Ee("clipboard.copied"))).subscribe(e)}function Gn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function Ja(e,t){let r=new Map;for(let o of $("url",e)){let n=P("loc",o),i=[Gn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let a of $("[rel=alternate]",o)){let s=a.getAttribute("href");s!=null&&i.push(Gn(new URL(s),t))}}return r}function ur(e){return mn(new URL("sitemap.xml",e)).pipe(m(t=>Ja(t,new URL(e))),ve(()=>I(new Map)))}function Xa(e,t){if(!(e.target instanceof Element))return M;let r=e.target.closest("a");if(r===null)return M;if(r.target||e.metaKey||e.ctrlKey)return M;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),I(new URL(r.href))):M}function Jn(e){let t=new Map;for(let r of $(":scope > *",e.head))t.set(r.outerHTML,r);return t}function Xn(e){for(let t of $("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return I(e)}function Za(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...G("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=fe(o),i=fe(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=Jn(document);for(let[o,n]of Jn(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Se("container");return je($("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),M}),X(),ne(document))}function Zn({location$:e,viewport$:t,progress$:r}){let o=ye();if(location.protocol==="file:")return M;let n=ur(o.base);I(document).subscribe(Xn);let i=d(document.body,"click").pipe(We(n),b(([p,c])=>Xa(p,c)),pe()),a=d(window,"popstate").pipe(m(xe),pe());i.pipe(ee(t)).subscribe(([p,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",p)}),S(i,a).subscribe(e);let s=e.pipe(Z("pathname"),b(p=>ln(p,{progress$:r}).pipe(ve(()=>(pt(p,!0),M)))),b(Xn),b(Za),pe());return S(s.pipe(ee(e,(p,c)=>c)),s.pipe(b(()=>e),Z("pathname"),b(()=>e),Z("hash")),e.pipe(K((p,c)=>p.pathname===c.pathname&&p.hash===c.hash),b(()=>i),E(()=>history.back()))).subscribe(p=>{var c,l;history.state!==null||!p.hash?window.scrollTo(0,(l=(c=history.state)==null?void 0:c.y)!=null?l:0):(history.scrollRestoration="auto",sn(p.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),d(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(Z("offset"),_e(100)).subscribe(({offset:p})=>{history.replaceState(p,"")}),s}var ri=Vt(ti());function oi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(0,ri.default)(a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function It(e){return e.type===1}function dr(e){return e.type===3}function ni(e,t){let r=vn(e);return S(I(location.protocol!=="file:"),Ve("search")).pipe(Ae(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:G("search.suggest")}}})),r}function ii({document$:e}){let t=ye(),r=Ne(new URL("../versions.json",t.base)).pipe(ve(()=>M)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:a,aliases:s})=>a===i||s.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>d(document.body,"click").pipe(v(i=>!i.metaKey&&!i.ctrlKey),ee(o),b(([i,a])=>{if(i.target instanceof Element){let s=i.target.closest("a");if(s&&!s.target&&n.has(s.href)){let p=s.href;return!i.target.closest(".md-version")&&n.get(p)===a?M:(i.preventDefault(),I(p))}}return M}),b(i=>ur(new URL(i)).pipe(m(a=>{let p=xe().href.replace(t.base,i);return a.has(p.split("#")[0])?new URL(p):new URL(i)})))))).subscribe(n=>pt(n,!0)),z([r,o]).subscribe(([n,i])=>{P(".md-header__topic").appendChild(Mn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let s=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(s)||(s=[s]);e:for(let p of s)for(let c of n.aliases.concat(n.version))if(new RegExp(p,"i").test(c)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let s of ae("outdated"))s.hidden=!1})}function ns(e,{worker$:t}){let{searchParams:r}=xe();r.has("q")&&(Je("search",!0),e.value=r.get("q"),e.focus(),Ve("search").pipe(Ae(i=>!i)).subscribe(()=>{let i=xe();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=et(e),n=S(t.pipe(Ae(It)),d(e,"keyup"),o).pipe(m(()=>e.value),K());return z([n,o]).pipe(m(([i,a])=>({value:i,focus:a})),B(1))}function ai(e,{worker$:t}){let r=new g,o=r.pipe(X(),ne(!0));z([t.pipe(Ae(It)),r],(i,a)=>a).pipe(Z("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(Z("focus")).subscribe(({focus:i})=>{i&&Je("search",i)}),d(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=P("header [for=__search]");return d(n,"click").subscribe(()=>e.focus()),ns(e,{worker$:t}).pipe(E(i=>r.next(i)),L(()=>r.complete()),m(i=>R({ref:e},i)),B(1))}function si(e,{worker$:t,query$:r}){let o=new g,n=tn(e.parentElement).pipe(v(Boolean)),i=e.parentElement,a=P(":scope > :first-child",e),s=P(":scope > :last-child",e);Ve("search").subscribe(l=>s.setAttribute("role",l?"list":"presentation")),o.pipe(ee(r),Ur(t.pipe(Ae(It)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:a.textContent=f.length?Ee("search.result.none"):Ee("search.result.placeholder");break;case 1:a.textContent=Ee("search.result.one");break;default:let u=sr(l.length);a.textContent=Ee("search.result.other",u)}});let p=o.pipe(E(()=>s.innerHTML=""),b(({items:l})=>S(I(...l.slice(0,10)),I(...l.slice(10)).pipe(Ye(4),Vr(n),b(([f])=>f)))),m(Tn),pe());return p.subscribe(l=>s.appendChild(l)),p.pipe(oe(l=>{let f=fe("details",l);return typeof f=="undefined"?M:d(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(v(dr),m(({data:l})=>l)).pipe(E(l=>o.next(l)),L(()=>o.complete()),m(l=>R({ref:e},l)))}function is(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=xe();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function ci(e,t){let r=new g,o=r.pipe(X(),ne(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),d(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),is(e,t).pipe(E(n=>r.next(n)),L(()=>r.complete()),m(n=>R({ref:e},n)))}function pi(e,{worker$:t,keyboard$:r}){let o=new g,n=Se("search-query"),i=S(d(n,"keydown"),d(n,"focus")).pipe(be(se),m(()=>n.value),K());return o.pipe(We(i),m(([{suggest:s},p])=>{let c=p.split(/([\s-]+)/);if(s!=null&&s.length&&c[c.length-1]){let l=s[s.length-1];l.startsWith(c[c.length-1])&&(c[c.length-1]=l)}else c.length=0;return c})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(v(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(v(dr),m(({data:s})=>s)).pipe(E(s=>o.next(s)),L(()=>o.complete()),m(()=>({ref:e})))}function li(e,{index$:t,keyboard$:r}){let o=ye();try{let n=ni(o.search,t),i=Se("search-query",e),a=Se("search-result",e);d(e,"click").pipe(v(({target:p})=>p instanceof Element&&!!p.closest("a"))).subscribe(()=>Je("search",!1)),r.pipe(v(({mode:p})=>p==="search")).subscribe(p=>{let c=Re();switch(p.type){case"Enter":if(c===i){let l=new Map;for(let f of $(":first-child [href]",a)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,h])=>h-u);f.click()}p.claim()}break;case"Escape":case"Tab":Je("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof c=="undefined")i.focus();else{let l=[i,...$(":not(details) > [href], summary, details[open] [href]",a)],f=Math.max(0,(Math.max(0,l.indexOf(c))+l.length+(p.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}p.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(v(({mode:p})=>p==="global")).subscribe(p=>{switch(p.type){case"f":case"s":case"/":i.focus(),i.select(),p.claim();break}});let s=ai(i,{worker$:n});return S(s,si(a,{worker$:n,query$:s})).pipe(Pe(...ae("search-share",e).map(p=>ci(p,{query$:s})),...ae("search-suggest",e).map(p=>pi(p,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ke}}function mi(e,{index$:t,location$:r}){return z([t,r.pipe(Q(xe()),v(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>oi(o.config)(n.searchParams.get("h"))),m(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)!=null&&a.offsetHeight){let p=s.textContent,c=o(p);c.length>p.length&&n.set(s,c)}for(let[s,p]of n){let{childNodes:c}=x("span",null,p);s.replaceWith(...Array.from(c))}return{ref:e,nodes:n}}))}function as(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:a},{offset:{y:s}}])=>(a=a+Math.min(n,Math.max(0,s-i))-n,{height:a,locked:s>=i+n})),K((i,a)=>i.height===a.height&&i.locked===a.locked))}function Jr(e,o){var n=o,{header$:t}=n,r=io(n,["header$"]);let i=P(".md-sidebar__scrollwrap",e),{y:a}=Ue(i);return C(()=>{let s=new g,p=s.pipe(X(),ne(!0)),c=s.pipe(Le(0,me));return c.pipe(ee(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*a}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),c.pipe(Ae()).subscribe(()=>{for(let l of $(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=ce(f);f.scrollTo({top:u-h/2})}}}),ue($("label[tabindex]",e)).pipe(oe(l=>d(l,"click").pipe(be(se),m(()=>l),U(p)))).subscribe(l=>{let f=P(`[id="${l.htmlFor}"]`);P(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),as(e,r).pipe(E(l=>s.next(l)),L(()=>s.complete()),m(l=>R({ref:e},l)))})}function fi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Ct(Ne(`${r}/releases/latest`).pipe(ve(()=>M),m(o=>({version:o.tag_name})),Be({})),Ne(r).pipe(ve(()=>M),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Be({}))).pipe(m(([o,n])=>R(R({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return Ne(r).pipe(m(o=>({repositories:o.public_repos})),Be({}))}}function ui(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ne(r).pipe(ve(()=>M),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Be({}))}function di(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return fi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ui(r,o)}return M}var ss;function cs(e){return ss||(ss=C(()=>{let t=__md_get("__source",sessionStorage);if(t)return I(t);if(ae("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return M}return di(e.href).pipe(E(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>M),v(t=>Object.keys(t).length>0),m(t=>({facts:t})),B(1)))}function hi(e){let t=P(":scope > :last-child",e);return C(()=>{let r=new g;return r.subscribe(({facts:o})=>{t.appendChild(Sn(o)),t.classList.add("md-source__repository--active")}),cs(e).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))})}function ps(e,{viewport$:t,header$:r}){return ge(document.body).pipe(b(()=>mr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),Z("hidden"))}function bi(e,t){return C(()=>{let r=new g;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(G("navigation.tabs.sticky")?I({hidden:!1}):ps(e,t)).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))})}function ls(e,{viewport$:t,header$:r}){let o=new Map,n=$(".md-nav__link",e);for(let s of n){let p=decodeURIComponent(s.hash.substring(1)),c=fe(`[id="${p}"]`);typeof c!="undefined"&&o.set(s,c)}let i=r.pipe(Z("height"),m(({height:s})=>{let p=Se("main"),c=P(":scope > :first-child",p);return s+.8*(c.offsetTop-p.offsetTop)}),pe());return ge(document.body).pipe(Z("height"),b(s=>C(()=>{let p=[];return I([...o].reduce((c,[l,f])=>{for(;p.length&&o.get(p[p.length-1]).tagName>=f.tagName;)p.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let h=f.offsetParent;for(;h;h=h.offsetParent)u+=h.offsetTop;return c.set([...p=[...p,l]].reverse(),u)},new Map))}).pipe(m(p=>new Map([...p].sort(([,c],[,l])=>c-l))),We(i),b(([p,c])=>t.pipe(jr(([l,f],{offset:{y:u},size:h})=>{let w=u+h.height>=Math.floor(s.height);for(;f.length;){let[,A]=f[0];if(A-c=u&&!w)f=[l.pop(),...f];else break}return[l,f]},[[],[...p]]),K((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([s,p])=>({prev:s.map(([c])=>c),next:p.map(([c])=>c)})),Q({prev:[],next:[]}),Ye(2,1),m(([s,p])=>s.prev.length{let i=new g,a=i.pipe(X(),ne(!0));if(i.subscribe(({prev:s,next:p})=>{for(let[c]of p)c.classList.remove("md-nav__link--passed"),c.classList.remove("md-nav__link--active");for(let[c,[l]]of s.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",c===s.length-1)}),G("toc.follow")){let s=S(t.pipe(_e(1),m(()=>{})),t.pipe(_e(250),m(()=>"smooth")));i.pipe(v(({prev:p})=>p.length>0),We(o.pipe(be(se))),ee(s)).subscribe(([[{prev:p}],c])=>{let[l]=p[p.length-1];if(l.offsetHeight){let f=cr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=ce(f);f.scrollTo({top:u-h/2,behavior:c})}}})}return G("navigation.tracking")&&t.pipe(U(a),Z("offset"),_e(250),Ce(1),U(n.pipe(Ce(1))),st({delay:250}),ee(i)).subscribe(([,{prev:s}])=>{let p=xe(),c=s[s.length-1];if(c&&c.length){let[l]=c,{hash:f}=new URL(l.href);p.hash!==f&&(p.hash=f,history.replaceState({},"",`${p}`))}else p.hash="",history.replaceState({},"",`${p}`)}),ls(e,{viewport$:t,header$:r}).pipe(E(s=>i.next(s)),L(()=>i.complete()),m(s=>R({ref:e},s)))})}function ms(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:a}})=>a),Ye(2,1),m(([a,s])=>a>s&&s>0),K()),i=r.pipe(m(({active:a})=>a));return z([i,n]).pipe(m(([a,s])=>!(a&&s)),K(),U(o.pipe(Ce(1))),ne(!0),st({delay:250}),m(a=>({hidden:a})))}function gi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new g,a=i.pipe(X(),ne(!0));return i.subscribe({next({hidden:s}){e.hidden=s,s?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(a),Z("height")).subscribe(({height:s})=>{e.style.top=`${s+16}px`}),d(e,"click").subscribe(s=>{s.preventDefault(),window.scrollTo({top:0})}),ms(e,{viewport$:t,main$:o,target$:n}).pipe(E(s=>i.next(s)),L(()=>i.complete()),m(s=>R({ref:e},s)))}function xi({document$:e,viewport$:t}){e.pipe(b(()=>$(".md-ellipsis")),oe(r=>tt(r).pipe(U(e.pipe(Ce(1))),v(o=>o),m(()=>r),Te(1))),v(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,lt(n,{viewport$:t}).pipe(U(e.pipe(Ce(1))),L(()=>n.removeAttribute("title")))})).subscribe(),e.pipe(b(()=>$(".md-status")),oe(r=>lt(r,{viewport$:t}))).subscribe()}function yi({document$:e,tablet$:t}){e.pipe(b(()=>$(".md-toggle--indeterminate")),E(r=>{r.indeterminate=!0,r.checked=!1}),oe(r=>d(r,"change").pipe(Dr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ee(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function fs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ei({document$:e}){e.pipe(b(()=>$("[data-md-scrollfix]")),E(t=>t.removeAttribute("data-md-scrollfix")),v(fs),oe(t=>d(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function wi({viewport$:e,tablet$:t}){z([Ve("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>I(r).pipe(Ge(r?400:100))),ee(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function us(){return location.protocol==="file:"?wt(`${new URL("search/search_index.js",Xr.base)}`).pipe(m(()=>__index),B(1)):Ne(new URL("search/search_index.json",Xr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ot=Yo(),jt=nn(),Ot=cn(jt),Zr=on(),Oe=bn(),hr=$t("(min-width: 960px)"),Si=$t("(min-width: 1220px)"),Oi=pn(),Xr=ye(),Mi=document.forms.namedItem("search")?us():Ke,eo=new g;Bn({alert$:eo});var to=new g;G("navigation.instant")&&Zn({location$:jt,viewport$:Oe,progress$:to}).subscribe(ot);var Ti;((Ti=Xr.version)==null?void 0:Ti.provider)==="mike"&&ii({document$:ot});S(jt,Ot).pipe(Ge(125)).subscribe(()=>{Je("drawer",!1),Je("search",!1)});Zr.pipe(v(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=fe("link[rel=prev]");typeof t!="undefined"&&pt(t);break;case"n":case".":let r=fe("link[rel=next]");typeof r!="undefined"&&pt(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});xi({viewport$:Oe,document$:ot});yi({document$:ot,tablet$:hr});Ei({document$:ot});wi({viewport$:Oe,tablet$:hr});var rt=Nn(Se("header"),{viewport$:Oe}),Ft=ot.pipe(m(()=>Se("main")),b(e=>Qn(e,{viewport$:Oe,header$:rt})),B(1)),ds=S(...ae("consent").map(e=>xn(e,{target$:Ot})),...ae("dialog").map(e=>Dn(e,{alert$:eo})),...ae("header").map(e=>zn(e,{viewport$:Oe,header$:rt,main$:Ft})),...ae("palette").map(e=>Kn(e)),...ae("progress").map(e=>Yn(e,{progress$:to})),...ae("search").map(e=>li(e,{index$:Mi,keyboard$:Zr})),...ae("source").map(e=>hi(e))),hs=C(()=>S(...ae("announce").map(e=>gn(e)),...ae("content").map(e=>Un(e,{viewport$:Oe,target$:Ot,print$:Oi})),...ae("content").map(e=>G("search.highlight")?mi(e,{index$:Mi,location$:jt}):M),...ae("header-title").map(e=>qn(e,{viewport$:Oe,header$:rt})),...ae("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Nr(Si,()=>Jr(e,{viewport$:Oe,header$:rt,main$:Ft})):Nr(hr,()=>Jr(e,{viewport$:Oe,header$:rt,main$:Ft}))),...ae("tabs").map(e=>bi(e,{viewport$:Oe,header$:rt})),...ae("toc").map(e=>vi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Ot})),...ae("top").map(e=>gi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Ot})))),Li=ot.pipe(b(()=>hs),Pe(ds),B(1));Li.subscribe();window.document$=ot;window.location$=jt;window.target$=Ot;window.keyboard$=Zr;window.viewport$=Oe;window.tablet$=hr;window.screen$=Si;window.print$=Oi;window.alert$=eo;window.progress$=to;window.component$=Li;})(); +//# sourceMappingURL=bundle.081f42fc.min.js.map + diff --git a/assets/javascripts/bundle.081f42fc.min.js.map b/assets/javascripts/bundle.081f42fc.min.js.map new file mode 100644 index 0000000..e055db5 --- /dev/null +++ b/assets/javascripts/bundle.081f42fc.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n *\n * @class BehaviorSubject\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an