Skip to content

Commit

Permalink
Add support for MD5 replacement models.
Browse files Browse the repository at this point in the history
From Jonathan <jonno.5000@gmail.com>
From Frank Richter <frank.richter@gmail.com>

Based on code from vkQuake, unofficial MD5Mesh and MD5Anim specs.

Closes skullernet#315.
  • Loading branch information
skullernet committed Oct 17, 2023
1 parent 547f253 commit 1b42134
Show file tree
Hide file tree
Showing 13 changed files with 1,570 additions and 33 deletions.
11 changes: 10 additions & 1 deletion doc/client.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,14 @@ gl_screenshot_template::
component. Template may contain slashes to save under subdirectory. Default
value is "quakeXXX".

gl_md5_load::
Enables loading of MD5 replacement models as found in re-release. Default
value is 1.

gl_md5_use::
Enables use of MD5 replacement models as found in re-release. Only
effective if ‘gl_md5_load’ is enabled. Default value is 1.

r_override_textures::
Enables automatic overriding of palettized textures (in WAL or PCX format)
with truecolor replacements (in PNG, JPG or TGA format) by stripping off
Expand Down Expand Up @@ -853,7 +861,8 @@ When Q2PRO attempts to load an alias model from disk, it determines actual
model format by file contents, rather than by filename extension. Therefore, if
you wish to override MD2 model with MD3 replacement, simply rename the MD3
model to ‘tris.md2’ and place it in appropriate packfile to make sure it gets
loaded first.
loaded first. Note that MD5 models can't be loaded this way, see ‘gl_md5_’
variables.
********************


Expand Down
127 changes: 127 additions & 0 deletions inc/common/hash_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
Copyright (C) 2023 Axel Gneiting
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

typedef struct hash_map_s hash_map_t;

hash_map_t *HashMap_CreateImpl(const uint32_t key_size, const uint32_t value_size,
uint32_t (*hasher)(const void *const),
qboolean (*comp)(const void *const, const void *const));
void HashMap_Destroy(hash_map_t *map);
void HashMap_Reserve(hash_map_t *map, int capacity);
qboolean HashMap_InsertImpl(hash_map_t *map, const uint32_t key_size, const uint32_t value_size, const void *const key, const void *const value);
qboolean HashMap_EraseImpl(hash_map_t *map, const uint32_t key_size, const void *const key);
void *HashMap_LookupImpl(hash_map_t *map, const uint32_t key_size, const void *const key);
uint32_t HashMap_Size(hash_map_t *map);
void *HashMap_GetKeyImpl(hash_map_t *map, uint32_t index);
void *HashMap_GetValueImpl(hash_map_t *map, uint32_t index);

#define HashMap_Create(key_type, value_type, hasher, comp) HashMap_CreateImpl(sizeof(key_type), sizeof(value_type), hasher, comp)
#define HashMap_Insert(map, key, value) HashMap_InsertImpl(map, sizeof(*key), sizeof(*value), key, value)
#define HashMap_Erase(map, key) HashMap_EraseImpl(map, sizeof(*key), key)
#define HashMap_Lookup(type, map, key) ((type *)HashMap_LookupImpl(map, sizeof(*key), key))
#define HashMap_GetKey(type, map, index) ((type *)HashMap_GetKeyImpl(map, index))
#define HashMap_GetValue(type, map, index) ((type *)HashMap_GetValueImpl(map, index))

// Murmur3 fmix32
static inline uint32_t HashInt32(const void *const val)
{
uint32_t h = *(uint32_t *)val;
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}

// Murmur3 fmix64
static inline uint32_t HashInt64(const void *const val)
{
uint64_t k = *(uint64_t *)val;
k ^= k >> 33;
k *= 0xff51afd7ed558ccdull;
k ^= k >> 33;
k *= 0xc4ceb9fe1a85ec53ull;
k ^= k >> 33;
// Truncates, but all bits should be equally good
return k;
}

static inline uint32_t HashFloat(const void *const val)
{
uint32_t float_bits;
memcpy(&float_bits, val, sizeof(uint32_t));
if (float_bits == 0x80000000)
float_bits = 0;
return HashInt32(&float_bits);
}

static inline uint32_t HashPtr(const void *const val)
{
if (sizeof(void *) == sizeof(uint64_t))
return HashInt64(val);
return HashInt32(val);
}

// Murmur3 hash combine
static inline uint32_t HashCombine(uint32_t a, uint32_t b)
{
a *= 0xcc9e2d51;
a = (a >> 17) | (a << 15);
a *= 0x1b873593;
b ^= a;
b = (b >> 19) | (b << 13);
return (b * 5) + 0xe6546b64;
}

static inline uint32_t HashVec2(const void *const val)
{
vec2_t *vec = (vec2_t *)val;
return HashCombine(HashFloat(&(*vec)[0]), HashFloat(&(*vec)[1]));
}

static inline uint32_t HashVec3(const void *const val)
{
vec3_t *vec = (vec3_t *)val;
return HashCombine(HashFloat(&(*vec)[0]), HashCombine(HashFloat(&(*vec)[1]), HashFloat(&(*vec)[2])));
}

// FNV-1a hash
static inline uint32_t HashStr(const void *const val)
{
const unsigned char *str = *(const unsigned char **)val;
static const uint32_t FNV_32_PRIME = 0x01000193;

uint32_t hval = 0;
while (*str) {
hval ^= (uint32_t) * str;
hval *= FNV_32_PRIME;
++str;
}

return hval;
}

