From 11337c9fa2fa45371182603863164a9186ff2b9e Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 27 Jul 2018 17:40:25 -0500 Subject: [PATCH] OpenGL2: Add GPU vertex skinning for IQM models Using GPU vertex skinning is significantly faster than CPU vertex skinning. Especially since OpenGL2 has to run R_VaoPackNormal() and R_VaoPackTangent() each vertex each frame which causes CPU vertex skinning to be significantly slower than OpenGL1 renderer. --- code/renderergl2/glsl/fogpass_vp.glsl | 14 ++ code/renderergl2/glsl/generic_vp.glsl | 14 ++ code/renderergl2/glsl/lightall_vp.glsl | 17 ++ code/renderergl2/glsl/shadowfill_vp.glsl | 27 ++- code/renderergl2/tr_glsl.c | 137 +++++++++++-- code/renderergl2/tr_init.c | 7 + code/renderergl2/tr_local.h | 56 +++++- code/renderergl2/tr_model_iqm.c | 244 ++++++++++++++++++++++- code/renderergl2/tr_shade.c | 50 ++++- code/renderergl2/tr_surface.c | 1 + 10 files changed, 533 insertions(+), 34 deletions(-) diff --git a/code/renderergl2/glsl/fogpass_vp.glsl b/code/renderergl2/glsl/fogpass_vp.glsl index c8ec9a93..72ea0af6 100644 --- a/code/renderergl2/glsl/fogpass_vp.glsl +++ b/code/renderergl2/glsl/fogpass_vp.glsl @@ -6,6 +6,9 @@ attribute vec4 attr_TexCoord0; #if defined(USE_VERTEX_ANIMATION) attribute vec3 attr_Position2; attribute vec3 attr_Normal2; +#elif defined(USE_BONE_ANIMATION) +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; #endif uniform vec4 u_FogDistance; @@ -22,6 +25,8 @@ uniform mat4 u_ModelViewProjectionMatrix; #if defined(USE_VERTEX_ANIMATION) uniform float u_VertexLerp; +#elif defined(USE_BONE_ANIMATION) +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; #endif uniform vec4 u_Color; @@ -102,6 +107,15 @@ void main() #if defined(USE_VERTEX_ANIMATION) vec3 position = mix(attr_Position, attr_Position2, u_VertexLerp); vec3 normal = mix(attr_Normal, attr_Normal2, u_VertexLerp); +#elif defined(USE_BONE_ANIMATION) + mat4 vtxMat = u_BoneMatrix[int(attr_BoneIndexes.x)] * attr_BoneWeights.x; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.y)] * attr_BoneWeights.y; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.z)] * attr_BoneWeights.z; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.w)] * attr_BoneWeights.w; + mat3 nrmMat = mat3(cross(vtxMat[1].xyz, vtxMat[2].xyz), cross(vtxMat[2].xyz, vtxMat[0].xyz), cross(vtxMat[0].xyz, vtxMat[1].xyz)); + + vec3 position = vec3(vtxMat * vec4(attr_Position, 1.0)); + vec3 normal = normalize(nrmMat * attr_Normal); #else vec3 position = attr_Position; vec3 normal = attr_Normal; diff --git a/code/renderergl2/glsl/generic_vp.glsl b/code/renderergl2/glsl/generic_vp.glsl index bbe08e84..a0055263 100644 --- a/code/renderergl2/glsl/generic_vp.glsl +++ b/code/renderergl2/glsl/generic_vp.glsl @@ -4,6 +4,9 @@ attribute vec3 attr_Normal; #if defined(USE_VERTEX_ANIMATION) attribute vec3 attr_Position2; attribute vec3 attr_Normal2; +#elif defined(USE_BONE_ANIMATION) +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; #endif attribute vec4 attr_Color; @@ -54,6 +57,8 @@ uniform float u_PortalRange; #if defined(USE_VERTEX_ANIMATION) uniform float u_VertexLerp; +#elif defined(USE_BONE_ANIMATION) +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; #endif varying vec2 var_DiffuseTex; @@ -204,6 +209,15 @@ void main() #if defined(USE_VERTEX_ANIMATION) vec3 position = mix(attr_Position, attr_Position2, u_VertexLerp); vec3 normal = mix(attr_Normal, attr_Normal2, u_VertexLerp); +#elif defined(USE_BONE_ANIMATION) + mat4 vtxMat = u_BoneMatrix[int(attr_BoneIndexes.x)] * attr_BoneWeights.x; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.y)] * attr_BoneWeights.y; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.z)] * attr_BoneWeights.z; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.w)] * attr_BoneWeights.w; + mat3 nrmMat = mat3(cross(vtxMat[1].xyz, vtxMat[2].xyz), cross(vtxMat[2].xyz, vtxMat[0].xyz), cross(vtxMat[0].xyz, vtxMat[1].xyz)); + + vec3 position = vec3(vtxMat * vec4(attr_Position, 1.0)); + vec3 normal = normalize(nrmMat * attr_Normal); #else vec3 position = attr_Position; vec3 normal = attr_Normal; diff --git a/code/renderergl2/glsl/lightall_vp.glsl b/code/renderergl2/glsl/lightall_vp.glsl index e5b3c4f9..428cf1e6 100644 --- a/code/renderergl2/glsl/lightall_vp.glsl +++ b/code/renderergl2/glsl/lightall_vp.glsl @@ -12,6 +12,9 @@ attribute vec4 attr_Tangent; attribute vec3 attr_Position2; attribute vec3 attr_Normal2; attribute vec4 attr_Tangent2; +#elif defined(USE_BONE_ANIMATION) +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; #endif #if defined(USE_LIGHT) && !defined(USE_LIGHT_VECTOR) @@ -48,6 +51,8 @@ uniform mat4 u_ModelMatrix; #if defined(USE_VERTEX_ANIMATION) uniform float u_VertexLerp; +#elif defined(USE_BONE_ANIMATION) +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; #endif #if defined(USE_LIGHT_VECTOR) @@ -151,6 +156,18 @@ void main() #if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) vec3 tangent = mix(attr_Tangent.xyz, attr_Tangent2.xyz, u_VertexLerp); #endif +#elif defined(USE_BONE_ANIMATION) + mat4 vtxMat = u_BoneMatrix[int(attr_BoneIndexes.x)] * attr_BoneWeights.x; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.y)] * attr_BoneWeights.y; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.z)] * attr_BoneWeights.z; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.w)] * attr_BoneWeights.w; + mat3 nrmMat = mat3(cross(vtxMat[1].xyz, vtxMat[2].xyz), cross(vtxMat[2].xyz, vtxMat[0].xyz), cross(vtxMat[0].xyz, vtxMat[1].xyz)); + + vec3 position = vec3(vtxMat * vec4(attr_Position, 1.0)); + vec3 normal = normalize(nrmMat * attr_Normal); + #if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) + vec3 tangent = normalize(nrmMat * attr_Tangent.xyz); + #endif #else vec3 position = attr_Position; vec3 normal = attr_Normal; diff --git a/code/renderergl2/glsl/shadowfill_vp.glsl b/code/renderergl2/glsl/shadowfill_vp.glsl index 7de901ba..03f8667c 100644 --- a/code/renderergl2/glsl/shadowfill_vp.glsl +++ b/code/renderergl2/glsl/shadowfill_vp.glsl @@ -2,10 +2,13 @@ attribute vec3 attr_Position; attribute vec3 attr_Normal; attribute vec4 attr_TexCoord0; -//#if defined(USE_VERTEX_ANIMATION) +#if defined(USE_VERTEX_ANIMATION) attribute vec3 attr_Position2; attribute vec3 attr_Normal2; -//#endif +#elif defined(USE_BONE_ANIMATION) +attribute vec4 attr_BoneIndexes; +attribute vec4 attr_BoneWeights; +#endif //#if defined(USE_DEFORM_VERTEXES) uniform int u_DeformGen; @@ -17,9 +20,11 @@ uniform mat4 u_ModelViewProjectionMatrix; uniform mat4 u_ModelMatrix; -//#if defined(USE_VERTEX_ANIMATION) +#if defined(USE_VERTEX_ANIMATION) uniform float u_VertexLerp; -//#endif +#elif defined(USE_BONE_ANIMATION) +uniform mat4 u_BoneMatrix[MAX_GLSL_BONES]; +#endif varying vec3 var_Position; @@ -78,8 +83,22 @@ vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) void main() { +#if defined(USE_VERTEX_ANIMATION) vec3 position = mix(attr_Position, attr_Position2, u_VertexLerp); vec3 normal = mix(attr_Normal, attr_Normal2, u_VertexLerp); +#elif defined(USE_BONE_ANIMATION) + mat4 vtxMat = u_BoneMatrix[int(attr_BoneIndexes.x)] * attr_BoneWeights.x; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.y)] * attr_BoneWeights.y; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.z)] * attr_BoneWeights.z; + vtxMat += u_BoneMatrix[int(attr_BoneIndexes.w)] * attr_BoneWeights.w; + mat3 nrmMat = mat3(cross(vtxMat[1].xyz, vtxMat[2].xyz), cross(vtxMat[2].xyz, vtxMat[0].xyz), cross(vtxMat[0].xyz, vtxMat[1].xyz)); + + vec3 position = vec3(vtxMat * vec4(attr_Position, 1.0)); + vec3 normal = normalize(nrmMat * attr_Normal); +#else + vec3 position = attr_Position; + vec3 normal = attr_Normal; +#endif position = DeformPosition(position, normal, attr_TexCoord0.st); diff --git a/code/renderergl2/tr_glsl.c b/code/renderergl2/tr_glsl.c index 596e27ee..218017de 100644 --- a/code/renderergl2/tr_glsl.c +++ b/code/renderergl2/tr_glsl.c @@ -148,6 +148,8 @@ static uniformInfo_t uniformsInfo[] = { "u_CubeMapInfo", GLSL_VEC4 }, { "u_AlphaTest", GLSL_INT }, + + { "u_BoneMatrix", GLSL_MAT16_BONEMATRIX }, }; typedef enum @@ -555,6 +557,12 @@ static int GLSL_InitGPUShader2(shaderProgram_t * program, const char *name, int if(attribs & ATTR_LIGHTDIRECTION) qglBindAttribLocation(program->program, ATTR_INDEX_LIGHTDIRECTION, "attr_LightDirection"); + if(attribs & ATTR_BONE_INDEXES) + qglBindAttribLocation(program->program, ATTR_INDEX_BONE_INDEXES, "attr_BoneIndexes"); + + if(attribs & ATTR_BONE_WEIGHTS) + qglBindAttribLocation(program->program, ATTR_INDEX_BONE_WEIGHTS, "attr_BoneWeights"); + if(attribs & ATTR_POSITION2) qglBindAttribLocation(program->program, ATTR_INDEX_POSITION2, "attr_Position2"); @@ -660,6 +668,9 @@ void GLSL_InitUniforms(shaderProgram_t *program) case GLSL_MAT16: size += sizeof(vec_t) * 16; break; + case GLSL_MAT16_BONEMATRIX: + size += sizeof(vec_t) * 16 * glRefConfig.glslMaxAnimatedBones; + break; default: break; } @@ -843,6 +854,38 @@ void GLSL_SetUniformMat4(shaderProgram_t *program, int uniformNum, const mat4_t qglProgramUniformMatrix4fvEXT(program->program, uniforms[uniformNum], 1, GL_FALSE, matrix); } +void GLSL_SetUniformMat4BoneMatrix(shaderProgram_t *program, int uniformNum, /*const*/ mat4_t *matrix, int numMatricies) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) { + return; + } + + if (uniformsInfo[uniformNum].type != GLSL_MAT16_BONEMATRIX) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformMat4BoneMatrix: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (numMatricies > glRefConfig.glslMaxAnimatedBones) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformMat4BoneMatrix: too many matricies (%d/%d) for uniform %i in program %s\n", + numMatricies, glRefConfig.glslMaxAnimatedBones, uniformNum, program->name); + return; + } + + if (!memcmp(matrix, compare, numMatricies * sizeof(mat4_t))) + { + return; + } + + Com_Memcpy(compare, matrix, numMatricies * sizeof(mat4_t)); + + qglProgramUniformMatrix4fvEXT(program->program, uniforms[uniformNum], numMatricies, GL_FALSE, &matrix[0][0]); +} + void GLSL_DeleteGPUShader(shaderProgram_t *program) { if(program->program) @@ -886,6 +929,12 @@ void GLSL_InitGPUShaders(void) for (i = 0; i < GENERICDEF_COUNT; i++) { + if ((i & GENERICDEF_USE_VERTEX_ANIMATION) && (i & GENERICDEF_USE_BONE_ANIMATION)) + continue; + + if ((i & GENERICDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones) + continue; + attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_NORMAL | ATTR_COLOR; extradefines[0] = '\0'; @@ -903,6 +952,11 @@ void GLSL_InitGPUShaders(void) Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); attribs |= ATTR_POSITION2 | ATTR_NORMAL2; } + else if (i & GENERICDEF_USE_BONE_ANIMATION) + { + Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones)); + attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS; + } if (i & GENERICDEF_USE_FOG) Q_strcat(extradefines, 1024, "#define USE_FOG\n"); @@ -943,14 +997,28 @@ void GLSL_InitGPUShaders(void) for (i = 0; i < FOGDEF_COUNT; i++) { - attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; + if ((i & FOGDEF_USE_VERTEX_ANIMATION) && (i & FOGDEF_USE_BONE_ANIMATION)) + continue; + + if ((i & FOGDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones) + continue; + + attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD; extradefines[0] = '\0'; if (i & FOGDEF_USE_DEFORM_VERTEXES) Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); if (i & FOGDEF_USE_VERTEX_ANIMATION) + { Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); + attribs |= ATTR_POSITION2 | ATTR_NORMAL2; + } + else if (i & FOGDEF_USE_BONE_ANIMATION) + { + Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones)); + attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS; + } if (!GLSL_InitGPUShader(&tr.fogShader[i], "fogpass", attribs, qtrue, extradefines, qtrue, fallbackShader_fogpass_vp, fallbackShader_fogpass_fp)) { @@ -1001,6 +1069,12 @@ void GLSL_InitGPUShaders(void) if ((i & LIGHTDEF_USE_SHADOWMAP) && (!lightType || !r_sunlightMode->integer)) continue; + if ((i & LIGHTDEF_ENTITY_VERTEX_ANIMATION) && (i & LIGHTDEF_ENTITY_BONE_ANIMATION)) + continue; + + if ((i & LIGHTDEF_ENTITY_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones) + continue; + attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR | ATTR_NORMAL; extradefines[0] = '\0'; @@ -1043,7 +1117,7 @@ void GLSL_InitGPUShaders(void) attribs |= ATTR_TANGENT; - if ((i & LIGHTDEF_USE_PARALLAXMAP) && !(i & LIGHTDEF_ENTITY) && r_parallaxMapping->integer) + if ((i & LIGHTDEF_USE_PARALLAXMAP) && !(i & LIGHTDEF_ENTITY_VERTEX_ANIMATION) && !(i & LIGHTDEF_ENTITY_BONE_ANIMATION) && r_parallaxMapping->integer) { Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP\n"); if (r_parallaxMapping->integer > 1) @@ -1093,7 +1167,7 @@ void GLSL_InitGPUShaders(void) Q_strcat(extradefines, 1024, "#define USE_TCMOD\n"); } - if (i & LIGHTDEF_ENTITY) + if (i & LIGHTDEF_ENTITY_VERTEX_ANIMATION) { Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n#define USE_MODELMATRIX\n"); attribs |= ATTR_POSITION2 | ATTR_NORMAL2; @@ -1103,6 +1177,12 @@ void GLSL_InitGPUShaders(void) attribs |= ATTR_TANGENT2; } } + else if (i & LIGHTDEF_ENTITY_BONE_ANIMATION) + { + Q_strcat(extradefines, 1024, "#define USE_MODELMATRIX\n"); + Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones)); + attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS; + } if (!GLSL_InitGPUShader(&tr.lightallShader[i], "lightall", attribs, qtrue, extradefines, qtrue, fallbackShader_lightall_vp, fallbackShader_lightall_fp)) { @@ -1124,20 +1204,41 @@ void GLSL_InitGPUShaders(void) numLightShaders++; } - attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; - - extradefines[0] = '\0'; - - if (!GLSL_InitGPUShader(&tr.shadowmapShader, "shadowfill", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowfill_vp, fallbackShader_shadowfill_fp)) + for (i = 0; i < SHADOWMAPDEF_COUNT; i++) { - ri.Error(ERR_FATAL, "Could not load shadowfill shader!"); + if ((i & SHADOWMAPDEF_USE_VERTEX_ANIMATION) && (i & SHADOWMAPDEF_USE_BONE_ANIMATION)) + continue; + + if ((i & SHADOWMAPDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones) + continue; + + attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD; + + extradefines[0] = '\0'; + + if (i & SHADOWMAPDEF_USE_VERTEX_ANIMATION) + { + Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); + attribs |= ATTR_POSITION2 | ATTR_NORMAL2; + } + + if (i & SHADOWMAPDEF_USE_BONE_ANIMATION) + { + Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones)); + attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS; + } + + if (!GLSL_InitGPUShader(&tr.shadowmapShader[i], "shadowfill", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowfill_vp, fallbackShader_shadowfill_fp)) + { + ri.Error(ERR_FATAL, "Could not load shadowfill shader!"); + } + + GLSL_InitUniforms(&tr.shadowmapShader[i]); + GLSL_FinishGPUShader(&tr.shadowmapShader[i]); + + numEtcShaders++; } - GLSL_InitUniforms(&tr.shadowmapShader); - GLSL_FinishGPUShader(&tr.shadowmapShader); - - numEtcShaders++; - attribs = ATTR_POSITION | ATTR_NORMAL; extradefines[0] = '\0'; @@ -1363,7 +1464,9 @@ void GLSL_ShutdownGPUShaders(void) for ( i = 0; i < LIGHTDEF_COUNT; i++) GLSL_DeleteGPUShader(&tr.lightallShader[i]); - GLSL_DeleteGPUShader(&tr.shadowmapShader); + for ( i = 0; i < SHADOWMAPDEF_COUNT; i++) + GLSL_DeleteGPUShader(&tr.shadowmapShader[i]); + GLSL_DeleteGPUShader(&tr.pshadowShader); GLSL_DeleteGPUShader(&tr.down4xShader); GLSL_DeleteGPUShader(&tr.bokehShader); @@ -1439,6 +1542,10 @@ shaderProgram_t *GLSL_GetGenericShaderProgram(int stage) { shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; } + else if (glState.boneAnimation) + { + shaderAttribs |= GENERICDEF_USE_BONE_ANIMATION; + } if (pStage->bundle[0].numTexMods) { diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index bf830321..ce48925d 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -275,6 +275,13 @@ static void InitOpenGL( void ) qglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &temp ); glConfig.numTextureUnits = temp; + + // reserve 160 components for other uniforms + qglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS, &temp ); + glRefConfig.glslMaxAnimatedBones = Com_Clamp( 0, IQM_MAX_JOINTS, ( temp - 160 ) / 16 ); + if ( glRefConfig.glslMaxAnimatedBones < 12 ) { + glRefConfig.glslMaxAnimatedBones = 0; + } } // set default state diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 193879c5..ea6d70fe 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -554,16 +554,18 @@ enum GENERICDEF_USE_VERTEX_ANIMATION = 0x0004, GENERICDEF_USE_FOG = 0x0008, GENERICDEF_USE_RGBAGEN = 0x0010, - GENERICDEF_ALL = 0x001F, - GENERICDEF_COUNT = 0x0020, + GENERICDEF_USE_BONE_ANIMATION = 0x0020, + GENERICDEF_ALL = 0x003F, + GENERICDEF_COUNT = 0x0040, }; enum { FOGDEF_USE_DEFORM_VERTEXES = 0x0001, FOGDEF_USE_VERTEX_ANIMATION = 0x0002, - FOGDEF_ALL = 0x0003, - FOGDEF_COUNT = 0x0004, + FOGDEF_USE_BONE_ANIMATION = 0x0004, + FOGDEF_ALL = 0x0007, + FOGDEF_COUNT = 0x0008, }; enum @@ -579,12 +581,21 @@ enum LIGHTDEF_USE_LIGHT_VECTOR = 0x0002, LIGHTDEF_USE_LIGHT_VERTEX = 0x0003, LIGHTDEF_LIGHTTYPE_MASK = 0x0003, - LIGHTDEF_ENTITY = 0x0004, + LIGHTDEF_ENTITY_VERTEX_ANIMATION = 0x0004, LIGHTDEF_USE_TCGEN_AND_TCMOD = 0x0008, LIGHTDEF_USE_PARALLAXMAP = 0x0010, LIGHTDEF_USE_SHADOWMAP = 0x0020, - LIGHTDEF_ALL = 0x003F, - LIGHTDEF_COUNT = 0x0040 + LIGHTDEF_ENTITY_BONE_ANIMATION = 0x0040, + LIGHTDEF_ALL = 0x007F, + LIGHTDEF_COUNT = 0x0080 +}; + +enum +{ + SHADOWMAPDEF_USE_VERTEX_ANIMATION = 0x0001, + SHADOWMAPDEF_USE_BONE_ANIMATION = 0x0002, + SHADOWMAPDEF_ALL = 0x0003, + SHADOWMAPDEF_COUNT = 0x0004 }; enum @@ -595,7 +606,8 @@ enum GLSL_VEC2, GLSL_VEC3, GLSL_VEC4, - GLSL_MAT16 + GLSL_MAT16, + GLSL_MAT16_BONEMATRIX }; typedef enum @@ -686,6 +698,8 @@ typedef enum UNIFORM_ALPHATEST, + UNIFORM_BONEMATRIX, + UNIFORM_COUNT } uniform_t; @@ -849,6 +863,7 @@ typedef enum { SF_FLARE, SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity SF_VAO_MDVMESH, + SF_VAO_IQM, SF_NUM_SURFACE_TYPES, SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) @@ -976,6 +991,9 @@ typedef struct { float *jointMats; float *poseMats; float *bounds; + + int numVaoSurfaces; + struct srfVaoIQModel_s *vaoSurfaces; } iqmData_t; // inter-quake-model surface @@ -989,6 +1007,21 @@ typedef struct srfIQModel_s { int first_influence, num_influences; } srfIQModel_t; +typedef struct srfVaoIQModel_s +{ + surfaceType_t surfaceType; + + iqmData_t *iqmData; + struct srfIQModel_s *iqmSurface; + + // backEnd stats + int numIndexes; + int numVerts; + + // static render data + vao_t *vao; +} srfVaoIQModel_t; + typedef struct srfVaoMdvMesh_s { surfaceType_t surfaceType; @@ -1332,6 +1365,8 @@ typedef struct { uint32_t storedGlState; float vertexAttribsInterpolation; qboolean vertexAnimation; + int boneAnimation; // number of bones + mat4_t boneMatrix[IQM_MAX_JOINTS]; uint32_t vertexAttribsEnabled; // global if no VAOs, tess only otherwise FBO_t *currentFBO; vao_t *currentVao; @@ -1361,6 +1396,7 @@ typedef struct { int glslMajorVersion; int glslMinorVersion; + int glslMaxAnimatedBones; memInfo_t memInfo; @@ -1541,7 +1577,7 @@ typedef struct { shaderProgram_t fogShader[FOGDEF_COUNT]; shaderProgram_t dlightShader[DLIGHTDEF_COUNT]; shaderProgram_t lightallShader[LIGHTDEF_COUNT]; - shaderProgram_t shadowmapShader; + shaderProgram_t shadowmapShader[SHADOWMAPDEF_COUNT]; shaderProgram_t pshadowShader; shaderProgram_t down4xShader; shaderProgram_t bokehShader; @@ -2190,6 +2226,7 @@ void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v); void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v); void GLSL_SetUniformMat4(shaderProgram_t *program, int uniformNum, const mat4_t matrix); +void GLSL_SetUniformMat4BoneMatrix(shaderProgram_t *program, int uniformNum, /*const*/ mat4_t *matrix, int numMatricies); shaderProgram_t *GLSL_GetGenericShaderProgram(int stage); @@ -2244,6 +2281,7 @@ void RB_MDRSurfaceAnim( mdrSurface_t *surface ); qboolean R_LoadIQM (model_t *mod, void *buffer, int filesize, const char *name ); void R_AddIQMSurfaces( trRefEntity_t *ent ); void RB_IQMSurfaceAnim( surfaceType_t *surface ); +void RB_IQMSurfaceAnimVao( srfVaoIQModel_t *surface ); int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, int startFrame, int endFrame, float frac, const char *tagName ); diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index 6fafe367..13b234fe 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -916,6 +916,170 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na } } + // Create VAO surfaces + if ( iqmData->num_surfaces && iqmData->num_joints <= glRefConfig.glslMaxAnimatedBones ) + { + srfVaoIQModel_t *vaoSurf; + srfIQModel_t *surf; + + iqmData->numVaoSurfaces = iqmData->num_surfaces; + iqmData->vaoSurfaces = ri.Hunk_Alloc(sizeof(*iqmData->vaoSurfaces) * iqmData->numVaoSurfaces, h_low); + + vaoSurf = iqmData->vaoSurfaces; + surf = iqmData->surfaces; + for (i = 0; i < iqmData->num_surfaces; i++, vaoSurf++, surf++) + { + uint32_t offset_xyz, offset_st, offset_normal, offset_tangent; + uint32_t offset_blendindexes, offset_blendweights, stride; + uint32_t dataSize, dataOfs; + uint8_t *data; + glIndex_t indexes[SHADER_MAX_INDEXES]; + glIndex_t *ptr; + int *tri; + + offset_xyz = 0; + offset_st = offset_xyz + sizeof(float) * 3; + offset_normal = offset_st + sizeof(float) * 2; + offset_tangent = offset_normal + sizeof(int16_t) * 4; + + if ( iqmData->num_joints ) + { + offset_blendindexes = offset_tangent + sizeof(int16_t) * 4; + offset_blendweights = offset_blendindexes + sizeof(byte) * 4; + + if ( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + stride = offset_blendweights + sizeof(float) * 4; + } else { + stride = offset_blendweights + sizeof(byte) * 4; + } + } + else + { + stride = offset_tangent + sizeof(int16_t) * 4; + } + + dataSize = surf->num_vertexes * stride; + + data = ri.Malloc(dataSize); + dataOfs = 0; + + for ( j = 0; j < surf->num_vertexes; j++ ) + { + int vtx = surf->first_vertex + j; + + // xyz + memcpy(data + dataOfs, &iqmData->positions[vtx*3], sizeof(float) * 3); + dataOfs += sizeof(float) * 3; + + // st + memcpy(data + dataOfs, &iqmData->texcoords[vtx*2], sizeof(float) * 2); + dataOfs += sizeof(float) * 2; + + // normal + R_VaoPackNormal((int16_t*)(data + dataOfs), &iqmData->normals[vtx*3]); + dataOfs += sizeof(int16_t) * 4; + + // tangent + R_VaoPackTangent((int16_t*)(data + dataOfs), &iqmData->tangents[vtx*4]); + dataOfs += sizeof(int16_t) * 4; + + if ( iqmData->num_joints ) + { + // blendindexes + memcpy(data + dataOfs, &blendIndexes[vtx*4], sizeof(byte) * 4); + dataOfs += sizeof(byte) * 4; + + // blendweights + if ( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + memcpy(data + dataOfs, &blendWeights.f[vtx*4], sizeof(float) * 4); + dataOfs += sizeof(float) * 4; + } else { + memcpy(data + dataOfs, &blendWeights.b[vtx*4], sizeof(byte) * 4); + dataOfs += sizeof(byte) * 4; + } + } + } + + tri = iqmData->triangles + 3 * surf->first_triangle; + ptr = indexes; + + for( j = 0; j < surf->num_triangles; j++ ) { + *ptr++ = (*tri++ - surf->first_vertex); + *ptr++ = (*tri++ - surf->first_vertex); + *ptr++ = (*tri++ - surf->first_vertex); + } + + vaoSurf->surfaceType = SF_VAO_IQM; + vaoSurf->iqmData = iqmData; + vaoSurf->iqmSurface = surf; + vaoSurf->numIndexes = surf->num_triangles * 3; + vaoSurf->numVerts = surf->num_vertexes; + + vaoSurf->vao = R_CreateVao(va("staticIQMMesh_VAO '%s'", surf->name), data, dataSize, (byte *)indexes, surf->num_triangles * 3 * sizeof(indexes[0]), VAO_USAGE_STATIC); + + vaoSurf->vao->attribs[ATTR_INDEX_POSITION].enabled = 1; + vaoSurf->vao->attribs[ATTR_INDEX_POSITION].enabled = 1; + vaoSurf->vao->attribs[ATTR_INDEX_TEXCOORD].enabled = 1; + vaoSurf->vao->attribs[ATTR_INDEX_NORMAL ].enabled = 1; + vaoSurf->vao->attribs[ATTR_INDEX_TANGENT ].enabled = 1; + + vaoSurf->vao->attribs[ATTR_INDEX_POSITION].count = 3; + vaoSurf->vao->attribs[ATTR_INDEX_TEXCOORD].count = 2; + vaoSurf->vao->attribs[ATTR_INDEX_NORMAL ].count = 4; + vaoSurf->vao->attribs[ATTR_INDEX_TANGENT ].count = 4; + + vaoSurf->vao->attribs[ATTR_INDEX_POSITION].type = GL_FLOAT; + vaoSurf->vao->attribs[ATTR_INDEX_TEXCOORD].type = GL_FLOAT; + vaoSurf->vao->attribs[ATTR_INDEX_NORMAL ].type = GL_SHORT; + vaoSurf->vao->attribs[ATTR_INDEX_TANGENT ].type = GL_SHORT; + + vaoSurf->vao->attribs[ATTR_INDEX_POSITION].normalized = GL_FALSE; + vaoSurf->vao->attribs[ATTR_INDEX_TEXCOORD].normalized = GL_FALSE; + vaoSurf->vao->attribs[ATTR_INDEX_NORMAL ].normalized = GL_TRUE; + vaoSurf->vao->attribs[ATTR_INDEX_TANGENT ].normalized = GL_TRUE; + + vaoSurf->vao->attribs[ATTR_INDEX_POSITION].offset = offset_xyz; + vaoSurf->vao->attribs[ATTR_INDEX_TEXCOORD].offset = offset_st; + vaoSurf->vao->attribs[ATTR_INDEX_NORMAL ].offset = offset_normal; + vaoSurf->vao->attribs[ATTR_INDEX_TANGENT ].offset = offset_tangent; + + vaoSurf->vao->attribs[ATTR_INDEX_POSITION].stride = stride; + vaoSurf->vao->attribs[ATTR_INDEX_TEXCOORD].stride = stride; + vaoSurf->vao->attribs[ATTR_INDEX_NORMAL ].stride = stride; + vaoSurf->vao->attribs[ATTR_INDEX_TANGENT ].stride = stride; + + if ( iqmData->num_joints ) + { + vaoSurf->vao->attribs[ATTR_INDEX_BONE_INDEXES].enabled = 1; + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].enabled = 1; + + vaoSurf->vao->attribs[ATTR_INDEX_BONE_INDEXES].count = 4; + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].count = 4; + + vaoSurf->vao->attribs[ATTR_INDEX_BONE_INDEXES].type = GL_UNSIGNED_BYTE; + vaoSurf->vao->attribs[ATTR_INDEX_BONE_INDEXES].normalized = GL_FALSE; + + if ( vertexArrayFormat[IQM_BLENDWEIGHTS] == IQM_FLOAT ) { + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].type = GL_FLOAT; + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].normalized = GL_FALSE; + } else { + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].type = GL_UNSIGNED_BYTE; + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].normalized = GL_TRUE; + } + + vaoSurf->vao->attribs[ATTR_INDEX_BONE_INDEXES].offset = offset_blendindexes; + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].offset = offset_blendweights; + + vaoSurf->vao->attribs[ATTR_INDEX_BONE_INDEXES].stride = stride; + vaoSurf->vao->attribs[ATTR_INDEX_BONE_WEIGHTS].stride = stride; + } + + Vao_SetVertexPointers(vaoSurf->vao); + + ri.Free(data); + } + } + return qtrue; } @@ -1017,6 +1181,7 @@ Add all surfaces of this model void R_AddIQMSurfaces( trRefEntity_t *ent ) { iqmData_t *data; srfIQModel_t *surface; + void *drawSurf; int i, j; qboolean personalModel; int cull; @@ -1097,6 +1262,12 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { shader = surface->shader; } + if ( data->numVaoSurfaces ) { + drawSurf = &data->vaoSurfaces[i]; + } else { + drawSurf = surface; + } + // we will add shadows even if the main object isn't visible in the view // stencil shadows can't do personal models unless I polyhedron clip @@ -1105,7 +1276,7 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { && fogNum == 0 && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0, 0, 0 ); + R_AddDrawSurf( drawSurf, tr.shadowShader, 0, 0, 0, 0 ); } // projection shadows work fine with personal models @@ -1113,11 +1284,11 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { && fogNum == 0 && (ent->e.renderfx & RF_SHADOW_PLANE ) && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0, 0, 0 ); + R_AddDrawSurf( drawSurf, tr.projectionShadowShader, 0, 0, 0, 0 ); } if( !personalModel ) { - R_AddDrawSurf( (void *)surface, shader, fogNum, 0, 0, cubemapIndex ); + R_AddDrawSurf( drawSurf, shader, fogNum, 0, 0, cubemapIndex ); } surface++; @@ -1418,6 +1589,73 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { tess.numVertexes += surf->num_vertexes; } +/* +================= +RB_IQMSurfaceAnimVao +================= +*/ +void RB_IQMSurfaceAnimVao(srfVaoIQModel_t * surface) +{ + iqmData_t *data = surface->iqmData; + + if (ShaderRequiresCPUDeforms(tess.shader)) + { + RB_IQMSurfaceAnim((surfaceType_t*)surface->iqmSurface); + return; + } + + if(!surface->vao) + return; + + //RB_CheckVao(surface->vao); + RB_EndSurface(); + RB_BeginSurface(tess.shader, tess.fogNum, tess.cubemapIndex); + + R_BindVao(surface->vao); + + tess.useInternalVao = qfalse; + + tess.numIndexes = surface->numIndexes; + tess.numVertexes = surface->numVerts; + + glState.boneAnimation = data->num_poses; + + if ( glState.boneAnimation ) { + float jointMats[IQM_MAX_JOINTS * 12]; + int frame = data->num_frames ? backEnd.currentEntity->e.frame % data->num_frames : 0; + int oldframe = data->num_frames ? backEnd.currentEntity->e.oldframe % data->num_frames : 0; + float backlerp = backEnd.currentEntity->e.backlerp; + int i; + + // compute interpolated joint matrices + ComputePoseMats( surface->iqmData, frame, oldframe, backlerp, jointMats ); + + // convert row-major order 3x4 matrix to column-major order 4x4 matrix + for ( i = 0; i < data->num_poses; i++ ) { + glState.boneMatrix[i][0] = jointMats[i*12+0]; + glState.boneMatrix[i][1] = jointMats[i*12+4]; + glState.boneMatrix[i][2] = jointMats[i*12+8]; + glState.boneMatrix[i][3] = 0.0f; + glState.boneMatrix[i][4] = jointMats[i*12+1]; + glState.boneMatrix[i][5] = jointMats[i*12+5]; + glState.boneMatrix[i][6] = jointMats[i*12+9]; + glState.boneMatrix[i][7] = 0.0f; + glState.boneMatrix[i][8] = jointMats[i*12+2]; + glState.boneMatrix[i][9] = jointMats[i*12+6]; + glState.boneMatrix[i][10] = jointMats[i*12+10]; + glState.boneMatrix[i][11] = 0.0f; + glState.boneMatrix[i][12] = jointMats[i*12+3]; + glState.boneMatrix[i][13] = jointMats[i*12+7]; + glState.boneMatrix[i][14] = jointMats[i*12+11]; + glState.boneMatrix[i][15] = 1.0f; + } + } + + RB_EndSurface(); + + glState.boneAnimation = 0; +} + int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, int startFrame, int endFrame, float frac, const char *tagName ) { diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index 219efbfd..5300898c 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -908,6 +908,8 @@ static void RB_FogPass( void ) { if (glState.vertexAnimation) index |= FOGDEF_USE_VERTEX_ANIMATION; + else if (glState.boneAnimation) + index |= FOGDEF_USE_BONE_ANIMATION; sp = &tr.fogShader[index]; } @@ -921,6 +923,11 @@ static void RB_FogPass( void ) { GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); GLSL_SetUniformFloat(sp, UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + if (glState.boneAnimation) + { + GLSL_SetUniformMat4BoneMatrix(sp, UNIFORM_BONEMATRIX, glState.boneMatrix, glState.boneAnimation); + } GLSL_SetUniformInt(sp, UNIFORM_DEFORMGEN, deformGen); if (deformGen != DGEN_NONE) @@ -1005,7 +1012,14 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) { - index |= LIGHTDEF_ENTITY; + if (glState.boneAnimation) + { + index |= LIGHTDEF_ENTITY_BONE_ANIMATION; + } + else + { + index |= LIGHTDEF_ENTITY_VERTEX_ANIMATION; + } } if (pStage->stateBits & GLS_ATEST_BITS) @@ -1028,6 +1042,10 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) { shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; } + else if (glState.boneAnimation) + { + shaderAttribs |= GENERICDEF_USE_BONE_ANIMATION; + } if (pStage->stateBits & GLS_ATEST_BITS) { @@ -1043,7 +1061,14 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) { - index |= LIGHTDEF_ENTITY; + if (glState.boneAnimation) + { + index |= LIGHTDEF_ENTITY_BONE_ANIMATION; + } + else + { + index |= LIGHTDEF_ENTITY_VERTEX_ANIMATION; + } } if (r_sunlightMode->integer && (backEnd.viewParms.flags & VPF_USESUNLIGHT) && (index & LIGHTDEF_LIGHTTYPE_MASK)) @@ -1074,6 +1099,11 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) GLSL_SetUniformVec3(sp, UNIFORM_LOCALVIEWORIGIN, backEnd.or.viewOrigin); GLSL_SetUniformFloat(sp, UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + if (glState.boneAnimation) + { + GLSL_SetUniformMat4BoneMatrix(sp, UNIFORM_BONEMATRIX, glState.boneMatrix, glState.boneAnimation); + } GLSL_SetUniformInt(sp, UNIFORM_DEFORMGEN, deformGen); if (deformGen != DGEN_NONE) @@ -1368,7 +1398,16 @@ static void RB_RenderShadowmap( shaderCommands_t *input ) ComputeDeformValues(&deformGen, deformParams); { - shaderProgram_t *sp = &tr.shadowmapShader; + shaderProgram_t *sp = &tr.shadowmapShader[0]; + + if (glState.vertexAnimation) + { + sp = &tr.shadowmapShader[SHADOWMAPDEF_USE_VERTEX_ANIMATION]; + } + else if (glState.boneAnimation) + { + sp = &tr.shadowmapShader[SHADOWMAPDEF_USE_BONE_ANIMATION]; + } vec4_t vector; @@ -1380,6 +1419,11 @@ static void RB_RenderShadowmap( shaderCommands_t *input ) GLSL_SetUniformFloat(sp, UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + if (glState.boneAnimation) + { + GLSL_SetUniformMat4BoneMatrix(sp, UNIFORM_BONEMATRIX, glState.boneMatrix, glState.boneAnimation); + } + GLSL_SetUniformInt(sp, UNIFORM_DEFORMGEN, deformGen); if (deformGen != DGEN_NONE) { diff --git a/code/renderergl2/tr_surface.c b/code/renderergl2/tr_surface.c index 66e894ce..3151638a 100644 --- a/code/renderergl2/tr_surface.c +++ b/code/renderergl2/tr_surface.c @@ -1313,4 +1313,5 @@ void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = { (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY (void(*)(void*))RB_SurfaceVaoMdvMesh, // SF_VAO_MDVMESH + (void(*)(void*))RB_IQMSurfaceAnimVao, // SF_VAO_IQM };