From 10cdecd3e4dacb48513687867bc0587680eced61 Mon Sep 17 00:00:00 2001 From: Conticop <58798963+Conticop@users.noreply.github.com> Date: Mon, 3 Aug 2020 17:24:41 +0200 Subject: [PATCH 1/6] Fixes #807 Original author: dd --- Resources/Scripts/Gui/Client/ChatSayWindow.as | 1 + Resources/Scripts/Gui/Preferences.as | 15 + Resources/Shaders/BasicBlockOutlines.fs | 16 + Resources/Shaders/BasicBlockOutlines.program | 4 + Resources/Shaders/BasicBlockOutlines.vs | 26 + Resources/Shaders/BasicBlockPhysTextures.fs | 105 ++++ .../Shaders/BasicBlockPhysTextures.program | 6 + Resources/Shaders/BasicBlockPhysTextures.vs | 102 ++++ Resources/Shaders/BasicBlockTextures.fs | 64 +++ Resources/Shaders/BasicBlockTextures.program | 4 + Resources/Shaders/BasicBlockTextures.vs | 86 +++ .../Shaders/OptimizedVoxelModelOccluded.fs | 4 + .../OptimizedVoxelModelOccluded.program | 4 + .../Shaders/OptimizedVoxelModelOccluded.vs | 12 + .../OptimizedVoxelModelOcclusionTest.fs | 11 + .../OptimizedVoxelModelOcclusionTest.program | 4 + .../OptimizedVoxelModelOcclusionTest.vs | 22 + .../Shaders/OptimizedVoxelModelOutlines.fs | 16 + .../OptimizedVoxelModelOutlines.program | 4 + .../Shaders/OptimizedVoxelModelOutlines.vs | 24 + Resources/Shaders/VoxelModelOccluded.fs | 4 + Resources/Shaders/VoxelModelOccluded.program | 4 + Resources/Shaders/VoxelModelOccluded.vs | 12 + Resources/Shaders/VoxelModelOcclusionTest.fs | 11 + .../Shaders/VoxelModelOcclusionTest.program | 4 + Resources/Shaders/VoxelModelOcclusionTest.vs | 22 + Resources/Shaders/VoxelModelOutlines.fs | 16 + Resources/Shaders/VoxelModelOutlines.program | 4 + Resources/Shaders/VoxelModelOutlines.vs | 24 + Resources/Textures/MapBlock.png | Bin 0 -> 3660 bytes Resources/Textures/MultiMapBlock.png | Bin 0 -> 49314 bytes Sources/Audio/ALDevice.cpp | 21 + Sources/Audio/YsrDevice.cpp | 24 +- Sources/Client/Client.cpp | 54 ++ Sources/Client/Client.h | 8 + Sources/Client/ClientPlayer.cpp | 7 + Sources/Client/Client_Draw.cpp | 38 +- Sources/Client/IRenderer.h | 6 + Sources/Draw/GLMapChunk.cpp | 272 ++++++++-- Sources/Draw/GLMapChunk.h | 16 +- Sources/Draw/GLMapRenderer.cpp | 178 +++++++ Sources/Draw/GLMapRenderer.h | 23 + Sources/Draw/GLModel.h | 17 +- Sources/Draw/GLModelRenderer.cpp | 226 +++++++- Sources/Draw/GLModelRenderer.h | 22 +- Sources/Draw/GLOptimizedVoxelModel.cpp | 324 +++++++++++- Sources/Draw/GLOptimizedVoxelModel.h | 20 +- Sources/Draw/GLRenderer.cpp | 488 +++++++++++++----- Sources/Draw/GLRenderer.h | 11 +- Sources/Draw/GLVoxelModel.cpp | 268 +++++++++- Sources/Draw/GLVoxelModel.h | 21 +- 51 files changed, 2472 insertions(+), 203 deletions(-) create mode 100644 Resources/Shaders/BasicBlockOutlines.fs create mode 100644 Resources/Shaders/BasicBlockOutlines.program create mode 100644 Resources/Shaders/BasicBlockOutlines.vs create mode 100644 Resources/Shaders/BasicBlockPhysTextures.fs create mode 100644 Resources/Shaders/BasicBlockPhysTextures.program create mode 100644 Resources/Shaders/BasicBlockPhysTextures.vs create mode 100644 Resources/Shaders/BasicBlockTextures.fs create mode 100644 Resources/Shaders/BasicBlockTextures.program create mode 100644 Resources/Shaders/BasicBlockTextures.vs create mode 100644 Resources/Shaders/OptimizedVoxelModelOccluded.fs create mode 100644 Resources/Shaders/OptimizedVoxelModelOccluded.program create mode 100644 Resources/Shaders/OptimizedVoxelModelOccluded.vs create mode 100644 Resources/Shaders/OptimizedVoxelModelOcclusionTest.fs create mode 100644 Resources/Shaders/OptimizedVoxelModelOcclusionTest.program create mode 100644 Resources/Shaders/OptimizedVoxelModelOcclusionTest.vs create mode 100644 Resources/Shaders/OptimizedVoxelModelOutlines.fs create mode 100644 Resources/Shaders/OptimizedVoxelModelOutlines.program create mode 100644 Resources/Shaders/OptimizedVoxelModelOutlines.vs create mode 100644 Resources/Shaders/VoxelModelOccluded.fs create mode 100644 Resources/Shaders/VoxelModelOccluded.program create mode 100644 Resources/Shaders/VoxelModelOccluded.vs create mode 100644 Resources/Shaders/VoxelModelOcclusionTest.fs create mode 100644 Resources/Shaders/VoxelModelOcclusionTest.program create mode 100644 Resources/Shaders/VoxelModelOcclusionTest.vs create mode 100644 Resources/Shaders/VoxelModelOutlines.fs create mode 100644 Resources/Shaders/VoxelModelOutlines.program create mode 100644 Resources/Shaders/VoxelModelOutlines.vs create mode 100644 Resources/Textures/MapBlock.png create mode 100644 Resources/Textures/MultiMapBlock.png diff --git a/Resources/Scripts/Gui/Client/ChatSayWindow.as b/Resources/Scripts/Gui/Client/ChatSayWindow.as index 6ccdd878a..5bba6121f 100644 --- a/Resources/Scripts/Gui/Client/ChatSayWindow.as +++ b/Resources/Scripts/Gui/Client/ChatSayWindow.as @@ -190,6 +190,7 @@ namespace spades { @field = CommandField(Manager, ui.chatHistory); field.Bounds = AABB2(winX, winY, winW, 30.f); field.Placeholder = _Tr("Client", "Chat Text"); + field.MaxLength = 93; // ADDED @field.Changed = spades::ui::EventHandler(this.OnFieldChanged); AddChild(field); } diff --git a/Resources/Scripts/Gui/Preferences.as b/Resources/Scripts/Gui/Preferences.as index 80dee63df..fd926dad3 100644 --- a/Resources/Scripts/Gui/Preferences.as +++ b/Resources/Scripts/Gui/Preferences.as @@ -644,6 +644,19 @@ namespace spades { _Tr("Preferences", "OFF")}, array = {2, 1, 0}); + layouter.AddHeading(_Tr("Preferences", "OpenGL Effects")); // ADDED + layouter.AddToggleField(_Tr("Preferences", "Outlines"), "cg_outlines"); // ADDED + layouter.AddSliderField(_Tr("Preferences", "Outline Strength"), "cg_outlineStrength", 2, 5, 1, // ADDED + ConfigNumberFormatter(0, "px")); // ADDED + layouter.AddToggleField(_Tr("Preferences", "Textures"), "cg_textures"); // ADDED + layouter.AddToggleField(_Tr("Preferences", "Multi-Texture Mode"), "cg_multiTextures"); // ADDED + layouter.AddSliderField(_Tr("Preferences", "Texture Strength"), "cg_textureStrength", 0, 100, 1, // ADDED + ConfigNumberFormatter(0, "%")); // ADDED + + layouter.AddHeading(_Tr("Preferences", "Spectator Tools")); // ADDED + layouter.AddToggleField(_Tr("Preferences", "Spectator Player Names"), "dd_specNames"); // ADDED + layouter.AddToggleField(_Tr("Preferences", "Spectator Wallhack"), "dd_specWallhack"); // ADDED + layouter.AddHeading(_Tr("Preferences", "Feedbacks")); layouter.AddToggleField(_Tr("Preferences", "Chat Notify Sounds"), "cg_chatBeep"); layouter.AddToggleField(_Tr("Preferences", "Hit Indicator"), "cg_hitIndicator"); @@ -654,6 +667,8 @@ namespace spades { layouter.AddToggleField(_Tr("Preferences", "Server Alert"), "cg_serverAlert"); layouter.AddHeading(_Tr("Preferences", "Misc")); + layouter.AddToggleField(_Tr("Preferences", "Cheats"), "sv_cheats"); // ADDED + layouter.AddSliderField(_Tr("Preferences", "Volume"), "s_volume", 0, 100, 1, ConfigNumberFormatter(0, "%")); // ADDED layouter.AddSliderField(_Tr("Preferences", "Field of View"), "cg_fov", 45, 90, 1, ConfigNumberFormatter(0, " deg")); layouter.AddSliderField(_Tr("Preferences", "Minimap size"), "cg_minimapSize", 128, 256, diff --git a/Resources/Shaders/BasicBlockOutlines.fs b/Resources/Shaders/BasicBlockOutlines.fs new file mode 100644 index 000000000..43b881838 --- /dev/null +++ b/Resources/Shaders/BasicBlockOutlines.fs @@ -0,0 +1,16 @@ +uniform vec3 fogColor; +uniform vec3 outlineColor; + +varying vec3 fogDensity; + +void main() { + + gl_FragColor.rgb = mix(outlineColor, fogColor, fogDensity); + gl_FragColor.a = 1.0; + +#if !LINEAR_FRAMEBUFFER + // gamma correct + gl_FragColor.xyz = sqrt(gl_FragColor.xyz); +#endif +} + diff --git a/Resources/Shaders/BasicBlockOutlines.program b/Resources/Shaders/BasicBlockOutlines.program new file mode 100644 index 000000000..fc9e20bc7 --- /dev/null +++ b/Resources/Shaders/BasicBlockOutlines.program @@ -0,0 +1,4 @@ +Shaders/BasicBlockOutlines.fs +Shaders/BasicBlockOutlines.vs +*shadow* +Shaders/Fog.vs \ No newline at end of file diff --git a/Resources/Shaders/BasicBlockOutlines.vs b/Resources/Shaders/BasicBlockOutlines.vs new file mode 100644 index 000000000..67e11603a --- /dev/null +++ b/Resources/Shaders/BasicBlockOutlines.vs @@ -0,0 +1,26 @@ +uniform mat4 projectionViewMatrix; +uniform mat4 viewMatrix; +uniform vec3 chunkPosition; +uniform vec3 viewOriginVector; + +attribute vec3 positionAttribute; + +varying vec3 fogDensity; + +vec4 FogDensity(float poweredLength); + +void main() { + + vec4 vertexPos = vec4(chunkPosition, 1.); + + vertexPos.xyz += positionAttribute.xyz; + + gl_Position = projectionViewMatrix * vertexPos; + + vec4 viewPos = viewMatrix * vertexPos; + vec2 horzRelativePos = vertexPos.xy - viewOriginVector.xy; + float horzDistance = dot(horzRelativePos, horzRelativePos); + fogDensity = FogDensity(horzDistance).xyz; + +} + diff --git a/Resources/Shaders/BasicBlockPhysTextures.fs b/Resources/Shaders/BasicBlockPhysTextures.fs new file mode 100644 index 000000000..9164bd92a --- /dev/null +++ b/Resources/Shaders/BasicBlockPhysTextures.fs @@ -0,0 +1,105 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + + + +varying vec4 color; +varying vec2 ambientOcclusionCoord; +varying vec3 fogDensity; +varying vec2 blockTexCoord; // ADDED + +varying vec3 viewSpaceCoord; +varying vec3 viewSpaceNormal; +uniform vec3 viewSpaceLight; + +varying vec3 reflectionDir; + +uniform sampler2D ambientOcclusionTexture; +uniform sampler2D detailTexture; +uniform vec3 fogColor; + +uniform sampler2D blockTexture; // ADDED +uniform float blockTextureStrength; // ADDED + +vec3 EvaluateSunLight(); +vec3 EvaluateAmbientLight(float detailAmbientOcclusion); +vec3 EvaluateDirectionalAmbientLight(float detailAmbientOcclusion, vec3 direction); +//void VisibilityOfSunLight_Model_Debug(); + +float OrenNayar(float sigma, float dotLight, float dotEye); +float CockTorrance(vec3 eyeVec, vec3 lightVec, vec3 normal); + +void main() { + // color is linear + gl_FragColor = vec4(color.xyz, 1.); + + gl_FragColor.rgb = mix(gl_FragColor.rgb, texture2D(blockTexture, blockTexCoord).rgb, blockTextureStrength); // ADDED + + vec3 shading = vec3(OrenNayar(.8, color.w, + -dot(viewSpaceNormal, normalize(viewSpaceCoord)))); + vec3 sunLight = EvaluateSunLight(); + shading *= sunLight; + + float ao = texture2D(ambientOcclusionTexture, ambientOcclusionCoord).x; + + shading += EvaluateAmbientLight(ao); + + // apply diffuse shading + gl_FragColor.xyz *= shading; + + // fresnel term + // FIXME: use split-sum approximation from UE4 + float fresnel2 = 1. - (-dot(viewSpaceNormal, normalize(viewSpaceCoord))); + float fresnel = fresnel2 * fresnel2; + + fresnel = .03 + fresnel * 0.1; + + // blurred reflections + vec3 reflectWS = normalize(reflectionDir); + vec3 reflection = EvaluateDirectionalAmbientLight(ao, reflectWS); + + gl_FragColor.xyz = mix(gl_FragColor.xyz, reflection, fresnel); + + // specular shading + if(color.w > .1 && dot(sunLight, vec3(1.)) > 0.001){ + vec3 specularColor = sunLight; + gl_FragColor.xyz += specularColor * CockTorrance(-normalize(viewSpaceCoord), + viewSpaceLight, + viewSpaceNormal); + } + + // apply fog + gl_FragColor.xyz = mix(gl_FragColor.xyz, fogColor, fogDensity); + + gl_FragColor.xyz = max(gl_FragColor.xyz, 0.); + + // gamma correct +#if !LINEAR_FRAMEBUFFER + gl_FragColor.xyz = sqrt(gl_FragColor.xyz); +#endif + +#if USE_HDR + // somehow denormal occurs, so detect it here and remove + // (denormal destroys screen) + if(gl_FragColor.xyz != gl_FragColor.xyz) + gl_FragColor.xyz = vec3(0.); +#endif +} + diff --git a/Resources/Shaders/BasicBlockPhysTextures.program b/Resources/Shaders/BasicBlockPhysTextures.program new file mode 100644 index 000000000..c6ef970e7 --- /dev/null +++ b/Resources/Shaders/BasicBlockPhysTextures.program @@ -0,0 +1,6 @@ +Shaders/BasicBlockPhysTextures.fs +Shaders/BasicBlockPhysTextures.vs +Shaders/PhysicalModel/OrenNayar.fs +Shaders/PhysicalModel/CookTorrance.fs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/BasicBlockPhysTextures.vs b/Resources/Shaders/BasicBlockPhysTextures.vs new file mode 100644 index 000000000..894e4828a --- /dev/null +++ b/Resources/Shaders/BasicBlockPhysTextures.vs @@ -0,0 +1,102 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + + + +uniform mat4 projectionViewMatrix; +uniform mat4 viewMatrix; +uniform vec3 chunkPosition; +uniform float fogDistance; +uniform vec3 viewOriginVector; + +// --- Vertex attribute --- +// [x, y, z] +attribute vec3 positionAttribute; + +// [ax, ay] +attribute vec2 ambientOcclusionCoordAttribute; + +// [R, G, B, diffuse] +attribute vec4 colorAttribute; + +// [nx, ny, nz] +attribute vec3 normalAttribute; + +// [sx, sy, sz] +attribute vec3 fixedPositionAttribute; + +// [ux, uy] // ADDED +attribute vec2 blockTexCoordAttribute; // ADDED + +varying vec2 ambientOcclusionCoord; +varying vec4 color; +varying vec3 fogDensity; +varying vec2 blockTexCoord; // ADDED + +varying vec3 viewSpaceCoord; +varying vec3 viewSpaceNormal; + +varying vec3 reflectionDir; + +void PrepareForShadowForMap(vec3 vertexCoord, vec3 fixedVertexCoord, vec3 normal); +vec4 FogDensity(float poweredLength); + +void main() { + + blockTexCoord = blockTexCoordAttribute; // ADDED + + vec4 vertexPos = vec4(chunkPosition, 1.); + + vertexPos.xyz += positionAttribute.xyz; + + gl_Position = projectionViewMatrix * vertexPos; + + color = colorAttribute; + color.xyz *= color.xyz; // linearize + + // lambert reflection + vec3 sunDir = normalize(vec3(0, -1., -1.)); + color.w = dot(sunDir, normalAttribute); + + + // ambient occlusion + ambientOcclusionCoord = (ambientOcclusionCoordAttribute + .5) * (1. / 256.); + + vec4 viewPos = viewMatrix * vertexPos; + vec2 horzRelativePos = vertexPos.xy - viewOriginVector.xy; + float horzDistance = dot(horzRelativePos, horzRelativePos); + fogDensity = FogDensity(horzDistance).xyz; + + vec3 fixedPosition = chunkPosition; + fixedPosition += fixedPositionAttribute * 0.5; + fixedPosition += normalAttribute * 0.1; + + vec3 normal = normalAttribute; + vec3 shadowVertexPos = vertexPos.xyz; + PrepareForShadowForMap(shadowVertexPos, fixedPosition, normal); + + // reflection vector (used for specular lighting) + reflectionDir = reflect(vertexPos.xyz - viewOriginVector, normal); + + // used for diffuse lighting + viewSpaceCoord = viewPos.xyz; + viewSpaceNormal = (viewMatrix * vec4(normal, 0.)).xyz; +} + diff --git a/Resources/Shaders/BasicBlockTextures.fs b/Resources/Shaders/BasicBlockTextures.fs new file mode 100644 index 000000000..bc91be9c6 --- /dev/null +++ b/Resources/Shaders/BasicBlockTextures.fs @@ -0,0 +1,64 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + + + +varying vec4 color; +varying vec2 ambientOcclusionCoord; +varying vec2 detailCoord; +varying vec3 fogDensity; +varying vec2 blockTexCoord; // ADDED + +uniform sampler2D ambientOcclusionTexture; +uniform sampler2D detailTexture; +uniform vec3 fogColor; + +uniform sampler2D blockTexture; // ADDED +uniform float blockTextureStrength; // ADDED + +vec3 EvaluateSunLight(); +vec3 EvaluateAmbientLight(float detailAmbientOcclusion); +//void VisibilityOfSunLight_Model_Debug(); + +void main() { + // color is linear + gl_FragColor = vec4(color.xyz, 1.); + + gl_FragColor.rgb = mix(gl_FragColor.rgb, texture2D(blockTexture, blockTexCoord).rgb, blockTextureStrength); // ADDED + + vec3 shading = vec3(color.w); + shading *= EvaluateSunLight(); + + float ao = texture2D(ambientOcclusionTexture, ambientOcclusionCoord).x; + + shading += EvaluateAmbientLight(ao); + + // apply diffuse shading + gl_FragColor.xyz *= shading; + + // apply fog + gl_FragColor.xyz = mix(gl_FragColor.xyz, fogColor, fogDensity); + +#if !LINEAR_FRAMEBUFFER + // gamma correct + gl_FragColor.xyz = sqrt(gl_FragColor.xyz); +#endif +} + diff --git a/Resources/Shaders/BasicBlockTextures.program b/Resources/Shaders/BasicBlockTextures.program new file mode 100644 index 000000000..dd2be0115 --- /dev/null +++ b/Resources/Shaders/BasicBlockTextures.program @@ -0,0 +1,4 @@ +Shaders/BasicBlockTextures.fs +Shaders/BasicBlockTextures.vs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/BasicBlockTextures.vs b/Resources/Shaders/BasicBlockTextures.vs new file mode 100644 index 000000000..550c3f7da --- /dev/null +++ b/Resources/Shaders/BasicBlockTextures.vs @@ -0,0 +1,86 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + + + +uniform mat4 projectionViewMatrix; +uniform mat4 viewMatrix; +uniform vec3 chunkPosition; +uniform float fogDistance; +uniform vec3 viewOriginVector; + +// --- Vertex attribute --- +// [x, y, z] +attribute vec3 positionAttribute; + +// [ax, ay] +attribute vec2 ambientOcclusionCoordAttribute; + +// [R, G, B, diffuse] +attribute vec4 colorAttribute; + +// [nx, ny, nz] +attribute vec3 normalAttribute; + +// [sx, sy, sz] +attribute vec3 fixedPositionAttribute; + +// [ux, uy] // ADDED +attribute vec2 blockTexCoordAttribute; // ADDED + +varying vec2 ambientOcclusionCoord; +varying vec4 color; +varying vec3 fogDensity; +varying vec2 detailCoord; +varying vec2 blockTexCoord; // ADDED + +void PrepareForShadowForMap(vec3 vertexCoord, vec3 fixedVertexCoord, vec3 normal); +vec4 FogDensity(float poweredLength); + +void main() { + + blockTexCoord = blockTexCoordAttribute; // ADDED + + vec4 vertexPos = vec4(chunkPosition, 1.); + + vertexPos.xyz += positionAttribute.xyz; + + gl_Position = projectionViewMatrix * vertexPos; + + color = colorAttribute; + color.xyz *= color.xyz; // linearize + + // ambient occlusion + ambientOcclusionCoord = (ambientOcclusionCoordAttribute + .5) * (1. / 256.); + + vec4 viewPos = viewMatrix * vertexPos; + vec2 horzRelativePos = vertexPos.xy - viewOriginVector.xy; + float horzDistance = dot(horzRelativePos, horzRelativePos); + fogDensity = FogDensity(horzDistance).xyz; + + vec3 fixedPosition = chunkPosition; + fixedPosition += fixedPositionAttribute * 0.5; + fixedPosition += normalAttribute * 0.1; + + vec3 normal = normalAttribute; + vec3 shadowVertexPos = vertexPos.xyz; + PrepareForShadowForMap(shadowVertexPos, fixedPosition, normal); +} + diff --git a/Resources/Shaders/OptimizedVoxelModelOccluded.fs b/Resources/Shaders/OptimizedVoxelModelOccluded.fs new file mode 100644 index 000000000..455c8756b --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOccluded.fs @@ -0,0 +1,4 @@ +void main() { + gl_FragColor.rgba = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/Resources/Shaders/OptimizedVoxelModelOccluded.program b/Resources/Shaders/OptimizedVoxelModelOccluded.program new file mode 100644 index 000000000..49533c13a --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOccluded.program @@ -0,0 +1,4 @@ +Shaders/OptimizedVoxelModelOccluded.fs +Shaders/OptimizedVoxelModelOccluded.vs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/OptimizedVoxelModelOccluded.vs b/Resources/Shaders/OptimizedVoxelModelOccluded.vs new file mode 100644 index 000000000..b52e96e8a --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOccluded.vs @@ -0,0 +1,12 @@ +uniform mat4 projectionViewModelMatrix; +uniform vec3 modelOrigin; + +attribute vec3 positionAttribute; + +void main() { + + vec4 vertexPos = vec4(positionAttribute.xyz, 1.0); + vertexPos.xyz += modelOrigin; + gl_Position = projectionViewModelMatrix * vertexPos; +} + diff --git a/Resources/Shaders/OptimizedVoxelModelOcclusionTest.fs b/Resources/Shaders/OptimizedVoxelModelOcclusionTest.fs new file mode 100644 index 000000000..2456baa0c --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOcclusionTest.fs @@ -0,0 +1,11 @@ +varying vec3 fogDensity; + +void main() { + + if (fogDensity.x > 0.9999 && fogDensity.y > 0.9999 && fogDensity.z > 0.9999) { + discard; + } + + gl_FragColor.rgba = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/Resources/Shaders/OptimizedVoxelModelOcclusionTest.program b/Resources/Shaders/OptimizedVoxelModelOcclusionTest.program new file mode 100644 index 000000000..6f7eef2d5 --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOcclusionTest.program @@ -0,0 +1,4 @@ +Shaders/OptimizedVoxelModelOcclusionTest.fs +Shaders/OptimizedVoxelModelOcclusionTest.vs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/OptimizedVoxelModelOcclusionTest.vs b/Resources/Shaders/OptimizedVoxelModelOcclusionTest.vs new file mode 100644 index 000000000..4cf3e187d --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOcclusionTest.vs @@ -0,0 +1,22 @@ +uniform mat4 projectionViewModelMatrix; +uniform vec3 modelOrigin; +uniform mat4 modelMatrix; +uniform vec3 viewOriginVector; + +attribute vec3 positionAttribute; + +varying vec3 fogDensity; + +vec4 FogDensity(float poweredLength); + +void main() { + + vec4 vertexPos = vec4(positionAttribute.xyz, 1.0); + vertexPos.xyz += modelOrigin; + gl_Position = projectionViewModelMatrix * vertexPos; + + vec2 horzRelativePos = (modelMatrix * vertexPos).xy - viewOriginVector.xy; + float horzDistance = dot(horzRelativePos, horzRelativePos); + fogDensity = FogDensity(horzDistance).xyz; +} + diff --git a/Resources/Shaders/OptimizedVoxelModelOutlines.fs b/Resources/Shaders/OptimizedVoxelModelOutlines.fs new file mode 100644 index 000000000..43b881838 --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOutlines.fs @@ -0,0 +1,16 @@ +uniform vec3 fogColor; +uniform vec3 outlineColor; + +varying vec3 fogDensity; + +void main() { + + gl_FragColor.rgb = mix(outlineColor, fogColor, fogDensity); + gl_FragColor.a = 1.0; + +#if !LINEAR_FRAMEBUFFER + // gamma correct + gl_FragColor.xyz = sqrt(gl_FragColor.xyz); +#endif +} + diff --git a/Resources/Shaders/OptimizedVoxelModelOutlines.program b/Resources/Shaders/OptimizedVoxelModelOutlines.program new file mode 100644 index 000000000..df7bf3d77 --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOutlines.program @@ -0,0 +1,4 @@ +Shaders/OptimizedVoxelModelOutlines.fs +Shaders/OptimizedVoxelModelOutlines.vs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/OptimizedVoxelModelOutlines.vs b/Resources/Shaders/OptimizedVoxelModelOutlines.vs new file mode 100644 index 000000000..9cde14ed6 --- /dev/null +++ b/Resources/Shaders/OptimizedVoxelModelOutlines.vs @@ -0,0 +1,24 @@ +uniform mat4 projectionViewModelMatrix; +uniform mat4 viewModelMatrix; +uniform mat4 modelMatrix; +uniform vec3 modelOrigin; +uniform vec3 viewOriginVector; + +// [x, y, z] +attribute vec3 positionAttribute; + +varying vec3 fogDensity; + +vec4 FogDensity(float poweredLength); + +void main() { + + vec4 vertexPos = vec4(positionAttribute.xyz, 1.); + vertexPos.xyz += modelOrigin; + gl_Position = projectionViewModelMatrix * vertexPos; + + vec2 horzRelativePos = (modelMatrix * vertexPos).xy - viewOriginVector.xy; + float horzDistance = dot(horzRelativePos, horzRelativePos); + fogDensity = FogDensity(horzDistance).xyz; +} + diff --git a/Resources/Shaders/VoxelModelOccluded.fs b/Resources/Shaders/VoxelModelOccluded.fs new file mode 100644 index 000000000..455c8756b --- /dev/null +++ b/Resources/Shaders/VoxelModelOccluded.fs @@ -0,0 +1,4 @@ +void main() { + gl_FragColor.rgba = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/Resources/Shaders/VoxelModelOccluded.program b/Resources/Shaders/VoxelModelOccluded.program new file mode 100644 index 000000000..0ddad6cfd --- /dev/null +++ b/Resources/Shaders/VoxelModelOccluded.program @@ -0,0 +1,4 @@ +Shaders/VoxelModelOccluded.fs +Shaders/VoxelModelOccluded.vs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/VoxelModelOccluded.vs b/Resources/Shaders/VoxelModelOccluded.vs new file mode 100644 index 000000000..6db3cd1bd --- /dev/null +++ b/Resources/Shaders/VoxelModelOccluded.vs @@ -0,0 +1,12 @@ +uniform mat4 projectionViewModelMatrix; +uniform vec3 modelOrigin; + +attribute vec4 positionAttribute; + +void main() { + + vec4 vertexPos = vec4(positionAttribute.xyz, 1.0); + vertexPos.xyz += modelOrigin; + gl_Position = projectionViewModelMatrix * vertexPos; +} + diff --git a/Resources/Shaders/VoxelModelOcclusionTest.fs b/Resources/Shaders/VoxelModelOcclusionTest.fs new file mode 100644 index 000000000..2456baa0c --- /dev/null +++ b/Resources/Shaders/VoxelModelOcclusionTest.fs @@ -0,0 +1,11 @@ +varying vec3 fogDensity; + +void main() { + + if (fogDensity.x > 0.9999 && fogDensity.y > 0.9999 && fogDensity.z > 0.9999) { + discard; + } + + gl_FragColor.rgba = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/Resources/Shaders/VoxelModelOcclusionTest.program b/Resources/Shaders/VoxelModelOcclusionTest.program new file mode 100644 index 000000000..23056b3e6 --- /dev/null +++ b/Resources/Shaders/VoxelModelOcclusionTest.program @@ -0,0 +1,4 @@ +Shaders/VoxelModelOcclusionTest.fs +Shaders/VoxelModelOcclusionTest.vs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/VoxelModelOcclusionTest.vs b/Resources/Shaders/VoxelModelOcclusionTest.vs new file mode 100644 index 000000000..4cf3e187d --- /dev/null +++ b/Resources/Shaders/VoxelModelOcclusionTest.vs @@ -0,0 +1,22 @@ +uniform mat4 projectionViewModelMatrix; +uniform vec3 modelOrigin; +uniform mat4 modelMatrix; +uniform vec3 viewOriginVector; + +attribute vec3 positionAttribute; + +varying vec3 fogDensity; + +vec4 FogDensity(float poweredLength); + +void main() { + + vec4 vertexPos = vec4(positionAttribute.xyz, 1.0); + vertexPos.xyz += modelOrigin; + gl_Position = projectionViewModelMatrix * vertexPos; + + vec2 horzRelativePos = (modelMatrix * vertexPos).xy - viewOriginVector.xy; + float horzDistance = dot(horzRelativePos, horzRelativePos); + fogDensity = FogDensity(horzDistance).xyz; +} + diff --git a/Resources/Shaders/VoxelModelOutlines.fs b/Resources/Shaders/VoxelModelOutlines.fs new file mode 100644 index 000000000..43b881838 --- /dev/null +++ b/Resources/Shaders/VoxelModelOutlines.fs @@ -0,0 +1,16 @@ +uniform vec3 fogColor; +uniform vec3 outlineColor; + +varying vec3 fogDensity; + +void main() { + + gl_FragColor.rgb = mix(outlineColor, fogColor, fogDensity); + gl_FragColor.a = 1.0; + +#if !LINEAR_FRAMEBUFFER + // gamma correct + gl_FragColor.xyz = sqrt(gl_FragColor.xyz); +#endif +} + diff --git a/Resources/Shaders/VoxelModelOutlines.program b/Resources/Shaders/VoxelModelOutlines.program new file mode 100644 index 000000000..795ca67e6 --- /dev/null +++ b/Resources/Shaders/VoxelModelOutlines.program @@ -0,0 +1,4 @@ +Shaders/VoxelModelOutlines.fs +Shaders/VoxelModelOutlines.vs +*shadow* +Shaders/Fog.vs diff --git a/Resources/Shaders/VoxelModelOutlines.vs b/Resources/Shaders/VoxelModelOutlines.vs new file mode 100644 index 000000000..25f4c06a8 --- /dev/null +++ b/Resources/Shaders/VoxelModelOutlines.vs @@ -0,0 +1,24 @@ +uniform mat4 projectionViewModelMatrix; +uniform mat4 viewModelMatrix; +uniform mat4 modelMatrix; +uniform vec3 modelOrigin; +uniform vec3 viewOriginVector; + +// [x, y, z, AO ID] +attribute vec4 positionAttribute; + +varying vec3 fogDensity; + +vec4 FogDensity(float poweredLength); + +void main() { + + vec4 vertexPos = vec4(positionAttribute.xyz, 1.); + vertexPos.xyz += modelOrigin; + gl_Position = projectionViewModelMatrix * vertexPos; + + vec2 horzRelativePos = (modelMatrix * vertexPos).xy - viewOriginVector.xy; + float horzDistance = dot(horzRelativePos, horzRelativePos); + fogDensity = FogDensity(horzDistance).xyz; +} + diff --git a/Resources/Textures/MapBlock.png b/Resources/Textures/MapBlock.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5848955f6a15f07a33240dc8c0df77987dafc0 GIT binary patch literal 3660 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/MultiMapBlock.png b/Resources/Textures/MultiMapBlock.png new file mode 100644 index 0000000000000000000000000000000000000000..76e7daf70593e6092f0ceb26366266a81de6b393 GIT binary patch literal 49314 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 WAKR+p=yU<_pS-k+RHcNe-~R!J)tsvU literal 0 HcmV?d00001 diff --git a/Sources/Audio/ALDevice.cpp b/Sources/Audio/ALDevice.cpp index a035d30be..601b03a92 100644 --- a/Sources/Audio/ALDevice.cpp +++ b/Sources/Audio/ALDevice.cpp @@ -40,6 +40,12 @@ DEFINE_SPADES_SETTING(s_eax, "1"); DEFINE_SPADES_SETTING(s_alPreciseErrorCheck, "1"); DEFINE_SPADES_SETTING(s_gain, "1"); +// ADDED: s_gameVolume field (Corresponds to Volume>) and related variables +SPADES_SETTING(s_volume); +extern int s_volume_previous = 100; // keep track of the "previous" volume so the dB isn't recomputed when unnecessary +extern float dBPrevious = 1.0f; +// END OF ADDED + // lm: seems to be missing for me.. #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 @@ -211,6 +217,21 @@ namespace spades { ALCheckErrorPrecise(); al::qalSourcef(handle, AL_REFERENCE_DISTANCE, param.referenceDistance); + // ADDED: Update master volume control + if (s_volume_previous != (int)s_volume) { + // update the previous volume + s_volume_previous = (int)s_volume; + // compute the new dB level, where 27.71373379 ~ 10^(1/log(2)), and update + // the master gain to it + if ((int)s_volume == 0) { + dBPrevious = 0; + } else { + dBPrevious = powf(27.71373379f, log(((float)s_volume) / 100.0f)); + } + al::qalListenerf(AL_GAIN, dBPrevious); + } + // END OF ADDED + ALCheckError(); this->param = param; } diff --git a/Sources/Audio/YsrDevice.cpp b/Sources/Audio/YsrDevice.cpp index 7ef3448c0..d024d8249 100644 --- a/Sources/Audio/YsrDevice.cpp +++ b/Sources/Audio/YsrDevice.cpp @@ -38,6 +38,12 @@ DEFINE_SPADES_SETTING(s_ysrDriver, "YSRSpades.dll"); DEFINE_SPADES_SETTING(s_ysrDriver, "libysrspades.so"); #endif +// ADDED: s_gameVolume field (Corresponds to Volume>) and related variables +SPADES_SETTING(s_volume); +extern int s_volume_previous; // defined in ALDevice.cpp +extern float dBPrevious; // defined in ALDevice.cpp +// END OF ADDED + DEFINE_SPADES_SETTING(s_ysrNumThreads, "2"); SPADES_SETTING(s_maxPolyphonics); SPADES_SETTING(s_gain); @@ -562,7 +568,23 @@ namespace spades { YsrContext::PlayParam param; param.pitch = base.pitch; param.referenceDistance = base.referenceDistance; - param.volume = base.volume * std::max(std::min(s_gain, 4.0f), 0.0f); + //param.volume = base.volume * std::max(std::min(s_gain, 4.0f), 0.0f); // COMMENTED OUT (unused) + + // ADDED: Update master volume control + if (s_volume_previous != (int)s_volume) { + // update the previous volume + s_volume_previous = (int)s_volume; + // compute the new dB level, where 27.71373379 ~ 10^(1/log(2)), and update the + // master gain to it + if ((int)s_volume == 0) { + dBPrevious = 0; + } else { + dBPrevious = powf(27.71373379f, log(((float)s_volume) / 100.0f)); + } + } + param.volume = dBPrevious; + // END OF ADDED + return param; } diff --git a/Sources/Client/Client.cpp b/Sources/Client/Client.cpp index c6a392619..06ae930dd 100644 --- a/Sources/Client/Client.cpp +++ b/Sources/Client/Client.cpp @@ -63,9 +63,28 @@ DEFINE_SPADES_SETTING(cg_skipDeadPlayersWhenDead, "1"); SPADES_SETTING(cg_playerName); +// ADDED: Define default values for added mod settings +DEFINE_SPADES_SETTING(sv_cheats, "0"); +DEFINE_SPADES_SETTING(dd_specNames, "1"); +DEFINE_SPADES_SETTING(dd_specWallhack, "1"); + +DEFINE_SPADES_SETTING(cg_textures, "1"); +DEFINE_SPADES_SETTING(cg_multiTextures, "0"); +DEFINE_SPADES_SETTING(cg_outlines, "1"); +DEFINE_SPADES_SETTING(cg_textureStrength, "25"); +DEFINE_SPADES_SETTING(cg_multiTextureStrength, "25"); +DEFINE_SPADES_SETTING(cg_outlineStrength, "2"); + +DEFINE_SPADES_SETTING(s_volume, "100"); +// END OF ADDED + namespace spades { namespace client { + // ADDED: Define static variables for Client class + Client *Client::globalInstance = nullptr; + // END OF ADDED + Client::Client(Handle r, Handle audioDev, const ServerAddress &host, Handle fontManager) : playerName(cg_playerName.operator std::string().substr(0, 15)), @@ -108,6 +127,8 @@ namespace spades { SPADES_MARK_FUNCTION(); SPLog("Initializing..."); + Client::globalInstance = this; // ADDED: set global instance + renderer->SetFogDistance(128.f); renderer->SetFogColor(MakeVector3(.8f, 1.f, 1.f)); @@ -199,6 +220,8 @@ namespace spades { NetLog("Disconnecting"); + Client::globalInstance = nullptr; // ADDED: remove global instance + DrawDisconnectScreen(); if (logStream) { @@ -756,5 +779,36 @@ namespace spades { followCameraState.enabled = true; } } + + // ADDED: helper functions +#pragma mark - Helper functions + + bool Client::AreCheatsEnabled() { + if (!globalInstance) + return false; + if (!globalInstance->world) + return false; + Player *p = globalInstance->world->GetLocalPlayer(); + if (!p) + return false; + + return sv_cheats || + (p->GetTeamId() >= 2 && // on spectator team + p->IsAlive()); // alive + } + + bool Client::WallhackActive() { return AreCheatsEnabled() && dd_specWallhack; } + + spades::Vector3 Client::TeamCol(unsigned int teamId) { + if (!globalInstance) { + return Vector3(0, 0, 0); + } + if (teamId >= 2) { + return Vector3(1, 1, 1); + } + spades::IntVector3 col = globalInstance->world->GetTeam(teamId).color; + return Vector3(col.x / 255.0f, col.y / 255.0f, col.z / 255.0f); + } + // END OF ADDED } // namespace client } // namespace spades diff --git a/Sources/Client/Client.h b/Sources/Client/Client.h index f0e272fbd..6e178ba22 100644 --- a/Sources/Client/Client.h +++ b/Sources/Client/Client.h @@ -393,6 +393,8 @@ namespace spades { void NetLog(const char *format, ...); + static Client *globalInstance; // ADDED: Global instance + protected: ~Client(); @@ -479,6 +481,12 @@ namespace spades { void LocalPlayerCreatedLineBlock(IntVector3, IntVector3) override; void LocalPlayerHurt(HurtType type, bool sourceGiven, Vector3 source) override; void LocalPlayerBuildError(BuildFailureReason reason) override; + + // ADDED: helper functions + static bool AreCheatsEnabled(); // 'cheats', i.e. spectator wallhack or player names + static bool WallhackActive(); + static spades::Vector3 TeamCol(unsigned int teamId); + // END OF ADDED }; } // namespace client } // namespace spades diff --git a/Sources/Client/ClientPlayer.cpp b/Sources/Client/ClientPlayer.cpp index e0f5fc58e..1d58b42ce 100644 --- a/Sources/Client/ClientPlayer.cpp +++ b/Sources/Client/ClientPlayer.cpp @@ -879,6 +879,13 @@ namespace spades { IntVector3 col = p->GetColor(); param.customColor = MakeVector3(col.x / 255.f, col.y / 255.f, col.z / 255.f); + // ADDED: Set the param to include player ID, player team id + param.playerID = GetPlayer()->GetId(); + if (client::Client::WallhackActive()) { + param.teamId = GetPlayer()->GetTeamId(); + } + // END OF ADDED + float yaw = atan2(front.y, front.x) + M_PI * .5f; float pitch = -atan2(front.z, sqrt(front.x * front.x + front.y * front.y)); diff --git a/Sources/Client/Client_Draw.cpp b/Sources/Client/Client_Draw.cpp index dc5efdefe..d30fd951d 100644 --- a/Sources/Client/Client_Draw.cpp +++ b/Sources/Client/Client_Draw.cpp @@ -74,6 +74,10 @@ DEFINE_SPADES_SETTING(cg_playerNames, "2"); DEFINE_SPADES_SETTING(cg_playerNameX, "0"); DEFINE_SPADES_SETTING(cg_playerNameY, "0"); +// ADDED: Settings +SPADES_SETTING(dd_specNames); +// END OF ADDED + namespace spades { namespace client { @@ -622,12 +626,37 @@ namespace spades { void Client::DrawSpectateHUD() { SPADES_MARK_FUNCTION(); + + IFont &font = *fontManager->GetGuiFont(); + + // ADDED: Draw player names + if (dd_specNames && AreCheatsEnabled()) { + for (int i = 0; i < world->GetNumPlayerSlots(); ++i) { + Player *pIter = world->GetPlayer(i); + + if (!pIter || !pIter->IsAlive() || pIter->GetTeamId() >= 2) { + continue; + } + + Vector3 posxyz = Project(pIter->GetEye()); + if (posxyz.z <= 0) { + continue; + } + Vector2 pos = {posxyz.x, posxyz.y}; + + Vector2 size = font.Measure(pIter->GetName()); + pos.x -= size.x * .5f; + pos.y -= size.y; + font.DrawShadow(pIter->GetName(), pos, 0.85, MakeVector4(1, 1, 1, 1), + MakeVector4(0, 0, 0, 0.5)); + } + } + // END OF ADDED if (cg_hideHud) { return; } - IFont &font = *fontManager->GetGuiFont(); float scrWidth = renderer->ScreenWidth(); float textX = scrWidth - 8.0f; @@ -642,8 +671,11 @@ namespace spades { }; if (HasTargetPlayer(GetCameraMode())) { - addLine(_Tr("Client", "Following {0}", - world->GetPlayerPersistent(GetCameraTargetPlayerId()).name)); + // MODIFIED: include player number + addLine(_Tr("Client", "Following {0} (#{1})", + world->GetPlayerPersistent(GetCameraTargetPlayerId()).name, + GetCameraTargetPlayerId())); + // END OF MODIFIED } textY += 10.0f; diff --git a/Sources/Client/IRenderer.h b/Sources/Client/IRenderer.h index 42e02ae0f..dc7343b45 100644 --- a/Sources/Client/IRenderer.h +++ b/Sources/Client/IRenderer.h @@ -60,6 +60,12 @@ namespace spades { bool ghost = false; /** Specifies the opacity of the model. Ignored if `ghost` is `false`. */ float opacity = 1.0; + // ADDED: PlayerID field, enemy field + // if -1, then the model for this param isn't a player model + // otherwise, it is the ID of the associated player + int playerID = -1; + bool teamId; // team id of player (if we care about playerId) + // END OF ADDED }; enum DynamicLightType { DynamicLightTypePoint, DynamicLightTypeSpotlight }; diff --git a/Sources/Draw/GLMapChunk.cpp b/Sources/Draw/GLMapChunk.cpp index a7bef7f14..5a6a83532 100644 --- a/Sources/Draw/GLMapChunk.cpp +++ b/Sources/Draw/GLMapChunk.cpp @@ -21,9 +21,6 @@ #include #include -#include -#include -#include #include "GLDynamicLightShader.h" #include "GLMapChunk.h" #include "GLMapRenderer.h" @@ -32,6 +29,19 @@ #include "GLRenderer.h" #include "IGLDevice.h" #include // for asOFFSET. somehow `offsetof` fails on gcc-4.8 +#include +#include +#include + +// ADDED: Additional headers +#include "../Client/PaletteView.h" +#include "GLImage.h" +// END OF ADDED + +// ADDED: cg_textures spade setting +SPADES_SETTING(cg_textures); +SPADES_SETTING(cg_multiTextures); +// END OF ADDED namespace spades { namespace draw { @@ -117,7 +127,9 @@ namespace spades { * @param z Chunk local Z coordinate */ void GLMapChunk::EmitVertex(int x, int y, int z, int aoX, int aoY, int aoZ, int ux, int uy, - int vx, int vy, uint32_t color, int nx, int ny, int nz) { + int vx, int vy, uint32_t color, + int tNumX, int tNumY, // ADDED: multi-texture texture coords + int nx, int ny, int nz) { SPADES_MARK_FUNCTION_DEBUG(); int uz = (ux == 0 && uy == 0) ? 1 : 0; @@ -163,25 +175,101 @@ namespace spades { inst.z = z; inst.aoX = aoTexX; inst.aoY = aoTexY; - vertices.push_back(inst); - inst.x = x + ux; - inst.y = y + uy; - inst.z = z + uz; - inst.aoX = aoTexX + 15; - inst.aoY = aoTexY; - vertices.push_back(inst); - inst.x = x + vx; - inst.y = y + vy; - inst.z = z + vz; - inst.aoX = aoTexX; - inst.aoY = aoTexY + 15; - vertices.push_back(inst); - inst.x = x + ux + vx; - inst.y = y + uy + vy; - inst.z = z + uz + vz; - inst.aoX = aoTexX + 15; - inst.aoY = aoTexY + 15; - vertices.push_back(inst); + + // MODIFIED: Add the vertices differently depending on texture mode + if (!renderer->previous_cg_textures) { + // don't bother with ux/uy + vertices.push_back(inst); + + inst.x = x + ux; + inst.y = y + uy; + inst.z = z + uz; + inst.aoX = aoTexX + 15; + inst.aoY = aoTexY; + vertices.push_back(inst); + + inst.x = x + vx; + inst.y = y + vy; + inst.z = z + vz; + inst.aoX = aoTexX; + inst.aoY = aoTexY + 15; + vertices.push_back(inst); + + inst.x = x + ux + vx; + inst.y = y + uy + vy; + inst.z = z + uz + vz; + inst.aoX = aoTexX + 15; + inst.aoY = aoTexY + 15; + vertices.push_back(inst); + } else if (renderer->previous_cg_multiTextures) { + + float ulen = 1.0f / 8.0f; + + // add the vertices + + inst.ux = (tNumX + 1) * ulen; + inst.uy = tNumY * ulen; + vertices.push_back(inst); + + inst.x = x + ux; + inst.y = y + uy; + inst.z = z + uz; + inst.aoX = aoTexX + 15; + inst.aoY = aoTexY; + inst.ux = (tNumX + 1) * ulen; + inst.uy = (tNumY + 1) * ulen; + vertices.push_back(inst); + + inst.x = x + vx; + inst.y = y + vy; + inst.z = z + vz; + inst.aoX = aoTexX; + inst.aoY = aoTexY + 15; + inst.ux = tNumX * ulen; + inst.uy = tNumY * ulen; + vertices.push_back(inst); + + inst.x = x + ux + vx; + inst.y = y + uy + vy; + inst.z = z + uz + vz; + inst.aoX = aoTexX + 15; + inst.aoY = aoTexY + 15; + inst.ux = tNumX * ulen; + inst.uy = (tNumY + 1) * ulen; + vertices.push_back(inst); + } else { // i.e. single texture mode + inst.ux = 1; + inst.uy = 0; + vertices.push_back(inst); + + inst.x = x + ux; + inst.y = y + uy; + inst.z = z + uz; + inst.aoX = aoTexX + 15; + inst.aoY = aoTexY; + inst.ux = 1; + inst.uy = 1; + vertices.push_back(inst); + + inst.x = x + vx; + inst.y = y + vy; + inst.z = z + vz; + inst.aoX = aoTexX; + inst.aoY = aoTexY + 15; + inst.ux = 0; + inst.uy = 0; + vertices.push_back(inst); + + inst.x = x + ux + vx; + inst.y = y + uy + vy; + inst.z = z + uz + vz; + inst.aoX = aoTexX + 15; + inst.aoY = aoTexY + 15; + inst.ux = 0; + inst.uy = 1; + vertices.push_back(inst); + } + // END OF MODIFIED indices.push_back(idx); indices.push_back(idx + 1); @@ -244,6 +332,44 @@ namespace spades { uint32_t col = map->GetColor(xx, yy, zz); // col = 0xffffffff; + // ADDED: do several things + // determine which faces are being added up here instead of below + bool nsolid1 = !IsSolid(xx, yy, zz + 1); + bool nsolid2 = !IsSolid(xx, yy, zz - 1); + bool nsolid3 = !IsSolid(xx - 1, yy, zz); + bool nsolid4 = !IsSolid(xx + 1, yy, zz); + bool nsolid5 = !IsSolid(xx, yy - 1, zz); + bool nsolid6 = !IsSolid(xx, yy + 1, zz); + + // compute the texture coords for this block (perhaps) + int tNumX, tNumY; + // only do the computation if at least one face added + if (nsolid1 || nsolid2 || nsolid3 || nsolid4 || nsolid5 || nsolid6) { + // only do the computation if in multi-texture mode + if (renderer->previous_cg_multiTextures && + renderer->previous_cg_textures) { + int r = (int)((uint8_t)(col)); + int g = (int)((uint8_t)(col >> 8)); + int b = (int)((uint8_t)(col >> 16)); + // determine the r,g,b coords of the color + int rCoord = r / 64; + int gCoord = g / 64; + int bCoord = b / 64; + // translate that into texture coords on the block + tNumX = rCoord; + tNumY = gCoord; + if (bCoord == 1 || bCoord == 3) { + tNumX += 4; + } + if (bCoord == 2 || bCoord == 3) { + tNumY += 4; + } + } + } else { + continue; // no faces being added + } + // END OF ADDED + // damaged block? int health = col >> 24; if (health < 100) { @@ -252,24 +378,26 @@ namespace spades { col >>= 1; } - if (!IsSolid(xx, yy, zz + 1)) { - EmitVertex(x + 1, y, z + 1, xx, yy, zz + 1, -1, 0, 0, 1, col, 0, 0, 1); + // MODIFIED: include multi-texture coords, use pre-calculated nsolid variables + if (nsolid1) { + EmitVertex(x + 1, y, z + 1, xx, yy, zz + 1, -1, 0, 0, 1, col, tNumX, tNumY, 0, 0, 1); } - if (!IsSolid(xx, yy, zz - 1)) { - EmitVertex(x, y, z, xx, yy, zz - 1, 1, 0, 0, 1, col, 0, 0, -1); + if (nsolid2) { + EmitVertex(x, y, z, xx, yy, zz - 1, 1, 0, 0, 1, col, tNumX, tNumY, 0, 0, -1); } - if (!IsSolid(xx - 1, yy, zz)) { - EmitVertex(x, y + 1, z, xx - 1, yy, zz, 0, 0, 0, -1, col, -1, 0, 0); + if (nsolid3) { + EmitVertex(x, y + 1, z, xx - 1, yy, zz, 0, 0, 0, -1, col, tNumX, tNumY, -1, 0, 0); } - if (!IsSolid(xx + 1, yy, zz)) { - EmitVertex(x + 1, y, z, xx + 1, yy, zz, 0, 0, 0, 1, col, 1, 0, 0); + if (nsolid4) { + EmitVertex(x + 1, y, z, xx + 1, yy, zz, 0, 0, 0, 1, col, tNumX, tNumY, 1, 0, 0); } - if (!IsSolid(xx, yy - 1, zz)) { - EmitVertex(x, y, z, xx, yy - 1, zz, 0, 0, 1, 0, col, 0, -1, 0); + if (nsolid5) { + EmitVertex(x, y, z, xx, yy - 1, zz, 0, 0, 1, 0, col, tNumX, tNumY, 0, -1, 0); } - if (!IsSolid(xx, yy + 1, zz)) { - EmitVertex(x + 1, y + 1, z, xx, yy + 1, zz, 0, 0, -1, 0, col, 0, 1, 0); + if (nsolid6) { + EmitVertex(x + 1, y + 1, z, xx, yy + 1, zz, 0, 0, -1, 0, col, tNumX, tNumY, 0, 1, 0); } + // END OF MODIFIED } } } @@ -405,6 +533,13 @@ namespace spades { static GLProgramAttribute normalAttribute("normalAttribute"); static GLProgramAttribute fixedPositionAttribute("fixedPositionAttribute"); + // ADDED: Setup the texture coordinate attribute + static GLProgramAttribute blockTexCoordAttribute("blockTexCoordAttribute"); + if (renderer->previous_cg_textures) { + blockTexCoordAttribute(basicProgram); + } + // END OF ADDED + positionAttribute(basicProgram); ambientOcclusionCoordAttribute(basicProgram); colorAttribute(basicProgram); @@ -427,6 +562,13 @@ namespace spades { device->VertexAttribPointer(fixedPositionAttribute(), 3, IGLDevice::Byte, false, sizeof(Vertex), (void *)asOFFSET(Vertex, sx)); + // ADDED: Bind the texture coordinates + if (renderer->previous_cg_textures) { + device->VertexAttribPointer(blockTexCoordAttribute(), 2, IGLDevice::FloatType, + false, sizeof(Vertex), (void *)asOFFSET(Vertex, ux)); + } + // END OF ADDED + device->BindBuffer(IGLDevice::ArrayBuffer, 0); device->BindBuffer(IGLDevice::ElementArrayBuffer, iBuffer); device->DrawElements(IGLDevice::Triangles, @@ -513,6 +655,66 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); } + // ADDED: RenderOutlinesPass definition + void GLMapChunk::RenderOutlinesPass() { + SPADES_MARK_FUNCTION(); + Vector3 eye = renderer->renderer->GetSceneDef().viewOrigin; + + if (!realized) + return; + if (needsUpdate) { + Update(); + needsUpdate = false; + } + if (!buffer) { + // empty chunk + return; + } + AABB3 bx = aabb; + + Vector3 diff = eye - centerPos; + float sx = 0.f, sy = 0.f; + // FIXME: variable map size? + if (diff.x > 256.f) + sx += 512.f; + if (diff.y > 256.f) + sy += 512.f; + if (diff.x < -256.f) + sx -= 512.f; + if (diff.y < -256.f) + sy -= 512.f; + + bx.min.x += sx; + bx.min.y += sy; + bx.max.x += sx; + bx.max.y += sy; + + if (!renderer->renderer->BoxFrustrumCull(bx)) + return; + + GLProgram *outlinesProgram = renderer->basicOutlinesProgram; + + static GLProgramUniform chunkPosition("chunkPosition"); + + chunkPosition(outlinesProgram); + chunkPosition.SetValue((float)(chunkX * Size) + sx, (float)(chunkY * Size) + sy, + (float)(chunkZ * Size)); + + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(outlinesProgram); + + device->BindBuffer(IGLDevice::ArrayBuffer, buffer); + device->VertexAttribPointer(positionAttribute(), 3, IGLDevice::UnsignedByte, false, + sizeof(Vertex), (void *)asOFFSET(Vertex, x)); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + device->BindBuffer(IGLDevice::ElementArrayBuffer, iBuffer); + device->DrawElements(IGLDevice::Triangles, indices.size(), IGLDevice::UnsignedShort, + NULL); + device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); + } + // END OF ADDED + float GLMapChunk::DistanceFromEye(const Vector3 &eye) { Vector3 diff = eye - centerPos; diff --git a/Sources/Draw/GLMapChunk.h b/Sources/Draw/GLMapChunk.h index 5f6545174..c1c66ac8b 100644 --- a/Sources/Draw/GLMapChunk.h +++ b/Sources/Draw/GLMapChunk.h @@ -22,6 +22,8 @@ #include +#include "GLDynamicLight.h" +#include "IGLDevice.h" #include #include #include @@ -49,6 +51,10 @@ namespace spades { int8_t sx, sy, sz; uint8_t pad3; + + // ADDED: Texture coordinates variables for the VBO + float ux, uy; + // END OF ADDED }; GLMapRenderer *renderer; @@ -70,8 +76,10 @@ namespace spades { uint8_t calcAOID(int x, int y, int z, int ux, int uy, int uz, int vx, int vy, int vz); - void EmitVertex(int aoX, int aoY, int aoZ, int x, int y, int z, int ux, int uy, int vx, - int vy, uint32_t color, int nx, int ny, int nz); + void EmitVertex(int x, int y, int z, int aoX, int aoY, int aoZ, int ux, int uy, + int vx, int vy, uint32_t color, + int tNumX, int tNumY, // ADDED: multi-texture texture coords + int nx, int ny, int nz); bool IsSolid(int x, int y, int z); @@ -91,6 +99,10 @@ namespace spades { void RenderSunlightPass(); void RenderDepthPass(); void RenderDLightPass(std::vector lights); + + // ADDED: RenderOutlinesPass declaration + void RenderOutlinesPass(); + // END OF ADDED }; } } diff --git a/Sources/Draw/GLMapRenderer.cpp b/Sources/Draw/GLMapRenderer.cpp index cb1d61f29..368c5c8c7 100644 --- a/Sources/Draw/GLMapRenderer.cpp +++ b/Sources/Draw/GLMapRenderer.cpp @@ -35,6 +35,12 @@ #include "GLShadowShader.h" #include "IGLDevice.h" +// ADDED: cg_textures spade setting +SPADES_SETTING(cg_textures); +SPADES_SETTING(cg_multiTextures); +SPADES_SETTING(cg_textureStrength); +// END OF ADDED + namespace spades { namespace draw { void GLMapRenderer::PreloadShaders(spades::draw::GLRenderer *renderer) { @@ -70,11 +76,32 @@ namespace spades { basicProgram = renderer->RegisterProgram("Shaders/BasicBlockPhys.program"); else basicProgram = renderer->RegisterProgram("Shaders/BasicBlock.program"); + + // ADDED: Load the texture shaders, set the no texture shader, load the outlines program + if (r->GetSettings().r_physicalLighting) { + basicTexturesProgram = + renderer->RegisterProgram("Shaders/BasicBlockPhysTextures.program"); + } else { + basicTexturesProgram = + renderer->RegisterProgram("Shaders/BasicBlockTextures.program"); + } + basicNoTexturesProgram = basicProgram; + + basicOutlinesProgram = renderer->RegisterProgram("Shaders/BasicBlockOutlines.program"); + // END OF ADDED + depthonlyProgram = renderer->RegisterProgram("Shaders/BasicBlockDepthOnly.program"); dlightProgram = renderer->RegisterProgram("Shaders/BasicBlockDynamicLit.program"); backfaceProgram = renderer->RegisterProgram("Shaders/BackFaceBlock.program"); aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.png"); + // ADDED: Load the map block images, init previous things + mapBlockImage = (GLImage *)renderer->RegisterImage("Textures/MapBlock.png"); + multiMapBlockImage = (GLImage *)renderer->RegisterImage("Textures/MultiMapBlock.png"); + previous_cg_textures = cg_textures; + previous_cg_multiTextures = cg_multiTextures; + // END OF ADDED + static const uint8_t squareVertices[] = {0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1}; squareVertexBuffer = device->GenBuffer(); device->BindBuffer(IGLDevice::ArrayBuffer, squareVertexBuffer); @@ -123,6 +150,23 @@ namespace spades { } } + // ADDED: UpdateTextureMode + void GLMapRenderer::UpdateTextureMode() { + // determine if texture mode changed + if ((((bool)cg_textures) != previous_cg_textures) || + (((bool)cg_multiTextures) != previous_cg_multiTextures)) { + + // force the chunks to update (need to update their tex-coords) + for (int i = 0; i < numChunks; i++) { + chunks[i]->SetNeedsUpdate(); + } + // update previous things + previous_cg_textures = cg_textures; + previous_cg_multiTextures = cg_multiTextures; + } + } + // END OF ADDED + void GLMapRenderer::RealizeChunks(spades::Vector3 eye) { SPADES_MARK_FUNCTION(); @@ -191,6 +235,14 @@ namespace spades { GLProfiler::Context profiler(renderer->GetGLProfiler(), "Map"); + // ADDED: Determine if texturing this frame, set the program to use + if (previous_cg_textures) { + basicProgram = basicTexturesProgram; + } else { + basicProgram = basicNoTexturesProgram; + } + // END OF ADDED + Vector3 eye = renderer->GetSceneDef().viewOrigin; // draw back face to avoid cheating. @@ -206,11 +258,37 @@ namespace spades { device->ActiveTexture(1); device->BindTexture(IGLDevice::Texture2D, 0); + // ADDED: Bind the map block texture to GL_TEXTURE_8 + static GLProgramUniform blockTextureStrength("blockTextureStrength"); + if (previous_cg_textures) { + + device->ActiveTexture(8); + if (previous_cg_multiTextures) { + multiMapBlockImage->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Nearest); + } else { + mapBlockImage->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Linear); + } + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, + IGLDevice::Nearest); + } + // END OF ADDED + device->Enable(IGLDevice::CullFace, true); device->Enable(IGLDevice::DepthTest, true); basicProgram->Use(); + // ADDED: Set the block texture strength + if (previous_cg_textures) { + blockTextureStrength(basicProgram); + blockTextureStrength.SetValue(((float)cg_textureStrength) / 100.0f); + } + // END OF ADDED + static GLShadowShader shadowShader; shadowShader(renderer, basicProgram, 2); @@ -237,6 +315,14 @@ namespace spades { detailTextureUnif(basicProgram); detailTextureUnif.SetValue(1); + // ADDED: Bind the GL_TEXTURE_8 to the map block texture in the shader + if (previous_cg_textures) { + static GLProgramUniform blockTexture("blockTexture"); + blockTexture(basicProgram); + blockTexture.SetValue(8); + } + // END OF ADDED + device->BindBuffer(IGLDevice::ArrayBuffer, 0); static GLProgramAttribute positionAttribute("positionAttribute"); @@ -246,6 +332,14 @@ namespace spades { static GLProgramAttribute normalAttribute("normalAttribute"); static GLProgramAttribute fixedPositionAttribute("fixedPositionAttribute"); + // ADDED: Setup the texture coordinate attribute + static GLProgramAttribute blockTexCoordAttribute("blockTexCoordAttribute"); + if (previous_cg_textures) { + blockTexCoordAttribute(basicProgram); + device->EnableVertexAttribArray(blockTexCoordAttribute(), true); + } + // END OF ADDED + positionAttribute(basicProgram); ambientOcclusionCoordAttribute(basicProgram); colorAttribute(basicProgram); @@ -300,6 +394,18 @@ namespace spades { device->EnableVertexAttribArray(normalAttribute(), false); device->EnableVertexAttribArray(fixedPositionAttribute(), false); + // ADDED: Clean up texture stuff + if (previous_cg_textures) { + device->EnableVertexAttribArray(blockTexCoordAttribute(), false); + + device->ActiveTexture(8); + device->BindTexture(IGLDevice::Texture2D, 0); + + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, + IGLDevice::Linear); + } + // END OF ADDED + device->ActiveTexture(1); device->BindTexture(IGLDevice::Texture2D, 0); device->ActiveTexture(0); @@ -405,6 +511,67 @@ namespace spades { GetChunk(cx, cy, z)->RenderSunlightPass(); } + // ADDED: RenderOutlinePass definition + void GLMapRenderer::RenderOutlinesPass(Vector3 outlineColor) { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), "Map"); + basicOutlinesProgram->Use(); + + Vector3 eye = renderer->GetSceneDef().viewOrigin; + + static GLProgramUniform fogColor("fogColor"); + fogColor(basicOutlinesProgram); + Vector3 fogCol = renderer->GetFogColorForSolidPass(); + fogCol *= fogCol; // linearize + fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z); + + static GLProgramUniform outlineColorUniform("outlineColor"); + outlineColorUniform(basicOutlinesProgram); + outlineColor *= outlineColor; + outlineColorUniform.SetValue(outlineColor.x, outlineColor.y, outlineColor.z); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(basicOutlinesProgram); + device->EnableVertexAttribArray(positionAttribute(), true); + + static GLProgramUniform viewOriginVector("viewOriginVector"); + viewOriginVector(basicOutlinesProgram); + const auto &viewOrigin = renderer->GetSceneDef().viewOrigin; + viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.z); + + static GLProgramUniform projectionViewMatrix("projectionViewMatrix"); + projectionViewMatrix(basicOutlinesProgram); + projectionViewMatrix.SetValue(renderer->GetProjectionViewMatrix()); + + static GLProgramUniform viewMatrix("viewMatrix"); + viewMatrix(basicOutlinesProgram); + viewMatrix.SetValue(renderer->GetViewMatrix()); + + RealizeChunks(eye); + + // draw from nearest to farthest + int cx = (int)floorf(eye.x) / GLMapChunk::Size; + int cy = (int)floorf(eye.y) / GLMapChunk::Size; + int cz = (int)floorf(eye.z) / GLMapChunk::Size; + DrawColumnOutlines(cx, cy, cz, eye); + for (int dist = 1; dist <= 128 / GLMapChunk::Size; dist++) { + for (int x = cx - dist; x <= cx + dist; x++) { + DrawColumnOutlines(x, cy + dist, cz, eye); + DrawColumnOutlines(x, cy - dist, cz, eye); + } + for (int y = cy - dist + 1; y <= cy + dist - 1; y++) { + DrawColumnOutlines(cx + dist, y, cz, eye); + DrawColumnOutlines(cx - dist, y, cz, eye); + } + } + + device->EnableVertexAttribArray(positionAttribute(), false); + } + // END OF ADDED + void GLMapRenderer::DrawColumnDLight(int cx, int cy, int cz, spades::Vector3 eye, const std::vector &lights) { cx &= numChunkWidth - 1; @@ -415,6 +582,17 @@ namespace spades { GetChunk(cx, cy, z)->RenderDLightPass(lights); } + // ADDED: DrawColumnOutlines definition + void GLMapRenderer::DrawColumnOutlines(int cx, int cy, int cz, spades::Vector3 eye) { + cx &= numChunkWidth - 1; + cy &= numChunkHeight - 1; + for (int z = std::max(cz, 0); z < numChunkDepth; z++) + GetChunk(cx, cy, z)->RenderOutlinesPass(); + for (int z = std::min(cz - 1, 63); z >= 0; z--) + GetChunk(cx, cy, z)->RenderOutlinesPass(); + } + // END OF ADDED + #pragma mark - BackFaceBlock struct BFVertex { diff --git a/Sources/Draw/GLMapRenderer.h b/Sources/Draw/GLMapRenderer.h index 3f0537bea..15e61d490 100644 --- a/Sources/Draw/GLMapRenderer.h +++ b/Sources/Draw/GLMapRenderer.h @@ -46,6 +46,17 @@ namespace spades { GLProgram *backfaceProgram; GLImage *aoImage; + // ADDED: Variables for textures & outlines + GLProgram *basicNoTexturesProgram; // The basic block shader with no textures + GLProgram *basicTexturesProgram; // The basic block shader with textures + GLProgram *basicOutlinesProgram; // The basic block shader for outlines + GLImage *mapBlockImage; // GLImage handle for the map block texture + GLImage *multiMapBlockImage; // GLImage handle for the multi map block texture + bool previous_cg_textures; // the cg_textures value of most recent prerender call + bool + previous_cg_multiTextures; // the cg_multiTextures value of most recent prerender call + // END OF ADDED + IGLDevice::UInteger squareVertexBuffer; struct ChunkRenderInfo { @@ -75,6 +86,10 @@ namespace spades { void DrawColumnDLight(int cx, int cy, int cz, Vector3 eye, const std::vector &lights); + // ADDED: DrawColumnOutlines declaration + void DrawColumnOutlines(int cx, int cy, int cz, Vector3 eye); + // END OF ADDED + void RenderBackface(); public: @@ -87,10 +102,18 @@ namespace spades { client::GameMap *GetMap() { return gameMap; } + // ADDED: Update texture mode + void UpdateTextureMode(); + // END OF ADDED + void Realize(); void Prerender(); void RenderSunlightPass(); void RenderDynamicLightPass(std::vector lights); + + // ADDED: RenderOutlinesPass declaration + void RenderOutlinesPass(Vector3 outlineColor); + // END OF ADDED }; } } diff --git a/Sources/Draw/GLModel.h b/Sources/Draw/GLModel.h index 9906d13b6..e199cc31c 100644 --- a/Sources/Draw/GLModel.h +++ b/Sources/Draw/GLModel.h @@ -22,9 +22,9 @@ #include +#include "GLDynamicLight.h" #include #include -#include "GLDynamicLight.h" namespace spades { namespace draw { @@ -45,11 +45,22 @@ namespace spades { /** Renders sunlighted solid geometry */ virtual void RenderSunlightPass(std::vector params, - bool ghostPass) = 0; + bool ghostPass, + bool farRender) = 0; // MODIFIED: Added farRender /** Adds dynamic light */ virtual void RenderDynamicLightPass(std::vector params, - std::vector lights) = 0; + std::vector lights, + bool farRender) = 0; // MODIFIED: Added farRender + + // ADDED: RenderOutlinesPass, RenderOccludedPass, RenderOcclusionTestPass declaration + virtual void RenderOutlinesPass(std::vector params, + Vector3 outlineColor, bool fog, bool farRender) = 0; + virtual void RenderOccludedPass(std::vector params, + bool farRender) = 0; + virtual void RenderOcclusionTestPass(std::vector params, + bool farRender) = 0; + // END OF ADDED private: // members used when rendering by GLModelRenderer diff --git a/Sources/Draw/GLModelRenderer.cpp b/Sources/Draw/GLModelRenderer.cpp index 3fbbd332e..d8367f49a 100644 --- a/Sources/Draw/GLModelRenderer.cpp +++ b/Sources/Draw/GLModelRenderer.cpp @@ -19,20 +19,37 @@ */ #include "GLModelRenderer.h" -#include #include "GLModel.h" #include "GLProfiler.h" #include "GLRenderer.h" +#include namespace spades { namespace draw { GLModelRenderer::GLModelRenderer(GLRenderer *r) : renderer(r), device(r->GetGLDevice()) { SPADES_MARK_FUNCTION(); + + // ADDED: Create the queries, default them with empty result + for (int i = 0; i < 32; ++i) { + playerVisibilityQueries[i] = device->GenQuery(); + device->BeginQuery(IGLDevice::SamplesPassed, playerVisibilityQueries[i]); + device->EndQuery(IGLDevice::SamplesPassed); + } + device->Flush(); + // END OF ADDED + modelCount = 0; } GLModelRenderer::~GLModelRenderer() { SPADES_MARK_FUNCTION(); + + // ADDED: Free occlusion query objects + for (int i = 0; i < 32; ++i) { + device->DeleteQuery(playerVisibilityQueries[i]); + } + // END OF ADDED + Clear(); } @@ -52,8 +69,9 @@ namespace spades { void GLModelRenderer::RenderShadowMapPass() { SPADES_MARK_FUNCTION(); - GLProfiler::Context profiler(renderer->GetGLProfiler(), "Model [%d model(s), %d unique model type(s)]", modelCount, - (int)models.size()); + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); int numModels = 0; for (size_t i = 0; i < models.size(); i++) { @@ -71,8 +89,9 @@ namespace spades { void GLModelRenderer::Prerender(bool ghostPass) { device->ColorMask(false, false, false, false); - GLProfiler::Context profiler(renderer->GetGLProfiler(), "Model [%d model(s), %d unique model type(s)]", modelCount, - (int)models.size()); + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); int numModels = 0; for (size_t i = 0; i < models.size(); i++) { @@ -87,22 +106,25 @@ namespace spades { void GLModelRenderer::RenderSunlightPass(bool ghostPass) { SPADES_MARK_FUNCTION(); - GLProfiler::Context profiler(renderer->GetGLProfiler(), "Model [%d model(s), %d unique model type(s)]", modelCount, - (int)models.size()); + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); for (size_t i = 0; i < models.size(); i++) { RenderModel &m = models[i]; GLModel *model = m.model; - model->RenderSunlightPass(m.params, ghostPass); + model->RenderSunlightPass(m.params, ghostPass, + false); // MODIFIED: don't do far render } } void GLModelRenderer::RenderDynamicLightPass(std::vector lights) { SPADES_MARK_FUNCTION(); - GLProfiler::Context profiler(renderer->GetGLProfiler(), "Model [%d model(s), %d unique model type(s)]", modelCount, - (int)models.size()); + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); if (!lights.empty()) { @@ -110,10 +132,192 @@ namespace spades { RenderModel &m = models[i]; GLModel *model = m.model; - model->RenderDynamicLightPass(m.params, lights); + model->RenderDynamicLightPass(m.params, lights, + false); // MODIFIED: don't do far render + } + } + } + + // ADDED: Additional definitions + void GLModelRenderer::RenderOutlinesPass() { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); + + for (size_t i = 0; i < models.size(); i++) { + RenderModel &m = models[i]; + GLModel *model = m.model; + + model->RenderOutlinesPass(m.params, Vector3(0.0f, 0.0f, 0.0f), true, false); + } + } + + void GLModelRenderer::DetermineVisiblePlayers(bool visiblePlayers[]) { + SPADES_MARK_FUNCTION(); + // determine player visbility via the last frame + for (int i = 0; i < 32; ++i) { + int iSamplesPassed = device->GetQueryObjectUInteger(playerVisibilityQueries[i], + IGLDevice::QueryResult); + visiblePlayers[i] = (iSamplesPassed > 0); + } + // set up the occlusion query + device->ColorMask(false, false, false, false); + device->DepthMask(false); + // iterate every player and get the new occlusion query going + for (int i = 0; i < 32; ++i) { + device->BeginQuery(IGLDevice::SamplesPassed, playerVisibilityQueries[i]); + for (RenderModel &m : models) { + std::vector playerParams; + for (client::ModelRenderParam p : m.params) { + if (p.playerID == i) { + playerParams.push_back(p); + } + } + m.model->RenderOcclusionTestPass(playerParams, false); } + device->EndQuery(IGLDevice::SamplesPassed); + } + // end with query stuff + device->ColorMask(true, true, true, true); + device->DepthMask(true); + } + + void GLModelRenderer::RenderSunlightPassNoPlayers() { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); + + for (RenderModel &m : models) { + std::vector params; + for (client::ModelRenderParam p : m.params) { + if (p.playerID == -1) { + params.push_back(p); + } + } + m.model->RenderSunlightPass(params, false, false); + } + } + + void GLModelRenderer::RenderDynamicLightPassNoPlayers(std::vector lights) { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); + + for (RenderModel &m : models) { + std::vector params; + for (client::ModelRenderParam p : m.params) { + if (p.playerID == -1) { + params.push_back(p); + } + } + m.model->RenderDynamicLightPass(params, lights, false); + } + } + void GLModelRenderer::RenderSunlightPassVisiblePlayers(bool visiblePlayers[]) { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); + + for (RenderModel &m : models) { + std::vector params; + for (client::ModelRenderParam p : m.params) { + if (p.playerID != -1 && visiblePlayers[p.playerID]) { + params.push_back(p); + } + } + m.model->RenderSunlightPass(params, false, true); + } + } + void + GLModelRenderer::RenderDynamicLightPassVisiblePlayers(bool visiblePlayers[], + std::vector lights) { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); + + for (RenderModel &m : models) { + std::vector params; + for (client::ModelRenderParam p : m.params) { + if (p.playerID != -1 && visiblePlayers[p.playerID]) { + params.push_back(p); + } + } + m.model->RenderDynamicLightPass(params, lights, true); + } + } + void GLModelRenderer::RenderOccludedPlayers(bool visiblePlayers[]) { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); + + for (RenderModel &m : models) { + std::vector params; + for (client::ModelRenderParam p : m.params) { + if (p.playerID != -1 && !visiblePlayers[p.playerID]) { + params.push_back(p); + } + } + m.model->RenderOccludedPass(params, true); + } + } + + void GLModelRenderer::RenderPlayerVisibilityOutlines(bool visiblePlayers[]) { + SPADES_MARK_FUNCTION(); + + GLProfiler::Context profiler(renderer->GetGLProfiler(), + "Model [%d model(s), %d unique model type(s)]", modelCount, + (int)models.size()); + + for (RenderModel &m : models) { + std::vector visibleTeam0; + std::vector nonVisibleTeam0; + std::vector visibleTeam1; + std::vector nonVisibleTeam1; + for (client::ModelRenderParam p : m.params) { + if (p.playerID != -1) { + if (visiblePlayers[p.playerID]) { + if (p.teamId == 0) { + visibleTeam0.push_back(p); + } else { + visibleTeam1.push_back(p); + } + } else { + if (p.teamId == 0) { + nonVisibleTeam0.push_back(p); + } else { + nonVisibleTeam1.push_back(p); + } + } + } + } + + Vector3 team0Col = client::Client::TeamCol(0); + Vector3 team1Col = client::Client::TeamCol(1); + + Vector3 nv0 = team0Col * 0.63; + Vector3 nv1 = team1Col * 0.63; + Vector3 v0 = team0Col; + Vector3 v1 = team1Col; + + m.model->RenderOutlinesPass(nonVisibleTeam0, nv0, false, true); + m.model->RenderOutlinesPass(visibleTeam0, v0, false, true); + m.model->RenderOutlinesPass(nonVisibleTeam1, nv1, false, true); + m.model->RenderOutlinesPass(visibleTeam1, v1, false, true); } } + // END OF ADDED void GLModelRenderer::Clear() { // last phase: clear scene diff --git a/Sources/Draw/GLModelRenderer.h b/Sources/Draw/GLModelRenderer.h index 578cc442a..0c4664f80 100644 --- a/Sources/Draw/GLModelRenderer.h +++ b/Sources/Draw/GLModelRenderer.h @@ -22,10 +22,10 @@ #include -#include -#include #include "GLDynamicLight.h" #include "IGLDevice.h" +#include +#include namespace spades { namespace draw { @@ -46,6 +46,10 @@ namespace spades { std::vector models; int modelCount; + // ADDED: player visibility occlusion query object + unsigned int playerVisibilityQueries[32]; + // END OF ADDED + public: GLModelRenderer(GLRenderer *); ~GLModelRenderer(); @@ -58,7 +62,19 @@ namespace spades { void RenderSunlightPass(bool ghostPass); void RenderDynamicLightPass(std::vector lights); + // ADDED: Additional declarations + void RenderOutlinesPass(); + void DetermineVisiblePlayers(bool visiblePlayers[]); + void RenderSunlightPassNoPlayers(); + void RenderDynamicLightPassNoPlayers(std::vector lights); + void RenderSunlightPassVisiblePlayers(bool visiblePlayers[]); + void RenderDynamicLightPassVisiblePlayers(bool visiblePlayers[], + std::vector lights); + void RenderOccludedPlayers(bool visiblePlayers[]); + void RenderPlayerVisibilityOutlines(bool visiblePlayers[]); + // END OF ADDED + void Clear(); }; } -} \ No newline at end of file +} diff --git a/Sources/Draw/GLOptimizedVoxelModel.cpp b/Sources/Draw/GLOptimizedVoxelModel.cpp index d36160ee1..14182a5bf 100644 --- a/Sources/Draw/GLOptimizedVoxelModel.cpp +++ b/Sources/Draw/GLOptimizedVoxelModel.cpp @@ -60,6 +60,15 @@ namespace spades { renderer->RegisterProgram("Shaders/OptimizedVoxelModelShadowMap.program"); aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.png"); + // ADDED: Load the outlines/occluded program + optimizedVoxelModelOutlinesProgram = + renderer->RegisterProgram("Shaders/OptimizedVoxelModelOutlines.program"); + optimizedVoxelModelOccludedProgram = + renderer->RegisterProgram("Shaders/OptimizedVoxelModelOccluded.program"); + optimizedVoxelModelOcclusionTestProgram = + renderer->RegisterProgram("Shaders/OptimizedVoxelModelOcclusionTest.program"); + // END OF ADDED + buffer = device->GenBuffer(); device->BindBuffer(IGLDevice::ArrayBuffer, buffer); device->BufferData(IGLDevice::ArrayBuffer, @@ -528,7 +537,7 @@ namespace spades { bool ghostPass) { SPADES_MARK_FUNCTION(); - RenderSunlightPass(params, ghostPass); + RenderSunlightPass(params, ghostPass, false); // MODIFIED: farRender } void @@ -613,9 +622,10 @@ namespace spades { device->ActiveTexture(0); device->BindTexture(IGLDevice::Texture2D, 0); } - + + // MODIFIED: farRender functionality void GLOptimizedVoxelModel::RenderSunlightPass(std::vector params, - bool ghostPass) { + bool ghostPass, bool farRender) { SPADES_MARK_FUNCTION(); bool mirror = renderer->IsRenderingMirror(); @@ -713,9 +723,11 @@ namespace spades { // frustrum cull float rad = radius; - rad *= param.matrix.GetAxis(0).GetLength(); - if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { - continue; + if (!farRender) { + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } } static GLProgramUniform customColor("customColor"); @@ -725,7 +737,9 @@ namespace spades { Matrix4 modelMatrix = param.matrix; static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(program); - projectionViewModelMatrix.SetValue(renderer->GetProjectionViewMatrix() * + projectionViewModelMatrix.SetValue((farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()) * modelMatrix); static GLProgramUniform viewModelMatrix("viewModelMatrix"); @@ -772,7 +786,8 @@ namespace spades { void GLOptimizedVoxelModel::RenderDynamicLightPass(std::vector params, - std::vector lights) { + std::vector lights, + bool farRender) { SPADES_MARK_FUNCTION(); bool mirror = renderer->IsRenderingMirror(); @@ -852,9 +867,11 @@ namespace spades { // frustrum cull float rad = radius; - rad *= param.matrix.GetAxis(0).GetLength(); - if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { - continue; + if (!farRender) { + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } } static GLProgramUniform customColor("customColor"); @@ -864,7 +881,9 @@ namespace spades { Matrix4 modelMatrix = param.matrix; static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(dlightProgram); - projectionViewModelMatrix.SetValue(renderer->GetProjectionViewMatrix() * + projectionViewModelMatrix.SetValue((farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()) * modelMatrix); static GLProgramUniform viewModelMatrix("viewModelMatrix"); @@ -907,5 +926,286 @@ namespace spades { device->ActiveTexture(0); } + // END OF MODIFIED + + // ADDED: RenderOutlinesPass definition + void GLOptimizedVoxelModel::RenderOutlinesPass(std::vector params, + Vector3 outlineColor, bool fog, + bool farRender) { + SPADES_MARK_FUNCTION(); + + bool mirror = renderer->IsRenderingMirror(); + + device->ActiveTexture(0); + aoImage->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Linear); + + device->ActiveTexture(1); + image->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Nearest); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, + IGLDevice::Nearest); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + optimizedVoxelModelOutlinesProgram->Use(); + + static GLProgramUniform fogColor("fogColor"); + fogColor(optimizedVoxelModelOutlinesProgram); + Vector3 fogCol = renderer->GetFogColorForSolidPass(); + if (!fog) { + fogCol = outlineColor; + } + fogCol *= fogCol; // linearize + fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z); + + static GLProgramUniform outlineColorUniform("outlineColor"); + outlineColorUniform(optimizedVoxelModelOutlinesProgram); + outlineColor *= outlineColor; + outlineColorUniform.SetValue(outlineColor.x, outlineColor.y, outlineColor.z); + + static GLProgramUniform modelOrigin("modelOrigin"); + modelOrigin(optimizedVoxelModelOutlinesProgram); + modelOrigin.SetValue(origin.x, origin.y, origin.z); + + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(optimizedVoxelModelOutlinesProgram); + + static GLProgramUniform viewOriginVector("viewOriginVector"); + viewOriginVector(optimizedVoxelModelOutlinesProgram); + const auto &viewOrigin = renderer->GetSceneDef().viewOrigin; + viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.z); + + device->BindBuffer(IGLDevice::ArrayBuffer, buffer); + device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, + sizeof(Vertex), (void *)0); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), true); + device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); + + for (size_t i = 0; i < params.size(); i++) { + const client::ModelRenderParam ¶m = params[i]; + + if (mirror && param.depthHack) + continue; + + // frustrum cull + if (!farRender) { + float rad = radius; + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } + } + + Matrix4 modelMatrix = param.matrix; + + static GLProgramUniform modelMatrixU("modelMatrix"); + modelMatrixU(optimizedVoxelModelOutlinesProgram); + modelMatrixU.SetValue(modelMatrix); + + static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); + projectionViewModelMatrix(optimizedVoxelModelOutlinesProgram); + const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()); + projectionViewModelMatrix.SetValue(pvMat * modelMatrix); + + static GLProgramUniform viewModelMatrix("viewModelMatrix"); + viewModelMatrix(optimizedVoxelModelOutlinesProgram); + viewModelMatrix.SetValue(renderer->GetViewMatrix() * modelMatrix); + + if (param.depthHack) { + device->DepthRange(0.f, 0.1f); + } + + device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, + (void *)0); + + if (param.depthHack) { + device->DepthRange(0.f, 1.f); + } + } + + device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), false); + } + // END OF ADDED + + // ADDED: RenderOccludedPass definition + void GLOptimizedVoxelModel::RenderOccludedPass(std::vector params, + bool farRender) { + SPADES_MARK_FUNCTION(); + + bool mirror = renderer->IsRenderingMirror(); + + device->ActiveTexture(0); + aoImage->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Linear); + + device->ActiveTexture(1); + image->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Nearest); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, + IGLDevice::Nearest); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + optimizedVoxelModelOccludedProgram->Use(); + + static GLProgramUniform modelOrigin("modelOrigin"); + modelOrigin(optimizedVoxelModelOccludedProgram); + modelOrigin.SetValue(origin.x, origin.y, origin.z); + + // setup attributes + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(optimizedVoxelModelOccludedProgram); + + device->BindBuffer(IGLDevice::ArrayBuffer, buffer); + device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, + sizeof(Vertex), (void *)0); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), true); + device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); + + for (size_t i = 0; i < params.size(); i++) { + const client::ModelRenderParam ¶m = params[i]; + + if (mirror && param.depthHack) + continue; + + // frustrum cull + if (!farRender) { + float rad = radius; + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } + } + + Matrix4 modelMatrix = param.matrix; + static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); + projectionViewModelMatrix(optimizedVoxelModelOccludedProgram); + const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()); + projectionViewModelMatrix.SetValue(pvMat * modelMatrix); + + if (param.depthHack) { + device->DepthRange(0.f, 0.1f); + } + + device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, + (void *)0); + if (param.depthHack) { + device->DepthRange(0.f, 1.f); + } + } + + device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), false); + } + // END OF ADDED + + // ADDED: RenderOcclusionTestPass definition + void + GLOptimizedVoxelModel::RenderOcclusionTestPass(std::vector params, + bool farRender) { + SPADES_MARK_FUNCTION(); + + bool mirror = renderer->IsRenderingMirror(); + + device->ActiveTexture(0); + aoImage->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Linear); + + device->ActiveTexture(1); + image->Bind(IGLDevice::Texture2D); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Nearest); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, + IGLDevice::Nearest); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + optimizedVoxelModelOcclusionTestProgram->Use(); + + static GLProgramUniform modelOrigin("modelOrigin"); + modelOrigin(optimizedVoxelModelOcclusionTestProgram); + modelOrigin.SetValue(origin.x, origin.y, origin.z); + + // setup attributes + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(optimizedVoxelModelOcclusionTestProgram); + + static GLProgramUniform viewOriginVector("viewOriginVector"); + viewOriginVector(optimizedVoxelModelOcclusionTestProgram); + const auto &viewOrigin = renderer->GetSceneDef().viewOrigin; + viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.z); + + device->BindBuffer(IGLDevice::ArrayBuffer, buffer); + device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, + sizeof(Vertex), (void *)0); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), true); + device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); + + for (size_t i = 0; i < params.size(); i++) { + const client::ModelRenderParam ¶m = params[i]; + + if (mirror && param.depthHack) + continue; + + // frustrum cull + if (!farRender) { + float rad = radius; + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } + } + + Matrix4 modelMatrix = param.matrix; + + static GLProgramUniform modelMatrixU("modelMatrix"); + modelMatrixU(optimizedVoxelModelOcclusionTestProgram); + modelMatrixU.SetValue(modelMatrix); + + static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); + projectionViewModelMatrix(optimizedVoxelModelOcclusionTestProgram); + const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()); + projectionViewModelMatrix.SetValue(pvMat * modelMatrix); + + if (param.depthHack) { + device->DepthRange(0.f, 0.1f); + } + + device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, + (void *)0); + if (param.depthHack) { + device->DepthRange(0.f, 1.f); + } + } + + device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), false); + } + // END OF ADDED } } diff --git a/Sources/Draw/GLOptimizedVoxelModel.h b/Sources/Draw/GLOptimizedVoxelModel.h index 0820f2e56..e3aaf3bf3 100644 --- a/Sources/Draw/GLOptimizedVoxelModel.h +++ b/Sources/Draw/GLOptimizedVoxelModel.h @@ -54,6 +54,12 @@ namespace spades { GLImage *image; GLImage *aoImage; + // ADDED: Outlines & occluded program + GLProgram *optimizedVoxelModelOutlinesProgram; + GLProgram *optimizedVoxelModelOccludedProgram; + GLProgram *optimizedVoxelModelOcclusionTestProgram; + // END OF ADDED + IGLDevice::UInteger buffer; IGLDevice::UInteger idxBuffer; std::vector vertices; @@ -91,12 +97,22 @@ namespace spades { void RenderShadowMapPass(std::vector params) override; void RenderSunlightPass(std::vector params, - bool ghostPass) override; + bool ghostPass, bool farRender) override; // MODIFIED: farRender void RenderDynamicLightPass(std::vector params, - std::vector lights) override; + std::vector lights, + bool farRender) override; // MODIFIED: farRender AABB3 GetBoundingBox() override { return boundingBox; } + + // ADDED: RenderOutlinesPass, RenderOccludedPass declaration + virtual void RenderOutlinesPass(std::vector params, + Vector3 outlineColor, bool fog, bool farRender); + virtual void RenderOccludedPass(std::vector params, + bool farRender); + virtual void RenderOcclusionTestPass(std::vector params, + bool farRender); + // END OF ADDED }; } } diff --git a/Sources/Draw/GLRenderer.cpp b/Sources/Draw/GLRenderer.cpp index 7e38adeb4..47138306a 100644 --- a/Sources/Draw/GLRenderer.cpp +++ b/Sources/Draw/GLRenderer.cpp @@ -21,12 +21,6 @@ #include #include -#include -#include -#include -#include -#include -#include #include "GLAmbientShadowRenderer.h" #include "GLAutoExposureFilter.h" #include "GLBloomFilter.h" @@ -56,6 +50,7 @@ #include "GLProgramUniform.h" #include "GLRadiosityRenderer.h" #include "GLRenderer.h" +#include "GLSSAOFilter.h" #include "GLSettings.h" #include "GLShadowMapShader.h" #include "GLSoftLitSpriteRenderer.h" @@ -63,9 +58,24 @@ #include "GLSpriteRenderer.h" #include "GLVoxelModel.h" #include "GLWaterRenderer.h" -#include "GLSSAOFilter.h" #include "IGLDevice.h" #include "IGLShadowMapRenderer.h" +#include +#include +#include +#include +#include +#include + +// ADDED: Additional headers +#include "../Client/Client.h" +#include "../Imports/OpenGL.h" +// END OF ADDED + +// ADDED: Outlines setting, player observe setting +SPADES_SETTING(cg_outlines); +SPADES_SETTING(cg_outlineStrength); +// END OF ADDED namespace spades { namespace draw { @@ -350,7 +360,8 @@ namespace spades { } Vector3 GLRenderer::GetFogColorForSolidPass() { - if (settings.r_fogShadow && mapShadowRenderer) { + if (settings.r_fogShadow && mapShadowRenderer && + !client::Client::WallhackActive()) { // MODIFIED: no volumetric fog in wallhack return MakeVector3(0, 0, 0); } else { return GetFogColor(); @@ -467,6 +478,12 @@ namespace spades { sceneUsedInThisFrame = true; duringSceneRendering = true; + // ADDED: Update texture mode + if (mapRenderer) { + mapRenderer->UpdateTextureMode(); + } + // END OF ADDED + profiler->BeginFrame(); // clear scene objects @@ -484,6 +501,36 @@ namespace spades { projectionViewMatrix = projectionMatrix * viewMatrix; + // ADDED: Compute the far pv-matrix + { + float near = sceneDef.zNear; + float far = 3000; // this seems to cover the whole map + + float t = near * tanf(sceneDef.fovY * .5f); + float r = near * tanf(sceneDef.fovX * .5f); + float a = r * 2.f, b = t * 2.f, c = far - near; + Matrix4 mat; + mat.m[0] = near * 2.f / a; + mat.m[1] = 0.f; + mat.m[2] = 0.f; + mat.m[3] = 0.f; + mat.m[4] = 0.f; + mat.m[5] = near * 2.f / b; + mat.m[6] = 0.f; + mat.m[7] = 0.f; + mat.m[8] = 0.f; + mat.m[9] = 0.f; + mat.m[10] = -(far + near) / c; + mat.m[11] = -1.f; + mat.m[12] = 0.f; + mat.m[13] = 0.f; + mat.m[14] = -(far * near * 2.f) / c; + mat.m[15] = 0.f; + + farProjectionViewMatrix = mat * viewMatrix; + } + // END OF ADDED + if (settings.r_srgb) device->Enable(IGLDevice::FramebufferSRGB, true); } @@ -605,7 +652,7 @@ namespace spades { device->EnableVertexAttribArray(colorAttribute(), false); } - void GLRenderer::RenderObjects() { + void GLRenderer::RenderObjects(bool reflections) { // draw opaque objects, and do dynamic lighting @@ -680,6 +727,34 @@ namespace spades { modelRenderer->RenderDynamicLightPass(lights); } + // ADDED: Do the outlines pass + if (cg_outlines && !reflections) { + GLProfiler::Context p(*profiler, "Outlines Pass"); + + device->Enable(IGLDevice::Blend, false); + device->Enable(IGLDevice::DepthTest, true); + device->Enable(IGLDevice::CullFace, true); + device->DepthFunc(IGLDevice::LessOrEqual); + + glCullFace(GL_FRONT); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(1, 1); + glLineWidth((int)cg_outlineStrength); + + if (!sceneDef.skipWorld && mapRenderer) { + mapRenderer->RenderOutlinesPass(Vector3(0, 0, 0)); + } + modelRenderer->RenderOutlinesPass(); + + glLineWidth(1); + glCullFace(GL_BACK); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glPolygonOffset(0, 0); + glDisable(GL_POLYGON_OFFSET_LINE); + } + // END OF ADDED + { GLProfiler::Context p(*profiler, "Debug Line"); device->Enable(IGLDevice::Blend, false); @@ -734,7 +809,8 @@ namespace spades { { GLProfiler::Context p(*profiler, "Upload Dynamic Data"); - if (mapShadowRenderer) { + if (mapShadowRenderer && + !client::Client::WallhackActive()) { // MODIFIED: not with wallhack mapShadowRenderer->Update(); } if (ambientShadowRenderer) { @@ -756,7 +832,8 @@ namespace spades { GLProfiler::Context p(*profiler, "Shadow Map Pass"); device->Enable(IGLDevice::DepthTest, true); device->DepthFunc(IGLDevice::Less); - if (shadowMapRenderer) + if (shadowMapRenderer && + !client::Client::WallhackActive()) // MODIFIED: not with wallhack shadowMapRenderer->Render(); } @@ -770,7 +847,8 @@ namespace spades { device->ClearDepth(1.f); device->DepthRange(0.f, 1.f); - if ((int)settings.r_water >= 2) { + if ((int)settings.r_water >= 2 && + !client::Client::WallhackActive()) { // MODIFIED: not with wallhack // for Water 2 (r_water >= 2), we need to render // reflection try { @@ -808,7 +886,7 @@ namespace spades { // render scene GLProfiler::Context p(*profiler, "Mirrored Objects"); - RenderObjects(); + RenderObjects(true); // MODIFIED: Reflection param // restore matrices std::swap(view, viewMatrix); @@ -846,18 +924,126 @@ namespace spades { { GLProfiler::Context p(*profiler, "Clear"); device->ClearColor(bgCol.x, bgCol.y, bgCol.z, 1.f); - device->Clear((IGLDevice::Enum)(IGLDevice::ColorBufferBit | IGLDevice::DepthBufferBit)); + device->Clear( + (IGLDevice::Enum)(IGLDevice::ColorBufferBit | IGLDevice::DepthBufferBit)); } device->FrontFace(IGLDevice::CW); - { + // ADDED: May be needed to determine and keep track of visible players + bool visiblePlayers[32]; + // END OF ADDED + + // MODIFIED: Do the normal pass if not in wallhack + if (!client::Client::WallhackActive()) { GLProfiler::Context p(*profiler, "Non-mirrored Objects"); - RenderObjects(); + RenderObjects(false); // also, not a reflection pass } + // otherwise, render the map, determine visible players, render all non player objects, + // debug lines + else { + + // do ssao stuff + device->Enable(IGLDevice::DepthTest, true); + device->Enable(IGLDevice::Texture2D, true); + device->Enable(IGLDevice::Blend, false); + + bool needsDepthPrepass = settings.r_depthPrepass || settings.r_ssao; + bool needsFullDepthPrepass = settings.r_ssao; + + GLFramebufferManager::BufferHandle ssaoBuffer; + + if (needsDepthPrepass) { + { + GLProfiler::Context p(*profiler, "Depth-only Prepass"); + device->DepthFunc(IGLDevice::Less); + if (!sceneDef.skipWorld && mapRenderer) { + mapRenderer->Prerender(); + } + if (needsFullDepthPrepass) { + modelRenderer->Prerender(false); + } + } + + if (settings.r_ssao) { + { + GLProfiler::Context p(*profiler, "Screen Space Ambient Occlusion"); + device->DepthMask(false); + device->Enable(IGLDevice::DepthTest, false); + device->Enable(IGLDevice::CullFace, false); + + ssaoBuffer = GLSSAOFilter(this).Filter(); + ssaoBufferTexture = ssaoBuffer.GetTexture(); + + device->BindTexture(IGLDevice::Texture2D, ssaoBufferTexture); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, + IGLDevice::Nearest); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Nearest); + + device->Enable(IGLDevice::CullFace, true); + } + GetFramebufferManager()->PrepareSceneRendering(); + } + } + { + GLProfiler::Context p(*profiler, "Sunlight Pass"); + + device->DepthFunc(IGLDevice::LessOrEqual); + if (!sceneDef.skipWorld && mapRenderer) { + mapRenderer->RenderSunlightPass(); + } + modelRenderer->RenderSunlightPass(false); + } + if (settings.r_ssao) { + device->BindTexture(IGLDevice::Texture2D, ssaoBufferTexture); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, + IGLDevice::Linear); + device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, + IGLDevice::Linear); + ssaoBuffer.Release(); + } + + // render the map (sunlight pass) first + device->DepthFunc(IGLDevice::LessOrEqual); + device->Enable(IGLDevice::DepthTest, true); + device->Enable(IGLDevice::Texture2D, true); + device->Enable(IGLDevice::Blend, false); + + if (!sceneDef.skipWorld && mapRenderer) { + mapRenderer->Prerender(); + mapRenderer->RenderSunlightPass(); + } + + // determine the visible players (via their ID's) + modelRenderer->Prerender(false); + modelRenderer->DetermineVisiblePlayers(visiblePlayers); + + // now render all other non-player objects normally + modelRenderer->Prerender(false); + modelRenderer->RenderSunlightPassNoPlayers(); + + device->Enable(IGLDevice::Blend, true); + device->Enable(IGLDevice::DepthTest, true); + device->DepthFunc(IGLDevice::Equal); + device->BlendFunc(IGLDevice::SrcAlpha, IGLDevice::One); + + if (!sceneDef.skipWorld && mapRenderer) { + mapRenderer->RenderDynamicLightPass(lights); + } + modelRenderer->RenderDynamicLightPassNoPlayers(lights); + + GLProfiler::Context p(*profiler, "Debug Line"); + device->Enable(IGLDevice::Blend, false); + device->Enable(IGLDevice::DepthTest, true); + device->DepthFunc(IGLDevice::Less); + RenderDebugLines(); + } + // END OF MODIFIED device->Enable(IGLDevice::CullFace, false); - if (settings.r_water && waterRenderer) { + if (settings.r_water && waterRenderer && + !client::Client::WallhackActive()) { // MODIFIED: not with wallhack GLProfiler::Context p(*profiler, "Water"); waterRenderer->Update(dt); waterRenderer->Render(); @@ -873,18 +1059,83 @@ namespace spades { device->DepthMask(false); if (!settings.r_softParticles) { // softparticle is a part of postprocess GLProfiler::Context p(*profiler, "Particles"); - device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha, - IGLDevice::Zero, IGLDevice::One); + device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha, IGLDevice::Zero, + IGLDevice::One); spriteRenderer->Render(); } { GLProfiler::Context p(*profiler, "Long Particles"); - device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha, - IGLDevice::Zero, IGLDevice::One); + device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha, IGLDevice::Zero, + IGLDevice::One); longSpriteRenderer->Render(); } + // ADDED: Render all players now, above everything else (if wallhack) + if (client::Client::WallhackActive()) { + // now clear the depth buffer, enable depth mask & culling + device->DepthMask(true); + device->Clear(IGLDevice::DepthBufferBit); + device->Enable(IGLDevice::CullFace, true); + + // render the non occluded players normally + { + // do the sunlight pass + device->Enable(IGLDevice::DepthTest, true); + device->DepthFunc(IGLDevice::Less); + device->Enable(IGLDevice::Texture2D, true); + device->Enable(IGLDevice::Blend, false); + + modelRenderer->RenderSunlightPassVisiblePlayers(visiblePlayers); + + // the dynamic light pass + device->Enable(IGLDevice::Blend, true); + device->Enable(IGLDevice::DepthTest, true); + device->DepthFunc(IGLDevice::Equal); + device->BlendFunc(IGLDevice::SrcAlpha, IGLDevice::One); + + modelRenderer->RenderDynamicLightPassVisiblePlayers(visiblePlayers, lights); + } + + // render the occluded players + device->Enable(IGLDevice::DepthTest, true); + device->DepthFunc(IGLDevice::Less); + device->Enable(IGLDevice::Texture2D, true); + device->Enable(IGLDevice::Blend, false); + modelRenderer->RenderOccludedPlayers(visiblePlayers); + + // render the outlines + { + GLProfiler::Context p(*profiler, "Outlines Pass"); + + device->Enable(IGLDevice::Blend, false); + device->Enable(IGLDevice::DepthTest, true); + device->Enable(IGLDevice::CullFace, true); + device->DepthFunc(IGLDevice::LessOrEqual); + + glCullFace(GL_FRONT); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(1, 1); + glLineWidth((int)cg_outlineStrength); + + modelRenderer->RenderPlayerVisibilityOutlines(visiblePlayers); + + glLineWidth(1); + glCullFace(GL_BACK); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glPolygonOffset(0, 0); + glDisable(GL_POLYGON_OFFSET_LINE); + } + + // disable settings + device->ColorMask(true, true, true, true); + device->Enable(IGLDevice::Blend, true); + device->DepthMask(false); + device->Enable(IGLDevice::CullFace, false); + } + // END OF ADDED + device->Enable(IGLDevice::DepthTest, false); GLFramebufferManager::BufferHandle handle; @@ -897,7 +1148,8 @@ namespace spades { GLProfiler::Context p(*profiler, "Preparation"); handle = fbManager->StartPostProcessing(); } - if (settings.r_fogShadow && mapShadowRenderer && + if (!client::Client::WallhackActive() && // MODIFIED: not with wallhack + settings.r_fogShadow && mapShadowRenderer && fogColor.GetPoweredLength() > .000001f) { GLProfiler::Context p(*profiler, "Volumetric Fog"); GLFogFilter fogfilter(this); @@ -905,121 +1157,125 @@ namespace spades { } device->BindFramebuffer(IGLDevice::Framebuffer, handle.GetFramebuffer()); - if (settings.r_softParticles) { // softparticle is a part of postprocess - GLProfiler::Context p(*profiler, "Soft Particle"); - device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha, - IGLDevice::Zero, IGLDevice::One); - spriteRenderer->Render(); - } + // MODIFIED: Don't do this in wallhack, (nothing changed, just added the if + // statement below) + if (!client::Client::WallhackActive()) { + if (settings.r_softParticles) { // softparticle is a part of postprocess + GLProfiler::Context p(*profiler, "Soft Particle"); + device->BlendFunc(IGLDevice::One, IGLDevice::OneMinusSrcAlpha, IGLDevice::Zero, + IGLDevice::One); + spriteRenderer->Render(); + } - device->BlendFunc(IGLDevice::SrcAlpha, IGLDevice::OneMinusSrcAlpha, - IGLDevice::Zero, IGLDevice::One); + device->BlendFunc(IGLDevice::SrcAlpha, IGLDevice::OneMinusSrcAlpha, IGLDevice::Zero, + IGLDevice::One); - if (settings.r_depthOfField && - (sceneDef.depthOfFieldFocalLength > 0.f || sceneDef.blurVignette > 0.f)) { - GLProfiler::Context p(*profiler, "Depth of Field"); - handle = GLDepthOfFieldFilter(this).Filter( - handle, sceneDef.depthOfFieldFocalLength, sceneDef.blurVignette, - sceneDef.globalBlur, sceneDef.depthOfFieldNearBlurStrength, - sceneDef.depthOfFieldFarBlurStrength); - } + if (settings.r_depthOfField && + (sceneDef.depthOfFieldFocalLength > 0.f || sceneDef.blurVignette > 0.f)) { + GLProfiler::Context p(*profiler, "Depth of Field"); + handle = GLDepthOfFieldFilter(this).Filter( + handle, sceneDef.depthOfFieldFocalLength, sceneDef.blurVignette, + sceneDef.globalBlur, sceneDef.depthOfFieldNearBlurStrength, + sceneDef.depthOfFieldFarBlurStrength); + } - if (settings.r_cameraBlur && !sceneDef.denyCameraBlur) { - GLProfiler::Context p(*profiler, "Camera Blur"); - // FIXME: better (correctly constructed) radial blur algorithm - handle = cameraBlur->Filter(handle, sceneDef.radialBlur); - } + if (settings.r_cameraBlur && !sceneDef.denyCameraBlur) { + GLProfiler::Context p(*profiler, "Camera Blur"); + // FIXME: better (correctly constructed) radial blur algorithm + handle = cameraBlur->Filter(handle, sceneDef.radialBlur); + } - if (settings.r_bloom) { - GLProfiler::Context p(*profiler, "Bloom"); - handle = lensDustFilter->Filter(handle); - } + if (settings.r_bloom) { + GLProfiler::Context p(*profiler, "Bloom"); + handle = lensDustFilter->Filter(handle); + } - // do r_fxaa before lens filter so that color aberration looks nice - if (settings.r_fxaa) { - GLProfiler::Context p(*profiler, "FXAA"); - handle = GLFXAAFilter(this).Filter(handle); - } + // do r_fxaa before lens filter so that color aberration looks nice + if (settings.r_fxaa) { + GLProfiler::Context p(*profiler, "FXAA"); + handle = GLFXAAFilter(this).Filter(handle); + } - if (settings.r_lens) { - GLProfiler::Context p(*profiler, "Lens Filter"); - handle = GLLensFilter(this).Filter(handle); - } + if (settings.r_lens) { + GLProfiler::Context p(*profiler, "Lens Filter"); + handle = GLLensFilter(this).Filter(handle); + } - if (settings.r_lensFlare) { - GLProfiler::Context p(*profiler, "Lens Flare"); - device->BindFramebuffer(IGLDevice::Framebuffer, handle.GetFramebuffer()); - GLLensFlareFilter(this).Draw(); - } + if (settings.r_lensFlare) { + GLProfiler::Context p(*profiler, "Lens Flare"); + device->BindFramebuffer(IGLDevice::Framebuffer, handle.GetFramebuffer()); + GLLensFlareFilter(this).Draw(); + } - if (settings.r_lensFlare && settings.r_lensFlareDynamic) { - GLProfiler::Context p(*profiler, "Dynamic Light Lens Flare"); - GLLensFlareFilter lensFlareRenderer(this); - device->BindFramebuffer(IGLDevice::Framebuffer, handle.GetFramebuffer()); - for (size_t i = 0; i < lights.size(); i++) { - const GLDynamicLight &dl = lights[i]; - const client::DynamicLightParam &prm = dl.GetParam(); - if (!prm.useLensFlare) - continue; - Vector3 color = prm.color * 0.6f; - { - // distance attenuation - float rad = (prm.origin - sceneDef.viewOrigin).GetPoweredLength(); - rad /= prm.radius * prm.radius * 18.f; - if (rad > 1.f) + if (settings.r_lensFlare && settings.r_lensFlareDynamic) { + GLProfiler::Context p(*profiler, "Dynamic Light Lens Flare"); + GLLensFlareFilter lensFlareRenderer(this); + device->BindFramebuffer(IGLDevice::Framebuffer, handle.GetFramebuffer()); + for (size_t i = 0; i < lights.size(); i++) { + const GLDynamicLight &dl = lights[i]; + const client::DynamicLightParam &prm = dl.GetParam(); + if (!prm.useLensFlare) continue; - color *= 1.f - rad; - } + Vector3 color = prm.color * 0.6f; + { + // distance attenuation + float rad = (prm.origin - sceneDef.viewOrigin).GetPoweredLength(); + rad /= prm.radius * prm.radius * 18.f; + if (rad > 1.f) + continue; + color *= 1.f - rad; + } - if (prm.type == client::DynamicLightTypeSpotlight) { - // spotlight - Vector3 diff = (sceneDef.viewOrigin - prm.origin).Normalize(); - Vector3 lightdir = prm.spotAxis[2]; - lightdir = lightdir.Normalize(); - float cosVal = Vector3::Dot(diff, lightdir); - float minCosVal = cosf(prm.spotAngle * 0.5f); - if (cosVal < minCosVal) { - // out of range + if (prm.type == client::DynamicLightTypeSpotlight) { + // spotlight + Vector3 diff = (sceneDef.viewOrigin - prm.origin).Normalize(); + Vector3 lightdir = prm.spotAxis[2]; + lightdir = lightdir.Normalize(); + float cosVal = Vector3::Dot(diff, lightdir); + float minCosVal = cosf(prm.spotAngle * 0.5f); + if (cosVal < minCosVal) { + // out of range + continue; + } + color *= (cosVal - minCosVal) / (1.f - minCosVal); + } + + // view cull + if (Vector3::Dot(sceneDef.viewAxis[2], (prm.origin - sceneDef.viewOrigin)) < + 0.f) { continue; } - color *= (cosVal - minCosVal) / (1.f - minCosVal); - } - // view cull - if (Vector3::Dot(sceneDef.viewAxis[2], (prm.origin - sceneDef.viewOrigin)) < - 0.f) { - continue; + lensFlareRenderer.Draw(prm.origin - sceneDef.viewOrigin, true, color, + false); } - - lensFlareRenderer.Draw(prm.origin - sceneDef.viewOrigin, true, color, - false); } - } - // FIXME: these passes should be combined for lower VRAM bandwidth usage + // FIXME: these passes should be combined for lower VRAM bandwidth usage - if (settings.r_hdr) { - GLProfiler::Context p(*profiler, "Auto Exposure"); - handle = autoExposureFilter->Filter(handle, dt); - } + if (settings.r_hdr) { + GLProfiler::Context p(*profiler, "Auto Exposure"); + handle = autoExposureFilter->Filter(handle, dt); + } - if (settings.r_hdr) { - GLProfiler::Context p(*profiler, "Gamma Correction"); - handle = GLNonlinearlizeFilter(this).Filter(handle); - } + if (settings.r_hdr) { + GLProfiler::Context p(*profiler, "Gamma Correction"); + handle = GLNonlinearlizeFilter(this).Filter(handle); + } - if (settings.r_colorCorrection) { - GLProfiler::Context p(*profiler, "Color Correction"); - Vector3 tint = smoothedFogColor + MakeVector3(1.f, 1.f, 1.f) * 0.5f; - tint = MakeVector3(1.f, 1.f, 1.f) / tint; - tint = Mix(tint, MakeVector3(1.f, 1.f, 1.f), 0.2f); - tint *= 1.f / std::min(std::min(tint.x, tint.y), tint.z); + if (settings.r_colorCorrection) { + GLProfiler::Context p(*profiler, "Color Correction"); + Vector3 tint = smoothedFogColor + MakeVector3(1.f, 1.f, 1.f) * 0.5f; + tint = MakeVector3(1.f, 1.f, 1.f) / tint; + tint = Mix(tint, MakeVector3(1.f, 1.f, 1.f), 0.2f); + tint *= 1.f / std::min(std::min(tint.x, tint.y), tint.z); - float exposure = powf(2.f, (float)settings.r_exposureValue * 0.5f); - handle = GLColorCorrectionFilter(this).Filter(handle, tint * exposure); + float exposure = powf(2.f, (float)settings.r_exposureValue * 0.5f); + handle = GLColorCorrectionFilter(this).Filter(handle, tint * exposure); - // update smoothed fog color - smoothedFogColor = Mix(smoothedFogColor, fogColor, 0.002f); + // update smoothed fog color + smoothedFogColor = Mix(smoothedFogColor, fogColor, 0.002f); + } } } diff --git a/Sources/Draw/GLRenderer.h b/Sources/Draw/GLRenderer.h index 656724173..a48a9d569 100644 --- a/Sources/Draw/GLRenderer.h +++ b/Sources/Draw/GLRenderer.h @@ -31,6 +31,11 @@ #include #include +// ADDED: include client, world stuff +#include +#include +// END OF ADDED + namespace spades { namespace draw { @@ -134,7 +139,7 @@ namespace spades { void RenderDebugLines(); - void RenderObjects(); + void RenderObjects(bool reflections); // MODIFIED: Add reflections parameter void RenderGhosts(); void EnsureInitialized(); @@ -227,6 +232,10 @@ namespace spades { bool BoxFrustrumCull(const AABB3 &); bool SphereFrustrumCull(const Vector3 ¢er, float radius); + + // ADDED: Far pv-matrix + Matrix4 farProjectionViewMatrix; + // END OF ADDED }; } } diff --git a/Sources/Draw/GLVoxelModel.cpp b/Sources/Draw/GLVoxelModel.cpp index 5c721b2a4..2089358c9 100644 --- a/Sources/Draw/GLVoxelModel.cpp +++ b/Sources/Draw/GLVoxelModel.cpp @@ -19,7 +19,6 @@ */ #include "GLVoxelModel.h" -#include #include "GLDynamicLightShader.h" #include "GLImage.h" #include "GLProgram.h" @@ -29,6 +28,7 @@ #include "GLShadowMapShader.h" #include "GLShadowShader.h" #include "IGLShadowMapRenderer.h" +#include namespace spades { namespace draw { @@ -49,6 +49,15 @@ namespace spades { shadowMapProgram = renderer->RegisterProgram("Shaders/VoxelModelShadowMap.program"); aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.png"); + // ADDED: Load the outlines program + voxelModelOutlinesProgram = + renderer->RegisterProgram("Shaders/VoxelModelOutlines.program"); + voxelModelOccludedProgram = + renderer->RegisterProgram("Shaders/VoxelModelOccluded.program"); + voxelModelOcclusionTestProgram = + renderer->RegisterProgram("Shaders/VoxelModelOcclusionTest.program"); + // END OF ADDED + BuildVertices(m); buffer = device->GenBuffer(); @@ -260,7 +269,7 @@ namespace spades { void GLVoxelModel::Prerender(std::vector params, bool ghostPass) { SPADES_MARK_FUNCTION(); - RenderSunlightPass(params, ghostPass); + RenderSunlightPass(params, ghostPass, false); // MODIFIED: farRender false } void GLVoxelModel::RenderShadowMapPass(std::vector params) { @@ -345,7 +354,9 @@ namespace spades { device->BindTexture(IGLDevice::Texture2D, 0); } - void GLVoxelModel::RenderSunlightPass(std::vector params, bool ghostPass) { + // MODIFIED: farRender functionality + void GLVoxelModel::RenderSunlightPass(std::vector params, bool ghostPass, + bool farRender) { SPADES_MARK_FUNCTION(); device->ActiveTexture(0); @@ -428,9 +439,11 @@ namespace spades { // frustrum cull float rad = radius; - rad *= param.matrix.GetAxis(0).GetLength(); - if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { - continue; + if (!farRender) { + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } } static GLProgramUniform customColor("customColor"); @@ -440,7 +453,9 @@ namespace spades { Matrix4 modelMatrix = param.matrix; static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(program); - projectionViewModelMatrix.SetValue(renderer->GetProjectionViewMatrix() * + projectionViewModelMatrix.SetValue((farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()) * modelMatrix); static GLProgramUniform viewModelMatrix("viewModelMatrix"); @@ -485,7 +500,8 @@ namespace spades { } void GLVoxelModel::RenderDynamicLightPass(std::vector params, - std::vector lights) { + std::vector lights, + bool farRender) { SPADES_MARK_FUNCTION(); device->ActiveTexture(0); @@ -548,9 +564,11 @@ namespace spades { // frustrum cull float rad = radius; - rad *= param.matrix.GetAxis(0).GetLength(); - if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { - continue; + if (!farRender) { + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } } static GLProgramUniform customColor("customColor"); @@ -560,7 +578,9 @@ namespace spades { Matrix4 modelMatrix = param.matrix; static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(dlightProgram); - projectionViewModelMatrix.SetValue(renderer->GetProjectionViewMatrix() * + projectionViewModelMatrix.SetValue((farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()) * modelMatrix); static GLProgramUniform viewModelMatrix("viewModelMatrix"); @@ -603,5 +623,229 @@ namespace spades { device->ActiveTexture(0); } + // END OF MODIFIED + + // ADDED: RenderOutlinesPass definition + void GLVoxelModel::RenderOutlinesPass(std::vector params, + Vector3 outlineColor, bool fog, bool farRender) { + SPADES_MARK_FUNCTION(); + + device->ActiveTexture(0); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + voxelModelOutlinesProgram->Use(); + + static GLProgramUniform fogColor("fogColor"); + fogColor(voxelModelOutlinesProgram); + Vector3 fogCol = renderer->GetFogColorForSolidPass(); + if (!fog) { + fogCol = outlineColor; + } + fogCol *= fogCol; + fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z); + + static GLProgramUniform outlineColorUniform("outlineColor"); + outlineColorUniform(voxelModelOutlinesProgram); + outlineColor *= outlineColor; + outlineColorUniform.SetValue(outlineColor.x, outlineColor.y, outlineColor.z); + + static GLProgramUniform modelOrigin("modelOrigin"); + modelOrigin(voxelModelOutlinesProgram); + modelOrigin.SetValue(origin.x, origin.y, origin.z); + + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(voxelModelOutlinesProgram); + + static GLProgramUniform viewOriginVector("viewOriginVector"); + viewOriginVector(voxelModelOutlinesProgram); + const auto &viewOrigin = renderer->GetSceneDef().viewOrigin; + viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.z); + + device->BindBuffer(IGLDevice::ArrayBuffer, buffer); + device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, + sizeof(Vertex), (void *)0); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), true); + device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); + + for (size_t i = 0; i < params.size(); i++) { + const client::ModelRenderParam ¶m = params[i]; + + // frustrum cull + if (!farRender) { + float rad = radius; + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } + } + + Matrix4 modelMatrix = param.matrix; + + static GLProgramUniform modelMatrixU("modelMatrix"); + modelMatrixU(voxelModelOutlinesProgram); + modelMatrixU.SetValue(modelMatrix); + + static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); + projectionViewModelMatrix(voxelModelOutlinesProgram); + const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()); + projectionViewModelMatrix.SetValue(pvMat * modelMatrix); + + static GLProgramUniform viewModelMatrix("viewModelMatrix"); + viewModelMatrix(voxelModelOutlinesProgram); + viewModelMatrix.SetValue(renderer->GetViewMatrix() * modelMatrix); + + if (param.depthHack) { + device->DepthRange(0.f, 0.1f); + } + + device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, + (void *)0); + + if (param.depthHack) { + device->DepthRange(0.f, 1.f); + } + } + + device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), false); + } + // END OF ADDED + + // ADDED: RenderOccludedPass definition + void GLVoxelModel::RenderOccludedPass(std::vector params, + bool farRender) { + SPADES_MARK_FUNCTION(); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + voxelModelOccludedProgram->Use(); + + static GLProgramUniform modelOrigin("modelOrigin"); + modelOrigin(voxelModelOccludedProgram); + modelOrigin.SetValue(origin.x, origin.y, origin.z); + + // setup attributes + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(voxelModelOccludedProgram); + + device->BindBuffer(IGLDevice::ArrayBuffer, buffer); + device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, + sizeof(Vertex), (void *)0); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), true); + device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); + + for (size_t i = 0; i < params.size(); i++) { + const client::ModelRenderParam ¶m = params[i]; + + // frustrum cull + if (!farRender) { + float rad = radius; + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } + } + + Matrix4 modelMatrix = param.matrix; + static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); + projectionViewModelMatrix(voxelModelOccludedProgram); + const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()); + projectionViewModelMatrix.SetValue(pvMat * modelMatrix); + + if (param.depthHack) { + device->DepthRange(0.f, 0.1f); + } + + device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, + (void *)0); + if (param.depthHack) { + device->DepthRange(0.f, 1.f); + } + } + + device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), false); + } + // END OF ADDED + + // ADDED: RenderOcclusionTestPass definition + void GLVoxelModel::RenderOcclusionTestPass(std::vector params, + bool farRender) { + SPADES_MARK_FUNCTION(); + + device->Enable(IGLDevice::CullFace, true); + device->Enable(IGLDevice::DepthTest, true); + + voxelModelOcclusionTestProgram->Use(); + + static GLProgramUniform modelOrigin("modelOrigin"); + modelOrigin(voxelModelOcclusionTestProgram); + modelOrigin.SetValue(origin.x, origin.y, origin.z); + + // setup attributes + static GLProgramAttribute positionAttribute("positionAttribute"); + positionAttribute(voxelModelOcclusionTestProgram); + + static GLProgramUniform viewOriginVector("viewOriginVector"); + viewOriginVector(voxelModelOcclusionTestProgram); + const auto &viewOrigin = renderer->GetSceneDef().viewOrigin; + viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.z); + + device->BindBuffer(IGLDevice::ArrayBuffer, buffer); + device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, + sizeof(Vertex), (void *)0); + + device->BindBuffer(IGLDevice::ArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), true); + device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); + + for (size_t i = 0; i < params.size(); i++) { + const client::ModelRenderParam ¶m = params[i]; + + // frustrum cull + if (!farRender) { + float rad = radius; + rad *= param.matrix.GetAxis(0).GetLength(); + if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { + continue; + } + } + + Matrix4 modelMatrix = param.matrix; + + static GLProgramUniform modelMatrixU("modelMatrix"); + modelMatrixU(voxelModelOcclusionTestProgram); + modelMatrixU.SetValue(modelMatrix); + + static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); + projectionViewModelMatrix(voxelModelOcclusionTestProgram); + const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix()); + projectionViewModelMatrix.SetValue(pvMat * modelMatrix); + + if (param.depthHack) { + device->DepthRange(0.f, 0.1f); + } + + device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, + (void *)0); + if (param.depthHack) { + device->DepthRange(0.f, 1.f); + } + } + + device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); + device->EnableVertexAttribArray(positionAttribute(), false); + } + // END OF ADDED } } diff --git a/Sources/Draw/GLVoxelModel.h b/Sources/Draw/GLVoxelModel.h index 820c7f6e4..b1843956c 100644 --- a/Sources/Draw/GLVoxelModel.h +++ b/Sources/Draw/GLVoxelModel.h @@ -54,6 +54,12 @@ namespace spades { GLProgram *shadowMapProgram; GLImage *aoImage; + // ADDED: Outlines, occluded program + GLProgram *voxelModelOutlinesProgram; + GLProgram *voxelModelOccludedProgram; + GLProgram *voxelModelOcclusionTestProgram; + // END OF ADDED + IGLDevice::UInteger buffer; IGLDevice::UInteger idxBuffer; std::vector vertices; @@ -84,10 +90,21 @@ namespace spades { void RenderShadowMapPass(std::vector params) override; void RenderSunlightPass(std::vector params, - bool ghostPass) override; + bool ghostPass, + bool farRender) override; // MODIFIED: farRender void RenderDynamicLightPass(std::vector params, - std::vector lights) override; + std::vector lights, + bool farRender) override; // MODIFIED: farRender + + // ADDED: Other things + virtual void RenderOutlinesPass(std::vector params, + Vector3 outlineColor, bool fog, bool farRender); + virtual void RenderOccludedPass(std::vector params, + bool farRender); + virtual void RenderOcclusionTestPass(std::vector params, + bool farRender); + // END OF ADDED AABB3 GetBoundingBox() override { return boundingBox; } }; From 2e8f7b207bb675a0d453340ad107ef1fd2a6b980 Mon Sep 17 00:00:00 2001 From: Conticop <58798963+Conticop@users.noreply.github.com> Date: Mon, 3 Aug 2020 17:53:54 +0200 Subject: [PATCH 2/6] ... Let's not do this. --- Resources/Scripts/Gui/Preferences.as | 1 - Sources/Client/Client.cpp | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Resources/Scripts/Gui/Preferences.as b/Resources/Scripts/Gui/Preferences.as index fd926dad3..35009c905 100644 --- a/Resources/Scripts/Gui/Preferences.as +++ b/Resources/Scripts/Gui/Preferences.as @@ -667,7 +667,6 @@ namespace spades { layouter.AddToggleField(_Tr("Preferences", "Server Alert"), "cg_serverAlert"); layouter.AddHeading(_Tr("Preferences", "Misc")); - layouter.AddToggleField(_Tr("Preferences", "Cheats"), "sv_cheats"); // ADDED layouter.AddSliderField(_Tr("Preferences", "Volume"), "s_volume", 0, 100, 1, ConfigNumberFormatter(0, "%")); // ADDED layouter.AddSliderField(_Tr("Preferences", "Field of View"), "cg_fov", 45, 90, 1, ConfigNumberFormatter(0, " deg")); diff --git a/Sources/Client/Client.cpp b/Sources/Client/Client.cpp index 06ae930dd..893242841 100644 --- a/Sources/Client/Client.cpp +++ b/Sources/Client/Client.cpp @@ -64,7 +64,6 @@ DEFINE_SPADES_SETTING(cg_skipDeadPlayersWhenDead, "1"); SPADES_SETTING(cg_playerName); // ADDED: Define default values for added mod settings -DEFINE_SPADES_SETTING(sv_cheats, "0"); DEFINE_SPADES_SETTING(dd_specNames, "1"); DEFINE_SPADES_SETTING(dd_specWallhack, "1"); @@ -792,9 +791,8 @@ namespace spades { if (!p) return false; - return sv_cheats || - (p->GetTeamId() >= 2 && // on spectator team - p->IsAlive()); // alive + return p->GetTeamId() >= 2 && // on spectator team + p->IsAlive(); // alive } bool Client::WallhackActive() { return AreCheatsEnabled() && dd_specWallhack; } From 34143d39adca82234aab61d29ce5e92af4ccc99f Mon Sep 17 00:00:00 2001 From: Conticop <58798963+Conticop@users.noreply.github.com> Date: Mon, 3 Aug 2020 21:48:39 +0200 Subject: [PATCH 3/6] Fixing stuff right offpress. * Removed useless comments. Rewritten a couple. * Modernized some code fragments. * Fixed indentation stuff (hopefully). --- Resources/Scripts/Gui/Client/ChatSayWindow.as | 2 +- Resources/Scripts/Gui/Preferences.as | 27 +++---- Resources/Shaders/BasicBlockPhysTextures.fs | 8 +- Resources/Shaders/BasicBlockPhysTextures.vs | 8 +- Resources/Shaders/BasicBlockTextures.fs | 8 +- Resources/Shaders/BasicBlockTextures.vs | 8 +- Sources/Audio/ALDevice.cpp | 7 +- Sources/Audio/YsrDevice.cpp | 10 +-- Sources/Client/Client.cpp | 30 +++----- Sources/Client/Client.h | 6 +- Sources/Client/ClientPlayer.cpp | 5 +- Sources/Client/Client_Draw.cpp | 19 ++--- Sources/Client/IRenderer.h | 8 +- Sources/Draw/GLMapChunk.cpp | 77 ++++++++----------- Sources/Draw/GLMapChunk.h | 12 +-- Sources/Draw/GLMapRenderer.cpp | 49 +++--------- Sources/Draw/GLMapRenderer.h | 13 +--- Sources/Draw/GLModel.h | 6 +- Sources/Draw/GLModelRenderer.cpp | 73 ++++++++---------- Sources/Draw/GLModelRenderer.h | 4 - Sources/Draw/GLOptimizedVoxelModel.cpp | 71 +++++++---------- Sources/Draw/GLOptimizedVoxelModel.h | 8 +- Sources/Draw/GLRenderer.cpp | 58 ++++---------- Sources/Draw/GLRenderer.h | 11 +-- Sources/Draw/GLVoxelModel.cpp | 61 ++++++--------- Sources/Draw/GLVoxelModel.h | 8 +- 26 files changed, 218 insertions(+), 379 deletions(-) diff --git a/Resources/Scripts/Gui/Client/ChatSayWindow.as b/Resources/Scripts/Gui/Client/ChatSayWindow.as index 5bba6121f..ab61d60ad 100644 --- a/Resources/Scripts/Gui/Client/ChatSayWindow.as +++ b/Resources/Scripts/Gui/Client/ChatSayWindow.as @@ -190,7 +190,7 @@ namespace spades { @field = CommandField(Manager, ui.chatHistory); field.Bounds = AABB2(winX, winY, winW, 30.f); field.Placeholder = _Tr("Client", "Chat Text"); - field.MaxLength = 93; // ADDED + field.MaxLength = 93; @field.Changed = spades::ui::EventHandler(this.OnFieldChanged); AddChild(field); } diff --git a/Resources/Scripts/Gui/Preferences.as b/Resources/Scripts/Gui/Preferences.as index 35009c905..fa1aa6d23 100644 --- a/Resources/Scripts/Gui/Preferences.as +++ b/Resources/Scripts/Gui/Preferences.as @@ -644,18 +644,18 @@ namespace spades { _Tr("Preferences", "OFF")}, array = {2, 1, 0}); - layouter.AddHeading(_Tr("Preferences", "OpenGL Effects")); // ADDED - layouter.AddToggleField(_Tr("Preferences", "Outlines"), "cg_outlines"); // ADDED - layouter.AddSliderField(_Tr("Preferences", "Outline Strength"), "cg_outlineStrength", 2, 5, 1, // ADDED - ConfigNumberFormatter(0, "px")); // ADDED - layouter.AddToggleField(_Tr("Preferences", "Textures"), "cg_textures"); // ADDED - layouter.AddToggleField(_Tr("Preferences", "Multi-Texture Mode"), "cg_multiTextures"); // ADDED - layouter.AddSliderField(_Tr("Preferences", "Texture Strength"), "cg_textureStrength", 0, 100, 1, // ADDED - ConfigNumberFormatter(0, "%")); // ADDED - - layouter.AddHeading(_Tr("Preferences", "Spectator Tools")); // ADDED - layouter.AddToggleField(_Tr("Preferences", "Spectator Player Names"), "dd_specNames"); // ADDED - layouter.AddToggleField(_Tr("Preferences", "Spectator Wallhack"), "dd_specWallhack"); // ADDED + layouter.AddHeading(_Tr("Preferences", "OpenGL Effects")); + layouter.AddToggleField(_Tr("Preferences", "Outlines"), "cg_outlines"); + layouter.AddSliderField(_Tr("Preferences", "Outline Strength"), "cg_outlineStrength", 2, 5, 1, + ConfigNumberFormatter(0, "px")); + layouter.AddToggleField(_Tr("Preferences", "Textures"), "cg_textures"); + layouter.AddToggleField(_Tr("Preferences", "Multi-Texture Mode"), "cg_multiTextures"); + layouter.AddSliderField(_Tr("Preferences", "Texture Strength"), "cg_textureStrength", 0, 100, 1, + ConfigNumberFormatter(0, "%")); + + layouter.AddHeading(_Tr("Preferences", "Spectator Tools")); + layouter.AddToggleField(_Tr("Preferences", "Spectator Player Names"), "dd_specNames"); + layouter.AddToggleField(_Tr("Preferences", "Spectator Wallhack"), "dd_specWallhack"); layouter.AddHeading(_Tr("Preferences", "Feedbacks")); layouter.AddToggleField(_Tr("Preferences", "Chat Notify Sounds"), "cg_chatBeep"); @@ -667,7 +667,8 @@ namespace spades { layouter.AddToggleField(_Tr("Preferences", "Server Alert"), "cg_serverAlert"); layouter.AddHeading(_Tr("Preferences", "Misc")); - layouter.AddSliderField(_Tr("Preferences", "Volume"), "s_volume", 0, 100, 1, ConfigNumberFormatter(0, "%")); // ADDED + layouter.AddSliderField(_Tr("Preferences", "Volume"), "s_volume", 0, 100, 1, + ConfigNumberFormatter(0, "%")); layouter.AddSliderField(_Tr("Preferences", "Field of View"), "cg_fov", 45, 90, 1, ConfigNumberFormatter(0, " deg")); layouter.AddSliderField(_Tr("Preferences", "Minimap size"), "cg_minimapSize", 128, 256, diff --git a/Resources/Shaders/BasicBlockPhysTextures.fs b/Resources/Shaders/BasicBlockPhysTextures.fs index 9164bd92a..6799257c1 100644 --- a/Resources/Shaders/BasicBlockPhysTextures.fs +++ b/Resources/Shaders/BasicBlockPhysTextures.fs @@ -23,7 +23,7 @@ varying vec4 color; varying vec2 ambientOcclusionCoord; varying vec3 fogDensity; -varying vec2 blockTexCoord; // ADDED +varying vec2 blockTexCoord; varying vec3 viewSpaceCoord; varying vec3 viewSpaceNormal; @@ -35,8 +35,8 @@ uniform sampler2D ambientOcclusionTexture; uniform sampler2D detailTexture; uniform vec3 fogColor; -uniform sampler2D blockTexture; // ADDED -uniform float blockTextureStrength; // ADDED +uniform sampler2D blockTexture; +uniform float blockTextureStrength; vec3 EvaluateSunLight(); vec3 EvaluateAmbientLight(float detailAmbientOcclusion); @@ -50,7 +50,7 @@ void main() { // color is linear gl_FragColor = vec4(color.xyz, 1.); - gl_FragColor.rgb = mix(gl_FragColor.rgb, texture2D(blockTexture, blockTexCoord).rgb, blockTextureStrength); // ADDED + gl_FragColor.rgb = mix(gl_FragColor.rgb, texture2D(blockTexture, blockTexCoord).rgb, blockTextureStrength); vec3 shading = vec3(OrenNayar(.8, color.w, -dot(viewSpaceNormal, normalize(viewSpaceCoord)))); diff --git a/Resources/Shaders/BasicBlockPhysTextures.vs b/Resources/Shaders/BasicBlockPhysTextures.vs index 894e4828a..f142af61b 100644 --- a/Resources/Shaders/BasicBlockPhysTextures.vs +++ b/Resources/Shaders/BasicBlockPhysTextures.vs @@ -42,13 +42,13 @@ attribute vec3 normalAttribute; // [sx, sy, sz] attribute vec3 fixedPositionAttribute; -// [ux, uy] // ADDED -attribute vec2 blockTexCoordAttribute; // ADDED +// [ux, uy] +attribute vec2 blockTexCoordAttribute; varying vec2 ambientOcclusionCoord; varying vec4 color; varying vec3 fogDensity; -varying vec2 blockTexCoord; // ADDED +varying vec2 blockTexCoord; varying vec3 viewSpaceCoord; varying vec3 viewSpaceNormal; @@ -60,7 +60,7 @@ vec4 FogDensity(float poweredLength); void main() { - blockTexCoord = blockTexCoordAttribute; // ADDED + blockTexCoord = blockTexCoordAttribute; vec4 vertexPos = vec4(chunkPosition, 1.); diff --git a/Resources/Shaders/BasicBlockTextures.fs b/Resources/Shaders/BasicBlockTextures.fs index bc91be9c6..2f5ea9eda 100644 --- a/Resources/Shaders/BasicBlockTextures.fs +++ b/Resources/Shaders/BasicBlockTextures.fs @@ -24,14 +24,14 @@ varying vec4 color; varying vec2 ambientOcclusionCoord; varying vec2 detailCoord; varying vec3 fogDensity; -varying vec2 blockTexCoord; // ADDED +varying vec2 blockTexCoord; uniform sampler2D ambientOcclusionTexture; uniform sampler2D detailTexture; uniform vec3 fogColor; -uniform sampler2D blockTexture; // ADDED -uniform float blockTextureStrength; // ADDED +uniform sampler2D blockTexture; +uniform float blockTextureStrength; vec3 EvaluateSunLight(); vec3 EvaluateAmbientLight(float detailAmbientOcclusion); @@ -41,7 +41,7 @@ void main() { // color is linear gl_FragColor = vec4(color.xyz, 1.); - gl_FragColor.rgb = mix(gl_FragColor.rgb, texture2D(blockTexture, blockTexCoord).rgb, blockTextureStrength); // ADDED + gl_FragColor.rgb = mix(gl_FragColor.rgb, texture2D(blockTexture, blockTexCoord).rgb, blockTextureStrength); vec3 shading = vec3(color.w); shading *= EvaluateSunLight(); diff --git a/Resources/Shaders/BasicBlockTextures.vs b/Resources/Shaders/BasicBlockTextures.vs index 550c3f7da..bfbe8cf10 100644 --- a/Resources/Shaders/BasicBlockTextures.vs +++ b/Resources/Shaders/BasicBlockTextures.vs @@ -42,21 +42,21 @@ attribute vec3 normalAttribute; // [sx, sy, sz] attribute vec3 fixedPositionAttribute; -// [ux, uy] // ADDED -attribute vec2 blockTexCoordAttribute; // ADDED +// [ux, uy] +attribute vec2 blockTexCoordAttribute; varying vec2 ambientOcclusionCoord; varying vec4 color; varying vec3 fogDensity; varying vec2 detailCoord; -varying vec2 blockTexCoord; // ADDED +varying vec2 blockTexCoord; void PrepareForShadowForMap(vec3 vertexCoord, vec3 fixedVertexCoord, vec3 normal); vec4 FogDensity(float poweredLength); void main() { - blockTexCoord = blockTexCoordAttribute; // ADDED + blockTexCoord = blockTexCoordAttribute; vec4 vertexPos = vec4(chunkPosition, 1.); diff --git a/Sources/Audio/ALDevice.cpp b/Sources/Audio/ALDevice.cpp index 601b03a92..1b1191761 100644 --- a/Sources/Audio/ALDevice.cpp +++ b/Sources/Audio/ALDevice.cpp @@ -40,11 +40,9 @@ DEFINE_SPADES_SETTING(s_eax, "1"); DEFINE_SPADES_SETTING(s_alPreciseErrorCheck, "1"); DEFINE_SPADES_SETTING(s_gain, "1"); -// ADDED: s_gameVolume field (Corresponds to Volume>) and related variables SPADES_SETTING(s_volume); -extern int s_volume_previous = 100; // keep track of the "previous" volume so the dB isn't recomputed when unnecessary +extern int s_volume_previous = 100; // keep track of the "previous" volume so the dB isn't recomputed when unnecessary extern float dBPrevious = 1.0f; -// END OF ADDED // lm: seems to be missing for me.. #ifndef ALC_ALL_DEVICES_SPECIFIER @@ -217,7 +215,7 @@ namespace spades { ALCheckErrorPrecise(); al::qalSourcef(handle, AL_REFERENCE_DISTANCE, param.referenceDistance); - // ADDED: Update master volume control + // Update master volume control if (s_volume_previous != (int)s_volume) { // update the previous volume s_volume_previous = (int)s_volume; @@ -230,7 +228,6 @@ namespace spades { } al::qalListenerf(AL_GAIN, dBPrevious); } - // END OF ADDED ALCheckError(); this->param = param; diff --git a/Sources/Audio/YsrDevice.cpp b/Sources/Audio/YsrDevice.cpp index d024d8249..6d0637a5c 100644 --- a/Sources/Audio/YsrDevice.cpp +++ b/Sources/Audio/YsrDevice.cpp @@ -38,11 +38,9 @@ DEFINE_SPADES_SETTING(s_ysrDriver, "YSRSpades.dll"); DEFINE_SPADES_SETTING(s_ysrDriver, "libysrspades.so"); #endif -// ADDED: s_gameVolume field (Corresponds to Volume>) and related variables SPADES_SETTING(s_volume); -extern int s_volume_previous; // defined in ALDevice.cpp -extern float dBPrevious; // defined in ALDevice.cpp -// END OF ADDED +extern int s_volume_previous; +extern float dBPrevious; DEFINE_SPADES_SETTING(s_ysrNumThreads, "2"); SPADES_SETTING(s_maxPolyphonics); @@ -568,9 +566,8 @@ namespace spades { YsrContext::PlayParam param; param.pitch = base.pitch; param.referenceDistance = base.referenceDistance; - //param.volume = base.volume * std::max(std::min(s_gain, 4.0f), 0.0f); // COMMENTED OUT (unused) - // ADDED: Update master volume control + // Update master volume control if (s_volume_previous != (int)s_volume) { // update the previous volume s_volume_previous = (int)s_volume; @@ -583,7 +580,6 @@ namespace spades { } } param.volume = dBPrevious; - // END OF ADDED return param; } diff --git a/Sources/Client/Client.cpp b/Sources/Client/Client.cpp index 893242841..a020f9cf3 100644 --- a/Sources/Client/Client.cpp +++ b/Sources/Client/Client.cpp @@ -63,26 +63,22 @@ DEFINE_SPADES_SETTING(cg_skipDeadPlayersWhenDead, "1"); SPADES_SETTING(cg_playerName); -// ADDED: Define default values for added mod settings -DEFINE_SPADES_SETTING(dd_specNames, "1"); -DEFINE_SPADES_SETTING(dd_specWallhack, "1"); +DEFINE_SPADES_SETTING(dd_specNames, "0"); +DEFINE_SPADES_SETTING(dd_specWallhack, "0"); -DEFINE_SPADES_SETTING(cg_textures, "1"); +DEFINE_SPADES_SETTING(cg_textures, "0"); DEFINE_SPADES_SETTING(cg_multiTextures, "0"); -DEFINE_SPADES_SETTING(cg_outlines, "1"); +DEFINE_SPADES_SETTING(cg_outlines, "0"); DEFINE_SPADES_SETTING(cg_textureStrength, "25"); DEFINE_SPADES_SETTING(cg_multiTextureStrength, "25"); DEFINE_SPADES_SETTING(cg_outlineStrength, "2"); DEFINE_SPADES_SETTING(s_volume, "100"); -// END OF ADDED namespace spades { namespace client { - // ADDED: Define static variables for Client class Client *Client::globalInstance = nullptr; - // END OF ADDED Client::Client(Handle r, Handle audioDev, const ServerAddress &host, Handle fontManager) @@ -126,7 +122,7 @@ namespace spades { SPADES_MARK_FUNCTION(); SPLog("Initializing..."); - Client::globalInstance = this; // ADDED: set global instance + globalInstance = this; renderer->SetFogDistance(128.f); renderer->SetFogColor(MakeVector3(.8f, 1.f, 1.f)); @@ -219,7 +215,7 @@ namespace spades { NetLog("Disconnecting"); - Client::globalInstance = nullptr; // ADDED: remove global instance + globalInstance = nullptr; DrawDisconnectScreen(); @@ -779,7 +775,6 @@ namespace spades { } } - // ADDED: helper functions #pragma mark - Helper functions bool Client::AreCheatsEnabled() { @@ -787,26 +782,25 @@ namespace spades { return false; if (!globalInstance->world) return false; - Player *p = globalInstance->world->GetLocalPlayer(); - if (!p) + auto *ply = globalInstance->world->GetLocalPlayer(); + if (!ply) return false; - return p->GetTeamId() >= 2 && // on spectator team - p->IsAlive(); // alive + return ply->GetTeamId() >= 2 && + ply->IsAlive(); } bool Client::WallhackActive() { return AreCheatsEnabled() && dd_specWallhack; } - spades::Vector3 Client::TeamCol(unsigned int teamId) { + Vector3 Client::TeamCol(unsigned int teamId) { if (!globalInstance) { return Vector3(0, 0, 0); } if (teamId >= 2) { return Vector3(1, 1, 1); } - spades::IntVector3 col = globalInstance->world->GetTeam(teamId).color; + const auto col = globalInstance->world->GetTeam(teamId).color; return Vector3(col.x / 255.0f, col.y / 255.0f, col.z / 255.0f); } - // END OF ADDED } // namespace client } // namespace spades diff --git a/Sources/Client/Client.h b/Sources/Client/Client.h index 6e178ba22..3ac94705a 100644 --- a/Sources/Client/Client.h +++ b/Sources/Client/Client.h @@ -393,7 +393,7 @@ namespace spades { void NetLog(const char *format, ...); - static Client *globalInstance; // ADDED: Global instance + static Client *globalInstance; protected: ~Client(); @@ -482,11 +482,9 @@ namespace spades { void LocalPlayerHurt(HurtType type, bool sourceGiven, Vector3 source) override; void LocalPlayerBuildError(BuildFailureReason reason) override; - // ADDED: helper functions static bool AreCheatsEnabled(); // 'cheats', i.e. spectator wallhack or player names static bool WallhackActive(); - static spades::Vector3 TeamCol(unsigned int teamId); - // END OF ADDED + static Vector3 TeamCol(unsigned int teamId); }; } // namespace client } // namespace spades diff --git a/Sources/Client/ClientPlayer.cpp b/Sources/Client/ClientPlayer.cpp index 1d58b42ce..5a6dafc2c 100644 --- a/Sources/Client/ClientPlayer.cpp +++ b/Sources/Client/ClientPlayer.cpp @@ -879,12 +879,11 @@ namespace spades { IntVector3 col = p->GetColor(); param.customColor = MakeVector3(col.x / 255.f, col.y / 255.f, col.z / 255.f); - // ADDED: Set the param to include player ID, player team id + // Set the param to include player ID and team ID param.playerID = GetPlayer()->GetId(); - if (client::Client::WallhackActive()) { + if (Client::WallhackActive()) { param.teamId = GetPlayer()->GetTeamId(); } - // END OF ADDED float yaw = atan2(front.y, front.x) + M_PI * .5f; float pitch = -atan2(front.z, sqrt(front.x * front.x + front.y * front.y)); diff --git a/Sources/Client/Client_Draw.cpp b/Sources/Client/Client_Draw.cpp index d30fd951d..8eb4a1615 100644 --- a/Sources/Client/Client_Draw.cpp +++ b/Sources/Client/Client_Draw.cpp @@ -74,9 +74,7 @@ DEFINE_SPADES_SETTING(cg_playerNames, "2"); DEFINE_SPADES_SETTING(cg_playerNameX, "0"); DEFINE_SPADES_SETTING(cg_playerNameY, "0"); -// ADDED: Settings SPADES_SETTING(dd_specNames); -// END OF ADDED namespace spades { namespace client { @@ -626,32 +624,31 @@ namespace spades { void Client::DrawSpectateHUD() { SPADES_MARK_FUNCTION(); - - IFont &font = *fontManager->GetGuiFont(); - // ADDED: Draw player names + auto &font = *fontManager->GetGuiFont(); + + // Draw player names if (dd_specNames && AreCheatsEnabled()) { for (int i = 0; i < world->GetNumPlayerSlots(); ++i) { - Player *pIter = world->GetPlayer(i); + auto *pIter = world->GetPlayer(i); if (!pIter || !pIter->IsAlive() || pIter->GetTeamId() >= 2) { continue; } - Vector3 posxyz = Project(pIter->GetEye()); + const auto posxyz = Project(pIter->GetEye()); if (posxyz.z <= 0) { continue; } Vector2 pos = {posxyz.x, posxyz.y}; - Vector2 size = font.Measure(pIter->GetName()); + const auto size = font.Measure(pIter->GetName()); pos.x -= size.x * .5f; pos.y -= size.y; font.DrawShadow(pIter->GetName(), pos, 0.85, MakeVector4(1, 1, 1, 1), - MakeVector4(0, 0, 0, 0.5)); + MakeVector4(0, 0, 0, 0.5)); } } - // END OF ADDED if (cg_hideHud) { return; @@ -671,11 +668,9 @@ namespace spades { }; if (HasTargetPlayer(GetCameraMode())) { - // MODIFIED: include player number addLine(_Tr("Client", "Following {0} (#{1})", world->GetPlayerPersistent(GetCameraTargetPlayerId()).name, GetCameraTargetPlayerId())); - // END OF MODIFIED } textY += 10.0f; diff --git a/Sources/Client/IRenderer.h b/Sources/Client/IRenderer.h index dc7343b45..f106262b3 100644 --- a/Sources/Client/IRenderer.h +++ b/Sources/Client/IRenderer.h @@ -60,12 +60,10 @@ namespace spades { bool ghost = false; /** Specifies the opacity of the model. Ignored if `ghost` is `false`. */ float opacity = 1.0; - // ADDED: PlayerID field, enemy field - // if -1, then the model for this param isn't a player model - // otherwise, it is the ID of the associated player + /** Specifies if the player is associated with the model, otherwise it is `-1`. */ int playerID = -1; - bool teamId; // team id of player (if we care about playerId) - // END OF ADDED + /** Specifies a player's team ID, ignored if `playerID` is set to `-1`. */ + unsigned char teamId : 1; }; enum DynamicLightType { DynamicLightTypePoint, DynamicLightTypeSpotlight }; diff --git a/Sources/Draw/GLMapChunk.cpp b/Sources/Draw/GLMapChunk.cpp index 5a6a83532..57286ea13 100644 --- a/Sources/Draw/GLMapChunk.cpp +++ b/Sources/Draw/GLMapChunk.cpp @@ -33,15 +33,10 @@ #include #include -// ADDED: Additional headers -#include "../Client/PaletteView.h" #include "GLImage.h" -// END OF ADDED -// ADDED: cg_textures spade setting SPADES_SETTING(cg_textures); SPADES_SETTING(cg_multiTextures); -// END OF ADDED namespace spades { namespace draw { @@ -125,10 +120,12 @@ namespace spades { * @param x Chunk local X coordinate * @param y Chunk local Y coordinate * @param z Chunk local Z coordinate + * @param tNumX Multi-texture X coordinate + * @param tNumY Multi-texture Y coordinate */ void GLMapChunk::EmitVertex(int x, int y, int z, int aoX, int aoY, int aoZ, int ux, int uy, int vx, int vy, uint32_t color, - int tNumX, int tNumY, // ADDED: multi-texture texture coords + int tNumX, int tNumY, int nx, int ny, int nz) { SPADES_MARK_FUNCTION_DEBUG(); @@ -176,7 +173,7 @@ namespace spades { inst.aoX = aoTexX; inst.aoY = aoTexY; - // MODIFIED: Add the vertices differently depending on texture mode + // Add the vertices differently depending on texture mode if (!renderer->previous_cg_textures) { // don't bother with ux/uy vertices.push_back(inst); @@ -202,7 +199,6 @@ namespace spades { inst.aoY = aoTexY + 15; vertices.push_back(inst); } else if (renderer->previous_cg_multiTextures) { - float ulen = 1.0f / 8.0f; // add the vertices @@ -269,7 +265,6 @@ namespace spades { inst.uy = 1; vertices.push_back(inst); } - // END OF MODIFIED indices.push_back(idx); indices.push_back(idx + 1); @@ -332,30 +327,29 @@ namespace spades { uint32_t col = map->GetColor(xx, yy, zz); // col = 0xffffffff; - // ADDED: do several things - // determine which faces are being added up here instead of below - bool nsolid1 = !IsSolid(xx, yy, zz + 1); - bool nsolid2 = !IsSolid(xx, yy, zz - 1); - bool nsolid3 = !IsSolid(xx - 1, yy, zz); - bool nsolid4 = !IsSolid(xx + 1, yy, zz); - bool nsolid5 = !IsSolid(xx, yy - 1, zz); - bool nsolid6 = !IsSolid(xx, yy + 1, zz); + // Determine which faces are being added up here instead of below + const auto nsolid1 = !IsSolid(xx, yy, zz + 1); + const auto nsolid2 = !IsSolid(xx, yy, zz - 1); + const auto nsolid3 = !IsSolid(xx - 1, yy, zz); + const auto nsolid4 = !IsSolid(xx + 1, yy, zz); + const auto nsolid5 = !IsSolid(xx, yy - 1, zz); + const auto nsolid6 = !IsSolid(xx, yy + 1, zz); - // compute the texture coords for this block (perhaps) + // Compute the texture coords for this block (perhaps) int tNumX, tNumY; - // only do the computation if at least one face added + // Only do the computation if at least one face added if (nsolid1 || nsolid2 || nsolid3 || nsolid4 || nsolid5 || nsolid6) { - // only do the computation if in multi-texture mode + // Only do the computation if in multi-texture mode if (renderer->previous_cg_multiTextures && renderer->previous_cg_textures) { - int r = (int)((uint8_t)(col)); - int g = (int)((uint8_t)(col >> 8)); - int b = (int)((uint8_t)(col >> 16)); - // determine the r,g,b coords of the color - int rCoord = r / 64; - int gCoord = g / 64; - int bCoord = b / 64; - // translate that into texture coords on the block + const auto r = static_cast(static_cast(col)); + const auto g = static_cast(static_cast(col >> 8)); + const auto b = static_cast(static_cast(col >> 16)); + // Determine the r,g,b coords of the color + const auto rCoord = r / 64; + const auto gCoord = g / 64; + const auto bCoord = b / 64; + // Translate that into texture coords on the block tNumX = rCoord; tNumY = gCoord; if (bCoord == 1 || bCoord == 3) { @@ -368,7 +362,6 @@ namespace spades { } else { continue; // no faces being added } - // END OF ADDED // damaged block? int health = col >> 24; @@ -378,7 +371,6 @@ namespace spades { col >>= 1; } - // MODIFIED: include multi-texture coords, use pre-calculated nsolid variables if (nsolid1) { EmitVertex(x + 1, y, z + 1, xx, yy, zz + 1, -1, 0, 0, 1, col, tNumX, tNumY, 0, 0, 1); } @@ -397,7 +389,6 @@ namespace spades { if (nsolid6) { EmitVertex(x + 1, y + 1, z, xx, yy + 1, zz, 0, 0, -1, 0, col, tNumX, tNumY, 0, 1, 0); } - // END OF MODIFIED } } } @@ -533,12 +524,10 @@ namespace spades { static GLProgramAttribute normalAttribute("normalAttribute"); static GLProgramAttribute fixedPositionAttribute("fixedPositionAttribute"); - // ADDED: Setup the texture coordinate attribute static GLProgramAttribute blockTexCoordAttribute("blockTexCoordAttribute"); if (renderer->previous_cg_textures) { blockTexCoordAttribute(basicProgram); } - // END OF ADDED positionAttribute(basicProgram); ambientOcclusionCoordAttribute(basicProgram); @@ -562,12 +551,10 @@ namespace spades { device->VertexAttribPointer(fixedPositionAttribute(), 3, IGLDevice::Byte, false, sizeof(Vertex), (void *)asOFFSET(Vertex, sx)); - // ADDED: Bind the texture coordinates if (renderer->previous_cg_textures) { device->VertexAttribPointer(blockTexCoordAttribute(), 2, IGLDevice::FloatType, false, sizeof(Vertex), (void *)asOFFSET(Vertex, ux)); } - // END OF ADDED device->BindBuffer(IGLDevice::ArrayBuffer, 0); device->BindBuffer(IGLDevice::ElementArrayBuffer, iBuffer); @@ -655,10 +642,9 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); } - // ADDED: RenderOutlinesPass definition void GLMapChunk::RenderOutlinesPass() { SPADES_MARK_FUNCTION(); - Vector3 eye = renderer->renderer->GetSceneDef().viewOrigin; + const auto eye = renderer->renderer->GetSceneDef().viewOrigin; if (!realized) return; @@ -670,10 +656,10 @@ namespace spades { // empty chunk return; } - AABB3 bx = aabb; + auto bx = aabb; - Vector3 diff = eye - centerPos; - float sx = 0.f, sy = 0.f; + const auto diff = eye - centerPos; + auto sx = 0.f, sy = 0.f; // FIXME: variable map size? if (diff.x > 256.f) sx += 512.f; @@ -692,28 +678,27 @@ namespace spades { if (!renderer->renderer->BoxFrustrumCull(bx)) return; - GLProgram *outlinesProgram = renderer->basicOutlinesProgram; + auto *const outlinesProgram = renderer->basicOutlinesProgram; static GLProgramUniform chunkPosition("chunkPosition"); chunkPosition(outlinesProgram); - chunkPosition.SetValue((float)(chunkX * Size) + sx, (float)(chunkY * Size) + sy, - (float)(chunkZ * Size)); + chunkPosition.SetValue(static_cast(chunkX * Size) + sx, static_cast(chunkY * Size) + sy, + static_cast(chunkZ * Size)); static GLProgramAttribute positionAttribute("positionAttribute"); positionAttribute(outlinesProgram); device->BindBuffer(IGLDevice::ArrayBuffer, buffer); device->VertexAttribPointer(positionAttribute(), 3, IGLDevice::UnsignedByte, false, - sizeof(Vertex), (void *)asOFFSET(Vertex, x)); + sizeof(Vertex), reinterpret_cast(asOFFSET(Vertex, x))); device->BindBuffer(IGLDevice::ArrayBuffer, 0); device->BindBuffer(IGLDevice::ElementArrayBuffer, iBuffer); device->DrawElements(IGLDevice::Triangles, indices.size(), IGLDevice::UnsignedShort, - NULL); + nullptr); device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); } - // END OF ADDED float GLMapChunk::DistanceFromEye(const Vector3 &eye) { Vector3 diff = eye - centerPos; diff --git a/Sources/Draw/GLMapChunk.h b/Sources/Draw/GLMapChunk.h index c1c66ac8b..70cc480e1 100644 --- a/Sources/Draw/GLMapChunk.h +++ b/Sources/Draw/GLMapChunk.h @@ -27,8 +27,6 @@ #include #include #include -#include "GLDynamicLight.h" -#include "IGLDevice.h" namespace spades { namespace draw { @@ -52,9 +50,7 @@ namespace spades { int8_t sx, sy, sz; uint8_t pad3; - // ADDED: Texture coordinates variables for the VBO float ux, uy; - // END OF ADDED }; GLMapRenderer *renderer; @@ -77,9 +73,9 @@ namespace spades { uint8_t calcAOID(int x, int y, int z, int ux, int uy, int uz, int vx, int vy, int vz); void EmitVertex(int x, int y, int z, int aoX, int aoY, int aoZ, int ux, int uy, - int vx, int vy, uint32_t color, - int tNumX, int tNumY, // ADDED: multi-texture texture coords - int nx, int ny, int nz); + int vx, int vy, uint32_t color, + int tNumX, int tNumY, + int nx, int ny, int nz); bool IsSolid(int x, int y, int z); @@ -100,9 +96,7 @@ namespace spades { void RenderDepthPass(); void RenderDLightPass(std::vector lights); - // ADDED: RenderOutlinesPass declaration void RenderOutlinesPass(); - // END OF ADDED }; } } diff --git a/Sources/Draw/GLMapRenderer.cpp b/Sources/Draw/GLMapRenderer.cpp index 368c5c8c7..26f7960d6 100644 --- a/Sources/Draw/GLMapRenderer.cpp +++ b/Sources/Draw/GLMapRenderer.cpp @@ -35,11 +35,9 @@ #include "GLShadowShader.h" #include "IGLDevice.h" -// ADDED: cg_textures spade setting SPADES_SETTING(cg_textures); SPADES_SETTING(cg_multiTextures); SPADES_SETTING(cg_textureStrength); -// END OF ADDED namespace spades { namespace draw { @@ -77,7 +75,6 @@ namespace spades { else basicProgram = renderer->RegisterProgram("Shaders/BasicBlock.program"); - // ADDED: Load the texture shaders, set the no texture shader, load the outlines program if (r->GetSettings().r_physicalLighting) { basicTexturesProgram = renderer->RegisterProgram("Shaders/BasicBlockPhysTextures.program"); @@ -88,19 +85,16 @@ namespace spades { basicNoTexturesProgram = basicProgram; basicOutlinesProgram = renderer->RegisterProgram("Shaders/BasicBlockOutlines.program"); - // END OF ADDED depthonlyProgram = renderer->RegisterProgram("Shaders/BasicBlockDepthOnly.program"); dlightProgram = renderer->RegisterProgram("Shaders/BasicBlockDynamicLit.program"); backfaceProgram = renderer->RegisterProgram("Shaders/BackFaceBlock.program"); aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.png"); - // ADDED: Load the map block images, init previous things mapBlockImage = (GLImage *)renderer->RegisterImage("Textures/MapBlock.png"); multiMapBlockImage = (GLImage *)renderer->RegisterImage("Textures/MultiMapBlock.png"); previous_cg_textures = cg_textures; previous_cg_multiTextures = cg_multiTextures; - // END OF ADDED static const uint8_t squareVertices[] = {0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1}; squareVertexBuffer = device->GenBuffer(); @@ -150,22 +144,20 @@ namespace spades { } } - // ADDED: UpdateTextureMode void GLMapRenderer::UpdateTextureMode() { - // determine if texture mode changed - if ((((bool)cg_textures) != previous_cg_textures) || - (((bool)cg_multiTextures) != previous_cg_multiTextures)) { + // Determine if texture mode changed + if ((static_cast(cg_textures) != previous_cg_textures) || + (static_cast(cg_multiTextures) != previous_cg_multiTextures)) { - // force the chunks to update (need to update their tex-coords) + // Force the chunks to update (need to update their tex-coords) for (int i = 0; i < numChunks; i++) { chunks[i]->SetNeedsUpdate(); } - // update previous things + // Update previous things previous_cg_textures = cg_textures; previous_cg_multiTextures = cg_multiTextures; } } - // END OF ADDED void GLMapRenderer::RealizeChunks(spades::Vector3 eye) { SPADES_MARK_FUNCTION(); @@ -235,13 +227,8 @@ namespace spades { GLProfiler::Context profiler(renderer->GetGLProfiler(), "Map"); - // ADDED: Determine if texturing this frame, set the program to use - if (previous_cg_textures) { - basicProgram = basicTexturesProgram; - } else { - basicProgram = basicNoTexturesProgram; - } - // END OF ADDED + // Determine if texturing this frame, set the program to use accordingly. + basicProgram = previous_cg_textures ? basicTexturesProgram : basicNoTexturesProgram; Vector3 eye = renderer->GetSceneDef().viewOrigin; @@ -258,7 +245,6 @@ namespace spades { device->ActiveTexture(1); device->BindTexture(IGLDevice::Texture2D, 0); - // ADDED: Bind the map block texture to GL_TEXTURE_8 static GLProgramUniform blockTextureStrength("blockTextureStrength"); if (previous_cg_textures) { @@ -275,19 +261,16 @@ namespace spades { device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Nearest); } - // END OF ADDED device->Enable(IGLDevice::CullFace, true); device->Enable(IGLDevice::DepthTest, true); basicProgram->Use(); - // ADDED: Set the block texture strength if (previous_cg_textures) { blockTextureStrength(basicProgram); blockTextureStrength.SetValue(((float)cg_textureStrength) / 100.0f); } - // END OF ADDED static GLShadowShader shadowShader; shadowShader(renderer, basicProgram, 2); @@ -315,13 +298,11 @@ namespace spades { detailTextureUnif(basicProgram); detailTextureUnif.SetValue(1); - // ADDED: Bind the GL_TEXTURE_8 to the map block texture in the shader if (previous_cg_textures) { static GLProgramUniform blockTexture("blockTexture"); blockTexture(basicProgram); blockTexture.SetValue(8); } - // END OF ADDED device->BindBuffer(IGLDevice::ArrayBuffer, 0); @@ -332,13 +313,11 @@ namespace spades { static GLProgramAttribute normalAttribute("normalAttribute"); static GLProgramAttribute fixedPositionAttribute("fixedPositionAttribute"); - // ADDED: Setup the texture coordinate attribute static GLProgramAttribute blockTexCoordAttribute("blockTexCoordAttribute"); if (previous_cg_textures) { blockTexCoordAttribute(basicProgram); device->EnableVertexAttribArray(blockTexCoordAttribute(), true); } - // END OF ADDED positionAttribute(basicProgram); ambientOcclusionCoordAttribute(basicProgram); @@ -394,7 +373,6 @@ namespace spades { device->EnableVertexAttribArray(normalAttribute(), false); device->EnableVertexAttribArray(fixedPositionAttribute(), false); - // ADDED: Clean up texture stuff if (previous_cg_textures) { device->EnableVertexAttribArray(blockTexCoordAttribute(), false); @@ -404,7 +382,6 @@ namespace spades { device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Linear); } - // END OF ADDED device->ActiveTexture(1); device->BindTexture(IGLDevice::Texture2D, 0); @@ -511,7 +488,6 @@ namespace spades { GetChunk(cx, cy, z)->RenderSunlightPass(); } - // ADDED: RenderOutlinePass definition void GLMapRenderer::RenderOutlinesPass(Vector3 outlineColor) { SPADES_MARK_FUNCTION(); @@ -553,9 +529,9 @@ namespace spades { RealizeChunks(eye); // draw from nearest to farthest - int cx = (int)floorf(eye.x) / GLMapChunk::Size; - int cy = (int)floorf(eye.y) / GLMapChunk::Size; - int cz = (int)floorf(eye.z) / GLMapChunk::Size; + const auto cx = static_cast(floorf(eye.x)) / GLMapChunk::Size; + const auto cy = static_cast(floorf(eye.y)) / GLMapChunk::Size; + const auto cz = static_cast(floorf(eye.z)) / GLMapChunk::Size; DrawColumnOutlines(cx, cy, cz, eye); for (int dist = 1; dist <= 128 / GLMapChunk::Size; dist++) { for (int x = cx - dist; x <= cx + dist; x++) { @@ -570,7 +546,6 @@ namespace spades { device->EnableVertexAttribArray(positionAttribute(), false); } - // END OF ADDED void GLMapRenderer::DrawColumnDLight(int cx, int cy, int cz, spades::Vector3 eye, const std::vector &lights) { @@ -582,8 +557,7 @@ namespace spades { GetChunk(cx, cy, z)->RenderDLightPass(lights); } - // ADDED: DrawColumnOutlines definition - void GLMapRenderer::DrawColumnOutlines(int cx, int cy, int cz, spades::Vector3 eye) { + void GLMapRenderer::DrawColumnOutlines(int cx, int cy, int cz, Vector3 eye) { cx &= numChunkWidth - 1; cy &= numChunkHeight - 1; for (int z = std::max(cz, 0); z < numChunkDepth; z++) @@ -591,7 +565,6 @@ namespace spades { for (int z = std::min(cz - 1, 63); z >= 0; z--) GetChunk(cx, cy, z)->RenderOutlinesPass(); } - // END OF ADDED #pragma mark - BackFaceBlock diff --git a/Sources/Draw/GLMapRenderer.h b/Sources/Draw/GLMapRenderer.h index 15e61d490..8f2adc473 100644 --- a/Sources/Draw/GLMapRenderer.h +++ b/Sources/Draw/GLMapRenderer.h @@ -46,16 +46,13 @@ namespace spades { GLProgram *backfaceProgram; GLImage *aoImage; - // ADDED: Variables for textures & outlines GLProgram *basicNoTexturesProgram; // The basic block shader with no textures GLProgram *basicTexturesProgram; // The basic block shader with textures GLProgram *basicOutlinesProgram; // The basic block shader for outlines GLImage *mapBlockImage; // GLImage handle for the map block texture GLImage *multiMapBlockImage; // GLImage handle for the multi map block texture - bool previous_cg_textures; // the cg_textures value of most recent prerender call - bool - previous_cg_multiTextures; // the cg_multiTextures value of most recent prerender call - // END OF ADDED + bool previous_cg_textures; // the cg_textures value of most recent prerender call + bool previous_cg_multiTextures; // the cg_multiTextures value of most recent prerender call IGLDevice::UInteger squareVertexBuffer; @@ -86,9 +83,7 @@ namespace spades { void DrawColumnDLight(int cx, int cy, int cz, Vector3 eye, const std::vector &lights); - // ADDED: DrawColumnOutlines declaration void DrawColumnOutlines(int cx, int cy, int cz, Vector3 eye); - // END OF ADDED void RenderBackface(); @@ -102,18 +97,14 @@ namespace spades { client::GameMap *GetMap() { return gameMap; } - // ADDED: Update texture mode void UpdateTextureMode(); - // END OF ADDED void Realize(); void Prerender(); void RenderSunlightPass(); void RenderDynamicLightPass(std::vector lights); - // ADDED: RenderOutlinesPass declaration void RenderOutlinesPass(Vector3 outlineColor); - // END OF ADDED }; } } diff --git a/Sources/Draw/GLModel.h b/Sources/Draw/GLModel.h index e199cc31c..9b78bd703 100644 --- a/Sources/Draw/GLModel.h +++ b/Sources/Draw/GLModel.h @@ -46,21 +46,19 @@ namespace spades { /** Renders sunlighted solid geometry */ virtual void RenderSunlightPass(std::vector params, bool ghostPass, - bool farRender) = 0; // MODIFIED: Added farRender + bool farRender) = 0; /** Adds dynamic light */ virtual void RenderDynamicLightPass(std::vector params, std::vector lights, - bool farRender) = 0; // MODIFIED: Added farRender + bool farRender) = 0; - // ADDED: RenderOutlinesPass, RenderOccludedPass, RenderOcclusionTestPass declaration virtual void RenderOutlinesPass(std::vector params, Vector3 outlineColor, bool fog, bool farRender) = 0; virtual void RenderOccludedPass(std::vector params, bool farRender) = 0; virtual void RenderOcclusionTestPass(std::vector params, bool farRender) = 0; - // END OF ADDED private: // members used when rendering by GLModelRenderer diff --git a/Sources/Draw/GLModelRenderer.cpp b/Sources/Draw/GLModelRenderer.cpp index d8367f49a..7f224e464 100644 --- a/Sources/Draw/GLModelRenderer.cpp +++ b/Sources/Draw/GLModelRenderer.cpp @@ -29,14 +29,13 @@ namespace spades { GLModelRenderer::GLModelRenderer(GLRenderer *r) : renderer(r), device(r->GetGLDevice()) { SPADES_MARK_FUNCTION(); - // ADDED: Create the queries, default them with empty result - for (int i = 0; i < 32; ++i) { - playerVisibilityQueries[i] = device->GenQuery(); - device->BeginQuery(IGLDevice::SamplesPassed, playerVisibilityQueries[i]); + // Create the queries, default them with empty result. + for (auto &playerVisibilityQuerie : playerVisibilityQueries) { + playerVisibilityQuerie = device->GenQuery(); + device->BeginQuery(IGLDevice::SamplesPassed, playerVisibilityQuerie); device->EndQuery(IGLDevice::SamplesPassed); } device->Flush(); - // END OF ADDED modelCount = 0; } @@ -44,11 +43,10 @@ namespace spades { GLModelRenderer::~GLModelRenderer() { SPADES_MARK_FUNCTION(); - // ADDED: Free occlusion query objects - for (int i = 0; i < 32; ++i) { - device->DeleteQuery(playerVisibilityQueries[i]); + // Free occlusion query objects. + for (auto playerVisibilityQuerie : playerVisibilityQueries) { + device->DeleteQuery(playerVisibilityQuerie); } - // END OF ADDED Clear(); } @@ -114,8 +112,7 @@ namespace spades { RenderModel &m = models[i]; GLModel *model = m.model; - model->RenderSunlightPass(m.params, ghostPass, - false); // MODIFIED: don't do far render + model->RenderSunlightPass(m.params, ghostPass, false); } } @@ -132,13 +129,11 @@ namespace spades { RenderModel &m = models[i]; GLModel *model = m.model; - model->RenderDynamicLightPass(m.params, lights, - false); // MODIFIED: don't do far render + model->RenderDynamicLightPass(m.params, lights, false); } } } - // ADDED: Additional definitions void GLModelRenderer::RenderOutlinesPass() { SPADES_MARK_FUNCTION(); @@ -146,9 +141,8 @@ namespace spades { "Model [%d model(s), %d unique model type(s)]", modelCount, (int)models.size()); - for (size_t i = 0; i < models.size(); i++) { - RenderModel &m = models[i]; - GLModel *model = m.model; + for (auto &m : models) { + auto *model = m.model; model->RenderOutlinesPass(m.params, Vector3(0.0f, 0.0f, 0.0f), true, false); } @@ -158,8 +152,8 @@ namespace spades { SPADES_MARK_FUNCTION(); // determine player visbility via the last frame for (int i = 0; i < 32; ++i) { - int iSamplesPassed = device->GetQueryObjectUInteger(playerVisibilityQueries[i], - IGLDevice::QueryResult); + const int iSamplesPassed = device->GetQueryObjectUInteger(playerVisibilityQueries[i], + IGLDevice::QueryResult); visiblePlayers[i] = (iSamplesPassed > 0); } // set up the occlusion query @@ -168,9 +162,9 @@ namespace spades { // iterate every player and get the new occlusion query going for (int i = 0; i < 32; ++i) { device->BeginQuery(IGLDevice::SamplesPassed, playerVisibilityQueries[i]); - for (RenderModel &m : models) { + for (auto &m : models) { std::vector playerParams; - for (client::ModelRenderParam p : m.params) { + for (auto p : m.params) { if (p.playerID == i) { playerParams.push_back(p); } @@ -191,9 +185,9 @@ namespace spades { "Model [%d model(s), %d unique model type(s)]", modelCount, (int)models.size()); - for (RenderModel &m : models) { + for (auto &m : models) { std::vector params; - for (client::ModelRenderParam p : m.params) { + for (auto p : m.params) { if (p.playerID == -1) { params.push_back(p); } @@ -209,9 +203,9 @@ namespace spades { "Model [%d model(s), %d unique model type(s)]", modelCount, (int)models.size()); - for (RenderModel &m : models) { + for (auto &m : models) { std::vector params; - for (client::ModelRenderParam p : m.params) { + for (auto p : m.params) { if (p.playerID == -1) { params.push_back(p); } @@ -226,9 +220,9 @@ namespace spades { "Model [%d model(s), %d unique model type(s)]", modelCount, (int)models.size()); - for (RenderModel &m : models) { + for (auto &m : models) { std::vector params; - for (client::ModelRenderParam p : m.params) { + for (auto p : m.params) { if (p.playerID != -1 && visiblePlayers[p.playerID]) { params.push_back(p); } @@ -245,9 +239,9 @@ namespace spades { "Model [%d model(s), %d unique model type(s)]", modelCount, (int)models.size()); - for (RenderModel &m : models) { + for (auto &m : models) { std::vector params; - for (client::ModelRenderParam p : m.params) { + for (auto p : m.params) { if (p.playerID != -1 && visiblePlayers[p.playerID]) { params.push_back(p); } @@ -262,9 +256,9 @@ namespace spades { "Model [%d model(s), %d unique model type(s)]", modelCount, (int)models.size()); - for (RenderModel &m : models) { + for (auto &m : models) { std::vector params; - for (client::ModelRenderParam p : m.params) { + for (auto p : m.params) { if (p.playerID != -1 && !visiblePlayers[p.playerID]) { params.push_back(p); } @@ -280,12 +274,12 @@ namespace spades { "Model [%d model(s), %d unique model type(s)]", modelCount, (int)models.size()); - for (RenderModel &m : models) { + for (auto &m : models) { std::vector visibleTeam0; std::vector nonVisibleTeam0; std::vector visibleTeam1; std::vector nonVisibleTeam1; - for (client::ModelRenderParam p : m.params) { + for (auto p : m.params) { if (p.playerID != -1) { if (visiblePlayers[p.playerID]) { if (p.teamId == 0) { @@ -303,13 +297,13 @@ namespace spades { } } - Vector3 team0Col = client::Client::TeamCol(0); - Vector3 team1Col = client::Client::TeamCol(1); + auto team0Col = client::Client::TeamCol(0); + auto team1Col = client::Client::TeamCol(1); - Vector3 nv0 = team0Col * 0.63; - Vector3 nv1 = team1Col * 0.63; - Vector3 v0 = team0Col; - Vector3 v1 = team1Col; + const auto nv0 = team0Col * 0.63; + const auto nv1 = team1Col * 0.63; + const auto v0 = team0Col; + const auto v1 = team1Col; m.model->RenderOutlinesPass(nonVisibleTeam0, nv0, false, true); m.model->RenderOutlinesPass(visibleTeam0, v0, false, true); @@ -317,7 +311,6 @@ namespace spades { m.model->RenderOutlinesPass(visibleTeam1, v1, false, true); } } - // END OF ADDED void GLModelRenderer::Clear() { // last phase: clear scene diff --git a/Sources/Draw/GLModelRenderer.h b/Sources/Draw/GLModelRenderer.h index 0c4664f80..e9e790d57 100644 --- a/Sources/Draw/GLModelRenderer.h +++ b/Sources/Draw/GLModelRenderer.h @@ -46,9 +46,7 @@ namespace spades { std::vector models; int modelCount; - // ADDED: player visibility occlusion query object unsigned int playerVisibilityQueries[32]; - // END OF ADDED public: GLModelRenderer(GLRenderer *); @@ -62,7 +60,6 @@ namespace spades { void RenderSunlightPass(bool ghostPass); void RenderDynamicLightPass(std::vector lights); - // ADDED: Additional declarations void RenderOutlinesPass(); void DetermineVisiblePlayers(bool visiblePlayers[]); void RenderSunlightPassNoPlayers(); @@ -72,7 +69,6 @@ namespace spades { std::vector lights); void RenderOccludedPlayers(bool visiblePlayers[]); void RenderPlayerVisibilityOutlines(bool visiblePlayers[]); - // END OF ADDED void Clear(); }; diff --git a/Sources/Draw/GLOptimizedVoxelModel.cpp b/Sources/Draw/GLOptimizedVoxelModel.cpp index 14182a5bf..f74f41f28 100644 --- a/Sources/Draw/GLOptimizedVoxelModel.cpp +++ b/Sources/Draw/GLOptimizedVoxelModel.cpp @@ -60,14 +60,12 @@ namespace spades { renderer->RegisterProgram("Shaders/OptimizedVoxelModelShadowMap.program"); aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.png"); - // ADDED: Load the outlines/occluded program optimizedVoxelModelOutlinesProgram = renderer->RegisterProgram("Shaders/OptimizedVoxelModelOutlines.program"); optimizedVoxelModelOccludedProgram = renderer->RegisterProgram("Shaders/OptimizedVoxelModelOccluded.program"); optimizedVoxelModelOcclusionTestProgram = renderer->RegisterProgram("Shaders/OptimizedVoxelModelOcclusionTest.program"); - // END OF ADDED buffer = device->GenBuffer(); device->BindBuffer(IGLDevice::ArrayBuffer, buffer); @@ -537,7 +535,7 @@ namespace spades { bool ghostPass) { SPADES_MARK_FUNCTION(); - RenderSunlightPass(params, ghostPass, false); // MODIFIED: farRender + RenderSunlightPass(params, ghostPass, false); } void @@ -623,7 +621,6 @@ namespace spades { device->BindTexture(IGLDevice::Texture2D, 0); } - // MODIFIED: farRender functionality void GLOptimizedVoxelModel::RenderSunlightPass(std::vector params, bool ghostPass, bool farRender) { SPADES_MARK_FUNCTION(); @@ -926,15 +923,13 @@ namespace spades { device->ActiveTexture(0); } - // END OF MODIFIED - // ADDED: RenderOutlinesPass definition void GLOptimizedVoxelModel::RenderOutlinesPass(std::vector params, Vector3 outlineColor, bool fog, bool farRender) { SPADES_MARK_FUNCTION(); - bool mirror = renderer->IsRenderingMirror(); + const auto mirror = renderer->IsRenderingMirror(); device->ActiveTexture(0); aoImage->Bind(IGLDevice::Texture2D); @@ -955,7 +950,7 @@ namespace spades { static GLProgramUniform fogColor("fogColor"); fogColor(optimizedVoxelModelOutlinesProgram); - Vector3 fogCol = renderer->GetFogColorForSolidPass(); + auto fogCol = renderer->GetFogColorForSolidPass(); if (!fog) { fogCol = outlineColor; } @@ -981,28 +976,26 @@ namespace spades { device->BindBuffer(IGLDevice::ArrayBuffer, buffer); device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, - sizeof(Vertex), (void *)0); + sizeof(Vertex), nullptr); device->BindBuffer(IGLDevice::ArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), true); device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); - for (size_t i = 0; i < params.size(); i++) { - const client::ModelRenderParam ¶m = params[i]; - + for (auto ¶m : params) { if (mirror && param.depthHack) continue; // frustrum cull if (!farRender) { - float rad = radius; + auto rad = radius; rad *= param.matrix.GetAxis(0).GetLength(); if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { continue; } } - Matrix4 modelMatrix = param.matrix; + auto modelMatrix = param.matrix; static GLProgramUniform modelMatrixU("modelMatrix"); modelMatrixU(optimizedVoxelModelOutlinesProgram); @@ -1010,8 +1003,9 @@ namespace spades { static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(optimizedVoxelModelOutlinesProgram); - const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix - : renderer->GetProjectionViewMatrix()); + const auto &pvMat = farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix(); projectionViewModelMatrix.SetValue(pvMat * modelMatrix); static GLProgramUniform viewModelMatrix("viewModelMatrix"); @@ -1023,7 +1017,7 @@ namespace spades { } device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, - (void *)0); + nullptr); if (param.depthHack) { device->DepthRange(0.f, 1.f); @@ -1033,14 +1027,12 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), false); } - // END OF ADDED - // ADDED: RenderOccludedPass definition void GLOptimizedVoxelModel::RenderOccludedPass(std::vector params, bool farRender) { SPADES_MARK_FUNCTION(); - bool mirror = renderer->IsRenderingMirror(); + const auto mirror = renderer->IsRenderingMirror(); device->ActiveTexture(0); aoImage->Bind(IGLDevice::Texture2D); @@ -1072,32 +1064,31 @@ namespace spades { device->BindBuffer(IGLDevice::ArrayBuffer, buffer); device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, - sizeof(Vertex), (void *)0); + sizeof(Vertex), nullptr); device->BindBuffer(IGLDevice::ArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), true); device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); - for (size_t i = 0; i < params.size(); i++) { - const client::ModelRenderParam ¶m = params[i]; - + for (auto ¶m : params) { if (mirror && param.depthHack) continue; // frustrum cull if (!farRender) { - float rad = radius; + auto rad = radius; rad *= param.matrix.GetAxis(0).GetLength(); if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { continue; } } - Matrix4 modelMatrix = param.matrix; + auto modelMatrix = param.matrix; static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(optimizedVoxelModelOccludedProgram); - const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix - : renderer->GetProjectionViewMatrix()); + const auto &pvMat = farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix(); projectionViewModelMatrix.SetValue(pvMat * modelMatrix); if (param.depthHack) { @@ -1105,7 +1096,7 @@ namespace spades { } device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, - (void *)0); + nullptr); if (param.depthHack) { device->DepthRange(0.f, 1.f); } @@ -1114,15 +1105,13 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), false); } - // END OF ADDED - // ADDED: RenderOcclusionTestPass definition void GLOptimizedVoxelModel::RenderOcclusionTestPass(std::vector params, bool farRender) { SPADES_MARK_FUNCTION(); - bool mirror = renderer->IsRenderingMirror(); + const auto mirror = renderer->IsRenderingMirror(); device->ActiveTexture(0); aoImage->Bind(IGLDevice::Texture2D); @@ -1159,28 +1148,26 @@ namespace spades { device->BindBuffer(IGLDevice::ArrayBuffer, buffer); device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, - sizeof(Vertex), (void *)0); + sizeof(Vertex), nullptr); device->BindBuffer(IGLDevice::ArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), true); device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); - for (size_t i = 0; i < params.size(); i++) { - const client::ModelRenderParam ¶m = params[i]; - + for (auto ¶m : params) { if (mirror && param.depthHack) continue; // frustrum cull if (!farRender) { - float rad = radius; + auto rad = radius; rad *= param.matrix.GetAxis(0).GetLength(); if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { continue; } } - Matrix4 modelMatrix = param.matrix; + auto modelMatrix = param.matrix; static GLProgramUniform modelMatrixU("modelMatrix"); modelMatrixU(optimizedVoxelModelOcclusionTestProgram); @@ -1188,8 +1175,9 @@ namespace spades { static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(optimizedVoxelModelOcclusionTestProgram); - const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix - : renderer->GetProjectionViewMatrix()); + const auto &pvMat = farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix(); projectionViewModelMatrix.SetValue(pvMat * modelMatrix); if (param.depthHack) { @@ -1197,7 +1185,7 @@ namespace spades { } device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, - (void *)0); + nullptr); if (param.depthHack) { device->DepthRange(0.f, 1.f); } @@ -1206,6 +1194,5 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), false); } - // END OF ADDED } } diff --git a/Sources/Draw/GLOptimizedVoxelModel.h b/Sources/Draw/GLOptimizedVoxelModel.h index e3aaf3bf3..116810661 100644 --- a/Sources/Draw/GLOptimizedVoxelModel.h +++ b/Sources/Draw/GLOptimizedVoxelModel.h @@ -54,11 +54,9 @@ namespace spades { GLImage *image; GLImage *aoImage; - // ADDED: Outlines & occluded program GLProgram *optimizedVoxelModelOutlinesProgram; GLProgram *optimizedVoxelModelOccludedProgram; GLProgram *optimizedVoxelModelOcclusionTestProgram; - // END OF ADDED IGLDevice::UInteger buffer; IGLDevice::UInteger idxBuffer; @@ -97,22 +95,20 @@ namespace spades { void RenderShadowMapPass(std::vector params) override; void RenderSunlightPass(std::vector params, - bool ghostPass, bool farRender) override; // MODIFIED: farRender + bool ghostPass, bool farRender) override; void RenderDynamicLightPass(std::vector params, std::vector lights, - bool farRender) override; // MODIFIED: farRender + bool farRender) override; AABB3 GetBoundingBox() override { return boundingBox; } - // ADDED: RenderOutlinesPass, RenderOccludedPass declaration virtual void RenderOutlinesPass(std::vector params, Vector3 outlineColor, bool fog, bool farRender); virtual void RenderOccludedPass(std::vector params, bool farRender); virtual void RenderOcclusionTestPass(std::vector params, bool farRender); - // END OF ADDED }; } } diff --git a/Sources/Draw/GLRenderer.cpp b/Sources/Draw/GLRenderer.cpp index 47138306a..047426ee4 100644 --- a/Sources/Draw/GLRenderer.cpp +++ b/Sources/Draw/GLRenderer.cpp @@ -67,15 +67,11 @@ #include #include -// ADDED: Additional headers #include "../Client/Client.h" #include "../Imports/OpenGL.h" -// END OF ADDED -// ADDED: Outlines setting, player observe setting SPADES_SETTING(cg_outlines); SPADES_SETTING(cg_outlineStrength); -// END OF ADDED namespace spades { namespace draw { @@ -361,7 +357,7 @@ namespace spades { Vector3 GLRenderer::GetFogColorForSolidPass() { if (settings.r_fogShadow && mapShadowRenderer && - !client::Client::WallhackActive()) { // MODIFIED: no volumetric fog in wallhack + !client::Client::WallhackActive()) { return MakeVector3(0, 0, 0); } else { return GetFogColor(); @@ -478,11 +474,9 @@ namespace spades { sceneUsedInThisFrame = true; duringSceneRendering = true; - // ADDED: Update texture mode if (mapRenderer) { mapRenderer->UpdateTextureMode(); } - // END OF ADDED profiler->BeginFrame(); @@ -501,14 +495,13 @@ namespace spades { projectionViewMatrix = projectionMatrix * viewMatrix; - // ADDED: Compute the far pv-matrix { - float near = sceneDef.zNear; - float far = 3000; // this seems to cover the whole map + const auto near = sceneDef.zNear; + const auto far = 3000.0f; // This seems to cover the whole map. - float t = near * tanf(sceneDef.fovY * .5f); - float r = near * tanf(sceneDef.fovX * .5f); - float a = r * 2.f, b = t * 2.f, c = far - near; + const auto t = near * tanf(sceneDef.fovY * .5f); + const auto r = near * tanf(sceneDef.fovX * .5f); + const auto a = r * 2.f, b = t * 2.f, c = far - near; Matrix4 mat; mat.m[0] = near * 2.f / a; mat.m[1] = 0.f; @@ -529,7 +522,6 @@ namespace spades { farProjectionViewMatrix = mat * viewMatrix; } - // END OF ADDED if (settings.r_srgb) device->Enable(IGLDevice::FramebufferSRGB, true); @@ -727,7 +719,6 @@ namespace spades { modelRenderer->RenderDynamicLightPass(lights); } - // ADDED: Do the outlines pass if (cg_outlines && !reflections) { GLProfiler::Context p(*profiler, "Outlines Pass"); @@ -753,7 +744,6 @@ namespace spades { glPolygonOffset(0, 0); glDisable(GL_POLYGON_OFFSET_LINE); } - // END OF ADDED { GLProfiler::Context p(*profiler, "Debug Line"); @@ -810,7 +800,7 @@ namespace spades { { GLProfiler::Context p(*profiler, "Upload Dynamic Data"); if (mapShadowRenderer && - !client::Client::WallhackActive()) { // MODIFIED: not with wallhack + !client::Client::WallhackActive()) { mapShadowRenderer->Update(); } if (ambientShadowRenderer) { @@ -833,7 +823,7 @@ namespace spades { device->Enable(IGLDevice::DepthTest, true); device->DepthFunc(IGLDevice::Less); if (shadowMapRenderer && - !client::Client::WallhackActive()) // MODIFIED: not with wallhack + !client::Client::WallhackActive()) shadowMapRenderer->Render(); } @@ -848,7 +838,7 @@ namespace spades { device->DepthRange(0.f, 1.f); if ((int)settings.r_water >= 2 && - !client::Client::WallhackActive()) { // MODIFIED: not with wallhack + !client::Client::WallhackActive()) { // for Water 2 (r_water >= 2), we need to render // reflection try { @@ -886,7 +876,7 @@ namespace spades { // render scene GLProfiler::Context p(*profiler, "Mirrored Objects"); - RenderObjects(true); // MODIFIED: Reflection param + RenderObjects(true); // restore matrices std::swap(view, viewMatrix); @@ -930,20 +920,12 @@ namespace spades { device->FrontFace(IGLDevice::CW); - // ADDED: May be needed to determine and keep track of visible players bool visiblePlayers[32]; - // END OF ADDED - // MODIFIED: Do the normal pass if not in wallhack if (!client::Client::WallhackActive()) { GLProfiler::Context p(*profiler, "Non-mirrored Objects"); - RenderObjects(false); // also, not a reflection pass - } - // otherwise, render the map, determine visible players, render all non player objects, - // debug lines - else { - - // do ssao stuff + RenderObjects(false); + } else { device->Enable(IGLDevice::DepthTest, true); device->Enable(IGLDevice::Texture2D, true); device->Enable(IGLDevice::Blend, false); @@ -1039,11 +1021,10 @@ namespace spades { device->DepthFunc(IGLDevice::Less); RenderDebugLines(); } - // END OF MODIFIED device->Enable(IGLDevice::CullFace, false); if (settings.r_water && waterRenderer && - !client::Client::WallhackActive()) { // MODIFIED: not with wallhack + !client::Client::WallhackActive()) { GLProfiler::Context p(*profiler, "Water"); waterRenderer->Update(dt); waterRenderer->Render(); @@ -1071,16 +1052,12 @@ namespace spades { longSpriteRenderer->Render(); } - // ADDED: Render all players now, above everything else (if wallhack) if (client::Client::WallhackActive()) { - // now clear the depth buffer, enable depth mask & culling device->DepthMask(true); device->Clear(IGLDevice::DepthBufferBit); device->Enable(IGLDevice::CullFace, true); - // render the non occluded players normally { - // do the sunlight pass device->Enable(IGLDevice::DepthTest, true); device->DepthFunc(IGLDevice::Less); device->Enable(IGLDevice::Texture2D, true); @@ -1088,7 +1065,6 @@ namespace spades { modelRenderer->RenderSunlightPassVisiblePlayers(visiblePlayers); - // the dynamic light pass device->Enable(IGLDevice::Blend, true); device->Enable(IGLDevice::DepthTest, true); device->DepthFunc(IGLDevice::Equal); @@ -1097,14 +1073,12 @@ namespace spades { modelRenderer->RenderDynamicLightPassVisiblePlayers(visiblePlayers, lights); } - // render the occluded players device->Enable(IGLDevice::DepthTest, true); device->DepthFunc(IGLDevice::Less); device->Enable(IGLDevice::Texture2D, true); device->Enable(IGLDevice::Blend, false); modelRenderer->RenderOccludedPlayers(visiblePlayers); - // render the outlines { GLProfiler::Context p(*profiler, "Outlines Pass"); @@ -1128,13 +1102,11 @@ namespace spades { glDisable(GL_POLYGON_OFFSET_LINE); } - // disable settings device->ColorMask(true, true, true, true); device->Enable(IGLDevice::Blend, true); device->DepthMask(false); device->Enable(IGLDevice::CullFace, false); } - // END OF ADDED device->Enable(IGLDevice::DepthTest, false); @@ -1148,7 +1120,7 @@ namespace spades { GLProfiler::Context p(*profiler, "Preparation"); handle = fbManager->StartPostProcessing(); } - if (!client::Client::WallhackActive() && // MODIFIED: not with wallhack + if (!client::Client::WallhackActive() && settings.r_fogShadow && mapShadowRenderer && fogColor.GetPoweredLength() > .000001f) { GLProfiler::Context p(*profiler, "Volumetric Fog"); @@ -1157,8 +1129,6 @@ namespace spades { } device->BindFramebuffer(IGLDevice::Framebuffer, handle.GetFramebuffer()); - // MODIFIED: Don't do this in wallhack, (nothing changed, just added the if - // statement below) if (!client::Client::WallhackActive()) { if (settings.r_softParticles) { // softparticle is a part of postprocess GLProfiler::Context p(*profiler, "Soft Particle"); diff --git a/Sources/Draw/GLRenderer.h b/Sources/Draw/GLRenderer.h index a48a9d569..199715a91 100644 --- a/Sources/Draw/GLRenderer.h +++ b/Sources/Draw/GLRenderer.h @@ -26,15 +26,12 @@ #include "GLCameraBlurFilter.h" #include "GLDynamicLight.h" #include "GLSettings.h" +#include #include #include #include -#include - -// ADDED: include client, world stuff -#include #include -// END OF ADDED +#include namespace spades { namespace draw { @@ -139,7 +136,7 @@ namespace spades { void RenderDebugLines(); - void RenderObjects(bool reflections); // MODIFIED: Add reflections parameter + void RenderObjects(bool reflections); void RenderGhosts(); void EnsureInitialized(); @@ -233,9 +230,7 @@ namespace spades { bool BoxFrustrumCull(const AABB3 &); bool SphereFrustrumCull(const Vector3 ¢er, float radius); - // ADDED: Far pv-matrix Matrix4 farProjectionViewMatrix; - // END OF ADDED }; } } diff --git a/Sources/Draw/GLVoxelModel.cpp b/Sources/Draw/GLVoxelModel.cpp index 2089358c9..e59f7dddd 100644 --- a/Sources/Draw/GLVoxelModel.cpp +++ b/Sources/Draw/GLVoxelModel.cpp @@ -49,14 +49,12 @@ namespace spades { shadowMapProgram = renderer->RegisterProgram("Shaders/VoxelModelShadowMap.program"); aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.png"); - // ADDED: Load the outlines program voxelModelOutlinesProgram = renderer->RegisterProgram("Shaders/VoxelModelOutlines.program"); voxelModelOccludedProgram = renderer->RegisterProgram("Shaders/VoxelModelOccluded.program"); voxelModelOcclusionTestProgram = renderer->RegisterProgram("Shaders/VoxelModelOcclusionTest.program"); - // END OF ADDED BuildVertices(m); @@ -269,7 +267,7 @@ namespace spades { void GLVoxelModel::Prerender(std::vector params, bool ghostPass) { SPADES_MARK_FUNCTION(); - RenderSunlightPass(params, ghostPass, false); // MODIFIED: farRender false + RenderSunlightPass(params, ghostPass, false); } void GLVoxelModel::RenderShadowMapPass(std::vector params) { @@ -354,7 +352,6 @@ namespace spades { device->BindTexture(IGLDevice::Texture2D, 0); } - // MODIFIED: farRender functionality void GLVoxelModel::RenderSunlightPass(std::vector params, bool ghostPass, bool farRender) { SPADES_MARK_FUNCTION(); @@ -623,9 +620,7 @@ namespace spades { device->ActiveTexture(0); } - // END OF MODIFIED - // ADDED: RenderOutlinesPass definition void GLVoxelModel::RenderOutlinesPass(std::vector params, Vector3 outlineColor, bool fog, bool farRender) { SPADES_MARK_FUNCTION(); @@ -639,7 +634,7 @@ namespace spades { static GLProgramUniform fogColor("fogColor"); fogColor(voxelModelOutlinesProgram); - Vector3 fogCol = renderer->GetFogColorForSolidPass(); + auto fogCol = renderer->GetFogColorForSolidPass(); if (!fog) { fogCol = outlineColor; } @@ -671,19 +666,17 @@ namespace spades { device->EnableVertexAttribArray(positionAttribute(), true); device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); - for (size_t i = 0; i < params.size(); i++) { - const client::ModelRenderParam ¶m = params[i]; - + for (auto ¶m : params) { // frustrum cull if (!farRender) { - float rad = radius; + auto rad = radius; rad *= param.matrix.GetAxis(0).GetLength(); if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { continue; } } - Matrix4 modelMatrix = param.matrix; + auto modelMatrix = param.matrix; static GLProgramUniform modelMatrixU("modelMatrix"); modelMatrixU(voxelModelOutlinesProgram); @@ -691,8 +684,9 @@ namespace spades { static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(voxelModelOutlinesProgram); - const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix - : renderer->GetProjectionViewMatrix()); + const auto &pvMat = farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix(); projectionViewModelMatrix.SetValue(pvMat * modelMatrix); static GLProgramUniform viewModelMatrix("viewModelMatrix"); @@ -704,7 +698,7 @@ namespace spades { } device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, - (void *)0); + nullptr); if (param.depthHack) { device->DepthRange(0.f, 1.f); @@ -714,9 +708,7 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), false); } - // END OF ADDED - // ADDED: RenderOccludedPass definition void GLVoxelModel::RenderOccludedPass(std::vector params, bool farRender) { SPADES_MARK_FUNCTION(); @@ -742,23 +734,22 @@ namespace spades { device->EnableVertexAttribArray(positionAttribute(), true); device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); - for (size_t i = 0; i < params.size(); i++) { - const client::ModelRenderParam ¶m = params[i]; - + for (auto ¶m : params) { // frustrum cull if (!farRender) { - float rad = radius; + auto rad = radius; rad *= param.matrix.GetAxis(0).GetLength(); if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { continue; } } - Matrix4 modelMatrix = param.matrix; + auto modelMatrix = param.matrix; static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(voxelModelOccludedProgram); - const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix - : renderer->GetProjectionViewMatrix()); + const auto &pvMat = farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix(); projectionViewModelMatrix.SetValue(pvMat * modelMatrix); if (param.depthHack) { @@ -766,7 +757,7 @@ namespace spades { } device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, - (void *)0); + nullptr); if (param.depthHack) { device->DepthRange(0.f, 1.f); } @@ -775,9 +766,7 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), false); } - // END OF ADDED - // ADDED: RenderOcclusionTestPass definition void GLVoxelModel::RenderOcclusionTestPass(std::vector params, bool farRender) { SPADES_MARK_FUNCTION(); @@ -802,25 +791,23 @@ namespace spades { device->BindBuffer(IGLDevice::ArrayBuffer, buffer); device->VertexAttribPointer(positionAttribute(), 4, IGLDevice::UnsignedByte, false, - sizeof(Vertex), (void *)0); + sizeof(Vertex), nullptr); device->BindBuffer(IGLDevice::ArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), true); device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer); - for (size_t i = 0; i < params.size(); i++) { - const client::ModelRenderParam ¶m = params[i]; - + for (auto ¶m : params) { // frustrum cull if (!farRender) { - float rad = radius; + auto rad = radius; rad *= param.matrix.GetAxis(0).GetLength(); if (!renderer->SphereFrustrumCull(param.matrix.GetOrigin(), rad)) { continue; } } - Matrix4 modelMatrix = param.matrix; + auto modelMatrix = param.matrix; static GLProgramUniform modelMatrixU("modelMatrix"); modelMatrixU(voxelModelOcclusionTestProgram); @@ -828,8 +815,9 @@ namespace spades { static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix"); projectionViewModelMatrix(voxelModelOcclusionTestProgram); - const Matrix4 &pvMat = (farRender ? renderer->farProjectionViewMatrix - : renderer->GetProjectionViewMatrix()); + const auto &pvMat = farRender + ? renderer->farProjectionViewMatrix + : renderer->GetProjectionViewMatrix(); projectionViewModelMatrix.SetValue(pvMat * modelMatrix); if (param.depthHack) { @@ -837,7 +825,7 @@ namespace spades { } device->DrawElements(IGLDevice::Triangles, numIndices, IGLDevice::UnsignedInt, - (void *)0); + nullptr); if (param.depthHack) { device->DepthRange(0.f, 1.f); } @@ -846,6 +834,5 @@ namespace spades { device->BindBuffer(IGLDevice::ElementArrayBuffer, 0); device->EnableVertexAttribArray(positionAttribute(), false); } - // END OF ADDED } } diff --git a/Sources/Draw/GLVoxelModel.h b/Sources/Draw/GLVoxelModel.h index b1843956c..b48a2988c 100644 --- a/Sources/Draw/GLVoxelModel.h +++ b/Sources/Draw/GLVoxelModel.h @@ -54,11 +54,9 @@ namespace spades { GLProgram *shadowMapProgram; GLImage *aoImage; - // ADDED: Outlines, occluded program GLProgram *voxelModelOutlinesProgram; GLProgram *voxelModelOccludedProgram; GLProgram *voxelModelOcclusionTestProgram; - // END OF ADDED IGLDevice::UInteger buffer; IGLDevice::UInteger idxBuffer; @@ -91,20 +89,18 @@ namespace spades { void RenderSunlightPass(std::vector params, bool ghostPass, - bool farRender) override; // MODIFIED: farRender + bool farRender) override; void RenderDynamicLightPass(std::vector params, std::vector lights, - bool farRender) override; // MODIFIED: farRender + bool farRender) override; - // ADDED: Other things virtual void RenderOutlinesPass(std::vector params, Vector3 outlineColor, bool fog, bool farRender); virtual void RenderOccludedPass(std::vector params, bool farRender); virtual void RenderOcclusionTestPass(std::vector params, bool farRender); - // END OF ADDED AABB3 GetBoundingBox() override { return boundingBox; } }; From 8689e83c40c25f695cca67778a145229c8001506 Mon Sep 17 00:00:00 2001 From: Conticop <58798963+Conticop@users.noreply.github.com> Date: Wed, 5 Aug 2020 12:45:55 +0200 Subject: [PATCH 4/6] Add a missing newline at the EOF. --- Resources/Shaders/BasicBlockOutlines.program | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Shaders/BasicBlockOutlines.program b/Resources/Shaders/BasicBlockOutlines.program index fc9e20bc7..96eaa6e81 100644 --- a/Resources/Shaders/BasicBlockOutlines.program +++ b/Resources/Shaders/BasicBlockOutlines.program @@ -1,4 +1,4 @@ Shaders/BasicBlockOutlines.fs Shaders/BasicBlockOutlines.vs *shadow* -Shaders/Fog.vs \ No newline at end of file +Shaders/Fog.vs From 85ea6b576aea99110cb41c87715097a8022a6310 Mon Sep 17 00:00:00 2001 From: Conticop <58798963+Conticop@users.noreply.github.com> Date: Wed, 5 Aug 2020 12:50:05 +0200 Subject: [PATCH 5/6] Remove weird indentation. --- Sources/Draw/GLOptimizedVoxelModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Draw/GLOptimizedVoxelModel.cpp b/Sources/Draw/GLOptimizedVoxelModel.cpp index f74f41f28..abc0ee346 100644 --- a/Sources/Draw/GLOptimizedVoxelModel.cpp +++ b/Sources/Draw/GLOptimizedVoxelModel.cpp @@ -620,7 +620,7 @@ namespace spades { device->ActiveTexture(0); device->BindTexture(IGLDevice::Texture2D, 0); } - + void GLOptimizedVoxelModel::RenderSunlightPass(std::vector params, bool ghostPass, bool farRender) { SPADES_MARK_FUNCTION(); From eff833f3bcc0dad77fe56ebb3a507c628e29134d Mon Sep 17 00:00:00 2001 From: Conticop <58798963+Conticop@users.noreply.github.com> Date: Wed, 5 Aug 2020 15:12:28 +0200 Subject: [PATCH 6/6] Twerking stuff. * Removed `cg_multiTextureStrength` (unused). * Decreased `cg_textureStrength` by half. * Adjusted min/max of `cg_outlineStrength`. --- Resources/Scripts/Gui/Preferences.as | 2 +- Sources/Client/Client.cpp | 7 +++---- Sources/Draw/GLMapChunk.cpp | 3 +-- Sources/Draw/GLMapRenderer.cpp | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Resources/Scripts/Gui/Preferences.as b/Resources/Scripts/Gui/Preferences.as index fa1aa6d23..d08703292 100644 --- a/Resources/Scripts/Gui/Preferences.as +++ b/Resources/Scripts/Gui/Preferences.as @@ -646,7 +646,7 @@ namespace spades { layouter.AddHeading(_Tr("Preferences", "OpenGL Effects")); layouter.AddToggleField(_Tr("Preferences", "Outlines"), "cg_outlines"); - layouter.AddSliderField(_Tr("Preferences", "Outline Strength"), "cg_outlineStrength", 2, 5, 1, + layouter.AddSliderField(_Tr("Preferences", "Outline Strength"), "cg_outlineStrength", 1, 3, 1, ConfigNumberFormatter(0, "px")); layouter.AddToggleField(_Tr("Preferences", "Textures"), "cg_textures"); layouter.AddToggleField(_Tr("Preferences", "Multi-Texture Mode"), "cg_multiTextures"); diff --git a/Sources/Client/Client.cpp b/Sources/Client/Client.cpp index a020f9cf3..a0e750a67 100644 --- a/Sources/Client/Client.cpp +++ b/Sources/Client/Client.cpp @@ -66,12 +66,11 @@ SPADES_SETTING(cg_playerName); DEFINE_SPADES_SETTING(dd_specNames, "0"); DEFINE_SPADES_SETTING(dd_specWallhack, "0"); -DEFINE_SPADES_SETTING(cg_textures, "0"); -DEFINE_SPADES_SETTING(cg_multiTextures, "0"); DEFINE_SPADES_SETTING(cg_outlines, "0"); -DEFINE_SPADES_SETTING(cg_textureStrength, "25"); -DEFINE_SPADES_SETTING(cg_multiTextureStrength, "25"); DEFINE_SPADES_SETTING(cg_outlineStrength, "2"); +DEFINE_SPADES_SETTING(cg_textures, "0"); +DEFINE_SPADES_SETTING(cg_textureStrength, "10"); +DEFINE_SPADES_SETTING(cg_multiTextures, "0"); DEFINE_SPADES_SETTING(s_volume, "100"); diff --git a/Sources/Draw/GLMapChunk.cpp b/Sources/Draw/GLMapChunk.cpp index 57286ea13..82e3bf620 100644 --- a/Sources/Draw/GLMapChunk.cpp +++ b/Sources/Draw/GLMapChunk.cpp @@ -22,6 +22,7 @@ #include #include "GLDynamicLightShader.h" +#include "GLImage.h" #include "GLMapChunk.h" #include "GLMapRenderer.h" #include "GLProgramAttribute.h" @@ -33,8 +34,6 @@ #include #include -#include "GLImage.h" - SPADES_SETTING(cg_textures); SPADES_SETTING(cg_multiTextures); diff --git a/Sources/Draw/GLMapRenderer.cpp b/Sources/Draw/GLMapRenderer.cpp index 26f7960d6..50be09df9 100644 --- a/Sources/Draw/GLMapRenderer.cpp +++ b/Sources/Draw/GLMapRenderer.cpp @@ -269,7 +269,7 @@ namespace spades { if (previous_cg_textures) { blockTextureStrength(basicProgram); - blockTextureStrength.SetValue(((float)cg_textureStrength) / 100.0f); + blockTextureStrength.SetValue(((float)cg_textureStrength) / 200.0f); } static GLShadowShader shadowShader;