taisei/src/resource/material.c
2024-05-17 14:11:48 +02:00

152 lines
3.8 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "material.h"
#include "util.h"
#include "util/io.h"
#include "util/kvparser.h"
static char *material_path(const char *basename);
static bool material_check_path(const char *path);
static void material_load_stage1(ResourceLoadState *st);
static bool material_transfer(void *dst, void *src);
ResourceHandler material_res_handler = {
.type = RES_MATERIAL,
.typename = "material",
.subdir = MATERIAL_PATH_PREFIX,
.procs = {
.find = material_path,
.check = material_check_path,
.load = material_load_stage1,
.transfer = material_transfer,
.unload = mem_free,
},
};
static char *material_path(const char *basename) {
return try_path(MATERIAL_PATH_PREFIX, basename, MATERIAL_EXTENSION);
}
static bool material_check_path(const char *path) {
return strendswith(path, MATERIAL_EXTENSION);
}
struct mat_load_data {
PBRMaterial *mat;
union {
struct {
char *diffuse_map;
char *normal_map;
char *ambient_map;
char *roughness_map;
char *depth_map;
char *ao_map;
};
char *maps[6];
};
};
static void free_mat_load_data(struct mat_load_data *ld) {
for(int i = 0; i < ARRAY_SIZE(ld->maps); ++i) {
mem_free(ld->maps[i]);
}
mem_free(ld);
}
static void material_load_stage2(ResourceLoadState *st);
static void material_load_stage1(ResourceLoadState *st) {
SDL_RWops *rw = res_open_file(st, st->path, VFS_MODE_READ);
if(UNLIKELY(!rw)) {
log_error("VFS error: %s", vfs_get_error());
res_load_failed(st);
}
auto ld = ALLOC(struct mat_load_data);
ld->mat = ALLOC(typeof(*ld->mat), {
.diffuse_color = { 1, 1, 1 },
.ambient_color = { 1, 1, 1 },
.roughness_value = 1,
.metallic_value = 0,
.depth_scale = 0,
});
bool ok = parse_keyvalue_stream_with_spec(rw, (KVSpec[]) {
{ "diffuse_map", .out_str = &ld->diffuse_map },
{ "normal_map", .out_str = &ld->normal_map },
{ "ambient_map", .out_str = &ld->ambient_map },
{ "roughness_map", .out_str = &ld->roughness_map },
{ "depth_map", .out_str = &ld->depth_map },
{ "ao_map", .out_str = &ld->ao_map },
{ "diffuse_color", .callback = kvparser_vec3, .callback_data = ld->mat->diffuse_color },
{ "ambient_color", .callback = kvparser_vec3, .callback_data = ld->mat->ambient_color },
{ "roughness", .out_float = &ld->mat->roughness_value },
{ "metallic", .out_float = &ld->mat->metallic_value },
{ "depth_scale", .out_float = &ld->mat->depth_scale },
{ NULL },
});
SDL_RWclose(rw);
if(!ok) {
free_mat_load_data(ld);
log_error("Failed to parse material file '%s'", st->path);
res_load_failed(st);
return;
}
for(int i = 0; i < ARRAY_SIZE(ld->maps); ++i) {
if(ld->maps[i]) {
res_load_dependency(st, RES_TEXTURE, ld->maps[i]);
}
}
res_load_continue_after_dependencies(st, material_load_stage2, ld);
}
#define LOADMAP(_map_) do { \
if(ld->_map_##_map) { \
ld->mat->_map_##_map = res_get_data( \
RES_TEXTURE, ld->_map_##_map, st->flags & ~RESF_RELOAD); \
if(UNLIKELY(ld->mat->_map_##_map == NULL)) { \
log_error("%s: failed to load " #_map_ " map '%s'", st->name, ld->_map_##_map); \
free_mat_load_data(ld); \
res_load_failed(st); \
return; \
} \
} \
} while(0)
static void material_load_stage2(ResourceLoadState *st) {
struct mat_load_data *ld = st->opaque;
LOADMAP(diffuse);
LOADMAP(normal);
LOADMAP(ambient);
LOADMAP(roughness);
LOADMAP(depth);
LOADMAP(ao);
res_load_finished(st, ld->mat);
free_mat_load_data(ld);
}
static bool material_transfer(void *dst, void *src) {
PBRMaterial *mdst = dst;
PBRMaterial *msrc = src;
*mdst = *msrc;
mem_free(msrc);
return true;
}