-
Notifications
You must be signed in to change notification settings - Fork 1.2k
KHR_implicit_shapes extension Draft Proposal #2370
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
Open
eoineoineoin
wants to merge
18
commits into
KhronosGroup:main
Choose a base branch
from
eoineoineoin:collisionShapeMerge
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
1778abe
Merge of KHR_collision_shapes extension
eoineoineoin fddb953
Add ability to skin/morph convex and trimesh shapes
eoineoineoin c8bcbd3
Feedback from review.
eoineoineoin 757ab3b
Convert some minimum properties to exclusiveMinimum, to avoid some de…
eoineoineoin f11f3ca
Merge `convex` and `trimesh` objects
eoineoineoin ef428bc
Add language prohibiting degenerate shapes
eoineoineoin 471f010
Various pieces of feedback:
eoineoineoin d04f5ab
Merge latest set of changes from development repository.
eoineoineoin ee968b6
Remove unnecessary known implementations section
eoineoineoin c1e2fb3
Merge changes from refactor of KHR_collision_shapes to KHR_implicit_s…
eoineoineoin 682e8ee
Feedback, rewording on cones/cylinders
eoineoineoin 52f219a
Clarifications on "type"
eoineoineoin f86a12c
Merge 35cc5dd4746b43bd from origin repo
eoineoineoin 185ad06
Add explicit links to every schema
eoineoineoin f6ecac3
Bump status to Release Candidate
eoineoineoin 7fce461
Trivial formatting changes; output not affected
eoineoineoin 16d6bff
Add planes to KHR_implicit_shapes
eoineoineoin f817a1b
Minor clarifications
eoineoineoin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| # KHR_collision_shapes | ||
|
|
||
| ## Contributors | ||
|
|
||
| * Eoin Mcloughlin, Microsoft, <mailto:[email protected]> | ||
| * Rory Mullane, Microsoft, <mailto:[email protected]> | ||
| * George Tian, Microsoft, <mailto:[email protected]> | ||
| * Aaron Franke, Godot Engine, <mailto:[email protected]> | ||
|
|
||
| ## Status | ||
|
|
||
| Draft | ||
|
|
||
| ## Dependencies | ||
|
|
||
| Written against glTF 2.0 spec. | ||
|
|
||
| ## Overview | ||
|
|
||
| This extension adds the ability to specify collision primitives inside a glTF asset. This extension does not mandate any particular behaviour for those objects aside from their collision geometry. These types are to be used in combination with other extensions that reference these collision primitives. | ||
|
|
||
| ## glTF Schema Updates | ||
|
|
||
| ### Shapes | ||
|
|
||
| This extension provides a set of document-level objects, which can be referenced by objects in the scene. The precise usage of these primitives should be specified by the extensions which utilize the shapes. The intended purpose of these these objects are to specify geometry which can be used for collision detection, which has informed the set of shapes which we have defined. Typically, the geometry specified by the shape will be simpler than any render meshes used by the node or its children, enabling real-time applications to perform queries such as intersection tests. | ||
|
|
||
| Each shape defines a mandatory `type` property which designates the type of shape, as well as an additional structure which provides parameterizations specific to that type. | ||
|
|
||
| To describe the geometry which represents the object, shapes must define at most one of the following properties: | ||
|
|
||
| | |Type|Description| | ||
| |-|-|-| | ||
| |**sphere**|`object`|A sphere centered at the origin in local space.| | ||
| |**box**|`object`|An axis-aligned box centered at the origin in local space.| | ||
| |**cylinder**|`object`|A cylinder centered at the origin and aligned along the Y axis in local space, with potentially different radii at each end.| | ||
| |**capsule**|`object`|A capsule (cylinder with hemispherical ends) centered at the origin and defined by two "capping" spheres with potentially different radii, aligned along the Y axis in local space.| | ||
| |**mesh**|`object`|A shape generated from a `mesh` object.| | ||
|
|
||
| The sphere, box, capsule, and cylinder all represent convex objects with a volume. However, the mesh type always represents an infinitely thin shell or sheet - for example a trimesh created from a `mesh` object in the shape of a box will be represented as a hollow box. | ||
|
|
||
| If a shape is required to have an offset from the local space of the node the shape is associated with (for example a sphere _not_ centered at local origin or a rotated box,) a child node should be added with the desired offset applied, and the shape properties added to that child. | ||
|
|
||
| As both the `mesh` shape references a `mesh`, it additionally allows for optional `skin` and `weights` properties, which have the same semantics and requirements enforced by the properties of the same name associated with a `node`. When specified on a `mesh` whose `convex` property is `true`, the resulting collision shape should be the convex hull of the deformed mesh. As collision detection is typically performed on CPU, the performance impact of deforming a mesh in such a use-case is typically higher than inside a vertex shader. As such, use of this functionality should be given careful consideration with respect to performance. When `convex` is `true`, the referenced mesh need not necessarily be convex itself, nor is there any requirement for the geometry to be closed. An implementation must generate a convex hull from the input vertices. | ||
|
|
||
| Degenerate shapes are prohibited. A `sphere` must have a positive, non-zero radius. `box` shapes must have positive non-zero values for each component of `size`. `cylinder` and `capsule` shapes must have a positive, non-zero `height` and both `radiusTop` and `radiusBottom` should be positive; at least one of the radii should be non-zero. For `mesh` shapes whose `convex` property is `false`, the referenced mesh must contain at least one non-degenerate triangle primitive. For `mesh` shapes whose `convex` property is `true`, the referenced mesh must contain contain primitives with at least four non-coplanar vertices. | ||
|
|
||
| ### JSON Schema | ||
|
|
||
| * **JSON schema**: [glTF.KHR_collision_shapes.schema.json](schema/glTF.KHR_collision_shapes.schema.json) | ||
|
|
||
| ### Object Model | ||
|
|
||
| With consideration to the glTF 2.0 Asset Object Model Specification document, the following pointer templates represent mutable properties defined in this extension. | ||
|
|
||
| | Pointer | Type| | ||
| |-|-| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/box/size` | `float3`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/capsule/height` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/capsule/radiusBottom` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/capsule/radiusTop` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/cylinder/height` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/cylinder/radiusBottom` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/cylinder/radiusTop` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/sphere/radius` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/mesh/weights/{}` | `float`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/mesh/weights` | `float[]`| | ||
|
|
||
| Additional read-only properties | ||
|
|
||
| | Pointer | Type| | ||
| |-|-| | ||
| | `/extensions/KHR_collision_shapes/shapes.length` | `int`| | ||
lexaknyazev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| | `/extensions/KHR_collision_shapes/shapes/{}/mesh/weights.length` | `int`| | ||
| | `/extensions/KHR_collision_shapes/shapes/{}/mesh/convex` | `boolean`| | ||
|
|
||
| ## Known Implementations | ||
|
|
||
| [Blender importer/exporter](https://github.com/eoineoineoin/glTF_Physics_Blender_Exporter) | ||
|
|
||
| [Babylon.js importer](https://github.com/eoineoineoin/glTF_Physics_Babylon) | ||
|
|
||
| [Godot importer](https://github.com/eoineoineoin/glTF_Physics_Godot_Importer) | ||
|
|
||
| ## Appendix: Full Khronos Copyright Statement | ||
|
|
||
| Copyright 2021-2023 The Khronos Group Inc. | ||
|
|
||
| This specification is protected by copyright laws and contains material proprietary | ||
| to Khronos. Except as described by these terms, it or any components | ||
| may not be reproduced, republished, distributed, transmitted, displayed, broadcast, | ||
| or otherwise exploited in any manner without the express prior written permission | ||
| of Khronos. | ||
|
|
||
| This specification has been created under the Khronos Intellectual Property Rights | ||
| Policy, which is Attachment A of the Khronos Group Membership Agreement available at | ||
| https://www.khronos.org/files/member_agreement.pdf. Khronos grants a conditional | ||
| copyright license to use and reproduce the unmodified specification for any purpose, | ||
| without fee or royalty, EXCEPT no licenses to any patent, trademark or other | ||
| intellectual property rights are granted under these terms. Parties desiring to | ||
| implement the specification and make use of Khronos trademarks in relation to that | ||
| implementation, and receive reciprocal patent license protection under the Khronos | ||
| IP Policy must become Adopters under the process defined by Khronos for this specification; | ||
| see https://www.khronos.org/conformance/adopters/file-format-adopter-program. | ||
|
|
||
| Some parts of this Specification are purely informative and do not define requirements | ||
| necessary for compliance and so are outside the Scope of this Specification. These | ||
| parts of the Specification are marked as being non-normative, or identified as | ||
| **Implementation Notes**. | ||
|
|
||
| Where this Specification includes normative references to external documents, only the | ||
| specifically identified sections and functionality of those external documents are in | ||
| Scope. Requirements defined by external documents not created by Khronos may contain | ||
| contributions from non-members of Khronos not covered by the Khronos Intellectual | ||
| Property Rights Policy. | ||
|
|
||
| Khronos makes no, and expressly disclaims any, representations or warranties, | ||
| express or implied, regarding this specification, including, without limitation: | ||
| merchantability, fitness for a particular purpose, non-infringement of any | ||
| intellectual property, correctness, accuracy, completeness, timeliness, and | ||
| reliability. Under no circumstances will Khronos, or any of its Promoters, | ||
| Contributors or Members, or their respective partners, officers, directors, | ||
| employees, agents or representatives be liable for any damages, whether direct, | ||
| indirect, special or consequential damages for lost revenues, lost profits, or | ||
| otherwise, arising from or in connection with these materials. | ||
|
|
||
| Khronos® and Vulkan® are registered trademarks, and ANARI™, WebGL™, glTF™, NNEF™, OpenVX™, | ||
| SPIR™, SPIR‑V™, SYCL™, OpenVG™ and 3D Commerce™ are trademarks of The Khronos Group Inc. | ||
| OpenXR™ is a trademark owned by The Khronos Group Inc. and is registered as a trademark in | ||
| China, the European Union, Japan and the United Kingdom. OpenCL™ is a trademark of Apple Inc. | ||
| and OpenGL® is a registered trademark and the OpenGL ES™ and OpenGL SC™ logos are trademarks | ||
| of Hewlett Packard Enterprise used under license by Khronos. ASTC is a trademark of | ||
| ARM Holdings PLC. All other product names, trademarks, and/or company names are used solely | ||
| for identification and belong to their respective owners. | ||
23 changes: 23 additions & 0 deletions
23
extensions/2.0/Khronos/KHR_collision_shapes/schema/glTF.KHR_collision_shapes.schema.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "KHR_collision_shapes glTF Document Extension", | ||
| "type": "object", | ||
| "description": "Top level collision primitives.", | ||
| "allOf": [ { "$ref" : "glTFProperty.schema.json" } ], | ||
| "properties": { | ||
| "shapes": { | ||
| "type": "array", | ||
| "description": "An array of shape descriptions.", | ||
| "items": { | ||
| "type": "object", | ||
| "$ref": "glTF.KHR_collision_shapes.shape.schema.json" | ||
| }, | ||
| "minItems": 1 | ||
| }, | ||
| "extensions": { }, | ||
| "extras": { } | ||
| }, | ||
| "required": [ | ||
| "shapes" | ||
| ] | ||
| } |
22 changes: 22 additions & 0 deletions
22
...s/2.0/Khronos/KHR_collision_shapes/schema/glTF.KHR_collision_shapes.shape.box.schema.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "KHR_collision_shapes Box Shape", | ||
| "type": "object", | ||
| "description": "Parameters describing a box shape.", | ||
| "allOf": [ { "$ref": "glTFProperty.schema.json" } ], | ||
| "properties": { | ||
| "size": { | ||
| "type": "array", | ||
| "description": "The extents of the box in each axis in local space.", | ||
| "items": { | ||
| "type": "number", | ||
| "exclusiveMinimum": 0.0 | ||
| }, | ||
| "minItems": 3, | ||
| "maxItems": 3, | ||
| "default": [ 1, 1, 1 ] | ||
| }, | ||
| "extensions": { }, | ||
| "extras": { } | ||
| } | ||
| } |
29 changes: 29 additions & 0 deletions
29
...0/Khronos/KHR_collision_shapes/schema/glTF.KHR_collision_shapes.shape.capsule.schema.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "KHR_collision_shapes Capsule Shape", | ||
| "type": "object", | ||
| "description": "Parameters describing a capsule shape.", | ||
| "allOf": [ { "$ref": "glTFProperty.schema.json" } ], | ||
| "properties": { | ||
| "height": { | ||
| "type": "number", | ||
| "description": "The distance between the centers of the two capping spheres of capsule.", | ||
| "exclusiveMinimum": 0.0, | ||
| "default": 0.5 | ||
| }, | ||
| "radiusBottom": { | ||
| "type": "number", | ||
| "description": "The radius of the sphere located at the bottom of the capsule (i.e. the sphere at the half-height along -Y)", | ||
| "minimum": 0.0, | ||
| "default": 0.25 | ||
| }, | ||
| "radiusTop": { | ||
| "type": "number", | ||
| "description": "The radius of the sphere located at the top of the capsule (i.e. the sphere at the half-height along +Y)", | ||
| "minimum": 0.0, | ||
| "default": 0.25 | ||
| }, | ||
| "extensions": { }, | ||
| "extras": { } | ||
| } | ||
| } |
29 changes: 29 additions & 0 deletions
29
.../Khronos/KHR_collision_shapes/schema/glTF.KHR_collision_shapes.shape.cylinder.schema.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "KHR_collision_shapes Cylinder Shape", | ||
| "type": "object", | ||
| "description": "Parameters describing a cylinder shape.", | ||
| "allOf": [ { "$ref": "glTFProperty.schema.json" } ], | ||
| "properties": { | ||
| "height": { | ||
| "type": "number", | ||
| "description": "The height of the cylinder, centered along the Y axis.", | ||
| "exclusiveMinimum": 0.0, | ||
| "default": 0.5 | ||
| }, | ||
| "radiusBottom": { | ||
| "type": "number", | ||
| "description": "The radius of the bottom of the cylinder (the disk located along -Y.)", | ||
| "minimum": 0.0, | ||
| "default": 0.25 | ||
| }, | ||
| "radiusTop": { | ||
| "type": "number", | ||
| "description": "The radius of the top of the cylinder (the disk located along +Y.)", | ||
| "minimum": 0.0, | ||
| "default": 0.25 | ||
| }, | ||
| "extensions": { }, | ||
| "extras": { } | ||
| } | ||
| } |
36 changes: 36 additions & 0 deletions
36
.../2.0/Khronos/KHR_collision_shapes/schema/glTF.KHR_collision_shapes.shape.mesh.schema.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "KHR_collision_shapes Mesh Shape", | ||
| "type": "object", | ||
| "description": "Parameters describing a mesh shape.", | ||
| "allOf": [ { "$ref": "glTFProperty.schema.json" } ], | ||
| "properties": { | ||
| "convex": { | ||
| "type": "boolean", | ||
| "description": "Specifies whether the collision geometry is a surface or a convex hull.", | ||
| "gltf_detailedDescription": "Specifies how the collision shape is generated from the referenced mesh. When this value is false, the collision volume of this shape is the surface of the referenced mesh. The primitives in the mesh **MUST** contain triangles. When this value is true, this shape represents the convex hull of the set of primitives in the referenced mesh." | ||
| }, | ||
| "mesh": { | ||
| "description": "The index of the mesh from which to build the collision representation.", | ||
| "allOf": [{"$ref": "glTFid.schema.json"}] | ||
| }, | ||
| "skin": { | ||
| "description": "The index of a skin with which to deform the mesh.", | ||
| "allOf": [ { "$ref": "glTFid.schema.json" } ] | ||
| }, | ||
| "weights": { | ||
| "type": "array", | ||
| "description": "The weights of the instantiated morph target.", | ||
| "minItems": 1, | ||
| "items": { | ||
| "type": "number" | ||
| } | ||
| }, | ||
lexaknyazev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "extensions": { }, | ||
| "extras": { } | ||
| }, | ||
| "required": [ | ||
| "convex", | ||
lexaknyazev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "mesh" | ||
| ] | ||
| } | ||
75 changes: 75 additions & 0 deletions
75
...sions/2.0/Khronos/KHR_collision_shapes/schema/glTF.KHR_collision_shapes.shape.schema.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "KHR_collision_shapes Shape Resource", | ||
| "type": "object", | ||
| "description": "Parameters describing a node's physics collision geometry.", | ||
| "allOf": [ { "$ref": "glTFChildOfRootProperty.schema.json" } ], | ||
| "properties": { | ||
| "type": { | ||
| "description": "Specifies the shape type.", | ||
| "anyOf": [ | ||
| { | ||
| "enum": [ "sphere" ], | ||
| "description": "A sphere with a specified radius, centered at the origin in local space." | ||
| }, | ||
| { | ||
| "enum": [ "box" ], | ||
| "description": "An axis-aligned box with a size per-axis, centered at the origin in local space" | ||
| }, | ||
| { | ||
| "enum": [ "capsule" ], | ||
| "description": "A capsule shape, centered at the origin in local space, equivalent to the convex hull of two spheres located along the Y axis (in local space) at a specified distance." | ||
| }, | ||
| { | ||
| "enum": [ "cylinder" ], | ||
| "description": "A cylinder shape, centered at the origin in local space, equivalent to the convex hull of two circles in the X/Z plane positioned along the Y axis at a specified distance." | ||
| }, | ||
| { | ||
| "enum": [ "mesh" ], | ||
| "description": "A shape generated from a referenced `mesh` object" | ||
| }, | ||
| { | ||
| "type": "string" | ||
| } | ||
| ] | ||
| }, | ||
lexaknyazev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "sphere": { | ||
| "type": "object", | ||
| "description": "A set of parameter values that are used to define a sphere shape.", | ||
| "$ref": "glTF.KHR_collision_shapes.shape.sphere.schema.json" | ||
| }, | ||
| "box": { | ||
| "type": "object", | ||
| "description": "A set of parameter values that are used to define a box shape.", | ||
| "$ref": "glTF.KHR_collision_shapes.shape.box.schema.json" | ||
| }, | ||
| "capsule": { | ||
| "type": "object", | ||
| "description": "A set of parameter values that are used to define a capsule shape.", | ||
| "$ref": "glTF.KHR_collision_shapes.shape.capsule.schema.json" | ||
| }, | ||
| "cylinder": { | ||
| "type": "object", | ||
| "description": "A set of parameter values that are used to define a cylinder shape.", | ||
| "$ref": "glTF.KHR_collision_shapes.shape.cylinder.schema.json" | ||
| }, | ||
| "mesh": { | ||
| "type": "object", | ||
| "description": "A set of parameter values that are used to define a shape derived from a mesh.", | ||
| "$ref": "glTF.KHR_collision_shapes.shape.mesh.schema.json" | ||
| }, | ||
| "name": { }, | ||
| "extensions": { }, | ||
| "extras": { } | ||
| }, | ||
| "oneOf": [ | ||
| { "required": ["sphere"] }, | ||
| { "required": ["box"] }, | ||
| { "required": ["capsule"] }, | ||
| { "required": ["cylinder"] }, | ||
| { "required": ["mesh"] } | ||
| ], | ||
| "required": [ | ||
| "type" | ||
| ] | ||
| } | ||
17 changes: 17 additions & 0 deletions
17
....0/Khronos/KHR_collision_shapes/schema/glTF.KHR_collision_shapes.shape.sphere.schema.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "KHR_collision_shapes Sphere Shape", | ||
| "type": "object", | ||
| "description": "Parameters describing a sphere shape.", | ||
| "allOf": [ { "$ref": "glTFProperty.schema.json" } ], | ||
| "properties": { | ||
| "radius": { | ||
| "type": "number", | ||
| "description": "The radius of the sphere.", | ||
| "exclusiveMinimum": 0.0, | ||
| "default": 0.5 | ||
| }, | ||
| "extensions": { }, | ||
| "extras": { } | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.