diff options
Diffstat (limited to 'gfx/src/core/geometry.c')
-rw-r--r-- | gfx/src/core/geometry.c | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/gfx/src/core/geometry.c b/gfx/src/core/geometry.c new file mode 100644 index 0000000..cfc749f --- /dev/null +++ b/gfx/src/core/geometry.c | |||
@@ -0,0 +1,326 @@ | |||
1 | #include "geometry.h" | ||
2 | |||
3 | #include "buffer.h" | ||
4 | #include "constants.h" | ||
5 | |||
6 | #include <gfx_assert.h> | ||
7 | |||
8 | #include <math/vec2.h> | ||
9 | #include <math/vec3.h> | ||
10 | |||
11 | /// Determines whether a view is populated. | ||
12 | /// | ||
13 | /// Note that views are allowed to have no data, in which case a buffer of the | ||
14 | /// specified size is created. | ||
15 | #define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.size_bytes > 0) | ||
16 | |||
17 | static GLenum primitive_type_to_gl(PrimitiveType type) { | ||
18 | switch (type) { | ||
19 | case Triangles: | ||
20 | return GL_TRIANGLES; | ||
21 | case TriangleFan: | ||
22 | return GL_TRIANGLE_FAN; | ||
23 | case TriangleStrip: | ||
24 | return GL_TRIANGLE_STRIP; | ||
25 | } | ||
26 | FAIL("primitive_type_to_gl(): missing case"); | ||
27 | return GL_INVALID_ENUM; | ||
28 | } | ||
29 | |||
30 | /// Create a typed buffer for the buffer view if the view does not already point | ||
31 | /// to a buffer. | ||
32 | void init_view_buffer( | ||
33 | GfxCore* gfxcore, BufferView* view, BufferType buffer_type, | ||
34 | BufferUsage buffer_usage) { | ||
35 | if (!view->buffer) { | ||
36 | view->buffer = gfx_make_buffer( | ||
37 | gfxcore, | ||
38 | &(BufferDesc){ | ||
39 | .usage = buffer_usage, | ||
40 | .type = buffer_type, | ||
41 | .data.data = view->data, | ||
42 | .data.count = view->size_bytes / | ||
43 | gfx_get_buffer_type_size_bytes(buffer_type)}); | ||
44 | } | ||
45 | assert(view->size_bytes <= view->buffer->size_bytes); | ||
46 | } | ||
47 | |||
48 | /// Configure the buffer in teh VAO. | ||
49 | static void configure_buffer( | ||
50 | GfxCore* gfxcore, const GeometryDesc* desc, BufferView* view, | ||
51 | size_t num_components, size_t component_size_bytes, GLenum component_type, | ||
52 | GLboolean normalized, GLuint channel) { | ||
53 | assert(gfxcore); | ||
54 | assert(desc); | ||
55 | assert(view); | ||
56 | assert(view->buffer); | ||
57 | assert( | ||
58 | desc->num_verts <= | ||
59 | view->size_bytes / (num_components * component_size_bytes)); | ||
60 | assert(view->size_bytes <= view->buffer->size_bytes); | ||
61 | |||
62 | glBindBuffer(GL_ARRAY_BUFFER, view->buffer->vbo); | ||
63 | glEnableVertexAttribArray(channel); | ||
64 | if ((component_type == GL_FLOAT) || normalized) { | ||
65 | glVertexAttribPointer( | ||
66 | channel, num_components, component_type, normalized, view->stride_bytes, | ||
67 | (const void*)view->offset_bytes); | ||
68 | } else { | ||
69 | assert(!normalized); | ||
70 | assert( | ||
71 | (component_type == GL_BYTE) || (component_type == GL_UNSIGNED_BYTE) || | ||
72 | (component_type == GL_SHORT) || (component_type == GL_UNSIGNED_SHORT) || | ||
73 | (component_type == GL_INT) || component_type == GL_UNSIGNED_INT); | ||
74 | glVertexAttribIPointer( | ||
75 | channel, num_components, component_type, view->stride_bytes, | ||
76 | (const void*)view->offset_bytes); | ||
77 | } | ||
78 | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
79 | } | ||
80 | |||
81 | static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { | ||
82 | assert(gfxcore); | ||
83 | assert(desc); | ||
84 | |||
85 | if (view_is_populated(desc->positions3d)) { | ||
86 | init_view_buffer( | ||
87 | gfxcore, (BufferView*)&desc->positions3d, Buffer3d, desc->buffer_usage); | ||
88 | if (!desc->positions3d.buffer) { | ||
89 | return false; | ||
90 | } | ||
91 | configure_buffer( | ||
92 | gfxcore, desc, (BufferView*)&desc->positions3d, 3, sizeof(float), | ||
93 | GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); | ||
94 | } else if (view_is_populated(desc->positions2d)) { | ||
95 | init_view_buffer( | ||
96 | gfxcore, (BufferView*)&desc->positions2d, Buffer2d, desc->buffer_usage); | ||
97 | if (!desc->positions2d.buffer) { | ||
98 | return false; | ||
99 | } | ||
100 | configure_buffer( | ||
101 | gfxcore, desc, (BufferView*)&desc->positions2d, 2, sizeof(float), | ||
102 | GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); | ||
103 | } | ||
104 | if (view_is_populated(desc->normals)) { | ||
105 | init_view_buffer( | ||
106 | gfxcore, (BufferView*)&desc->normals, Buffer3d, desc->buffer_usage); | ||
107 | if (!desc->normals.buffer) { | ||
108 | return false; | ||
109 | } | ||
110 | configure_buffer( | ||
111 | gfxcore, desc, (BufferView*)&desc->normals, 3, sizeof(float), GL_FLOAT, | ||
112 | GL_FALSE, GFX_NORMAL_CHANNEL); | ||
113 | } | ||
114 | if (view_is_populated(desc->tangents)) { | ||
115 | init_view_buffer( | ||
116 | gfxcore, (BufferView*)&desc->tangents, Buffer4d, desc->buffer_usage); | ||
117 | if (!desc->tangents.buffer) { | ||
118 | return false; | ||
119 | } | ||
120 | configure_buffer( | ||
121 | gfxcore, desc, (BufferView*)&desc->tangents, 4, sizeof(float), GL_FLOAT, | ||
122 | GL_FALSE, GFX_TANGENT_CHANNEL); | ||
123 | } | ||
124 | if (view_is_populated(desc->texcoords)) { | ||
125 | init_view_buffer( | ||
126 | gfxcore, (BufferView*)&desc->texcoords, Buffer2d, desc->buffer_usage); | ||
127 | if (!desc->texcoords.buffer) { | ||
128 | return false; | ||
129 | } | ||
130 | configure_buffer( | ||
131 | gfxcore, desc, (BufferView*)&desc->texcoords, 2, sizeof(float), | ||
132 | GL_FLOAT, GL_FALSE, GFX_TEXCOORDS_CHANNEL); | ||
133 | } | ||
134 | if (view_is_populated(desc->joints.u8)) { | ||
135 | init_view_buffer( | ||
136 | gfxcore, (BufferView*)&desc->joints.u8, BufferU8, desc->buffer_usage); | ||
137 | if (!desc->joints.u8.buffer) { | ||
138 | return false; | ||
139 | } | ||
140 | configure_buffer( | ||
141 | gfxcore, desc, (BufferView*)&desc->joints.u8, 4, sizeof(uint8_t), | ||
142 | GL_UNSIGNED_BYTE, GL_FALSE, GFX_JOINTS_CHANNEL); | ||
143 | } else if (view_is_populated(desc->joints.u16)) { | ||
144 | init_view_buffer( | ||
145 | gfxcore, (BufferView*)&desc->joints.u16, BufferU16, desc->buffer_usage); | ||
146 | if (!desc->joints.u16.buffer) { | ||
147 | return false; | ||
148 | } | ||
149 | configure_buffer( | ||
150 | gfxcore, desc, (BufferView*)&desc->joints.u16, 4, sizeof(uint16_t), | ||
151 | GL_UNSIGNED_SHORT, GL_FALSE, GFX_JOINTS_CHANNEL); | ||
152 | } | ||
153 | |||
154 | // If weights are given as unsigned integers, then they are normalized | ||
155 | // when read by the shader. | ||
156 | if (view_is_populated(desc->weights.u8)) { | ||
157 | init_view_buffer( | ||
158 | gfxcore, (BufferView*)&desc->weights.u8, BufferU8, desc->buffer_usage); | ||
159 | if (!desc->weights.u8.buffer) { | ||
160 | return false; | ||
161 | } | ||
162 | configure_buffer( | ||
163 | gfxcore, desc, (BufferView*)&desc->weights.u8, 4, sizeof(uint8_t), | ||
164 | GL_UNSIGNED_BYTE, GL_TRUE, GFX_WEIGHTS_CHANNEL); | ||
165 | } else if (view_is_populated(desc->weights.u16)) { | ||
166 | init_view_buffer( | ||
167 | gfxcore, (BufferView*)&desc->weights.u16, BufferU16, | ||
168 | desc->buffer_usage); | ||
169 | if (!desc->weights.u16.buffer) { | ||
170 | return false; | ||
171 | } | ||
172 | configure_buffer( | ||
173 | gfxcore, desc, (BufferView*)&desc->weights.u16, 4, sizeof(uint16_t), | ||
174 | GL_UNSIGNED_SHORT, GL_TRUE, GFX_WEIGHTS_CHANNEL); | ||
175 | } else if (view_is_populated(desc->weights.floats)) { | ||
176 | init_view_buffer( | ||
177 | gfxcore, (BufferView*)&desc->weights.floats, BufferFloat, | ||
178 | desc->buffer_usage); | ||
179 | if (!desc->weights.floats.buffer) { | ||
180 | return false; | ||
181 | } | ||
182 | configure_buffer( | ||
183 | gfxcore, desc, (BufferView*)&desc->weights.floats, 4, sizeof(float), | ||
184 | GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL); | ||
185 | } | ||
186 | |||
187 | return true; | ||
188 | } | ||
189 | |||
190 | static bool configure_indices(GfxCore* gfxcore, GeometryDesc* desc) { | ||
191 | assert(gfxcore); | ||
192 | assert(desc); | ||
193 | |||
194 | if (view_is_populated(desc->indices8)) { | ||
195 | assert(desc->num_indices > 0); | ||
196 | assert( | ||
197 | desc->num_indices <= desc->indices8.size_bytes / sizeof(VertexIndex8)); | ||
198 | init_view_buffer( | ||
199 | gfxcore, (BufferView*)&desc->indices8, BufferU8, desc->buffer_usage); | ||
200 | if (!desc->indices8.buffer) { | ||
201 | return false; | ||
202 | } | ||
203 | } else if (view_is_populated(desc->indices16)) { | ||
204 | assert(desc->num_indices > 0); | ||
205 | assert( | ||
206 | desc->num_indices <= | ||
207 | desc->indices16.size_bytes / sizeof(VertexIndex16)); | ||
208 | init_view_buffer( | ||
209 | gfxcore, (BufferView*)&desc->indices16, BufferU16, desc->buffer_usage); | ||
210 | if (!desc->indices16.buffer) { | ||
211 | return false; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | return true; | ||
216 | } | ||
217 | |||
218 | bool gfx_init_geometry( | ||
219 | Geometry* geometry, GfxCore* gfxcore, const GeometryDesc* input_desc) { | ||
220 | assert(geometry); | ||
221 | assert(gfxcore); | ||
222 | assert(input_desc); | ||
223 | assert( | ||
224 | view_is_populated(input_desc->positions3d) || | ||
225 | view_is_populated(input_desc->positions2d)); | ||
226 | assert(input_desc->num_verts > 0); | ||
227 | |||
228 | geometry->mode = primitive_type_to_gl(input_desc->type); | ||
229 | geometry->desc = *input_desc; | ||
230 | geometry->num_verts = input_desc->num_verts; | ||
231 | geometry->gfxcore = gfxcore; | ||
232 | |||
233 | // The geometry's copy of the descriptor is manipulated below. Create a | ||
234 | // shorter name for it. | ||
235 | GeometryDesc* desc = &geometry->desc; | ||
236 | |||
237 | glGenVertexArrays(1, &geometry->vao); | ||
238 | glBindVertexArray(geometry->vao); | ||
239 | if (!configure_vertex_attributes(gfxcore, desc)) { | ||
240 | goto cleanup; | ||
241 | } | ||
242 | if (!configure_indices(gfxcore, desc)) { | ||
243 | goto cleanup; | ||
244 | } | ||
245 | glBindVertexArray(0); | ||
246 | ASSERT_GL; | ||
247 | |||
248 | return true; | ||
249 | |||
250 | cleanup: | ||
251 | gfx_del_geometry(geometry); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | void gfx_del_geometry(Geometry* geometry) { | ||
256 | assert(geometry); | ||
257 | if (geometry->vao) { | ||
258 | glDeleteVertexArrays(1, &geometry->vao); | ||
259 | geometry->vao = 0; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { | ||
264 | assert(geometry); | ||
265 | assert(desc); | ||
266 | // New geometry size cannot exceed original size. | ||
267 | assert(desc->positions3d.size_bytes <= geometry->desc.positions3d.size_bytes); | ||
268 | assert(desc->positions2d.size_bytes <= geometry->desc.positions2d.size_bytes); | ||
269 | assert(desc->normals.size_bytes <= geometry->desc.normals.size_bytes); | ||
270 | assert(desc->tangents.size_bytes <= geometry->desc.tangents.size_bytes); | ||
271 | assert(desc->texcoords.size_bytes <= geometry->desc.texcoords.size_bytes); | ||
272 | assert(desc->joints.u8.size_bytes <= geometry->desc.joints.u8.size_bytes); | ||
273 | assert(desc->joints.u16.size_bytes <= geometry->desc.joints.u16.size_bytes); | ||
274 | assert(desc->weights.u8.size_bytes <= geometry->desc.weights.u8.size_bytes); | ||
275 | assert(desc->weights.u16.size_bytes <= geometry->desc.weights.u16.size_bytes); | ||
276 | assert( | ||
277 | desc->weights.floats.size_bytes <= | ||
278 | geometry->desc.weights.floats.size_bytes); | ||
279 | |||
280 | if (desc->positions3d.data) { | ||
281 | // The geometry must already have an underlying GPU buffer. | ||
282 | assert(geometry->desc.positions3d.buffer); | ||
283 | gfx_update_buffer( | ||
284 | geometry->desc.positions3d.buffer, | ||
285 | &(BufferDataDesc){ | ||
286 | .vec3s = desc->positions3d.data, | ||
287 | .count = desc->positions3d.size_bytes / sizeof(vec3)}); | ||
288 | } | ||
289 | // TODO: more | ||
290 | else { | ||
291 | FAIL("TODO: gfx_update_geometry() - handle other buffer types"); | ||
292 | } | ||
293 | |||
294 | if (desc->num_verts != 0) { | ||
295 | geometry->num_verts = desc->num_verts; | ||
296 | } | ||
297 | } | ||
298 | |||
299 | void gfx_render_geometry(const Geometry* geometry) { | ||
300 | assert(geometry); | ||
301 | assert(geometry->vao); | ||
302 | |||
303 | const GeometryDesc* desc = &geometry->desc; | ||
304 | glBindVertexArray(geometry->vao); | ||
305 | |||
306 | if (desc->indices8.buffer) { | ||
307 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices8.buffer->vbo); | ||
308 | glDrawElements( | ||
309 | geometry->mode, desc->num_indices, GL_UNSIGNED_BYTE, | ||
310 | (const void*)desc->indices8.offset_bytes); | ||
311 | } else if (desc->indices16.buffer) { | ||
312 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices16.buffer->vbo); | ||
313 | glDrawElements( | ||
314 | geometry->mode, desc->num_indices, GL_UNSIGNED_SHORT, | ||
315 | (const void*)desc->indices16.offset_bytes); | ||
316 | } else { | ||
317 | glDrawArrays(geometry->mode, 0, geometry->num_verts); | ||
318 | } | ||
319 | |||
320 | glBindVertexArray(0); | ||
321 | } | ||
322 | |||
323 | aabb3 gfx_get_geometry_aabb(const Geometry* geometry) { | ||
324 | assert(geometry); | ||
325 | return geometry->desc.aabb; | ||
326 | } | ||