From 13e1df2bda11d83204fc8d00efc83962097675cf Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Mon, 6 May 2024 23:38:22 +0300 Subject: [PATCH] renders: inital support of SiN def/sbm/sam load Added only file format support without model overwrite. Based on https://web.archive.org/web/20001212060900/http://starbase.neosoft.com:80/~otaku/program.html --- README.md | 6 +- src/client/refresh/files/models.c | 315 ++++++++++++++++++++++++++++- src/client/refresh/gl1/gl1_model.c | 19 +- src/client/refresh/gl3/gl3_model.c | 19 +- src/client/refresh/gl4/gl4_model.c | 19 +- src/client/refresh/ref_shared.h | 4 +- src/client/refresh/soft/sw_model.c | 19 +- src/client/refresh/vk/vk_model.c | 19 +- src/common/header/files.h | 8 +- 9 files changed, 414 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7c769ec0..9b2a4c6c 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,15 @@ Models support: | md2 | Anachronox | 8/10/16 bit | Single | No tagged surfaces, unchecked with game | | mdx | Kingpin | 8 bit | Many | No sfx support, unchecked with game | | fm | Heretic 2 | 8 bit | Many | | +| def | SiN | Part of sam | Many | Unchecked with game | | dkm | Daikatana DKM1 | 8 bit | Many | Unchecked with game | | dkm | Daikatana DKM2 | 10 bit | Many | Unchecked with game | | md3 | Quake 3 | 16 bit | Many | No tags support | | md5 | Doom 3/Quake 4 | float | Many | Requires md2 for skins | +| sbm | SiN | Part of sam | Many | Unchecked with game | +| sam | SiN | 8 bit | Many | Unchecked with game | -All models support only single texture for all meshes and only up to 255 frames. +All models support only single texture for all meshes and frames limit based on game protocol. Texture support: @@ -88,6 +91,7 @@ Games: * SDK: https://www.quaddicted.com/files/idgames2/planetquake/hereticii/files/Ht2Toolkit_v1.06.exe * Tech info: http://h2vault.infinityfreeapp.com/index.html * SiN: + * Tools: [SiNview](https://web.archive.org/web/20001212060900/http://starbase.neosoft.com:80/~otaku/program.html) * Tools: https://www.moddb.com/games/sin/downloads/sin-modding-tools-and-other-stuff * SDK: https://github.com/NightDive-Studio/sin-ex-game diff --git a/src/client/refresh/files/models.c b/src/client/refresh/files/models.c index d135a246..a8a1429d 100644 --- a/src/client/refresh/files/models.c +++ b/src/client/refresh/files/models.c @@ -150,7 +150,8 @@ Mod_LoadFrames_MD2(dmdx_t *pheader, byte *src, size_t inframesize, vec3_t transl poutframe->translate[j] = LittleFloat(pinframe->translate[j]); poutframe->translate[j] += translate[j]; } - // verts are all 8 bit, so no swapping needed + + /* verts are all 8 bit, so no swapping needed */ for (j=0; j < pheader->num_xyz; j ++) { Mod_LoadFrames_VertMD2(poutframe->verts + j, pinframe->verts[j].v); @@ -159,6 +160,57 @@ Mod_LoadFrames_MD2(dmdx_t *pheader, byte *src, size_t inframesize, vec3_t transl } } +/* +================= +Mod_LoadFrames_SAM + +Load the SiN sam animation format frames +================= +*/ +static void +Mod_LoadFrames_SAM(dmdx_t *pheader, sin_sam_header_t **anims, int anim_num, + vec3_t translate, float scale) +{ + int curr_frame = 0, i; + + for (i = 0; i < anim_num; i++) + { + sin_frame_t *pinframe; + int k; + + pinframe = (sin_frame_t *) ((char *)anims[i] + anims[i]->ofs_frames); + + for (k = 0; k < anims[i]->num_frames; k++) + { + daliasxframe_t *poutframe; + dtrivertx_t *verts; + int j; + + poutframe = (daliasxframe_t *) ((byte *)pheader + + pheader->ofs_frames + curr_frame * pheader->framesize); + + memcpy(poutframe->name, anims[i]->name, sizeof(poutframe->name)); + + for (j = 0; j < 3; j++) + { + poutframe->scale[j] = LittleFloat(pinframe[k].scale[j]) * scale / 0xFF; + poutframe->translate[j] = LittleFloat(pinframe[k].translate[j]); + poutframe->translate[j] += translate[j]; + } + + verts = (dtrivertx_t*)((char *)(pinframe + k) + pinframe[k].ofs_verts); + /* verts are all 8 bit, so no swapping needed */ + for (j=0; j < pheader->num_xyz; j ++) + { + Mod_LoadFrames_VertMD2(poutframe->verts + j, verts[j].v); + poutframe->verts[j].lightnormalindex = verts[j].lightnormalindex; + } + + curr_frame ++; + } + } +} + /* ================= Mod_LoadFrames_MD2Anox @@ -2479,6 +2531,259 @@ Mod_LoadModel_MDX(const char *mod_name, const void *buffer, int modfilelen, return extradata; } +static void * +Mod_LoadModel_SDEF_Text(const char *mod_name, char *curr_buff, readfile_t read_file, + struct image_s ***skins, int *numskins) +{ + char models_path[MAX_QPATH]; + char base_model[MAX_QPATH]; + char skinnames[255][MAX_SKINNAME]; + char animations[255][MAX_QPATH]; + char fullbasename[MAX_QPATH * 2]; + int actions_num, skinnames_num, i, base_size; + sin_sbm_header_t *base; + sin_sam_header_t *anim[255]; + void *extradata = NULL; + vec3_t translate = {0, 0, 0}; + float scale; + + actions_num = 0; + skinnames_num = 0; + scale = 1.0f; + memset(models_path, 0, sizeof(models_path)); + + if (strcmp(COM_Parse(&curr_buff), "SDEF")) + { + return NULL; + } + + while (curr_buff) + { + const char *token; + + token = COM_Parse(&curr_buff); + if (!*token) + { + continue; + } + /* found start of comment */ + else if (!strncmp(token, "/*", 2)) + { + token = COM_Parse(&curr_buff); + + /* search end of comment */ + while (curr_buff && strncmp(token, "*/", 2)) + { + token = COM_Parse(&curr_buff); + } + } + else if (!strcmp(token, "path")) + { + strncpy(models_path, COM_Parse(&curr_buff), sizeof(models_path)); + } + else if (!strcmp(token, "scale")) + { + scale = (float)strtod(COM_Parse(&curr_buff), (char **)NULL); + } + else if (!strcmp(token, "id") || + !strcmp(token, "group") || + !strcmp(token, "!init:") || + !strcmp(token, "!main:") || + !strcmp(token, "client") || + !strcmp(token, "server")) + { + /* Just skipped for now */ + curr_buff = strchr(curr_buff, '\n'); + if (*curr_buff == '\n') + { + curr_buff ++; + } + } + else + { + const char *ext; + + ext = COM_FileExtension(token); + if (!strcmp(ext, "sbm")) + { + snprintf(base_model, sizeof(base_model), + "%s/%s", models_path, token); + } + else + { + token = COM_Parse(&curr_buff); + ext = COM_FileExtension(token); + if (!strcmp(ext, "sam")) + { + snprintf(animations[actions_num], sizeof(animations[actions_num]), + "%s/%s", models_path, token); + actions_num ++; + } + if (!strcmp(ext, "tga")) + { + snprintf(skinnames[skinnames_num], sizeof(skinnames[skinnames_num]), + "%s/%s", models_path, token); + skinnames_num ++; + } + } + } + } + + base = (sin_sbm_header_t*)read_file(base_model, &base_size); + if (base_size <= 0) + { + R_Printf(PRINT_DEVELOPER, "%s: %s, No base model for animation for %s\n", + __func__, mod_name, base_model); + return NULL; + } + + int num_tris = 0; + { + sin_trigroup_t *trigroup = (sin_trigroup_t *)((char*)base + sizeof(sin_sbm_header_t)); + + for(i = 0; i < base->num_groups; i ++) + { + num_tris += trigroup[i].num_tris; + } + } + + int framescount = 0; + int animation_num = 0; + + for (i = 0; i < actions_num; i++) + { + int anim_size; + + anim[animation_num] = (sin_sam_header_t*)read_file(animations[i], &anim_size); + if (anim_size <= 0) + { + R_Printf(PRINT_DEVELOPER, "%s: %s empty animation %s\n", + __func__, mod_name, animations[i]); + continue; + } + if (anim[animation_num]->num_xyz != base->num_xyz) + { + R_Printf(PRINT_DEVELOPER, "%s: %s, incorrect count tris in animation in %s\n", + __func__, mod_name, animations[i]); + continue; + } + + animation_num ++; + framescount += anim[i]->num_frames; + } + + if (!animation_num) + { + R_Printf(PRINT_DEVELOPER, "%s: %s no aanimation found\n", + __func__, mod_name); + free(base); + return NULL; + } + + dmdx_t dmdxheader, *pheader = NULL; + dmdxheader.skinwidth = 256; + dmdxheader.skinheight = 256; + + dmdxheader.framesize = sizeof(daliasxframe_t) - sizeof(dxtrivertx_t); + dmdxheader.framesize += base->num_xyz * sizeof(dxtrivertx_t); + + dmdxheader.num_meshes = base->num_groups; + dmdxheader.num_skins = skinnames_num; + dmdxheader.num_xyz = base->num_xyz; + dmdxheader.num_st = base->num_st; + dmdxheader.num_tris = num_tris; + dmdxheader.num_frames = framescount; + /* (count vert + 3 vert * (2 float + 1 int)) + final zero; */ + dmdxheader.num_glcmds = (10 * dmdxheader.num_tris) + 1 * dmdxheader.num_meshes; + + /* just skip header */ + dmdxheader.ofs_meshes = sizeof(dmdxheader); + dmdxheader.ofs_skins = dmdxheader.ofs_meshes + dmdxheader.num_meshes * sizeof(dmdxmesh_t); + dmdxheader.ofs_st = dmdxheader.ofs_skins + dmdxheader.num_skins * MAX_SKINNAME; + dmdxheader.ofs_tris = dmdxheader.ofs_st + dmdxheader.num_st * sizeof(dstvert_t); + dmdxheader.ofs_frames = dmdxheader.ofs_tris + dmdxheader.num_tris * sizeof(dtriangle_t); + dmdxheader.ofs_glcmds = dmdxheader.ofs_frames + dmdxheader.num_frames * dmdxheader.framesize; + dmdxheader.ofs_end = dmdxheader.ofs_glcmds + dmdxheader.num_glcmds * sizeof(int); + + *numskins = dmdxheader.num_skins; + extradata = Hunk_Begin(dmdxheader.ofs_end + Q_max(*numskins, MAX_MD2SKINS) * sizeof(struct image_s *)); + pheader = Hunk_Alloc(dmdxheader.ofs_end); + *skins = Hunk_Alloc((*numskins) * sizeof(struct image_s *)); + + memcpy(pheader, &dmdxheader, sizeof(dmdxheader)); + + memcpy((byte*)pheader + pheader->ofs_skins, (byte *)skinnames, + pheader->num_skins * MAX_SKINNAME); + + /* st */ + st_vert_t *st_in = (st_vert_t *)((char *)base + base->ofs_st); + dstvert_t *st_out = (dstvert_t *)((char *)pheader + pheader->ofs_st); + for (i = 0; i < pheader->num_st; i ++) + { + st_out[i].s = (int)(st_in[i].s * pheader->skinwidth + 256) % 256; + st_out[i].t = (int)(st_in[i].t * pheader->skinheight + 256) % 256; + } + + /* tris and mesh */ + sin_trigroup_t *trigroup_in = (sin_trigroup_t *)((char*)base + sizeof(sin_sbm_header_t)); + dtriangle_t *pouttriofs = (dtriangle_t *) ((char *)pheader + pheader->ofs_tris); + dmdxmesh_t *mesh_nodes = (dmdxmesh_t *)((char *)pheader + pheader->ofs_meshes); + + int tris_ofs = 0; + for(i = 0; i < base->num_groups; i ++) + { + sin_triangle_t *tri_in; + int j; + + mesh_nodes[i].ofs_tris = tris_ofs; + mesh_nodes[i].num_tris = trigroup_in[i].num_tris; + + /* offset from current group */ + tri_in = (sin_triangle_t *)((char*)(trigroup_in + i) + trigroup_in[i].ofs_tris); + for (j = 0; j < trigroup_in[i].num_tris; j++) + { + int k; + + for (k = 0; k < 3; k++) + { + pouttriofs[tris_ofs + j].index_st[k] = LittleShort(tri_in[j].index_st[k]); + pouttriofs[tris_ofs + j].index_xyz[k] = LittleShort(tri_in[j].index_xyz[k]); + } + } + tris_ofs += mesh_nodes[i].num_tris; + } + + Mod_LoadFrames_SAM(pheader, anim, animation_num, translate, scale); + Mod_LoadCmdGenerate(pheader); + Mod_LoadFixImages(mod_name, pheader, false); + + for (i = 0; i < animation_num; i++) + { + free(anim[i]); + } + + free(base); + return extradata; +} + +static void * +Mod_LoadModel_SDEF(const char *mod_name, const void *buffer, int modfilelen, + readfile_t read_file, struct image_s ***skins, int *numskins, modtype_t *type) +{ + void *extradata; + char *text; + + text = malloc(modfilelen + 1); + memcpy(text, buffer, modfilelen); + text[modfilelen] = 0; + extradata = Mod_LoadModel_SDEF_Text(mod_name, text, read_file, skins, numskins); + free(text); + + *type = mod_alias; + + return extradata; +} + /* ================= Mod_LoadSprite_SP2 @@ -2649,7 +2954,8 @@ Mod_LoadModel void * Mod_LoadModel(const char *mod_name, const void *buffer, int modfilelen, vec3_t mins, vec3_t maxs, struct image_s ***skins, int *numskins, - findimage_t find_image, loadimage_t load_image, modtype_t *type) + findimage_t find_image, loadimage_t load_image, readfile_t read_file, + modtype_t *type) { void *extradata = NULL; @@ -2661,6 +2967,11 @@ Mod_LoadModel(const char *mod_name, const void *buffer, int modfilelen, switch (LittleLong(*(unsigned *)buffer)) { + case SDEFHEADER: + extradata = Mod_LoadModel_SDEF(mod_name, buffer, modfilelen, + read_file, skins, numskins, type); + break; + case MDXHEADER: extradata = Mod_LoadModel_MDX(mod_name, buffer, modfilelen, skins, numskins, type); diff --git a/src/client/refresh/gl1/gl1_model.c b/src/client/refresh/gl1/gl1_model.c index 96bea71e..64a699f4 100644 --- a/src/client/refresh/gl1/gl1_model.c +++ b/src/client/refresh/gl1/gl1_model.c @@ -370,6 +370,23 @@ Mod_LoadBrushModel(model_t *mod, const void *buffer, int modfilelen) mod->numframes = 2; /* regular and alternate animation */ } +/* Temporary solution, need to use load file dirrectly */ +static char * +Mod_ReadFile(const char *name, int *size) +{ + char *buffer, *data; + + *size = ri.FS_LoadFile(name, (void **)&buffer); + if (*size <= 0) + { + return NULL; + } + + data = malloc(*size); + memcpy(data, buffer, *size); + return data; +} + /* * Loads in a model for the given name */ @@ -476,7 +493,7 @@ Mod_ForName(const char *name, model_t *parent_model, qboolean crash) mod->extradata = Mod_LoadModel(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s ***)&mod->skins, &mod->numskins, - (findimage_t)R_FindImage, (loadimage_t)R_LoadPic, + (findimage_t)R_FindImage, (loadimage_t)R_LoadPic, Mod_ReadFile, &(mod->type)); if (!mod->extradata) { diff --git a/src/client/refresh/gl3/gl3_model.c b/src/client/refresh/gl3/gl3_model.c index def4276d..795534a1 100644 --- a/src/client/refresh/gl3/gl3_model.c +++ b/src/client/refresh/gl3/gl3_model.c @@ -371,6 +371,23 @@ Mod_LoadBrushModel(gl3model_t *mod, const void *buffer, int modfilelen) mod->numframes = 2; /* regular and alternate animation */ } +/* Temporary solution, need to use load file dirrectly */ +static char * +Mod_ReadFile(const char *name, int *size) +{ + char *buffer, *data; + + *size = ri.FS_LoadFile(name, (void **)&buffer); + if (*size <= 0) + { + return NULL; + } + + data = malloc(*size); + memcpy(data, buffer, *size); + return data; +} + /* * Loads in a model for the given name */ @@ -477,7 +494,7 @@ Mod_ForName(const char *name, gl3model_t *parent_model, qboolean crash) mod->extradata = Mod_LoadModel(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s ***)&mod->skins, &mod->numskins, - (findimage_t)GL3_FindImage, (loadimage_t)GL3_LoadPic, + (findimage_t)GL3_FindImage, (loadimage_t)GL3_LoadPic, Mod_ReadFile, &(mod->type)); if (!mod->extradata) { diff --git a/src/client/refresh/gl4/gl4_model.c b/src/client/refresh/gl4/gl4_model.c index 12b48b7a..5107a935 100644 --- a/src/client/refresh/gl4/gl4_model.c +++ b/src/client/refresh/gl4/gl4_model.c @@ -371,6 +371,23 @@ Mod_LoadBrushModel(gl4model_t *mod, const void *buffer, int modfilelen) mod->numframes = 2; /* regular and alternate animation */ } +/* Temporary solution, need to use load file dirrectly */ +static char * +Mod_ReadFile(const char *name, int *size) +{ + char *buffer, *data; + + *size = ri.FS_LoadFile(name, (void **)&buffer); + if (*size <= 0) + { + return NULL; + } + + data = malloc(*size); + memcpy(data, buffer, *size); + return data; +} + /* * Loads in a model for the given name */ @@ -477,7 +494,7 @@ Mod_ForName(const char *name, gl4model_t *parent_model, qboolean crash) mod->extradata = Mod_LoadModel(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s ***)&mod->skins, &mod->numskins, - (findimage_t)GL4_FindImage, (loadimage_t)GL4_LoadPic, + (findimage_t)GL4_FindImage, (loadimage_t)GL4_LoadPic, Mod_ReadFile, &(mod->type)); if (!mod->extradata) { diff --git a/src/client/refresh/ref_shared.h b/src/client/refresh/ref_shared.h index 0d516444..9820e476 100644 --- a/src/client/refresh/ref_shared.h +++ b/src/client/refresh/ref_shared.h @@ -327,10 +327,12 @@ typedef struct } bspxlightgrid_t; /* Shared models func */ +typedef char* (*readfile_t)(const char *name, int *size); typedef struct image_s* (*findimage_t)(const char *name, imagetype_t type); extern void *Mod_LoadModel(const char *mod_name, const void *buffer, int modfilelen, vec3_t mins, vec3_t maxs, struct image_s ***skins, int *numskins, - findimage_t find_image, loadimage_t load_image, modtype_t *type); + findimage_t find_image, loadimage_t load_image, readfile_t read_file, + modtype_t *type); extern int Mod_ReLoadSkins(struct image_s **skins, findimage_t find_image, loadimage_t load_image, void *extradata, modtype_t type); extern struct image_s *GetSkyImage(const char *skyname, const char* surfname, diff --git a/src/client/refresh/soft/sw_model.c b/src/client/refresh/soft/sw_model.c index 3c06d26a..17d86c38 100644 --- a/src/client/refresh/soft/sw_model.c +++ b/src/client/refresh/soft/sw_model.c @@ -378,6 +378,23 @@ Mod_LoadBrushModel(model_t *mod, const void *buffer, int modfilelen) R_InitSkyBox(mod); } +/* Temporary solution, need to use load file dirrectly */ +static char * +Mod_ReadFile(const char *name, int *size) +{ + char *buffer, *data; + + *size = ri.FS_LoadFile(name, (void **)&buffer); + if (*size <= 0) + { + return NULL; + } + + data = malloc(*size); + memcpy(data, buffer, *size); + return data; +} + /* * Loads in a model for the given name */ @@ -484,7 +501,7 @@ Mod_ForName(const char *name, model_t *parent_model, qboolean crash) mod->extradata = Mod_LoadModel(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s ***)&mod->skins, &mod->numskins, - (findimage_t)R_FindImage, (loadimage_t)R_LoadPic, + (findimage_t)R_FindImage, (loadimage_t)R_LoadPic, Mod_ReadFile, &(mod->type)); if (!mod->extradata) { diff --git a/src/client/refresh/vk/vk_model.c b/src/client/refresh/vk/vk_model.c index 6a4cee84..150d25c8 100644 --- a/src/client/refresh/vk/vk_model.c +++ b/src/client/refresh/vk/vk_model.c @@ -345,6 +345,23 @@ Mod_LoadBrushModel(model_t *mod, const void *buffer, int modfilelen) mod->numframes = 2; /* regular and alternate animation */ } +/* Temporary solution, need to use load file dirrectly */ +static char * +Mod_ReadFile(const char *name, int *size) +{ + char *buffer, *data; + + *size = ri.FS_LoadFile(name, (void **)&buffer); + if (*size <= 0) + { + return NULL; + } + + data = malloc(*size); + memcpy(data, buffer, *size); + return data; +} + /* * Loads in a model for the given name */ @@ -460,7 +477,7 @@ Mod_ForName(const char *name, model_t *parent_model, qboolean crash) mod->extradata = Mod_LoadModel(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s ***)&mod->skins, &mod->numskins, - (findimage_t)Vk_FindImage, (loadimage_t)Vk_LoadPic, + (findimage_t)Vk_FindImage, (loadimage_t)Vk_LoadPic, Mod_ReadFile, &(mod->type)); if (!mod->extradata) { diff --git a/src/common/header/files.h b/src/common/header/files.h index ffbf99c2..cc06138f 100644 --- a/src/common/header/files.h +++ b/src/common/header/files.h @@ -1013,11 +1013,9 @@ typedef struct } drbrushside_t; #define SDEFHEADER (('F' << 24) + ('E' << 16) + ('D' << 8) + 'S') /* little-endian "SDEF" */ - -typedef struct -{ - unsigned char x,y,z,normal_index; -} trivertx_t; +#define SBMHEADER ((' ' << 24) + ('M' << 16) + ('B' << 8) + 'S') /* little-endian "SBM " */ +#define SAMHEADER ((' ' << 24) + ('M' << 16) + ('A' << 8) + 'S') /* little-endian "SAM " */ +#define MDSINVERSION 1 typedef struct {