2021-11-14 22:53:50 +01:00
|
|
|
#include "vk_materials.h"
|
2023-10-24 17:32:42 +02:00
|
|
|
#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"
|
2022-08-13 22:15:17 +02:00
|
|
|
#include "profiler.h"
|
2023-08-29 18:31:57 +02:00
|
|
|
#include "vk_logs.h"
|
2023-10-30 17:43:26 +01:00
|
|
|
#include "unordered_roadmap.h"
|
2021-11-14 22:53:50 +01:00
|
|
|
|
2021-11-15 00:50:33 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2023-12-04 17:25:35 +01:00
|
|
|
#define LOG_MODULE mat
|
2023-08-29 18:31:57 +02:00
|
|
|
|
2021-12-24 07:12:36 +01:00
|
|
|
#define MAX_INCLUDE_DEPTH 4
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
#define MAX_MATERIALS 2048
|
2023-10-30 17:43:26 +01:00
|
|
|
#define MAX_NEW_MATERIALS 128
|
2023-10-03 18:48:26 +02:00
|
|
|
|
2023-09-19 19:05:12 +02:00
|
|
|
static r_vk_material_t k_default_material = {
|
2023-10-10 19:27:10 +02:00
|
|
|
.tex_base_color = -1,
|
|
|
|
.tex_metalness = 0,
|
|
|
|
.tex_roughness = 0,
|
2023-10-26 17:38:37 +02:00
|
|
|
.tex_normalmap = 0, // 0 means no normal map, checked in shaders
|
2021-12-01 19:32:32 +01:00
|
|
|
|
2023-10-10 19:27:10 +02:00
|
|
|
.metalness = 0.f,
|
|
|
|
.roughness = 1.f,
|
|
|
|
.normal_scale = 1.f,
|
|
|
|
.base_color = { 1.f, 1.f, 1.f, 1.f },
|
2021-12-01 19:32:32 +01:00
|
|
|
};
|
|
|
|
|
2023-10-10 19:27:10 +02:00
|
|
|
/* TODO
|
|
|
|
enum {
|
|
|
|
#define X(bit, type, name, key, func) kMatField_##key = (1 << (bit)),
|
|
|
|
MATERIAL_FIELDS_LIST(X)
|
|
|
|
#undef X
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
2023-09-29 19:58:02 +02:00
|
|
|
#define MAX_RENDERMODE_MATERIALS 32
|
|
|
|
typedef struct {
|
|
|
|
struct {
|
|
|
|
int tex_id;
|
2023-10-03 18:48:26 +02:00
|
|
|
r_vk_material_ref_t mat;
|
|
|
|
} map[MAX_RENDERMODE_MATERIALS];
|
2023-09-29 19:58:02 +02:00
|
|
|
int count;
|
|
|
|
} r_vk_material_per_mode_t;
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
enum {
|
|
|
|
kMaterialNotChecked = 0,
|
|
|
|
kMaterialNoReplacement = -1,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int mat_id;
|
|
|
|
|
|
|
|
// TODO rendermode chain
|
|
|
|
} texture_to_material_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
//int for_tex_id;
|
|
|
|
string name;
|
|
|
|
|
|
|
|
r_vk_material_t material;
|
|
|
|
} material_entry_t;
|
|
|
|
|
2023-10-30 17:43:26 +01:00
|
|
|
typedef struct {
|
|
|
|
urmom_header_t hdr_;
|
|
|
|
int mat_id; // into g_materials.table
|
|
|
|
} material_name_map_t;
|
|
|
|
|
2021-11-14 22:53:50 +01:00
|
|
|
static struct {
|
2023-10-03 18:48:26 +02:00
|
|
|
int count;
|
|
|
|
material_entry_t table[MAX_MATERIALS];
|
2023-09-29 19:58:02 +02:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
texture_to_material_t tex_to_mat[MAX_TEXTURES];
|
|
|
|
|
|
|
|
// TODO embed into tex_to_mat
|
|
|
|
r_vk_material_per_mode_t for_rendermode[kRenderTransAdd+1];
|
|
|
|
|
2023-10-30 17:43:26 +01:00
|
|
|
urmom_desc_t map_desc;
|
|
|
|
material_name_map_t map[MAX_NEW_MATERIALS];
|
2021-11-14 22:53:50 +01:00
|
|
|
} g_materials;
|
|
|
|
|
2023-05-04 18:35:29 +02:00
|
|
|
static struct {
|
|
|
|
int mat_files_read;
|
|
|
|
int texture_lookups;
|
|
|
|
int texture_loads;
|
|
|
|
uint64_t material_file_read_duration_ns;
|
|
|
|
uint64_t texture_lookup_duration_ns;
|
|
|
|
uint64_t texture_load_duration_ns;
|
|
|
|
} g_stats;
|
|
|
|
|
2023-10-06 21:06:44 +02:00
|
|
|
static int loadTexture( const char *filename, qboolean force_reload, colorspace_hint_e colorspace ) {
|
2023-05-04 18:35:29 +02:00
|
|
|
const uint64_t load_begin_ns = aprof_time_now_ns();
|
2023-10-27 15:33:01 +02:00
|
|
|
const int tex_id = R_TextureUploadFromFileExAcquire( filename, colorspace, force_reload );
|
2023-08-29 18:31:57 +02:00
|
|
|
DEBUG("Loaded texture %s => %d", filename, tex_id);
|
2023-05-04 18:35:29 +02:00
|
|
|
g_stats.texture_loads++;
|
|
|
|
g_stats.texture_load_duration_ns += aprof_time_now_ns() - load_begin_ns;
|
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)
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
static void printMaterial(int index) {
|
|
|
|
const char* const name = g_materials.table[index].name;
|
|
|
|
const r_vk_material_t* const mat = &g_materials.table[index].material;
|
|
|
|
|
|
|
|
DEBUG("material[%d] \"%s\" (tbc=%d, tr=%d, tm=%d, tn=%d bc=(%.03f,%.03f,%.03f,%.03f) r=%.03f m=%.03f ns=%.03f",
|
|
|
|
index, name,
|
|
|
|
mat->tex_base_color, mat->tex_roughness, mat->tex_metalness, mat->tex_normalmap,
|
|
|
|
mat->base_color[0], mat->base_color[1], mat->base_color[2], mat->base_color[3],
|
|
|
|
mat->roughness, mat->metalness, mat->normal_scale
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-10-20 16:55:19 +02:00
|
|
|
static void acquireTexturesForMaterial( int index ) {
|
|
|
|
const r_vk_material_t *mat = &g_materials.table[index].material;
|
|
|
|
DEBUG("%s(%d: %s)", __FUNCTION__, index, g_materials.table[index].name);
|
2023-11-06 16:31:55 +01:00
|
|
|
if (mat->tex_base_color > 0)
|
|
|
|
R_TextureAcquire(mat->tex_base_color);
|
2023-10-20 16:55:19 +02:00
|
|
|
R_TextureAcquire(mat->tex_metalness);
|
|
|
|
R_TextureAcquire(mat->tex_roughness);
|
2023-10-30 17:43:26 +01:00
|
|
|
if (mat->tex_normalmap > 0)
|
|
|
|
R_TextureAcquire(mat->tex_normalmap);
|
2023-10-20 16:55:19 +02:00
|
|
|
}
|
|
|
|
|
2023-10-26 18:00:34 +02:00
|
|
|
static void releaseTexturesForMaterialPtr( const r_vk_material_t *mat ) {
|
2023-11-06 16:31:55 +01:00
|
|
|
if (mat->tex_base_color > 0)
|
|
|
|
R_TextureRelease(mat->tex_base_color);
|
2023-10-20 16:55:19 +02:00
|
|
|
R_TextureRelease(mat->tex_metalness);
|
|
|
|
R_TextureRelease(mat->tex_roughness);
|
2023-10-30 17:43:26 +01:00
|
|
|
if (mat->tex_normalmap > 0)
|
|
|
|
R_TextureRelease(mat->tex_normalmap);
|
2023-10-20 16:55:19 +02:00
|
|
|
}
|
|
|
|
|
2023-10-26 18:00:34 +02:00
|
|
|
static void releaseTexturesForMaterial( int index ) {
|
|
|
|
const r_vk_material_t *mat = &g_materials.table[index].material;
|
|
|
|
DEBUG("%s(%d: %s)", __FUNCTION__, index, g_materials.table[index].name);
|
|
|
|
releaseTexturesForMaterialPtr( mat );
|
|
|
|
}
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
static int addMaterial(const char *name, const r_vk_material_t* mat) {
|
|
|
|
if (g_materials.count == MAX_MATERIALS) {
|
|
|
|
ERR("Max count of materials %d reached", MAX_MATERIALS);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-05-04 18:35:29 +02:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
Q_strncpy(g_materials.table[g_materials.count].name, name, sizeof g_materials.table[g_materials.count].name);
|
|
|
|
g_materials.table[g_materials.count].material = *mat;
|
2023-10-20 16:55:19 +02:00
|
|
|
acquireTexturesForMaterial(g_materials.count);
|
2023-10-03 18:48:26 +02:00
|
|
|
|
|
|
|
printMaterial(g_materials.count);
|
|
|
|
|
2023-11-07 19:30:46 +01:00
|
|
|
ASSERT(mat->tex_base_color >= 0);
|
|
|
|
ASSERT(mat->tex_metalness >= 0);
|
|
|
|
ASSERT(mat->tex_roughness >= 0);
|
|
|
|
ASSERT(mat->tex_normalmap >= 0);
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
return g_materials.count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void assignMaterialForTexture(const char *name, int for_tex_id, int mat_id) {
|
2023-10-19 17:15:45 +02:00
|
|
|
const char* const tex_name = R_TextureGetNameByIndex(for_tex_id);
|
2023-10-03 18:48:26 +02:00
|
|
|
DEBUG("Assigning material \"%s\" for_tex_id=\"%s\"(%d)", name, tex_name, for_tex_id);
|
|
|
|
|
|
|
|
ASSERT(mat_id >= 0);
|
|
|
|
ASSERT(mat_id < g_materials.count);
|
|
|
|
|
|
|
|
ASSERT(for_tex_id < COUNTOF(g_materials.tex_to_mat));
|
|
|
|
texture_to_material_t* const t2m = g_materials.tex_to_mat + for_tex_id;
|
|
|
|
|
|
|
|
if (t2m->mat_id == kMaterialNoReplacement) {
|
|
|
|
ERR("Texture \"%s\"(%d) has been already queried by something. Only future queries will get the new material", tex_name, for_tex_id);
|
|
|
|
} else if (t2m->mat_id != kMaterialNotChecked) {
|
|
|
|
ERR("Texture \"%s\"(%d) already has material assigned, will replace", tex_name, for_tex_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
t2m->mat_id = mat_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void loadMaterialsFromFile( const char *filename, int depth ) {
|
2023-05-04 18:35:29 +02:00
|
|
|
const uint64_t load_file_begin_ns = aprof_time_now_ns();
|
2022-08-08 09:23:38 +02:00
|
|
|
byte *data = gEngine.fsapi->LoadFile( filename, 0, false );
|
2023-05-04 18:35:29 +02:00
|
|
|
g_stats.material_file_read_duration_ns += aprof_time_now_ns() - load_file_begin_ns;
|
|
|
|
|
2023-09-19 19:05:12 +02:00
|
|
|
r_vk_material_t current_material = k_default_material;
|
2023-10-03 18:48:26 +02:00
|
|
|
int for_tex_id = -1;
|
2021-11-25 23:38:03 +01:00
|
|
|
qboolean force_reload = false;
|
2021-12-22 21:02:27 +01:00
|
|
|
qboolean create = false;
|
2023-04-12 19:16:10 +02:00
|
|
|
qboolean metalness_set = false;
|
2021-11-15 00:44:07 +01:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
string name;
|
2023-05-04 18:35:29 +02:00
|
|
|
string basecolor_map, normal_map, metal_map, roughness_map;
|
2023-10-10 19:27:10 +02:00
|
|
|
//uint32_t fields;
|
2023-05-04 18:35:29 +02:00
|
|
|
|
2023-09-29 19:58:02 +02:00
|
|
|
int rendermode = 0;
|
|
|
|
|
2023-09-26 18:25:40 +02:00
|
|
|
DEBUG("Loading materials from %s (exists=%d)", filename, data != 0);
|
2021-11-25 22:51:39 +01:00
|
|
|
|
2021-11-15 00:44:07 +01:00
|
|
|
if ( !data )
|
|
|
|
return;
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
const char *const path_begin = filename;
|
|
|
|
const char *path_end = Q_strrchr(filename, '/');
|
2021-11-15 00:44:07 +01:00
|
|
|
if ( !path_end )
|
|
|
|
path_end = path_begin;
|
|
|
|
else
|
|
|
|
path_end++;
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
char *pos = (char*)data;
|
2021-11-15 00:44:07 +01:00
|
|
|
for (;;) {
|
|
|
|
char key[1024];
|
|
|
|
char value[1024];
|
|
|
|
|
2023-05-04 19:42:37 +02:00
|
|
|
const char *const line_begin = pos;
|
2021-11-15 00:44:07 +01:00
|
|
|
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;
|
2023-10-03 18:48:26 +02:00
|
|
|
for_tex_id = -1;
|
2021-11-25 23:38:03 +01:00
|
|
|
force_reload = false;
|
2021-12-22 21:02:27 +01:00
|
|
|
create = false;
|
2023-04-12 19:16:10 +02:00
|
|
|
metalness_set = false;
|
2023-10-03 18:48:26 +02:00
|
|
|
name[0] = basecolor_map[0] = normal_map[0] = metal_map[0] = roughness_map[0] = '\0';
|
2023-09-29 19:58:02 +02:00
|
|
|
rendermode = 0;
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields = 0;
|
2021-11-15 00:44:07 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key[0] == '}') {
|
2023-10-26 18:00:34 +02:00
|
|
|
if (for_tex_id <= 0 && !create) {
|
2023-10-03 18:48:26 +02:00
|
|
|
// Skip this material, as its texture hasn't been loaded
|
|
|
|
// NOTE: might want to check whether it makes sense wrt late-loading stuff
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-10-26 18:00:34 +02:00
|
|
|
if (name[0] == '\0') {
|
2023-10-03 18:48:26 +02:00
|
|
|
WARN("Unreferenceable (no \"for_texture\", no \"new\") material found in %s", filename);
|
2021-12-22 21:02:27 +01:00
|
|
|
continue;
|
2023-10-03 18:48:26 +02:00
|
|
|
}
|
2021-12-22 21:02:27 +01:00
|
|
|
|
2023-11-06 16:31:55 +01:00
|
|
|
// If basecolor_map wasn't inherited
|
|
|
|
if (current_material.tex_base_color < 0) {
|
2023-10-26 18:00:34 +02:00
|
|
|
// Start with *default texture for base color, it will be acquired if no replacement is specified or could be loaded.
|
2023-11-06 16:31:55 +01:00
|
|
|
current_material.tex_base_color = for_tex_id >= 0 ? for_tex_id : 0;
|
|
|
|
}
|
2023-10-26 18:00:34 +02:00
|
|
|
|
|
|
|
#define LOAD_TEXTURE_FOR(name, field, colorspace) \
|
|
|
|
do { \
|
|
|
|
if (name[0] != '\0') { \
|
|
|
|
char texture_path[256]; \
|
|
|
|
MAKE_PATH(texture_path, name); \
|
|
|
|
const int tex_id = loadTexture(texture_path, force_reload, colorspace); \
|
|
|
|
if (tex_id < 0) { \
|
|
|
|
ERR("Failed to load texture \"%s\" for "#name"", name); \
|
2023-10-30 17:43:26 +01:00
|
|
|
if (current_material.field > 0) \
|
|
|
|
R_TextureAcquire(current_material.field); \
|
2023-10-26 18:00:34 +02:00
|
|
|
} else { \
|
|
|
|
current_material.field = tex_id; \
|
|
|
|
} \
|
2023-05-04 18:35:29 +02:00
|
|
|
} else { \
|
2023-10-30 17:43:26 +01:00
|
|
|
if (current_material.field > 0) \
|
|
|
|
R_TextureAcquire(current_material.field); \
|
2023-05-04 18:35:29 +02:00
|
|
|
} \
|
2023-10-26 18:00:34 +02:00
|
|
|
} while(0)
|
2023-05-04 18:35:29 +02:00
|
|
|
|
2023-10-10 18:08:25 +02:00
|
|
|
LOAD_TEXTURE_FOR(basecolor_map, tex_base_color, kColorspaceNative);
|
2023-10-06 21:06:44 +02:00
|
|
|
LOAD_TEXTURE_FOR(normal_map, tex_normalmap, kColorspaceLinear);
|
|
|
|
LOAD_TEXTURE_FOR(metal_map, tex_metalness, kColorspaceLinear);
|
|
|
|
LOAD_TEXTURE_FOR(roughness_map, tex_roughness, kColorspaceLinear);
|
2023-05-04 18:35:29 +02:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
if (!metalness_set && current_material.tex_metalness != tglob.whiteTexture) {
|
2023-04-12 19:16:10 +02:00
|
|
|
// If metalness factor wasn't set explicitly, but texture was specified, set it to match the texture value.
|
|
|
|
current_material.metalness = 1.f;
|
|
|
|
}
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
const int mat_id = addMaterial(name, ¤t_material);
|
2021-12-22 08:30:48 +01:00
|
|
|
|
2023-10-26 18:00:34 +02:00
|
|
|
releaseTexturesForMaterialPtr(¤t_material);
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
if (mat_id < 0) {
|
2023-10-19 17:15:45 +02:00
|
|
|
ERR("Cannot add material \"%s\" for_tex_id=\"%s\"(%d)", name, for_tex_id >= 0 ? R_TextureGetNameByIndex(for_tex_id) : "N/A", for_tex_id);
|
2023-10-03 18:48:26 +02:00
|
|
|
continue;
|
|
|
|
}
|
2023-09-29 19:58:02 +02:00
|
|
|
|
2023-10-30 17:43:26 +01:00
|
|
|
if (create)
|
|
|
|
{
|
|
|
|
const urmom_insert_t insert = urmomInsert(&g_materials.map_desc, name);
|
|
|
|
if (insert.index < 0) {
|
|
|
|
ERR("Cannot add new material '%s', ran out of space (max=%d)", name, MAX_NEW_MATERIALS);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
material_name_map_t *const item = g_materials.map + insert.index;
|
|
|
|
|
|
|
|
if (!insert.created)
|
|
|
|
WARN("Replacing material '%s'@%d %d=>%d", name, insert.index, item->mat_id, mat_id);
|
|
|
|
else
|
|
|
|
DEBUG("Mapping new material '%s'@%d => %d", name, insert.index, mat_id);
|
|
|
|
|
|
|
|
item->mat_id = mat_id;
|
2023-10-03 18:48:26 +02:00
|
|
|
}
|
2023-09-29 19:58:02 +02:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
// Assign from-texture mapping if there's a texture
|
|
|
|
if (for_tex_id >= 0) {
|
|
|
|
// Assign rendermode-specific materials
|
|
|
|
if (rendermode > 0) {
|
2023-10-19 17:15:45 +02:00
|
|
|
const char* const tex_name = R_TextureGetNameByIndex(for_tex_id);
|
2023-10-03 18:48:26 +02:00
|
|
|
DEBUG("Adding material \"%s\" for_tex_id=\"%s\"(%d) for rendermode %d", name, tex_name, for_tex_id, rendermode);
|
|
|
|
|
|
|
|
r_vk_material_per_mode_t* const rm = g_materials.for_rendermode + rendermode;
|
|
|
|
if (rm->count == COUNTOF(rm->map)) {
|
|
|
|
ERR("Too many rendermode/tex_id mappings");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
rm->map[rm->count].tex_id = for_tex_id;
|
|
|
|
rm->map[rm->count].mat.index = mat_id;
|
|
|
|
rm->count++;
|
|
|
|
} else {
|
|
|
|
assignMaterialForTexture(name, for_tex_id, mat_id);
|
|
|
|
}
|
2023-09-29 19:58:02 +02:00
|
|
|
}
|
2023-10-03 18:48:26 +02:00
|
|
|
|
2021-11-15 00:44:07 +01:00
|
|
|
continue;
|
2023-10-03 18:48:26 +02:00
|
|
|
} // if (key[0] == '}') -- closing material block
|
2021-11-15 00:44:07 +01:00
|
|
|
|
|
|
|
pos = COM_ParseFile(pos, value, sizeof(value));
|
|
|
|
if (!pos)
|
|
|
|
break;
|
|
|
|
|
2023-10-26 15:56:19 +02:00
|
|
|
//DEBUG("key=\"%s\", value=\"%s\"", key, value);
|
|
|
|
|
2021-11-15 00:44:07 +01:00
|
|
|
if (Q_stricmp(key, "for") == 0) {
|
2023-10-03 18:48:26 +02:00
|
|
|
if (name[0] != '\0')
|
|
|
|
WARN("Material already has \"new\" or \"for_texture\" old=\"%s\" new=\"%s\"", name, value);
|
|
|
|
|
2022-08-13 22:15:17 +02:00
|
|
|
const uint64_t lookup_begin_ns = aprof_time_now_ns();
|
2023-10-19 17:15:45 +02:00
|
|
|
for_tex_id = R_TextureFindByNameLike(value);
|
2023-10-26 15:56:19 +02:00
|
|
|
DEBUG("R_TextureFindByNameLike(%s)=%d", value, for_tex_id);
|
|
|
|
if (for_tex_id >= 0)
|
2023-10-27 15:05:53 +02:00
|
|
|
ASSERT(Q_stristr(R_TextureGetNameByIndex(for_tex_id), value) != NULL);
|
2022-08-13 22:15:17 +02:00
|
|
|
g_stats.texture_lookup_duration_ns += aprof_time_now_ns() - lookup_begin_ns;
|
|
|
|
g_stats.texture_lookups++;
|
2023-10-03 18:48:26 +02:00
|
|
|
Q_strncpy(name, value, sizeof name);
|
2021-12-22 21:02:27 +01:00
|
|
|
} else if (Q_stricmp(key, "new") == 0) {
|
2023-10-03 18:48:26 +02:00
|
|
|
if (name[0] != '\0')
|
|
|
|
WARN("Material already has \"new\" or \"for_texture\" old=\"%s\" new=\"%s\"", name, value);
|
|
|
|
Q_strncpy(name, value, sizeof name);
|
2021-12-22 21:02:27 +01:00
|
|
|
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 {
|
2023-08-29 18:31:57 +02:00
|
|
|
ERR("material: max include depth %d reached when including '%s' from '%s'", MAX_INCLUDE_DEPTH, value, filename);
|
2021-12-24 07:12:36 +01:00
|
|
|
}
|
2021-11-25 22:51:39 +01:00
|
|
|
} else {
|
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) {
|
2023-05-04 18:35:29 +02:00
|
|
|
Q_strncpy(basecolor_map, value, sizeof(basecolor_map));
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_basecolor_map;
|
2021-11-25 22:51:39 +01:00
|
|
|
} else if (Q_stricmp(key, "normal_map") == 0) {
|
2023-05-04 18:35:29 +02:00
|
|
|
Q_strncpy(normal_map, value, sizeof(normal_map));
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_normal_map;
|
2021-11-25 22:51:39 +01:00
|
|
|
} else if (Q_stricmp(key, "metal_map") == 0) {
|
2023-05-04 18:35:29 +02:00
|
|
|
Q_strncpy(metal_map, value, sizeof(metal_map));
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_metal_map;
|
2021-11-25 22:51:39 +01:00
|
|
|
} else if (Q_stricmp(key, "roughness_map") == 0) {
|
2023-05-04 18:35:29 +02:00
|
|
|
Q_strncpy(roughness_map, value, sizeof(roughness_map));
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_roughness_map;
|
|
|
|
} else if (Q_stricmp(key, "inherit") == 0 || Q_stricmp(key, "use") == 0) {
|
|
|
|
const r_vk_material_ref_t ref = R_VkMaterialGetForName(value);
|
|
|
|
if (ref.index < 0) {
|
|
|
|
ERR("In material \"%s\" cannot find material \"%s\" to inherit", name, value);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const r_vk_material_t inherited = R_VkMaterialGetForRef(ref);
|
|
|
|
current_material = inherited;
|
2021-12-01 19:32:32 +01:00
|
|
|
} else if (Q_stricmp(key, "roughness") == 0) {
|
|
|
|
sscanf(value, "%f", ¤t_material.roughness);
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_roughness;
|
2021-12-01 19:32:32 +01:00
|
|
|
} else if (Q_stricmp(key, "metalness") == 0) {
|
|
|
|
sscanf(value, "%f", ¤t_material.metalness);
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_metalness;
|
2023-04-12 19:16:10 +02:00
|
|
|
metalness_set = true;
|
2023-04-12 19:26:37 +02:00
|
|
|
} else if (Q_stricmp(key, "normal_scale") == 0) {
|
|
|
|
sscanf(value, "%f", ¤t_material.normal_scale);
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_normal_scale;
|
2021-12-01 19:32:32 +01:00
|
|
|
} else if (Q_stricmp(key, "base_color") == 0) {
|
2023-04-12 18:58:11 +02:00
|
|
|
sscanf(value, "%f %f %f %f", ¤t_material.base_color[0], ¤t_material.base_color[1], ¤t_material.base_color[2], ¤t_material.base_color[3]);
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_base_color;
|
2023-09-29 19:58:02 +02:00
|
|
|
} else if (Q_stricmp(key, "for_rendermode") == 0) {
|
|
|
|
rendermode = R_VkRenderModeFromString(value);
|
|
|
|
if (rendermode < 0)
|
|
|
|
ERR("Invalid rendermode \"%s\"", value);
|
2023-10-03 18:48:26 +02:00
|
|
|
ASSERT(rendermode < COUNTOF(g_materials.for_rendermode[0].map));
|
2023-10-10 19:27:10 +02:00
|
|
|
//fields |= kMatField_rendermode;
|
2021-11-25 22:51:39 +01:00
|
|
|
} else {
|
2023-08-29 18:31:57 +02:00
|
|
|
ERR("Unknown material key \"%s\" on line `%.*s`", key, (int)(pos - line_begin), line_begin);
|
2021-11-25 22:51:39 +01:00
|
|
|
continue;
|
2021-11-15 00:44:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Mem_Free( data );
|
2022-08-13 22:15:17 +02:00
|
|
|
g_stats.mat_files_read++;
|
2021-11-15 00:44:07 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-09-26 18:27:24 +02:00
|
|
|
static int findFilenameExtension(const char *s, int len) {
|
|
|
|
if (len < 0)
|
|
|
|
len = Q_strlen(s);
|
|
|
|
|
|
|
|
for (int i = len - 1; i >= 0; --i) {
|
|
|
|
if (s[i] == '.')
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2023-10-20 16:55:19 +02:00
|
|
|
static void materialsReleaseTextures( void ) {
|
|
|
|
for (int i = 1; i < g_materials.count; ++i)
|
|
|
|
releaseTexturesForMaterial(i);
|
|
|
|
}
|
|
|
|
|
2023-09-19 19:05:12 +02:00
|
|
|
void R_VkMaterialsReload( void ) {
|
2022-08-13 22:15:17 +02:00
|
|
|
const uint64_t begin_time_ns = aprof_time_now_ns();
|
2023-10-03 18:48:26 +02:00
|
|
|
memset(&g_stats, 0, sizeof(g_stats));
|
2022-08-13 22:15:17 +02:00
|
|
|
|
2023-10-20 16:55:19 +02:00
|
|
|
materialsReleaseTextures();
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
g_materials.count = 1;
|
2023-09-29 19:58:02 +02:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
memset(g_materials.tex_to_mat, 0, sizeof g_materials.tex_to_mat);
|
2023-04-12 19:16:10 +02:00
|
|
|
|
2023-10-30 17:43:26 +01:00
|
|
|
g_materials.map_desc = (urmom_desc_t){
|
|
|
|
.type = kUrmomStringInsensitive,
|
|
|
|
.array = g_materials.map,
|
|
|
|
.count = COUNTOF(g_materials.map),
|
|
|
|
.item_size = sizeof(g_materials.map[0]),
|
|
|
|
};
|
|
|
|
urmomInit(&g_materials.map_desc);
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
for (int i = 0; i < COUNTOF(g_materials.for_rendermode); ++i)
|
|
|
|
g_materials.for_rendermode[i].count = 0;
|
2021-11-14 22:53:50 +01:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
// TODO make these texture constants static constants
|
|
|
|
k_default_material.tex_metalness = tglob.whiteTexture;
|
|
|
|
k_default_material.tex_roughness = tglob.whiteTexture;
|
|
|
|
|
|
|
|
// TODO name?
|
|
|
|
g_materials.table[0].material = k_default_material;
|
|
|
|
g_materials.table[0].material.tex_base_color = 0;
|
2021-11-15 00:44:07 +01:00
|
|
|
|
2021-12-24 07:12:36 +01:00
|
|
|
loadMaterialsFromFile( "pbr/materials.mat", MAX_INCLUDE_DEPTH );
|
2021-11-25 22:51:39 +01:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
// Load materials by WAD files
|
2021-11-25 22:51:39 +01:00
|
|
|
{
|
2023-09-25 18:44:41 +02:00
|
|
|
for(const char *wad = g_map_entities.wadlist; *wad;) {
|
|
|
|
const char *wad_end = wad;
|
|
|
|
const char *ext = NULL;
|
|
|
|
while (*wad_end && *wad_end != ';') {
|
|
|
|
if (*wad_end == '.')
|
|
|
|
ext = wad_end;
|
|
|
|
++wad_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int full_length = wad_end - wad;
|
|
|
|
|
|
|
|
// Length without extension
|
|
|
|
const int short_length = ext ? ext - wad : full_length;
|
|
|
|
|
|
|
|
loadMaterialsFromFileF("pbr/%.*s/%.*s.mat", full_length, wad, short_length, wad);
|
2021-11-25 22:51:39 +01:00
|
|
|
wad = wad_end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
// Load materials by map/BSP file
|
2021-11-25 22:51:39 +01:00
|
|
|
{
|
|
|
|
const model_t *map = gEngine.pfnGetModelByIndex( 1 );
|
2023-09-25 18:44:41 +02:00
|
|
|
const char *filename = COM_FileWithoutPath(map->name);
|
2023-09-29 18:39:36 +02:00
|
|
|
const int no_ext_len = findFilenameExtension(filename, -1);
|
2023-09-26 18:27:24 +02:00
|
|
|
loadMaterialsFromFileF("pbr/%s/%.*s.mat", map->name, no_ext_len, filename);
|
2021-11-25 22:51:39 +01:00
|
|
|
}
|
2022-08-13 22:15:17 +02:00
|
|
|
|
|
|
|
// Print out statistics
|
|
|
|
{
|
|
|
|
const int duration_ms = (aprof_time_now_ns() - begin_time_ns) / 1000000ull;
|
2023-08-29 18:31:57 +02:00
|
|
|
INFO("Loading materials took %dms, .mat files parsed: %d (fread: %dms). Texture lookups: %d (%dms). Texture loads: %d (%dms).",
|
2022-08-13 22:15:17 +02:00
|
|
|
duration_ms,
|
|
|
|
g_stats.mat_files_read,
|
2023-05-04 18:35:29 +02:00
|
|
|
(int)(g_stats.material_file_read_duration_ns / 1000000ull),
|
2022-08-13 22:15:17 +02:00
|
|
|
g_stats.texture_lookups,
|
|
|
|
(int)(g_stats.texture_lookup_duration_ns / 1000000ull),
|
|
|
|
g_stats.texture_loads,
|
|
|
|
(int)(g_stats.texture_load_duration_ns / 1000000ull)
|
|
|
|
);
|
|
|
|
}
|
2021-11-14 22:53:50 +01:00
|
|
|
}
|
|
|
|
|
2023-09-26 18:27:24 +02:00
|
|
|
void R_VkMaterialsLoadForModel( const struct model_s* mod ) {
|
|
|
|
// Brush models are loaded separately
|
|
|
|
if (mod->type == mod_brush)
|
|
|
|
return;
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
// TODO add stats
|
|
|
|
|
2023-09-26 18:27:24 +02:00
|
|
|
const char *filename = COM_FileWithoutPath(mod->name);
|
|
|
|
const int no_ext_len = findFilenameExtension(filename, -1);
|
|
|
|
loadMaterialsFromFileF("pbr/%s/%.*s.mat", mod->name, no_ext_len, filename);
|
|
|
|
}
|
|
|
|
|
2023-09-22 15:43:27 +02:00
|
|
|
r_vk_material_t R_VkMaterialGetForTexture( int tex_index ) {
|
2023-10-16 01:59:22 +02:00
|
|
|
return R_VkMaterialGetForTextureWithFlags( tex_index, kVkMaterialFlagNone );
|
|
|
|
}
|
|
|
|
|
|
|
|
r_vk_material_t R_VkMaterialGetForTextureWithFlags( int tex_index, uint32_t flags ) {
|
2023-10-03 18:48:26 +02:00
|
|
|
//DEBUG("Getting material for tex_id=%d", tex_index);
|
2021-11-14 22:53:50 +01:00
|
|
|
ASSERT(tex_index >= 0);
|
|
|
|
ASSERT(tex_index < MAX_TEXTURES);
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
texture_to_material_t* const t2m = g_materials.tex_to_mat + tex_index;
|
|
|
|
|
|
|
|
if (t2m->mat_id > 0) {
|
|
|
|
ASSERT(t2m->mat_id < g_materials.count);
|
|
|
|
//DEBUG("Getting material for tex_id=%d", tex_index);
|
|
|
|
//printMaterial(t2m->mat_id);
|
|
|
|
return g_materials.table[t2m->mat_id].material;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t2m->mat_id == kMaterialNotChecked) {
|
|
|
|
// TODO check for replacement textures named in a predictable way
|
|
|
|
// If there are, create a new material and assign it here
|
|
|
|
|
2023-10-19 17:15:45 +02:00
|
|
|
const char* texname = R_TextureGetNameByIndex(tex_index);
|
2023-10-03 18:48:26 +02:00
|
|
|
DEBUG("Would try to load texture files by default names of \"%s\"", texname);
|
|
|
|
|
|
|
|
// If no PBR textures found, continue using legacy+default ones
|
|
|
|
t2m->mat_id = kMaterialNoReplacement;
|
|
|
|
}
|
|
|
|
|
|
|
|
r_vk_material_t ret = k_default_material;
|
|
|
|
ret.tex_base_color = tex_index;
|
2023-10-16 01:59:22 +02:00
|
|
|
|
|
|
|
if ( flags & kVkMaterialFlagChrome )
|
2023-11-06 18:47:21 +01:00
|
|
|
ret.tex_roughness = tglob.grayTexture;
|
2023-10-16 01:59:22 +02:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
//DEBUG("Returning default material with tex_base_color=%d", tex_index);
|
|
|
|
return ret;
|
2021-11-14 22:53:50 +01:00
|
|
|
}
|
2023-09-29 18:48:32 +02:00
|
|
|
|
|
|
|
r_vk_material_ref_t R_VkMaterialGetForName( const char *name ) {
|
2023-10-30 17:43:26 +01:00
|
|
|
// Find in internal map first
|
|
|
|
// New materials have preference over texture names
|
|
|
|
const int index = urmomFind(&g_materials.map_desc, name);
|
|
|
|
if (index >= 0)
|
|
|
|
return (r_vk_material_ref_t){.index = g_materials.map[index].mat_id};
|
|
|
|
DEBUG("Couldn't find material '%s', fallback to texture lookup", name);
|
|
|
|
|
|
|
|
// Find by texture name
|
2023-10-31 16:03:08 +01:00
|
|
|
const int tex_id = R_TextureFindByNameLike(name);
|
2023-10-30 17:43:26 +01:00
|
|
|
if (tex_id <= 0) {
|
|
|
|
ERR("Neither material nor texture with name \"%s\" was found", name);
|
2023-10-03 18:48:26 +02:00
|
|
|
return (r_vk_material_ref_t){.index = -1,};
|
|
|
|
}
|
|
|
|
|
2023-10-30 17:43:26 +01:00
|
|
|
ASSERT(tex_id > 0);
|
|
|
|
ASSERT(tex_id < MAX_TEXTURES);
|
2023-10-03 18:48:26 +02:00
|
|
|
|
2023-10-30 17:43:26 +01:00
|
|
|
return (r_vk_material_ref_t){.index = g_materials.tex_to_mat[tex_id].mat_id};
|
2023-09-29 18:48:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
r_vk_material_t R_VkMaterialGetForRef( r_vk_material_ref_t ref ) {
|
2023-10-03 18:48:26 +02:00
|
|
|
if (ref.index < 0) {
|
|
|
|
r_vk_material_t ret = k_default_material;
|
|
|
|
ret.tex_base_color = 0; // Default/error texture
|
|
|
|
return ret;
|
|
|
|
}
|
2023-09-29 18:48:32 +02:00
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
ASSERT(ref.index < g_materials.count);
|
|
|
|
return g_materials.table[ref.index].material;
|
2023-09-29 18:48:32 +02:00
|
|
|
}
|
2023-09-29 19:58:02 +02:00
|
|
|
|
2023-10-02 17:39:00 +02:00
|
|
|
qboolean R_VkMaterialGetEx( int tex_id, int rendermode, r_vk_material_t *out_material ) {
|
2023-09-29 19:58:02 +02:00
|
|
|
DEBUG("Getting material for tex_id=%d rendermode=%d", tex_id, rendermode);
|
|
|
|
|
2023-10-02 17:39:00 +02:00
|
|
|
if (rendermode == 0) {
|
|
|
|
WARN("rendermode==0: fallback to regular tex_id=%d", tex_id);
|
|
|
|
*out_material = R_VkMaterialGetForTexture(tex_id);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-10-03 18:48:26 +02:00
|
|
|
// TODO move rendermode-specifit things to by-texid-chains
|
|
|
|
ASSERT(rendermode < COUNTOF(g_materials.for_rendermode));
|
|
|
|
const r_vk_material_per_mode_t* const mode = &g_materials.for_rendermode[rendermode];
|
2023-10-02 17:39:00 +02:00
|
|
|
for (int i = 0; i < mode->count; ++i) {
|
2023-10-03 18:48:26 +02:00
|
|
|
if (mode->map[i].tex_id == tex_id) {
|
|
|
|
const int index = mode->map[i].mat.index;
|
|
|
|
ASSERT(index >= 0);
|
|
|
|
ASSERT(index < g_materials.count);
|
|
|
|
*out_material = g_materials.table[index].material;
|
2023-10-02 17:39:00 +02:00
|
|
|
return true;
|
2023-09-29 19:58:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-02 17:39:00 +02:00
|
|
|
return false;
|
2023-09-29 19:58:02 +02:00
|
|
|
}
|
2023-10-24 18:24:14 +02:00
|
|
|
|
|
|
|
void R_VkMaterialsShutdown( void ) {
|
|
|
|
materialsReleaseTextures();
|
|
|
|
}
|
|
|
|
|