# gltf

`import "github.com/matjam/bunyip/gltf"`

Package gltf loads glTF 2.0 models (.gltf with external or embedded buffers, and .glb) into plain Go slices: positions, normals, UVs, vertex colours, joints and weights, indices, materials with their textures and the KHR extensions the renderer supports (clearcoat, sheen, transmission, volume, IOR, emissive strength, texture transforms, specular, iridescence, anisotropy and converted specular-glossiness), decoded images, morph targets with their default weights, skins, animation clips (node transforms and morph weights) and the flattened node hierarchy with world matrices. Sparse accessors, which store only the elements a morph target moves, decode over the base data or over zeros when the accessor has no buffer view.

Load reads a file, Parse a byte slice; both return a Document. The package has no GPU dependency, so a tool or a server can read models too; gfx.LoadModel uploads a Document into meshes and materials, and phys can take a mesh's triangles as a static collider. Parse errors identify the invalid buffer, accessor, image, node or scene. Hierarchies are validated before resources are resolved: cycles, repeated children, multiple parents and invalid references are rejected. Scene roots must have no parent and be unique within each scene; the same root may be reused across scenes. Validation and flattening take linear work in the hierarchy size and support deep trees without a depth cutoff.

## Types

<a id="AlphaMode"></a>

### AlphaMode

```go
type AlphaMode uint8
```

AlphaMode is how a material's alpha is used.

<a id="AlphaOpaque"></a>

<a id="AlphaMask"></a>

<a id="AlphaBlend"></a>

```go
const (
	AlphaOpaque AlphaMode = iota
	AlphaMask             // fragments below AlphaCutoff are discarded
	AlphaBlend            // alpha-blended
)
```

<a id="Animation"></a>

<a id="Animation.Name"></a>

<a id="Animation.Duration"></a>

<a id="Animation.Channels"></a>

### Animation

```go
type Animation struct {
	Name     string
	Duration float32 // final channel key time in seconds
	Channels []Channel
}
```

Animation is a named clip of node channels.

<a id="Channel"></a>

<a id="Channel.Node"></a>

<a id="Channel.Path"></a>

<a id="Channel.Times"></a>

<a id="Channel.Values"></a>

<a id="Channel.Weights"></a>

<a id="Channel.Step"></a>

### Channel

```go
type Channel struct {
	Node   int
	Path   ChannelPath
	Times  []float32  // key times in seconds
	Values []lin.Vec4 // xyz for translation and scale, xyzw for rotation; nil for weights
	// Weights holds a PathWeights channel's keys: one weight per morph
	// target for each time, in time order.
	Weights []float32
	Step    bool // STEP interpolation; otherwise linear (cubic falls back to linear)
}
```

Channel is one animated node property with keyframes.

<a id="Channel.WeightCount"></a>

#### Channel.WeightCount

```go
func (c *Channel) WeightCount() int
```

WeightCount is the number of morph target weights per key of a PathWeights channel; zero for other paths.

<a id="ChannelPath"></a>

### ChannelPath

```go
type ChannelPath uint8
```

ChannelPath says which node property a channel animates.

<a id="PathTranslation"></a>

<a id="PathRotation"></a>

<a id="PathScale"></a>

<a id="PathWeights"></a>

```go
const (
	PathTranslation ChannelPath = iota
	PathRotation
	PathScale
	PathWeights // the morph target weights of the node's mesh
)
```

<a id="Document"></a>

<a id="Document.Meshes"></a>

<a id="Document.Materials"></a>

<a id="Document.Images"></a>

<a id="Document.Instances"></a>

<a id="Document.Nodes"></a>

<a id="Document.Skins"></a>

<a id="Document.Animations"></a>

### Document

```go
type Document struct {
	Meshes     []Mesh
	Materials  []Material
	Images     []image.Image
	Instances  []Instance // every mesh placement in the default scene, flattened
	Nodes      []Node     // the node hierarchy, for animation
	Skins      []Skin
	Animations []Animation
}
```

Document is a decoded model.

<a id="Load"></a>

#### Load

```go
func Load(path string) (*Document, error)
```

Load reads a .gltf or .glb file, resolving relative URIs next to it.

