Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ungerik/go3d

Repository files navigation

go3d

Performance-oriented 3D math library for Go, optimized for graphics programming, game development, and scientific computing.

Go Reference Go Report Card

Features

  • Zero-allocation value types: Stack-allocated arrays for maximum performance
  • Dual precision: Both float32 and float64 versions of all types
  • Method chaining: In-place operations return *T for fluent API
  • Immutable operations: Past-tense methods return copies without modifying originals
  • OpenGL compatible: Column-major matrices, right-handed coordinates
  • Cache-friendly: Optimized data structures for modern CPU caches
  • Well-tested: Comprehensive test coverage with benchmarks

Installation

go get github.com/ungerik/go3d

Quick Start

package main

import (
    "github.com/ungerik/go3d/vec3"
    "github.com/ungerik/go3d/mat4"
    "github.com/ungerik/go3d/quaternion"
    math "github.com/chewxy/math32"
)

func main() {
    // Vector operations
    a := vec3.T{1, 2, 3}
    b := vec3.UnitX

    // In-place modification with chaining
    a.Add(&b).Scale(5)  // a is now {10, 10, 15}

    // Immutable operations
    c := a.Scaled(0.5)  // c = {5, 5, 7.5}, a unchanged

    // Matrix transformations
    var transform mat4.T
    transform.AssignPerspective(math.Pi/4, 16.0/9.0, 0.1, 1000.0)

    // Quaternions for rotations
    q := quaternion.FromYAxisAngle(math.Pi / 4)  // 45° around Y
    rotated := q.RotatedVec3(&a)
}

Package Organization

Every type has its own sub-package and is named T. Packages under float64/ use float64 instead of float32.

Main Packages (float32)

Package Description Type Size
vec2 2D vectors 8 bytes
vec3 3D vectors 12 bytes
vec4 4D vectors / homogeneous coordinates 16 bytes
mat2 2×2 matrices 16 bytes
mat3 3×3 matrices 36 bytes
mat4 4×4 matrices 64 bytes (one cache line!)
quaternion Quaternions for 3D rotations 16 bytes

Float64 Packages

All types are available in float64 precision under float64/:

  • float64/vec2, float64/vec3, float64/vec4
  • float64/mat2, float64/mat3, float64/mat4
  • float64/quaternion

Utility Packages

  • generic - Generic matrix/vector interfaces
  • hermit2 - 2D Hermite splines
  • hermit3 - 3D Hermite splines

Float32 Math Functions

This library uses github.com/chewxy/math32 for float32 math functions. Import it as:

import math "github.com/chewxy/math32"

The math32 package provides float32 versions of Go's standard math library functions, offering better performance for float32-based graphics calculations compared to converting to/from float64.

Core Concepts

Naming Conventions

Present tense methods modify in place and return *T for chaining:

v := vec3.T{1, 2, 3}
v.Scale(2)           // v is now {2, 4, 6}
v.Add(&vec3.UnitX)   // v is now {3, 4, 6}

Past tense methods return a modified copy:

v := vec3.T{1, 2, 3}
v2 := v.Scaled(2)        // v2 = {2, 4, 6}, v unchanged
v3 := v.Added(&vec3.UnitX)  // v3 = {2, 2, 3}, v unchanged

Method Chaining

result := vec3.Zero
result.Add(&vec3.UnitX).Scale(5).Add(&vec3.UnitY)
// result = {5, 1, 0}

Matrix Layout

Matrices use column-major layout (OpenGL convention):

mat[column][row]

For DirectX (row-major), use Transpose().

Coordinate Systems

  • Right-handed coordinates: X × Y = Z
  • Rotation direction: Counter-clockwise (right-hand rule)
  • Angles: Radians (use math32.Pi constants)

Common Operations

Vectors

// Construction
v := vec3.T{1, 2, 3}

// Length
length := v.Length()         // sqrt(x² + y² + z²)
lengthSq := v.LengthSqr()   // x² + y² + z² (faster, no sqrt)

