2021-11-14 22:53:50 +01:00
|
|
|
#include "vk_materials.h"
|
|
|
|
#include "vk_textures.h"
|
2021-11-15 00:44:07 +01:00
|
|
|
#include "vk_mapents.h"
|
2021-11-14 22:53:50 +01:00
|
|
|
#include "vk_const.h"
|
|
|
|
|
2021-11-15 00:50:33 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2021-12-24 07:12:36 +01:00
|
|
|
#define MAX_INCLUDE_DEPTH 4
|
|
|
|
|
2021-12-01 19:32:32 +01:00
|
|
|
static const xvk_material_t k_default_material = {
|
|
|
|
.tex_base_color = -1,
|
|
|
|
.tex_metalness = 0,
|
|
|
|
.tex_roughness = 0,
|
|
|
|
.tex_normalmap = 0,
|
|
|
|
|
|
|
|
.metalness = 0.f,
|
|
|
|
.roughness = 1.f,
|
|
|
|
.base_color = { 1.f, 1.f, 1.f },
|
|
|
|
|
|
|
|
.set = false,
|
|
|
|
};
|
|
|
|
|
2021-11-14 22:53:50 +01:00
|
|
|
static struct {
|
|
|
|
xvk_material_t materials[MAX_TEXTURES];
|
|
|
|
} g_materials;
|
|
|
|
|
2021-11-25 23:38:03 +01:00
|
|
|
static int loadTexture( const char *filename, qboolean force_reload ) {
|
|
|
|
const int tex_id = force_reload ? XVK_LoadTextureReplace( filename, NULL, 0, 0 ) : VK_LoadTexture( filename, NULL, 0, 0 );
|
2021-11-25 22:51:39 +01:00
|
|
|
gEngine.Con_Reportf("Loading texture %s => %d\n", filename, tex_id);
|
2021-11-15 00:44:07 +01:00
|
|
|
return tex_id ? tex_id : -1;
|
|
|
|
}
|
|
|
|
|
2021-12-24 07:12:36 +01:00
|
|
|
static void makePath(char *out, size_t out_size, const char *value, const char *path_begin, const char *path_end) {
|
|
|
|
if (value[0] == '/') {
|
|
|
|
// Path relative to valve/pbr dir
|
|
|
|
Q_snprintf(out, out_size, "pbr%s", value);
|
|
|
|
} else {
|
|
|
|
// Path relative to current material.mat file
|
|
|
|
Q_snprintf(out, out_size, "%.*s%s", (int)(path_end - path_begin), path_begin, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAKE_PATH(out, value) \
|
|
|
|
makePath(out, sizeof(out), value, path_begin, path_end)
|
|
|
|
|
|
|
|
static void loadMaterialsFromFile( const char *filename, int depth ) {
|
2021-11-15 00:44:07 +01:00
|
|
|
fs_offset_t size;
|
|
|
|
const char *const path_begin = filename;
|
|
|
|
const char *path_end = Q_strrchr(filename, '/');
|
2022-08-08 09:23:38 +02:00
|
|
|
byte *data = gEngine.fsapi->LoadFile( filename, 0, false );
|
2021-12-24 07:12:36 +01:00
|
|
|
char *pos = (char*)data;
|
2021-11-15 00:44:07 +01:00
|
|
|
xvk_material_t current_material = {
|
|
|
|
.base_color = -1,
|
|
|
|
.metalness = tglob.blackTexture,
|
|
|
|
.roughness = tglob.whiteTexture,
|
2021-12-01 19:32:32 +01:00
|
|
|
.tex_normalmap = 0,
|
2021-11-15 00:44:07 +01:00
|
|
|
};
|
|
|
|
int current_material_index = -1;
|
2021-11-25 23:38:03 +01:00
|
|
|
qboolean force_reload = false;
|
2021-12-22 21:02:27 +01:00
|
|
|
qboolean create = false;
|
2021-11-15 00:44:07 +01:00
|
|
|
|
2021-11-25 22:51:39 +01:00
|
|
|
gEngine.Con_Reportf("Loading materials from %s\n", filename);
|
|
|
|
|
2021-11-15 00:44:07 +01:00
|
|
|
if ( !data )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( !path_end )
|
|
|
|
path_end = path_begin;
|
|
|
|
else
|
|
|
|
path_end++;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
char key[1024];
|
|
|
|
char value[1024];
|
|
|
|
|
|
|
|
pos = COM_ParseFile(pos, key, sizeof(key));
|
|
|
|
ASSERT(Q_strlen(key) < sizeof(key));
|
|
|
|
if (!pos)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (key[0] == '{') {
|
2021-12-01 19:32:32 +01:00
|
|
|
current_material = k_default_material;
|
2021-12-22 08:30:48 +01:00
|
|
|
current_material_index = -1;
|
2021-11-25 23:38:03 +01:00
|
|
|
force_reload = false;
|
2021-12-22 21:02:27 +01:00
|
|
|
create = false;
|
2021-11-15 00:44:07 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key[0] == '}') {
|
2021-12-22 21:02:27 +01:00
|
|
|
if (current_material_index < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If there's no explicit basecolor_map value, use the "for" target texture
|
|
|
|
if (current_material.tex_base_color == -1)
|
|
|
|
current_material.tex_base_color = current_material_index;
|
2021-12-22 08:30:48 +01:00
|
|
|
|
|
|
|
gEngine.Con_Reportf("Creating%s material for texture %s(%d)\n", create?" new":"",
|
|
|
|
findTexture(current_material_index)->name, current_material_index);
|
|
|
|
|
|
|
|
g_materials.materials[current_material_index] = current_material;
|
|
|
|
g_materials.materials[current_material_index].set = true;
|
2021-11-15 00:44:07 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = COM_ParseFile(pos, value, sizeof(value));
|
|
|
|
if (!pos)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (Q_stricmp(key, "for") == 0) {
|
2021-12-22 20:02:34 +01:00
|
|
|
current_material_index = XVK_FindTextureNamedLike(value);
|
2021-12-22 21:02:27 +01:00
|
|
|
create = false;
|
|
|
|
} else if (Q_stricmp(key, "new") == 0) {
|
|
|
|
current_material_index = XVK_CreateDummyTexture(value);
|
|
|
|
create = true;
|
2021-11-25 23:38:03 +01:00
|
|
|
} else if (Q_stricmp(key, "force_reload") == 0) {
|
|
|
|
force_reload = Q_atoi(value) != 0;
|
2021-12-24 07:12:36 +01:00
|
|
|
} else if (Q_stricmp(key, "include") == 0) {
|
|
|
|
if (depth > 0) {
|
|
|
|
char include_path[256];
|
|
|
|
MAKE_PATH(include_path, value);
|
|
|
|
loadMaterialsFromFile( include_path, depth - 1);
|
|
|
|
} else {
|
|
|
|
gEngine.Con_Printf(S_ERROR "material: max include depth %d reached when including '%s' from '%s'\n", MAX_INCLUDE_DEPTH, value, filename);
|
|
|
|
}
|
2021-11-25 22:51:39 +01:00
|
|
|
} else {
|
|
|
|
char texture_path[256];
|
2021-12-01 19:32:32 +01:00
|
|
|
int *tex_id_dest = NULL;
|
2021-11-25 22:51:39 +01:00
|
|
|
if (Q_stricmp(key, "basecolor_map") == 0) {
|
2021-12-01 19:32:32 +01:00
|
|
|
tex_id_dest = ¤t_material.tex_base_color;
|
2021-11-25 22:51:39 +01:00
|
|
|
} else if (Q_stricmp(key, "normal_map") == 0) {
|
2021-12-01 19:32:32 +01:00
|
|
|
tex_id_dest = ¤t_material.tex_normalmap;
|
2021-11-25 22:51:39 +01:00
|
|
|
} else if (Q_stricmp(key, "metal_map") == 0) {
|
2021-12-01 19:32:32 +01:00
|
|
|
tex_id_dest = ¤t_material.tex_metalness;
|
2021-11-25 22:51:39 +01:00
|
|
|
} else if (Q_stricmp(key, "roughness_map") == 0) {
|
2021-12-01 19:32:32 +01:00
|
|
|
tex_id_dest = ¤t_material.tex_roughness;
|
|
|
|
} else if (Q_stricmp(key, "roughness") == 0) {
|
|
|
|
sscanf(value, "%f", ¤t_material.roughness);
|
|
|
|
} else if (Q_stricmp(key, "metalness") == 0) {
|
|
|
|
sscanf(value, "%f", ¤t_material.metalness);
|
|
|
|
} else if (Q_stricmp(key, "base_color") == 0) {
|
|
|
|
sscanf(value, "%f %f %f", ¤t_material.base_color[0], ¤t_material.base_color[1], ¤t_material.base_color[2]);
|
2021-11-25 22:51:39 +01:00
|
|
|
} else {
|
|
|
|
gEngine.Con_Printf(S_ERROR "Unknown material key %s\n", key);
|
|
|
|
continue;
|
2021-11-15 00:44:07 +01:00
|
|
|
}
|
2021-11-25 22:51:39 +01:00
|
|
|
|
2021-12-24 07:12:36 +01:00
|
|
|
MAKE_PATH(texture_path, value);
|
2021-11-25 22:51:39 +01:00
|
|
|
|
2021-12-01 19:32:32 +01:00
|
|
|
if (tex_id_dest) {
|
|
|
|
const int tex_id = loadTexture(texture_path, force_reload);
|
|
|
|
if (tex_id < 0) {
|
|
|
|
gEngine.Con_Printf(S_ERROR "Failed to load texture \"%s\" for key \"%s\"\n", value, key);
|
|
|
|
continue;
|
|
|
|
}
|
2021-11-25 22:51:39 +01:00
|
|
|
|
2021-12-01 19:32:32 +01:00
|
|
|
*tex_id_dest = tex_id;
|
|
|
|
}
|
2021-11-15 00:44:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Mem_Free( data );
|
|
|
|
}
|
|
|
|
|
2021-11-25 22:51:39 +01:00
|
|
|
static void loadMaterialsFromFileF( const char *fmt, ... ) {
|
|
|
|
char buffer[256];
|
|
|
|
va_list argptr;
|
|
|
|
|
|
|
|
va_start( argptr, fmt );
|
|
|
|
vsnprintf( buffer, sizeof buffer, fmt, argptr );
|
|
|
|
va_end( argptr );
|
|
|
|
|
2021-12-24 07:12:36 +01:00
|
|
|
loadMaterialsFromFile( buffer, MAX_INCLUDE_DEPTH );
|
2021-11-25 22:51:39 +01:00
|
|
|
}
|
|
|
|
|
2021-11-14 22:53:50 +01:00
|
|
|
void XVK_ReloadMaterials( void ) {
|
|
|
|
for (int i = 0; i < MAX_TEXTURES; ++i) {
|
|
|
|
xvk_material_t *const mat = g_materials.materials + i;
|
|
|
|
const vk_texture_t *const tex = findTexture( i );
|
2021-12-01 19:32:32 +01:00
|
|
|
*mat = k_default_material;
|
2021-11-14 22:53:50 +01:00
|
|
|
|
2021-12-01 19:32:32 +01:00
|
|
|
if (tex)
|
|
|
|
mat->tex_base_color = i;
|
2021-11-14 22:53:50 +01:00
|
|
|
}
|
2021-11-15 00:44:07 +01:00
|
|
|
|
2021-12-24 07:12:36 +01:00
|
|
|
loadMaterialsFromFile( "pbr/materials.mat", MAX_INCLUDE_DEPTH );
|
|
|
|
loadMaterialsFromFile( "pbr/models/materials.mat", MAX_INCLUDE_DEPTH );
|
|
|
|
loadMaterialsFromFile( "pbr/sprites/materials.mat", MAX_INCLUDE_DEPTH );
|
2021-11-25 22:51:39 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
const char *wad = g_map_entities.wadlist;
|
|
|
|
for (; *wad;) {
|
|
|
|
const char *const wad_end = Q_strchr(wad, ';');
|
|
|
|
loadMaterialsFromFileF("pbr/%.*s/materials.mat", wad_end - wad, wad);
|
|
|
|
wad = wad_end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const model_t *map = gEngine.pfnGetModelByIndex( 1 );
|
|
|
|
loadMaterialsFromFileF("pbr/%s/materials.mat", map->name);
|
|
|
|
}
|
2021-11-14 22:53:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
xvk_material_t* XVK_GetMaterialForTextureIndex( int tex_index ) {
|
|
|
|
xvk_material_t *mat = NULL;
|
|
|
|
ASSERT(tex_index >= 0);
|
|
|
|
ASSERT(tex_index < MAX_TEXTURES);
|
|
|
|
|
|
|
|
mat = g_materials.materials + tex_index;
|
|
|
|
if (mat->base_color >= 0)
|
|
|
|
return mat;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|