Example:

Load parses .gltf and .glb files without touching the GPU; gfx.LoadModel uploads the result.

```go
package main

import (
	"fmt"

	"github.com/matjam/bunyip/gltf"
)

func main() {
	doc, err := gltf.Load("robot.glb")
	if err != nil {
		return
	}
	fmt.Println(len(doc.Meshes), "meshes,", len(doc.Animations), "animation clips")
}
```

<a id="Parse"></a>

#### Parse

```go
func Parse(data []byte, resolve Resolver) (*Document, error)
```

Parse decodes .gltf JSON or a .glb container from memory. resolve may be nil when every buffer and image is embedded. Invalid buffer bounds and animation accessor shapes or key counts return an error. The hierarchy must be a forest with unique children and valid child, scene-root and default-scene indices. Malformed hierarchies return errors before resolving external resources.

<a id="Document.Bounds"></a>

#### Document.Bounds

```go
func (d *Document) Bounds() (lo, hi lin.Vec3)
```

Bounds returns the axis-aligned box around every placed primitive.

<a id="Document.IsDataImage"></a>

#### Document.IsDataImage

```go
func (d *Document) IsDataImage(i int) bool
```

IsDataImage reports whether any material uses image i as non-colour data, such as normals, metallic-roughness or extension factor maps. Pass a valid index into Images. An image reused for both colour and data is classified as data; separate images avoid that ambiguity.

<a id="Instance"></a>

<a id="Instance.Name"></a>

<a id="Instance.Mesh"></a>

<a id="Instance.Node"></a>

<a id="Instance.Skin"></a>

<a id="Instance.World"></a>

### Instance

```go
type Instance struct {
	Name  string
	Mesh  int
	Node  int // node index, for animation
	Skin  int // -1 when not skinned
	World lin.Mat4
}
```

Instance places a mesh in the world.

<a id="Material"></a>

<a id="Material.Name"></a>

<a id="Material.BaseColor"></a>

<a id="Material.Image"></a>

<a id="Material.Linear"></a>

<a id="Material.Metallic"></a>

<a id="Material.Roughness"></a>

<a id="Material.MetalRoughImage"></a>

<a id="Material.NormalImage"></a>

<a id="Material.EmissiveImage"></a>

<a id="Material.Emissive"></a>

<a id="Material.OcclusionImage"></a>

<a id="Material.OcclusionStrength"></a>

<a id="Material.AlphaMode"></a>

<a id="Material.AlphaCutoff"></a>

<a id="Material.DoubleSided"></a>

<a id="Material.Unlit"></a>

<a id="Material.OcclusionUV2"></a>

<a id="Material.UVOffset"></a>

<a id="Material.UVRotation"></a>

<a id="Material.UVScale"></a>

<a id="Material.Clearcoat"></a>

<a id="Material.ClearcoatRoughness"></a>

<a id="Material.SheenColor"></a>

<a id="Material.SheenRoughness"></a>

<a id="Material.Transmission"></a>

<a id="Material.TransmissionImage"></a>

<a id="Material.IOR"></a>

<a id="Material.Thickness"></a>

<a id="Material.ThicknessImage"></a>

<a id="Material.AttenuationDistance"></a>

<a id="Material.AttenuationColor"></a>

<a id="Material.SpecularFactor"></a>

<a id="Material.SpecularColor"></a>

<a id="Material.SpecularImage"></a>

<a id="Material.SpecularColorImage"></a>

<a id="Material.IridescenceFactor"></a>

<a id="Material.IridescenceIOR"></a>

<a id="Material.IridescenceThicknessMin"></a>

<a id="Material.IridescenceThicknessMax"></a>

<a id="Material.IridescenceImage"></a>

<a id="Material.IridescenceThicknessImage"></a>

<a id="Material.AnisotropyStrength"></a>

<a id="Material.AnisotropyRotation"></a>

<a id="Material.AnisotropyImage"></a>

<a id="Material.SpecGloss"></a>

<a id="Material.SpecGlossImage"></a>

<a id="Material.Glossiness"></a>

### Material

