This document describes the coordinate system used by Cogs.
It also contains some advice how to endure good numeric precision when rendering.
Contents:
This coordinate system represents the mesh points prior to any transformation. Expected to be single precision. It is recommended to use coordinates close to the origin or pivot point of the mesh. Any precision lost using large offsets in a local coordinate can never be regained.
When an entity has a parent on its hierarchy the local coordinates of the mesh are always relative to those of the parent and transformations will be concatenated from root to leaf in order to take the mesh points to engine space or world space.
Also know as absolute World Coordinates.
This coordinate system is introduced to handle large offsets from origin where normal 32-bit floating point numbers give low precision and a lot of numerical problems. This coordinate system is exposed to client applications and is what developers should use in most cases.
The World Coordinates is a right-handed coordinate system defined using dvec3, e.g. double precision.
For root entities the transform between Local and World Coordinate is given by:
glm::dvec3 WorldCoordinate = glm::dvec3(LocalCoordinate + transformComponent.position)
+ transformComponent.coordinates;
glm::vec3 LocalCoordinate = glm::vec3(WorldCoordinate - transformComponent.coordinates)
- transformComponent.position;
When using large coordinate offsets in entities, an offset close to the centre of the
bounding box of the entity should be set in non-child Entity's transformComponent.coordinates
.
One example is rendering using UTM Projections. Use small offsets of a marker in Local Coordinates. Put the UTM offset in transformComponent.coordinates
.
Avoid specifying a large offset in transformComponent.position
as this has lower floating point precision.
Also known as relative world coordinates. This coordinate system is used by the render engine with shaders etc. Also used for getting pick results, bounding boxes etc. This coordinate system is used internally by cogs to set up numerically stable transformation matrices.
Engine coordinates uses vec3, e.g. single precision and it is very rare that an application needs to know about it, except setting the Scene Origin.
For example in UTM coords (offset about one million) precision of 6-7 digits implies that a triangle a few centimeters in size may be degenerate. Therefore we use a reference point (glm::dvec3 Scene.origin) to transform World Coordinates to single precision Engine Coordinates.
The transformation between World and Engine Coordinates is defined as:
glm::vec3 engineCoords = glm::vec3(worldCoordinate - scene.origin);
glm::dvec3 worldCoords = glm::dvec3(engineCoordinate) + scene.origin;
Avoid using Engine Coordinates in an application. The application shall only det up a suitable Engine Coordinate system by setting the Scene Origin.
Avoid reading the Scene Origin. The Scene origin read back is the origin used in last rendering, not always same as origin just set.
Scene Origin is set using:
C++ Bridge: ::setOrigin(context, glm::value_ptr(originDvec3));
Cogs.js: runtime.scene.origin = dvec3.fromValues(x, y, z);
Cogs.Net: runtime.Scene.Origin = new Vector3(x, y, z);
A suitable origin is the Camera Focal position. Avoid doing continuos updates of origin this stresses rendering shadows etc. Find a suitable step size. Some code have only set origin in Z=0 plane. Note that terrain applications subsea or high altitudes will use large Z offsets.
Be aware that having a very large far distance can also reduce numeric precision.
Use Picking, BoundaryBox, and Depth queries that return World Coordinates, avoid the older queries that use Engine Coordinates.
Note that GPU Shaders uses Engine Coordinates. An application that uses rare custom shaders handling World Coordinates have to control Origin setting and update shader parameters when changed.
The translation sent to the graphics engine is:
const glm::dvec3 origin = ctx->transformSystem->getOrigin();
const glm::vec3 delta = glm::vec3(myEntity.transformComponent->coordinates - origin) + myEntity.transformComponent->position;
Keeping these deltas small gives best rendering results.
Coordinates in 2D window. Note that these coordinates may have to be scaled by DPI scaling.
Cogs uses origin in lower left corner of the Window.
Coordinates in 2D that allow addressing individual pixels in a texture buffer. Usually aliased as (u,v) coordinates, they map to the horizontal and vertical values of the 2 dimensional array representing the texture surface.
Along the pipeline all geometry goes through a series of transformations, converting the 3D mesh points to 2D screen coordinates. The usual process includes but it consists not exclusively of transformations in the following spaces:
[TO-BE-EXPANDED]
For each space, a transformation matrix is created that mutate the coordinates from one space to the next.