Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OMI_collider #63

Closed
wants to merge 9 commits into from
259 changes: 259 additions & 0 deletions extensions/2.0/OMI_collider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
<!-- Based on structure of the OMI_audio_emitter -->

# OMI_collider

## Contributors

* Mauve Signweaver, Mauve Software Inc.
* YOUR NAME HERE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add all contributors to the spec.


## Status

Open Metaverse Interoperability Group Stage 1 Proposal

## Dependencies

Written against the glTF 2.0 spec.

## Overview

This extension allows for the addition of colliders to gLTF scenes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo, gLTF should be either glTF or GLTF (I see both of these in the file).


Colliders can be added to GLTF nodes along with information about the "type" of collider it is representing.

This extension does not perscribe what Colliders should do within a scene, but engines may choose to treat colliders as Static physics bodies by default.
For a more thorough physics body specification, implement the `OMI_physics_body` extension.

### Example:

```json=
{
"nodes": [
{
"name": "ball",
"extensions": {
"OMI_collider": {
"type": "sphere",
"radius": 0.5
}
}
}
]
}
```

## glTF Scheme Updates

This extension consists of a new `Collider` data structure which can be added to a glTF `Node` in an an object called `OMI_collider` added to the `extensions` object within the `Node` object.

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.

### Example:


```json=
{
"extensionsUsed": [
"OMI_collider"
],
"nodes": [
{
"name": "ball",
"extensions": {
"OMI_collider": {
"type": "sphere",
"radius": 0.5
}
}
}
]
}
```

### `isTrigger`

Specifying `isTrigger: true` in the collider hints to engines that this collider should not be used for physics interactions and should exist as a "trigger volume" which emits events when an entity intersects with it.
By default an engine may assume that the collider is a static (or rigid) physics body.
Copy link
Member

@aaronfranke aaronfranke Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isTrigger should be removed from OMI_collider, it's out-of-scope to define usage for the collision shapes in this extension, and it's already a part of OMI_physics_body.

EDIT: Hmm, good points below, see my latest comment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some engines combine whether a collider is a trigger or not with the physics body (I think it was unreal) so I'm not sure I agree with that. 🤔

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm in my opinion I think "isTrigger" actually should be here in OMI_collider and not in OMI_physics, a collider may be a trigger without the need of having a rigidbody (physics), but a rigidbody -without- a collider, wont work with "isTrigger" flag.

Also, Im taking as reference from how its applied in Unity3D (https://docs.unity3d.com/ScriptReference/Collider.html) where isTrigger is applied from Collider component, and also im taking as reference how is applied in Rapier3D (https://rapier.rs/docs/user_guides/javascript/colliders) where sensor is set from Collider definition

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@memelotsqui Good points! In Godot, a trigger needs an Area node as a parent of the colliders.

How does it work in Unity if you want multiple collider shapes to trigger the same thing?

If we have this API on both the colliders and physics bodies, we can still import this in Godot, but we should specify how these are imported. Perhaps if a trigger collider has a parent physics object of type trigger, then they use that parent physics object Area node (and possibly multiple shapes could use it, not sure how this would work in Unity). Otherwise if trigger colliders are on their own without a trigger physics object parent, we generate 2 nodes in Godot, an Area and a collider shape child, or 1 GameObject with a component in Unity, and then this trigger is on its own.

If we move this API to only be on the colliders and not on the physics bodies, then this basically means that we would always have to generate 2 nodes in Godot, and always 1 GameObject with a component in Unity, and it wouldn't be possible to make a trigger have multiple shapes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding for Godot is that you can add an Area node as a child of a physics body, and then put all of the "isTrigger" collision shapes as children of the area node, while adding the non-isTrigger shapes as direct children of the physics body itself.

I have seen multiple posts online that use this trick inside of Godot to detect collision events separate from the body itself, such as: https://godotengine.org/qa/92172/collision-detection-independent-of-a-kinematicbody

It would be good to make a demo project so we can show that this works, but I'm pretty sure it is possible.


### Collider Types

We've looked at the different types of collision shapes between major game engines (Unity/Unreal/Godot) and looked at what properties are common between them.

Colliders inherit whatever transform is given to them by the `node` they are attached to. This includes rotation and translation, however it is discouraged to scale these nodes as that can cause problems in some physics engines.

TODO: There's limits on the sorts of transforms, Unreal can't rotate colliders, has to be axis aligned (local axis to the game component) Maybe only more restrictive only when on a rigid body direcly.

Here is a table of what the shapes can be represented with in different game engines:

| Shape | Unity | Godot | Unreal | Ammo |
| -------- | ------------ | --------------------- | ------- | ---------- |
| Box | Box | BoxShape | Box | Box |
| Sphere | Sphere | SphereShape | Sphere | Sphere |
| Capsule | Capsule | CapsuleShape | Capsule | Capsule |
| Hull | Mesh(convex) | ConvexPolygonShape | Mesh? | ConvexHull |
| Mesh | Mesh | ConcavePolygonShape | Mesh | Mesh |
| Compound | Compound | Add mutiple colliders | ??? | ??? |
Comment on lines +88 to +95
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I highly recommend adding cylinders to this list. Cylinders are very useful, I would use them extensively. It's the perfect shape to use for barrels and circular platforms (which I'd like to store in GLTF files).

