From a6df505d591483d1813ff844319930b9171f7162 Mon Sep 17 00:00:00 2001 From: kungfooman Date: Fri, 14 Dec 2018 02:23:13 +0100 Subject: [PATCH 01/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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 | 216 ++++++++++++++++++++------------ code/renderergl2/tr_local.h | 11 +- code/renderergl2/tr_model_iqm.c | 216 ++++++++++++++++++++------------ 5 files changed, 294 insertions(+), 164 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/11] 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];