// Normalization
v.Normalize()               // In-place
unit := v.Normalized()      // Copy

// Arithmetic
v.Add(&other)               // v += other
v.Sub(&other)               // v -= other
v.Scale(2.5)                // v *= 2.5

// Dot and cross products
dot := vec3.Dot(&a, &b)
cross := vec3.Cross(&a, &b)

// Distance
dist := vec3.Distance(&a, &b)

Matrices

// Identity and zero
m := mat4.Ident
z := mat4.Zero

// Translation
m.SetTranslation(&position)

// Rotation
m.AssignXRotation(angle)
m.AssignYRotation(angle)
m.AssignZRotation(angle)
m.AssignQuaternion(&quat)

// Scaling
m.AssignScaling(sx, sy, sz)

// Camera matrix
var view mat4.T
view.AssignLookAt(&eye, &center, &up)

// Projection matrices
var proj mat4.T

// Symmetric perspective (typical 3D game)
proj.AssignPerspective(fovy, aspect, znear, zfar)

// Asymmetric frustum
proj.AssignFrustum(left, right, bottom, top, znear, zfar)

// Orthographic (2D or CAD)
proj.AssignOrthogonalProjection(left, right, bottom, top, znear, zfar)

// Matrix multiplication
mvp := mat4.Ident
mvp.AssignMul(&projection, &view)
mvp.Mul(&model)

Quaternions

// Construction
axis := vec3.T{0, 1, 0}
q := quaternion.FromAxisAngle(&axis, math.Pi/4)

// Convenience constructors
qx := quaternion.FromXAxisAngle(angle)
qy := quaternion.FromYAxisAngle(angle)
qz := quaternion.FromZAxisAngle(angle)

// From Euler angles
q := quaternion.FromEulerAngles(yaw, pitch, roll)

// Rotate vector
v := vec3.T{1, 0, 0}
q.RotateVec3(&v)        // In-place
rotated := q.RotatedVec3(&v)  // Copy

// Combine rotations (order matters!)
q3 := quaternion.Mul(&q1, &q2)  // Apply q2 first, then q1

// Smooth interpolation
halfway := quaternion.Slerp(&start, &end, 0.5)

// Convert to matrix
var m mat4.T
m.AssignQuaternion(&q)

Performance Tips

1. Use Appropriate Precision

// For graphics (sufficient for most cases)
import "github.com/ungerik/go3d/vec3"

// For scientific computing (higher precision)
import "github.com/ungerik/go3d/float64/vec3"

2. Prefer In-Place Operations

// ✓ Good: Minimal allocations
v.Scale(2).Add(&other)

// ✗ Less efficient: Creates copies
result := v.Scaled(2).Added(&other)

3. Use LengthSqr for Comparisons

// ✓ Faster: No sqrt
thresholdSq := threshold * threshold
if v.LengthSqr() < thresholdSq {
    // ...
}

// ✗ Slower: Uses sqrt
if v.Length() < threshold {
    // ...
}

4. Pass Pointers to Functions

// ✓ Efficient: No copy
dot := vec3.Dot(&a, &b)

// The API uses pointers for better performance

5. Reuse Matrix Allocations

// ✓ Good: Single allocation reused
var transform mat4.T
for i := range objects {
    transform.AssignTranslation(&objects[i].Position)
    // Use transform...
}

// ✗ Bad: Allocates every iteration
for i := range objects {
    var transform mat4.T  // New allocation
    // ...
}

API Reference

Rectangle (vec2 package)

type Rect struct {
    Min vec2.T
    Max vec2.T
}

Operations:

r := vec2.Rect{Min: vec2.T{0, 0}, Max: vec2.T{100, 100}}

width := r.Width()
height := r.Height()
center := r.Center()
area := r.Area()

