2010-02-15 23:26:55 +00:00
/*
Copyright ( C ) 1996 - 2001 Id Software , Inc .
Copyright ( C ) 2002 - 2009 John Fitzgibbons and others
2014-09-22 08:55:46 +00:00
Copyright ( C ) 2010 - 2014 QuakeSpasm developers
2010-02-15 23:26:55 +00:00
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 .
*/
// gl_mesh.c: triangle model functions
# include "quakedef.h"
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
ALIAS MODEL DISPLAY LIST GENERATION
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = =
GL_MakeAliasModelDisplayLists
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
Saves data needed to build the VBO for this model on the hunk . Afterwards this
is copied to Mod_Extradata .
Original code by MH from RMQEngine
= = = = = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
void GL_MakeAliasModelDisplayLists ( qmodel_t * m , aliashdr_t * paliashdr )
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
{
int i , j ;
int maxverts_vbo ;
unsigned short * indexes ;
2017-09-17 02:12:53 +00:00
trivertx_t * verts ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
aliasmesh_t * desc ;
// there can never be more than this number of verts and we just put them all on the hunk
2017-09-17 02:12:53 +00:00
// front/back logic says we can never have more than numverts*2
maxverts_vbo = paliashdr - > numverts * 2 ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
desc = ( aliasmesh_t * ) Hunk_Alloc ( sizeof ( aliasmesh_t ) * maxverts_vbo ) ;
// there will always be this number of indexes
2017-09-17 02:12:53 +00:00
indexes = ( unsigned short * ) Hunk_Alloc ( sizeof ( unsigned short ) * paliashdr - > numtris * 3 ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
paliashdr - > indexes = ( intptr_t ) indexes - ( intptr_t ) paliashdr ;
paliashdr - > meshdesc = ( intptr_t ) desc - ( intptr_t ) paliashdr ;
paliashdr - > numindexes = 0 ;
paliashdr - > numverts_vbo = 0 ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
for ( i = 0 ; i < paliashdr - > numtris ; i + + )
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
{
for ( j = 0 ; j < 3 ; j + + )
{
int v ;
// index into hdr->vertexes
unsigned short vertindex = triangles [ i ] . vertindex [ j ] ;
// basic s/t coords
int s = stverts [ vertindex ] . s ;
int t = stverts [ vertindex ] . t ;
// check for back side and adjust texcoord s
2017-09-17 02:12:53 +00:00
if ( ! triangles [ i ] . facesfront & & stverts [ vertindex ] . onseam ) s + = paliashdr - > skinwidth / 2 ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
// see does this vert already exist
2017-09-17 02:12:53 +00:00
for ( v = 0 ; v < paliashdr - > numverts_vbo ; v + + )
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
{
// it could use the same xyz but have different s and t
if ( desc [ v ] . vertindex = = vertindex & & ( int ) desc [ v ] . st [ 0 ] = = s & & ( int ) desc [ v ] . st [ 1 ] = = t )
{
// exists; emit an index for it
2017-09-17 02:12:53 +00:00
indexes [ paliashdr - > numindexes + + ] = v ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
// no need to check any more
break ;
}
}
2017-09-17 02:12:53 +00:00
if ( v = = paliashdr - > numverts_vbo )
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
{
// doesn't exist; emit a new vert and index
2017-09-17 02:12:53 +00:00
indexes [ paliashdr - > numindexes + + ] = paliashdr - > numverts_vbo ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
desc [ paliashdr - > numverts_vbo ] . vertindex = vertindex ;
desc [ paliashdr - > numverts_vbo ] . st [ 0 ] = s ;
desc [ paliashdr - > numverts_vbo + + ] . st [ 1 ] = t ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
}
}
}
2017-09-17 02:12:53 +00:00
2019-01-30 01:54:49 +00:00
switch ( paliashdr - > poseverttype )
{
case PV_QUAKEFORGE :
2019-11-02 15:50:53 +00:00
verts = ( trivertx_t * ) Hunk_Alloc ( paliashdr - > nummorphposes * paliashdr - > numverts_vbo * 2 * sizeof ( * verts ) ) ;
2019-01-30 01:54:49 +00:00
paliashdr - > vertexes = ( byte * ) verts - ( byte * ) paliashdr ;
2019-11-02 15:50:53 +00:00
for ( i = 0 ; i < paliashdr - > nummorphposes ; i + + )
2019-01-30 01:54:49 +00:00
for ( j = 0 ; j < paliashdr - > numverts_vbo ; j + + )
{
verts [ i * paliashdr - > numverts_vbo * 2 + j ] = poseverts_mdl [ i ] [ desc [ j ] . vertindex ] ;
verts [ i * paliashdr - > numverts_vbo * 2 + j + paliashdr - > numverts_vbo ] = poseverts_mdl [ i ] [ desc [ j ] . vertindex + paliashdr - > numverts_vbo ] ;
}
break ;
case PV_QUAKE1 :
2019-11-02 15:50:53 +00:00
verts = ( trivertx_t * ) Hunk_Alloc ( paliashdr - > nummorphposes * paliashdr - > numverts_vbo * sizeof ( * verts ) ) ;
2019-01-30 01:54:49 +00:00
paliashdr - > vertexes = ( byte * ) verts - ( byte * ) paliashdr ;
2019-11-02 15:50:53 +00:00
for ( i = 0 ; i < paliashdr - > nummorphposes ; i + + )
2019-01-30 01:54:49 +00:00
for ( j = 0 ; j < paliashdr - > numverts_vbo ; j + + )
verts [ i * paliashdr - > numverts_vbo + j ] = poseverts_mdl [ i ] [ desc [ j ] . vertindex ] ;
break ;
2019-11-02 15:50:53 +00:00
case PV_IQM :
2019-01-30 01:54:49 +00:00
case PV_QUAKE3 :
break ; //invalid here.
}
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
}
# define NUMVERTEXNORMALS 162
extern float r_avertexnormals [ NUMVERTEXNORMALS ] [ 3 ] ;
/*
= = = = = = = = = = = = = = = =
2015-09-20 20:10:49 +00:00
GLMesh_LoadVertexBuffer
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2015-09-20 20:10:49 +00:00
Upload the given alias model ' s mesh to a VBO
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
Original code by MH from RMQEngine
2017-09-17 02:12:53 +00:00
may update the mesh vbo / ebo offsets .
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
= = = = = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
void GLMesh_LoadVertexBuffer ( qmodel_t * m , aliashdr_t * mainhdr )
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
{
2017-09-17 02:12:53 +00:00
//we always need vertex array data.
//if we don't support vbos(gles?) then we just use system memory.
//if we're not using glsl(gles1?), then we don't actually need all the data, but we do still need some so its easier to just alloc the lot.
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
int totalvbosize = 0 ;
2015-09-20 20:10:49 +00:00
const aliasmesh_t * desc ;
2017-09-17 02:12:53 +00:00
const void * trivertexes ;
byte * ebodata ;
2015-09-20 20:10:49 +00:00
byte * vbodata ;
int f ;
2017-09-17 02:12:53 +00:00
aliashdr_t * hdr ;
unsigned int numindexes , numverts ;
intptr_t stofs ;
intptr_t vertofs ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
//count how much space we're going to need.
for ( hdr = mainhdr , numverts = 0 , numindexes = 0 ; ; )
{
2019-01-30 01:54:49 +00:00
switch ( hdr - > poseverttype )
{
case PV_QUAKE1 :
2019-11-02 15:50:53 +00:00
totalvbosize + = ( hdr - > nummorphposes * hdr - > numverts_vbo * sizeof ( meshxyz_mdl_t ) ) ; // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
2019-01-30 01:54:49 +00:00
break ;
case PV_QUAKEFORGE :
2019-11-02 15:50:53 +00:00
totalvbosize + = ( hdr - > nummorphposes * hdr - > numverts_vbo * sizeof ( meshxyz_mdl16_t ) ) ;
2019-01-30 01:54:49 +00:00
break ;
case PV_QUAKE3 :
2019-11-02 15:50:53 +00:00
totalvbosize + = ( hdr - > nummorphposes * hdr - > numverts_vbo * sizeof ( meshxyz_md3_t ) ) ;
break ;
case PV_IQM :
totalvbosize + = ( hdr - > nummorphposes * hdr - > numverts_vbo * sizeof ( iqmvert_t ) ) ;
2019-01-30 01:54:49 +00:00
break ;
}
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
numverts + = hdr - > numverts_vbo ;
numindexes + = hdr - > numindexes ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
if ( hdr - > nextsurface )
hdr = ( aliashdr_t * ) ( ( byte * ) hdr + hdr - > nextsurface ) ;
else
break ;
}
hdr = NULL ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
vertofs = 0 ;
totalvbosize = ( totalvbosize + 7 ) & ~ 7 ; //align it.
stofs = totalvbosize ;
totalvbosize + = ( numverts * sizeof ( meshst_t ) ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
if ( ! totalvbosize ) return ;
if ( ! numindexes ) return ;
//create an elements buffer
ebodata = ( byte * ) malloc ( numindexes * sizeof ( unsigned short ) ) ;
if ( ! ebodata )
return ; //fatal
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
// create the vertex buffer (empty)
2015-09-20 20:10:49 +00:00
vbodata = ( byte * ) malloc ( totalvbosize ) ;
2017-09-17 02:12:53 +00:00
if ( ! vbodata )
{ //fatal
free ( ebodata ) ;
return ;
}
2015-09-20 20:10:49 +00:00
memset ( vbodata , 0 , totalvbosize ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
numindexes = 0 ;
for ( hdr = mainhdr , numverts = 0 , numindexes = 0 ; ; )
2015-09-20 20:10:49 +00:00
{
2017-09-17 02:12:53 +00:00
// grab the pointers to data in the extradata
desc = ( aliasmesh_t * ) ( ( byte * ) hdr + hdr - > meshdesc ) ;
trivertexes = ( void * ) ( ( byte * ) hdr + hdr - > vertexes ) ;
//submit the index data.
hdr - > eboofs = numindexes * sizeof ( unsigned short ) ;
numindexes + = hdr - > numindexes ;
memcpy ( ebodata + hdr - > eboofs , ( short * ) ( ( byte * ) hdr + hdr - > indexes ) , hdr - > numindexes * sizeof ( unsigned short ) ) ;
hdr - > vbovertofs = vertofs ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
// fill in the vertices at the start of the buffer
2019-01-30 01:54:49 +00:00
switch ( hdr - > poseverttype )
2015-09-20 20:10:49 +00:00
{
2019-01-30 01:54:49 +00:00
case PV_QUAKE1 :
2019-11-02 15:50:53 +00:00
for ( f = 0 ; f < hdr - > nummorphposes ; f + + ) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
2017-09-17 02:12:53 +00:00
{
int v ;
meshxyz_mdl_t * xyz = ( meshxyz_mdl_t * ) ( vbodata + vertofs ) ;
2019-11-02 15:50:53 +00:00
const trivertx_t * tv = ( const trivertx_t * ) trivertexes + ( hdr - > numverts_vbo * f ) ;
2017-09-17 02:12:53 +00:00
vertofs + = hdr - > numverts_vbo * sizeof ( * xyz ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
for ( v = 0 ; v < hdr - > numverts_vbo ; v + + , tv + + )
{
xyz [ v ] . xyz [ 0 ] = tv - > v [ 0 ] ;
xyz [ v ] . xyz [ 1 ] = tv - > v [ 1 ] ;
xyz [ v ] . xyz [ 2 ] = tv - > v [ 2 ] ;
xyz [ v ] . xyz [ 3 ] = 1 ; // need w 1 for 4 byte vertex compression
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
xyz [ v ] . normal [ 0 ] = 127 * r_avertexnormals [ tv - > lightnormalindex ] [ 0 ] ;
xyz [ v ] . normal [ 1 ] = 127 * r_avertexnormals [ tv - > lightnormalindex ] [ 1 ] ;
xyz [ v ] . normal [ 2 ] = 127 * r_avertexnormals [ tv - > lightnormalindex ] [ 2 ] ;
xyz [ v ] . normal [ 3 ] = 0 ; // unused; for 4-byte alignment
}
}
2019-01-30 01:54:49 +00:00
break ;
case PV_QUAKEFORGE :
2019-11-02 15:50:53 +00:00
for ( f = 0 ; f < hdr - > nummorphposes ; f + + ) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
2019-01-30 01:54:49 +00:00
{
int v ;
meshxyz_mdl16_t * xyz = ( meshxyz_mdl16_t * ) ( vbodata + vertofs ) ;
2019-11-02 15:50:53 +00:00
const trivertx_t * tv = ( const trivertx_t * ) trivertexes + ( hdr - > numverts_vbo * 2 * f ) ;
2019-01-30 01:54:49 +00:00
vertofs + = hdr - > numverts_vbo * sizeof ( * xyz ) ;
for ( v = 0 ; v < hdr - > numverts_vbo ; v + + , tv + + )
{
xyz [ v ] . xyz [ 0 ] = ( tv - > v [ 0 ] < < 8 ) | tv [ hdr - > numverts_vbo ] . v [ 0 ] ;
xyz [ v ] . xyz [ 1 ] = ( tv - > v [ 1 ] < < 8 ) | tv [ hdr - > numverts_vbo ] . v [ 0 ] ;
xyz [ v ] . xyz [ 2 ] = ( tv - > v [ 2 ] < < 8 ) | tv [ hdr - > numverts_vbo ] . v [ 0 ] ;
xyz [ v ] . xyz [ 3 ] = 1 ; // need w 1 for 4 byte vertex compression
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
xyz [ v ] . normal [ 0 ] = 127 * r_avertexnormals [ tv - > lightnormalindex ] [ 0 ] ;
xyz [ v ] . normal [ 1 ] = 127 * r_avertexnormals [ tv - > lightnormalindex ] [ 1 ] ;
xyz [ v ] . normal [ 2 ] = 127 * r_avertexnormals [ tv - > lightnormalindex ] [ 2 ] ;
xyz [ v ] . normal [ 3 ] = 0 ; // unused; for 4-byte alignment
}
}
break ;
case PV_QUAKE3 :
2019-11-02 15:50:53 +00:00
for ( f = 0 ; f < hdr - > nummorphposes ; f + + ) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
2017-09-17 02:12:53 +00:00
{
int v ;
meshxyz_md3_t * xyz = ( meshxyz_md3_t * ) ( vbodata + vertofs ) ;
2019-11-02 15:50:53 +00:00
const md3XyzNormal_t * tv = ( const md3XyzNormal_t * ) trivertexes + ( hdr - > numverts_vbo * f ) ;
2017-09-17 02:12:53 +00:00
float lat , lng ;
vertofs + = hdr - > numverts_vbo * sizeof ( * xyz ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
for ( v = 0 ; v < hdr - > numverts_vbo ; v + + , tv + + )
{
xyz [ v ] . xyz [ 0 ] = tv - > xyz [ 0 ] ;
xyz [ v ] . xyz [ 1 ] = tv - > xyz [ 1 ] ;
xyz [ v ] . xyz [ 2 ] = tv - > xyz [ 2 ] ;
xyz [ v ] . xyz [ 3 ] = 1 ; // need w 1 for 4 byte vertex compression
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
lat = ( float ) tv - > latlong [ 0 ] * ( 2 * M_PI ) * ( 1.0 / 255.0 ) ;
lng = ( float ) tv - > latlong [ 1 ] * ( 2 * M_PI ) * ( 1.0 / 255.0 ) ;
xyz [ v ] . normal [ 0 ] = 127 * cos ( lng ) * sin ( lat ) ;
xyz [ v ] . normal [ 1 ] = 127 * sin ( lng ) * sin ( lat ) ;
xyz [ v ] . normal [ 2 ] = 127 * cos ( lat ) ;
xyz [ v ] . normal [ 3 ] = 0 ; // unused; for 4-byte alignment
}
}
2019-01-30 01:54:49 +00:00
break ;
2019-11-02 15:50:53 +00:00
case PV_IQM :
for ( f = 0 ; f < hdr - > nummorphposes ; f + + ) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
{
int v ;
iqmvert_t * xyz = ( iqmvert_t * ) ( vbodata + vertofs ) ;
const iqmvert_t * tv = ( const iqmvert_t * ) trivertexes + ( hdr - > numverts_vbo * f ) ;
vertofs + = hdr - > numverts_vbo * sizeof ( * xyz ) ;
for ( v = 0 ; v < hdr - > numverts_vbo ; v + + , tv + + )
xyz [ v ] = * tv ;
}
break ;
2017-09-17 02:12:53 +00:00
}
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
// fill in the ST coords at the end of the buffer
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
{
2017-09-17 02:12:53 +00:00
meshst_t * st ;
float hscale , vscale ;
//johnfitz -- padded skins
hscale = ( float ) hdr - > skinwidth / ( float ) TexMgr_PadConditional ( hdr - > skinwidth ) ;
vscale = ( float ) hdr - > skinheight / ( float ) TexMgr_PadConditional ( hdr - > skinheight ) ;
//johnfitz
hdr - > vbostofs = stofs ;
st = ( meshst_t * ) ( vbodata + stofs ) ;
stofs + = hdr - > numverts_vbo * sizeof ( * st ) ;
2019-01-30 01:54:49 +00:00
switch ( hdr - > poseverttype )
2017-09-17 02:12:53 +00:00
{
2019-01-30 01:54:49 +00:00
case PV_QUAKE3 :
2017-09-17 02:12:53 +00:00
for ( f = 0 ; f < hdr - > numverts_vbo ; f + + )
{ //md3 has floating-point skin coords. use the values directly.
st [ f ] . st [ 0 ] = hscale * desc [ f ] . st [ 0 ] ;
st [ f ] . st [ 1 ] = vscale * desc [ f ] . st [ 1 ] ;
}
2019-01-30 01:54:49 +00:00
break ;
case PV_QUAKEFORGE :
case PV_QUAKE1 :
2017-09-17 02:12:53 +00:00
for ( f = 0 ; f < hdr - > numverts_vbo ; f + + )
{
st [ f ] . st [ 0 ] = hscale * ( ( float ) desc [ f ] . st [ 0 ] + 0.5f ) / ( float ) hdr - > skinwidth ;
st [ f ] . st [ 1 ] = vscale * ( ( float ) desc [ f ] . st [ 1 ] + 0.5f ) / ( float ) hdr - > skinheight ;
}
2019-01-30 01:54:49 +00:00
break ;
2019-11-02 15:50:53 +00:00
case PV_IQM :
//st coords are interleaved.
break ;
2017-09-17 02:12:53 +00:00
}
2015-09-20 20:10:49 +00:00
}
2017-09-17 02:12:53 +00:00
if ( hdr - > nextsurface )
hdr = ( aliashdr_t * ) ( ( byte * ) hdr + hdr - > nextsurface ) ;
else
break ;
2015-09-20 20:10:49 +00:00
}
2017-09-17 02:12:53 +00:00
hdr = NULL ;
if ( gl_vbo_able )
{
// upload indexes buffer
GL_DeleteBuffersFunc ( 1 , & m - > meshindexesvbo ) ;
GL_GenBuffersFunc ( 1 , & m - > meshindexesvbo ) ;
GL_BindBufferFunc ( GL_ELEMENT_ARRAY_BUFFER , m - > meshindexesvbo ) ;
GL_BufferDataFunc ( GL_ELEMENT_ARRAY_BUFFER , numindexes * sizeof ( unsigned short ) , ebodata , GL_STATIC_DRAW ) ;
// upload vertexes buffer
GL_DeleteBuffersFunc ( 1 , & m - > meshvbo ) ;
GL_GenBuffersFunc ( 1 , & m - > meshvbo ) ;
GL_BindBufferFunc ( GL_ARRAY_BUFFER , m - > meshvbo ) ;
GL_BufferDataFunc ( GL_ARRAY_BUFFER , totalvbosize , vbodata , GL_STATIC_DRAW ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
free ( vbodata ) ;
free ( ebodata ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
m - > meshvboptr = NULL ;
m - > meshindexesvboptr = NULL ;
}
else
{
m - > meshvboptr = vbodata ;
m - > meshindexesvboptr = ebodata ;
}
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2015-09-20 20:10:49 +00:00
// invalidate the cached bindings
GL_ClearBufferBindings ( ) ;
}
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2015-09-20 20:10:49 +00:00
/*
= = = = = = = = = = = = = = = =
GLMesh_LoadVertexBuffers
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2015-09-20 20:10:49 +00:00
Loop over all precached alias models , and upload each one to a VBO .
= = = = = = = = = = = = = = = =
*/
void GLMesh_LoadVertexBuffers ( void )
{
int j ;
qmodel_t * m ;
2017-09-17 02:12:53 +00:00
aliashdr_t * hdr ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2015-09-20 20:10:49 +00:00
for ( j = 1 ; j < MAX_MODELS ; j + + )
{
if ( ! ( m = cl . model_precache [ j ] ) ) break ;
if ( m - > type ! = mod_alias ) continue ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2017-09-17 02:12:53 +00:00
hdr = ( aliashdr_t * ) Mod_Extradata ( m ) ;
2015-09-20 20:10:49 +00:00
GLMesh_LoadVertexBuffer ( m , hdr ) ;
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
}
2015-09-20 20:10:49 +00:00
}
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
2015-09-20 20:10:49 +00:00
/*
= = = = = = = = = = = = = = = =
GLMesh_DeleteVertexBuffers
Delete VBOs for all loaded alias models
= = = = = = = = = = = = = = = =
*/
void GLMesh_DeleteVertexBuffers ( void )
{
int j ;
qmodel_t * m ;
2017-09-17 02:12:53 +00:00
if ( ! gl_vbo_able )
2015-09-20 20:10:49 +00:00
return ;
for ( j = 1 ; j < MAX_MODELS ; j + + )
{
if ( ! ( m = cl . model_precache [ j ] ) ) break ;
if ( m - > type ! = mod_alias ) continue ;
2017-09-17 02:12:53 +00:00
if ( m - > meshvbo )
GL_DeleteBuffersFunc ( 1 , & m - > meshvbo ) ;
2015-09-20 20:10:49 +00:00
m - > meshvbo = 0 ;
2017-09-17 02:12:53 +00:00
free ( m - > meshvboptr ) ;
m - > meshvboptr = NULL ;
2015-09-20 20:10:49 +00:00
2017-09-17 02:12:53 +00:00
if ( m - > meshindexesvbo )
GL_DeleteBuffersFunc ( 1 , & m - > meshindexesvbo ) ;
2015-09-20 20:10:49 +00:00
m - > meshindexesvbo = 0 ;
2017-09-17 02:12:53 +00:00
free ( m - > meshindexesvboptr ) ;
m - > meshindexesvboptr = NULL ;
2015-09-20 20:10:49 +00:00
}
2017-09-17 02:12:53 +00:00
Alias model rendering fast-path using OpenGL 2.
In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).
Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.
Special thanks to MH for some of the code from RMQEngine and the general concept.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
2015-01-20 18:59:15 +00:00
GL_ClearBufferBindings ( ) ;
}
2017-09-17 02:12:53 +00:00
//from gl_model.c
extern char loadname [ ] ; // for hunk tags
void Mod_CalcAliasBounds ( aliashdr_t * a ) ;
# define MD3_VERSION 15
//structures from Tenebrae
typedef struct {
int ident ;
int version ;
char name [ 64 ] ;
int flags ; //assumed to match quake1 models, for lack of somewhere better.
int numFrames ;
int numTags ;
int numSurfaces ;
int numSkins ;
int ofsFrames ;
int ofsTags ;
int ofsSurfaces ;
int ofsEnd ;
} md3Header_t ;
//then has header->numFrames of these at header->ofs_Frames
typedef struct md3Frame_s {
vec3_t bounds [ 2 ] ;
vec3_t localOrigin ;
float radius ;
char name [ 16 ] ;
} md3Frame_t ;
//there are header->numSurfaces of these at header->ofsSurfaces, following from ofsEnd
typedef struct {
int ident ; //
char name [ 64 ] ; // polyset name
int flags ;
int numFrames ; // all surfaces in a model should have the same
int numShaders ; // all surfaces in a model should have the same
int numVerts ;
int numTriangles ;
int ofsTriangles ;
int ofsShaders ; // offset from start of md3Surface_t
int ofsSt ; // texture coords are common for all frames
int ofsXyzNormals ; // numVerts * numFrames
int ofsEnd ; // next surface follows
} md3Surface_t ;
//at surf+surf->ofsXyzNormals
/*typedef struct {
short xyz [ 3 ] ;
byte latlong [ 2 ] ;
} md3XyzNormal_t ; */
//surf->numTriangles at surf+surf->ofsTriangles
typedef struct {
int indexes [ 3 ] ;
} md3Triangle_t ;
//surf->numVerts at surf+surf->ofsSt
typedef struct {
float s ;
float t ;
} md3St_t ;
typedef struct {
char name [ 64 ] ;
int shaderIndex ;
} md3Shader_t ;
void Mod_LoadMD3Model ( qmodel_t * mod , void * buffer )
{
md3Header_t * pinheader ;
md3Surface_t * pinsurface ;
md3Frame_t * pinframes ;
md3Triangle_t * pintriangle ;
unsigned short * poutindexes ;
md3XyzNormal_t * pinvert ;
md3XyzNormal_t * poutvert ;
md3St_t * pinst ;
aliasmesh_t * poutst ;
md3Shader_t * pinshader ;
int size ;
int start , end , total ;
int ival , j ;
int numsurfs , surf ;
int numframes ;
aliashdr_t * outhdr ;
start = Hunk_LowMark ( ) ;
pinheader = ( md3Header_t * ) buffer ;
ival = LittleLong ( pinheader - > version ) ;
if ( ival ! = MD3_VERSION )
Sys_Error ( " %s has wrong version number (%i should be %i) " ,
mod - > name , ival , MD3_VERSION ) ;
numsurfs = LittleLong ( pinheader - > numSurfaces ) ;
numframes = LittleLong ( pinheader - > numFrames ) ;
if ( numframes > MAXALIASFRAMES )
Sys_Error ( " %s has too many frames (%i vs %i) " ,
mod - > name , numframes , MAXALIASFRAMES ) ;
if ( ! numsurfs )
Sys_Error ( " %s has nosurfaces " , mod - > name ) ;
pinframes = ( md3Frame_t * ) ( ( byte * ) buffer + LittleLong ( pinheader - > ofsFrames ) ) ;
//
// allocate space for a working header, plus all the data except the frames,
// skin and group info
//
size = sizeof ( aliashdr_t ) + ( numframes - 1 ) * sizeof ( outhdr - > frames [ 0 ] ) ;
outhdr = ( aliashdr_t * ) Hunk_AllocName ( size * numsurfs , loadname ) ;
for ( surf = 0 , pinsurface = ( md3Surface_t * ) ( ( byte * ) buffer + LittleLong ( pinheader - > ofsSurfaces ) ) ; surf < numsurfs ; surf + + , pinsurface = ( md3Surface_t * ) ( ( byte * ) pinsurface + LittleLong ( pinsurface - > ofsEnd ) ) )
{
aliashdr_t * osurf = ( aliashdr_t * ) ( ( byte * ) outhdr + size * surf ) ;
if ( LittleLong ( pinsurface - > ident ) ! = ( ( ' I ' < < 0 ) | ( ' D ' < < 8 ) | ( ' P ' < < 16 ) | ( ' 3 ' < < 24 ) ) )
Sys_Error ( " %s corrupt surface ident " , mod - > name ) ;
if ( LittleLong ( pinsurface - > numFrames ) ! = numframes )
Sys_Error ( " %s mismatched framecounts " , mod - > name ) ;
if ( surf + 1 < numsurfs )
osurf - > nextsurface = size ;
else
osurf - > nextsurface = 0 ;
2019-01-30 01:54:49 +00:00
osurf - > poseverttype = PV_QUAKE3 ;
2017-09-17 02:12:53 +00:00
osurf - > numverts_vbo = osurf - > numverts = LittleLong ( pinsurface - > numVerts ) ;
pinvert = ( md3XyzNormal_t * ) ( ( byte * ) pinsurface + LittleLong ( pinsurface - > ofsXyzNormals ) ) ;
poutvert = ( md3XyzNormal_t * ) Hunk_Alloc ( numframes * osurf - > numverts * sizeof ( * poutvert ) ) ;
osurf - > vertexes = ( byte * ) poutvert - ( byte * ) osurf ;
for ( ival = 0 ; ival < numframes ; ival + + )
{
osurf - > frames [ ival ] . firstpose = ival ;
osurf - > frames [ ival ] . numposes = 1 ;
osurf - > frames [ ival ] . interval = 0.1 ;
q_strlcpy ( osurf - > frames [ ival ] . name , pinframes - > name , sizeof ( osurf - > frames [ ival ] . name ) ) ;
for ( j = 0 ; j < 3 ; j + + )
{ //fixme...
osurf - > frames [ ival ] . bboxmin . v [ j ] = 0 ;
osurf - > frames [ ival ] . bboxmax . v [ j ] = 255 ;
}
for ( j = 0 ; j < osurf - > numverts ; j + + )
poutvert [ j ] = pinvert [ j ] ;
poutvert + = osurf - > numverts ;
pinvert + = osurf - > numverts ;
}
2019-11-02 15:50:53 +00:00
osurf - > nummorphposes = osurf - > numframes = numframes ;
2017-09-17 02:12:53 +00:00
osurf - > numtris = LittleLong ( pinsurface - > numTriangles ) ;
osurf - > numindexes = osurf - > numtris * 3 ;
pintriangle = ( md3Triangle_t * ) ( ( byte * ) pinsurface + LittleLong ( pinsurface - > ofsTriangles ) ) ;
poutindexes = ( unsigned short * ) Hunk_Alloc ( sizeof ( * poutindexes ) * osurf - > numindexes ) ;
osurf - > indexes = ( intptr_t ) poutindexes - ( intptr_t ) osurf ;
for ( ival = 0 ; ival < osurf - > numtris ; ival + + , pintriangle + + , poutindexes + = 3 )
{
for ( j = 0 ; j < 3 ; j + + )
poutindexes [ j ] = LittleLong ( pintriangle - > indexes [ j ] ) ;
}
for ( j = 0 ; j < 3 ; j + + )
{
osurf - > scale_origin [ j ] = 0 ;
osurf - > scale [ j ] = 1 / 64.0 ;
}
//guess at skin sizes
osurf - > skinwidth = 320 ;
osurf - > skinheight = 200 ;
//load the textures
2019-02-10 04:03:04 +00:00
if ( ! isDedicated )
2017-09-17 02:12:53 +00:00
{
2019-02-10 04:03:04 +00:00
pinshader = ( md3Shader_t * ) ( ( byte * ) pinsurface + LittleLong ( pinsurface - > ofsShaders ) ) ;
osurf - > numskins = LittleLong ( pinsurface - > numShaders ) ;
for ( j = 0 ; j < osurf - > numskins ; j + + , pinshader + + )
{
char texturename [ MAX_QPATH ] ;
char fullbrightname [ MAX_QPATH ] ;
char * ext ;
//texture names in md3s are kinda fucked. they could be just names relative to the mdl, or full paths, or just simple shader names.
//our texture manager is too lame to scan all 1000 possibilities
if ( strchr ( pinshader - > name , ' / ' ) | | strchr ( pinshader - > name , ' \\ ' ) )
{ //so if there's a path then we want to use that.
q_strlcpy ( texturename , pinshader - > name , sizeof ( texturename ) ) ;
}
else
{ //and if there's no path then we want to prefix it with our own.
q_strlcpy ( texturename , mod - > name , sizeof ( texturename ) ) ;
* ( char * ) COM_SkipPath ( texturename ) = 0 ;
//and concat the specified name
q_strlcat ( texturename , pinshader - > name , sizeof ( texturename ) ) ;
}
//and make sure there's no extensions. these get ignored in q3, which is kinda annoying, but this is an md3 and standards are standards (and it makes luma easier).
ext = ( char * ) COM_FileGetExtension ( texturename ) ;
if ( * ext )
* - - ext = 0 ;
//luma has an extra postfix.
q_snprintf ( fullbrightname , sizeof ( fullbrightname ) , " %s_luma " , texturename ) ;
osurf - > gltextures [ j ] [ 0 ] = TexMgr_LoadImage ( mod , texturename , osurf - > skinwidth , osurf - > skinheight , SRC_EXTERNAL , NULL , texturename , 0 , TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOBRIGHT | TEXPREF_MIPMAP ) ;
osurf - > fbtextures [ j ] [ 0 ] = TexMgr_LoadImage ( mod , fullbrightname , osurf - > skinwidth , osurf - > skinheight , SRC_EXTERNAL , NULL , texturename , 0 , TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_FULLBRIGHT | TEXPREF_MIPMAP ) ;
osurf - > gltextures [ j ] [ 3 ] = osurf - > gltextures [ j ] [ 2 ] = osurf - > gltextures [ j ] [ 1 ] = osurf - > gltextures [ j ] [ 0 ] ;
osurf - > fbtextures [ j ] [ 3 ] = osurf - > fbtextures [ j ] [ 2 ] = osurf - > fbtextures [ j ] [ 1 ] = osurf - > fbtextures [ j ] [ 0 ] ;
2017-09-17 02:12:53 +00:00
}
2019-02-10 04:03:04 +00:00
if ( osurf - > numskins )
{
osurf - > skinwidth = osurf - > gltextures [ 0 ] [ 0 ] - > source_width ;
osurf - > skinheight = osurf - > gltextures [ 0 ] [ 0 ] - > source_height ;
2017-09-17 02:12:53 +00:00
}
}
//and figure out the texture coords properly, now we know the actual sizes.
pinst = ( md3St_t * ) ( ( byte * ) pinsurface + LittleLong ( pinsurface - > ofsSt ) ) ;
poutst = ( aliasmesh_t * ) Hunk_Alloc ( sizeof ( * poutst ) * osurf - > numverts ) ;
osurf - > meshdesc = ( intptr_t ) poutst - ( intptr_t ) osurf ;
for ( j = 0 ; j < osurf - > numverts ; j + + )
{
poutst [ j ] . vertindex = j ; //how is this useful?
2022-01-30 03:58:46 +00:00
poutst [ j ] . st [ 0 ] = pinst [ j ] . s ;
poutst [ j ] . st [ 1 ] = pinst [ j ] . t ;
2017-09-17 02:12:53 +00:00
}
}
GLMesh_LoadVertexBuffer ( mod , outhdr ) ;
//small violation of the spec, but it seems like noone else uses it.
mod - > flags = LittleLong ( pinheader - > flags ) ;
mod - > type = mod_alias ;
Mod_CalcAliasBounds ( outhdr ) ; //johnfitz
//
// move the complete, relocatable alias model to the cache
//
end = Hunk_LowMark ( ) ;
total = end - start ;
Cache_Alloc ( & mod - > cache , total , loadname ) ;
if ( ! mod - > cache . data )
return ;
memcpy ( mod - > cache . data , outhdr , total ) ;
Hunk_FreeToLowMark ( start ) ;
}
2019-11-02 15:50:53 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
InterQuake Models .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Header :
*/
//Copyright (c) 2010-2019 Lee Salzman
//MIT License etc at: https://github.com/lsalzman/iqm
# define IQM_MAGIC "INTERQUAKEMODEL"
# define IQM_VERSION 2
struct iqmheader
{
char magic [ 16 ] ;
unsigned int version ;
unsigned int filesize ;
unsigned int flags ;
unsigned int num_text , ofs_text ; //text strings
unsigned int num_meshes , ofs_meshes ; //surface info
unsigned int num_vertexarrays , num_vertexes , ofs_vertexarrays ; //for loading vertex data
unsigned int num_triangles , ofs_triangles , ofs_adjacency ; //the index data+neighbours(which we ignore)
unsigned int num_joints , ofs_joints ; //mesh joints (base pose info)
unsigned int num_poses , ofs_poses ; //animated joints (num_poses should match num_joints)
unsigned int num_anims , ofs_anims ; //animations info
unsigned int num_frames , num_framechannels , ofs_frames , ofs_bounds ; //the actual per-pose(aka:single-frame) data
unsigned int num_comment , ofs_comment ; //extra stuff
unsigned int num_extensions , ofs_extensions ; //extra stuff
} ;
struct iqmmesh
{
unsigned int name ;
unsigned int material ;
unsigned int first_vertex , num_vertexes ;
unsigned int first_triangle , num_triangles ;
} ;
enum
{
IQM_POSITION = 0 ,
IQM_TEXCOORD = 1 ,
IQM_NORMAL = 2 ,
IQM_TANGENT = 3 ,
IQM_BLENDINDEXES = 4 ,
IQM_BLENDWEIGHTS = 5 ,
IQM_COLOR = 6 ,
IQM_CUSTOM = 0x10
} ;
enum
{
IQM_BYTE = 0 ,
IQM_UBYTE = 1 ,
IQM_SHORT = 2 ,
IQM_USHORT = 3 ,
IQM_INT = 4 ,
IQM_UINT = 5 ,
IQM_HALF = 6 ,
IQM_FLOAT = 7 ,
IQM_DOUBLE = 8
} ;
/*struct iqmtriangle
{
unsigned int vertex [ 3 ] ;
} ;
struct iqmadjacency
{
unsigned int triangle [ 3 ] ;
} ;
struct iqmjointv1
{
unsigned int name ;
int parent ;
float translate [ 3 ] , rotate [ 3 ] , scale [ 3 ] ;
} ; */
struct iqmjoint
{
unsigned int name ;
int parent ;
float translate [ 3 ] , rotate [ 4 ] , scale [ 3 ] ;
} ;
/*struct iqmposev1
{
int parent ;
unsigned int mask ;
float channeloffset [ 9 ] ;
float channelscale [ 9 ] ;
} ; */
struct iqmpose
{
int parent ;
unsigned int mask ;
float channeloffset [ 10 ] ;
float channelscale [ 10 ] ;
} ;
struct iqmanim
{
unsigned int name ;
unsigned int first_frame , num_frames ;
float framerate ;
unsigned int flags ;
} ;
enum
{
IQM_LOOP = 1 < < 0
} ;
struct iqmvertexarray
{
unsigned int type ;
unsigned int flags ;
unsigned int format ;
unsigned int size ;
unsigned int offset ;
} ;
/*struct iqmbounds
{
float bbmin [ 3 ] , bbmax [ 3 ] ;
float xyradius , radius ;
2021-09-03 07:11:57 +00:00
} ; */
2019-11-02 15:50:53 +00:00
struct iqmextension
{
unsigned int name ;
unsigned int num_data , ofs_data ;
unsigned int ofs_extensions ; // pointer to next extension
2021-09-03 07:11:57 +00:00
} ;
//skin lump is made of 3 parts
struct iqmext_fte_skin
{
unsigned int numskinframes ;
unsigned int nummeshskins ;
//unsigned int numskins[nummeshes];
//iqmext_fte_skin_skinframe[numskinframes];
//iqmext_fte_skin_meshskin mesh0[numskins[0]];
//iqmext_fte_skin_meshskin mesh1[numskins[1]]; etc
} ;
struct iqmext_fte_skin_skinframe
{ //as many as needed
unsigned int material_idx ;
unsigned int shadertext_idx ;
} ;
struct iqmext_fte_skin_meshskin
{
unsigned int firstframe ; //index into skinframes
unsigned int countframes ; //skinframes
float interval ;
} ;
2019-11-02 15:50:53 +00:00
//IQM Implementation: Copyright 2019 spike, licensed like the rest of quakespasm.
static void IQM_LoadVertexes_Float ( float * o , size_t c , size_t numverts , const byte * buffer , const struct iqmvertexarray * va )
{
size_t j , k ;
if ( c ! = va - > size )
return ; //erk, too lazy to handle weirdness.
switch ( va - > format )
{
// case IQM_BYTE:
case IQM_UBYTE :
{ //weights+colours are often normalised bytes.
const byte * in = ( const byte * ) ( buffer + va - > offset ) ;
for ( j = 0 ; j < numverts ; j + + , in + = va - > size , o + = sizeof ( iqmvert_t ) / sizeof ( * o ) )
{
for ( k = 0 ; k < c ; k + + )
o [ k ] = in [ k ] / 255.0 ;
}
}
break ;
// case IQM_SHORT:
// case IQM_USHORT:
// case IQM_INT:
// case IQM_UINT:
// case IQM_HALF:
case IQM_FLOAT :
{
const float * in = ( const float * ) ( buffer + va - > offset ) ;
for ( j = 0 ; j < numverts ; j + + , in + = va - > size , o + = sizeof ( iqmvert_t ) / sizeof ( * o ) )
{
for ( k = 0 ; k < c ; k + + )
o [ k ] = in [ k ] ;
}
}
break ;
case IQM_DOUBLE :
{ //truncate, sorry...
const double * in = ( const double * ) ( buffer + va - > offset ) ;
for ( j = 0 ; j < numverts ; j + + , in + = va - > size , o + = sizeof ( iqmvert_t ) / sizeof ( * o ) )
{
for ( k = 0 ; k < c ; k + + )
o [ k ] = in [ k ] ;
}
}
break ;
default :
return ; //oh bum. my laziness strikes again.
}
}
static void IQM_LoadVertexes_Index ( byte * o , size_t c , size_t numverts , const byte * buffer , const struct iqmvertexarray * va )
{
size_t j , k ;
if ( c ! = va - > size )
return ; //erk, too lazy to handle weirdness.
switch ( va - > format )
{
// case IQM_BYTE:
case IQM_UBYTE :
{
const byte * in = ( const byte * ) ( buffer + va - > offset ) ;
for ( j = 0 ; j < numverts ; j + + , in + = va - > size , o + = sizeof ( iqmvert_t ) / sizeof ( * o ) )
{
for ( k = 0 ; k < c ; k + + )
o [ k ] = in [ k ] ;
}
}
break ;
// case IQM_SHORT:
case IQM_USHORT :
{ //truncate...
const unsigned short * in = ( const unsigned short * ) ( buffer + va - > offset ) ;
for ( j = 0 ; j < numverts ; j + + , in + = va - > size , o + = sizeof ( iqmvert_t ) / sizeof ( * o ) )
{
for ( k = 0 ; k < c ; k + + )
o [ k ] = in [ k ] ;
}
}
break ;
// case IQM_INT:
case IQM_UINT :
{ //truncate... noesis likes writing these.
const unsigned int * in = ( const unsigned int * ) ( buffer + va - > offset ) ;
for ( j = 0 ; j < numverts ; j + + , in + = va - > size , o + = sizeof ( iqmvert_t ) / sizeof ( * o ) )
{
for ( k = 0 ; k < c ; k + + )
o [ k ] = in [ k ] ;
}
}
break ;
// case IQM_HALF:
// case IQM_FLOAT:
// case IQM_DOUBLE:
default :
return ; //oh bum. my laziness strikes again.
}
}
static void GenMatrixPosQuat4Scale ( const vec3_t pos , const vec4_t quat , const vec3_t scale , float result [ 12 ] )
{
float xx , xy , xz , xw , yy , yz , yw , zz , zw ;
float x2 , y2 , z2 ;
float s ;
x2 = quat [ 0 ] + quat [ 0 ] ;
y2 = quat [ 1 ] + quat [ 1 ] ;
z2 = quat [ 2 ] + quat [ 2 ] ;
xx = quat [ 0 ] * x2 ; xy = quat [ 0 ] * y2 ; xz = quat [ 0 ] * z2 ;
yy = quat [ 1 ] * y2 ; yz = quat [ 1 ] * z2 ; zz = quat [ 2 ] * z2 ;
xw = quat [ 3 ] * x2 ; yw = quat [ 3 ] * y2 ; zw = quat [ 3 ] * z2 ;
s = scale [ 0 ] ;
result [ 0 * 4 + 0 ] = s * ( 1.0f - ( yy + zz ) ) ;
result [ 1 * 4 + 0 ] = s * ( xy + zw ) ;
result [ 2 * 4 + 0 ] = s * ( xz - yw ) ;
s = scale [ 1 ] ;
result [ 0 * 4 + 1 ] = s * ( xy - zw ) ;
result [ 1 * 4 + 1 ] = s * ( 1.0f - ( xx + zz ) ) ;
result [ 2 * 4 + 1 ] = s * ( yz + xw ) ;
s = scale [ 2 ] ;
result [ 0 * 4 + 2 ] = s * ( xz + yw ) ;
result [ 1 * 4 + 2 ] = s * ( yz - xw ) ;
result [ 2 * 4 + 2 ] = s * ( 1.0f - ( xx + yy ) ) ;
result [ 0 * 4 + 3 ] = pos [ 0 ] ;
result [ 1 * 4 + 3 ] = pos [ 1 ] ;
result [ 2 * 4 + 3 ] = pos [ 2 ] ;
}
static void Matrix3x4_Invert_Simple ( const float * in1 , float * out )
{
// we only support uniform scaling, so assume the first row is enough
// (note the lack of sqrt here, because we're trying to undo the scaling,
// this means multiplying by the inverse scale twice - squaring it, which
// makes the sqrt a waste of time)
# if 1
double scale = 1.0 / ( in1 [ 0 ] * in1 [ 0 ] + in1 [ 1 ] * in1 [ 1 ] + in1 [ 2 ] * in1 [ 2 ] ) ;
# else
double scale = 3.0 / sqrt
( in1 - > m [ 0 ] [ 0 ] * in1 - > m [ 0 ] [ 0 ] + in1 - > m [ 0 ] [ 1 ] * in1 - > m [ 0 ] [ 1 ] + in1 - > m [ 0 ] [ 2 ] * in1 - > m [ 0 ] [ 2 ]
+ in1 - > m [ 1 ] [ 0 ] * in1 - > m [ 1 ] [ 0 ] + in1 - > m [ 1 ] [ 1 ] * in1 - > m [ 1 ] [ 1 ] + in1 - > m [ 1 ] [ 2 ] * in1 - > m [ 1 ] [ 2 ]
+ in1 - > m [ 2 ] [ 0 ] * in1 - > m [ 2 ] [ 0 ] + in1 - > m [ 2 ] [ 1 ] * in1 - > m [ 2 ] [ 1 ] + in1 - > m [ 2 ] [ 2 ] * in1 - > m [ 2 ] [ 2 ] ) ;
scale * = scale ;
# endif
// invert the rotation by transposing and multiplying by the squared
// recipricol of the input matrix scale as described above
out [ 0 ] = in1 [ 0 ] * scale ;
out [ 1 ] = in1 [ 4 ] * scale ;
out [ 2 ] = in1 [ 8 ] * scale ;
out [ 4 ] = in1 [ 1 ] * scale ;
out [ 5 ] = in1 [ 5 ] * scale ;
out [ 6 ] = in1 [ 9 ] * scale ;
out [ 8 ] = in1 [ 2 ] * scale ;
out [ 9 ] = in1 [ 6 ] * scale ;
out [ 10 ] = in1 [ 10 ] * scale ;
// invert the translate
out [ 3 ] = - ( in1 [ 3 ] * out [ 0 ] + in1 [ 7 ] * out [ 1 ] + in1 [ 11 ] * out [ 2 ] ) ;
out [ 7 ] = - ( in1 [ 3 ] * out [ 4 ] + in1 [ 7 ] * out [ 5 ] + in1 [ 11 ] * out [ 6 ] ) ;
out [ 11 ] = - ( in1 [ 3 ] * out [ 8 ] + in1 [ 7 ] * out [ 9 ] + in1 [ 11 ] * out [ 10 ] ) ;
}
2021-09-03 07:11:57 +00:00
static const void * IQM_FindExtension ( const char * buffer , size_t buffersize , const char * extname , int index , size_t * extsize )
{
const struct iqmheader * h = ( const struct iqmheader * ) buffer ;
const char * strings = buffer + h - > ofs_text ;
const struct iqmextension * ext ;
int i ;
for ( i = 0 , ext = ( const struct iqmextension * ) ( buffer + h - > ofs_extensions ) ; i < h - > num_extensions ; i + + , ext = ( const struct iqmextension * ) ( buffer + ext - > ofs_extensions ) )
{
if ( ( const char * ) ext > buffer + buffersize | | ext - > name > h - > num_text | | ext - > ofs_data + ext - > num_data > buffersize )
break ;
if ( ! q_strcasecmp ( strings + ext - > name , extname ) & & index - - > = 0 )
{
* extsize = ext - > num_data ;
return buffer + ext - > ofs_data ;
}
}
* extsize = 0 ;
return NULL ;
}
static void Mod_LoadIQMSkin ( qmodel_t * mod , const struct iqmheader * pinheader , aliashdr_t * osurf , unsigned int meshidx , unsigned int nummeshes , const char * fallback )
{
unsigned int j , k ;
size_t extsize ;
const struct iqmext_fte_skin * iqmext = IQM_FindExtension ( ( const char * ) pinheader , pinheader - > filesize , " FTE_SKINS " , 0 , & extsize ) ;
if ( iqmext )
{
const struct iqmext_fte_skin_skinframe * skinframe = ( const struct iqmext_fte_skin_skinframe * ) ( ( const unsigned int * ) ( iqmext + 1 ) + nummeshes ) , * sf ;
const struct iqmext_fte_skin_meshskin * skin = ( const struct iqmext_fte_skin_meshskin * ) ( skinframe + iqmext - > numskinframes ) ;
osurf - > numskins = ( ( const unsigned int * ) ( iqmext + 1 ) ) [ meshidx ] ;
for ( j = 0 ; j < meshidx ; j + + )
skin + = ( ( const unsigned int * ) ( iqmext + 1 ) ) [ j ] ;
for ( j = 0 ; j < osurf - > numskins & & j < MAX_SKINS ; j + + , skin + + )
{
if ( ! skin - > countframes )
break ; //doesn't make sense.
if ( skin - > firstframe + skin - > countframes > iqmext - > numskinframes )
break ; //some kind of error
sf = skinframe + skin - > firstframe ;
for ( k = 0 ; k < skin - > countframes & & k < 4 ; k + + , sf + + )
{
const char * texturename = ( const char * ) pinheader + pinheader - > ofs_text + sf - > material_idx ;
char hackytexturename [ MAX_QPATH ] ;
COM_StripExtension ( texturename , hackytexturename , sizeof ( hackytexturename ) ) ;
osurf - > gltextures [ j ] [ k ] = TexMgr_LoadImage ( mod , texturename , osurf - > skinwidth , osurf - > skinheight , SRC_EXTERNAL , NULL , hackytexturename , 0 , TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOBRIGHT | TEXPREF_MIPMAP ) ;
osurf - > fbtextures [ j ] [ k ] = NULL ; //TexMgr_LoadImage(mod, fullbrightname, osurf->skinwidth, osurf->skinheight, SRC_EXTERNAL, NULL, fullbrightname, 0, TEXPREF_PAD|TEXPREF_ALPHA|TEXPREF_FULLBRIGHT|TEXPREF_MIPMAP);
}
for ( ; k < 4 ; k + + )
{
osurf - > gltextures [ j ] [ k ] = osurf - > gltextures [ j ] [ k % skin - > countframes ] ;
osurf - > fbtextures [ j ] [ k ] = osurf - > fbtextures [ j ] [ k % skin - > countframes ] ;
}
}
osurf - > numskins = j ;
}
else
{
osurf - > numskins = 1 ;
for ( j = 0 ; j < 1 ; j + + )
{
char texturename [ MAX_QPATH ] ;
char fullbrightname [ MAX_QPATH ] ;
char * ext ;
//texture names in md3s are kinda fucked. they could be just names relative to the mdl, or full paths, or just simple shader names.
//our texture manager is too lame to scan all 1000 possibilities
if ( strchr ( fallback , ' / ' ) | | strchr ( fallback , ' \\ ' ) )
{ //so if there's a path then we want to use that.
q_strlcpy ( texturename , fallback , sizeof ( texturename ) ) ;
}
else
{ //and if there's no path then we want to prefix it with our own.
q_strlcpy ( texturename , mod - > name , sizeof ( texturename ) ) ;
* ( char * ) COM_SkipPath ( texturename ) = 0 ;
//and concat the specified name
q_strlcat ( texturename , fallback , sizeof ( texturename ) ) ;
}
//and make sure there's no extensions. these get ignored in q3, which is kinda annoying, but this is an md3 and standards are standards (and it makes luma easier).
ext = ( char * ) COM_FileGetExtension ( texturename ) ;
if ( * ext )
* - - ext = 0 ;
//luma has an extra postfix.
q_snprintf ( fullbrightname , sizeof ( fullbrightname ) , " %s_luma " , texturename ) ;
osurf - > gltextures [ j ] [ 0 ] = TexMgr_LoadImage ( mod , texturename , osurf - > skinwidth , osurf - > skinheight , SRC_EXTERNAL , NULL , texturename , 0 , TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOBRIGHT | TEXPREF_MIPMAP ) ;
osurf - > fbtextures [ j ] [ 0 ] = NULL ; //TexMgr_LoadImage(mod, fullbrightname, osurf->skinwidth, osurf->skinheight, SRC_EXTERNAL, NULL, fullbrightname, 0, TEXPREF_PAD|TEXPREF_ALPHA|TEXPREF_FULLBRIGHT|TEXPREF_MIPMAP);
osurf - > gltextures [ j ] [ 3 ] = osurf - > gltextures [ j ] [ 2 ] = osurf - > gltextures [ j ] [ 1 ] = osurf - > gltextures [ j ] [ 0 ] ;
osurf - > fbtextures [ j ] [ 3 ] = osurf - > fbtextures [ j ] [ 2 ] = osurf - > fbtextures [ j ] [ 1 ] = osurf - > fbtextures [ j ] [ 0 ] ;
}
}
if ( osurf - > numskins )
{
osurf - > skinwidth = osurf - > gltextures [ 0 ] [ 0 ] - > source_width ;
osurf - > skinheight = osurf - > gltextures [ 0 ] [ 0 ] - > source_height ;
}
}
2019-11-02 15:50:53 +00:00
void Mod_LoadIQMModel ( qmodel_t * mod , const void * buffer )
{
const struct iqmheader * pinheader ;
const char * pintext ;
const struct iqmmesh * pinsurface ;
const struct iqmanim * pinframes ;
const unsigned int * pintriangle ;
unsigned short * poutindexes ;
iqmvert_t * poutvert ;
int size ;
int start , end , total ;
int ival , j , a ;
int numsurfs , surf ;
aliashdr_t * outhdr ;
int numverts , firstidx , firstvert ;
int numanims ;
bonepose_t * outposes ;
boneinfo_t * outbones ;
int numposes , numjoints ;
start = Hunk_LowMark ( ) ;
pinheader = ( const struct iqmheader * ) buffer ;
if ( strcmp ( pinheader - > magic , IQM_MAGIC ) )
Sys_Error ( " %s has invalid magic for iqm file " , mod - > name ) ;
if ( LittleLong ( pinheader - > version ) ! = IQM_VERSION ) //v1 is outdated.
Sys_Error ( " %s is an unsupported version, %i must be %i " , mod - > name , LittleLong ( pinheader - > version ) , IQM_VERSION ) ;
2020-01-12 08:04:55 +00:00
pintext = ( const char * ) buffer + LittleLong ( pinheader - > ofs_text ) ;
2019-11-02 15:50:53 +00:00
numsurfs = LittleLong ( pinheader - > num_meshes ) ;
if ( ! numsurfs )
Sys_Error ( " %s has no surfaces (animation-only iqms are not supported) " , mod - > name ) ;
if ( pinheader - > num_vertexes > 0xffff ) //indexes is an unsigned short.
Sys_Error ( " %s has too many verts (%u>%u) " , mod - > name , pinheader - > num_vertexes , 0xffffu ) ;
numanims = LittleLong ( pinheader - > num_anims ) ;
size = sizeof ( aliashdr_t ) + q_max ( 1 , numanims - 1 ) * sizeof ( outhdr - > frames [ 0 ] ) ;
outhdr = ( aliashdr_t * ) Hunk_AllocName ( size * numsurfs , loadname ) ;
numverts = LittleLong ( pinheader - > num_vertexes ) ;
poutvert = ( iqmvert_t * ) Hunk_Alloc ( sizeof ( * poutvert ) * numverts ) ;
for ( j = 0 ; j < numverts ; j + + ) //initialise verts, just in case.
poutvert [ j ] . rgba [ 0 ] = poutvert [ j ] . rgba [ 1 ] = poutvert [ j ] . rgba [ 2 ] = poutvert [ j ] . rgba [ 3 ] = poutvert [ j ] . weight [ 0 ] = 1 ;
for ( a = 0 ; a < LittleLong ( pinheader - > num_vertexarrays ) ; a + + )
{
2020-01-12 08:04:55 +00:00
const struct iqmvertexarray * va = ( const struct iqmvertexarray * ) ( ( const byte * ) buffer + LittleLong ( pinheader - > ofs_vertexarrays ) ) + a ;
2019-11-02 15:50:53 +00:00
switch ( va - > type )
{
case IQM_POSITION : IQM_LoadVertexes_Float ( poutvert - > xyz , 3 , numverts , buffer , va ) ; break ;
case IQM_TEXCOORD : IQM_LoadVertexes_Float ( poutvert - > st , 2 , numverts , buffer , va ) ; break ;
case IQM_NORMAL : IQM_LoadVertexes_Float ( poutvert - > norm , 3 , numverts , buffer , va ) ; break ;
//case IQM_TANGENT: IQM_LoadVertexes_Float(poutvert->tang, 4, numverts, buffer, va); break; //bitangent must be calced using a crossproduct and the fourth component (for direction). we don't need this (unless you want rtlights or bumpmaps)
case IQM_COLOR : IQM_LoadVertexes_Float ( poutvert - > rgba , 4 , numverts , buffer , va ) ; break ;
case IQM_BLENDINDEXES : IQM_LoadVertexes_Index ( poutvert - > idx , 4 , numverts , buffer , va ) ; break ;
case IQM_BLENDWEIGHTS : IQM_LoadVertexes_Float ( poutvert - > weight , 4 , numverts , buffer , va ) ; break ;
default :
continue ; //no idea what it is. probably custom
}
}
numposes = LittleLong ( pinheader - > num_frames ) ;
numjoints = LittleLong ( pinheader - > num_poses ) ;
if ( pinheader - > num_poses = = pinheader - > num_joints )
{
2020-01-12 08:04:55 +00:00
const unsigned short * pinframedata = ( const unsigned short * ) ( ( const byte * ) buffer + pinheader - > ofs_frames ) ;
const struct iqmpose * pinajoint = ( const struct iqmpose * ) ( ( const byte * ) buffer + pinheader - > ofs_poses ) , * p ;
2019-11-02 15:50:53 +00:00
vec3_t pos , scale ;
vec4_t quat ;
outposes = Hunk_Alloc ( sizeof ( * outposes ) * numposes * numjoints ) ;
for ( a = 0 ; a < numposes ; a + + )
{
for ( j = 0 , p = pinajoint ; j < numjoints ; j + + , p + + )
{
unsigned int mask = LittleLong ( p - > mask ) ;
pos [ 0 ] = LittleFloat ( p - > channeloffset [ 0 ] ) ; if ( mask & 1 ) pos [ 0 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 0 ] ) ;
pos [ 1 ] = LittleFloat ( p - > channeloffset [ 1 ] ) ; if ( mask & 2 ) pos [ 1 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 1 ] ) ;
pos [ 2 ] = LittleFloat ( p - > channeloffset [ 2 ] ) ; if ( mask & 4 ) pos [ 2 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 2 ] ) ;
quat [ 0 ] = LittleFloat ( p - > channeloffset [ 3 ] ) ; if ( mask & 8 ) quat [ 0 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 3 ] ) ;
quat [ 1 ] = LittleFloat ( p - > channeloffset [ 4 ] ) ; if ( mask & 16 ) quat [ 1 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 4 ] ) ;
quat [ 2 ] = LittleFloat ( p - > channeloffset [ 5 ] ) ; if ( mask & 32 ) quat [ 2 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 5 ] ) ;
quat [ 3 ] = LittleFloat ( p - > channeloffset [ 6 ] ) ; if ( mask & 64 ) quat [ 3 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 6 ] ) ;
scale [ 0 ] = LittleFloat ( p - > channeloffset [ 7 ] ) ; if ( mask & 128 ) scale [ 0 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 7 ] ) ;
scale [ 1 ] = LittleFloat ( p - > channeloffset [ 8 ] ) ; if ( mask & 256 ) scale [ 1 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 8 ] ) ;
scale [ 2 ] = LittleFloat ( p - > channeloffset [ 9 ] ) ; if ( mask & 512 ) scale [ 2 ] + = ( unsigned short ) LittleShort ( * pinframedata + + ) * LittleFloat ( p - > channelscale [ 9 ] ) ;
//fixme: should probably save the 10 values above and slerp, but its simpler to just save+lerp a matrix (although this does result in denormalisation when interpolating).
GenMatrixPosQuat4Scale ( pos , quat , scale , outposes [ ( a * numjoints + j ) ] . mat ) ;
}
}
}
else
{ //panic! panic! something weird is going on!
numposes = 0 ;
numjoints = 0 ;
outposes = NULL ;
}
{
2020-01-12 08:04:55 +00:00
const struct iqmjoint * pinbjoint = ( const struct iqmjoint * ) ( ( const byte * ) buffer + pinheader - > ofs_joints ) ;
2019-11-02 15:50:53 +00:00
bonepose_t basepose [ 256 ] , rel ;
vec3_t pos , scale ;
vec4_t quat ;
outbones = Hunk_Alloc ( sizeof ( * outbones ) * numjoints ) ;
for ( j = 0 ; j < numjoints ; j + + )
{
outbones [ j ] . parent = LittleLong ( pinbjoint [ j ] . parent ) ;
q_strlcpy ( outbones [ j ] . name , pintext + LittleLong ( pinbjoint [ j ] . name ) , sizeof ( outbones [ j ] . name ) ) ;
pos [ 0 ] = LittleFloat ( pinbjoint [ j ] . translate [ 0 ] ) ;
pos [ 1 ] = LittleFloat ( pinbjoint [ j ] . translate [ 1 ] ) ;
pos [ 2 ] = LittleFloat ( pinbjoint [ j ] . translate [ 2 ] ) ;
quat [ 0 ] = LittleFloat ( pinbjoint [ j ] . rotate [ 0 ] ) ;
quat [ 1 ] = LittleFloat ( pinbjoint [ j ] . rotate [ 1 ] ) ;
quat [ 2 ] = LittleFloat ( pinbjoint [ j ] . rotate [ 2 ] ) ;
quat [ 3 ] = LittleFloat ( pinbjoint [ j ] . rotate [ 3 ] ) ;
scale [ 0 ] = LittleFloat ( pinbjoint [ j ] . scale [ 0 ] ) ;
scale [ 1 ] = LittleFloat ( pinbjoint [ j ] . scale [ 1 ] ) ;
scale [ 2 ] = LittleFloat ( pinbjoint [ j ] . scale [ 2 ] ) ;
GenMatrixPosQuat4Scale ( pos , quat , scale , rel . mat ) ;
//urgh, these are relative.
if ( outbones [ j ] . parent < 0 )
memcpy ( basepose [ j ] . mat , rel . mat , sizeof ( rel . mat ) ) ;
else
R_ConcatTransforms ( ( void * ) basepose [ outbones [ j ] . parent ] . mat , ( void * ) rel . mat , ( void * ) basepose [ j ] . mat ) ;
Matrix3x4_Invert_Simple ( basepose [ j ] . mat , outbones [ j ] . inverse . mat ) ;
//and now we have the inversion matrix to use to undo the bone positions baked into the vertex data.
}
}
mod - > numframes = q_max ( 1 , numanims ) ;
2020-01-12 08:04:55 +00:00
for ( surf = 0 , pinsurface = ( const struct iqmmesh * ) ( ( const byte * ) buffer + LittleLong ( pinheader - > ofs_meshes ) ) ; surf < numsurfs ; surf + + , pinsurface + + )
2019-11-02 15:50:53 +00:00
{
aliashdr_t * osurf = ( aliashdr_t * ) ( ( byte * ) outhdr + size * surf ) ;
if ( surf + 1 < numsurfs )
osurf - > nextsurface = size ;
else
osurf - > nextsurface = 0 ;
osurf - > poseverttype = PV_IQM ;
osurf - > numverts_vbo = osurf - > numverts = LittleLong ( pinsurface - > num_vertexes ) ;
firstvert = LittleLong ( pinsurface - > first_vertex ) ;
osurf - > vertexes = ( intptr_t ) ( poutvert + firstvert ) - ( intptr_t ) osurf ;
osurf - > numverts = LittleLong ( pinsurface - > num_vertexes ) ;
osurf - > nummorphposes = 1 ; //as a skeletal model, we do all our animations via bones rather than vertex morphs.
osurf - > numtris = LittleLong ( pinsurface - > num_triangles ) ;
osurf - > numindexes = osurf - > numtris * 3 ;
poutindexes = ( unsigned short * ) Hunk_Alloc ( sizeof ( * poutindexes ) * osurf - > numindexes ) ;
osurf - > indexes = ( intptr_t ) poutindexes - ( intptr_t ) osurf ;
2020-01-12 08:04:55 +00:00
pintriangle = ( const unsigned int * ) ( ( const byte * ) buffer + LittleLong ( pinheader - > ofs_triangles ) ) ;
2019-11-02 15:50:53 +00:00
firstidx = LittleLong ( pinsurface - > first_triangle ) * 3 ;
pintriangle + = firstidx ;
for ( j = 0 ; j < osurf - > numindexes ; j + + )
poutindexes [ j ] = pintriangle [ j ] - firstvert ;
2020-01-12 08:04:55 +00:00
pinframes = ( const struct iqmanim * ) ( ( const byte * ) buffer + pinheader - > ofs_anims ) ;
2019-11-02 15:50:53 +00:00
for ( a = 0 ; a < numanims ; a + + , pinframes + + )
{
osurf - > frames [ a ] . firstpose = LittleLong ( pinframes - > first_frame ) ;
osurf - > frames [ a ] . numposes = LittleLong ( pinframes - > num_frames ) ;
osurf - > frames [ a ] . interval = LittleFloat ( pinframes - > framerate ) ;
if ( ! osurf - > frames [ a ] . interval )
osurf - > frames [ a ] . interval = 20 ;
osurf - > frames [ a ] . interval = 1.0 / osurf - > frames [ a ] . interval ;
if ( LittleLong ( pinframes - > flags ) & IQM_LOOP )
/*FIXME*/ ;
q_strlcpy ( osurf - > frames [ a ] . name , pintext + LittleLong ( pinframes - > name ) , sizeof ( osurf - > frames [ ival ] . name ) ) ;
for ( j = 0 ; j < 3 ; j + + )
{ //fixme...
osurf - > frames [ a ] . bboxmin . v [ j ] = 0 ;
osurf - > frames [ a ] . bboxmax . v [ j ] = 255 ;
}
}
for ( ; a < 1 ; a + + , pinframes + + )
{ //unanimated models need to pick their morphpose without warnings.
osurf - > frames [ a ] . firstpose = 0 ;
osurf - > frames [ a ] . numposes = 1 ;
osurf - > frames [ a ] . interval = 0.1 ;
q_strlcpy ( osurf - > frames [ a ] . name , " " , sizeof ( osurf - > frames [ ival ] . name ) ) ;
for ( j = 0 ; j < 3 ; j + + )
{ //fixme...
osurf - > frames [ a ] . bboxmin . v [ j ] = 0 ;
osurf - > frames [ a ] . bboxmax . v [ j ] = 255 ;
}
}
osurf - > numframes = a ;
if ( numposes )
{
osurf - > numboneposes = numposes ;
osurf - > boneposedata = ( intptr_t ) outposes - ( intptr_t ) osurf ;
}
osurf - > numbones = numjoints ;
osurf - > boneinfo = ( intptr_t ) outbones - ( intptr_t ) osurf ;
for ( j = 0 ; j < 3 ; j + + )
{
osurf - > scale_origin [ j ] = 0 ;
osurf - > scale [ j ] = 1.0 ;
}
//skin size is irrelevant
osurf - > skinwidth = 1 ;
osurf - > skinheight = 1 ;
//load the textures
if ( ! isDedicated )
2021-09-03 07:11:57 +00:00
Mod_LoadIQMSkin ( mod , pinheader , osurf , surf , pinheader - > num_meshes , pintext + LittleLong ( pinsurface - > material ) ) ;
2019-11-02 15:50:53 +00:00
}
GLMesh_LoadVertexBuffer ( mod , outhdr ) ;
//small violation of the spec, but it seems like noone else uses it.
mod - > flags = LittleLong ( pinheader - > flags ) ;
2020-06-24 01:57:44 +00:00
mod - > synctype = ST_FRAMETIME ; //keep IQM animations synced to when .frame is changed. framegroups are otherwise not very useful.
2019-11-02 15:50:53 +00:00
mod - > type = mod_alias ;
Mod_CalcAliasBounds ( outhdr ) ; //johnfitz
//
// move the complete, relocatable alias model to the cache
//
end = Hunk_LowMark ( ) ;
total = end - start ;
Cache_Alloc ( & mod - > cache , total , loadname ) ;
if ( ! mod - > cache . data )
return ;
memcpy ( mod - > cache . data , outhdr , total ) ;
Hunk_FreeToLowMark ( start ) ;
2020-01-12 08:04:55 +00:00
}
2021-09-05 15:41:35 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
MD5 Models , for compat with the rerelease and NOT doom3 .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
md5mesh :
MD5Version 10
commandline " "
numJoints N
numMeshes N
joints {
" name " ParentIdx ( Pos_X Y Z ) ( Quat_X Y Z )
}
mesh {
shader " name " //file-relative path, with _%02d_%02d postfixed for skin/framegroup support. unlike doom3.
numverts N
vert # ( S T ) FirstWeight count
numtris N
tri # A B C
numweights N
weight # BoneIdx Scale ( X Y Z )
}
2021-09-05 17:31:18 +00:00
md5anim :
MD5Version 10
commandline " "
numFrames N
numJoints N
frameRate FPS
numAnimatedComponents N //bones*6ish
hierachy {
" name " ParentIdx Flags DataStart
}
bounds {
( X Y Z ) ( X Y Z )
}
baseframe {
( pos_X Y Z ) ( quad_X Y Z )
}
frame # {
RAW . . .
}
We ' ll unpack the animation to separate framegroups ( one - pose per , for consistency with most q1 models ) .
2021-09-05 15:41:35 +00:00
*/
static qboolean MD5_ParseCheck ( const char * s , const void * * buffer )
{
if ( strcmp ( com_token , s ) )
return false ;
* buffer = COM_Parse ( * buffer ) ;
return true ;
}
static size_t MD5_ParseUInt ( const void * * buffer )
{
size_t i = SDL_strtoull ( com_token , NULL , 0 ) ;
* buffer = COM_Parse ( * buffer ) ;
return i ;
}
static long MD5_ParseSInt ( const void * * buffer )
{
long i = SDL_strtol ( com_token , NULL , 0 ) ;
* buffer = COM_Parse ( * buffer ) ;
return i ;
}
static double MD5_ParseFloat ( const void * * buffer )
{
double i = SDL_strtod ( com_token , NULL ) ;
* buffer = COM_Parse ( * buffer ) ;
return i ;
}
2021-09-05 17:31:18 +00:00
# define MD5EXPECT(s) do{if (strcmp(com_token, s)) Sys_Error ("Mod_LoadMD5MeshModel(%s): Expected \"%s\"", fname, s); buffer = COM_Parse(buffer); }while(0)
2021-09-05 15:41:35 +00:00
# define MD5UINT() MD5_ParseUInt(&buffer)
# define MD5SINT() MD5_ParseSInt(&buffer)
# define MD5FLOAT() MD5_ParseFloat(&buffer)
# define MD5CHECK(s) MD5_ParseCheck(s, &buffer)
struct md5vertinfo_s
{
size_t firstweight ;
unsigned int count ;
} ;
struct md5weightinfo_s
{
size_t bone ;
vec4_t pos ;
} ;
static void Matrix3x4_RM_Transform4 ( const float * matrix , const float * vector , float * product )
{
product [ 0 ] = matrix [ 0 ] * vector [ 0 ] + matrix [ 1 ] * vector [ 1 ] + matrix [ 2 ] * vector [ 2 ] + matrix [ 3 ] * vector [ 3 ] ;
product [ 1 ] = matrix [ 4 ] * vector [ 0 ] + matrix [ 5 ] * vector [ 1 ] + matrix [ 6 ] * vector [ 2 ] + matrix [ 7 ] * vector [ 3 ] ;
product [ 2 ] = matrix [ 8 ] * vector [ 0 ] + matrix [ 9 ] * vector [ 1 ] + matrix [ 10 ] * vector [ 2 ] + matrix [ 11 ] * vector [ 3 ] ;
}
2021-09-08 00:48:18 +00:00
static void MD5_BakeInfluences ( const char * fname , bonepose_t * outposes , iqmvert_t * vert , struct md5vertinfo_s * vinfo , struct md5weightinfo_s * weight , size_t numverts , size_t numweights )
2021-09-05 15:41:35 +00:00
{
size_t v , i , lowidx , k ;
struct md5weightinfo_s * w ;
vec3_t pos ;
float lowval , scale ;
2021-09-08 00:48:18 +00:00
unsigned int maxinfluences = 0 ;
float scaleimprecision = 1 ;
2021-09-05 15:41:35 +00:00
for ( v = 0 ; v < numverts ; v + + , vert + + , vinfo + + )
{
//st were already loaded
//norm will need to be calculated after we have xyz info
vert - > xyz [ 0 ] = vert - > xyz [ 1 ] = vert - > xyz [ 2 ] = 0 ;
vert - > idx [ 0 ] = vert - > idx [ 1 ] = vert - > idx [ 2 ] = vert - > idx [ 3 ] = 0 ;
vert - > weight [ 0 ] = vert - > weight [ 1 ] = vert - > weight [ 2 ] = vert - > weight [ 3 ] = 0 ;
vert - > rgba [ 0 ] = vert - > rgba [ 1 ] = vert - > rgba [ 2 ] = vert - > rgba [ 3 ] = 1 ; //for consistency with iqm, though irrelevant here
if ( vinfo - > firstweight + vinfo - > count > numweights )
2021-09-08 00:48:18 +00:00
Sys_Error ( " %s: weight index out of bounds " , fname ) ;
if ( maxinfluences < vinfo - > count )
maxinfluences = vinfo - > count ;
2021-09-05 15:41:35 +00:00
w = weight + vinfo - > firstweight ;
for ( i = 0 ; i < vinfo - > count ; i + + , w + + )
{
Matrix3x4_RM_Transform4 ( outposes [ w - > bone ] . mat , w - > pos , pos ) ;
VectorAdd ( vert - > xyz , pos , vert - > xyz ) ;
if ( i < countof ( vert - > weight ) )
{
vert - > weight [ i ] = w - > pos [ 3 ] ;
vert - > idx [ i ] = w - > bone ;
}
else
{
//obnoxious code to find the lowest of the current possible bone indexes.
lowval = vert - > weight [ 0 ] ;
lowidx = 0 ;
for ( k = 1 ; k < countof ( vert - > weight ) ; k + + )
if ( vert - > weight [ k ] < lowval )
{
lowval = vert - > weight [ k ] ;
lowidx = k ;
}
if ( vert - > weight [ lowidx ] < w - > pos [ 3 ] )
{ //found a lower/unset weight, replace it.
vert - > weight [ lowidx ] = w - > pos [ 3 ] ;
vert - > idx [ lowidx ] = w - > bone ;
}
}
}
//normalize in case we dropped some weights.
scale = vert - > weight [ 0 ] + vert - > weight [ 1 ] + vert - > weight [ 2 ] + vert - > weight [ 3 ] ;
if ( scale > 0 )
{
2021-09-08 00:48:18 +00:00
if ( scaleimprecision < scale )
scaleimprecision = scale ;
2021-09-05 15:41:35 +00:00
scale = 1 / scale ;
2021-09-08 00:48:18 +00:00
for ( k = 0 ; k < countof ( vert - > weight ) ; k + + )
2021-09-05 15:41:35 +00:00
vert - > weight [ k ] * = scale ;
}
else //something bad...
vert - > weight [ 0 ] = 1 , vert - > weight [ 1 ] = vert - > weight [ 2 ] = vert - > weight [ 3 ] = 0 ;
}
2021-09-08 00:48:18 +00:00
if ( maxinfluences > countof ( vert - > weight ) )
Con_DWarning ( " %s uses up to %u influences per vertex (weakest: %g) \n " , fname , maxinfluences , scaleimprecision ) ;
2021-09-05 15:41:35 +00:00
}
static void MD5_ComputeNormals ( iqmvert_t * vert , size_t numverts , unsigned short * indexes , size_t numindexes )
{
size_t v , t ;
iqmvert_t * v0 , * v1 , * v2 ;
vec3_t d1 , d2 , norm ;
for ( v = 0 ; v < numverts ; v + + )
vert [ v ] . norm [ 0 ] = vert [ v ] . norm [ 1 ] = vert [ v ] . norm [ 2 ] = 0 ;
for ( t = 0 ; t < numindexes ; t + = 3 )
{
v0 = & vert [ indexes [ t + 0 ] ] ;
v1 = & vert [ indexes [ t + 1 ] ] ;
v2 = & vert [ indexes [ t + 2 ] ] ;
VectorSubtract ( v1 - > xyz , v0 - > xyz , d1 ) ;
VectorSubtract ( v2 - > xyz , v0 - > xyz , d2 ) ;
CrossProduct ( d1 , d2 , norm ) ;
VectorNormalize ( norm ) ;
//FIXME: this should be weighted by each vertex angle.
VectorAdd ( v0 - > norm , norm , v0 - > norm ) ;
VectorAdd ( v1 - > norm , norm , v1 - > norm ) ;
VectorAdd ( v2 - > norm , norm , v2 - > norm ) ;
}
//and make sure it actually makes sense.
for ( v = 0 ; v < numverts ; v + + )
VectorNormalize ( vert [ v ] . norm ) ;
}
static unsigned int MD5_HackyModelFlags ( const char * name )
{
unsigned int ret = 0 ;
char oldmodel [ MAX_QPATH ] ;
mdl_t * f ;
COM_StripExtension ( name , oldmodel , sizeof ( oldmodel ) ) ;
COM_AddExtension ( oldmodel , " .mdl " , sizeof ( oldmodel ) ) ;
f = ( mdl_t * ) COM_LoadMallocFile ( oldmodel , NULL ) ;
if ( f )
{
if ( com_filesize > = sizeof ( * f ) & & LittleLong ( f - > ident ) = = IDPOLYHEADER & & LittleLong ( f - > version ) = = ALIAS_VERSION )
ret = f - > flags ;
free ( f ) ;
}
return ret ;
}
2021-09-05 17:31:18 +00:00
struct md5animctx_s
{
void * animfile ;
const void * buffer ;
char fname [ MAX_QPATH ] ;
size_t numposes ;
size_t numjoints ;
bonepose_t * posedata ;
} ;
//This is split into two because aliashdr_t has silly trailing framegroup info.
static void MD5Anim_Begin ( struct md5animctx_s * ctx , const char * fname )
{
//Load an md5anim into it, if we can.
COM_StripExtension ( fname , ctx - > fname , sizeof ( ctx - > fname ) ) ;
COM_AddExtension ( ctx - > fname , " .md5anim " , sizeof ( ctx - > fname ) ) ;
fname = ctx - > fname ;
ctx - > animfile = COM_LoadMallocFile ( fname , NULL ) ;
ctx - > numposes = 0 ;
if ( ctx - > animfile )
{
const void * buffer = COM_Parse ( ctx - > animfile ) ;
MD5EXPECT ( " MD5Version " ) ;
MD5EXPECT ( " 10 " ) ;
if ( MD5CHECK ( " commandline " ) ) buffer = COM_Parse ( buffer ) ;
MD5EXPECT ( " numFrames " ) ; ctx - > numposes = MD5UINT ( ) ;
MD5EXPECT ( " numJoints " ) ; ctx - > numjoints = MD5UINT ( ) ;
MD5EXPECT ( " frameRate " ) ; /*irrelevant here*/
if ( ctx - > numposes < = 0 )
Sys_Error ( " %s has no poses " , fname ) ;
ctx - > buffer = buffer ;
}
}
static void MD5Anim_Load ( struct md5animctx_s * ctx , boneinfo_t * bones , size_t numbones )
{
const char * fname = ctx - > fname ;
struct { unsigned int flags , offset ; } * ab ;
size_t rawcount ;
float * raw , * r ;
bonepose_t * outposes ;
const void * buffer = COM_Parse ( ctx - > buffer ) ;
size_t j ;
if ( ! buffer )
{
free ( ctx - > animfile ) ;
return ;
}
MD5EXPECT ( " numAnimatedComponents " ) ; rawcount = MD5UINT ( ) ;
if ( ctx - > numjoints ! = numbones )
Sys_Error ( " %s has incorrect bone count " , fname ) ;
raw = Z_Malloc ( sizeof ( * raw ) * ( rawcount + 6 ) ) ;
ab = Z_Malloc ( sizeof ( * ab ) * ctx - > numjoints ) ;
ctx - > posedata = outposes = Hunk_Alloc ( sizeof ( * outposes ) * ctx - > numjoints * ctx - > numposes ) ;
MD5EXPECT ( " hierarchy " ) ;
MD5EXPECT ( " { " ) ;
for ( j = 0 ; j < ctx - > numjoints ; j + + )
{
//validate stuff
if ( strcmp ( bones [ j ] . name , com_token ) )
Sys_Error ( " %s: bone was renamed " , fname ) ;
buffer = COM_Parse ( buffer ) ;
if ( bones [ j ] . parent ! = MD5SINT ( ) )
Sys_Error ( " %s: bone has wrong parent " , fname ) ;
//new info
ab [ j ] . flags = MD5UINT ( ) ;
if ( ab [ j ] . flags & ~ 63 )
Sys_Error ( " %s: bone has unsupported flags " , fname ) ;
ab [ j ] . offset = MD5UINT ( ) ;
if ( ab [ j ] . offset > rawcount + 6 )
Sys_Error ( " %s: bone has bad offset " , fname ) ;
}
MD5EXPECT ( " } " ) ;
MD5EXPECT ( " bounds " ) ;
MD5EXPECT ( " { " ) ;
while ( MD5CHECK ( " ( " ) )
{
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
MD5EXPECT ( " ) " ) ;
MD5EXPECT ( " ( " ) ;
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
MD5EXPECT ( " ) " ) ;
}
MD5EXPECT ( " } " ) ;
MD5EXPECT ( " baseframe " ) ;
MD5EXPECT ( " { " ) ;
while ( MD5CHECK ( " ( " ) )
{
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
MD5EXPECT ( " ) " ) ;
MD5EXPECT ( " ( " ) ;
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
( void ) MD5FLOAT ( ) ;
MD5EXPECT ( " ) " ) ;
}
MD5EXPECT ( " } " ) ;
while ( MD5CHECK ( " frame " ) )
{
size_t idx = MD5UINT ( ) ;
if ( idx > = ctx - > numposes )
Sys_Error ( " %s: invalid pose index " , fname ) ;
MD5EXPECT ( " { " ) ;
for ( j = 0 ; j < rawcount ; j + + )
raw [ j ] = MD5FLOAT ( ) ;
MD5EXPECT ( " } " ) ;
//okay, we have our raw info, unpack the actual bone info.
for ( j = 0 ; j < ctx - > numjoints ; j + + )
{
vec3_t pos = { 0 , 0 , 0 } ;
static vec3_t scale = { 1 , 1 , 1 } ;
vec4_t quat = { 0 , 0 , 0 } ;
r = raw + ab [ j ] . offset ;
if ( ab [ j ] . flags & 1 ) pos [ 0 ] = * r + + ;
if ( ab [ j ] . flags & 2 ) pos [ 1 ] = * r + + ;
if ( ab [ j ] . flags & 4 ) pos [ 2 ] = * r + + ;
if ( ab [ j ] . flags & 8 ) quat [ 0 ] = * r + + ;
if ( ab [ j ] . flags & 16 ) quat [ 1 ] = * r + + ;
if ( ab [ j ] . flags & 32 ) quat [ 2 ] = * r + + ;
quat [ 3 ] = 1 - DotProduct ( quat , quat ) ;
if ( quat [ 3 ] < 0 )
quat [ 3 ] = 0 ; //we have no imagination.
quat [ 3 ] = - sqrt ( quat [ 3 ] ) ;
GenMatrixPosQuat4Scale ( pos , quat , scale , outposes [ idx * ctx - > numjoints + j ] . mat ) ;
}
}
Z_Free ( raw ) ;
Z_Free ( ab ) ;
free ( ctx - > animfile ) ;
}
2021-09-05 15:41:35 +00:00
void Mod_LoadMD5MeshModel ( qmodel_t * mod , const void * buffer )
{
2021-09-05 17:31:18 +00:00
const char * fname = mod - > name ;
2021-09-05 15:41:35 +00:00
unsigned short * poutindexes ;
iqmvert_t * poutvert ;
int start , end , total ;
aliashdr_t * outhdr , * surf ;
2021-09-05 17:31:18 +00:00
size_t hdrsize ;
2021-09-05 15:41:35 +00:00
bonepose_t * outposes ;
boneinfo_t * outbones ;
size_t numjoints , j ;
size_t nummeshes , m ;
char texname [ MAX_QPATH ] ;
struct md5vertinfo_s * vinfo ;
struct md5weightinfo_s * weight ;
size_t numweights ;
2021-09-05 17:31:18 +00:00
struct md5animctx_s anim = { NULL } ;
2021-09-05 15:41:35 +00:00
start = Hunk_LowMark ( ) ;
buffer = COM_Parse ( buffer ) ;
MD5EXPECT ( " MD5Version " ) ;
MD5EXPECT ( " 10 " ) ;
if ( MD5CHECK ( " commandline " ) ) buffer = COM_Parse ( buffer ) ;
MD5EXPECT ( " numJoints " ) ; numjoints = MD5UINT ( ) ;
MD5EXPECT ( " numMeshes " ) ; nummeshes = MD5UINT ( ) ;
if ( numjoints < = 0 )
Sys_Error ( " %s has no bones " , mod - > name ) ;
if ( nummeshes < = 0 )
Sys_Error ( " %s has no meshes " , mod - > name ) ;
2021-09-05 17:31:18 +00:00
if ( strcmp ( com_token , " joints " ) ) Sys_Error ( " Mod_LoadMD5MeshModel(%s): Expected \" %s \" " , fname , " joints " ) ;
MD5Anim_Begin ( & anim , fname ) ;
buffer = COM_Parse ( buffer ) ;
hdrsize = sizeof ( * outhdr ) - sizeof ( outhdr - > frames ) ;
hdrsize + = sizeof ( outhdr - > frames ) * anim . numposes ;
outhdr = Hunk_Alloc ( hdrsize * numjoints ) ;
2021-09-05 15:41:35 +00:00
outbones = Hunk_Alloc ( sizeof ( * outbones ) * numjoints ) ;
outposes = Z_Malloc ( sizeof ( * outposes ) * numjoints ) ;
2021-09-05 17:31:18 +00:00
2021-09-05 15:41:35 +00:00
MD5EXPECT ( " { " ) ;
for ( j = 0 ; j < numjoints ; j + + )
{
vec3_t pos ;
static vec3_t scale = { 1 , 1 , 1 } ;
vec4_t quat ;
q_strlcpy ( outbones [ j ] . name , com_token , sizeof ( outbones [ j ] . name ) ) ; buffer = COM_Parse ( buffer ) ;
outbones [ j ] . parent = MD5SINT ( ) ;
if ( outbones [ j ] . parent < - 1 & & outbones [ j ] . parent > = numjoints )
Sys_Error ( " bone index out of bounds " ) ;
MD5EXPECT ( " ( " ) ;
pos [ 0 ] = MD5FLOAT ( ) ;
pos [ 1 ] = MD5FLOAT ( ) ;
pos [ 2 ] = MD5FLOAT ( ) ;
MD5EXPECT ( " ) " ) ;
MD5EXPECT ( " ( " ) ;
quat [ 0 ] = MD5FLOAT ( ) ;
quat [ 1 ] = MD5FLOAT ( ) ;
quat [ 2 ] = MD5FLOAT ( ) ;
quat [ 3 ] = 1 - DotProduct ( quat , quat ) ;
if ( quat [ 3 ] < 0 )
quat [ 3 ] = 0 ; //we have no imagination.
quat [ 3 ] = - sqrt ( quat [ 3 ] ) ;
MD5EXPECT ( " ) " ) ;
GenMatrixPosQuat4Scale ( pos , quat , scale , outposes [ j ] . mat ) ;
Matrix3x4_Invert_Simple ( outposes [ j ] . mat , outbones [ j ] . inverse . mat ) ; //absolute, so we can just invert now.
}
2021-09-05 17:31:18 +00:00
if ( strcmp ( com_token , " } " ) ) Sys_Error ( " Mod_LoadMD5MeshModel(%s): Expected \" %s \" " , fname , " } " ) ;
MD5Anim_Load ( & anim , outbones , numjoints ) ;
buffer = COM_Parse ( buffer ) ;
2021-09-05 15:41:35 +00:00
for ( m = 0 ; m < nummeshes ; m + + )
{
MD5EXPECT ( " mesh " ) ;
MD5EXPECT ( " { " ) ;
2021-09-05 17:31:18 +00:00
surf = ( aliashdr_t * ) ( ( byte * ) outhdr + m * hdrsize ) ;
2021-09-05 15:41:35 +00:00
if ( m + 1 < nummeshes )
2021-09-05 17:31:18 +00:00
surf - > nextsurface = hdrsize ;
2021-09-05 15:41:35 +00:00
else
surf - > nextsurface = 0 ;
2021-09-05 17:31:18 +00:00
2021-09-05 15:41:35 +00:00
surf - > poseverttype = PV_IQM ;
for ( j = 0 ; j < 3 ; j + + )
{
surf - > scale_origin [ j ] = 0 ;
surf - > scale [ j ] = 1.0 ;
}
surf - > numbones = numjoints ;
surf - > boneinfo = ( byte * ) outbones - ( byte * ) surf ;
2021-09-05 17:31:18 +00:00
if ( anim . numposes )
{
surf - > boneposedata = ( byte * ) anim . posedata - ( byte * ) surf ;
surf - > numboneposes = anim . numposes ;
for ( j = 0 ; j < anim . numposes ; j + + )
{
surf - > frames [ j ] . firstpose = j ;
surf - > frames [ j ] . numposes = 1 ;
surf - > frames [ j ] . interval = 0.1 ;
}
surf - > numframes = j ;
}
2021-09-05 15:41:35 +00:00
MD5EXPECT ( " shader " ) ;
//MD5 violation: the skin is a single material. adding prefixes/postfixes here is the wrong thing to do.
//but we do so anyway, because rerelease compat.
for ( surf - > numskins = 0 ; surf - > numskins < MAX_SKINS ; surf - > numskins + + )
{
2021-09-13 03:03:27 +00:00
unsigned int fwidth , fheight , f ;
enum srcformat fmt ;
qboolean malloced ;
void * data ;
int mark = Hunk_LowMark ( ) ;
for ( f = 0 ; f < countof ( surf - > gltextures [ 0 ] ) ; f + + )
{
q_snprintf ( texname , sizeof ( texname ) , " progs/%s_%02u_%02u " , com_token , surf - > numskins , f ) ;
2021-11-12 02:32:31 +00:00
data = Image_LoadImage ( texname , ( int * ) & fwidth , ( int * ) & fheight , & fmt , & malloced ) ;
2021-09-13 03:03:27 +00:00
//now load whatever we found
if ( data ) //load external image
{
surf - > gltextures [ surf - > numskins ] [ f ] = TexMgr_LoadImage ( mod , texname , fwidth , fheight , fmt , data , texname , 0 , TEXPREF_ALPHA | TEXPREF_NOBRIGHT | TEXPREF_MIPMAP ) ;
surf - > fbtextures [ surf - > numskins ] [ f ] = NULL ;
if ( fmt = = SRC_INDEXED )
{ //8bit base texture. use it for fullbrights.
for ( j = 0 ; j < fwidth * fheight ; j + + )
{
if ( ( ( byte * ) data ) [ j ] > 223 )
{
surf - > fbtextures [ surf - > numskins ] [ f ] = TexMgr_LoadImage ( mod , va ( " %s_luma " , texname ) , fwidth , fheight , fmt , data , texname , 0 , TEXPREF_ALPHA | TEXPREF_FULLBRIGHT | TEXPREF_MIPMAP ) ;
break ;
}
}
}
else
{ //we found a 32bit base texture.
if ( ! surf - > fbtextures [ surf - > numskins ] [ f ] )
{
q_snprintf ( texname , sizeof ( texname ) , " progs/%s_%02u_%02u_glow " , com_token , surf - > numskins , f ) ;
surf - > fbtextures [ surf - > numskins ] [ f ] = TexMgr_LoadImage ( mod , texname , surf - > skinwidth , surf - > skinheight , SRC_EXTERNAL , NULL , texname , 0 , TEXPREF_ALLOWMISSING | TEXPREF_MIPMAP ) ;
}
if ( ! surf - > fbtextures [ surf - > numskins ] [ f ] )
{
q_snprintf ( texname , sizeof ( texname ) , " progs/%s_%02u_%02u_luma " , com_token , surf - > numskins , f ) ;
surf - > fbtextures [ surf - > numskins ] [ f ] = TexMgr_LoadImage ( mod , texname , surf - > skinwidth , surf - > skinheight , SRC_EXTERNAL , NULL , texname , 0 , TEXPREF_ALLOWMISSING | TEXPREF_MIPMAP ) ;
}
}
//now try to load glow/luma image from the same place
if ( malloced )
free ( data ) ;
Hunk_FreeToLowMark ( mark ) ;
}
else
break ;
}
if ( f = = 0 )
break ; //no images loaded...
//this stuff is hideous.
if ( f < 2 )
{
surf - > gltextures [ surf - > numskins ] [ 1 ] = surf - > gltextures [ surf - > numskins ] [ 0 ] ;
surf - > fbtextures [ surf - > numskins ] [ 1 ] = surf - > fbtextures [ surf - > numskins ] [ 0 ] ;
}
if ( f = = 3 )
Con_Warning ( " progs/%s_%02u_##: 3 skinframes found... \n " , com_token , surf - > numskins ) ;
if ( f < 4 )
{
surf - > gltextures [ surf - > numskins ] [ 3 ] = surf - > gltextures [ surf - > numskins ] [ 1 ] ;
surf - > gltextures [ surf - > numskins ] [ 2 ] = surf - > gltextures [ surf - > numskins ] [ 0 ] ;
surf - > fbtextures [ surf - > numskins ] [ 3 ] = surf - > fbtextures [ surf - > numskins ] [ 1 ] ;
surf - > fbtextures [ surf - > numskins ] [ 2 ] = surf - > fbtextures [ surf - > numskins ] [ 0 ] ;
}
2021-09-05 15:41:35 +00:00
}
surf - > skinwidth = surf - > gltextures [ 0 ] [ 0 ] ? surf - > gltextures [ 0 ] [ 0 ] - > width : 1 ;
surf - > skinheight = surf - > gltextures [ 0 ] [ 0 ] ? surf - > gltextures [ 0 ] [ 0 ] - > height : 1 ;
buffer = COM_Parse ( buffer ) ;
MD5EXPECT ( " numverts " ) ;
surf - > numverts_vbo = surf - > numverts = MD5UINT ( ) ;
vinfo = Z_Malloc ( sizeof ( * vinfo ) * surf - > numverts ) ;
poutvert = Hunk_Alloc ( sizeof ( * poutvert ) * surf - > numverts ) ;
surf - > vertexes = ( byte * ) poutvert - ( byte * ) surf ;
surf - > nummorphposes = 1 ;
while ( MD5CHECK ( " vert " ) )
{
size_t idx = MD5UINT ( ) ;
if ( idx > = surf - > numverts )
Sys_Error ( " vertex index out of bounds " ) ;
MD5EXPECT ( " ( " ) ;
poutvert [ idx ] . st [ 0 ] = MD5FLOAT ( ) ;
poutvert [ idx ] . st [ 1 ] = MD5FLOAT ( ) ;
MD5EXPECT ( " ) " ) ;
vinfo [ idx ] . firstweight = MD5UINT ( ) ;
vinfo [ idx ] . count = MD5UINT ( ) ;
}
MD5EXPECT ( " numtris " ) ;
surf - > numtris = MD5UINT ( ) ;
surf - > numindexes = surf - > numtris * 3 ;
poutindexes = Hunk_Alloc ( sizeof ( * poutindexes ) * surf - > numindexes ) ;
surf - > indexes = ( byte * ) poutindexes - ( byte * ) surf ;
while ( MD5CHECK ( " tri " ) )
{
size_t idx = MD5UINT ( ) ;
if ( idx > = surf - > numtris )
Sys_Error ( " triangle index out of bounds " ) ;
idx * = 3 ;
for ( j = 0 ; j < 3 ; j + + )
{
size_t t = MD5UINT ( ) ;
if ( t > surf - > numverts )
Sys_Error ( " vertex index out of bounds " ) ;
poutindexes [ idx + j ] = t ;
}
}
//md5 is a gpu-unfriendly interchange format. :(
MD5EXPECT ( " numweights " ) ;
numweights = MD5UINT ( ) ;
weight = Z_Malloc ( sizeof ( * weight ) * numweights ) ;
while ( MD5CHECK ( " weight " ) )
{
size_t idx = MD5UINT ( ) ;
if ( idx > = numweights )
Sys_Error ( " weight index out of bounds " ) ;
weight [ idx ] . bone = MD5UINT ( ) ;
if ( weight [ idx ] . bone > = numjoints )
Sys_Error ( " bone index out of bounds " ) ;
weight [ idx ] . pos [ 3 ] = MD5FLOAT ( ) ;
MD5EXPECT ( " ( " ) ;
weight [ idx ] . pos [ 0 ] = MD5FLOAT ( ) * weight [ idx ] . pos [ 3 ] ;
weight [ idx ] . pos [ 1 ] = MD5FLOAT ( ) * weight [ idx ] . pos [ 3 ] ;
weight [ idx ] . pos [ 2 ] = MD5FLOAT ( ) * weight [ idx ] . pos [ 3 ] ;
MD5EXPECT ( " ) " ) ;
}
//so make it gpu-friendly.
2021-09-08 00:48:18 +00:00
MD5_BakeInfluences ( fname , outposes , poutvert , vinfo , weight , surf - > numverts , numweights ) ;
2021-09-05 15:41:35 +00:00
//and now make up the normals that the format lacks. we'll still probably have issues from seams, but then so did qme, so at least its faithful... :P
MD5_ComputeNormals ( poutvert , surf - > numverts , poutindexes , surf - > numindexes ) ;
Z_Free ( weight ) ;
Z_Free ( vinfo ) ;
MD5EXPECT ( " } " ) ;
}
Z_Free ( outposes ) ;
GLMesh_LoadVertexBuffer ( mod , outhdr ) ;
//the md5 format does not have its own modelflags, yet we still need to know about trails and rotating etc
mod - > flags = MD5_HackyModelFlags ( mod - > name ) ;
mod - > synctype = ST_FRAMETIME ; //keep IQM animations synced to when .frame is changed. framegroups are otherwise not very useful.
mod - > type = mod_alias ;
Mod_CalcAliasBounds ( outhdr ) ; //johnfitz
//
// move the complete, relocatable alias model to the cache
//
end = Hunk_LowMark ( ) ;
total = end - start ;
Cache_Alloc ( & mod - > cache , total , loadname ) ;
if ( ! mod - > cache . data )
return ;
memcpy ( mod - > cache . data , outhdr , total ) ;
Hunk_FreeToLowMark ( start ) ;
}