```go
type Material struct {
	Name      string
	BaseColor [4]float32 // linear RGBA factor
	Image     int        // albedo image index into Document.Images, or -1
	Linear    bool       // sampler asks for linear filtering

	Metallic          float32 // factor; default 1
	Roughness         float32 // factor; default 1
	MetalRoughImage   int     // G roughness, B metallic; -1 none
	NormalImage       int     // -1 none
	EmissiveImage     int     // -1 none
	Emissive          [3]float32
	OcclusionImage    int     // R occlusion; -1 none
	OcclusionStrength float32 // default 1

	AlphaMode   AlphaMode
	AlphaCutoff float32 // for AlphaMask; default 0.5
	DoubleSided bool
	Unlit       bool // KHR_materials_unlit: draw the base colour without lighting

	OcclusionUV2 bool // the occlusion texture uses TEXCOORD_1
	// UVOffset, UVRotation and UVScale are the base colour texture's
	// KHR_texture_transform; Scale is 1,1 without one.
	UVOffset   [2]float32
	UVRotation float32
	UVScale    [2]float32

	Clearcoat           float32    // KHR_materials_clearcoat factor
	ClearcoatRoughness  float32    // default 0
	SheenColor          [3]float32 // KHR_materials_sheen; zero for none
	SheenRoughness      float32
	Transmission        float32    // KHR_materials_transmission factor; zero for opaque
	TransmissionImage   int        // R scales the factor; -1 none
	IOR                 float32    // KHR_materials_ior; default 1.5
	Thickness           float32    // KHR_materials_volume thickness factor
	ThicknessImage      int        // G scales the thickness, as glTF stores it; -1 none
	AttenuationDistance float32    // zero for no absorption
	AttenuationColor    [3]float32 // default white

	// Specular is KHR_materials_specular: SpecularFactor scales a
	// dielectric's reflection (default 1) and SpecularColor tints it
	// (default white). SpecularImage holds the strength in its alpha and
	// SpecularColorImage the tint in its RGB; each is -1 when the file
	// gives none.
	SpecularFactor     float32
	SpecularColor      [3]float32
	SpecularImage      int
	SpecularColorImage int

	// Iridescence is KHR_materials_iridescence, a thin film over the
	// surface: IridescenceFactor is its strength (default 0),
	// IridescenceIOR its index of refraction (default 1.3) and the film
	// is between IridescenceThicknessMin and IridescenceThicknessMax
	// nanometres thick (defaults 100 and 400). IridescenceImage scales
	// the strength by its red channel and IridescenceThicknessImage
	// places the thickness between the two by its green channel; each is
	// -1 when the file gives none.
	IridescenceFactor         float32
	IridescenceIOR            float32
	IridescenceThicknessMin   float32
	IridescenceThicknessMax   float32
	IridescenceImage          int
	IridescenceThicknessImage int

	// Anisotropy is KHR_materials_anisotropy, a highlight stretched along
	// the surface: AnisotropyStrength is how far (default 0) and
	// AnisotropyRotation which way, in radians. AnisotropyImage holds a
	// direction in red and green and a strength in blue, or -1.
	AnisotropyStrength float32
	AnisotropyRotation float32
	AnisotropyImage    int

	// SpecGloss reports that the material came from
	// KHR_materials_pbrSpecularGlossiness, which the loader converts to
	// metallic-roughness: the factors above are the converted ones.
	// SpecGlossImage is the extension's specular-glossiness image, whose
	// alpha is the glossiness, or -1; the renderer turns it into a
	// metallic-roughness map. Its RGB, the specular colour per texel, is
	// not used, so a file whose specular colour varies across one
	// material loads with the converted factors alone.
	SpecGloss      bool
	SpecGlossImage int
	// Glossiness is the extension's glossiness factor, which scales the
	// image's alpha; 1 without the extension.
	Glossiness float32
}
```

Material is a decoded metallic-roughness material. Defaults below are supplied by Parse for omitted file properties, not by the Go zero value. Image references index Document.Images; -1 means no image. A hand-built material must set unused image references to -1 explicitly.

<a id="Mesh"></a>

<a id="Mesh.Name"></a>

<a id="Mesh.Primitives"></a>

