1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
// Gawain vertex format
//
// This code is part of the Gawain library, with modifications
// specific to integration with Blender.
//
// Copyright 2016 Mike Erwin
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
// the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
#include "vertex_format.h"
#include <stdlib.h>
#include <string.h>
#define PACK_DEBUG 0
#if PACK_DEBUG
#include <stdio.h>
#endif
void VertexFormat_clear(VertexFormat* format)
{
for (unsigned a = 0; a < format->attrib_ct; ++a)
free(format->attribs[a].name);
#if TRUST_NO_ONE
memset(format, 0, sizeof(VertexFormat));
#else
format->attrib_ct = 0;
format->packed = false;
#endif
}
void VertexFormat_copy(VertexFormat* dest, const VertexFormat* src)
{
// discard dest format's old name strings
for (unsigned a = 0; a < dest->attrib_ct; ++a)
free(dest->attribs[a].name);
// copy regular struct fields
memcpy(dest, src, sizeof(VertexFormat));
// give dest attribs their own copy of name strings
for (unsigned i = 0; i < src->attrib_ct; ++i)
dest->attribs[i].name = strdup(src->attribs[i].name);
}
static unsigned comp_sz(GLenum type)
{
#if TRUST_NO_ONE
assert(type >= GL_BYTE && type <= GL_FLOAT);
#endif
const GLubyte sizes[] = {1,1,2,2,4,4,4};
return sizes[type - GL_BYTE];
}
static unsigned attrib_sz(const Attrib *a)
{
return a->comp_ct * comp_sz(a->comp_type);
}
static unsigned attrib_align(const Attrib *a)
{
unsigned c = comp_sz(a->comp_type);
if (a->comp_ct == 3 && c <= 2)
return 4 * c; // AMD HW can't fetch these well, so pad it out (other vendors too?)
else
return c; // most fetches are ok if components are naturally aligned
}
unsigned vertex_buffer_size(const VertexFormat* format, unsigned vertex_ct)
{
#if TRUST_NO_ONE
assert(format->packed && format->stride > 0);
#endif
return format->stride * vertex_ct;
}
unsigned add_attrib(VertexFormat* format, const char* name, GLenum comp_type, unsigned comp_ct, VertexFetchMode fetch_mode)
{
#if TRUST_NO_ONE
assert(format->attrib_ct < MAX_VERTEX_ATTRIBS); // there's room for more
assert(!format->packed); // packed means frozen/locked
assert(comp_ct >= 1 && comp_ct <= 4);
switch (comp_type)
{
case GL_FLOAT:
// float type can only kept as float
assert(fetch_mode == KEEP_FLOAT);
break;
#if 0 // enable this after switching to our own enum for comp_type
default:
// integer types can be kept as int or converted/normalized to float
assert(fetch_mode != KEEP_FLOAT);
#else
case GL_BYTE:
case GL_UNSIGNED_BYTE:
case GL_SHORT:
case GL_UNSIGNED_SHORT:
case GL_INT:
case GL_UNSIGNED_INT:
// integer types can be converted, normalized, or kept as int
assert(fetch_mode != KEEP_FLOAT);
break;
default:
assert(false); // invalid comp_type
#endif
}
#endif
const unsigned attrib_id = format->attrib_ct++;
Attrib* attrib = format->attribs + attrib_id;
attrib->name = strdup(name);
attrib->comp_type = comp_type;
attrib->comp_ct = comp_ct;
attrib->sz = attrib_sz(attrib);
attrib->offset = 0; // offsets & stride are calculated later (during pack)
attrib->fetch_mode = fetch_mode;
return attrib_id;
}
unsigned padding(unsigned offset, unsigned alignment)
{
const unsigned mod = offset % alignment;
return (mod == 0) ? 0 : (alignment - mod);
}
#if PACK_DEBUG
static void show_pack(unsigned a_idx, unsigned sz, unsigned pad)
{
const char c = 'A' + a_idx;
for (unsigned i = 0; i < pad; ++i)
putchar('-');
for (unsigned i = 0; i < sz; ++i)
putchar(c);
}
#endif
void VertexFormat_pack(VertexFormat* format)
{
// for now, attributes are packed in the order they were added,
// making sure each attrib is naturally aligned (add padding where necessary)
// later we can implement more efficient packing w/ reordering
// (keep attrib ID order, adjust their offsets to reorder in buffer)
// TODO: concatentate name strings into attribs[0].name, point attribs[i] to
// offset into the combined string. Free all other name strings. Could save more
// space by storing combined string in VertexFormat, with each attrib having an
// offset into it. Could also append each name string as it's added... pack()
// could alloc just enough to hold the final combo string. And just enough to
// hold used attribs, not all 16.
Attrib* a0 = format->attribs + 0;
a0->offset = 0;
unsigned offset = a0->sz;
#if PACK_DEBUG
show_pack(0, a0->sz, 0);
#endif
for (unsigned a_idx = 1; a_idx < format->attrib_ct; ++a_idx)
{
Attrib* a = format->attribs + a_idx;
unsigned mid_padding = padding(offset, attrib_align(a));
offset += mid_padding;
a->offset = offset;
offset += a->sz;
#if PACK_DEBUG
show_pack(a_idx, a->sz, mid_padding);
#endif
}
unsigned end_padding = padding(offset, attrib_align(a0));
#if PACK_DEBUG
show_pack(0, 0, end_padding);
putchar('\n');
#endif
format->stride = offset + end_padding;
format->packed = true;
}
|