joined := r.Join(&other)        // Bounding rectangle
clamped := r.Clamp(&point)      // Clamp point to rectangle
contains := r.Contains(&point)   // Point-in-rectangle test

Box (vec3 package)

type Box struct {
    Min vec3.T
    Max vec3.T
}

Operations:

b := vec3.Box{Min: vec3.T{0, 0, 0}, Max: vec3.T{10, 10, 10}}

size := b.Size()
center := b.Center()
volume := b.Volume()

joined := b.Join(&other)        // Bounding box
contains := b.Contains(&point)   // Point-in-box test

Migration Notes

Matrix Multiplication Order

Transforms apply right to left:

// Mathematical notation: MVP = P × V × M
// In go3d:
var mvp mat4.T
mvp.AssignMul(&projection, &view)  // mvp = P × V
mvp.Mul(&model)                     // mvp = (P × V) × M

Vec4 Homogeneous Coordinates

// Point in space (affected by translation): w=1
point := vec4.T{x, y, z, 1.0}

// Direction vector (not affected by translation): w=0
direction := vec4.T{x, y, z, 0.0}

Examples

Complete Transform Pipeline

// Object transform
position := vec3.T{10, 5, 0}
rotation := quaternion.FromYAxisAngle(math.Pi / 4)
scale := vec3.T{2, 2, 2}

var model mat4.T
model.SetTranslation(&position)
model.AssignQuaternion(&rotation)
model.ScaleVec3(&scale)

// Camera
eye := vec3.T{0, 10, 20}
center := vec3.T{0, 0, 0}
up := vec3.UnitY

var view mat4.T
view.AssignLookAt(&eye, &center, &up)

// Projection
var projection mat4.T
projection.AssignPerspective(math.Pi/4, 16.0/9.0, 0.1, 1000.0)

// Combined MVP
var mvp mat4.T
mvp.AssignMul(&projection, &view)
mvp.Mul(&model)

// Transform vertex
vertex := vec4.T{1, 0, 0, 1}
transformed := mat4.MulVec4(&mvp, &vertex)

Camera Rotation with Slerp

func SmoothCamera(start, end quaternion.T, t float32) quaternion.T {
    // Clamp t to [0, 1]
    if t < 0 { t = 0 }
    if t > 1 { t = 1 }

    // Smooth interpolation
    return quaternion.Slerp(&start, &end, t)
}

Billboard Matrix

Make objects always face the camera:

func CreateBillboard(objectPos, cameraPos, cameraUp vec3.T) mat4.T {
    // Calculate direction to camera
    direction := vec3.Sub(&cameraPos, &objectPos)
    direction.Normalize()

    // Calculate right vector
    right := vec3.Cross(&cameraUp, &direction)
    right.Normalize()

    // Recalculate up vector
    up := vec3.Cross(&direction, &right)

    // Build matrix
    var billboard mat4.T
    billboard[0] = vec4.T{right[0], right[1], right[2], 0}
    billboard[1] = vec4.T{up[0], up[1], up[2], 0}
    billboard[2] = vec4.T{direction[0], direction[1], direction[2], 0}
    billboard[3] = vec4.T{objectPos[0], objectPos[1], objectPos[2], 1}

    return billboard
}

Design Philosophy

The package is designed for performance over convenience where necessary:

  • Pointer arguments: Reduces allocations and copies
  • Stack allocation: Value types (arrays) avoid GC pressure
  • Column-major matrices: Direct GPU upload without transpose
  • Method chaining: Fluent API without sacrificing performance

Performance comparison (see mat4/mat4_test.go):

cd mat4
go test -bench=BenchmarkMulAddVec4_PassBy*

Contributing

Contributions are welcome! Please ensure:

  1. All tests pass: go test ./...
  2. Code is formatted: go fmt ./...
  3. Documentation is updated
  4. Benchmarks show no regressions

License

MIT - see LICENSE file for details.

About

A performance oriented 2D/3D math package for Go

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 11

Languages