From a6df505d591483d1813ff844319930b9171f7162 Mon Sep 17 00:00:00 2001 From: kungfooman Date: Fri, 14 Dec 2018 02:23:13 +0100 Subject: [PATCH 01/16] Prevent Q_IsColorString from asserting on negative ascii chars --- code/qcommon/q_shared.c | 22 ++++++++++++++++++++++ code/qcommon/q_shared.h | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) 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..7d8b509c 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -409,7 +409,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' From b0d2b141e702aafc3dcf77a026e12757f00e45ed Mon Sep 17 00:00:00 2001 From: kungfooman Date: Fri, 14 Dec 2018 01:37:12 +0100 Subject: [PATCH 02/16] Add .gitignore for misc/msvc12 --- misc/msvc12/.gitignore | 338 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 misc/msvc12/.gitignore 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/ From e5da13f14635b6accc5eefd6986de03d2cff9662 Mon Sep 17 00:00:00 2001 From: James Canete Date: Fri, 21 Dec 2018 19:53:18 -0800 Subject: [PATCH 03/16] OpenGL2: r_cubemapping 2 for box cubemap parallax. --- code/renderergl2/glsl/lightall_fp.glsl | 34 ++++++++++++++++++++++++++ code/renderergl2/tr_glsl.c | 6 +++++ 2 files changed, 40 insertions(+) diff --git a/code/renderergl2/glsl/lightall_fp.glsl b/code/renderergl2/glsl/lightall_fp.glsl index 6465bd84..d77cd7c7 100644 --- a/code/renderergl2/glsl/lightall_fp.glsl +++ b/code/renderergl2/glsl/lightall_fp.glsl @@ -194,6 +194,36 @@ float CalcLightAttenuation(float point, float normDist) } +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); +} + void main() { vec3 viewDir, lightColor, ambientColor, reflectance; @@ -374,7 +404,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 diff --git a/code/renderergl2/tr_glsl.c b/code/renderergl2/tr_glsl.c index 218017de..4ed49370 100644 --- a/code/renderergl2/tr_glsl.c +++ b/code/renderergl2/tr_glsl.c @@ -1129,9 +1129,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) { From d068e1dce174d2631d2d760d1d729a9d1d4151b8 Mon Sep 17 00:00:00 2001 From: James Canete Date: Wed, 6 Feb 2019 01:10:42 -0800 Subject: [PATCH 04/16] Add r_parallaxMapShadows. --- code/renderergl2/glsl/lightall_fp.glsl | 44 +++++++++++++++++++++++++- code/renderergl2/tr_glsl.c | 3 ++ code/renderergl2/tr_init.c | 2 ++ code/renderergl2/tr_local.h | 1 + opengl2-readme.md | 5 +++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/code/renderergl2/glsl/lightall_fp.glsl b/code/renderergl2/glsl/lightall_fp.glsl index d77cd7c7..027b54a5 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) @@ -252,7 +281,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; @@ -319,6 +348,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); @@ -457,6 +493,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 4ed49370..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"); } } diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index ce48925d..06b78173 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -131,6 +131,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; @@ -1235,6 +1236,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); diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index ea6d70fe..12b469c7 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -1771,6 +1771,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/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. From 44b18d72ac321668d79c8dac35ba07f2ac29e408 Mon Sep 17 00:00:00 2001 From: Ane-Jouke Schat Date: Tue, 16 Apr 2019 06:13:36 +0200 Subject: [PATCH 05/16] Fix PRINT_ERROR print level missing from CL_RefPrintf It's used for one message in the OpenGL2 renderer. --- code/client/cl_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 } } From 9b4dcc809e7ed4ed3f36ab82d811ff61bb254afc Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 15 Apr 2019 21:59:03 -0500 Subject: [PATCH 06/16] OpenGL2: Fix compiling lightall GLSL on OpenGL 2.1 textureCubeLod() requires OpenGL 3.0 / GLSL 1.30. Added in 2018-12-21 commit e5da13f14635b6accc5eefd6986de03d2cff9662 "OpenGL2: r_cubemapping 2 for box cubemap parallax." Report by brugal and Simon McVittie. Bug-Debian: https://bugs.debian.org/923226 --- code/renderergl2/glsl/lightall_fp.glsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/renderergl2/glsl/lightall_fp.glsl b/code/renderergl2/glsl/lightall_fp.glsl index 027b54a5..26f9a995 100644 --- a/code/renderergl2/glsl/lightall_fp.glsl +++ b/code/renderergl2/glsl/lightall_fp.glsl @@ -222,7 +222,7 @@ 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 @@ -252,6 +252,7 @@ vec4 hitCube(vec3 ray, vec3 pos, vec3 invSize, float lod, samplerCube tex) //return vec4(textureCubeLod(tex, tc, lod).rgb * fade, fade); return vec4(textureCubeLod(tex, tc, lod).rgb, 1.0); } +#endif void main() { From c61417b8e2e453e14e5a0d012910df5062186f2e Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 15 Apr 2019 22:04:20 -0500 Subject: [PATCH 07/16] Add current (custom) resolution to Q3 UI video mode list Custom resolution is displayed when entering system options menu and settings can be changed without being forced to change video resolution. --- code/q3_ui/ui_video.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) 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; } } From 9fcb2bb336583d998cc886ecf7e37ea2278b6b78 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 15 Apr 2019 22:40:09 -0500 Subject: [PATCH 08/16] Override video mode list in Team Arena UI Override the video mode list in the Team Arena data files with detected modes from SDL like in ioquake3's Q3 UI. Add the aspect ratio to the end of the video resolution (i.e., "640x480 (4:3)"). Add the current (custom) video mode to the list. Before when using a custom resolution in the menu you could not change the video mode using the mouse because the resolution text was blank. Now custom video resolution is displayed and can be clicked. --- code/ui/ui_main.c | 7 ++ code/ui/ui_shared.c | 160 ++++++++++++++++++++++++++++++++++++++++++++ code/ui/ui_shared.h | 1 + 3 files changed, 168 insertions(+) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 446be087..4480d50d 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); @@ -5871,6 +5877,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 c97fc59b..74bc77eb 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 { @@ -5003,6 +5023,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; @@ -5051,6 +5072,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; @@ -5227,6 +5249,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 @@ -5261,6 +5338,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 ea4a4596..e70c716f 100644 --- a/code/ui/ui_shared.h +++ b/code/ui/ui_shared.h @@ -203,6 +203,7 @@ typedef struct multiDef_s { float cvarValue[MAX_MULTI_CVARS]; int count; qboolean strDef; + qboolean videoMode; } multiDef_t; typedef struct modelDef_s { From d13d06424e5a8f0cf1ce68c939397e55bd5cdca7 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 16 Apr 2019 11:46:18 -0500 Subject: [PATCH 09/16] OpenGL2: Disable r_cubeMapping if not OpenGL 3.0+ r_cubeMapping requires textureCubeLod() which is only in OpenGL 3.0 (GLSL 1.30) and later. It's not in OpenGL ES 3.0 / GLSL ES 3.00. This needs to be checked before R_InitImages() so can't just check in GLSL_InitGPUShaders(). --- code/renderergl2/tr_init.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index 06b78173..bd34cc61 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -285,6 +285,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(); } From d404519cce565402aa98c3f9943221ed6ddb2790 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 29 Apr 2019 14:39:10 -0500 Subject: [PATCH 10/16] Fix rendering IQM models between model frames For lerped frames (refEntity_t frame not equal oldframe) IQM joint matrices may have incorrect axis scale. This can cause significant model distortion. The matrix lerp is linear causing each vector to move in a straight line between frames instead of arcing like a circle. Each joint frame can have a different scale so can't just normalize the joint matrix. Store joints as quaternions and spherical lerp between them and then convert to a matrix. For my test model, setting up the skeleton is four times slower now but it still seems to be fast enough to be usable. --- code/qcommon/q_shared.h | 4 + code/renderergl1/tr_local.h | 11 +- code/renderergl1/tr_model_iqm.c | 218 ++++++++++++++++++++------------ code/renderergl2/tr_local.h | 11 +- code/renderergl2/tr_model_iqm.c | 218 ++++++++++++++++++++------------ 5 files changed, 296 insertions(+), 166 deletions(-) diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index 7d8b509c..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; @@ -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/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..fc0e5810 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,61 @@ 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] = out[3] = 0; + } + + return length; +} /* ================= @@ -139,7 +178,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 +598,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 +673,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 +847,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 +878,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 +921,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 +1162,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 +1225,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 +1237,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 ); } } diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 12b469c7..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; diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index 1a6e1e5a..649c4dc4 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,61 @@ 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] = out[3] = 0; + } + + return length; +} /* ================= @@ -139,7 +178,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 +601,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 +676,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 +850,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 +881,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 +924,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 +1340,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 +1403,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 +1415,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 ); } } From 95b9cab4d644fa3bf757cfff821cc4f7d76e38b0 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 29 Apr 2019 14:39:25 -0500 Subject: [PATCH 11/16] Fix warnings that IQM blendWeights may not be initialized --- code/renderergl1/tr_model_iqm.c | 27 ++++++++++++++++----------- code/renderergl2/tr_model_iqm.c | 27 ++++++++++++++++----------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/code/renderergl1/tr_model_iqm.c b/code/renderergl1/tr_model_iqm.c index fc0e5810..59444a93 100644 --- a/code/renderergl1/tr_model_iqm.c +++ b/code/renderergl1/tr_model_iqm.c @@ -1302,19 +1302,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]; @@ -1344,7 +1345,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/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index 649c4dc4..eff80312 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -1484,19 +1484,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]; @@ -1526,7 +1527,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]; From 2a2646fe43887bf19494769f562904c4d2ed551a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 28 May 2019 21:00:02 -0500 Subject: [PATCH 12/16] Fix IQM quat normalize fail case Should only happen with invalid models though. --- code/renderergl1/tr_model_iqm.c | 3 ++- code/renderergl2/tr_model_iqm.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/renderergl1/tr_model_iqm.c b/code/renderergl1/tr_model_iqm.c index 59444a93..fe205ff4 100644 --- a/code/renderergl1/tr_model_iqm.c +++ b/code/renderergl1/tr_model_iqm.c @@ -154,7 +154,8 @@ static vec_t QuatNormalize2( const quat_t v, quat_t out) { out[2] = v[2]*ilength; out[3] = v[3]*ilength; } else { - out[0] = out[1] = out[2] = out[3] = 0; + out[0] = out[1] = out[2] = 0; + out[3] = -1; } return length; diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index eff80312..fc345a32 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -154,7 +154,8 @@ static vec_t QuatNormalize2( const quat_t v, quat_t out) { out[2] = v[2]*ilength; out[3] = v[3]*ilength; } else { - out[0] = out[1] = out[2] = out[3] = 0; + out[0] = out[1] = out[2] = 0; + out[3] = -1; } return length; From 0fbde293db42b26e41c54150a11abb10ec04df74 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 28 May 2019 21:01:28 -0500 Subject: [PATCH 13/16] Remove erroneous newlines from Com_Error in sdl_glimp.c --- code/sdl/sdl_glimp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index d904837b..28524ce4 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 ) { @@ -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.2 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 ); } } From 350b8f9c7c88c002dccea4f0350f1919b86d3b4e Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 28 May 2019 22:44:57 -0500 Subject: [PATCH 14/16] Restore OpenGL 1.1 support (GL_CLAMP) GL_CLAMP (clamp to border) was changed to GL_CLAMP_TO_EDGE in 2008 (f2baf359). In 2018 (ce1d5406) I made OpenGL 1.2 be required since GL_CLAMP_TO_EDGE is used. Restore support for GL_CLAMP in order to support OpenGL 1.1 like vanilla Quake 3 does. This should allow using the default Microsoft Windows GDI Generic OpenGL 1.1 driver (untested but it won't fail the version check at least). From gpuinfo.org, it looks like drivers stopped advertising support for GL_SGIS_texture_edge_clamp so use a version check in addition to the extension check. r_allowExtensions 0 disables using GL_CLAMP_TO_EDGE in the opengl1 renderer. GL_CLAMP support wasn't added to the opengl2 renderer. --- code/renderercommon/tr_common.h | 1 + code/renderergl1/tr_backend.c | 4 ++-- code/renderergl1/tr_image.c | 2 +- code/renderergl1/tr_init.c | 2 ++ code/renderergl2/tr_init.c | 2 ++ code/sdl/sdl_glimp.c | 15 +++++++++++++-- 6 files changed, 21 insertions(+), 5 deletions(-) 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/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index bd34cc61..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; @@ -1552,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/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index 28524ce4..4b284fdb 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -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; @@ -291,7 +291,7 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) { // error so this doesn't segfault due to NULL desktop GL functions being used Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s", version ); } else { - Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 1.2 is required", version ); + Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 1.1 is required", version ); } } else { if ( QGL_VERSION_ATLEAST( 2, 0 ) ) { @@ -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 From a96f32eba2ed01f0a7ad8a321d1863e8ef43da3d Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" Date: Wed, 10 Jul 2019 17:17:37 +0200 Subject: [PATCH 15/16] q3rcc: Allow to override build date Allow to override __DATE__ and __TIME__ with SOURCE_DATE_EPOCH in order to make builds reproducible. See https://reproducible-builds.org/ for why this is good and https://reproducible-builds.org/specs/source-date-epoch/ for the definition of this variable. This behavior is what gcc already does. --- code/tools/lcc/cpp/cpp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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(); From 4d82b8b5257675b8010cd59a203d713c33640046 Mon Sep 17 00:00:00 2001 From: "Zachary J. Slater" Date: Thu, 22 Aug 2019 18:17:02 -1000 Subject: [PATCH 16/16] Create Funding.yml TODO: add more of these things https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository --- .github/FUNDING.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/FUNDING.yml 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!