Callisto Game Engine
A desktop game engine written in Odin.
Callisto is a desktop game engine written in Odin with a Vulkan renderer. It is still early in development and the API is subject to change.
When there is a stable release I will add documentation here.
At the moment, the notes here are to record decisions that I have made.
In the meantime, see the Callisto GitHub repository for a brief overview of how the engine is used, or
Callisto-Sandbox for a sample implementation.
1 - Introduction
Document Conventions
Normative Terminology
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY and OPTIONAL,
when uppercase and emboldened, are normative and have special meaning.
These words are to be interpreted as described in BCP 14.
Technical Terminology
slice - A bounded view into an array represented by a pointer or index of the first element of the slice, and a count of elements.
External specifications
Bradner, S., Key words for use in RFCs to Indicate Requirement Levels, BCP 14, RFC 2119, March 1997.
Leiba, B., Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words, BCP 14, RFC 8174, May 2017.
https://www.rfc-editor.org/info/bcp14
What’s next?
2 - Assets
Game data stored on the disk.
Assets are classified as either Primitive or Aggregate.
Primitive assets MUST contain only information about themselves, while aggregate assets MAY contain references to other assets.
Planned primitives:
Image
Mesh
Audio
Input action map
Shader
Planned aggregates:
Material
(shader + images)Model
(mesh + materials)Construct
(fixed-length transform hierarchy)Level
This section is a work in progress. I plan to use Concise Binary Object Representation (CBOR) for serialization,
as it provides some desired attributes to the asset files:
- Named fields to avoid data loss when struct definitions change
- Small file size
- Existing Odin core library
File loading
Inspiration from Timothy Cain’s video, Arcanum “dat” files, development builds use a raw OS file system, release builds use packed bundles.
- data locality on the disk
- “seek” is much faster than “open”
- avoid file descriptor limits
- less overhead for many tiny assets (block alignment, OS file nodes)
- easy patches/modding with sequential overriding of the asset database
- more efficient compression
2.1 - Mesh
A primitive asset for storing vertex data.
Mesh overview
Mesh assets store an object’s vertex data, and how the vertices are connected to form triangles.
This page is currently being rewritten to support the new asset format.
2.2 - Material
An aggregate asset to describe how meshes are rendered.
WIP
Materials are an asset that associate a Shader with properties
such as Textures and number values.
3 - Graphics
Details about the renderer, shaders, render passes etc.
3.1 - Coordinates for rendering
Transforming coordinates from world space to other spaces.
World coordinates
World coordinates are right-handed, +Z forward, +Y up, -X right.
Why?
- Want right handed coords
- glTF default, easy import
- Y-up intuitive for gravity
Projection and Clip space
Vulkan’s NDC space is defined as [x: -1, y: -1] top-left, [x: 1, y: 1] bottom-right, and z depth in the range [0, 1].
Callisto uses a reversed-depth, infinite far plane perspective projection.
- Better distribution of floating point precision
- No far plane clipping artifacts
Because of the reversed depth, shaders must use Greater
depth test (higher depth is closer).
To get from Callisto coords to Vulkan coords, the x and y axes must be flipped.
perspective :: proc(fovy, aspect, near: f32) -> (mat: Matrix4x4) {
ep :: math.pow(2, -20) // ep is a small value to prevent float rounding errors
g := 1 / math.tan(0.5 * fovy)
// Column-major matrix
//
// -g/s 0 0 0
// 0 -g 0 0
// 0 0 ep near * (1-ep)
// 0 0 1 0
mat[0, 0] = -g / aspect
mat[1, 1] = -g
mat[2, 2] = ep
mat[2, 3] = 1
mat[3, 2] = near * (1 - ep)
return mat
}