diff --git a/data/gfx/gfx_mudPot_a.xml b/data/gfx/gfx_mudPot_a.xml new file mode 100644 index 000000000..cc6ead88f --- /dev/null +++ b/data/gfx/gfx_mudPot_a.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/shaders/fog.frag b/data/shaders/fog.frag index cb5ec7348..104b57d83 100644 --- a/data/shaders/fog.frag +++ b/data/shaders/fog.frag @@ -1,10 +1,6 @@ uniform sampler2D tex; -uniform float fogmax; -uniform float startH; -uniform float endH; -uniform float start; -uniform float end; +uniform float density; uniform vec3 col; out vec4 FragColor; @@ -14,14 +10,12 @@ vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); void main() { - vec2 uv = gl_FragCoord.xy / screen; - float z = texture(tex, uv).x; - vec4 xpos = getPosFromUVDepth(vec3(uv, z), InverseProjectionMatrix); + vec2 uv = 2. * gl_FragCoord.xy / screen; + float z = texture(tex, uv).x; + vec4 xpos = getPosFromUVDepth(vec3(uv, z), InverseProjectionMatrix); - float dist = length(xpos.xyz); - float fog = smoothstep(start, end, dist); + float dist = length(xpos.xyz); + vec3 fog = col * (1. - exp(- density * dist)); - fog = min(fog, fogmax); - - FragColor = vec4(col, fog); + FragColor = vec4(fog, 1.); } diff --git a/data/shaders/gaussian6h.frag b/data/shaders/gaussian6h.frag index e90cfd132..f788df9d1 100644 --- a/data/shaders/gaussian6h.frag +++ b/data/shaders/gaussian6h.frag @@ -1,7 +1,6 @@ uniform sampler2D tex; uniform vec2 pixel; - -float sigma = 1.; +uniform float sigma; // Gaussian separated blur with radius 6. diff --git a/data/shaders/gaussian6v.frag b/data/shaders/gaussian6v.frag index cf5ec0ce6..4df5905b0 100644 --- a/data/shaders/gaussian6v.frag +++ b/data/shaders/gaussian6v.frag @@ -1,7 +1,6 @@ uniform sampler2D tex; uniform vec2 pixel; - -float sigma = 1.; +uniform float sigma; // Gaussian separated blur with radius 6. diff --git a/data/shaders/pointlightscatter.frag b/data/shaders/pointlightscatter.frag new file mode 100644 index 000000000..2d6ee6cba --- /dev/null +++ b/data/shaders/pointlightscatter.frag @@ -0,0 +1,46 @@ +uniform sampler2D dtex; +uniform float density; +uniform vec3 fogcol; + +flat in vec3 center; +flat in float energy; +flat in vec3 col; +flat in float radius; + +out vec4 Fog; + +vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); + +void main() +{ + vec4 pseudocenter = ViewMatrix * vec4(center.xyz, 1.0); + pseudocenter /= pseudocenter.w; + vec3 light_pos = pseudocenter.xyz; + vec3 light_col = col.xyz; + + // Compute pixel position + vec2 texc = 2. * gl_FragCoord.xy / screen; + float z = texture(dtex, texc).x; + vec4 pixelpos = getPosFromUVDepth(vec3(texc, z), InverseProjectionMatrix); + vec3 eyedir = -normalize(pixelpos.xyz); + + vec3 farthestpoint = - eyedir * (min(dot(-eyedir, light_pos) + radius, length(pixelpos.xyz))); + vec3 closestpoint = - eyedir * (dot(-eyedir, light_pos) - radius); + if (closestpoint.z < 1.) closestpoint = vec3(0.); + + float stepsize = length(farthestpoint - closestpoint) / 16; + vec3 fog = vec3(0.); + vec3 xpos = farthestpoint; + + for (int i = 0; i < 16; i++) + { + float d = distance(light_pos, xpos); + float l = (16 - i) * stepsize; + float att = energy * 20. / (1. + d * d); + att *= max((radius - d) / radius, 0.); + fog += density * light_col * att * exp(- density * d) * exp(- density * l) * stepsize; + xpos += stepsize * eyedir; + } + + Fog = vec4(fogcol * fog, 1.); +} diff --git a/data/shaders/volumetriclight.frag b/data/shaders/volumetriclight.frag new file mode 100644 index 000000000..0b4952068 --- /dev/null +++ b/data/shaders/volumetriclight.frag @@ -0,0 +1,43 @@ +#ifdef GL_ARB_bindless_texture +layout(bindless_sampler) uniform sampler2D tex; +#else +uniform sampler2D tex; +#endif + + +uniform float fogmax; +uniform float startH; +uniform float endH; +uniform float start; +uniform float end; +uniform vec3 col; + +in vec2 uv; +in vec4 color; +out vec4 FragColor; + + +void main() +{ + vec4 diffusecolor = texture(tex, uv); +#ifdef GL_ARB_bindless_texture + diffusecolor.xyz = pow(diffusecolor.xyz, vec3(2.2)); +#endif + diffusecolor.xyz *= pow(color.xyz, vec3(2.2)); + diffusecolor.a *= color.a; + vec3 tmp = vec3(gl_FragCoord.xy / screen, gl_FragCoord.z); + tmp = 2. * tmp - 1.; + + vec4 xpos = vec4(tmp, 1.0); + xpos = InverseProjectionMatrix * xpos; + xpos.xyz /= xpos.w; + + float dist = length(xpos.xyz); + float fog = smoothstep(start, end, dist); + + fog = min(fog, fogmax); + + vec4 finalcolor = vec4(col, 0.) * fog + diffusecolor *(1. - fog); + FragColor = vec4(finalcolor.rgb * finalcolor.a, finalcolor.a); + //FragColor = vec4(1.0, 0.0, 0.0, finalcolor.a); +} diff --git a/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h b/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h index 1036c576c..caea847f8 100644 --- a/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h +++ b/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h @@ -632,6 +632,10 @@ public: { if (m_inverseMass) { + btAssert(!isnan(impulseMagnitude)); + btAssert(!isnan(linearComponent.getX())); + btAssert(!isnan(linearComponent.getY())); + btAssert(!isnan(linearComponent.getZ())); m_deltaLinearVelocity += linearComponent*impulseMagnitude; m_deltaAngularVelocity += angularComponent*(impulseMagnitude*m_angularFactor); } diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 60cd0c1fd..9bffa86cc 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -416,6 +416,7 @@ private: void renderGlow(std::vector& glows); void renderSSAO(); void renderLights(unsigned pointlightCount, bool hasShadow); + void renderLightsScatter(unsigned pointlightCount); void renderShadowsDebug(); void doScreenShot(); void PrepareDrawCalls(scene::ICameraSceneNode *camnode); diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 1b2503be5..0190fce43 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -327,15 +327,15 @@ void PostProcessing::renderGaussian6BlurLayer(FrameBuffer &in_fbo) glGenTextures(1, &LayerTex); glTextureView(LayerTex, GL_TEXTURE_2D, in_fbo.getRTT()[0], GL_R32F, 0, 1, i, 1); FullScreenShader::Gaussian6VBlurShader::getInstance()->SetTextureUnits(LayerTex); - DrawFullScreenEffect(core::vector2df(1. / 1024., 1. / 1024.)); + DrawFullScreenEffect(core::vector2df(1.f / 1024.f, 1.f / 1024.f), 1.f); in_fbo.BindLayer(i); FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(irr_driver->getFBO(FBO_BLOOM_1024).getRTT()[0]); - DrawFullScreenEffect(core::vector2df(1. / 1024., 1. / 1024.)); + DrawFullScreenEffect(core::vector2df(1.f / 1024.f, 1.f / 1024.f), 1.f); glDeleteTextures(1, &LayerTex); } } -void PostProcessing::renderGaussian6Blur(FrameBuffer &in_fbo, FrameBuffer &auxiliary) +void PostProcessing::renderGaussian6Blur(FrameBuffer &in_fbo, FrameBuffer &auxiliary, float sigmaV, float sigmaH) { assert(in_fbo.getWidth() == auxiliary.getWidth() && in_fbo.getHeight() == auxiliary.getHeight()); float inv_width = 1.0f / in_fbo.getWidth(), inv_height = 1.0f / in_fbo.getHeight(); @@ -343,57 +343,16 @@ void PostProcessing::renderGaussian6Blur(FrameBuffer &in_fbo, FrameBuffer &auxil auxiliary.Bind(); FullScreenShader::Gaussian6VBlurShader::getInstance()->SetTextureUnits(in_fbo.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); + DrawFullScreenEffect(core::vector2df(inv_width, inv_height), sigmaV); } { in_fbo.Bind(); FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(auxiliary.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); + DrawFullScreenEffect(core::vector2df(inv_width, inv_height), sigmaH); } } -void PostProcessing::renderHorizontalBlur(FrameBuffer &in_fbo, FrameBuffer &auxiliary) -{ - assert(in_fbo.getWidth() == auxiliary.getWidth() && in_fbo.getHeight() == auxiliary.getHeight()); - float inv_width = 1.0f / in_fbo.getWidth(), inv_height = 1.0f / in_fbo.getHeight(); - { - auxiliary.Bind(); - - FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(in_fbo.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); - } - { - in_fbo.Bind(); - - FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(auxiliary.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); - } - { - auxiliary.Bind(); - - FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(in_fbo.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); - } - { - in_fbo.Bind(); - - FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(auxiliary.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); - } - { - auxiliary.Bind(); - - FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(in_fbo.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); - } - { - in_fbo.Bind(); - - FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(auxiliary.getRTT()[0]); - DrawFullScreenEffect(core::vector2df(inv_width, inv_height)); - } -} void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &auxiliary) { @@ -515,11 +474,11 @@ void PostProcessing::renderFog() glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc(GL_ONE, GL_ONE); FullScreenShader::FogShader::getInstance()->SetTextureUnits(irr_driver->getDepthStencilTexture()); - DrawFullScreenEffect(fogmax, startH, endH, start, end, col); + DrawFullScreenEffect(1. / (40. * start), col); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); @@ -763,13 +722,14 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo // Blur - renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_TMP_512)); - renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_TMP_256)); - renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128)); + + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_TMP_512), 1., 1.); + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_TMP_256), 1., 1.); + renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 1., 1.); - renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_512), irr_driver->getFBO(FBO_TMP_512)); - renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_256), irr_driver->getFBO(FBO_TMP_256)); - renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_128), irr_driver->getFBO(FBO_TMP_128)); + renderGaussian6Blur(irr_driver->getFBO(FBO_LENS_512), irr_driver->getFBO(FBO_TMP_512), 0.01, 10.); + renderGaussian6Blur(irr_driver->getFBO(FBO_LENS_256), irr_driver->getFBO(FBO_TMP_256), 0.01, 10.); + renderGaussian6Blur(irr_driver->getFBO(FBO_LENS_128), irr_driver->getFBO(FBO_TMP_128), 0.01, 10.); // Additively blend on top of tmp1 in_fbo->Bind(); diff --git a/src/graphics/post_processing.hpp b/src/graphics/post_processing.hpp index cced5ec2a..702b7981c 100644 --- a/src/graphics/post_processing.hpp +++ b/src/graphics/post_processing.hpp @@ -87,8 +87,9 @@ public: /** Blur the in texture */ void renderGaussian3Blur(FrameBuffer &in_fbo, FrameBuffer &auxiliary); - void renderGaussian6Blur(FrameBuffer &in_fbo, FrameBuffer &auxiliary); - void renderHorizontalBlur(FrameBuffer &in_fbo, FrameBuffer &auxiliary); + + void renderGaussian6Blur(FrameBuffer &in_fbo, FrameBuffer &auxiliary, float sigmaV, float sigmaH); + void renderGaussian6BlurLayer(FrameBuffer &in_fbo); void renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &auxiliary); diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 654f8d6f5..10d9e9530 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -420,7 +420,7 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po World::getWorld()->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- Fog", 0xFF, 0x00, 0x00); - m_post_processing->renderFog(); + renderLightsScatter(pointlightcount); PROFILER_POP_CPU_MARKER(); } diff --git a/src/graphics/render_lighting.cpp b/src/graphics/render_lighting.cpp index 0d0ac9a6e..32749a468 100644 --- a/src/graphics/render_lighting.cpp +++ b/src/graphics/render_lighting.cpp @@ -185,3 +185,38 @@ void IrrDriver::renderSSAO() m_post_processing->renderGaussian17TapBlur(irr_driver->getFBO(FBO_HALF1_R), irr_driver->getFBO(FBO_HALF2_R)); } + +void IrrDriver::renderLightsScatter(unsigned pointlightcount) +{ + getFBO(FBO_HALF1).Bind(); + glClearColor(0., 0., 0., 0.); + glClear(GL_COLOR_BUFFER_BIT); + m_post_processing->renderFog(); + + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ONE); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + const Track * const track = World::getWorld()->getTrack(); + + const float start = track->getFogStart(); + const video::SColor tmpcol = track->getFogColor(); + core::vector3df col(1., 1., 1.); + + glUseProgram(LightShader::PointLightScatterShader::getInstance()->Program); + glBindVertexArray(LightShader::PointLightScatterShader::getInstance()->vao); + + LightShader::PointLightScatterShader::getInstance()->SetTextureUnits(irr_driver->getDepthStencilTexture()); + LightShader::PointLightScatterShader::getInstance()->setUniforms(1. / (40. * start), col); + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, pointlightcount); + + glDisable(GL_BLEND); + m_post_processing->renderGaussian6Blur(getFBO(FBO_HALF1), getFBO(FBO_HALF2), 5., 5.); + + glEnable(GL_BLEND); + getFBO(FBO_COLORS).Bind(); + m_post_processing->renderPassThrough(getRenderTargetTexture(RTT_HALF1)); +} \ No newline at end of file diff --git a/src/graphics/render_skybox.cpp b/src/graphics/render_skybox.cpp index 0016016f2..dca9dd889 100644 --- a/src/graphics/render_skybox.cpp +++ b/src/graphics/render_skybox.cpp @@ -538,6 +538,7 @@ void IrrDriver::renderSkybox(const scene::ICameraSceneNode *camera) core::matrix4 transform = translate * scale; core::matrix4 invtransform; transform.getInverse(invtransform); + glDisable(GL_BLEND); glUseProgram(MeshShader::SkyboxShader::getInstance()->Program); MeshShader::SkyboxShader::getInstance()->setUniforms(transform); diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 0093dd2f7..9b3f4d5af 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1468,6 +1468,41 @@ namespace LightShader glVertexAttribDivisorARB(attrib_Color, 1); glVertexAttribDivisorARB(attrib_Radius, 1); } + + PointLightScatterShader::PointLightScatterShader() + { + Program = LoadProgram(OBJECT, + GL_VERTEX_SHADER, file_manager->getAsset("shaders/pointlight.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/pointlightscatter.frag").c_str()); + + AssignUniforms("density", "fogcol"); + AssignSamplerNames(Program, 0, "dtex"); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, PointLightShader::getInstance()->vbo); + + GLuint attrib_Position = glGetAttribLocation(Program, "Position"); + GLuint attrib_Color = glGetAttribLocation(Program, "Color"); + GLuint attrib_Energy = glGetAttribLocation(Program, "Energy"); + GLuint attrib_Radius = glGetAttribLocation(Program, "Radius"); + + glEnableVertexAttribArray(attrib_Position); + glVertexAttribPointer(attrib_Position, 3, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), 0); + glEnableVertexAttribArray(attrib_Energy); + glVertexAttribPointer(attrib_Energy, 1, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), (GLvoid*)(3 * sizeof(float))); + glEnableVertexAttribArray(attrib_Color); + glVertexAttribPointer(attrib_Color, 3, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), (GLvoid*)(4 * sizeof(float))); + glEnableVertexAttribArray(attrib_Radius); + glVertexAttribPointer(attrib_Radius, 1, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), (GLvoid*)(7 * sizeof(float))); + + glVertexAttribDivisorARB(attrib_Position, 1); + glVertexAttribDivisorARB(attrib_Energy, 1); + glVertexAttribDivisorARB(attrib_Color, 1); + glVertexAttribDivisorARB(attrib_Radius, 1); + } } @@ -1712,7 +1747,7 @@ namespace FullScreenShader Program = LoadProgram(OBJECT, GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian6h.frag").c_str()); - AssignUniforms("pixel"); + AssignUniforms("pixel", "sigma"); AssignSamplerNames(Program, 0, "tex"); } @@ -1762,7 +1797,7 @@ namespace FullScreenShader Program = LoadProgram(OBJECT, GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian6v.frag").c_str()); - AssignUniforms("pixel"); + AssignUniforms("pixel", "sigma"); AssignSamplerNames(Program, 0, "tex"); } @@ -1862,7 +1897,7 @@ namespace FullScreenShader GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(), GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/fog.frag").c_str()); - AssignUniforms("fogmax", "startH", "endH", "start", "end", "col"); + AssignUniforms("density", "col"); AssignSamplerNames(Program, 0, "tex"); } diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 2c1928cd6..3088e6b2c 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -328,6 +328,14 @@ namespace LightShader GLuint vao; PointLightShader(); }; + + class PointLightScatterShader : public ShaderHelperSingleton, public TextureRead + { + public: + GLuint vbo; + GLuint vao; + PointLightScatterShader(); + }; } namespace ParticleShader @@ -456,7 +464,7 @@ public: ComputeGaussian17TapHShader(); }; -class Gaussian6HBlurShader : public ShaderHelperSingleton, public TextureRead +class Gaussian6HBlurShader : public ShaderHelperSingleton, public TextureRead { public: Gaussian6HBlurShader(); @@ -489,7 +497,7 @@ public: }; -class Gaussian6VBlurShader : public ShaderHelperSingleton, public TextureRead +class Gaussian6VBlurShader : public ShaderHelperSingleton, public TextureRead { public: Gaussian6VBlurShader(); @@ -550,7 +558,7 @@ public: SSAOShader(); }; -class FogShader : public ShaderHelperSingleton, public TextureRead +class FogShader : public ShaderHelperSingleton, public TextureRead { public: FogShader(); diff --git a/supertuxkart.project b/supertuxkart.project new file mode 100644 index 000000000..829e4235f --- /dev/null +++ b/supertuxkart.project @@ -0,0 +1,2164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make clean + make + + + + None + $(WorkspacePath) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make clean + make + + + + None + $(WorkspacePath) + + + + + + + + + + + + + +