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

KITTYCAD_boundary_representation #2343

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft

Conversation

alteous
Copy link

@alteous alteous commented Nov 1, 2023

The glTF core specification provides geometric data in the form of polygonal meshes. Meshes are ubiquitous in hardware accelerated rendering of 3D models; however, since they are composed of flat faces, they cannot represent smooth surfaces accurately. Boundary representation ('B-rep') is an alternative approach, providing an accurate and flexible method of describing 3D shapes. In B-rep models, solid volumes are expressed as the composition of connected faces, bound by curves on surfaces, of which both are described by smooth parametric functions.

The aims of this extension are: (1) to reduce the processing required to import B-rep information when compared to existing formats such as STEP (ISO 10303); (2) to provide an open, royalty-free specification for B-rep data; and (3) to provide additional metadata alongside existing glTF-based manufacturing workflows.

The extension is intended to provide supplementary information to standard mesh-based glTF models. By providing the B-rep data alongside meshes, applications can render approximated previews of solid models without implementing a computationally expensive and complicated tessellation operation. Alternatively, to reduce the size of the asset, the extension could feasibly be used in place of mesh-based models.

Please refer to the README for a technical introduction.

Copy link
Author

Choose a reason for hiding this comment

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

This schema was automatically generated. It will likely require some tweaks to match the rest of the spec.

@javagl
Copy link
Contributor

javagl commented Nov 1, 2023

The KITTYCAD prefix does not (yet) appear in https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md . You might consider an issue/PR to add it.

Beyond that, there are some issues/discussions that may be related, e.g. #2306 or #1783 . It might be worthwhile checking these, to see whether there has been any previous discussion or similar proposals.

@alteous
Copy link
Author

alteous commented Nov 2, 2023

@javagl, noted, the vendor prefix is tentative.

Bézier surfaces are a subset of the more general NURBS curves and surfaces proposed in this extension.

#1783 mentions NURBS and B-reps but makes no proposal. This extension is intended for engineering use cases such as manufacturing and simulation.

@alteous
Copy link
Author

alteous commented Nov 2, 2023

The ideas in this extension have been developed on a branch of the Rust glTF library: https://github.com/alteous/gltf/blob/brep/gltf-json/src/extensions/kittycad_boundary_representation.rs

This branch also contains the JSON schema generator: https://github.com/alteous/gltf/blob/brep/gltf-json/src/bin/generate_schema.rs

@javagl
Copy link
Contributor

javagl commented Nov 2, 2023

the vendor prefix is tentative.

I created a PR for adding it via #2345 , but should emphasize that there currently is no established policy for handling renamings of prefixes. Iff there is no existing extension with such a prefix, then we could probably handle that, but the goal of the prefixes is to provide some sort of stability/reliability for the actual, final extension name.

Also have a look at the "Naming" section:

Names SHOULD be structured as <PREFIX>_<scope>_<feature>, where scope is an existing glTF concept (e.g. mesh, texture, image) and feature describes the functionality being added within that scope. This structure is recommended, but not required.

