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