Harbor

branch main
showing the latest snapshot on main
vertex_layout.odin 5.2 KB · Plain text
backend/vertex_layout.odin 0644 Raw
package backend

// Vertex layout helpers: build layouts from semantic attributes,
// convert layouts to pipeline descriptor arrays.

vertex_format_size :: proc(fmt: Vertex_Format) -> u32 {
	switch fmt {
	case .Float2:
		return 8
	case .Float3:
		return 12
	case .Float4:
		return 16
	}
	return 0
}

vertex_attrib_default_format :: proc(attrib: Vertex_Attrib) -> Vertex_Format {
	switch attrib {
	case .Position:
		return .Float3
	case .Normal:
		return .Float3
	case .Tex_Coord:
		return .Float2
	case .Tex_Coord_1:
		return .Float2
	case .Color:
		return .Float4
	case .Tangent:
		return .Float4
	}
	return .Float3
}

vertex_layout_build :: proc(attribs: ..Vertex_Attrib) -> Vertex_Layout {
	layout: Vertex_Layout
	offset: u32 = 0
	for a in attribs {
		if int(layout.count) >= MAX_VERTEX_ATTRIBS {break}
		fmt := vertex_attrib_default_format(a)
		layout.entries[layout.count] = {
			attrib = a,
			format = fmt,
			offset = offset,
		}
		offset += vertex_format_size(fmt)
		layout.count += 1
	}
	layout.stride = offset
	return layout
}

vertex_layout_has :: proc(
	layout: ^Vertex_Layout,
	attrib: Vertex_Attrib,
) -> (
	offset: u32,
	ok: bool,
) {
	for i in 0 ..< layout.count {
		if layout.entries[i].attrib == attrib {
			return layout.entries[i].offset, true
		}
	}
	return 0, false
}

// Convert a Vertex_Layout to pipeline descriptor arrays.
// Returns a binding (stride) and up to MAX_VERTEX_ATTRIBS attributes.
// Attribute locations are assigned sequentially (0, 1, 2, ...).
vertex_layout_to_pipeline_attrs :: proc(
	layout: ^Vertex_Layout,
) -> (
	binding: Vertex_Binding,
	attributes: [MAX_VERTEX_ATTRIBS]Vertex_Attribute,
	attr_count: int,
) {
	return vertex_layout_to_pipeline_attrs_for_binding(layout, 0, .Vertex, 0)
}

vertex_layout_to_pipeline_attrs_for_binding :: proc(
	layout: ^Vertex_Layout,
	binding_index: u32,
	input_rate: Vertex_Input_Rate,
	first_location: u32,
) -> (
	binding: Vertex_Binding,
	attributes: [MAX_VERTEX_ATTRIBS]Vertex_Attribute,
	attr_count: int,
) {
	binding = Vertex_Binding {
		binding = binding_index,
		stride  = layout.stride,
		input_rate = input_rate,
	}
	for i in 0 ..< int(layout.count) {
		e := &layout.entries[i]
		attributes[i] = Vertex_Attribute {
			location = first_location + u32(i),
			binding  = binding_index,
			offset   = e.offset,
			format   = e.format,
		}
	}
	return binding, attributes, int(layout.count)
}

// Layout variants: enumerated vertex layout configurations.
// Each variant maps to a specific set of shaders and pipelines.
Layout_Variant :: enum u8 {
	PNU, // Position + Normal + UV (32 bytes)
	PN, // Position + Normal (24 bytes)
	PNUC, // Position + Normal + UV + Color (48 bytes)
	PNUT, // Position + Normal + UV + Tangent (48 bytes)
	PNUCT, // Position + Normal + UV + Color + Tangent (64 bytes)
}
MAX_LAYOUT_VARIANTS :: 5

// Determine the layout variant from a Vertex_Layout descriptor.
layout_to_variant :: proc(layout: ^Vertex_Layout) -> Layout_Variant {
	has_uv := false
	has_color := false
	has_tangent := false
	for i in 0 ..< layout.count {
		switch layout.entries[i].attrib {
		case .Tex_Coord:
			has_uv = true
		case .Color:
			has_color = true
		case .Tangent:
			has_tangent = true
		case .Position, .Normal, .Tex_Coord_1: // always present or ignored
		}
	}
	if has_uv && has_color && has_tangent {return .PNUCT}
	if has_uv && has_tangent {return .PNUT}
	if has_uv && has_color {return .PNUC}
	if has_uv {return .PNU}
	return .PN
}

// Get the predefined layout for a given variant.
variant_to_layout :: proc(variant: Layout_Variant) -> Vertex_Layout {
	switch variant {
	case .PN:
		return LAYOUT_POS_NORM
	case .PNU:
		return LAYOUT_POS_NORM_UV
	case .PNUC:
		return LAYOUT_POS_NORM_UV_COLOR
	case .PNUT:
		return LAYOUT_POS_NORM_UV_TANGENT
	case .PNUCT:
		return LAYOUT_POS_NORM_UV_COLOR_TANGENT
	}
	return LAYOUT_POS_NORM_UV
}

// Predefined layouts

LAYOUT_POS_NORM :: Vertex_Layout {
	entries = {
		0 = {attrib = .Position, format = .Float3, offset = 0},
		1 = {attrib = .Normal, format = .Float3, offset = 12},
	},
	count = 2,
	stride = 24,
}

LAYOUT_POS_NORM_UV :: Vertex_Layout {
	entries = {
		0 = {attrib = .Position, format = .Float3, offset = 0},
		1 = {attrib = .Normal, format = .Float3, offset = 12},
		2 = {attrib = .Tex_Coord, format = .Float2, offset = 24},
	},
	count = 3,
	stride = 32,
}

LAYOUT_POS_NORM_UV_COLOR :: Vertex_Layout {
	entries = {
		0 = {attrib = .Position, format = .Float3, offset = 0},
		1 = {attrib = .Normal, format = .Float3, offset = 12},
		2 = {attrib = .Tex_Coord, format = .Float2, offset = 24},
		3 = {attrib = .Color, format = .Float4, offset = 32},
	},
	count = 4,
	stride = 48,
}

LAYOUT_POS_NORM_UV_TANGENT :: Vertex_Layout {
	entries = {
		0 = {attrib = .Position, format = .Float3, offset = 0},
		1 = {attrib = .Normal, format = .Float3, offset = 12},
		2 = {attrib = .Tex_Coord, format = .Float2, offset = 24},
		3 = {attrib = .Tangent, format = .Float4, offset = 32},
	},
	count = 4,
	stride = 48,
}

LAYOUT_POS_NORM_UV_COLOR_TANGENT :: Vertex_Layout {
	entries = {
		0 = {attrib = .Position, format = .Float3, offset = 0},
		1 = {attrib = .Normal, format = .Float3, offset = 12},
		2 = {attrib = .Tex_Coord, format = .Float2, offset = 24},
		3 = {attrib = .Color, format = .Float4, offset = 32},
		4 = {attrib = .Tangent, format = .Float4, offset = 48},
	},
	count = 5,
	stride = 64,
}