In this case, it might be something like KITTYCAD_mesh_brep or so (I haven't looked at the details of this PR yet).

The main reason for pointing to these issues is the encourage a form of "collaboration", with the goal of finding something that multiple people can agree on. For example (depending on the maturity of this PR and your own judgement!) you could consider to @mention someone from these issues and point them to this PR - maybe they want to review it and chime in with their thoughts and ideas...

@alteous
Copy link
Author

alteous commented Nov 6, 2023

Iff there is no existing extension with such a prefix, then we could probably handle that, but the goal of the prefixes is to provide some sort of stability/reliability for the actual, final extension name.

That's fine. By 'tentative' I was thinking along the lines of "another party might be interested in this" and therefore it could be made EXT.

In this case, it might be something like KITTYCAD_mesh_brep or so (I haven't looked at the details of this PR yet).

That's a good point. The mesh scope is related but feels inappropriate. What we're proposing essentially is a new 'solid' scope. So perhaps X_solid_brep would be a better name. This extension is related more closely to the proposed EXT_manifold extension. Is manifold is the name of the scope in that case?

@javagl
Copy link
Contributor

javagl commented Nov 6, 2023

Leaving the option to change the prefix to EXT makes sense. After all, I assume that this PR is still work in progress, and possibly taking into account further feedback. (I cannot give that - I'm not technically involved in the topic - but I'm sure others might be interested in that)

(BTW: One could consider changing this to a 'Draft' unless it is supposed to be 'Ready For Review'. But admittedly, the processes here are not perfectly sorted out. I once started trying to clarify some aspects of the extension development process, but ... the PR at #2225 is itself still a 'Draft' 😬 )

@alteous alteous marked this pull request as draft November 6, 2023 20:53
@prideout
Copy link

prideout commented Nov 10, 2023

This spec has several example JSON snippets for individual features, but I think it would be helpful to add a higher level JSON example near the top. This would help readers who wish to quickly review the overall structure of a Brep.

{
"type": "curveType",
"curveType": {
"curveSpecificValue": 1

Choose a reason for hiding this comment

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

What is curveSpecificValue?

Copy link
Author

Choose a reason for hiding this comment

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

This snippet is meant to convey the general structure of a curve entry. The structure is similar to that of cameras where there is a type field set to either "perspective" or "orthographic" and the camera-specific fields are put under camera.perspective/camera.orthographic respectively.

https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera

On a slightly unrelated note, this kind of sum type has been discussed in #2311.

}
```

#### Circle

Choose a reason for hiding this comment

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

Would it be more flexible for a curve to be "circular arc" rather than a "circle"?

Copy link
Author

Choose a reason for hiding this comment

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

It's a good point. I think this is a matter of naming. All curves have an optional subdomain so they are meant to be flexible in this way already, i.e., a circle curve with a subdomain would be an arc of the circle.

}
```

#### Torus (revolved circle)
Copy link

@prideout prideout Nov 10, 2023

Choose a reason for hiding this comment

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

NURBS, RevolutionSurface, and Plane all seem like fundamental surface types to me, but torus does not. Although, it's unclear if the object key here is just a human-readable name or if it defines the actual type of parametric surface.

Copy link
Author

Choose a reason for hiding this comment

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

Surfaces of revolution and extrusion are planned and in progress at the moment but haven't been added to this PR yet.

This particular section was intended to introduce a type of a parametric surface. Perhaps that could be made clearer.

Now that you mention it, I do wonder if it's worth having distinct types for cylinders and tori at all since they could be simply inferred as circle + extrusion/revolution.

@prideout
Copy link

prideout commented Nov 10, 2023

I love that someone is interested in opening up a modern standard for Brep representation!

As it now stands, this spec does not clearly leverage glTF's core strengths like its well-defined materials model, its animation model (skinning/morphing), and the node model (allows efficient mesh instancing and hierarchical transformations).

I think "playing nicely" with all those features is possible, but the spec should probably make it clear how it all works.

@alteous
Copy link
Author

alteous commented Nov 13, 2023

I love that someone is interested in opening up a modern standard for Brep representation!

It's great to have another party interested too, thanks for your review! Modernising B-reps is exactly what we're trying to achieve here. Our main use case is for manufacturing. We want to be able to ship an exact boundary representation, an optional tessellation, and manufacturing data in one bundle. The JSON + binary design of glTF allows this data to be stored more densely than existing formats and with potentially more diverse metadata.

As it now stands, this spec does not clearly leverage glTF's core strengths like its well-defined materials model, its animation model (skinning/morphing), and the node model (allows efficient mesh instancing and hierarchical transformations).

That's a great point. The intention is to integrate well the materials and node model. I'll expand on the documentation there. Solids are meant be to instantiated in the same way as meshes, i.e., they don't appear in the scene unless they are referenced in the node hierarchy. It's up to the application whether they render the pre-tessellated mesh approximation or do something with the B-rep directly.

Animations targeting B-reps are expected to be limited to simple node transformations, i.e., translation, rotation, and scaling.

@prideout
Copy link

@alteous all your responses make sense to me. My own interest is the architecture sector as opposed to manufacturing, but I think we'd both want the same things from a Brep format.

I look forward to seeing how this develops, let me know if there's anything I can do to help.

Comment on lines 156 to 157
"start": [-1, -1, 0],
"end": [1, -1, 0]

Choose a reason for hiding this comment

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

Should the start and end fields of a line be integers that refer to vertices, rather than coordinates?

Copy link
Author

Choose a reason for hiding this comment

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

Possibly. The idea here is that straight edges are a portion of a line curve. For example, an edge may span from vertex [-1, -1, -1] to vertex [1, 1, 1], but be associated with a line curve starting from [-2, -2, -2] in the direction [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.

Perhaps it could be either an indexed vertex or an explicit co-ordinate.

{
    "curves": [
        {
            "type": "line",
            "line": {
                "start": 0,
                "end": 1
            }
        },
        {
            "type": "line",
            "line": {
                "start": [0.0, 0.0, 0.0],
                "end": [1.0, 1.0, 1.0]
            }
        }
    ]
}

Copy link
Author

Choose a reason for hiding this comment

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

Line curves have been simplified to a single form: an origin plus a normalised direction.

Comment on lines +138 to +139
"start": 0,
"end": 1,

Choose a reason for hiding this comment

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

I'm fuzzy on the purpose of start and end, since the curve already has start and end points; also fuzzy as to why edges exists at all -- couldn't a loop refer to curves directly?

Copy link
Author

@alteous alteous Nov 24, 2023

Choose a reason for hiding this comment

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

Edges typically join surfaces with differing surface equations. For example, consider the edge at the top of a closed cylinder which can be represented equivalently in three ways: a circle in 3D space, a circle on a planar surface in 2D space, or a line on a cylindrical surface in 2D space. The point of having edges is to allow all these curves to be provided: a 3D space curve is associated with the edge, and a 2D surface curves may be associated a with loop.

{
    "loops": [
        {
            "edges": [0, 1, 2, 3],
            "uvCurves": [0, 1, 2, 3]
        },
        {
            // edges 0 & 1 are shared but have differing UV curves
            "edges": [0, 1, 3 ,4],
            "uvCurves": [4, 5, 6, 7]
        }
    ],
    "faces": [
        {
            "outerLoop": 0,
            "surface": 0
        },
        {
            "outerLoop": 1,
            "surface": 1
        }
    ],
    "edges": [
        {
            "curve": [4],
            ...
        }
    ]
}


### Shell

A _shell_ is a collection of _faces_ in 3D space which form a 'watertight' volume. A shell is represented by the `Shell` data structure.

Choose a reason for hiding this comment

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

Some applications might permit non-manifold modeling, in which case it would be desirable to allow shells to be non-closed.

Copy link
Author

Choose a reason for hiding this comment

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

That's a good point. I'm thinking of reworking many of the data structures in this direction. In particular, I'm considering removing the outer/inner loop distinction in faces, perhaps replacing it with extra metadata in the loop data structure instead. The same idea can be applied to shells.

Comment on lines 387 to 388
Many objects in boundary representation have two _orientation states_. Such objects are called _orientable_ objects. These are often given colloquial terms such as 'right/wrong side', 'up/down', 'in/out', 'forward/backward', et cetera. When referencing orientable objects, it is important to state which orientation of the object is desired. The orientation of a referenced object is described as being 'same-sense' or 'opposite-sense'. This extension utilizes the sign bit of floating point numbers to select the desired orientation. A positive sign selects the 'same-sense' and a negative sign selects the 'opposite-sense'.

Copy link
Author

Choose a reason for hiding this comment

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

In the working group meeting, it was raised that -0 would likely be parsed as 0 by some JSON parsers. Also, it was raised that the IEEE754 specification says -0 == 0 should evaluate as true. These are both good points, so I will come up with an alternative.

I have a few ideas:

  1. Encode using an object { "index": 0, "reverse": true }. This was actually the original proposal but since this use case is so common, this approach was deemed too verbose/bloated.
  2. Use indices starting at 1. This avoids the tricky zeroth index but would be inconsistent with the core specification. I would like to avoid this one if possible.
  3. Encode as a string instead, i.e,. "-0" instead of -0. This could work; however, it was raised this has its own set of problems. For example, glTF JSON permits additional integer representations such as exponential notation.
  4. Move this data into the binary blob and encode as a one's complement integer.

Copy link
Author

Choose a reason for hiding this comment

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

A couple more ideas:

  1. Encode using two integers per array, an index followed by a sign.
{
    "edges": [
        0, 1,
        1, -1,
        2, 1,
        3, -1
    ]
}
  1. Encode the sign using a separate paired array, which could be omitted if all items are same-sense:
{
    "edges": [0, 1, 2, 3],
    "edgeOrientations": [1, -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've reworked this as an integer/orientation pair. It looks like this:

Before

{
    "loops": [0, -1],
    "surface": 0
}

After

{
    "loops": [
        [0, 1],
        [1, -1]
    ],
    "surface": [0, 1]
}

[index, 1] could be implied by [index] but I'll keep it explicit for now.

* Curves have been split into 2D and 3D variants.
* Curve and surface domains have been removed.
* Edges now have an interval for its curve parameter.
* The 'xbasis' and 'normal' parameters have been replaced
  with 'xAxis' and 'yAxis' parameters.
* Line curves have been reduced to a single form, that is,
  an origin and a normalised direction.
* Weights in NURBS curves and surfaces are now independent
  from the control points. This allows B-splines curves and
  surfaces to be defined as a NURBS curve or surface without
  the weights array.
* The definition of cylindrical, spherical, and toroidal
  surfaces have been simplified. All of the parameters are
  now contained within a single JSON object.
@elalish
Copy link
Contributor

elalish commented Feb 28, 2024

This is interesting, thanks! I agree with @prideout that it would be nice if we could find a way to represent smoothly curved geometry within the existing data structures of glTF, since it would avoid duplication, which can inevitably result in conflicting data. I have some thoughts and progress on this, though it's a pretty different concept from NURBS, or even from subdivision surfaces - it's more a form of curved triangles. I have a short write-up here: https://elalish.blogspot.com/2022/03/smoothing-triangle-meshes.html - I have some improvements I want to make first, but I'm interested in attempting a glTF extension on top of my EXT_mesh_manifold to enable this. Of course, I'm not sure how helpful it'll be for engines that are already NURBS-based, but I'm curious if anyone else is interested in trying a new approach to B-reps?

One semantic nit: glTF meshes are boundary-representations. The difference between triangles and NURBS is first-order vs higher-order, and explicit edges vs implicit edges. I wrote up a taxonomy: https://elalish.blogspot.com/2022/03/solid-geometry-representations.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants