mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2025-04-11 20:51:37 +00:00
Anachronox: update MDA load code
Support select skin by tag. Based on: * https://anachrodox.talonbrave.info/models/shaders.html
This commit is contained in:
parent
a33d6b091d
commit
3ba7edd995
2 changed files with 376 additions and 60 deletions
|
@ -157,8 +157,7 @@ Games check videos:
|
|||
Goals, fully finished goals could be checked in [here](CHANGELOG):
|
||||
|
||||
* [ ] CTC entity format from Anachronox,
|
||||
* [ ] MDA model skin selection by tag,
|
||||
* [ ] SDEF/MDA dynamicaly allocate list of skins,
|
||||
* [ ] SDEF dynamicaly allocate list of skins,
|
||||
* [ ] Support material load textures/textureinfo.dat from Anachronox,
|
||||
* [ ] Fix invisiable entities in basicsjam1_ziutek,
|
||||
* [ ] Make lightmap textures dynamic n64jam_palmlix,
|
||||
|
|
|
@ -2878,13 +2878,227 @@ Mod_LoadModel_SDEF(const char *mod_name, const void *buffer, int modfilelen,
|
|||
return extradata;
|
||||
}
|
||||
|
||||
static void *
|
||||
Mod_LoadModel_MDA_Text(const char *mod_name, char *curr_buff,
|
||||
readfile_t read_file, struct image_s ***skins, int *numskins, modtype_t *type)
|
||||
{
|
||||
char base_model[MAX_QPATH * 2] = {0};
|
||||
char base_skin[MAX_QPATH * 2] = {0};
|
||||
typedef struct {
|
||||
char *map;
|
||||
char *alphafunc;
|
||||
char *depthwrite;
|
||||
char *uvgen;
|
||||
char *blendmode;
|
||||
char *depthfunc;
|
||||
char *cull;
|
||||
char *rgbgen;
|
||||
char *uvmod;
|
||||
} mda_pass_t;
|
||||
|
||||
typedef struct {
|
||||
mda_pass_t *passes;
|
||||
size_t pass_count;
|
||||
} mda_skin_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *evaluate;
|
||||
mda_skin_t *skins;
|
||||
size_t skin_count;
|
||||
} mda_profile_t;
|
||||
|
||||
typedef struct {
|
||||
char *basemodel;
|
||||
vec3_t headtri;
|
||||
mda_profile_t *profiles;
|
||||
size_t profile_count;
|
||||
} mda_model_t;
|
||||
|
||||
static void
|
||||
Mod_LoadModel_MDA_Parse_SkipComment(char **curr_buff)
|
||||
{
|
||||
size_t linesize;
|
||||
|
||||
/* skip comment */
|
||||
linesize = strcspn(*curr_buff, "\n\r");
|
||||
*curr_buff += linesize;
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadModel_MDA_Parse_Pass(const char *mod_name, char **curr_buff, char *curr_end,
|
||||
mda_pass_t *pass)
|
||||
{
|
||||
while (*curr_buff && *curr_buff < curr_end)
|
||||
{
|
||||
const char *token;
|
||||
|
||||
token = COM_Parse(curr_buff);
|
||||
if (!*token)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (token[0] == '}')
|
||||
{
|
||||
/* skip end of section */
|
||||
break;
|
||||
}
|
||||
else if (token[0] == '#')
|
||||
{
|
||||
Mod_LoadModel_MDA_Parse_SkipComment(curr_buff);
|
||||
}
|
||||
else if (!strcmp(token, "map"))
|
||||
{
|
||||
token = COM_Parse(curr_buff);
|
||||
pass->map = strdup(token);
|
||||
Q_replacebackslash(pass->map);
|
||||
}
|
||||
else if (!strcmp(token, "uvmod"))
|
||||
{
|
||||
size_t linesize;
|
||||
char *value;
|
||||
|
||||
linesize = strcspn(*curr_buff, "\n\r");
|
||||
value = malloc(linesize + 1);
|
||||
memcpy(value, *curr_buff, linesize);
|
||||
value[linesize] = 0;
|
||||
*curr_buff += linesize;
|
||||
|
||||
pass->uvmod = value;
|
||||
}
|
||||
else if (!strcmp(token, "alphafunc") ||
|
||||
!strcmp(token, "depthwrite") ||
|
||||
!strcmp(token, "uvgen") ||
|
||||
!strcmp(token, "blendmode") ||
|
||||
!strcmp(token, "depthfunc") ||
|
||||
!strcmp(token, "cull") ||
|
||||
!strcmp(token, "rgbgen"))
|
||||
{
|
||||
char token_section[MAX_TOKEN_CHARS];
|
||||
|
||||
strncpy(token_section, token, sizeof(token_section) - 1);
|
||||
token = COM_Parse(curr_buff);
|
||||
|
||||
if (!strcmp(token_section, "alphafunc"))
|
||||
{
|
||||
pass->alphafunc = strdup(token);
|
||||
}
|
||||
else if (!strcmp(token_section, "depthwrite"))
|
||||
{
|
||||
pass->depthwrite = strdup(token);
|
||||
}
|
||||
else if (!strcmp(token_section, "uvgen"))
|
||||
{
|
||||
pass->uvgen = strdup(token);
|
||||
}
|
||||
else if (!strcmp(token_section, "blendmode"))
|
||||
{
|
||||
pass->blendmode = strdup(token);
|
||||
}
|
||||
else if (!strcmp(token_section, "depthfunc"))
|
||||
{
|
||||
pass->depthfunc = strdup(token);
|
||||
}
|
||||
else if (!strcmp(token_section, "cull"))
|
||||
{
|
||||
pass->cull = strdup(token);
|
||||
}
|
||||
else if (!strcmp(token_section, "rgbgen"))
|
||||
{
|
||||
pass->rgbgen = strdup(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadModel_MDA_Parse_Skin(const char *mod_name, char **curr_buff, char *curr_end,
|
||||
mda_skin_t *skin)
|
||||
{
|
||||
while (*curr_buff && *curr_buff < curr_end)
|
||||
{
|
||||
const char *token;
|
||||
|
||||
token = COM_Parse(curr_buff);
|
||||
if (!*token)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (token[0] == '}')
|
||||
{
|
||||
/* skip end of section */
|
||||
break;
|
||||
}
|
||||
else if (token[0] == '#')
|
||||
{
|
||||
Mod_LoadModel_MDA_Parse_SkipComment(curr_buff);
|
||||
}
|
||||
else if (!strcmp(token, "pass"))
|
||||
{
|
||||
mda_pass_t *pass;
|
||||
|
||||
skin->pass_count++;
|
||||
skin->passes = realloc(skin->passes, skin->pass_count * sizeof(mda_pass_t));
|
||||
pass = &skin->passes[skin->pass_count - 1];
|
||||
memset(pass, 0, sizeof(*pass));
|
||||
|
||||
token = COM_Parse(curr_buff);
|
||||
if (!token || token[0] != '{')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Mod_LoadModel_MDA_Parse_Pass(mod_name, curr_buff, curr_end, pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadModel_MDA_Parse_Profile(const char *mod_name, char **curr_buff, char *curr_end,
|
||||
mda_profile_t *profile)
|
||||
{
|
||||
while (*curr_buff && *curr_buff < curr_end)
|
||||
{
|
||||
const char *token;
|
||||
|
||||
token = COM_Parse(curr_buff);
|
||||
if (!*token)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (token[0] == '}')
|
||||
{
|
||||
/* skip end of section */
|
||||
break;
|
||||
}
|
||||
else if (token[0] == '#')
|
||||
{
|
||||
Mod_LoadModel_MDA_Parse_SkipComment(curr_buff);
|
||||
}
|
||||
else if (!strcmp(token, "skin")) {
|
||||
mda_skin_t *skin;
|
||||
|
||||
profile->skin_count++;
|
||||
profile->skins = realloc(profile->skins, profile->skin_count * sizeof(mda_skin_t));
|
||||
skin = &profile->skins[profile->skin_count - 1];
|
||||
memset(skin, 0, sizeof(*skin));
|
||||
|
||||
token = COM_Parse(curr_buff);
|
||||
if (!token || token[0] != '{')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Mod_LoadModel_MDA_Parse_Skin(mod_name, curr_buff, curr_end, skin);
|
||||
}
|
||||
else if (!strcmp(token, "evaluate"))
|
||||
{
|
||||
profile->evaluate = strdup(COM_Parse(curr_buff));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Mod_LoadModel_MDA_Parse(const char *mod_name, char *curr_buff, char *curr_end,
|
||||
mda_model_t *mda)
|
||||
{
|
||||
while (curr_buff)
|
||||
{
|
||||
const char *token;
|
||||
|
@ -2895,69 +3109,139 @@ Mod_LoadModel_MDA_Text(const char *mod_name, char *curr_buff,
|
|||
continue;
|
||||
}
|
||||
|
||||
else if (token[0] == '}')
|
||||
{
|
||||
/* skip end of section */
|
||||
continue;
|
||||
}
|
||||
else if (token[0] == '#')
|
||||
{
|
||||
Mod_LoadModel_MDA_Parse_SkipComment(&curr_buff);
|
||||
|
||||
}
|
||||
|
||||
/* found basemodel */
|
||||
else if (!strcmp(token, "basemodel"))
|
||||
{
|
||||
token = COM_Parse(&curr_buff);
|
||||
if (!token)
|
||||
{
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
strncpy(base_model, token, sizeof(base_model) - 1);
|
||||
mda->basemodel = strdup(token);
|
||||
Q_replacebackslash(mda->basemodel);
|
||||
}
|
||||
else if (!strcmp(token, "headtri"))
|
||||
{
|
||||
int i;
|
||||
|
||||
Q_replacebackslash(base_model);
|
||||
|
||||
if (base_skin[0])
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
/* other fields is unused for now */
|
||||
break;
|
||||
token = COM_Parse(&curr_buff);
|
||||
mda->headtri[i] = (float)strtod(token, (char **)NULL);
|
||||
}
|
||||
}
|
||||
/* TODO: should be profile {*} -> skin -> pass -> map */
|
||||
else if (!strcmp(token, "map"))
|
||||
else if (!strcmp(token, "profile"))
|
||||
{
|
||||
char* token_end = NULL;
|
||||
mda_profile_t *profile;
|
||||
|
||||
token = COM_Parse(&curr_buff);
|
||||
if (!token)
|
||||
mda->profile_count++;
|
||||
mda->profiles = realloc(mda->profiles, mda->profile_count * sizeof(mda_profile_t));
|
||||
profile = &mda->profiles[mda->profile_count - 1];
|
||||
memset(profile, 0, sizeof(*profile));
|
||||
|
||||
if (!token || token[0] == '{')
|
||||
{
|
||||
return NULL;
|
||||
profile->name = strdup("");
|
||||
}
|
||||
else
|
||||
{
|
||||
profile->name = strdup(token);
|
||||
token = COM_Parse(&curr_buff);
|
||||
if (!token || token[0] != '{')
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (token[0] == '"')
|
||||
{
|
||||
token ++;
|
||||
}
|
||||
|
||||
token_end = strchr(token, '"');
|
||||
if (token_end)
|
||||
{
|
||||
/* remove end " */
|
||||
*token_end = 0;
|
||||
}
|
||||
|
||||
strncpy(base_skin, token, sizeof(base_skin) - 1);
|
||||
|
||||
Q_replacebackslash(base_skin);
|
||||
|
||||
if (base_model[0])
|
||||
{
|
||||
/* other fields is unused for now */
|
||||
break;
|
||||
}
|
||||
Mod_LoadModel_MDA_Parse_Profile(mod_name, &curr_buff, curr_end, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (base_model[0])
|
||||
static void
|
||||
Mod_LoadModel_MDA_Free(mda_model_t *mda)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
free(mda->basemodel);
|
||||
for (i = 0; i < mda->profile_count; i++) {
|
||||
mda_profile_t *profile;
|
||||
size_t j;
|
||||
|
||||
profile = &mda->profiles[i];
|
||||
free(profile->name);
|
||||
free(profile->evaluate);
|
||||
for (j = 0; j < profile->skin_count; j++)
|
||||
{
|
||||
mda_skin_t *skin;
|
||||
size_t k;
|
||||
|
||||
skin = &profile->skins[j];
|
||||
for (k = 0; k < skin->pass_count; k++)
|
||||
{
|
||||
mda_pass_t *pass;
|
||||
|
||||
pass = &skin->passes[k];
|
||||
free(pass->map);
|
||||
free(pass->alphafunc);
|
||||
free(pass->depthwrite);
|
||||
free(pass->uvgen);
|
||||
free(pass->blendmode);
|
||||
free(pass->depthfunc);
|
||||
free(pass->cull);
|
||||
free(pass->rgbgen);
|
||||
free(pass->uvmod);
|
||||
}
|
||||
free(skin->passes);
|
||||
}
|
||||
free(profile->skins);
|
||||
}
|
||||
free(mda->profiles);
|
||||
}
|
||||
|
||||
static void *
|
||||
Mod_LoadModel_MDA_Text(const char *mod_name, char *curr_buff, size_t len,
|
||||
readfile_t read_file, struct image_s ***skins, int *numskins, modtype_t *type)
|
||||
{
|
||||
mda_model_t mda = {0};
|
||||
const char *profile_name;
|
||||
|
||||
profile_name = strrchr(mod_name, '!');
|
||||
if (profile_name)
|
||||
{
|
||||
/* skip ! */
|
||||
profile_name += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
profile_name = "";
|
||||
}
|
||||
|
||||
Mod_LoadModel_MDA_Parse(mod_name, curr_buff, curr_buff + len, &mda);
|
||||
|
||||
if (mda.basemodel)
|
||||
{
|
||||
void *extradata, *base;
|
||||
int base_size;
|
||||
|
||||
base_size = read_file(base_model, (void **)&base);
|
||||
base_size = read_file(mda.basemodel, (void **)&base);
|
||||
if (base_size <= 0)
|
||||
{
|
||||
R_Printf(PRINT_DEVELOPER, "%s: %s No base model for %s\n",
|
||||
__func__, mod_name, base_model);
|
||||
__func__, mod_name, mda.basemodel);
|
||||
Mod_LoadModel_MDA_Free(&mda);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2965,36 +3249,69 @@ Mod_LoadModel_MDA_Text(const char *mod_name, char *curr_buff,
|
|||
extradata = Mod_LoadModelFile(mod_name, base, base_size,
|
||||
skins, numskins,
|
||||
read_file, type);
|
||||
free(base);
|
||||
|
||||
/* check skin path */
|
||||
if (extradata && *type == mod_alias)
|
||||
if (extradata && *type == mod_alias && mda.profile_count)
|
||||
{
|
||||
dmdx_t *pheader;
|
||||
int i;
|
||||
mda_profile_t *profile = NULL;
|
||||
|
||||
pheader = (dmdx_t *)extradata;
|
||||
for (i=0; i < pheader->num_skins; i++)
|
||||
if (profile_name)
|
||||
{
|
||||
char *skin;
|
||||
size_t i;
|
||||
|
||||
/* Update included model with skin path */
|
||||
skin = (char *)pheader + pheader->ofs_skins + i * MAX_SKINNAME;
|
||||
if (!strchr(skin, '/') && !strchr(skin, '\\'))
|
||||
for (i = 0; i < mda.profile_count; i++)
|
||||
{
|
||||
char skin_path[MAX_QPATH * 2] = {0};
|
||||
if (!strcmp(mda.profiles[i].name, profile_name))
|
||||
{
|
||||
profile = &mda.profiles[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(skin_path, base_skin, sizeof(skin_path));
|
||||
strcpy(strrchr(skin_path, '/') + 1, skin);
|
||||
if (!profile)
|
||||
{
|
||||
/* use first in list */
|
||||
profile = &mda.profiles[0];
|
||||
}
|
||||
|
||||
strncpy(skin, skin_path, MAX_SKINNAME);
|
||||
if (profile)
|
||||
{
|
||||
size_t skins_count, i;
|
||||
dmdx_t *pheader;
|
||||
|
||||
pheader = (dmdx_t *)extradata;
|
||||
skins_count = Q_min(profile->skin_count, pheader->num_skins);
|
||||
|
||||
for (i = 0; i < skins_count; i++)
|
||||
{
|
||||
if (profile->skins[i].pass_count)
|
||||
{
|
||||
char *skin;
|
||||
|
||||
/* Update included model with skin path */
|
||||
skin = (char *)pheader + pheader->ofs_skins + i * MAX_SKINNAME;
|
||||
if (!strchr(skin, '/') && !strchr(skin, '\\'))
|
||||
{
|
||||
char skin_path[MAX_QPATH * 2] = {0};
|
||||
char *base_skin;
|
||||
|
||||
base_skin = profile->skins[i].passes[0].map;
|
||||
strncpy(skin_path, base_skin, sizeof(skin_path));
|
||||
strcpy(strrchr(skin_path, '/') + 1, skin);
|
||||
strncpy(skin, skin_path, MAX_SKINNAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(base);
|
||||
Mod_LoadModel_MDA_Free(&mda);
|
||||
|
||||
return extradata;
|
||||
}
|
||||
|
||||
Mod_LoadModel_MDA_Free(&mda);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -3009,8 +3326,8 @@ Mod_LoadModel_MDA(const char *mod_name, const void *buffer, int modfilelen,
|
|||
memcpy(text, (char *)buffer + 4, modfilelen - 4);
|
||||
text[modfilelen - 4] = 0;
|
||||
|
||||
extradata = Mod_LoadModel_MDA_Text(mod_name, text, read_file, skins,
|
||||
numskins, type);
|
||||
extradata = Mod_LoadModel_MDA_Text(mod_name, text, modfilelen - 4,
|
||||
read_file, skins, numskins, type);
|
||||
|
||||
free(text);
|
||||
|
||||
|
|
Loading…
Reference in a new issue