diff --git a/README.md b/README.md index 146aca2e1..6d98155f8 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ Hope you enjoy the game. ##Compiling SuperTuxKart ###Windows -1. Install VS 2012 or later. The free express versions work fine. +1. Install VS 2013 (or later). The free express versions work fine. 2. Download and install a source package - either a released package or from our [git/svn repositories](http://supertuxkart.sourceforge.net/Source_control) 3. Download the latest dependency package depdendencies_for_0.8.2.zip from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/). Unzip it in the root directory, so that the dependencies directory is next to the src and data directory (if you are updating from a previous dependency package, you can delete the .dll files in the root directory, they are not needed anymore). 4. Download cmake and install it. Then start cmake-gui and select the STK root directory as 'Where is the source code', and a new directory in the root directory (next to src, data etc) as build directory (for now I assume that this directory is called bld). -5. Click on configure. You will be asked to create the directory (yes), then for your VS version. Make sure to select the right version (be aware of the easy to confuse version numbers: VS 2012 = version 11; VS 2013 = version 12). Click on configure, then generate. This will create the directory 'bld', and a VS solution in that directory. +5. Click on configure. You will be asked to create the directory (yes), then for your VS version. Make sure to select the right version (be aware of the easy to confuse version numbers: VS 2013 = version 12). Click on configure, then generate. This will create the directory 'bld', and a VS solution in that directory. 6. In Visual Studio open the project file generated in the 'bld' folder 7. Right click on the supertuxkart project in the solution explorer, and select "Set as StartUp Project". 8. Select Build->Build Solution (or press F7) to compile. diff --git a/data/grandprix/4_atworldsend.grandprix b/data/grandprix/4_atworldsend.grandprix index 0aa43f1e2..0341793a1 100644 --- a/data/grandprix/4_atworldsend.grandprix +++ b/data/grandprix/4_atworldsend.grandprix @@ -4,7 +4,7 @@ - + diff --git a/data/shaders/Lightspaceboundingbox.comp b/data/shaders/Lightspaceboundingbox.comp new file mode 100644 index 000000000..e55e69c68 --- /dev/null +++ b/data/shaders/Lightspaceboundingbox.comp @@ -0,0 +1,142 @@ +uniform sampler2D depth; +uniform float split0; +uniform float split1; +uniform float split2; +uniform float splitmax; +uniform mat4 SunCamMatrix; + +layout (local_size_x = 8, local_size_y = 8) in; + +struct CascadeBoundingBox +{ + int xmin; + int xmax; + int ymin; + int ymax; + int zmin; + int zmax; +}; + +layout (std430) buffer BoundingBoxes +{ + CascadeBoundingBox BB[4]; +}; + +vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); + +shared int xmin[4]; +shared int xmax[4]; +shared int ymin[4]; +shared int ymax[4]; +shared int zmin[4]; +shared int zmax[4]; + +void main() +{ + if (gl_LocalInvocationIndex < 4) { + xmin[gl_LocalInvocationIndex] = ymin[gl_LocalInvocationIndex] = zmin[gl_LocalInvocationIndex] = 1000; + xmax[gl_LocalInvocationIndex] = ymax[gl_LocalInvocationIndex] = zmax[gl_LocalInvocationIndex] = -1000; + } + + barrier(); + + ivec3 lmax0 = ivec3(-1000); + ivec3 lmin0 = ivec3(1000); + ivec3 lmax1 = ivec3(-1000); + ivec3 lmin1 = ivec3(1000); + ivec3 lmax2 = ivec3(-1000); + ivec3 lmin2 = ivec3(1000); + ivec3 lmax3 = ivec3(-1000); + ivec3 lmin3 = ivec3(1000); + + vec2 start_xy = gl_LocalInvocationID.xy + gl_WorkGroupID.xy * gl_WorkGroupSize.xy * 8; + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + + + vec2 uv = (start_xy + vec2(i, j) * gl_WorkGroupID.xy) / screen; + float z = texture(depth, uv).x; + vec4 xpos = getPosFromUVDepth(vec3(uv, z), InverseProjectionMatrix); + vec4 lightcoord = InverseViewMatrix * xpos; + lightcoord /= lightcoord.w; + lightcoord = SunCamMatrix * lightcoord; + lightcoord /= lightcoord.w; + ivec3 lc = ivec3(lightcoord.xyz) * 4; + + if (xpos.z < split0) { + lmax0 = max(lmax0, lc); + lmin0 = min(lmin0, lc); + } else if (xpos.z < split1) { + lmax1 = max(lmax1, lc); + lmin1 = min(lmin1, lc); + } else if (xpos.z < split2) { + lmax2 = max(lmax2, lc); + lmin2 = min(lmin2, lc); + } else if (xpos.z < splitmax) { + lmax3 = max(lmax3, lc); + lmin3 = min(lmin3, lc); + } + } + } + + atomicMax(xmax[0], lmax0.x); + atomicMax(ymax[0], lmax0.y); + atomicMax(zmax[0], lmax0.z); + atomicMin(xmin[0], lmin0.x); + atomicMin(ymin[0], lmin0.y); + atomicMin(zmin[0], lmin0.z); + + atomicMax(xmax[1], lmax1.x); + atomicMax(ymax[1], lmax1.y); + atomicMax(zmax[1], lmax1.z); + atomicMin(xmin[1], lmin1.x); + atomicMin(ymin[1], lmin1.y); + atomicMin(zmin[1], lmin1.z); + + atomicMax(xmax[2], lmax2.x); + atomicMax(ymax[2], lmax2.y); + atomicMax(zmax[2], lmax2.z); + atomicMin(xmin[2], lmin2.x); + atomicMin(ymin[2], lmin2.y); + atomicMin(zmin[2], lmin2.z); + + atomicMax(xmax[3], lmax3.x); + atomicMax(ymax[3], lmax3.y); + atomicMax(zmax[3], lmax3.z); + atomicMin(xmin[3], lmin3.x); + atomicMin(ymin[3], lmin3.y); + atomicMin(zmin[3], lmin3.z); + + barrier(); + + if (gl_LocalInvocationIndex == 0) { + atomicMax(BB[0].xmax, xmax[0]); + atomicMax(BB[0].ymax, ymax[0]); + atomicMax(BB[0].zmax, zmax[0]); + atomicMin(BB[0].xmin, xmin[0]); + atomicMin(BB[0].ymin, ymin[0]); + atomicMin(BB[0].zmin, zmin[0]); + + atomicMax(BB[1].xmax, xmax[1]); + atomicMax(BB[1].ymax, ymax[1]); + atomicMax(BB[1].zmax, zmax[1]); + atomicMin(BB[1].xmin, xmin[1]); + atomicMin(BB[1].ymin, ymin[1]); + atomicMin(BB[1].zmin, zmin[1]); + + atomicMax(BB[2].xmax, xmax[2]); + atomicMax(BB[2].ymax, ymax[2]); + atomicMax(BB[2].zmax, zmax[2]); + atomicMin(BB[2].xmin, xmin[2]); + atomicMin(BB[2].ymin, ymin[2]); + atomicMin(BB[2].zmin, zmin[2]); + + atomicMax(BB[3].xmax, xmax[3]); + atomicMax(BB[3].ymax, ymax[3]); + atomicMax(BB[3].zmax, zmax[3]); + atomicMin(BB[3].xmin, xmin[3]); + atomicMin(BB[3].ymin, ymin[3]); + atomicMin(BB[3].zmin, zmin[3]); + } +} + diff --git a/data/shaders/depthhistogram.comp b/data/shaders/depthhistogram.comp new file mode 100644 index 000000000..76c46e701 --- /dev/null +++ b/data/shaders/depthhistogram.comp @@ -0,0 +1,55 @@ +uniform sampler2D depth; + +layout (local_size_x = 32, local_size_y = 32) in; + +layout (std430) buffer Histogram +{ + int bin[1024]; + int mindepth; + int maxdepth; + int count; +}; + +vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); + +shared int sbin[1024]; +shared int smindepth; +shared int smaxdepth; +shared int scount; + +void main() +{ + int x = int(gl_GlobalInvocationID.x), y = int(gl_GlobalInvocationID.y); + vec2 uv = vec2(x, y) / screen; + float z = texture(depth, uv).x; + vec4 xpos = getPosFromUVDepth(vec3(uv, z), InverseProjectionMatrix); + + int lineardepth = int(xpos.z * 4); + + + sbin[gl_LocalInvocationIndex] = 0; + if (gl_LocalInvocationIndex == 0) { + smindepth = 1000; + smaxdepth = 0; + scount = 0; + } + + barrier(); + + if (lineardepth < 1000) { + atomicAdd(sbin[lineardepth], 1); + atomicAdd(scount, 1); + atomicMin(smindepth, lineardepth); + atomicMax(smaxdepth, lineardepth); + } + + barrier(); + + atomicAdd(bin[gl_LocalInvocationIndex], sbin[gl_LocalInvocationIndex]); + if (gl_LocalInvocationIndex == 0) { + atomicAdd(count, scount); + atomicMin(mindepth, smindepth); + atomicMax(maxdepth, smaxdepth); + } +} + diff --git a/data/shaders/sunlightshadow.frag b/data/shaders/sunlightshadow.frag index 0bb01c51d..7ad3e08a0 100644 --- a/data/shaders/sunlightshadow.frag +++ b/data/shaders/sunlightshadow.frag @@ -1,14 +1,14 @@ uniform sampler2D ntex; uniform sampler2D dtex; uniform sampler2DArray shadowtex; -//uniform sampler2D warpx; -///uniform sampler2D warpy; + +uniform float split0; +uniform float split1; +uniform float split2; +uniform float splitmax; uniform vec3 direction; uniform vec3 col; -//uniform int hasclouds; -//uniform vec2 wind; -//uniform float shadowoffset; in vec2 uv; out vec4 Diff; @@ -20,8 +20,7 @@ vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); float getShadowFactor(vec3 pos, float bias, int index) { - //float a[5] = float[](3.4, 4.2, 5.0, 5.2, 1.1); - + vec2 shadowoffset[4] = vec2[]( vec2(-1., -1.), vec2(-1., 1.), @@ -32,27 +31,6 @@ float getShadowFactor(vec3 pos, float bias, int index) vec4 shadowcoord = (ShadowViewProjMatrixes[index] * InverseViewMatrix * vec4(pos, 1.0)); shadowcoord.xy /= shadowcoord.w; vec2 shadowtexcoord = shadowcoord.xy * 0.5 + 0.5; -// shadowcoord = (shadowcoord * 0.5) + vec3(0.5); - -// float movex = decdepth(texture(warpx, shadowcoord.xy)); -// float movey = decdepth(texture(warpy, shadowcoord.xy)); -// float dx = movex * 2.0 - 1.0; -// float dy = movey * 2.0 - 1.0; -// shadowcoord.xy += vec2(dx, dy);*/ - -//float shadowmapz = 2. * texture(shadowtex, vec3(shadowtexcoord, shadowcoord.z).x - 1.; -// bias += smoothstep(0.001, 0.1, moved) * 0.014; // According to the warping - -// float sum = 0.; -// for (float i = -1.5; i <= 1.5; i+= 1.) -// { -// for (float j = -1.5; j <= 1.5; j+= 1.) -// { -// float z = texture(shadowtex, vec3(shadowtexcoord +vec2(i, j) / 1024., float(index))).x; -// sum += (z > 0.5 * shadowcoord.z + 0.5) ? 1. : 0.; -// } -// } -// return sum / 16.; float z = texture(shadowtex, vec3(shadowtexcoord, float(index))).x; float d = shadowcoord.z; @@ -78,86 +56,41 @@ void main() { vec3 outcol = NdotL * col; -// if (hasclouds == 1) -// { -// vec2 cloudcoord = (xpos.xz * 0.00833333) + wind; -// float cloud = texture(cloudtex, cloudcoord).x; -// //float cloud = step(0.5, cloudcoord.x) * step(0.5, cloudcoord.y); - -// outcol *= cloud; -// } // Shadows float bias = 0.005 * tan(acos(NdotL)); // According to the slope bias = clamp(bias, 0., 0.01); float factor; - if (xpos.z < 5.) + if (xpos.z < split0) factor = getShadowFactor(xpos.xyz, bias, 0); - else if (xpos.z < 6.) +/* else if (xpos.z < 6.) { float a = getShadowFactor(xpos.xyz, bias, 0), b = getShadowFactor(xpos.xyz, bias, 1); factor = mix(a, b, (xpos.z - 5.)); - } - else if (xpos.z < 20.) + }*/ + else if (xpos.z < split1) factor = getShadowFactor(xpos.xyz, bias, 1); - else if (xpos.z < 21.) +/* else if (xpos.z < 21.) { float a = getShadowFactor(xpos.xyz, bias, 1), b = getShadowFactor(xpos.xyz, bias, 2); factor = mix(a, b, (xpos.z - 20.)); - } - else if (xpos.z < 50.) + }*/ + else if (xpos.z < split2) factor = getShadowFactor(xpos.xyz, bias, 2); - else if (xpos.z < 55.) +/* else if (xpos.z < 55.) { float a = getShadowFactor(xpos.xyz, bias, 2), b = getShadowFactor(xpos.xyz, bias, 3); factor = mix(a, b, (xpos.z - 50.) / 5.); - } - else if (xpos.z < 145.) + }*/ + else if (xpos.z < splitmax) factor = getShadowFactor(xpos.xyz, bias, 3); - else if (xpos.z < 150.) +/* else if (xpos.z < 150.) { factor = mix(getShadowFactor(xpos.xyz, bias, 3), 1., (xpos.z - 145.) / 5.); - } + }*/ else factor = 1.; Diff = vec4(factor * NdotL * col, 1.); Spec = vec4(factor * Specular, 1.); return; - -// float moved = (abs(dx) + abs(dy)) * 0.5; - -// float avi = 0.002; -// float abi = 0.0025; - -/* float avi = 0.0018; - float abi = 0.002; - - float bias = avi * tan(acos(NdotL)); // According to the slope - bias += smoothstep(0.001, 0.1, moved) * abi; // According to the warping - bias = clamp(bias, 0.001, abi); - - // This ID, and four IDs around this must match for a shadow pixel - float right = texture(shadowtex, shadowcoord.xy + vec2(shadowoffset, 0.0)).a; - float left = texture(shadowtex, shadowcoord.xy + vec2(-shadowoffset, 0.0)).a; - float up = texture(shadowtex, shadowcoord.xy + vec2(0.0, shadowoffset)).a; - float down = texture(shadowtex, shadowcoord.xy + vec2(0.0, -shadowoffset)).a; - - float matching = ((right + left + up + down) * 0.25) - shadowread.a; - matching = abs(matching) * 400.0; - - // If the ID is different, we're likely in shadow - cut the bias to cut peter panning - float off = 7.0 - step(abs(shadowread.a - depthread.a) - matching, 0.004) * 6.0; - bias /= off; - - const float softness = 8.0; // How soft is the light? - float shadowed = step(shadowmapz + bias, shadowcoord.z); - float dist = (shadowcoord.z / shadowmapz) - 1.0; - float penumbra = dist * softness / gl_FragCoord.z; - penumbra *= shadowed;*/ - -/* outcol.r = (shadowcoord.z - shadowmapz) * 50.0; - outcol.g = moved;*/ - -// FragColor = vec4(outcol, 0.05); -// OtherOutput = vec4(shadowed, penumbra, shadowed, shadowed); } diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index 5195773dd..32460a4f4 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -64,7 +64,10 @@ Camera::Camera(int camera_index, AbstractKart* kart) : m_kart(NULL) setupCamera(); if (kart != NULL) { - m_distance = kart->getKartProperties()->getCameraDistance(); + if(UserConfigParams::m_camera_debug==2) + m_distance = kart->getKartModel()->getLength(); + else + m_distance = kart->getKartProperties()->getCameraDistance(); setKart(kart); } else @@ -378,37 +381,6 @@ void Camera::smoothMoveCamera(float dt) } } // smoothMoveCamera -//----------------------------------------------------------------------------- -/** Computes the wanted camera position and target for normal camera mode. - * Besides being used in update(dt), it is also used when switching the - * camera from reverse mode to normal mode - in which case we don't want - * to have a smooth camera. - * \param wanted_position The position the camera should be. - * \param wanted_target The target position the camera should target. - */ -void Camera::computeNormalCameraPosition(Vec3 *wanted_position, - Vec3 *wanted_target) -{ - *wanted_target = m_kart->getXYZ(); - wanted_target->setY(wanted_target->getY()+ 0.75f); - - // This first line moves the camera around behind the kart, pointing it - // towards where the kart is turning (and turning even more while skidding). - // The skidding effect is dampened. - float steering = m_kart->getSteerPercent() - * (1.0f + (m_kart->getSkidding()->getSkidFactor() - 1.0f) - /2.3f ); - // quadratically to dampen small variations (but keep sign) - float dampened_steer = fabsf(steering) * steering; - - float tan_up = tan(m_kart->getKartProperties()->getCameraForwardUpAngle()); - Vec3 relative_position(-m_distance*m_rotation_range*dampened_steer*0.5f, - m_distance*tan_up+0.75f, - -m_distance); - *wanted_position = m_kart->getTrans()(relative_position); - -} // computeNormalCameraPosition - //----------------------------------------------------------------------------- /** Determine the camera settings for the current frame. * \param above_kart How far above the camera should aim at. @@ -549,7 +521,10 @@ void Camera::positionCamera(float dt, float above_kart, float cam_angle, Vec3 wanted_position; Vec3 wanted_target = m_kart->getXYZ(); if(UserConfigParams::m_camera_debug==2) - wanted_target.setY(m_kart->getVehicle()->getWheelInfo(2).m_raycastInfo.m_contactPointWS.getY()); + { + const btWheelInfo &w = m_kart->getVehicle()->getWheelInfo(2); + wanted_target.setY(w.m_raycastInfo.m_contactPointWS.getY()); + } else wanted_target.setY(wanted_target.getY()+above_kart); float tan_up = tan(cam_angle); @@ -567,7 +542,7 @@ void Camera::positionCamera(float dt, float above_kart, float cam_angle, } wanted_position = t(relative_position); - if (smoothing) + if (smoothing && UserConfigParams::m_camera_debug==0) { smoothMoveCamera(dt); } @@ -591,8 +566,8 @@ void Camera::positionCamera(float dt, float above_kart, float cam_angle, // Rotate the up vector (0,1,0) by the rotation ... which is just column 1 Vec3 up = m_kart->getTrans().getBasis().getColumn(1); float f = 0.04f; // weight for new up vector to reduce shaking - m_camera->setUpVector(f * up.toIrrVector() + - (1.0f - f) * m_camera->getUpVector()); + m_camera->setUpVector( f * up.toIrrVector() + + (1.0f - f) * m_camera->getUpVector()); } // kart && !flying else m_camera->setUpVector(core::vector3df(0, 1, 0)); diff --git a/src/graphics/camera.hpp b/src/graphics/camera.hpp index c105ee4d3..50f7ba906 100644 --- a/src/graphics/camera.hpp +++ b/src/graphics/camera.hpp @@ -186,8 +186,6 @@ private: void setupCamera(); void smoothMoveCamera(float dt); - void computeNormalCameraPosition(Vec3 *wanted_position, - Vec3 *wanted_target); void handleEndCamera(float dt); void getCameraSettings(float *above_kart, float *cam_angle, float *side_way, float *distance, diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 700481678..8e6b9d95b 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -485,6 +485,7 @@ void IrrDriver::initDevice() m_need_ubo_workaround = false; m_need_rh_workaround = false; m_need_srgb_workaround = false; + m_support_sdsm = false; #ifdef WIN32 // Fix for Intel Sandy Bridge on Windows which supports GL up to 3.1 only if (strstr((const char *)glGetString(GL_VENDOR), "Intel") != NULL && (m_gl_major_version == 3 && m_gl_minor_version == 1)) @@ -492,7 +493,10 @@ void IrrDriver::initDevice() #endif // Fix for Nvidia and instanced RH if (strstr((const char *)glGetString(GL_VENDOR), "NVIDIA") != NULL) + { m_need_rh_workaround = true; + m_support_sdsm = false; + } // Fix for AMD and bindless sRGB textures if (strstr((const char *)glGetString(GL_VENDOR), "ATI") != NULL) @@ -546,6 +550,7 @@ void IrrDriver::initDevice() hasTextureView = true; Log::info("GLDriver", "ARB Texture View enabled"); } + m_support_sdsm = m_support_sdsm && hasComputeShaders && hasBuffserStorage; } #endif diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 4860de85f..60cd0c1fd 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -189,6 +189,7 @@ private: bool hasComputeShaders; bool hasTextureStorage; bool hasTextureView; + bool m_support_sdsm; bool m_need_ubo_workaround; bool m_need_rh_workaround; bool m_need_srgb_workaround; @@ -275,6 +276,11 @@ public: return 120; } + bool supportsSDSM() const + { + return m_support_sdsm; + } + bool needUBOWorkaround() const { return m_need_ubo_workaround; @@ -781,6 +787,7 @@ public: void renderScene(scene::ICameraSceneNode * const camnode, unsigned pointlightcount, std::vector& glows, float dt, bool hasShadows, bool forceRTT); unsigned UpdateLightsInfo(scene::ICameraSceneNode * const camnode, float dt); + void UpdateSplitAndLightcoordRangeFromComputeShaders(size_t width, size_t height); void computeCameraMatrix(scene::ICameraSceneNode * const camnode, size_t width, size_t height); // --------------------- OLD RTT -------------------- diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index d26530762..1b2503be5 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -282,6 +282,8 @@ void PostProcessing::renderSunlight() DrawFullScreenEffect(cb->getPosition(), video::SColorf(cb->getRed(), cb->getGreen(), cb->getBlue())); } +extern float shadowSplit[5]; + void PostProcessing::renderShadowedSunlight(const std::vector &sun_ortho_matrix, GLuint depthtex) { SunLightProvider * const cb = (SunLightProvider *)irr_driver->getCallback(ES_SUNLIGHT); @@ -292,7 +294,7 @@ void PostProcessing::renderShadowedSunlight(const std::vector &su glBlendEquation(GL_FUNC_ADD); FullScreenShader::ShadowedSunLightShader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), depthtex); - DrawFullScreenEffect(cb->getPosition(), video::SColorf(cb->getRed(), cb->getGreen(), cb->getBlue())); + DrawFullScreenEffect(shadowSplit[1], shadowSplit[2], shadowSplit[3], shadowSplit[4], cb->getPosition(), video::SColorf(cb->getRed(), cb->getGreen(), cb->getBlue())); } @@ -493,6 +495,7 @@ void PostProcessing::renderSSAO() DrawFullScreenEffect(irr_driver->getSSAORadius(), irr_driver->getSSAOK(), irr_driver->getSSAOSigma()); } + void PostProcessing::renderFog() { const Track * const track = World::getWorld()->getTrack(); diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 21e64047d..654f8d6f5 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -638,12 +638,155 @@ core::matrix4 getTighestFitOrthoProj(const core::matrix4 &transform, const std:: return tmp_matrix; tmp_matrix.buildProjectionMatrixOrthoLH(left, right, down, up, - 30, zmax); + zmin - 100, zmax); return tmp_matrix; } +float shadowSplit[5] = {1., 5., 20., 50., 150 }; + +struct CascadeBoundingBox +{ + int xmin; + int xmax; + int ymin; + int ymax; + int zmin; + int zmax; +}; + +static size_t currentCBB = 0; +static CascadeBoundingBox *CBB[2]; + +struct Histogram +{ + int bin[1024]; + int mindepth; + int maxdepth; + int count; +}; + + +/** Update shadowSplit values and make Cascade Bounding Box pointer valid. +* The function aunches two compute kernel that generates an histogram of the depth buffer value (between 0 and 250 with increment of 0.25) +* and get an axis aligned bounding box (from SunCamMatrix view) containing all depth buffer value. +* It also retrieves the result from the previous computations (in a Round Robin fashion) and update CBB pointer. +* \param width of the depth buffer +* \param height of the depth buffer +* TODO : The depth histogram part is commented out, needs to tweak it when I have some motivation +*/ +void IrrDriver::UpdateSplitAndLightcoordRangeFromComputeShaders(size_t width, size_t height) +{ + // Value that should be kept between multiple calls + static GLuint ssbo[2]; + static Histogram *Hist[2]; + static GLsync LightcoordBBFence = 0; + static size_t currentHist = 0; + static GLuint ssboSplit[2]; + static float tmpshadowSplit[5] = { 1., 5., 20., 50., 150. }; + + if (!LightcoordBBFence) + { + glGenBuffers(2, ssbo); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[0]); + glBufferStorage(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(CascadeBoundingBox), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + CBB[0] = (CascadeBoundingBox *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 4 * sizeof(CascadeBoundingBox), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[1]); + glBufferStorage(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(CascadeBoundingBox), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + CBB[1] = (CascadeBoundingBox *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 4 * sizeof(CascadeBoundingBox), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + +/* glGenBuffers(2, ssboSplit); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboSplit[0]); + glBufferStorage(GL_SHADER_STORAGE_BUFFER, sizeof(Histogram), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + Hist[0] = (Histogram *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(Histogram), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboSplit[1]); + glBufferStorage(GL_SHADER_STORAGE_BUFFER, sizeof(Histogram), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + Hist[1] = (Histogram *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(Histogram), GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);*/ + } + + // Use bounding boxes from last frame + if (LightcoordBBFence) + { + while (glClientWaitSync(LightcoordBBFence, GL_SYNC_FLUSH_COMMANDS_BIT, 0) != GL_ALREADY_SIGNALED); + glDeleteSync(LightcoordBBFence); + } + +/* { + memcpy(shadowSplit, tmpshadowSplit, 5 * sizeof(float)); + unsigned numpix = Hist[currentHist]->count; + unsigned split = 0; + unsigned i; + for (i = 0; i < 1022; i++) + { + split += Hist[currentHist]->bin[i]; + if (split > numpix / 2) + break; + } + tmpshadowSplit[1] = (float)++i / 4.; + + for (; i < 1023; i++) + { + split += Hist[currentHist]->bin[i]; + if (split > 3 * numpix / 4) + break; + } + tmpshadowSplit[2] = (float)++i / 4.; + + for (; i < 1024; i++) + { + split += Hist[currentHist]->bin[i]; + if (split > 7 * numpix / 8) + break; + } + tmpshadowSplit[3] = (float)++i / 4.; + + for (; i < 1024; i++) + { + split += Hist[currentHist]->bin[i]; + } + + tmpshadowSplit[0] = (float)(Hist[currentHist]->bin[1024] - 1) / 4.; + tmpshadowSplit[4] = (float)(Hist[currentHist]->bin[1025] + 1) / 4.; + printf("numpix is %d\n", numpix); + printf("total : %d\n", split); + printf("split 0 : %f\n", tmpshadowSplit[1]); + printf("split 1 : %f\n", tmpshadowSplit[2]); + printf("split 2 : %f\n", tmpshadowSplit[3]); + printf("min %f max %f\n", tmpshadowSplit[0], tmpshadowSplit[4]); + currentHist = (currentHist + 1) % 2; + }*/ + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ssbo[currentCBB]); +// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssboSplit[currentHist]); + for (unsigned i = 0; i < 4; i++) + { + CBB[currentCBB][i].xmin = CBB[currentCBB][i].ymin = CBB[currentCBB][i].zmin = 1000; + CBB[currentCBB][i].xmax = CBB[currentCBB][i].ymax = CBB[currentCBB][i].zmax = -1000; + } +// memset(Hist[currentHist], 0, sizeof(Histogram)); +// Hist[currentHist]->mindepth = 3000; + glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); + glUseProgram(FullScreenShader::LightspaceBoundingBoxShader::getInstance()->Program); + FullScreenShader::LightspaceBoundingBoxShader::getInstance()->SetTextureUnits(getDepthStencilTexture()); + FullScreenShader::LightspaceBoundingBoxShader::getInstance()->setUniforms(m_suncam->getViewMatrix(), tmpshadowSplit[1], tmpshadowSplit[2], tmpshadowSplit[3], tmpshadowSplit[4]); + glDispatchCompute((int)width / 64, (int)height / 64, 1); + +/* glUseProgram(FullScreenShader::DepthHistogramShader::getInstance()->Program); + FullScreenShader::DepthHistogramShader::getInstance()->SetTextureUnits(getDepthStencilTexture()); + FullScreenShader::DepthHistogramShader::getInstance()->setUniforms(); + glDispatchCompute((int)width / 32, (int)height / 32, 1);*/ + + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + LightcoordBBFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + currentCBB = (currentCBB + 1) % 2; + +} + + void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, size_t width, size_t height) { + if (irr_driver->supportsSDSM()) + UpdateSplitAndLightcoordRangeFromComputeShaders(width, height); static_cast(m_scene_manager)->OnAnimate(os::Timer::getTime()); camnode->render(); irr_driver->setProjMatrix(irr_driver->getVideoDriver()->getTransform(video::ETS_PROJECTION)); @@ -656,17 +799,17 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz const float oldnear = camnode->getNearValue(); float FarValues[] = { - 6., - 21., - 55., - 150., + shadowSplit[1], + shadowSplit[2], + shadowSplit[3], + shadowSplit[4], }; float NearValues[] = { - oldnear, - 5., - 20., - 50., + shadowSplit[0], + shadowSplit[1], + shadowSplit[2], + shadowSplit[3] }; @@ -697,9 +840,12 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz // Build the 3 ortho projection (for the 3 shadow resolution levels) for (unsigned i = 0; i < 4; i++) { - camnode->setFarValue(FarValues[i]); - camnode->setNearValue(NearValues[i]); - camnode->render(); + if (!irr_driver->supportsSDSM()) + { + camnode->setFarValue(FarValues[i]); + camnode->setNearValue(NearValues[i]); + camnode->render(); + } const scene::SViewFrustum *frustrum = camnode->getViewFrustum(); float tmp[24] = { frustrum->getFarLeftDown().X, @@ -737,7 +883,6 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz core::aabbox3df box = smallcambox; box = box.intersect(trackbox); - std::vector vectors; vectors.push_back(frustrum->getFarLeftDown()); vectors.push_back(frustrum->getFarLeftUp()); @@ -748,19 +893,24 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz vectors.push_back(frustrum->getNearRightDown()); vectors.push_back(frustrum->getNearRightUp()); -/* SunCamViewMatrix.transformBoxEx(trackbox); - SunCamViewMatrix.transformBoxEx(box); + core::matrix4 tmp_matrix; - core::vector3df extent = box.getExtent(); - const float w = fabsf(extent.X); - const float h = fabsf(extent.Y); - float z = box.MaxEdge.Z; + if (irr_driver->supportsSDSM()){ + float left = CBB[currentCBB][i].xmin / 4 - 2; + float right = CBB[currentCBB][i].xmax / 4 + 2; + float up = CBB[currentCBB][i].ymin / 4 - 2; + float down = CBB[currentCBB][i].ymax / 4 + 2; - // Snap to texels - const float units_per_w = w / 1024; - const float units_per_h = h / 1024;*/ + // Prevent Matrix without extend + if (left != right && up != down) + tmp_matrix.buildProjectionMatrixOrthoLH(left, right, + down, up, + CBB[currentCBB][i].zmin / 4 - 100, CBB[currentCBB][i].zmax / 4 + 2); + } + else + tmp_matrix = getTighestFitOrthoProj(SunCamViewMatrix, vectors); - m_shadow_camnodes[i]->setProjectionMatrix(getTighestFitOrthoProj(SunCamViewMatrix, vectors) , true); + m_shadow_camnodes[i]->setProjectionMatrix(tmp_matrix , true); m_shadow_camnodes[i]->render(); sun_ortho_matrix.push_back(getVideoDriver()->getTransform(video::ETS_PROJECTION) * getVideoDriver()->getTransform(video::ETS_VIEW)); diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 9e08d1f83..0093dd2f7 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1629,7 +1629,7 @@ namespace FullScreenShader // Use 8 to circumvent a catalyst bug when binding sampler AssignSamplerNames(Program, 0, "ntex", 1, "dtex", 8, "shadowtex"); - AssignUniforms("direction", "col"); + AssignUniforms("split0", "split1", "split2", "splitmax", "direction", "col"); } RadianceHintsConstructionShader::RadianceHintsConstructionShader() @@ -1809,6 +1809,29 @@ namespace FullScreenShader AssignSamplerNames(Program, 0, "texture"); } + + LightspaceBoundingBoxShader::LightspaceBoundingBoxShader() + { + Program = LoadProgram(OBJECT, + GL_COMPUTE_SHADER, file_manager->getAsset("shaders/Lightspaceboundingbox.comp").c_str(), + GL_COMPUTE_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str()); + AssignSamplerNames(Program, 0, "depth"); + AssignUniforms("SunCamMatrix", "split0", "split1", "split2", "splitmax"); + GLuint block_idx = glGetProgramResourceIndex(Program, GL_SHADER_STORAGE_BLOCK, "BoundingBoxes"); + glShaderStorageBlockBinding(Program, block_idx, 2); + } + + DepthHistogramShader::DepthHistogramShader() + { + Program = LoadProgram(OBJECT, + GL_COMPUTE_SHADER, file_manager->getAsset("shaders/depthhistogram.comp").c_str(), + GL_COMPUTE_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str()); + AssignSamplerNames(Program, 0, "depth"); + + GLuint block_idx = glGetProgramResourceIndex(Program, GL_SHADER_STORAGE_BLOCK, "Histogram"); + glShaderStorageBlockBinding(Program, block_idx, 1); + } + GlowShader::GlowShader() { Program = LoadProgram(OBJECT, diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 0a44751af..2c1928cd6 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -409,7 +409,7 @@ public: EnvMapShader(); }; -class ShadowedSunLightShader : public ShaderHelperSingleton, public TextureRead +class ShadowedSunLightShader : public ShaderHelperSingleton, public TextureRead { public: ShadowedSunLightShader(); @@ -524,6 +524,18 @@ public: LinearizeDepthShader(); }; +class LightspaceBoundingBoxShader : public ShaderHelperSingleton, public TextureRead < Nearest_Filtered > +{ +public: + LightspaceBoundingBoxShader(); +}; + +class DepthHistogramShader : public ShaderHelperSingleton, public TextureRead +{ +public: + DepthHistogramShader(); +}; + class GlowShader : public ShaderHelperSingleton, public TextureRead { public: diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 2f13521b3..4a6060fe5 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -77,6 +77,9 @@ void Skidding::reset() m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); m_kart->getControls().m_skid = KartControl::SC_NONE; + + btVector3 rot(0, 0, 0); + m_kart->getVehicle()->setTimedRotation(0, rot); } // reset // ---------------------------------------------------------------------------- @@ -120,8 +123,8 @@ void Skidding::updateSteering(float steer, float dt) break; case SKID_BREAK: m_real_steering = steer; - if (m_visual_rotation > 0.05f) m_visual_rotation -= 0.05f; - else if (m_visual_rotation < -0.05f) m_visual_rotation += 0.05f; + if (m_visual_rotation > 0.1f) m_visual_rotation -= 0.1f; + else if (m_visual_rotation < -0.1f) m_visual_rotation += 0.1f; else { reset(); @@ -212,7 +215,7 @@ void Skidding::update(float dt, bool is_on_ground, } // No skidding backwards or while stopped - if(m_kart->getSpeed() < 0.001f && + if(m_kart->getSpeed() < m_min_skid_speed && m_skid_state != SKID_NONE && m_skid_state != SKID_BREAK) { m_skid_state = SKID_BREAK; @@ -355,7 +358,6 @@ void Skidding::update(float dt, bool is_on_ground, } case SKID_BREAK: { - updateSteering(steering, dt); break; } case SKID_ACCUMULATE_LEFT: diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 503f22f0c..7afd4c198 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -420,7 +420,7 @@ void btKart::updateVehicle( btScalar step ) if(-v_down.getY() > max_compensate_speed) { btVector3 impulse = down * (-v_down.getY() - max_compensate_speed) - / m_chassisBody->getInvMass(); + / m_chassisBody->getInvMass()*0.5f; //float v_old = m_chassisBody->getLinearVelocity().getY(); //float x = m_wheelInfo[0].m_raycastInfo.m_isInContact ? m_wheelInfo[0].m_raycastInfo.m_contactPointWS.getY() : -100; m_chassisBody->applyCentralImpulse(impulse); diff --git a/src/race/grand_prix_data.cpp b/src/race/grand_prix_data.cpp index e1c7d8f25..cc91402b1 100644 --- a/src/race/grand_prix_data.cpp +++ b/src/race/grand_prix_data.cpp @@ -106,6 +106,9 @@ void GrandPrixData::changeTrackNumber(const unsigned int number_of_tracks, // Ignore no-racing tracks: if(!track->isRaceTrack()) continue; + + if (PlayerManager::getCurrentPlayer()->isLocked(track->getIdent())) + continue; // Only add tracks that are not already picked. if(std::find(m_tracks.begin(), m_tracks.end(), track->getIdent())== @@ -129,10 +132,27 @@ void GrandPrixData::changeTrackNumber(const unsigned int number_of_tracks, const Track *track = track_manager->getTrack(track_index); std::string id = track->getIdent(); + + if (PlayerManager::getCurrentPlayer()->isLocked(track->getIdent())) + continue; + + bool is_already_added = false; + for (unsigned int i = 0; i < m_tracks.size(); i++) + { + if (m_tracks[i] == id) + { + is_already_added = true; + break; + } + } + + if (!is_already_added) + { + m_tracks.push_back(id); + m_laps.push_back(track->getDefaultNumberOfLaps()); + m_reversed.push_back(false); // This will be changed later in the code + } - m_tracks.push_back(id); - m_laps.push_back(track->getDefaultNumberOfLaps()); - m_reversed.push_back(false); // This will be changed later in the code track_indices.erase(track_indices.begin()+index); } } diff --git a/src/states_screens/gp_info_screen.cpp b/src/states_screens/gp_info_screen.cpp index db587f75c..f08911cb1 100644 --- a/src/states_screens/gp_info_screen.cpp +++ b/src/states_screens/gp_info_screen.cpp @@ -59,6 +59,7 @@ GPInfoScreen::GPInfoScreen() : Screen("gp_info.stkgui") // Necessary to test if loadedFroMFile() was executed (in setGP) m_reverse_spinner = NULL; m_screenshot_widget = NULL; + m_max_num_tracks = 0; } // GPInfoScreen // ---------------------------------------------------------------------------- @@ -192,16 +193,12 @@ void GPInfoScreen::init() else m_group_name = stringc(m_group_spinner->getStringValue().c_str()).c_str(); - // If there are more tracks selected atm as in the group (which can - // happen if the group has been changed since last time this screen - // was shown), adjust it: - int max_num_tracks = m_group_name=="all" - ? track_manager->getNumberOfRaceTracks() - : (int)track_manager->getTracksInGroup(m_group_name).size(); - m_num_tracks_spinner->setMax(max_num_tracks); - if(m_num_tracks_spinner->getValue() > max_num_tracks) + m_max_num_tracks = getMaxNumTracks(m_group_name); + + m_num_tracks_spinner->setMax(m_max_num_tracks); + if(m_num_tracks_spinner->getValue() > m_max_num_tracks) { - m_num_tracks_spinner->setValue(max_num_tracks); + m_num_tracks_spinner->setValue(m_max_num_tracks); } // Now create the random GP: @@ -330,16 +327,11 @@ void GPInfoScreen::eventCallback(Widget *, const std::string &name, { m_group_name = stringc(m_group_spinner->getStringValue()).c_str(); - // Update the maximum for the number of tracks since it's depending on - // the current track. The current value in the Number-of-tracks-spinner - // has to be updated, since otherwise the displayed (and used) value - // can be bigger than the maximum. (Might be a TODO to fix this) - int max_num_tracks = m_group_name=="all" - ? track_manager->getNumberOfRaceTracks() - : (int)track_manager->getTracksInGroup(m_group_name).size(); - m_num_tracks_spinner->setMax(max_num_tracks); - if (m_num_tracks_spinner->getValue() > max_num_tracks) - m_num_tracks_spinner->setValue(max_num_tracks); + m_max_num_tracks = getMaxNumTracks(m_group_name); + + m_num_tracks_spinner->setMax(m_max_num_tracks); + if (m_num_tracks_spinner->getValue() > m_max_num_tracks) + m_num_tracks_spinner->setValue(m_max_num_tracks); // Create a new (i.e. with new tracks) random gp, since the old // tracks might not all belong to the newly selected group. @@ -389,3 +381,41 @@ void GPInfoScreen::onUpdate(float dt) m_screenshot_widget->setImage(file, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); m_screenshot_widget->m_properties[PROP_ICON] = file; } // onUpdate + +/** Get number of available tracks for random GPs + */ +int GPInfoScreen::getMaxNumTracks(std::string group) +{ + int max_num_tracks = 0; + + if (group == "all") + { + for (unsigned int i = 0; i < track_manager->getNumberOfTracks(); i++) + { + std::string id = track_manager->getTrack(i)->getIdent(); + + if (!PlayerManager::getCurrentPlayer()->isLocked(id) && + track_manager->getTrack(i)->isRaceTrack()) + { + max_num_tracks++; + } + } + } + else + { + std::vector tracks = track_manager->getTracksInGroup(group); + + for (unsigned int i = 0; i < tracks.size(); i++) + { + std::string id = track_manager->getTrack(tracks[i])->getIdent(); + + if (!PlayerManager::getCurrentPlayer()->isLocked(id) && + track_manager->getTrack(tracks[i])->isRaceTrack()) + { + max_num_tracks++; + } + } + } + + return max_num_tracks; +} diff --git a/src/states_screens/gp_info_screen.hpp b/src/states_screens/gp_info_screen.hpp index 039936e61..2e2149c04 100644 --- a/src/states_screens/gp_info_screen.hpp +++ b/src/states_screens/gp_info_screen.hpp @@ -53,6 +53,12 @@ private: /** The currently selected group name. */ std::string m_group_name; + + /** Number of available tracks */ + int m_max_num_tracks; + + /** Get number of available tracks for random GPs */ + int getMaxNumTracks(std::string group); protected: // Necessary for RandomGPInfoScreen GUIEngine::IconButtonWidget* m_screenshot_widget; diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 87289896b..14a1f4aaf 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -419,6 +419,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, PlayerProfile *player = PlayerManager::getCurrentPlayer(); if (player->isFirstTime()) { + CutsceneWorld::setUseDuration(true); StateManager::get()->enterGameState(); race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts( 0 ); diff --git a/src/states_screens/options_screen_video.cpp b/src/states_screens/options_screen_video.cpp index c6c3a121c..561b30144 100644 --- a/src/states_screens/options_screen_video.cpp +++ b/src/states_screens/options_screen_video.cpp @@ -97,7 +97,7 @@ static GFXPreset GFX_PRESETS[] = { true /* light */, 2 /* shadow */, true /* bloom */, true /* motionblur */, true /* lightshaft */, true /* glow */, true /* mlaa */, true /* ssao */, true /* weather */, - true /* animatedScenery */, 2 /* animatedCharacters */, 8 /* anisotropy */, + true /* animatedScenery */, 2 /* animatedCharacters */, 16 /* anisotropy */, true /* depth of field */, true /* global illumination */ } }; diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index e9153d64f..bd59348e5 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -261,6 +261,7 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, StateManager::get()->getActivePlayer(playerID)->setKart(NULL); World::deleteWorld(); + CutsceneWorld::setUseDuration(true); StateManager::get()->enterGameState(); race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE); race_manager->setNumKarts( 0 );