304 lines
8.6 KiB
C
304 lines
8.6 KiB
C
#include "colla/colla.h"
|
|
#include "libs/sokol_gfx.h"
|
|
#include "libs/stb_image.h"
|
|
|
|
#define CGLTF_IMPLEMENTATION
|
|
#include "libs/cgltf.h"
|
|
|
|
#include "libs/handmademath.h"
|
|
|
|
typedef struct vertex_t vertex_t;
|
|
struct vertex_t {
|
|
vec3 pos;
|
|
vec3 norm;
|
|
vec2 tex;
|
|
vec2 lightmap;
|
|
};
|
|
|
|
typedef struct obj_t obj_t;
|
|
struct obj_t {
|
|
sg_bindings bindings;
|
|
u16 draw_count;
|
|
};
|
|
|
|
cgltf_result obj__cgltf_file_read_cb(
|
|
const cgltf_memory_options *m,
|
|
const cgltf_file_options *opt,
|
|
const char *path,
|
|
cgltf_size *size,
|
|
void **data
|
|
) {
|
|
debug("(cgltf) loading %s", path);
|
|
|
|
arena_t *arena = opt->user_data;
|
|
buffer_t buf = os_file_read_all(arena, strv(path));
|
|
if (!buf.data) {
|
|
return cgltf_result_io_error;
|
|
}
|
|
|
|
*data = buf.data;
|
|
*size = buf.len;
|
|
|
|
return cgltf_result_success;
|
|
}
|
|
|
|
void obj__cgltf_file_release_callback(
|
|
const cgltf_memory_options *m,
|
|
const cgltf_file_options *opt,
|
|
void *data,
|
|
cgltf_size size
|
|
) {
|
|
// no-op
|
|
}
|
|
|
|
#define MAX_SCENE_OBJECTS 64
|
|
|
|
typedef struct scene_t scene_t;
|
|
struct scene_t {
|
|
obj_t objects[MAX_SCENE_OBJECTS];
|
|
int object_count;
|
|
};
|
|
|
|
#define CACHE_SIZE (64)
|
|
#define MAX_CACHE_NAME_LEN (64)
|
|
|
|
typedef struct cache_item_t cache_item_t;
|
|
struct cache_item_t {
|
|
char name[MAX_CACHE_NAME_LEN];
|
|
sg_image image;
|
|
};
|
|
cache_item_t tex_cache[CACHE_SIZE] = {0};
|
|
|
|
int tex_cache_size = 0;
|
|
|
|
sg_image obj__load_texture(arena_t scratch, cgltf_texture *texture) {
|
|
if (!texture || !texture->image) {
|
|
return (sg_image){0};
|
|
}
|
|
|
|
cgltf_image *image = texture->image;
|
|
const char *name = image->name;
|
|
|
|
for (int i = 0; i < tex_cache_size; ++i) {
|
|
if (strcmp(tex_cache[i].name, name) == 0) {
|
|
return tex_cache[i].image;
|
|
}
|
|
}
|
|
|
|
u8 *bytes = NULL;
|
|
usize size = 0;
|
|
if (image->uri) {
|
|
buffer_t buf = os_file_read_all(&scratch, strv(image->uri));
|
|
bytes = buf.data;
|
|
size = buf.len;
|
|
}
|
|
else if (image->buffer_view) {
|
|
cgltf_buffer_view *view = image->buffer_view;
|
|
bytes = (u8*)view->buffer->data + view->offset;
|
|
size = view->size;
|
|
}
|
|
else {
|
|
fatal("idk how do load this dog (%s)", texture->name);
|
|
}
|
|
|
|
int width, height, comp;
|
|
u8 *pixels = stbi_load_from_memory(bytes, (int)size, &width, &height, &comp, 4);
|
|
|
|
if (!pixels) {
|
|
fatal("(stbi) couldn't load image %s: %s", image->name, stbi_failure_reason());
|
|
}
|
|
|
|
sg_image out = sg_make_image(&(sg_image_desc){
|
|
.pixel_format = SG_PIXELFORMAT_RGBA8,
|
|
.width = width,
|
|
.height = height,
|
|
.data.mip_levels[0] = {
|
|
.ptr = pixels,
|
|
.size = width * height * 4,
|
|
},
|
|
});
|
|
|
|
stbi_image_free(pixels);
|
|
|
|
cache_item_t *item = &tex_cache[tex_cache_size++];
|
|
colla_assert(tex_cache_size < CACHE_SIZE, "texture cache full");
|
|
|
|
item->image = out;
|
|
usize len = strlen(name);
|
|
memmove(item->name, name, MIN(len, MAX_CACHE_NAME_LEN));
|
|
|
|
return out;
|
|
}
|
|
|
|
scene_t obj_load_gltf(arena_t scratch, strview_t filename) {
|
|
scene_t scene = {0};
|
|
|
|
str_t fname = str(&scratch, filename);
|
|
buffer_t buf = os_file_read_all(&scratch, filename);
|
|
|
|
cgltf_options options = {
|
|
.file = {
|
|
.read = obj__cgltf_file_read_cb,
|
|
.release = obj__cgltf_file_release_callback,
|
|
.user_data = &scratch,
|
|
},
|
|
};
|
|
|
|
cgltf_data *data = NULL;
|
|
cgltf_result result = cgltf_parse(
|
|
&options,
|
|
buf.data,
|
|
buf.len,
|
|
&data
|
|
);
|
|
|
|
if (result != cgltf_result_success) {
|
|
fatal("(cgltf) failed to load %v", filename);
|
|
return (scene_t){0};
|
|
}
|
|
|
|
result = cgltf_load_buffers(&options, data, fname.buf);
|
|
if (result != cgltf_result_success) {
|
|
fatal("(cgltf) failed to load %v's buffers", filename);
|
|
return (scene_t){0};
|
|
}
|
|
|
|
colla_assert(data->meshes_count == 1);
|
|
|
|
sg_sampler tex_sampler = sg_make_sampler(&(sg_sampler_desc){
|
|
.min_filter = SG_FILTER_NEAREST,
|
|
.mag_filter = SG_FILTER_NEAREST,
|
|
});
|
|
|
|
sg_sampler lightmap_sampler = sg_make_sampler(&(sg_sampler_desc){
|
|
.min_filter = SG_FILTER_LINEAR,
|
|
.mag_filter = SG_FILTER_LINEAR,
|
|
});
|
|
|
|
u32 missing_texture[] = {
|
|
0xFFFF00FF, 0x00000000,
|
|
0x00000000, 0xFFFF00FF,
|
|
};
|
|
sg_image missing_img = sg_make_image(&(sg_image_desc){
|
|
.width = 2,
|
|
.height = 2,
|
|
.pixel_format = SG_PIXELFORMAT_RGBA8,
|
|
.data.mip_levels[0] = SG_RANGE(missing_texture),
|
|
});
|
|
|
|
for (usize i = 0; i < data->meshes_count; ++i) {
|
|
cgltf_mesh *mesh = &data->meshes[i];
|
|
|
|
colla_assert(MAX_SCENE_OBJECTS > mesh->primitives_count);
|
|
|
|
scene.object_count = mesh->primitives_count;
|
|
|
|
for (usize p = 0; p < mesh->primitives_count; ++p) {
|
|
cgltf_primitive *prim = &mesh->primitives[p];
|
|
obj_t *obj = &scene.objects[p];
|
|
|
|
// HACK: HELL!
|
|
usize vert_count = prim->attributes[0].data->count;
|
|
vertex_t *verts = alloc(&scratch, vertex_t, vert_count);
|
|
|
|
cgltf_accessor *in = prim->indices;
|
|
u16 *indices = alloc(&scratch, u16, in->count);
|
|
for (usize k = 0; k < in->count; ++k) {
|
|
indices[k] = (u16)cgltf_accessor_read_index(in, k);
|
|
}
|
|
|
|
cgltf_material *mat = prim->material;
|
|
|
|
// diffuse texture
|
|
cgltf_texture_view texture_view = mat->pbr_metallic_roughness.base_color_texture;
|
|
sg_image image = obj__load_texture(scratch, texture_view.texture);
|
|
if (image.id == 0) {
|
|
image = missing_img;
|
|
}
|
|
sg_view view = sg_make_view(&(sg_view_desc){
|
|
.texture.image = image,
|
|
});
|
|
|
|
obj->bindings.views[0] = view;
|
|
obj->bindings.samplers[0] = tex_sampler;
|
|
|
|
// lightmap
|
|
sg_image lightmap = missing_img;
|
|
if (mat->emissive_texture.texture) {
|
|
lightmap = obj__load_texture(scratch, mat->emissive_texture.texture);
|
|
}
|
|
sg_view lm_view = sg_make_view(&(sg_view_desc){
|
|
.texture.image = lightmap,
|
|
});
|
|
obj->bindings.views[1] = lm_view;
|
|
obj->bindings.samplers[1] = lightmap_sampler;
|
|
|
|
for (usize a = 0; a < prim->attributes_count; ++a) {
|
|
cgltf_attribute *attr = &prim->attributes[a];
|
|
|
|
cgltf_accessor *acc = attr->data;
|
|
|
|
switch (attr->type) {
|
|
case cgltf_attribute_type_position:
|
|
{
|
|
for (usize k = 0; k < acc->count; ++k) {
|
|
cgltf_accessor_read_float(acc, k, verts[k].pos.elements, 3);
|
|
}
|
|
break;
|
|
}
|
|
case cgltf_attribute_type_normal:
|
|
for (usize k = 0; k < acc->count; ++k) {
|
|
cgltf_accessor_read_float(acc, k, verts[k].norm.elements, 3);
|
|
}
|
|
break;
|
|
case cgltf_attribute_type_texcoord:
|
|
{
|
|
if (attr->index == 0) {
|
|
for (usize k = 0; k < acc->count; ++k) {
|
|
cgltf_accessor_read_float(acc, k, verts[k].tex.elements, 2);
|
|
}
|
|
}
|
|
else {
|
|
for (usize k = 0; k < acc->count; ++k) {
|
|
cgltf_accessor_read_float(acc, k, verts[k].lightmap.elements, 2);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
sg_buffer vertex_buffer = sg_make_buffer(&(sg_buffer_desc){
|
|
.data = {
|
|
.ptr = verts,
|
|
.size = sizeof(vertex_t) * vert_count,
|
|
},
|
|
.usage = {
|
|
.vertex_buffer = true,
|
|
.immutable = true,
|
|
},
|
|
});
|
|
|
|
sg_buffer index_buffer = sg_make_buffer(&(sg_buffer_desc){
|
|
.data = {
|
|
.ptr = indices,
|
|
.size = sizeof(u16) * in->count,
|
|
},
|
|
.usage = {
|
|
.index_buffer = true,
|
|
.immutable = true,
|
|
},
|
|
});
|
|
|
|
obj->bindings.index_buffer = index_buffer;
|
|
obj->bindings.vertex_buffers[0] = vertex_buffer;
|
|
obj->draw_count = in->count;
|
|
}
|
|
}
|
|
|
|
cgltf_free(data);
|
|
|
|
return scene;
|
|
}
|