diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..8724a8aa --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,14 @@ +# These are supported funding model platforms + +patreon: timedoctor +open_collective: # Replace with a single Open Collective username +ko_fi: jackslater +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://www.patreon.com/icculus', 'https://www.patreon.com/SmileTheory', 'https://ko-fi.com/zturtleman'] + + +#please add your links to this if you've been a long-time contributor! diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 528df6a9..6c257952 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -3088,8 +3088,10 @@ static __attribute__ ((format (printf, 2, 3))) void QDECL CL_RefPrintf( int prin Com_Printf ("%s", msg); } else if ( print_level == PRINT_WARNING ) { Com_Printf (S_COLOR_YELLOW "%s", msg); // yellow + } else if ( print_level == PRINT_ERROR ) { + Com_Printf (S_COLOR_RED "%s", msg); // red } else if ( print_level == PRINT_DEVELOPER ) { - Com_DPrintf (S_COLOR_RED "%s", msg); // red + Com_DPrintf (S_COLOR_RED "%s", msg); // red - developer only } } diff --git a/code/q3_ui/ui_video.c b/code/q3_ui/ui_video.c index 093b5a2c..536055db 100644 --- a/code/q3_ui/ui_video.c +++ b/code/q3_ui/ui_video.c @@ -358,6 +358,7 @@ static int resToRatio[ MAX_RESOLUTIONS ]; static char resbuf[ MAX_STRING_CHARS ]; static const char* detectedResolutions[ MAX_RESOLUTIONS ]; +static char currentResolution[ 20 ]; static const char** resolutions = builtinResolutions; static qboolean resolutionsDetected = qfalse; @@ -487,7 +488,7 @@ GraphicsOptions_GetResolutions */ static void GraphicsOptions_GetResolutions( void ) { - Q_strncpyz(resbuf, UI_Cvar_VariableString("r_availableModes"), sizeof(resbuf)); + trap_Cvar_VariableStringBuffer("r_availableModes", resbuf, sizeof(resbuf)); if(*resbuf) { char* s = resbuf; @@ -501,11 +502,26 @@ static void GraphicsOptions_GetResolutions( void ) } detectedResolutions[ i ] = NULL; - if( i > 0 ) + // add custom resolution if not in mode list + if ( i < ARRAY_LEN(detectedResolutions)-1 ) { - resolutions = detectedResolutions; - resolutionsDetected = qtrue; + Com_sprintf( currentResolution, sizeof ( currentResolution ), "%dx%d", uis.glconfig.vidWidth, uis.glconfig.vidHeight ); + + for( i = 0; detectedResolutions[ i ]; i++ ) + { + if ( strcmp( detectedResolutions[ i ], currentResolution ) == 0 ) + break; + } + + if ( detectedResolutions[ i ] == NULL ) + { + detectedResolutions[ i++ ] = currentResolution; + detectedResolutions[ i ] = NULL; + } } + + resolutions = detectedResolutions; + resolutionsDetected = qtrue; } } diff --git a/code/qcommon/q_shared.c b/code/qcommon/q_shared.c index a9fd68c7..e845c283 100644 --- a/code/qcommon/q_shared.c +++ b/code/qcommon/q_shared.c @@ -23,6 +23,28 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // q_shared.c -- stateless support routines that are included in each code dll #include "q_shared.h" +// ^[0-9a-zA-Z] +qboolean Q_IsColorString(const char *p) { + if (!p) + return qfalse; + + if (p[0] != Q_COLOR_ESCAPE) + return qfalse; + + if (p[1] == 0) + return qfalse; + + // isalnum expects a signed integer in the range -1 (EOF) to 255, or it might assert on undefined behaviour + // a dereferenced char pointer has the range -128 to 127, so we just need to rangecheck the negative part + if (p[1] < 0) + return qfalse; + + if (isalnum(p[1]) == 0) + return qfalse; + + return qtrue; +} + float Com_Clamp( float min, float max, float value ) { if ( value < min ) { return min; diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index 629334af..86d9ac47 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -368,6 +368,8 @@ typedef vec_t vec3_t[3]; typedef vec_t vec4_t[4]; typedef vec_t vec5_t[5]; +typedef vec_t quat_t[4]; + typedef int fixed4_t; typedef int fixed8_t; typedef int fixed16_t; @@ -409,7 +411,7 @@ extern vec4_t colorMdGrey; extern vec4_t colorDkGrey; #define Q_COLOR_ESCAPE '^' -#define Q_IsColorString(p) ((p) && *(p) == Q_COLOR_ESCAPE && *((p)+1) && isalnum(*((p)+1))) // ^[0-9a-zA-Z] +qboolean Q_IsColorString(const char *p); // ^[0-9a-zA-Z] #define COLOR_BLACK '0' #define COLOR_RED '1' @@ -578,6 +580,8 @@ typedef struct { #define Byte4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) +#define QuatCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) + #define SnapVector(v) {v[0]=((int)(v[0]));v[1]=((int)(v[1]));v[2]=((int)(v[2]));} // just in case you don't want to use the macros vec_t _DotProduct( const vec3_t v1, const vec3_t v2 ); diff --git a/code/renderercommon/tr_common.h b/code/renderercommon/tr_common.h index 9c7e176b..c183ca8b 100644 --- a/code/renderercommon/tr_common.h +++ b/code/renderercommon/tr_common.h @@ -80,6 +80,7 @@ extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared duri extern qboolean textureFilterAnisotropic; extern int maxAnisotropy; extern float displayAspect; +extern qboolean haveClampToEdge; // // cvars diff --git a/code/renderergl1/tr_backend.c b/code/renderergl1/tr_backend.c index abf15f2e..56cf37d1 100644 --- a/code/renderergl1/tr_backend.c +++ b/code/renderergl1/tr_backend.c @@ -794,8 +794,8 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, haveClampToEdge ? GL_CLAMP_TO_EDGE : GL_CLAMP ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, haveClampToEdge ? GL_CLAMP_TO_EDGE : GL_CLAMP ); } else { if (dirty) { // otherwise, just subimage upload it so that drivers can tell we are going to be changing diff --git a/code/renderergl1/tr_image.c b/code/renderergl1/tr_image.c index 9603ff71..a63de580 100644 --- a/code/renderergl1/tr_image.c +++ b/code/renderergl1/tr_image.c @@ -866,7 +866,7 @@ image_t *R_CreateImage( const char *name, byte *pic, int width, int height, image->width = width; image->height = height; if (flags & IMGFLAG_CLAMPTOEDGE) - glWrapClampMode = GL_CLAMP_TO_EDGE; + glWrapClampMode = haveClampToEdge ? GL_CLAMP_TO_EDGE : GL_CLAMP; else glWrapClampMode = GL_REPEAT; diff --git a/code/renderergl1/tr_init.c b/code/renderergl1/tr_init.c index 2c7d91de..2539ffa7 100644 --- a/code/renderergl1/tr_init.c +++ b/code/renderergl1/tr_init.c @@ -27,6 +27,7 @@ glconfig_t glConfig; qboolean textureFilterAnisotropic = qfalse; int maxAnisotropy = 0; float displayAspect = 0.0f; +qboolean haveClampToEdge = qfalse; glstate_t glState; @@ -1294,6 +1295,7 @@ void RE_Shutdown( qboolean destroyWindow ) { textureFilterAnisotropic = qfalse; maxAnisotropy = 0; displayAspect = 0.0f; + haveClampToEdge = qfalse; Com_Memset( &glState, 0, sizeof( glState ) ); } diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index fe42f4de..e07f575b 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -589,6 +589,12 @@ typedef struct { drawVert_t *verts; } srfTriangles_t; +typedef struct { + vec3_t translate; + quat_t rotate; + vec3_t scale; +} iqmTransform_t; + // inter-quake-model typedef struct { int num_vertexes; @@ -623,8 +629,9 @@ typedef struct { char *jointNames; int *jointParents; - float *jointMats; - float *poseMats; + float *bindJoints; // [num_joints * 12] + float *invBindJoints; // [num_joints * 12] + iqmTransform_t *poses; // [num_frames * num_poses] float *bounds; } iqmData_t; diff --git a/code/renderergl1/tr_model_iqm.c b/code/renderergl1/tr_model_iqm.c index 02616469..fe205ff4 100644 --- a/code/renderergl1/tr_model_iqm.c +++ b/code/renderergl1/tr_model_iqm.c @@ -2,6 +2,7 @@ =========================================================================== Copyright (C) 2011 Thilo Schulz Copyright (C) 2011 Matthias Bentrup +Copyright (C) 2011-2019 Zack Middleton This file is part of Quake III Arena source code. @@ -44,7 +45,7 @@ static qboolean IQM_CheckRange( iqmHeader_t *header, int offset, } // "multiply" 3x4 matrices, these are assumed to be the top 3 rows // of a 4x4 matrix with the last row = (0 0 0 1) -static void Matrix34Multiply( float *a, float *b, float *out ) { +static void Matrix34Multiply( const float *a, const float *b, float *out ) { out[ 0] = a[0] * b[0] + a[1] * b[4] + a[ 2] * b[ 8]; out[ 1] = a[0] * b[1] + a[1] * b[5] + a[ 2] * b[ 9]; out[ 2] = a[0] * b[2] + a[1] * b[6] + a[ 2] * b[10]; @@ -58,23 +59,7 @@ static void Matrix34Multiply( float *a, float *b, float *out ) { out[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10]; out[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11]; } -static void InterpolateMatrix( float *a, float *b, float lerp, float *mat ) { - float unLerp = 1.0f - lerp; - - mat[ 0] = a[ 0] * unLerp + b[ 0] * lerp; - mat[ 1] = a[ 1] * unLerp + b[ 1] * lerp; - mat[ 2] = a[ 2] * unLerp + b[ 2] * lerp; - mat[ 3] = a[ 3] * unLerp + b[ 3] * lerp; - mat[ 4] = a[ 4] * unLerp + b[ 4] * lerp; - mat[ 5] = a[ 5] * unLerp + b[ 5] * lerp; - mat[ 6] = a[ 6] * unLerp + b[ 6] * lerp; - mat[ 7] = a[ 7] * unLerp + b[ 7] * lerp; - mat[ 8] = a[ 8] * unLerp + b[ 8] * lerp; - mat[ 9] = a[ 9] * unLerp + b[ 9] * lerp; - mat[10] = a[10] * unLerp + b[10] * lerp; - mat[11] = a[11] * unLerp + b[11] * lerp; -} -static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, +static void JointToMatrix( const quat_t rot, const vec3_t scale, const vec3_t trans, float *mat ) { float xx = 2.0f * rot[0] * rot[0]; float yy = 2.0f * rot[1] * rot[1]; @@ -99,8 +84,7 @@ static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, mat[10] = scale[2] * (1.0f - (xx + yy)); mat[11] = trans[2]; } -static void Matrix34Invert( float *inMat, float *outMat ) -{ +static void Matrix34Invert( const float *inMat, float *outMat ) { vec3_t trans; float invSqrLen, *v; @@ -120,6 +104,62 @@ static void Matrix34Invert( float *inMat, float *outMat ) outMat[ 7] = -DotProduct(outMat + 4, trans); outMat[11] = -DotProduct(outMat + 8, trans); } +static void QuatSlerp(const quat_t from, const quat_t _to, float fraction, quat_t out) { + float angle, cosAngle, sinAngle, backlerp, lerp; + quat_t to; + + // cos() of angle + cosAngle = from[0] * _to[0] + from[1] * _to[1] + from[2] * _to[2] + from[3] * _to[3]; + + // negative handling is needed for taking shortest path (required for model joints) + if ( cosAngle < 0.0f ) { + cosAngle = -cosAngle; + to[0] = - _to[0]; + to[1] = - _to[1]; + to[2] = - _to[2]; + to[3] = - _to[3]; + } else { + QuatCopy( _to, to ); + } + + if ( cosAngle < 0.999999f ) { + // spherical lerp (slerp) + angle = acosf( cosAngle ); + sinAngle = sinf( angle ); + backlerp = sinf( ( 1.0f - fraction ) * angle ) / sinAngle; + lerp = sinf( fraction * angle ) / sinAngle; + } else { + // linear lerp + backlerp = 1.0f - fraction; + lerp = fraction; + } + + out[0] = from[0] * backlerp + to[0] * lerp; + out[1] = from[1] * backlerp + to[1] * lerp; + out[2] = from[2] * backlerp + to[2] * lerp; + out[3] = from[3] * backlerp + to[3] * lerp; +} +static vec_t QuatNormalize2( const quat_t v, quat_t out) { + float length, ilength; + + length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + v[3]*v[3]; + + if (length) { + /* writing it this way allows gcc to recognize that rsqrt can be used */ + ilength = 1/(float)sqrt (length); + /* sqrt(length) = length * (1 / sqrt(length)) */ + length *= ilength; + out[0] = v[0]*ilength; + out[1] = v[1]*ilength; + out[2] = v[2]*ilength; + out[3] = v[3]*ilength; + } else { + out[0] = out[1] = out[2] = 0; + out[3] = -1; + } + + return length; +} /* ================= @@ -139,7 +179,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na unsigned short *framedata; char *str; int i, j, k; - float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f}; + iqmTransform_t *transform; float *mat, *matInv; size_t size, joint_names; byte *dataPtr; @@ -559,10 +599,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na if( header->num_joints ) { size += joint_names; // joint names size += header->num_joints * sizeof(int); // joint parents - size += header->num_joints * 12 * sizeof( float ); // joint mats + size += header->num_joints * 12 * sizeof(float); // bind joint matricies + size += header->num_joints * 12 * sizeof(float); // inverse bind joint matricies } if( header->num_poses ) { - size += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats + size += header->num_poses * header->num_frames * sizeof(iqmTransform_t); // pose transforms } if( header->ofs_bounds ) { size += header->num_frames * 6 * sizeof(float); // model bounds @@ -633,12 +674,15 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na iqmData->jointParents = (int*)dataPtr; dataPtr += header->num_joints * sizeof(int); // joint parents - iqmData->jointMats = (float*)dataPtr; - dataPtr += header->num_joints * 12 * sizeof( float ); // joint mats + iqmData->bindJoints = (float*)dataPtr; + dataPtr += header->num_joints * 12 * sizeof(float); // bind joint matricies + + iqmData->invBindJoints = (float*)dataPtr; + dataPtr += header->num_joints * 12 * sizeof(float); // inverse bind joint matricies } if( header->num_poses ) { - iqmData->poseMats = (float*)dataPtr; - dataPtr += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats + iqmData->poses = (iqmTransform_t*)dataPtr; + dataPtr += header->num_poses * header->num_frames * sizeof(iqmTransform_t); // pose transforms } if( header->ofs_bounds ) { iqmData->bounds = (float*)dataPtr; @@ -804,22 +848,23 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na iqmData->jointParents[i] = joint->parent; } - // calculate joint matrices and their inverses - // joint inverses are needed only until the pose matrices are calculated - mat = iqmData->jointMats; - matInv = jointInvMats; + // calculate bind joint matrices and their inverses + mat = iqmData->bindJoints; + matInv = iqmData->invBindJoints; joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); for( i = 0; i < header->num_joints; i++, joint++ ) { float baseFrame[12], invBaseFrame[12]; + QuatNormalize2( joint->rotate, joint->rotate ); + JointToMatrix( joint->rotate, joint->scale, joint->translate, baseFrame ); Matrix34Invert( baseFrame, invBaseFrame ); if ( joint->parent >= 0 ) { - Matrix34Multiply( iqmData->jointMats + 12 * joint->parent, baseFrame, mat ); + Matrix34Multiply( iqmData->bindJoints + 12 * joint->parent, baseFrame, mat ); mat += 12; - Matrix34Multiply( invBaseFrame, jointInvMats + 12 * joint->parent, matInv ); + Matrix34Multiply( invBaseFrame, iqmData->invBindJoints + 12 * joint->parent, matInv ); matInv += 12; } else @@ -834,16 +879,15 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na if( header->num_poses ) { - // calculate pose matrices + // calculate pose transforms + transform = iqmData->poses; framedata = (unsigned short *)((byte *)header + header->ofs_frames); - mat = iqmData->poseMats; for( i = 0; i < header->num_frames; i++ ) { pose = (iqmPose_t *)((byte *)header + header->ofs_poses); - for( j = 0; j < header->num_poses; j++, pose++ ) { + for( j = 0; j < header->num_poses; j++, pose++, transform++ ) { vec3_t translate; - vec4_t rotate; + quat_t rotate; vec3_t scale; - float mat1[12], mat2[12]; translate[0] = pose->channeloffset[0]; if( pose->mask & 0x001) @@ -878,18 +922,9 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na if( pose->mask & 0x200) scale[2] += *framedata++ * pose->channelscale[9]; - // construct transformation matrix - JointToMatrix( rotate, scale, translate, mat1 ); - - if( pose->parent >= 0 ) { - Matrix34Multiply( iqmData->jointMats + 12 * pose->parent, - mat1, mat2 ); - } else { - Com_Memcpy( mat2, mat1, sizeof(mat1) ); - } - - Matrix34Multiply( mat2, jointInvMats + 12 * j, mat ); - mat += 12; + VectorCopy( translate, transform->translate ); + QuatNormalize2( rotate, transform->rotate ); + VectorCopy( scale, transform->scale ); } } } @@ -1128,37 +1163,59 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { static void ComputePoseMats( iqmData_t *data, int frame, int oldframe, - float backlerp, float *mat ) { - float *mat1, *mat2; - int *joint = data->jointParents; - int i; - + float backlerp, float *poseMats ) { + iqmTransform_t relativeJoints[IQM_MAX_JOINTS]; + iqmTransform_t *relativeJoint; + const iqmTransform_t *pose; + const iqmTransform_t *oldpose; + const int *jointParent; + const float *invBindMat; + float *poseMat, lerp; + int i; + + relativeJoint = relativeJoints; + + // copy or lerp animation frame pose if ( oldframe == frame ) { - mat1 = data->poseMats + 12 * data->num_poses * frame; - for( i = 0; i < data->num_poses; i++, joint++ ) { - if( *joint >= 0 ) { - Matrix34Multiply( mat + 12 * *joint, - mat1 + 12*i, mat + 12*i ); - } else { - Com_Memcpy( mat + 12*i, mat1 + 12*i, 12 * sizeof(float) ); - } + pose = &data->poses[frame * data->num_poses]; + for ( i = 0; i < data->num_poses; i++, pose++, relativeJoint++ ) { + VectorCopy( pose->translate, relativeJoint->translate ); + QuatCopy( pose->rotate, relativeJoint->rotate ); + VectorCopy( pose->scale, relativeJoint->scale ); } - } else { - mat1 = data->poseMats + 12 * data->num_poses * frame; - mat2 = data->poseMats + 12 * data->num_poses * oldframe; - - for( i = 0; i < data->num_poses; i++, joint++ ) { - if( *joint >= 0 ) { - float tmpMat[12]; - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, tmpMat ); - Matrix34Multiply( mat + 12 * *joint, - tmpMat, mat + 12*i ); - - } else { - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, mat + 12*i ); - } + } else { + lerp = 1.0f - backlerp; + pose = &data->poses[frame * data->num_poses]; + oldpose = &data->poses[oldframe * data->num_poses]; + for ( i = 0; i < data->num_poses; i++, oldpose++, pose++, relativeJoint++ ) { + relativeJoint->translate[0] = oldpose->translate[0] * backlerp + pose->translate[0] * lerp; + relativeJoint->translate[1] = oldpose->translate[1] * backlerp + pose->translate[1] * lerp; + relativeJoint->translate[2] = oldpose->translate[2] * backlerp + pose->translate[2] * lerp; + + relativeJoint->scale[0] = oldpose->scale[0] * backlerp + pose->scale[0] * lerp; + relativeJoint->scale[1] = oldpose->scale[1] * backlerp + pose->scale[1] * lerp; + relativeJoint->scale[2] = oldpose->scale[2] * backlerp + pose->scale[2] * lerp; + + QuatSlerp( oldpose->rotate, pose->rotate, lerp, relativeJoint->rotate ); + } + } + + // multiply by inverse of bind pose and parent 'pose mat' (bind pose transform matrix) + relativeJoint = relativeJoints; + jointParent = data->jointParents; + invBindMat = data->invBindJoints; + poseMat = poseMats; + for ( i = 0; i < data->num_poses; i++, relativeJoint++, jointParent++, invBindMat += 12, poseMat += 12 ) { + float mat1[12], mat2[12]; + + JointToMatrix( relativeJoint->rotate, relativeJoint->scale, relativeJoint->translate, mat1 ); + + if ( *jointParent >= 0 ) { + Matrix34Multiply( &data->bindJoints[(*jointParent)*12], mat1, mat2 ); + Matrix34Multiply( mat2, invBindMat, mat1 ); + Matrix34Multiply( &poseMats[(*jointParent)*12], mat1, poseMat ); + } else { + Matrix34Multiply( mat1, invBindMat, poseMat ); } } } @@ -1169,7 +1226,7 @@ static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, int i; if ( data->num_poses == 0 ) { - Com_Memcpy( mat, data->jointMats, data->num_joints * 12 * sizeof(float) ); + Com_Memcpy( mat, data->bindJoints, data->num_joints * 12 * sizeof(float) ); return; } @@ -1181,7 +1238,7 @@ static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, Com_Memcpy(outmat, mat1, sizeof(outmat)); - Matrix34Multiply( outmat, data->jointMats + 12*i, mat1 ); + Matrix34Multiply( outmat, data->bindJoints + 12*i, mat1 ); } } @@ -1246,19 +1303,20 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { float *nrmMat = &influenceNrmMat[9*i]; int j; float blendWeights[4]; - int numWeights; - - for ( numWeights = 0; numWeights < 4; numWeights++ ) { - if ( data->blendWeightsType == IQM_FLOAT ) - blendWeights[numWeights] = data->influenceBlendWeights.f[4*influence + numWeights]; - else - blendWeights[numWeights] = (float)data->influenceBlendWeights.b[4*influence + numWeights] / 255.0f; - if ( blendWeights[numWeights] <= 0.0f ) - break; + if ( data->blendWeightsType == IQM_FLOAT ) { + blendWeights[0] = data->influenceBlendWeights.f[4*influence + 0]; + blendWeights[1] = data->influenceBlendWeights.f[4*influence + 1]; + blendWeights[2] = data->influenceBlendWeights.f[4*influence + 2]; + blendWeights[3] = data->influenceBlendWeights.f[4*influence + 3]; + } else { + blendWeights[0] = (float)data->influenceBlendWeights.b[4*influence + 0] / 255.0f; + blendWeights[1] = (float)data->influenceBlendWeights.b[4*influence + 1] / 255.0f; + blendWeights[2] = (float)data->influenceBlendWeights.b[4*influence + 2] / 255.0f; + blendWeights[3] = (float)data->influenceBlendWeights.b[4*influence + 3] / 255.0f; } - if ( numWeights == 0 ) { + if ( blendWeights[0] <= 0.0f ) { // no blend joint, use identity matrix. vtxMat[0] = identityMatrix[0]; vtxMat[1] = identityMatrix[1]; @@ -1288,7 +1346,11 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { vtxMat[10] = blendWeights[0] * poseMats[12 * data->influenceBlendIndexes[4*influence + 0] + 10]; vtxMat[11] = blendWeights[0] * poseMats[12 * data->influenceBlendIndexes[4*influence + 0] + 11]; - for( j = 1; j < numWeights; j++ ) { + for( j = 1; j < 3; j++ ) { + if ( blendWeights[j] <= 0.0f ) { + break; + } + vtxMat[0] += blendWeights[j] * poseMats[12 * data->influenceBlendIndexes[4*influence + j] + 0]; vtxMat[1] += blendWeights[j] * poseMats[12 * data->influenceBlendIndexes[4*influence + j] + 1]; vtxMat[2] += blendWeights[j] * poseMats[12 * data->influenceBlendIndexes[4*influence + j] + 2]; diff --git a/code/renderergl2/glsl/lightall_fp.glsl b/code/renderergl2/glsl/lightall_fp.glsl index 6465bd84..26f9a995 100644 --- a/code/renderergl2/glsl/lightall_fp.glsl +++ b/code/renderergl2/glsl/lightall_fp.glsl @@ -143,6 +143,35 @@ float RayIntersectDisplaceMap(vec2 dp, vec2 ds, sampler2D normalMap) return bestDepth; } + +float LightRay(vec2 dp, vec2 ds, sampler2D normalMap) +{ + const int linearSearchSteps = 16; + + // current size of search window + float size = 1.0 / float(linearSearchSteps); + + // current height from initial texel depth + float height = 0.0; + + float startDepth = SampleDepth(normalMap, dp); + + // find a collision or escape + for(int i = 0; i < linearSearchSteps - 1; ++i) + { + height += size; + + if (startDepth < height) + return 1.0; + + float t = SampleDepth(normalMap, dp + ds * height); + + if (startDepth > t + height) + return 0.0; + } + + return 1.0; +} #endif vec3 CalcDiffuse(vec3 diffuseAlbedo, float NH, float EH, float roughness) @@ -193,6 +222,37 @@ float CalcLightAttenuation(float point, float normDist) return attenuation; } +#if defined(USE_BOX_CUBEMAP_PARALLAX) +vec4 hitCube(vec3 ray, vec3 pos, vec3 invSize, float lod, samplerCube tex) +{ + // find any hits on cubemap faces facing the camera + vec3 scale = (sign(ray) - pos) / ray; + + // find the nearest hit + float minScale = min(min(scale.x, scale.y), scale.z); + + // if the nearest hit is behind the camera, ignore + // should not be necessary as long as pos is inside the cube + //if (minScale < 0.0) + //return vec4(0.0); + + // calculate the hit position, that's our texture coordinates + vec3 tc = pos + ray * minScale; + + // if the texture coordinates are outside the cube, ignore + // necessary since we're not fading out outside the cube + if (any(greaterThan(abs(tc), vec3(1.00001)))) + return vec4(0.0); + + // fade out when approaching the cubemap edges + //vec3 fade3 = abs(pos); + //float fade = max(max(fade3.x, fade3.y), fade3.z); + //fade = clamp(1.0 - fade, 0.0, 1.0); + + //return vec4(textureCubeLod(tex, tc, lod).rgb * fade, fade); + return vec4(textureCubeLod(tex, tc, lod).rgb, 1.0); +} +#endif void main() { @@ -222,7 +282,7 @@ void main() vec2 texCoords = var_TexCoords.xy; #if defined(USE_PARALLAXMAP) - vec3 offsetDir = viewDir * tangentToWorld; + vec3 offsetDir = E * tangentToWorld; offsetDir.xy *= -u_NormalScale.a / offsetDir.z; @@ -289,6 +349,13 @@ void main() #endif #endif + #if defined(USE_PARALLAXMAP) && defined(USE_PARALLAXMAP_SHADOWS) + offsetDir = L * tangentToWorld; + offsetDir.xy *= u_NormalScale.a / offsetDir.z; + lightColor *= LightRay(texCoords, offsetDir.xy, u_NormalMap); + #endif + + #if !defined(USE_LIGHT_VECTOR) ambientColor = lightColor; float surfNL = clamp(dot(var_Normal.xyz, L), 0.0, 1.0); @@ -374,7 +441,11 @@ void main() // from http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ vec3 parallax = u_CubeMapInfo.xyz + u_CubeMapInfo.w * viewDir; + #if defined(USE_BOX_CUBEMAP_PARALLAX) + vec3 cubeLightColor = hitCube(R * u_CubeMapInfo.w, parallax, u_CubeMapInfo.www, ROUGHNESS_MIPS * roughness, u_CubeMap).rgb * u_EnableTextures.w; + #else vec3 cubeLightColor = textureCubeLod(u_CubeMap, R + parallax, ROUGHNESS_MIPS * roughness).rgb * u_EnableTextures.w; + #endif // normalize cubemap based on last roughness mip (~diffuse) // multiplying cubemap values by lighting below depends on either this or the cubemap being normalized at generation @@ -423,6 +494,12 @@ void main() // enable when point lights are supported as primary lights //lightColor *= CalcLightAttenuation(float(u_PrimaryLightDir.w > 0.0), u_PrimaryLightDir.w / sqrLightDist); + #if defined(USE_PARALLAXMAP) && defined(USE_PARALLAXMAP_SHADOWS) + offsetDir = L2 * tangentToWorld; + offsetDir.xy *= u_NormalScale.a / offsetDir.z; + lightColor *= LightRay(texCoords, offsetDir.xy, u_NormalMap); + #endif + gl_FragColor.rgb += lightColor * reflectance * NL2; #endif diff --git a/code/renderergl2/tr_glsl.c b/code/renderergl2/tr_glsl.c index 218017de..0d95d0df 100644 --- a/code/renderergl2/tr_glsl.c +++ b/code/renderergl2/tr_glsl.c @@ -1122,6 +1122,9 @@ void GLSL_InitGPUShaders(void) Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP\n"); if (r_parallaxMapping->integer > 1) Q_strcat(extradefines, 1024, "#define USE_RELIEFMAP\n"); + + if (r_parallaxMapShadows->integer) + Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP_SHADOWS\n"); } } @@ -1129,9 +1132,15 @@ void GLSL_InitGPUShaders(void) Q_strcat(extradefines, 1024, "#define USE_SPECULARMAP\n"); if (r_cubeMapping->integer) + { Q_strcat(extradefines, 1024, "#define USE_CUBEMAP\n"); + if (r_cubeMapping->integer == 2) + Q_strcat(extradefines, 1024, "#define USE_BOX_CUBEMAP_PARALLAX\n"); + } else if (r_deluxeSpecular->value > 0.000001f) + { Q_strcat(extradefines, 1024, va("#define r_deluxeSpecular %f\n", r_deluxeSpecular->value)); + } switch (r_glossType->integer) { diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index ce48925d..275d621e 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -30,6 +30,7 @@ glRefConfig_t glRefConfig; qboolean textureFilterAnisotropic = qfalse; int maxAnisotropy = 0; float displayAspect = 0.0f; +qboolean haveClampToEdge = qfalse; glstate_t glState; @@ -131,6 +132,7 @@ cvar_t *r_normalMapping; cvar_t *r_specularMapping; cvar_t *r_deluxeMapping; cvar_t *r_parallaxMapping; +cvar_t *r_parallaxMapShadows; cvar_t *r_cubeMapping; cvar_t *r_cubemapSize; cvar_t *r_deluxeSpecular; @@ -284,6 +286,12 @@ static void InitOpenGL( void ) } } + // check for GLSL function textureCubeLod() + if ( r_cubeMapping->integer && !QGL_VERSION_ATLEAST( 3, 0 ) ) { + ri.Printf( PRINT_WARNING, "WARNING: Disabled r_cubeMapping because it requires OpenGL 3.0\n" ); + ri.Cvar_Set( "r_cubeMapping", "0" ); + } + // set default state GL_SetDefaultState(); } @@ -1235,6 +1243,7 @@ void R_Register( void ) r_specularMapping = ri.Cvar_Get( "r_specularMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_deluxeMapping = ri.Cvar_Get( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_parallaxMapping = ri.Cvar_Get( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_parallaxMapShadows = ri.Cvar_Get( "r_parallaxMapShadows", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_cubeMapping = ri.Cvar_Get( "r_cubeMapping", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_cubemapSize = ri.Cvar_Get( "r_cubemapSize", "128", CVAR_ARCHIVE | CVAR_LATCH ); r_deluxeSpecular = ri.Cvar_Get("r_deluxeSpecular", "0.3", CVAR_ARCHIVE | CVAR_LATCH); @@ -1544,6 +1553,7 @@ void RE_Shutdown( qboolean destroyWindow ) { textureFilterAnisotropic = qfalse; maxAnisotropy = 0; displayAspect = 0.0f; + haveClampToEdge = qfalse; Com_Memset( &glState, 0, sizeof( glState ) ); } diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index ea6d70fe..39f6f7a2 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -954,6 +954,12 @@ typedef struct srfBspSurface_s float *heightLodError; } srfBspSurface_t; +typedef struct { + vec3_t translate; + quat_t rotate; + vec3_t scale; +} iqmTransform_t; + // inter-quake-model typedef struct { int num_vertexes; @@ -988,8 +994,9 @@ typedef struct { char *jointNames; int *jointParents; - float *jointMats; - float *poseMats; + float *bindJoints; // [num_joints * 12] + float *invBindJoints; // [num_joints * 12] + iqmTransform_t *poses; // [num_frames * num_poses] float *bounds; int numVaoSurfaces; @@ -1771,6 +1778,7 @@ extern cvar_t *r_normalMapping; extern cvar_t *r_specularMapping; extern cvar_t *r_deluxeMapping; extern cvar_t *r_parallaxMapping; +extern cvar_t *r_parallaxMapShadows; extern cvar_t *r_cubeMapping; extern cvar_t *r_cubemapSize; extern cvar_t *r_deluxeSpecular; diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index 1a6e1e5a..fc345a32 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -2,6 +2,7 @@ =========================================================================== Copyright (C) 2011 Thilo Schulz Copyright (C) 2011 Matthias Bentrup +Copyright (C) 2011-2019 Zack Middleton This file is part of Quake III Arena source code. @@ -44,7 +45,7 @@ static qboolean IQM_CheckRange( iqmHeader_t *header, int offset, } // "multiply" 3x4 matrices, these are assumed to be the top 3 rows // of a 4x4 matrix with the last row = (0 0 0 1) -static void Matrix34Multiply( float *a, float *b, float *out ) { +static void Matrix34Multiply( const float *a, const float *b, float *out ) { out[ 0] = a[0] * b[0] + a[1] * b[4] + a[ 2] * b[ 8]; out[ 1] = a[0] * b[1] + a[1] * b[5] + a[ 2] * b[ 9]; out[ 2] = a[0] * b[2] + a[1] * b[6] + a[ 2] * b[10]; @@ -58,23 +59,7 @@ static void Matrix34Multiply( float *a, float *b, float *out ) { out[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10]; out[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11]; } -static void InterpolateMatrix( float *a, float *b, float lerp, float *mat ) { - float unLerp = 1.0f - lerp; - - mat[ 0] = a[ 0] * unLerp + b[ 0] * lerp; - mat[ 1] = a[ 1] * unLerp + b[ 1] * lerp; - mat[ 2] = a[ 2] * unLerp + b[ 2] * lerp; - mat[ 3] = a[ 3] * unLerp + b[ 3] * lerp; - mat[ 4] = a[ 4] * unLerp + b[ 4] * lerp; - mat[ 5] = a[ 5] * unLerp + b[ 5] * lerp; - mat[ 6] = a[ 6] * unLerp + b[ 6] * lerp; - mat[ 7] = a[ 7] * unLerp + b[ 7] * lerp; - mat[ 8] = a[ 8] * unLerp + b[ 8] * lerp; - mat[ 9] = a[ 9] * unLerp + b[ 9] * lerp; - mat[10] = a[10] * unLerp + b[10] * lerp; - mat[11] = a[11] * unLerp + b[11] * lerp; -} -static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, +static void JointToMatrix( const quat_t rot, const vec3_t scale, const vec3_t trans, float *mat ) { float xx = 2.0f * rot[0] * rot[0]; float yy = 2.0f * rot[1] * rot[1]; @@ -99,8 +84,7 @@ static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, mat[10] = scale[2] * (1.0f - (xx + yy)); mat[11] = trans[2]; } -static void Matrix34Invert( float *inMat, float *outMat ) -{ +static void Matrix34Invert( const float *inMat, float *outMat ) { vec3_t trans; float invSqrLen, *v; @@ -120,6 +104,62 @@ static void Matrix34Invert( float *inMat, float *outMat ) outMat[ 7] = -DotProduct(outMat + 4, trans); outMat[11] = -DotProduct(outMat + 8, trans); } +static void QuatSlerp(const quat_t from, const quat_t _to, float fraction, quat_t out) { + float angle, cosAngle, sinAngle, backlerp, lerp; + quat_t to; + + // cos() of angle + cosAngle = from[0] * _to[0] + from[1] * _to[1] + from[2] * _to[2] + from[3] * _to[3]; + + // negative handling is needed for taking shortest path (required for model joints) + if ( cosAngle < 0.0f ) { + cosAngle = -cosAngle; + to[0] = - _to[0]; + to[1] = - _to[1]; + to[2] = - _to[2]; + to[3] = - _to[3]; + } else { + QuatCopy( _to, to ); + } + + if ( cosAngle < 0.999999f ) { + // spherical lerp (slerp) + angle = acosf( cosAngle ); + sinAngle = sinf( angle ); + backlerp = sinf( ( 1.0f - fraction ) * angle ) / sinAngle; + lerp = sinf( fraction * angle ) / sinAngle; + } else { + // linear lerp + backlerp = 1.0f - fraction; + lerp = fraction; + } + + out[0] = from[0] * backlerp + to[0] * lerp; + out[1] = from[1] * backlerp + to[1] * lerp; + out[2] = from[2] * backlerp + to[2] * lerp; + out[3] = from[3] * backlerp + to[3] * lerp; +} +static vec_t QuatNormalize2( const quat_t v, quat_t out) { + float length, ilength; + + length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + v[3]*v[3]; + + if (length) { + /* writing it this way allows gcc to recognize that rsqrt can be used */ + ilength = 1/(float)sqrt (length); + /* sqrt(length) = length * (1 / sqrt(length)) */ + length *= ilength; + out[0] = v[0]*ilength; + out[1] = v[1]*ilength; + out[2] = v[2]*ilength; + out[3] = v[3]*ilength; + } else { + out[0] = out[1] = out[2] = 0; + out[3] = -1; + } + + return length; +} /* ================= @@ -139,7 +179,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na unsigned short *framedata; char *str; int i, j, k; - float jointInvMats[IQM_MAX_JOINTS * 12] = {0.0f}; + iqmTransform_t *transform; float *mat, *matInv; size_t size, joint_names; byte *dataPtr; @@ -562,10 +602,11 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na if( header->num_joints ) { size += joint_names; // joint names size += header->num_joints * sizeof(int); // joint parents - size += header->num_joints * 12 * sizeof( float ); // joint mats + size += header->num_joints * 12 * sizeof(float); // bind joint matricies + size += header->num_joints * 12 * sizeof(float); // inverse bind joint matricies } if( header->num_poses ) { - size += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats + size += header->num_poses * header->num_frames * sizeof(iqmTransform_t); // pose transforms } if( header->ofs_bounds ) { size += header->num_frames * 6 * sizeof(float); // model bounds @@ -636,12 +677,15 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na iqmData->jointParents = (int*)dataPtr; dataPtr += header->num_joints * sizeof(int); // joint parents - iqmData->jointMats = (float*)dataPtr; - dataPtr += header->num_joints * 12 * sizeof( float ); // joint mats + iqmData->bindJoints = (float*)dataPtr; + dataPtr += header->num_joints * 12 * sizeof(float); // bind joint matricies + + iqmData->invBindJoints = (float*)dataPtr; + dataPtr += header->num_joints * 12 * sizeof(float); // inverse bind joint matricies } if( header->num_poses ) { - iqmData->poseMats = (float*)dataPtr; - dataPtr += header->num_poses * header->num_frames * 12 * sizeof( float ); // pose mats + iqmData->poses = (iqmTransform_t*)dataPtr; + dataPtr += header->num_poses * header->num_frames * sizeof(iqmTransform_t); // pose transforms } if( header->ofs_bounds ) { iqmData->bounds = (float*)dataPtr; @@ -807,22 +851,23 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na iqmData->jointParents[i] = joint->parent; } - // calculate joint matrices and their inverses - // joint inverses are needed only until the pose matrices are calculated - mat = iqmData->jointMats; - matInv = jointInvMats; + // calculate bind joint matrices and their inverses + mat = iqmData->bindJoints; + matInv = iqmData->invBindJoints; joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); for( i = 0; i < header->num_joints; i++, joint++ ) { float baseFrame[12], invBaseFrame[12]; + QuatNormalize2( joint->rotate, joint->rotate ); + JointToMatrix( joint->rotate, joint->scale, joint->translate, baseFrame ); Matrix34Invert( baseFrame, invBaseFrame ); if ( joint->parent >= 0 ) { - Matrix34Multiply( iqmData->jointMats + 12 * joint->parent, baseFrame, mat ); + Matrix34Multiply( iqmData->bindJoints + 12 * joint->parent, baseFrame, mat ); mat += 12; - Matrix34Multiply( invBaseFrame, jointInvMats + 12 * joint->parent, matInv ); + Matrix34Multiply( invBaseFrame, iqmData->invBindJoints + 12 * joint->parent, matInv ); matInv += 12; } else @@ -837,16 +882,15 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na if( header->num_poses ) { - // calculate pose matrices + // calculate pose transforms + transform = iqmData->poses; framedata = (unsigned short *)((byte *)header + header->ofs_frames); - mat = iqmData->poseMats; for( i = 0; i < header->num_frames; i++ ) { pose = (iqmPose_t *)((byte *)header + header->ofs_poses); - for( j = 0; j < header->num_poses; j++, pose++ ) { + for( j = 0; j < header->num_poses; j++, pose++, transform++ ) { vec3_t translate; - vec4_t rotate; + quat_t rotate; vec3_t scale; - float mat1[12], mat2[12]; translate[0] = pose->channeloffset[0]; if( pose->mask & 0x001) @@ -881,18 +925,9 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na if( pose->mask & 0x200) scale[2] += *framedata++ * pose->channelscale[9]; - // construct transformation matrix - JointToMatrix( rotate, scale, translate, mat1 ); - - if( pose->parent >= 0 ) { - Matrix34Multiply( iqmData->jointMats + 12 * pose->parent, - mat1, mat2 ); - } else { - Com_Memcpy( mat2, mat1, sizeof(mat1) ); - } - - Matrix34Multiply( mat2, jointInvMats + 12 * j, mat ); - mat += 12; + VectorCopy( translate, transform->translate ); + QuatNormalize2( rotate, transform->rotate ); + VectorCopy( scale, transform->scale ); } } } @@ -1306,37 +1341,59 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { static void ComputePoseMats( iqmData_t *data, int frame, int oldframe, - float backlerp, float *mat ) { - float *mat1, *mat2; - int *joint = data->jointParents; - int i; - + float backlerp, float *poseMats ) { + iqmTransform_t relativeJoints[IQM_MAX_JOINTS]; + iqmTransform_t *relativeJoint; + const iqmTransform_t *pose; + const iqmTransform_t *oldpose; + const int *jointParent; + const float *invBindMat; + float *poseMat, lerp; + int i; + + relativeJoint = relativeJoints; + + // copy or lerp animation frame pose if ( oldframe == frame ) { - mat1 = data->poseMats + 12 * data->num_poses * frame; - for( i = 0; i < data->num_poses; i++, joint++ ) { - if( *joint >= 0 ) { - Matrix34Multiply( mat + 12 * *joint, - mat1 + 12*i, mat + 12*i ); - } else { - Com_Memcpy( mat + 12*i, mat1 + 12*i, 12 * sizeof(float) ); - } + pose = &data->poses[frame * data->num_poses]; + for ( i = 0; i < data->num_poses; i++, pose++, relativeJoint++ ) { + VectorCopy( pose->translate, relativeJoint->translate ); + QuatCopy( pose->rotate, relativeJoint->rotate ); + VectorCopy( pose->scale, relativeJoint->scale ); } - } else { - mat1 = data->poseMats + 12 * data->num_poses * frame; - mat2 = data->poseMats + 12 * data->num_poses * oldframe; - - for( i = 0; i < data->num_poses; i++, joint++ ) { - if( *joint >= 0 ) { - float tmpMat[12]; - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, tmpMat ); - Matrix34Multiply( mat + 12 * *joint, - tmpMat, mat + 12*i ); - - } else { - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, mat + 12*i ); - } + } else { + lerp = 1.0f - backlerp; + pose = &data->poses[frame * data->num_poses]; + oldpose = &data->poses[oldframe * data->num_poses]; + for ( i = 0; i < data->num_poses; i++, oldpose++, pose++, relativeJoint++ ) { + relativeJoint->translate[0] = oldpose->translate[0] * backlerp + pose->translate[0] * lerp; + relativeJoint->translate[1] = oldpose->translate[1] * backlerp + pose->translate[1] * lerp; + relativeJoint->translate[2] = oldpose->translate[2] * backlerp + pose->translate[2] * lerp; + + relativeJoint->scale[0] = oldpose->scale[0] * backlerp + pose->scale[0] * lerp; + relativeJoint->scale[1] = oldpose->scale[1] * backlerp + pose->scale[1] * lerp; + relativeJoint->scale[2] = oldpose->scale[2] * backlerp + pose->scale[2] * lerp; + + QuatSlerp( oldpose->rotate, pose->rotate, lerp, relativeJoint->rotate ); + } + } + + // multiply by inverse of bind pose and parent 'pose mat' (bind pose transform matrix) + relativeJoint = relativeJoints; + jointParent = data->jointParents; + invBindMat = data->invBindJoints; + poseMat = poseMats; + for ( i = 0; i < data->num_poses; i++, relativeJoint++, jointParent++, invBindMat += 12, poseMat += 12 ) { + float mat1[12], mat2[12]; + + JointToMatrix( relativeJoint->rotate, relativeJoint->scale, relativeJoint->translate, mat1 ); + + if ( *jointParent >= 0 ) { + Matrix34Multiply( &data->bindJoints[(*jointParent)*12], mat1, mat2 ); + Matrix34Multiply( mat2, invBindMat, mat1 ); + Matrix34Multiply( &poseMats[(*jointParent)*12], mat1, poseMat ); + } else { + Matrix34Multiply( mat1, invBindMat, poseMat ); } } } @@ -1347,7 +1404,7 @@ static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, int i; if ( data->num_poses == 0 ) { - Com_Memcpy( mat, data->jointMats, data->num_joints * 12 * sizeof(float) ); + Com_Memcpy( mat, data->bindJoints, data->num_joints * 12 * sizeof(float) ); return; } @@ -1359,7 +1416,7 @@ static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, Com_Memcpy(outmat, mat1, sizeof(outmat)); - Matrix34Multiply( outmat, data->jointMats + 12*i, mat1 ); + Matrix34Multiply( outmat, data->bindJoints + 12*i, mat1 ); } } @@ -1428,19 +1485,20 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { float *nrmMat = &influenceNrmMat[9*i]; int j; float blendWeights[4]; - int numWeights; - - for ( numWeights = 0; numWeights < 4; numWeights++ ) { - if ( data->blendWeightsType == IQM_FLOAT ) - blendWeights[numWeights] = data->influenceBlendWeights.f[4*influence + numWeights]; - else - blendWeights[numWeights] = (float)data->influenceBlendWeights.b[4*influence + numWeights] / 255.0f; - if ( blendWeights[numWeights] <= 0.0f ) - break; + if ( data->blendWeightsType == IQM_FLOAT ) { + blendWeights[0] = data->influenceBlendWeights.f[4*influence + 0]; + blendWeights[1] = data->influenceBlendWeights.f[4*influence + 1]; + blendWeights[2] = data->influenceBlendWeights.f[4*influence + 2]; + blendWeights[3] = data->influenceBlendWeights.f[4*influence + 3]; + } else { + blendWeights[0] = (float)data->influenceBlendWeights.b[4*influence + 0] / 255.0f; + blendWeights[1] = (float)data->influenceBlendWeights.b[4*influence + 1] / 255.0f; + blendWeights[2] = (float)data->influenceBlendWeights.b[4*influence + 2] / 255.0f; + blendWeights[3] = (float)data->influenceBlendWeights.b[4*influence + 3] / 255.0f; } - if ( numWeights == 0 ) { + if ( blendWeights[0] <= 0.0f ) { // no blend joint, use identity matrix. vtxMat[0] = identityMatrix[0]; vtxMat[1] = identityMatrix[1]; @@ -1470,7 +1528,11 @@ void RB_IQMSurfaceAnim( surfaceType_t *surface ) { vtxMat[10] = blendWeights[0] * poseMats[12 * data->influenceBlendIndexes[4*influence + 0] + 10]; vtxMat[11] = blendWeights[0] * poseMats[12 * data->influenceBlendIndexes[4*influence + 0] + 11]; - for( j = 1; j < numWeights; j++ ) { + for( j = 1; j < 3; j++ ) { + if ( blendWeights[j] <= 0.0f ) { + break; + } + vtxMat[0] += blendWeights[j] * poseMats[12 * data->influenceBlendIndexes[4*influence + j] + 0]; vtxMat[1] += blendWeights[j] * poseMats[12 * data->influenceBlendIndexes[4*influence + j] + 1]; vtxMat[2] += blendWeights[j] * poseMats[12 * data->influenceBlendIndexes[4*influence + j] + 2]; diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index d904837b..4b284fdb 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -261,7 +261,7 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) { version = (const char *)qglGetString( GL_VERSION ); if ( !version ) { - Com_Error( ERR_FATAL, "GL_VERSION is NULL\n" ); + Com_Error( ERR_FATAL, "GL_VERSION is NULL" ); } if ( Q_stricmpn( "OpenGL ES", version, 9 ) == 0 ) { @@ -277,7 +277,7 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) { } if ( fixedFunction ) { - if ( QGL_VERSION_ATLEAST( 1, 2 ) ) { + if ( QGL_VERSION_ATLEAST( 1, 1 ) ) { QGL_1_1_PROCS; QGL_1_1_FIXED_FUNCTION_PROCS; QGL_DESKTOP_1_1_PROCS; @@ -289,9 +289,9 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) { QGL_ES_1_1_PROCS; QGL_ES_1_1_FIXED_FUNCTION_PROCS; // error so this doesn't segfault due to NULL desktop GL functions being used - Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s\n", version ); + Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s", version ); } else { - Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 1.2 is required\n", version ); + Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 1.1 is required", version ); } } else { if ( QGL_VERSION_ATLEAST( 2, 0 ) ) { @@ -307,9 +307,9 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) { QGL_1_5_PROCS; QGL_2_0_PROCS; // error so this doesn't segfault due to NULL desktop GL functions being used - Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s\n", version ); + Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s", version ); } else { - Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 2.0 is required\n", version ); + Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 2.0 is required", version ); } } @@ -965,6 +965,17 @@ static void GLimp_InitExtensions( qboolean fixedFunction ) { ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); } + + haveClampToEdge = qfalse; + if ( QGL_VERSION_ATLEAST( 1, 2 ) || QGLES_VERSION_ATLEAST( 1, 0 ) || SDL_GL_ExtensionSupported( "GL_SGIS_texture_edge_clamp" ) ) + { + ri.Printf( PRINT_ALL, "...using GL_SGIS_texture_edge_clamp\n" ); + haveClampToEdge = qtrue; + } + else + { + ri.Printf( PRINT_ALL, "...GL_SGIS_texture_edge_clamp not found\n" ); + } } #define R_MODE_FALLBACK 3 // 640 * 480 diff --git a/code/tools/lcc/cpp/cpp.c b/code/tools/lcc/cpp/cpp.c index 5c0cfd7b..13e04850 100644 --- a/code/tools/lcc/cpp/cpp.c +++ b/code/tools/lcc/cpp/cpp.c @@ -19,6 +19,15 @@ int ifdepth; int ifsatisfied[NIF]; int skipping; +time_t reproducible_time() +{ + char *source_date_epoch; + time_t t; + if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")) == NULL || + (t = (time_t)strtol(source_date_epoch, NULL, 10)) <= 0) + return time(NULL); + return t; +} int main(int argc, char **argv) @@ -28,7 +37,7 @@ main(int argc, char **argv) char ebuf[BUFSIZ]; setbuf(stderr, ebuf); - t = time(NULL); + t = reproducible_time(); curtime = ctime(&t); maketokenrow(3, &tr); expandlex(); diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 000934d4..31bb1d66 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -3086,6 +3086,7 @@ static void UI_Update(const char *name) { trap_Cvar_SetValue( "r_stencilbits", 8 ); trap_Cvar_SetValue( "r_picmip", 0 ); trap_Cvar_SetValue( "r_mode", 4 ); + trap_Cvar_Set( "ui_videomode", "800x600" ); trap_Cvar_SetValue( "r_texturebits", 32 ); trap_Cvar_SetValue( "r_fastSky", 0 ); trap_Cvar_SetValue( "r_inGameVideo", 1 ); @@ -3103,6 +3104,7 @@ static void UI_Update(const char *name) { trap_Cvar_Reset( "r_stencilbits" ); trap_Cvar_SetValue( "r_picmip", 1 ); trap_Cvar_SetValue( "r_mode", 3 ); + trap_Cvar_Set( "ui_videomode", "640x480" ); trap_Cvar_SetValue( "r_texturebits", 0 ); trap_Cvar_SetValue( "r_fastSky", 0 ); trap_Cvar_SetValue( "r_inGameVideo", 1 ); @@ -3120,6 +3122,7 @@ static void UI_Update(const char *name) { trap_Cvar_Reset( "r_stencilbits" ); trap_Cvar_SetValue( "r_picmip", 1 ); trap_Cvar_SetValue( "r_mode", 3 ); + trap_Cvar_Set( "ui_videomode", "640x480" ); trap_Cvar_SetValue( "r_texturebits", 0 ); trap_Cvar_SetValue( "cg_shadows", 0 ); trap_Cvar_SetValue( "r_fastSky", 1 ); @@ -3136,6 +3139,7 @@ static void UI_Update(const char *name) { trap_Cvar_SetValue( "r_depthbits", 16 ); trap_Cvar_SetValue( "r_stencilbits", 0 ); trap_Cvar_SetValue( "r_mode", 3 ); + trap_Cvar_Set( "ui_videomode", "640x480" ); trap_Cvar_SetValue( "r_picmip", 2 ); trap_Cvar_SetValue( "r_texturebits", 16 ); trap_Cvar_SetValue( "cg_shadows", 0 ); @@ -5079,6 +5083,8 @@ void _UI_Init( qboolean inGameLoad ) { // cache redundant calulations trap_GetGlconfig( &uiInfo.uiDC.glconfig ); + trap_Cvar_Set("ui_videomode", va( "%dx%d", uiInfo.uiDC.glconfig.vidWidth, uiInfo.uiDC.glconfig.vidHeight ) ); + // for 640x480 virtualized screen uiInfo.uiDC.yscale = uiInfo.uiDC.glconfig.vidHeight * (1.0/480.0); uiInfo.uiDC.xscale = uiInfo.uiDC.glconfig.vidWidth * (1.0/640.0); @@ -5874,6 +5880,7 @@ static cvarTable_t cvarTable[] = { { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART}, { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE}, + { NULL, "ui_videomode", "", CVAR_ROM }, { NULL, "g_localTeamPref", "", 0 }, }; diff --git a/code/ui/ui_shared.c b/code/ui/ui_shared.c index 6e05105b..f1ed435b 100644 --- a/code/ui/ui_shared.c +++ b/code/ui/ui_shared.c @@ -2055,6 +2055,26 @@ qboolean Item_Multi_HandleKey(itemDef_t *item, int key) { } else if ( current >= max ) { current = 0; } + + if (multiPtr->videoMode) { + if (multiPtr->cvarValue[current] != -1) { + DC->setCVar("r_mode", va("%i", (int) multiPtr->cvarValue[current] )); + } else { + int w, h; + char *x; + char str[8]; + + x = strchr( multiPtr->cvarStr[current], 'x' ) + 1; + Q_strncpyz( str, multiPtr->cvarStr[current], MIN( x-multiPtr->cvarStr[current], sizeof( str ) ) ); + w = atoi( str ); + h = atoi( x ); + + DC->setCVar("r_mode", "-1"); + DC->setCVar("r_customwidth", va("%i", w)); + DC->setCVar("r_customheight", va("%i", h)); + } + } + if (multiPtr->strDef) { DC->setCVar(item->cvar, multiPtr->cvarStr[current]); } else { @@ -5017,6 +5037,7 @@ qboolean ItemParse_cvarStrList( itemDef_t *item, int handle ) { multiPtr = (multiDef_t*)item->typeData; multiPtr->count = 0; multiPtr->strDef = qtrue; + multiPtr->videoMode = qfalse; if (!trap_PC_ReadToken(handle, &token)) return qfalse; @@ -5065,6 +5086,7 @@ qboolean ItemParse_cvarFloatList( itemDef_t *item, int handle ) { multiPtr = (multiDef_t*)item->typeData; multiPtr->count = 0; multiPtr->strDef = qfalse; + multiPtr->videoMode = qfalse; if (!trap_PC_ReadToken(handle, &token)) return qfalse; @@ -5241,6 +5263,61 @@ void Item_SetupKeywordHash(void) { } } +static const char *builtinResolutions[ ] = +{ + "320x240", + "400x300", + "512x384", + "640x480", + "800x600", + "960x720", + "1024x768", + "1152x864", + "1280x1024", + "1600x1200", + "2048x1536", + "856x480", + NULL +}; + +static const char *knownRatios[ ][2] = +{ + { "1.25:1", "5:4" }, + { "1.33:1", "4:3" }, + { "1.50:1", "3:2" }, + { "1.56:1", "14:9" }, + { "1.60:1", "16:10" }, + { "1.67:1", "5:3" }, + { "1.78:1", "16:9" }, + { NULL , NULL } +}; + +/* +=============== +UI_ResolutionToAspect +=============== +*/ +static void UI_ResolutionToAspect( const char *resolution, char *aspect, size_t aspectLength ) { + int i, w, h; + char *x; + char str[8]; + + // calculate resolution's aspect ratio + x = strchr( resolution, 'x' ) + 1; + Q_strncpyz( str, resolution, MIN( x-resolution, sizeof( str ) ) ); + w = atoi( str ); + h = atoi( x ); + Com_sprintf( aspect, aspectLength, "%.2f:1", (float)w / (float)h ); + + // rename common ratios ("1.33:1" -> "4:3") + for( i = 0; knownRatios[i][0]; i++ ) { + if( !Q_stricmp( aspect, knownRatios[i][0] ) ) { + Q_strncpyz( aspect, knownRatios[i][1], aspectLength ); + break; + } + } +} + /* =============== Item_ApplyHacks @@ -5275,6 +5352,89 @@ static void Item_ApplyHacks( itemDef_t *item ) { } } + // Replace mode list and use a temporary ui_videomode cvar for handling custom modes + if ( item->type == ITEM_TYPE_MULTI && item->cvar && !Q_stricmp( item->cvar, "r_mode" ) ) { + multiDef_t *multiPtr = (multiDef_t*)item->typeData; + int i, oldCount; + char resbuf[MAX_STRING_CHARS]; + char modeName[32], aspect[8]; + + item->cvar = "ui_videomode"; + multiPtr->strDef = qtrue; + multiPtr->videoMode = qtrue; + + oldCount = multiPtr->count; + multiPtr->count = 0; + + DC->getCVarString( "r_availableModes", resbuf, sizeof( resbuf ) ); + + if ( *resbuf ) { + char *s = resbuf, *mode; + + while ( s && multiPtr->count < MAX_MULTI_CVARS ) { + mode = s; + + s = strchr(s, ' '); + if( s ) + *s++ = '\0'; + + UI_ResolutionToAspect( mode, aspect, sizeof( aspect ) ); + Com_sprintf( modeName, sizeof( modeName ), "%s (%s)", mode, aspect ); + + multiPtr->cvarList[multiPtr->count] = String_Alloc( modeName ); + + for ( i = 0; builtinResolutions[i]; i++ ) { + if( !Q_stricmp( builtinResolutions[i], mode ) ) { + multiPtr->cvarStr[multiPtr->count] = builtinResolutions[i]; + multiPtr->cvarValue[multiPtr->count] = i; + break; + } + } + + if ( builtinResolutions[i] == NULL ) { + multiPtr->cvarStr[multiPtr->count] = String_Alloc( mode ); + multiPtr->cvarValue[multiPtr->count] = -1; + } + + multiPtr->count++; + } + } else { + for ( i = 0; builtinResolutions[i] && multiPtr->count < MAX_MULTI_CVARS; i++ ) { + UI_ResolutionToAspect( builtinResolutions[i], aspect, sizeof( aspect ) ); + Com_sprintf( modeName, sizeof( modeName ), "%s (%s)", builtinResolutions[i], aspect ); + + multiPtr->cvarList[multiPtr->count] = String_Alloc( modeName ); + multiPtr->cvarStr[multiPtr->count] = builtinResolutions[i]; + multiPtr->cvarValue[multiPtr->count] = i; + multiPtr->count++; + } + } + + // Add custom resolution if not in mode list + if ( multiPtr->count < MAX_MULTI_CVARS ) { + char currentResolution[20]; + + Com_sprintf( currentResolution, sizeof ( currentResolution ), "%dx%d", DC->glconfig.vidWidth, DC->glconfig.vidHeight ); + for ( i = 0; i < multiPtr->count; i++ ) { + if ( !Q_stricmp( multiPtr->cvarStr[i], currentResolution ) ) { + break; + } + } + + if ( i == multiPtr->count ) { + UI_ResolutionToAspect( currentResolution, aspect, sizeof( aspect ) ); + Com_sprintf( modeName, sizeof( modeName ), "%s (%s)", currentResolution, aspect ); + + multiPtr->cvarList[multiPtr->count] = String_Alloc( modeName ); + multiPtr->cvarStr[multiPtr->count] = String_Alloc( currentResolution ); + multiPtr->cvarValue[multiPtr->count] = -1; + multiPtr->count++; + } + } + + Com_Printf( "Found video mode list with %d modes, replaced list with %d modes\n", oldCount, multiPtr->count ); + } + } /* diff --git a/code/ui/ui_shared.h b/code/ui/ui_shared.h index 779a8cbc..aad6b687 100644 --- a/code/ui/ui_shared.h +++ b/code/ui/ui_shared.h @@ -220,6 +220,7 @@ typedef struct multiDef_s { float cvarValue[MAX_MULTI_CVARS]; int count; qboolean strDef; + qboolean videoMode; } multiDef_t; typedef struct modelDef_s { diff --git a/misc/msvc12/.gitignore b/misc/msvc12/.gitignore new file mode 100644 index 00000000..4ba92b04 --- /dev/null +++ b/misc/msvc12/.gitignore @@ -0,0 +1,338 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ diff --git a/opengl2-readme.md b/opengl2-readme.md index 7e572611..c1a031c7 100644 --- a/opengl2-readme.md +++ b/opengl2-readme.md @@ -184,6 +184,11 @@ Cvars for advanced material usage: 1 - Use parallax occlusion mapping. 2 - Use relief mapping. (slower) +* `r_parallaxMapShadows` - Enable self-shadowing on parallax map + supported materials. + 0 - No. (default) + 1 - Yes. + * `r_baseSpecular` - Set the specular reflectance of materials which don't include a specular map or use the specularReflectance keyword.