<a id="Mesh.Weights"></a>

<a id="Mesh.TargetNames"></a>

### Mesh

```go
type Mesh struct {
	Name       string
	Primitives []Primitive
	// Weights are the default morph target weights, one per target; nil
	// means every target at zero.
	Weights []float32
	// TargetNames names the morph targets when the file carries them in
	// the mesh's extras; nil otherwise.
	TargetNames []string
}
```

Mesh is one glTF mesh: a set of primitives drawn together.

<a id="Mesh.TargetCount"></a>

#### Mesh.TargetCount

```go
func (m *Mesh) TargetCount() int
```

TargetCount is the number of morph targets, from the first primitive.

<a id="MorphTarget"></a>

<a id="MorphTarget.Positions"></a>

<a id="MorphTarget.Normals"></a>

### MorphTarget

```go
type MorphTarget struct {
	Positions []lin.Vec3 // one per vertex
	Normals   []lin.Vec3 // nil when the target has none
}
```

MorphTarget is one blend shape of a primitive: offsets added to each vertex's position, and to its normal when the target has normals, scaled by the target's weight.

<a id="Node"></a>

<a id="Node.Name"></a>

<a id="Node.Parent"></a>

<a id="Node.Children"></a>

<a id="Node.Translation"></a>

<a id="Node.Rotation"></a>

<a id="Node.Scale"></a>

<a id="Node.Mesh"></a>

<a id="Node.Skin"></a>

<a id="Node.Weights"></a>

### Node

```go
type Node struct {
	Name        string
	Parent      int      // -1 for a root
	Children    []int    // indices into Document.Nodes
	Translation lin.Vec3 // relative to Parent, in model units
	Rotation    lin.Quat // local rotation; Parse supplies identity when omitted
	Scale       lin.Vec3 // local scale; Parse supplies (1, 1, 1) when omitted
	Mesh        int      // -1 none
	Skin        int      // -1 none
	// Weights are the node's own morph target weights, overriding the
	// mesh's defaults; nil means the mesh's.
	Weights []float32
}
```

Node is one node of the hierarchy with its rest-pose local transform.

<a id="Node.Local"></a>

#### Node.Local

```go
func (n Node) Local() lin.Mat4
```

Local returns the node's rest-pose local matrix.

<a id="Primitive"></a>

<a id="Primitive.Positions"></a>

<a id="Primitive.Normals"></a>

<a id="Primitive.UVs"></a>

<a id="Primitive.Indices"></a>

<a id="Primitive.Material"></a>

<a id="Primitive.Joints"></a>

<a id="Primitive.Weights"></a>

<a id="Primitive.Colors"></a>

<a id="Primitive.UVs2"></a>

<a id="Primitive.Targets"></a>

### Primitive

```go
type Primitive struct {
	Positions []lin.Vec3
	Normals   []lin.Vec3 // computed from the triangles when the file has none
	UVs       []lin.Vec2 // zero-filled when the file has none
	Indices   []uint32
	Material  int        // index into Document.Materials, or -1
	Joints    [][4]uint8 // per vertex, when skinned
	Weights   [][4]float32
	Colors    []lin.Vec4 // COLOR_0, linear RGBA; nil when the file has none
	UVs2      []lin.Vec2 // TEXCOORD_1; nil when the file has none
	Targets   []MorphTarget
}
```

Primitive is a triangle list with one material.

<a id="Primitive.Skinned"></a>

#### Primitive.Skinned

```go
func (p *Primitive) Skinned() bool
```

Skinned reports whether the primitive carries joint weights.

<a id="Resolver"></a>

### Resolver

```go
type Resolver func(uri string) ([]byte, error)
```

Resolver fetches the bytes behind a relative URI in a .gltf file.

<a id="Skin"></a>

<a id="Skin.Name"></a>

<a id="Skin.Joints"></a>

<a id="Skin.InverseBind"></a>

### Skin

```go
type Skin struct {
	Name        string
	Joints      []int      // Document.Nodes indices, addressed by vertex joint indices
	InverseBind []lin.Mat4 // inverse bind matrix for each entry in Joints
}
```

Skin binds joints (node indices) with their inverse bind matrices.
