GeezerVison is showing github.com — Define the coordinate system · Issue #460 · pygfx/pygfx
Define the coordinate system #460DescriptionTodo
- Decide on a coordinate system
- Document it
- Document that our coordinates are unit-free, see this comment in this issue for some example explanations for threejs.
- Modify the
look_at functions (cameras and lights should work as they do now, but all other objects should consider +Z the default forward).
What do others use?
pygfx currently we seem to have a right-handed system with right +X, up +Y and forward -Z.
glTF uses a right-handed coordinate system. glTF defines +Y as up, +Z as forward, and -X as right; the front of a glTF asset faces +Z. The units for all linear distances are meters. All angles are in radians. Positive rotation is counterclockwise.
OpenGL by convention uses a right-handed system. What this basically says is that the positive x-axis is to your right, the positive y-axis is up and the positive z-axis is backwards.
Blender uses the right hand coordinate system with the Z axis pointing upwards. This is common with the coordinate systems used by most common 3D CAD packages.
From my understanding, CAD software like 3D studio max and blender historically have Z point upwards, whereas game and gfx related software has Y point upwards. Not sure why glTF swaps directions for X and Z ...
Decision
We've decided to adopt glTF (threeJS did too):
- glTF uses a right-handed coordinate system. glTF defines +Y as up, +Z as forward, and -X as right; the front of a glTF asset faces +Z.
- The camera is defined such that the local +X axis is to the right, the “lens” looks towards the local -Z axis, and the top of the camera is aligned with the local +Y axis.
The view matrix is derived from the global transform of the node containing the camera with the scaling ignored. If the node’s global transform is identity, the location of the camera is at the origin.
- For light types that have a direction (directional and spot lights), the light's direction is defined as the 3-vector (0.0, 0.0, -1.0) and the rotation of the node orients the light accordingly. That is, an untransformed light points down the -Z axis. The light's transform is affected by the node's world scale, but all properties of the light (such as range and intensity) are unaffected.
ActivityKorijn commented on Feb 17, 2023 Can you clarify in each instance whether or not the coordinate system you describe applies to model/view/clip space? Because the conventions are different between those spaces usually as well. almarklein commented on Feb 17, 2023 Isn't it only the model space that matters? The clip space is an implementation detail (if an application has backends for both OpenGL and wgpu, the conventions for their clip planes would be different in these cases). FirefoxMetzger commented on Feb 17, 2023
pygfx currently we seem to have a right-handed system with right +X, up +Y and forward -Z.
😆 sigh, so which is it now, does forward mean positive Z or negative Z? 😅 Korijn commented on Feb 17, 2023 I found this nice picture: :)

I would like to add that Three.js is Y-up, and right-handed.
Also, you can actually change the coordinate system in three like this:
THREE.Object3D.DefaultUp = new THREE.Vector3(0,0,1);
But that's all besides the point. We just need to pick something and document it. What do we want?
Personally, I would prefer to stay right-handed, and Y-up. The Z negative/positive thing seems to be our only real uncertainty here. Let's pick positive Z? almarklein commented on Feb 17, 2023
😆 sigh, so which is it now, does forward mean positive Z or negative Z? 😅
Sorry, I know! I was confused between NDC and model coords! 😆
To quote Linkin Park: In the end, it doesn't even matter (what we choose). Users can always set the camera's up value, and/or flip the camera's scale to move from a left-handed to right-handed system. The main issue (as I understand it) is that when a user loads left-handed models into a right-handed system (or vise versa) the models will be inside-out. And that only is really a problem when people set the material.side = 'front'.
The Z negative/positive thing seems to be our only real uncertainty here. Let's pick positive Z?
From what I understand, given +X is right, and +Y is up, if we'd pick forward to mean +Z it'd be left-handed, and -Z would be right-handed. changed the title [-]Define on the coordinate system[/-] [+]Define the coordinate system[/+] on Feb 17, 2023almarklein commented on Feb 17, 2023 Addition: we're not actually deciding on the coordinate system, but only on the default. Korijn commented on Feb 20, 2023
From what I understand, given +X is right, and +Y is up, if we'd pick forward to mean +Z it'd be left-handed, and -Z would be right-handed.
You are right. And since we want to be right-handed, let's pick -Z as forward.

That aligns with three.js defaults.
- +X is right
- +Y is up
- -Z is forward
Can we close this? almarklein commented on Feb 20, 2023 Korijn commented on Feb 22, 2023 I'm reopening this because I discovered some new information. Three.js seems to follow the glTF conventions.
I summarized them here: pygfx/pylinalg#43 (comment)
I propose that we align with those conventions as well, both in pygfx and pylinalg. Korijn commented on Feb 25, 2023 So to summarize from the glTF spec:
- glTF uses a right-handed coordinate system. glTF defines +Y as up, +Z as forward, and -X as right; the front of a glTF asset faces +Z.
- The camera is defined such that the local +X axis is to the right, the “lens” looks towards the local -Z axis, and the top of the camera is aligned with the local +Y axis.
The view matrix is derived from the global transform of the node containing the camera with the scaling ignored. If the node’s global transform is identity, the location of the camera is at the origin.
- For light types that have a direction (directional and spot lights), the light's direction is defined as the 3-vector (0.0, 0.0, -1.0) and the rotation of the node orients the light accordingly. That is, an untransformed light points down the -Z axis. The light's transform is affected by the node's world scale, but all properties of the light (such as range and intensity) are unaffected.
almarklein commented on Feb 27, 2023 I've copied this to the top post and also started a todo list. Feel free to add items. Korijn commented on Jul 12, 2023 @almarklein can you comment on the status of this item? I think we've completed all the implementation changes, and just need to document the conventions now, right? almarklein commented on Jul 12, 2023 I thought we already documented it too, but searching for "handed" in the docs shows nothing, so I guess we should add it. My suggestion would be to explain in in the guide, as well as a brief technical summary on the base camera. DennyLindberg commented on Jul 18, 2023 Could I request documentation on linear distance units as well? I tried figuring out the default units and could not find any information on it. glTF is defined in meters (see 3.4). The helmet in the PBR example is 1.5-2.0 meter sized when opened in Blender and that size is reflected in pygfx when using the trimesh python package. If I create a reference mesh in Blender and export to various formats using the default scene settings the size is consistent in pygfx to the meter standard. I get the impression that many of the pygfx examples are either using cm or unitless which can sometimes make it confusing when the camera setup is automatic for framing the object. I see values in the 20, 50, 100, 600 ranges just skimming through the examples now. Seeing that you are already documenting the coordinate system standards it would be nice if all units were clearly defined. I like pygfx so far so I would love for it to become a "standard" python package. almarklein commented on Jul 19, 2023 @DennyLindberg I agree that documenting this in the docs is a good idea.
The short answer is that pygfx does not impose any units. It's the responsibility of the user to ensure that all objects are represented in the same units. In Blender-like use-case, and loading stuff using trimesh this would indeed often be meters. But for scientific applications it might be different. E.g. microscopy vs GIS vs astronomy. Reacted by Denny Lindberg DennyLindberg commented on Jul 19, 2023 Korijn commented on Jul 19, 2023 It's a matter of application vs library, I think. For applications it's expected that everything is defined, but for a library, well, it's used to build applications. So to give application developers the freedom to pick their own unit convention, we should be agnostic here. DennyLindberg commented on Jul 19, 2023 Reacted by Korijn van Golen and Almar Klein almarklein commented on Jul 19, 2023 I added a checkbox in the top post. Reacted by Denny Lindberg
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|