static inline qboolean HashStrCmp(const void *const a, const void *const b)
{
const char *str_a = *(const char **)a;
const char *str_b = *(const char **)b;
return strcmp(str_a, str_b) == 0;
}
18 changes: 18 additions & 0 deletions inc/common/math.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,22 @@ static inline vec_t PlaneDiffFast(const vec3_t v, const cplane_t *p)
return PlaneDiff(v, p);
}

#if USE_REF

void SetupRotationMatrix(vec3_t matrix[3], const vec3_t dir, float degrees);

// quaternion routines, for MD5 skeletons
#if USE_MD5
typedef vec4_t quat_t;

void Quat_ComputeW(quat_t q);
void Quat_SLerp(const quat_t qa, const quat_t qb, float backlerp, float frontlerp, quat_t out);
float Quat_Normalize(quat_t q);
void Quat_MultiplyQuat(const quat_t qa, const quat_t qb, quat_t out);
void Quat_MultiplyVector(const quat_t q, const vec3_t v, quat_t out);
void Quat_RotatePoint(const quat_t q, const vec3_t in, vec3_t out);
// Conjugate quaternion. Also, inverse, for unit quaternions (which MD5 quats are)
void Quat_Conjugate(const quat_t in, quat_t out);
#endif

#endif // USE_REF
15 changes: 9 additions & 6 deletions inc/shared/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,14 @@ typedef struct vrect_s {
(e)[2]=(a)[2]*(c)+(b)[2]*(d))
#define PlaneDiff(v,p) (DotProduct(v,(p)->normal)-(p)->dist)

#define Vector4Subtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2],(c)[3]=(a)[3]-(b)[3])
#define Vector4Add(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2],(c)[3]=(a)[3]+(b)[3])
#define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3])
#define Vector4Clear(a) ((a)[0]=(a)[1]=(a)[2]=(a)[3]=0)
#define Vector4Negate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2],(b)[3]=-(a)[3])
#define Vector4Subtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2],(c)[3]=(a)[3]-(b)[3])
#define Vector4Add(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2],(c)[3]=(a)[3]+(b)[3])
#define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3])
#define Vector4Clear(a) ((a)[0]=(a)[1]=(a)[2]=(a)[3]=0)
#define Vector4Negate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2],(b)[3]=-(a)[3])
#define Vector4Set(v, a, b, c, d) ((v)[0]=(a),(v)[1]=(b),(v)[2]=(c),(v)[3]=(d))
#define Vector4Compare(v1,v2) ((v1)[0]==(v2)[0]&&(v1)[1]==(v2)[1]&&(v1)[2]==(v2)[2]&&(v1)[3]==(v2)[3])
#define Vector4Compare(v1,v2) ((v1)[0]==(v2)[0]&&(v1)[1]==(v2)[1]&&(v1)[2]==(v2)[2]&&(v1)[3]==(v2)[3])
#define Dot4Product(x, y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]+(x)[3]*(y)[3])

void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
vec_t VectorNormalize(vec3_t v); // returns vector length
Expand Down Expand Up @@ -460,6 +461,8 @@ char *COM_SkipPath(const char *pathname);
size_t COM_StripExtension(char *out, const char *in, size_t size);
size_t COM_DefaultExtension(char *path, const char *ext, size_t size);
char *COM_FileExtension(const char *in);
void COM_SplitPath(const char *in, char *name, size_t name_size,
char *path, size_t path_size, bool strip_ext);

#define COM_CompareExtension(in, ext) \
Q_strcasecmp(COM_FileExtension(in), ext)
Expand Down
3 changes: 3 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ common_src = [
'src/common/field.c',
'src/common/fifo.c',
'src/common/files.c',
'src/common/hash_map.c',
'src/common/math.c',
'src/common/mdfour.c',
'src/common/msg.c',
Expand Down Expand Up @@ -453,6 +454,7 @@ config.set10('USE_DEBUG', get_option('debug'))
config.set10('USE_FPS', get_option('variable-fps'))
config.set10('USE_ICMP', get_option('icmp-errors').require(win32 or cc.has_header('linux/errqueue.h')).allowed())
config.set10('USE_MD3', get_option('md3'))
config.set10('USE_MD5', get_option('md5'))
config.set10('USE_PACKETDUP', get_option('packetdup-hack'))
config.set10('USE_TGA', get_option('tga'))
config.set10('USE_' + host_machine.endian().to_upper() + '_ENDIAN', true)
Expand All @@ -475,6 +477,7 @@ summary({
'libjpeg' : config.get('USE_JPG', 0) != 0,
'libpng' : config.get('USE_PNG', 0) != 0,
'md3' : config.get('USE_MD3', 0) != 0,
'md5' : config.get('USE_MD5', 0) != 0,
'mvd-client' : config.get('USE_MVD_CLIENT', 0) != 0,
'mvd-server' : config.get('USE_MVD_SERVER', 0) != 0,
'ogg' : config.get('USE_OGG', 0) != 0,
Expand Down
5 changes: 5 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ option('md3',
value: true,
description: 'MD3 models support')

option('md5',
type: 'boolean',
value: true,
description: 'MD5 (re-release) models support')

option('mvd-client',
type: 'boolean',
value: true,
Expand Down

0 comments on commit 1b42134

Please sign in to comment.