For engines that do not support cylinders, they can be approximated with capsules, and we can provide guidelines of what to do if this is needed. Maybe even allow providing extra data to hint at what the best way to approximate the shape is (like shrink/grow/average)? We could also discourage the use of cylinders in general, but I think it's very much worth having it in the standard since there are many use cases.



#### Box

This represents a "box" shape which can either be a cube or an arbitrary rectangular prism. You can specify an `extents` property which is an array that contains the `[x, y, z]` sizes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider making this size instead of extents, and making this be an array representing the "diameter" of the box. I think it's noticeably more intuitive this way, so much so that Godot switched from extents to size in Godot 4.0.

Also, like Sphere with a default radius of 0.5, we should specify that a box has a default size of [1, 1, 1].

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I came up with these names from looking at existing properties in different engines. Are any engines using a generic extents for everything?


#### Sphere

This represents a "ball" shape which has a `radius` property which must be set to 0.5 if a `radius` field is omitted.

#### Capsule

This represents a "pill" shape which has a `radius` and `height` property. By default the `capsule`'s height is aligned with the vertical axis. If you wish to align it along a different axis, apply the appropriate transform to the `node`. The `radius` field must be set to `0.5` if omitted, and the `height` must be set to `1` if omitted. The final height of the capsule is `height + radius * 2`. TODO: Improve wording?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From our meeting: Is there any way to represent a cylinder with a capsule? Is that currently the right way to represent a cylinder in a physics engine that doesn't support cylinders?


#### Hull

This type represents "convex hull" meshes which can be used to represent complex shapes in 3D. Note that it being "convex" means that it cannot have "holes" or divets that lead inside. This use can can be optimized by most if not all physics engines to improve speeds compared to doing collision detection on all faces within the mesh.

The `mesh` property of the extension must be used to link to an element in the `meshes` array. Note that the mesh MUST be a `trimesh` to work.

Due to limitations of some game engines (e.g. Unity), authors SHOULD limit the polygon count to 255 vertexes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hull meshes can be used to be used compound, multiples of 255 vertices compunded.

#### Mesh

Avoid using this type unless you're absolutely sure you need this level of detail.
Most objects can be represented with a convex hull (or a combination of convex hulls) or a set of primitives and will perform drastically better. It is recommended to use one or more convex hull mesh or primitive collider.

This type represents an arbitrary 3D mesh for collision detection which can be useful for shapes that cannot be represented by a convex hull. (e.g. something you can go inside of).

The `mesh` property of the extension must be used to link to an element in the `meshes` array. Note that the mesh MUST be a `trimesh` to work.

TODO: Same limits as regular meshes in GLTF

#### Compound

Compound colliders can be used to group several colliders together in order to give an entity a complex shape using several primitives without resorting to an entire mesh.

A `Node` set to be a `compund` collider will use any direct child nodes with the `OMI_collider` extension as part of it's collision shape.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compund -> compound


### Using Colliders

### JSON Schema
```json=
{
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Collider",
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"description": "The type of collider this node represents",
"enum": [
"box",
"sphere",
"capsule",
"hull",
"mesh",
"compound"
]
},
"isTrigger": {
"type": "boolean",
"description": "When true this collider will not collide with physics bodies in the scene",
"default": false
}
},
"oneOf": [
{
"properties": {
"required": [
"type",
"extents"
],
"type": {
"const": "box"
},
"extents": {
"type": "array",
"description": "half-extents for a Box (x, y, z)."
}
}
},
{
"properties": {
"required": [
"type",
"radius",
"height"
],
"type": {
"const": "sphere"
},
"radius": {
"type": "number",
"description": "The radius to use for the collision shape. Applies to sphere/capsule"
},
"height": {
"type": "number",
"description": "The height of the capsule shape."
}
}
},
{
"properties": {
"required": [
"type",
"mesh"
],
"type": {
"const": "capsule"
}
}
},
{
"properties": {
"required": [
"type",
"mesh"
],
"type": {
"enum": [
"mesh",
"hull"
]
},
"mesh": {
"description": "A reference to the mesh from the `meshes` array to use for either the Hull or Mesh shapes. The mesh MUST be a trimesh. Make sure your mesh is actually a convex hull if you use Hull, and try to avoid a full Mesh since they are very slow.",
"allOf": [
{
"$ref": "glTFid.schema.json"
}
]
}
}
},
{
"properties": {
"required": [
"type"
],
"type": {
"const": "compound"
}
}
}
]
}
```

## Known Implementations

TODO

## Resources:

- Godot Collision Shapes (Box, Sphere, Capsule, Convvex, Concave): https://docs.godotengine.org/en/stable/classes/class_sphereshape.html#class-sphereshape
- Unity Colliders (Box, Sphere, Capsule, Mesh / Convex Hull): https://docs.unity3d.com/Manual/CollidersOverview.html
- Unreal Engine Collision Shapes (Sphere, Capsule, Box, or Line): 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/
- Mozilla Hubs ammo-shape (Box, Sphere, Hull, Mesh): https://github.com/MozillaReality/hubs-blender-exporter/blob/bb28096159e1049b6b80da00b1ae1534a6ca0855/default-config.json#L608
- Babylon AmmoJS MeshBuilder (Box, Capsule, Cylinder, ???): https://doc.babylonjs.com/typedoc#meshbuilder