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, }