diff --git a/include/QF/GL/qf_iqm.h b/include/QF/GL/qf_iqm.h new file mode 100644 index 000000000..8c6b3e11c --- /dev/null +++ b/include/QF/GL/qf_iqm.h @@ -0,0 +1,41 @@ +/* + qf_iqm.h + + GL specific IQM stuff + + Copyright (C) 2012 Bill Currie + + Author: Bill Currie + Date: 2012/5/17 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_GL_qf_iqm_h +#define __QF_GL_qf_iqm_h + +#include "QF/iqm.h" + +typedef struct glsliqm_s { + int *textures; + iqmblend_t *blend_palette; // includes base data from iqm + int palette_size; // includes base data from iqm +} gliqm_t; + +#endif//__QF_GL_qf_iqm_h diff --git a/include/QF/Makefile.am b/include/QF/Makefile.am index e38f8304a..362e2f459 100644 --- a/include/QF/Makefile.am +++ b/include/QF/Makefile.am @@ -12,7 +12,7 @@ nobase_pkginclude_HEADERS = \ vrect.h view.h wad.h wadfile.h winding.h zone.h \ \ GL/ati.h GL/defines.h GL/extensions.h GL/funcs.h GL/qf_explosions.h \ - GL/qf_funcs_list.h GL/qf_lightmap.h \ + GL/qf_funcs_list.h GL/qf_iqm.h GL/qf_lightmap.h \ GL/qf_rlight.h GL/qf_rmain.h GL/qf_rsurf.h GL/qf_sky.h GL/qf_textures.h \ GL/qf_vid.h GL/types.h \ \ diff --git a/include/QF/iqm.h b/include/QF/iqm.h index 969fc8c33..fb6517c76 100644 --- a/include/QF/iqm.h +++ b/include/QF/iqm.h @@ -122,6 +122,11 @@ typedef struct { quat_t scale; } iqmframe_t; +typedef struct { + byte indices[4]; + byte weights[4]; +} iqmblend_t; + typedef struct { char *text; int num_meshes; diff --git a/include/mod_internal.h b/include/mod_internal.h index 434894bc3..794f7d5c5 100644 --- a/include/mod_internal.h +++ b/include/mod_internal.h @@ -53,6 +53,7 @@ void sw_Mod_SpriteLoadTexture (mspriteframe_t *pspriteframe, int framenum); void Mod_LoadIQM (model_t *mod, void *buffer); void Mod_FreeIQM (iqm_t *iqm); +iqmblend_t *Mod_IQMBuildBlendPalette (iqm_t *iqm, int *size); void Mod_LoadAliasModel (model_t *mod, void *buffer, cache_allocator_t allocator); void Mod_LoadSpriteModel (model_t *mod, void *buffer); diff --git a/libs/models/iqm/gl_model_iqm.c b/libs/models/iqm/gl_model_iqm.c index 25591ad2e..a93ab09f1 100644 --- a/libs/models/iqm/gl_model_iqm.c +++ b/libs/models/iqm/gl_model_iqm.c @@ -39,15 +39,68 @@ # include #endif +#include "QF/dstring.h" #include "QF/image.h" -#include "QF/qendian.h" #include "QF/quakefs.h" -#include "QF/skin.h" -#include "QF/sys.h" #include "QF/va.h" -#include "QF/vid.h" +#include "QF/GL/qf_iqm.h" #include "QF/GL/qf_textures.h" #include "mod_internal.h" -#include "compat.h" +static byte null_texture[] = { + 204, 204, 204, 255, + 204, 204, 204, 255, + 204, 204, 204, 255, + 204, 204, 204, 255, +}; + +static void +gl_iqm_clear (model_t *mod) +{ + iqm_t *iqm = (iqm_t *) mod->aliashdr; + gliqm_t *gl = (gliqm_t *) iqm->extra_data; + + mod->needload = true; + + free (gl->blend_palette); + free (gl); + Mod_FreeIQM (iqm); +} + +static void +gl_iqm_load_textures (iqm_t *iqm) +{ + gliqm_t *gl = (gliqm_t *) iqm->extra_data; + int i; + dstring_t *str = dstring_new (); + tex_t *tex; + + gl->textures = malloc (iqm->num_meshes * sizeof (int)); + for (i = 0; i < iqm->num_meshes; i++) { + dstring_copystr (str, iqm->text + iqm->meshes[i].material); + QFS_StripExtension (str->str, str->str); + if ((tex = LoadImage (va ("textures/%s", str->str)))) + gl->textures[i] = GL_LoadTexture (str->str, tex->width, + tex->height, tex->data, true, + false, + tex->format > 2 ? tex->format + : 1); + else + gl->textures[i] = GL_LoadTexture ("", 2, 2, null_texture, true, + false, 4); + } + dstring_delete (str); +} + +void +gl_Mod_IQMFinish (model_t *mod) +{ + iqm_t *iqm = (iqm_t *) mod->aliashdr; + gliqm_t *gl; + + mod->clear = gl_iqm_clear; + iqm->extra_data = gl = calloc (1, sizeof (gliqm_t)); + gl_iqm_load_textures (iqm); + gl->blend_palette = Mod_IQMBuildBlendPalette (iqm, &gl->palette_size); +} diff --git a/libs/models/iqm/model_iqm.c b/libs/models/iqm/model_iqm.c index f4ff29d35..597b35a87 100644 --- a/libs/models/iqm/model_iqm.c +++ b/libs/models/iqm/model_iqm.c @@ -40,8 +40,8 @@ #endif #include "QF/crc.h" +#include "QF/hash.h" #include "QF/iqm.h" -#include "QF/msg.h" #include "QF/qendian.h" #include "QF/quakefs.h" #include "QF/sys.h" @@ -542,3 +542,129 @@ Mod_FreeIQM (iqm_t *iqm) free (iqm->frames); free (iqm); } + +static void +swap_bones (byte *bi, byte *bw, int b1, int b2) +{ + byte t; + + t = bi[b1]; + bi[b1] = bi[b2]; + bi[b2] = t; + + t = bw[b1]; + bw[b1] = bw[b2]; + bw[b2] = t; +} + +static uintptr_t +blend_get_hash (void *e, void *unused) +{ + iqmblend_t *b = (iqmblend_t *) e; + return CRC_Block ((byte *) b, sizeof (iqmblend_t)); +} + +static int +blend_compare (void *e1, void *e2, void *unused) +{ + iqmblend_t *b1 = (iqmblend_t *) e1; + iqmblend_t *b2 = (iqmblend_t *) e2; + return !memcmp (b1, b2, sizeof (iqmblend_t)); +} + +#define MAX_BLENDS 1024 + +iqmblend_t * +Mod_IQMBuildBlendPalette (iqm_t *iqm, int *size) +{ + int i, j; + iqmvertexarray *bindices = 0; + iqmvertexarray *bweights = 0; + iqmblend_t *blend_list; + int num_blends; + hashtab_t *blend_hash; + + for (i = 0; i < iqm->num_arrays; i++) { + if (iqm->vertexarrays[i].type == IQM_BLENDINDEXES) + bindices = &iqm->vertexarrays[i]; + if (iqm->vertexarrays[i].type == IQM_BLENDWEIGHTS) + bweights = &iqm->vertexarrays[i]; + } + if (!bindices || !bweights) { + // Not necessarily an error: might be a static model with no bones + // Either way, no need to make a blend palette + Sys_MaskPrintf (SYS_MODEL, "bone index or weight array missing\n"); + *size = 0; + return 0; + } + + blend_list = calloc (MAX_BLENDS, sizeof (iqmblend_t)); + for (i = 0; i < iqm->num_joints; i++) { + blend_list[i].indices[0] = i; + blend_list[i].weights[0] = 255; + } + num_blends = iqm->num_joints; + + blend_hash = Hash_NewTable (1023, 0, 0, 0); + Hash_SetHashCompare (blend_hash, blend_get_hash, blend_compare); + + for (i = 0; i < iqm->num_verts; i++) { + byte *vert = iqm->vertices + i * iqm->stride; + byte *bi = vert + bindices->offset; + byte *bw = vert + bweights->offset; + iqmblend_t blend; + iqmblend_t *bl; + + // First, canonicalize vextex bone data: + // bone indices are in increasing order + // bone weight of zero is never followed by a non-zero weight + // bone weight of zero has bone index of zero + + // if the weight is zero, ensure the index is also zero + // also, ensure non-zero weights never follow zero weights + for (j = 0; j < 4; j++) { + if (!bw[j]) { + bi[j] = 0; + } else { + if (j && !bw[j-1]) { + swap_bones (bi, bw, j - 1, j); + j = 0; // force a rescan + } + } + } + // sort the bones such that the indeces are increasing (unless the + // weight is zero) + for (j = 0; j < 3; j++) { + if (!bw[j+1]) // zero weight == end of list + break; + if (bi[j] > bi[j+1]) { + swap_bones (bi, bw, j, j + 1); + j = -1; // force rescan + } + } + + // Now that the bone data is canonical, it can be hashed. + // However, no need to check other combinations if the vertex has + // only one influencing bone: the bone index will only change format. + if (!bw[1]) { + *(uint32_t *) bi = bi[0]; + continue; + } + QuatCopy (bi, blend.indices); + QuatCopy (bw, blend.weights); + if ((bl = Hash_FindElement (blend_hash, &blend))) { + *(uint32_t *) bi = (bl - blend_list); + continue; + } + if (num_blends >= MAX_BLENDS) + Sys_Error ("Too many blends. Tell taniwha to stop being lazy."); + blend_list[num_blends] = blend; + Hash_AddElement (blend_hash, &blend_list[num_blends]); + *(uint32_t *) bi = num_blends; + num_blends++; + } + + Hash_DelTable (blend_hash); + *size = num_blends; + return realloc (blend_list, num_blends * sizeof (iqmblend_t)); +} diff --git a/libs/video/renderer/vid_render_gl.c b/libs/video/renderer/vid_render_gl.c index 96b187f1f..f21f224cc 100644 --- a/libs/video/renderer/vid_render_gl.c +++ b/libs/video/renderer/vid_render_gl.c @@ -55,7 +55,7 @@ static vid_model_funcs_t model_funcs = { gl_Mod_LoadSkin, gl_Mod_FinalizeAliasModel, gl_Mod_LoadExternalSkins, - 0, + gl_Mod_IQMFinish, 1, gl_Mod_SpriteLoadTexture,