This commit is contained in:
hiker 2016-10-21 08:31:11 +11:00
commit df21ba55b4
74 changed files with 1379 additions and 381 deletions

BIN
data/gui/heart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -3,6 +3,7 @@ layout(bindless_sampler) uniform sampler2D tex;
#else
uniform sampler2D tex;
#endif
uniform float custom_alpha;
in vec2 uv;
in vec4 color;
@ -18,5 +19,5 @@ void main()
Color.xyz *= pow(color.xyz, vec3(2.2));
Color.a *= color.a;
// Premultiply alpha
FragColor = vec4(Color.rgb * Color.a, Color.a);
FragColor = vec4(Color.rgb * (Color.a * custom_alpha), Color.a * custom_alpha);
}

View File

@ -899,7 +899,7 @@ bool CIrrDeviceLinux::createWindow()
WMCheck, 0L, 1L, False, XA_WINDOW,
&type, &form, &len, &remain,
(unsigned char **)&list);
XFree(list);
bool netWM = (s == Success) && len;
attributes.override_redirect = !netWM && CreationParams.Fullscreen;

View File

@ -1,5 +1,5 @@
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
# This will then trigger a new cmake run automatically.
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")

View File

@ -424,7 +424,7 @@ void SFXOpenAL::setPosition(const Vec3 &position)
{
// Don't send a position command to the thread if the sound is not playing
// (or sfx disabled or the sound was not initialised correctly)
// if (m_status != SFX_PLAYING|| !SFXManager::get()->sfxAllowed()) return;
if (!SFXManager::get()->sfxAllowed()) return;
SFXManager::get()->queue(SFXManager::SFX_POSITION, this, position);
} // setPosition

View File

@ -34,14 +34,14 @@
// ============================================================================
class Primitive2DList : public TextureShader<Primitive2DList, 1>
class Primitive2DList : public TextureShader<Primitive2DList, 1, float>
{
public:
Primitive2DList()
{
loadProgram(OBJECT, GL_VERTEX_SHADER, "primitive2dlist.vert",
GL_FRAGMENT_SHADER, "transparent.frag");
assignUniforms();
assignUniforms("custom_alpha");
assignSamplerNames(0, "tex", ST_BILINEAR_FILTERED);
} // Primitive2DList
}; //Primitive2DList
@ -703,7 +703,7 @@ void draw2DVertexPrimitiveList(video::ITexture *tex, const void* vertices,
VertexUtils::bindVertexArrayAttrib(vType);
Primitive2DList::getInstance()->use();
Primitive2DList::getInstance()->setUniforms();
Primitive2DList::getInstance()->setUniforms(1.0f);
compressTexture(tex, false);
Primitive2DList::getInstance()->setTextureUnits(getTextureGLuint(tex));
glDrawElements(GL_TRIANGLE_FAN, primitiveCount, GL_UNSIGNED_SHORT, 0);

View File

@ -245,8 +245,11 @@ void Camera::setMode(Mode mode)
{
Vec3 start_offset(0, 1.6f, -3);
Vec3 current_position = m_kart->getTrans()(start_offset);
Vec3 target_position = m_kart->getTrans()(Vec3(0, 0, 1));
// Don't set position and target the same, otherwise
// nan values will be calculated in ViewArea of camera
m_camera->setPosition(current_position.toIrrVector());
m_camera->setTarget(m_camera->getPosition());
m_camera->setTarget(target_position.toIrrVector());
}
m_mode = mode;
@ -291,9 +294,9 @@ void Camera::setInitialTransform()
// direction till smoothMoveCamera has corrected this. Setting target
// to position doesn't make sense, but smoothMoves will adjust the
// value before the first frame is rendered
m_camera->setTarget(m_camera->getPosition());
Vec3 target_position = m_kart->getTrans()(Vec3(0, 0, 1));
m_camera->setTarget(target_position.toIrrVector());
m_camera->setRotation(core::vector3df(0, 0, 0));
m_camera->setRotation( core::vector3df( 0.0f, 0.0f, 0.0f ) );
m_camera->setFOV(m_fov);
} // setInitialTransform

View File

@ -160,6 +160,7 @@ IrrDriver::~IrrDriver()
}
assert(m_device != NULL);
cleanUnicolorTextures();
m_device->drop();
m_device = NULL;
m_modes.clear();
@ -869,11 +870,11 @@ void IrrDriver::applyResolutionSettings()
UserConfigParams::m_prev_height) );
m_video_driver->endScene();
track_manager->removeAllCachedData();
attachment_manager->removeTextures();
delete attachment_manager;
projectile_manager->removeTextures();
ItemManager::removeTextures();
kart_properties_manager->unloadAllKarts();
powerup_manager->unloadPowerups();
delete powerup_manager;
Referee::cleanup();
ParticleKindManager::get()->cleanup();
delete input_manager;
@ -900,11 +901,23 @@ void IrrDriver::applyResolutionSettings()
RSMPassCmd::getInstance()->kill();
GlowPassCmd::getInstance()->kill();
resetTextureTable();
// initDevice will drop the current device.
if (m_post_processing)
{
// check if we createad the OpenGL device by calling initDevice()
m_post_processing->drop();
}
cleanUnicolorTextures();
delete m_shadow_matrices;
if (CVS->isGLSL())
{
Shaders::destroy();
}
delete m_spherical_harmonics;
// initDevice will drop the current device.
initDevice();
font_manager = new FontManager();
@ -917,6 +930,8 @@ void IrrDriver::applyResolutionSettings()
material_manager->loadMaterial();
input_manager = new InputManager ();
input_manager->setMode(InputManager::MENU);
powerup_manager = new PowerupManager();
attachment_manager = new AttachmentManager();
GUIEngine::addLoadingIcon(
irr_driver->getTexture(file_manager
@ -2649,8 +2664,6 @@ scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos,
else
light = new SunNode(m_scene_manager, parent, r, g, b);
light->grab();
light->setPosition(pos);
light->updateAbsolutePosition();
@ -2680,9 +2693,7 @@ scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos,
void IrrDriver::clearLights()
{
u32 i;
const u32 max = (int)m_lights.size();
for (i = 0; i < max; i++)
for (unsigned int i = 0; i < m_lights.size(); i++)
{
m_lights[i]->drop();
}

View File

@ -740,6 +740,26 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
m->setTexture(7, colorization_mask_tex);
}
if (race_manager->getReverseTrack() &&
m_mirror_axis_when_reverse != ' ')
{
if (m_mirrorred_mesh_buffers.find((void*)mb) == m_mirrorred_mesh_buffers.end())
{
m_mirrorred_mesh_buffers[(void*)mb] = true;
//irr::video::S3DVertex* mbVertices = (video::S3DVertex*)mb->getVertices();
for (unsigned int i = 0; i < mb->getVertexCount(); i++)
{
core::vector2df &tc = mb->getTCoords(i);
if (m_mirror_axis_when_reverse == 'V')
tc.Y = 1 - tc.Y;
else
tc.X = 1 - tc.X;
}
}
} // reverse track and texture needs mirroring
switch (m_shader_type)
{
case SHADERTYPE_SOLID_UNLIT:
@ -986,21 +1006,6 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
}
#endif
if (race_manager->getReverseTrack() &&
m_mirror_axis_when_reverse != ' ')
{
//irr::video::S3DVertex* mbVertices = (video::S3DVertex*)mb->getVertices();
for (unsigned int i = 0; i < mb->getVertexCount(); i++)
{
core::vector2df &tc = mb->getTCoords(i);
if (m_mirror_axis_when_reverse == 'V')
tc.Y = 1 - tc.Y;
else
tc.X = 1 - tc.X;
}
} // reverse track and texture needs mirroring
} // setMaterialProperties
//-----------------------------------------------------------------------------

View File

@ -162,6 +162,12 @@ private:
* the direction. */
char m_mirror_axis_when_reverse;
/**
* Associated with m_mirror_axis_when_reverse, to avoid mirroring the same material twice
* (setAllMaterialFlags can be called multiple times on the same mesh buffer)
*/
std::map<void*, bool> m_mirrorred_mesh_buffers;
ParticleKind* m_particles_effects[EMIT_KINDS_COUNT];
/** For normal maps */

View File

@ -28,6 +28,7 @@
#include <irrlicht.h>
#include <IMesh.h>
#include <IMeshBuffer.h>
#include <SSkinMeshBuffer.h>
void MeshTools::minMax3D(scene::IMesh* mesh, Vec3 *min, Vec3 *max) {
@ -444,29 +445,86 @@ scene::IMesh* MeshTools::createMeshWithTangents(scene::IMesh* mesh,
clone->recalculateBoundingBox();
if (calculate_tangents)
recalculateTangents(clone, recalculate_normals, smooth, angle_weighted);
int mbcount = clone->getMeshBufferCount();
for (int i = 0; i < mbcount; i++)
{
scene::IMeshBuffer* mb = clone->getMeshBuffer(i);
for (u32 t = 0; t < video::MATERIAL_MAX_TEXTURES; t++)
{
video::ITexture* texture = mb->getMaterial().TextureLayer[t].Texture;
if (texture != NULL)
texture->grab();
}
}
scene::IMeshCache* meshCache = irr_driver->getSceneManager()->getMeshCache();
io::SNamedPath path = meshCache->getMeshName(mesh);
irr_driver->removeMeshFromCache(mesh);
if (path.getPath() == "")
{
// This mesh is not in irrlicht cache, drop it directly
assert(mesh->getReferenceCount() == 1);
mesh->drop();
return clone;
}
else
{
// Cache the calcuated tangent mesh with path
irr_driver->removeMeshFromCache(mesh);
scene::SAnimatedMesh* amesh = new scene::SAnimatedMesh(clone);
clone->drop();
meshCache->addMesh(path, amesh);
if (World::getWorld())
{
irr_driver->grabAllTextures(amesh);
World::getWorld()->getTrack()->addCachedMesh(amesh);
return amesh;
}
amesh->drop();
return amesh;
}
scene::SAnimatedMesh* amesh = new scene::SAnimatedMesh(clone);
meshCache->addMesh(path, amesh);
World::getWorld()->getTrack()->addCachedMesh(amesh);
return clone;
}
void MeshTools::createSkinnedMeshWithTangents(scene::ISkinnedMesh* mesh,
bool(*predicate)(scene::IMeshBuffer*))
{
core::array<scene::SSkinMeshBuffer*>& all_mb = mesh->getMeshBuffers();
const int all_mb_size = all_mb.size();
for (int i = 0; i < all_mb_size; i++)
{
scene::SSkinMeshBuffer* mb = all_mb[i];
if (mb && predicate(mb))
{
mb->convertToTangents();
const int index_count = mb->getIndexCount();
uint16_t* idx = mb->getIndices();
video::S3DVertexTangents* v =
(video::S3DVertexTangents*)mb->getVertices();
for (int i = 0; i < index_count; i += 3)
{
calculateTangents(
v[idx[i+0]].Normal,
v[idx[i+0]].Tangent,
v[idx[i+0]].Binormal,
v[idx[i+0]].Pos,
v[idx[i+1]].Pos,
v[idx[i+2]].Pos,
v[idx[i+0]].TCoords,
v[idx[i+1]].TCoords,
v[idx[i+2]].TCoords);
calculateTangents(
v[idx[i+1]].Normal,
v[idx[i+1]].Tangent,
v[idx[i+1]].Binormal,
v[idx[i+1]].Pos,
v[idx[i+2]].Pos,
v[idx[i+0]].Pos,
v[idx[i+1]].TCoords,
v[idx[i+2]].TCoords,
v[idx[i+0]].TCoords);
calculateTangents(
v[idx[i+2]].Normal,
v[idx[i+2]].Tangent,
v[idx[i+2]].Binormal,
v[idx[i+2]].Pos,
v[idx[i+0]].Pos,
v[idx[i+1]].Pos,
v[idx[i+2]].TCoords,
v[idx[i+0]].TCoords,
v[idx[i+1]].TCoords);
}
}
}
}

View File

@ -21,7 +21,7 @@
namespace irr
{
namespace scene { class IMesh; class IMeshBuffer; }
namespace scene { class IMesh; class IMeshBuffer; class ISkinnedMesh; }
}
using namespace irr;
@ -37,8 +37,14 @@ namespace MeshTools
bool isNormalMap(scene::IMeshBuffer* mb);
// Copied from irrlicht
scene::IMesh* createMeshWithTangents(scene::IMesh* mesh, bool(*predicate)(scene::IMeshBuffer*),
bool recalculateNormals = false, bool smooth = false, bool angleWeighted = false, bool calculateTangents = true);
scene::IMesh* createMeshWithTangents(scene::IMesh* mesh,
bool(*predicate)(scene::IMeshBuffer*), bool recalculateNormals = false,
bool smooth = false, bool angleWeighted = false,
bool calculateTangents = true);
void createSkinnedMeshWithTangents(scene::ISkinnedMesh* mesh,
bool(*predicate)(scene::IMeshBuffer*));
} // MeshTools
#endif

View File

@ -1555,14 +1555,25 @@ static video::ITexture *displaceTex = 0;
// ----------------------------------------------------------------------------
void IrrDriver::renderTransparent()
{
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glDisable(GL_CULL_FACE);
irr_driver->setPhase(TRANSPARENT_PASS);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glEnable(GL_CULL_FACE);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
renderTransparenPass<Shaders::TransparentShader, video::EVT_STANDARD, 3, 2, 1>(
TexUnits(RenderGeometry::TexUnit(0, true)),
ListGhostKart::getInstance());
renderTransparenPass<Shaders::TransparentShader, video::EVT_TANGENTS, 3, 2, 1>(
TexUnits(RenderGeometry::TexUnit(0, true)),
ListGhostKartTangents::getInstance());
glDepthMask(GL_FALSE);
glDisable(GL_CULL_FACE);
for (unsigned i = 0; i < ImmediateDrawList::getInstance()->size(); i++)
ImmediateDrawList::getInstance()->at(i)->render();
@ -1586,11 +1597,11 @@ void IrrDriver::renderTransparent()
{
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
renderTransparenPass<Shaders::TransparentShader,
video::EVT_STANDARD, 2, 1>(
video::EVT_STANDARD, 3, 2, 1>(
TexUnits(RenderGeometry::TexUnit(0, true)),
ListBlendTransparent::getInstance());
glBlendFunc(GL_ONE, GL_ONE);
renderTransparenPass<Shaders::TransparentShader, video::EVT_STANDARD, 2, 1>(
renderTransparenPass<Shaders::TransparentShader, video::EVT_STANDARD, 3, 2, 1>(
TexUnits(RenderGeometry::TexUnit(0, true)),
ListAdditiveTransparent::getInstance());
}

View File

@ -365,7 +365,7 @@ Shaders::TransparentShader::TransparentShader()
{
loadProgram(OBJECT, GL_VERTEX_SHADER, "object_pass.vert",
GL_FRAGMENT_SHADER, "transparent.frag");
assignUniforms("ModelMatrix", "TextureMatrix");
assignUniforms("ModelMatrix", "TextureMatrix", "custom_alpha");
assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED);
} // TransparentShader

View File

@ -120,7 +120,8 @@ public:
// ========================================================================
class TransparentShader : public TextureShader<TransparentShader, 1,
core::matrix4, core::matrix4 >
core::matrix4, core::matrix4,
float >
{
public:
TransparentShader();

View File

@ -126,15 +126,34 @@ ShadowMatrices::ShadowMatrices()
m_shadow_cam_nodes[1] = NULL;
m_shadow_cam_nodes[2] = NULL;
m_shadow_cam_nodes[3] = NULL;
m_rsm_map_available = false;
m_rsm_matrix_initialized = false;
} // ShadowMatrices
// ----------------------------------------------------------------------------
ShadowMatrices::~ShadowMatrices()
{
resetShadowCamNodes();
m_sun_cam->drop();
} // ~ShadowMatrices
// ----------------------------------------------------------------------------
void ShadowMatrices::resetShadowCamNodes()
{
for (unsigned i = 0; i < 4; i++)
{
if (m_shadow_cam_nodes[i])
{
m_shadow_cam_nodes[i]->drop();
m_shadow_cam_nodes[i] = NULL;
}
}
} // resetShadowCamNodes
// ----------------------------------------------------------------------------
void ShadowMatrices::addLight(const core::vector3df &pos)
{
m_sun_cam->setPosition(pos);
m_sun_cam->updateAbsolutePosition();
m_rsm_matrix_initialized = false;
} // addLight
// ----------------------------------------------------------------------------
@ -334,17 +353,6 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca
if (World::getWorld() && World::getWorld()->getTrack())
{
// Compute track extent
btVector3 btmin, btmax;
if (World::getWorld()->getTrack()->getPtrTriangleMesh())
{
World::getWorld()->getTrack()->getTriangleMesh().getCollisionShape()
.getAabb(btTransform::getIdentity(), btmin, btmax);
}
const Vec3 vmin = btmin, vmax = btmax;
core::aabbox3df trackbox(vmin.toIrrVector(), vmax.toIrrVector() -
core::vector3df(0, 30, 0));
float FarValues[] =
{
ShadowMatrices::m_shadow_split[1],
@ -411,8 +419,16 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca
}
// Rsm Matrix and camera
if (!m_rsm_matrix_initialized)
if (!m_rsm_matrix_initialized &&
World::getWorld()->getTrack()->getPtrTriangleMesh())
{
// Compute track extent
Vec3 vmin, vmax;
World::getWorld()->getTrack()->getTriangleMesh().getCollisionShape()
.getAabb(btTransform::getIdentity(), vmin, vmax);
core::aabbox3df trackbox(vmin.toIrrVector(), vmax.toIrrVector() -
core::vector3df(0, 30, 0));
if (trackbox.MinEdge.X != trackbox.MaxEdge.X &&
trackbox.MinEdge.Y != trackbox.MaxEdge.Y &&
// Cover the case where sun_cam_view_matrix is null

View File

@ -61,6 +61,7 @@ private:
public:
ShadowMatrices();
~ShadowMatrices();
void computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode,
unsigned int width, unsigned int height);
@ -69,10 +70,7 @@ public:
void renderShadowsDebug();
// ------------------------------------------------------------------------
void resetShadowCamNodes()
{
memset(m_shadow_cam_nodes, 0, 4 * sizeof(void*));
} // resetShadowCamNodes
void resetShadowCamNodes();
// ------------------------------------------------------------------------
scene::ICameraSceneNode** getShadowCamNodes()
{

View File

@ -165,11 +165,14 @@ void STKAnimatedMesh::updateNoGL()
}
else if (mesh.m_render_info != NULL && mesh.m_render_info->isTransparent())
{
TransparentMesh[TM_ADDITIVE].push_back(&mesh);
if (mesh.VAOType == video::EVT_TANGENTS)
TransparentMesh[TM_GHOST_KART_TANGENTS].push_back(&mesh);
else
TransparentMesh[TM_GHOST_KART].push_back(&mesh);
}
else
{
Material::ShaderType MatType = material->getShaderType();// getMeshMaterialFromType(type, mb->getVertexType(), material);
Material::ShaderType MatType = getMeshMaterialFromType(type, mb->getVertexType(), material, NULL);
MeshSolidMaterial[MatType].push_back(&mesh);
}
}

View File

@ -37,6 +37,8 @@ enum TransparentMaterial
TM_DEFAULT,
TM_ADDITIVE,
TM_DISPLACEMENT,
TM_GHOST_KART,
TM_GHOST_KART_TANGENTS,
TM_COUNT
}; // TransparentMaterial
@ -197,13 +199,26 @@ class MiscList : public Singleton<T>, public std::vector<STK::Tuple<Args...> >
// ----------------------------------------------------------------------------
class ListBlendTransparent : public MiscList<ListBlendTransparent, GLMesh *,
core::matrix4, core::matrix4>
core::matrix4, core::matrix4,
float>
{};
// ----------------------------------------------------------------------------
class ListAdditiveTransparent : public MiscList<ListAdditiveTransparent,
GLMesh *, core::matrix4,
core::matrix4>
core::matrix4, float>
{};
// ----------------------------------------------------------------------------
class ListGhostKart : public MiscList<ListGhostKart,
GLMesh *, core::matrix4,
core::matrix4, float>
{};
// ----------------------------------------------------------------------------
class ListGhostKartTangents : public MiscList<ListGhostKartTangents,
GLMesh *, core::matrix4,
core::matrix4, float>
{};
// ----------------------------------------------------------------------------

View File

@ -207,12 +207,13 @@ void STKMeshSceneNode::updateNoGL()
GLMesh &mesh = GLmeshes[i];
Material* material = material_manager->getMaterialFor(mb->getMaterial().getTexture(0), mb);
if (mesh.m_render_info != NULL && mesh.m_render_info->isTransparent())
if (mesh.m_render_info != NULL && mesh.m_render_info->isTransparent() && !rnd->isTransparent())
{
if (!immediate_draw)
TransparentMesh[TM_ADDITIVE].push_back(&mesh);
assert(!immediate_draw);
if (mesh.VAOType == video::EVT_TANGENTS)
TransparentMesh[TM_GHOST_KART_TANGENTS].push_back(&mesh);
else
additive = true;
TransparentMesh[TM_GHOST_KART].push_back(&mesh);
}
else if (rnd->isTransparent())
{
@ -567,7 +568,7 @@ void STKMeshSceneNode::render()
#endif
Shaders::TransparentShader::getInstance()->setTextureUnits(getTextureGLuint(mesh.textures[0]));
Shaders::TransparentShader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix);
Shaders::TransparentShader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix, 1.0f);
assert(mesh.vao);
glBindVertexArray(mesh.vao);
glDrawElements(ptype, count, itype, 0);

View File

@ -340,10 +340,24 @@ handleSTKCommon(scene::ISceneNode *Node, std::vector<scene::ISceneNode *> *Immed
else
{
for (GLMesh *mesh : node->TransparentMesh[TM_DEFAULT])
pushVector(ListBlendTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix);
pushVector(ListBlendTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, 1.0f);
for (GLMesh *mesh : node->TransparentMesh[TM_ADDITIVE])
pushVector(ListAdditiveTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix);
pushVector(ListAdditiveTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, 1.0f);
}
// Use sun color to determine custom alpha for ghost karts
float custom_alpha = 1.0f;
if (World::getWorld())
{
const video::SColor& c = World::getWorld()->getTrack()->getSunColor();
float y = 0.2126f * c.getRed() + 0.7152f * c.getGreen() + 0.0722f * c.getBlue();
custom_alpha = y > 128.0f ? 0.5f : 0.35f;
}
for (GLMesh *mesh : node->TransparentMesh[TM_GHOST_KART])
pushVector(ListGhostKart::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, custom_alpha);
for (GLMesh *mesh : node->TransparentMesh[TM_GHOST_KART_TANGENTS])
pushVector(ListGhostKartTangents::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, custom_alpha);
for (GLMesh *mesh : node->TransparentMesh[TM_DISPLACEMENT])
pushVector(ListDisplacement::getInstance(), mesh, Node->getAbsoluteTransformation());
@ -606,6 +620,8 @@ void IrrDriver::PrepareDrawCalls(scene::ICameraSceneNode *camnode)
windDir = getWindDir();
ListBlendTransparent::getInstance()->clear();
ListAdditiveTransparent::getInstance()->clear();
ListGhostKart::getInstance()->clear();
ListGhostKartTangents::getInstance()->clear();
ListBlendTransparentFog::getInstance()->clear();
ListAdditiveTransparentFog::getInstance()->clear();
ListDisplacement::getInstance()->clear();

View File

@ -59,6 +59,14 @@ static std::map<int, video::ITexture*> unicolor_cache;
void resetTextureTable()
{
AlreadyTransformedTexture.clear();
}
void cleanUnicolorTextures()
{
for (std::pair<const int, video::ITexture*>& uc : unicolor_cache)
{
uc.second->drop();
}
unicolor_cache.clear();
}
@ -228,7 +236,6 @@ video::ITexture* getUnicolorTexture(const video::SColor &c)
std::map<int, video::ITexture*>::iterator it = unicolor_cache.find(c.color);
if (it != unicolor_cache.end())
{
it->second->grab();
return it->second;
}
else
@ -240,10 +247,12 @@ video::ITexture* getUnicolorTexture(const video::SColor &c)
c.color
};
video::IImage *img = irr_driver->getVideoDriver()->createImageFromData(video::ECF_A8R8G8B8, core::dimension2d<u32>(2, 2), tmp);
img->grab();
std::stringstream name;
name << "color" << c.color;
video::ITexture* tex = irr_driver->getVideoDriver()->addTexture(name.str().c_str(), img);
tex->grab();
// Only let our map hold the unicolor texture
irr_driver->getVideoDriver()->removeTexture(tex);
unicolor_cache[c.color] = tex;
img->drop();
return tex;

View File

@ -26,6 +26,7 @@
GLuint getTextureGLuint(irr::video::ITexture *tex);
GLuint getDepthTexture(irr::video::ITexture *tex);
void resetTextureTable();
void cleanUnicolorTextures();
void compressTexture(irr::video::ITexture *tex, bool srgb, bool premul_alpha = false);
bool loadCompressedTexture(const std::string& compressed_tex);
void saveCompressedTexture(const std::string& compressed_tex);

View File

@ -50,6 +50,7 @@ IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_TEXTURE_ASPECT_RATIO, false,
m_old_rtt_provider = NULL;
m_rotation_mode = ROTATE_OFF;
m_render_info = new RenderInfo();
m_angle = 0;
// so that the base class doesn't complain there is no icon defined
m_properties[PROP_ICON]="gui/main_help.png";
@ -85,8 +86,6 @@ void ModelViewWidget::add()
*/
GUIEngine::needsUpdate.push_back(this);
angle = 0;
} // add
// -----------------------------------------------------------------------------
@ -133,28 +132,28 @@ void ModelViewWidget::update(float delta)
if (m_rotation_mode == ROTATE_CONTINUOUSLY)
{
angle += delta*m_rotation_speed;
if (angle > 360) angle -= 360;
m_angle += delta*m_rotation_speed;
if (m_angle > 360) m_angle -= 360;
}
else if (m_rotation_mode == ROTATE_TO)
{
// check if we should rotate clockwise or counter-clockwise to reach the target faster
// (taking wrap-arounds into account)
const int angle_distance_from_end = (int)(360 - angle);
const int target_distance_from_end = (int)(360 - angle);
const int angle_distance_from_end = (int)(360 - m_angle);
const int target_distance_from_end = (int)(360 - m_angle);
int distance_with_positive_rotation;
int distance_with_negative_rotation;
if (angle < m_rotation_target)
if (m_angle < m_rotation_target)
{
distance_with_positive_rotation = (int)(m_rotation_target - angle);
distance_with_negative_rotation = (int)(angle + target_distance_from_end);
distance_with_positive_rotation = (int)(m_rotation_target - m_angle);
distance_with_negative_rotation = (int)(m_angle + target_distance_from_end);
}
else
{
distance_with_positive_rotation = (int)(angle_distance_from_end + m_rotation_target);
distance_with_negative_rotation = (int)(angle - m_rotation_target);
distance_with_negative_rotation = (int)(m_angle - m_rotation_target);
}
//Log::info("ModelViewWidget", "distance_with_positive_rotation = %d; "
@ -163,17 +162,17 @@ void ModelViewWidget::update(float delta)
if (distance_with_positive_rotation < distance_with_negative_rotation)
{
angle += m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f);
m_angle += m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f);
}
else
{
angle -= m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f);
m_angle -= m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f);
}
if (angle > 360) angle -= 360;
if (angle < 0) angle += 360;
if (m_angle > 360) m_angle -= 360;
if (m_angle < 0) m_angle += 360;
// stop rotating when target reached
if (fabsf(angle - m_rotation_target) < 2.0f) m_rotation_mode = ROTATE_OFF;
if (fabsf(m_angle - m_rotation_target) < 2.0f) m_rotation_mode = ROTATE_OFF;
}
if (CVS->isGLSL())
@ -188,7 +187,7 @@ void ModelViewWidget::update(float delta)
setupRTTScene();
}
m_rtt_main_node->setRotation(core::vector3df(0.0f, angle, 0.0f));
m_rtt_main_node->setRotation(core::vector3df(0.0f, m_angle, 0.0f));
m_rtt_main_node->setVisible(true);
@ -206,7 +205,7 @@ void ModelViewWidget::update(float delta)
m_old_rtt_provider->setupRTTScene(m_models, m_model_location, m_model_scale, m_model_frames);
}
m_texture = m_old_rtt_provider->renderToTexture(angle);
m_texture = m_old_rtt_provider->renderToTexture(m_angle);
if (m_texture == NULL)
{

View File

@ -56,7 +56,7 @@ namespace GUIEngine
RTT* m_rtt_provider;
IrrDriver::RTTProvider* m_old_rtt_provider;
float angle;
float m_angle;
bool m_rtt_unsupported;

View File

@ -43,10 +43,7 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
m_distance_2 = 1.2f;
initItem(type, xyz);
Vec3 axis = -normal.cross(Vec3(0, 1, 0));
if (axis.length() == 0)
axis = Vec3(0, 0, 1);
m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0)));
m_original_rotation = Track::createRotationFromNormal(normal);
m_rotation_angle = 0.0f;
m_original_mesh = mesh;
m_original_lowmesh = lowres_mesh;

View File

@ -29,6 +29,7 @@
#include "graphics/material_manager.hpp"
#include "io/file_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/spare_tire_ai.hpp"
#include "modes/linear_world.hpp"
#include "network/network_config.hpp"
#include "network/race_event_manager.hpp"
@ -287,6 +288,8 @@ Item* ItemManager::newItem(const Vec3& xyz, float distance,
void ItemManager::collectedItem(Item *item, AbstractKart *kart, int add_info)
{
assert(item);
// Spare tire karts don't collect items
if (dynamic_cast<SpareTireAI*>(kart->getController()) != NULL) return;
if( (item->getType() == Item::ITEM_BUBBLEGUM ||
item->getType() == Item::ITEM_BUBBLEGUM_NOLOK) && kart->isShielded())
{

View File

@ -75,6 +75,8 @@ protected:
float *radius) const;
virtual void update (float delta);
virtual void setSteering (float angle, float dt);
// ------------------------------------------------------------------------
/** Return true if AI can skid now. */
virtual bool canSkid(float steer_fraction) = 0;
public:

View File

@ -48,7 +48,6 @@ void ArenaAI::reset()
m_target_node = Graph::UNKNOWN_SECTOR;
m_current_forward_node = Graph::UNKNOWN_SECTOR;
m_current_forward_point = Vec3(0, 0, 0);
m_adjusting_side = false;
m_closest_kart = NULL;
m_closest_kart_node = Graph::UNKNOWN_SECTOR;
m_closest_kart_point = Vec3(0, 0, 0);
@ -57,6 +56,7 @@ void ArenaAI::reset()
m_mini_skid = false;
m_target_point = Vec3(0, 0, 0);
m_target_point_lc = Vec3(0, 0, 0);
m_reverse_point = Vec3(0, 0, 0);
m_time_since_last_shot = 0.0f;
m_time_since_driving = 0.0f;
m_time_since_off_road = 0.0f;
@ -74,6 +74,7 @@ void ArenaAI::reset()
/** This is the main entry point for the AI.
* It is called once per frame for each AI and determines the behaviour of
* the AI, e.g. steering, accelerating/braking, firing.
* \param dt Time step size.
*/
void ArenaAI::update(float dt)
{
@ -81,6 +82,11 @@ void ArenaAI::update(float dt)
m_controls->setLookBack(false);
m_controls->setNitro(false);
// Let the function below to reset it later
m_controls->setAccel(0.0f);
m_controls->setBrake(false);
m_mini_skid = false;
// Don't do anything if there is currently a kart animations shown.
if (m_kart->getKartAnimation())
{
@ -116,14 +122,20 @@ void ArenaAI::update(float dt)
if (gettingUnstuck(dt))
return;
findClosestKart(true);
findTarget();
// After found target, convert it to local coordinate, used for skidding or
// u-turn
m_target_point_lc = m_kart->getTrans().inverse()(m_target_point);
doSkiddingTest();
configSteering();
if (!m_is_uturn)
{
m_target_point_lc = m_kart->getTrans().inverse()(m_target_point);
doSkiddingTest();
configSteering();
}
else
{
m_target_point_lc = m_kart->getTrans().inverse()(m_reverse_point);
}
useItems(dt);
if (m_kart->getSpeed() > 15.0f && !m_is_uturn && m_turn_radius > 30.0f &&
@ -159,10 +171,7 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point)
m_debug_sphere_next->setVisible(false);
#endif
// Notice: we use the point ahead of kart to determine next node,
// to compensate the time difference between steering
m_current_forward_point =
m_kart->getTrans()(Vec3(0, 0, m_kart->getKartLength()));
m_current_forward_point = m_kart->getTrans()(Vec3(0, 0, m_kart_length));
m_turn_radius = 0.0f;
std::vector<int>* test_nodes = NULL;
@ -227,7 +236,7 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point)
} // updateAimingPosition
//-----------------------------------------------------------------------------
/** This function config the steering of AI.
/** This function config the steering (\ref m_steering_angle) of AI.
*/
void ArenaAI::configSteering()
{
@ -249,9 +258,8 @@ void ArenaAI::configSteering()
#endif
if (m_target_point_lc.z() < 0)
{
// Local coordinate z < 0 == target point is behind
m_adjusting_side = m_target_point_lc.x() < 0;
m_is_uturn = true;
m_reverse_point = m_target_point;
}
else
{
@ -273,8 +281,8 @@ void ArenaAI::configSteering()
#endif
if (m_target_point_lc.z() < 0)
{
m_adjusting_side = m_target_point_lc.x() < 0;
m_is_uturn = true;
m_reverse_point = m_target_point;
}
else
{
@ -284,6 +292,11 @@ void ArenaAI::configSteering()
} // configSteering
//-----------------------------------------------------------------------------
/** Determine whether AI is stuck, by checking if it stays on the same node for
* a long period of time (see \ref m_on_node), or \ref isStuck() is true.
* \param dt Time step size.
* \return True if AI is stuck
*/
void ArenaAI::checkIfStuck(const float dt)
{
if (m_is_stuck) return;
@ -302,8 +315,7 @@ void ArenaAI::checkIfStuck(const float dt)
&& m_on_node.size() < 2 && !m_is_uturn &&
fabsf(m_kart->getSpeed()) < 3.0f) || isStuck() == true)
{
// Check whether a kart stay on the same node for a period of time
// Or crashed 3 times
// AI is stuck, reset now and try to get unstuck at next frame
m_on_node.clear();
m_time_since_driving = 0.0f;
AIBaseController::reset();
@ -323,9 +335,6 @@ void ArenaAI::checkIfStuck(const float dt)
*/
void ArenaAI::configSpeed()
{
m_controls->setAccel(0.0f);
m_controls->setBrake(false);
// A kart will not brake when the speed is already slower than this
// value. This prevents a kart from going too slow (or even backwards)
// in tight curves.
@ -348,50 +357,41 @@ void ArenaAI::configSpeed()
} // configSpeed
//-----------------------------------------------------------------------------
/** Make AI reverse so that it faces in front of the last target point.
*/
void ArenaAI::doUTurn(const float dt)
{
const float turn_side = (m_adjusting_side ? 1.0f : -1.0f);
if (fabsf(m_kart->getSpeed()) >
(m_kart->getKartProperties()->getEngineMaxSpeed() / 5)
&& m_kart->getSpeed() < 0) // Try to emulate reverse like human players
m_controls->setAccel(-0.06f);
else
m_controls->setAccel(-5.0f);
if (m_time_since_uturn >=
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 2.0f : 1.5f))
setSteering(-(turn_side), dt); // Preventing keep going around circle
else
setSteering(turn_side, dt);
float turn_angle = atan2f(m_target_point_lc.x(),
fabsf(m_target_point_lc.z()));
m_controls->setBrake(true);
setSteering(turn_angle > 0.0f ? -1.0f : 1.0f, dt);
m_time_since_uturn += dt;
if (m_target_point_lc.z() > 0 || m_time_since_uturn >
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 3.5f : 3.0f))
if ((m_target_point_lc.z() > 0 && fabsf(turn_angle) < 0.2f) ||
m_time_since_uturn > 1.5f)
{
// End U-turn until target point is in front of this AI
m_is_uturn = false;
m_time_since_uturn = 0.0f;
m_time_since_driving = 0.0f;
m_reverse_point = Vec3(0, 0, 0);
}
else
m_is_uturn = true;
} // doUTurn
//-----------------------------------------------------------------------------
/** Function to let AI get unstuck.
* \param dt Time step size.
* \return True if getting stuck is needed to be done.
*/
bool ArenaAI::gettingUnstuck(const float dt)
{
if (!m_is_stuck || m_is_uturn) return false;
resetAfterStop();
setSteering(0.0f, dt);
if (fabsf(m_kart->getSpeed()) >
(m_kart->getKartProperties()->getEngineMaxSpeed() / 5)
&& m_kart->getSpeed() < 0)
m_controls->setAccel(-0.06f);
else
m_controls->setAccel(-4.0f);
m_controls->setBrake(true);
m_time_since_reversing += dt;
@ -406,6 +406,10 @@ bool ArenaAI::gettingUnstuck(const float dt)
} // gettingUnstuck
//-----------------------------------------------------------------------------
/** Determine how AI should use its item, different \ref m_cur_difficulty will
* have a corresponding strategy.
* \param dt Time step size.
*/
void ArenaAI::useItems(const float dt)
{
m_controls->setFire(false);
@ -414,7 +418,7 @@ void ArenaAI::useItems(const float dt)
return;
// Find a closest kart again, this time we ignore difficulty
findClosestKart(false);
findClosestKart(false/*consider_difficulty*/, false/*find_sta*/);
if (!m_closest_kart) return;
Vec3 closest_kart_point_lc =
@ -566,7 +570,12 @@ void ArenaAI::useItems(const float dt)
} // useItems
//-----------------------------------------------------------------------------
void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const
/** Try to collect item in arena, if no suitable item is found, like they are
* swapped, it will follow closest kart instead.
* \param[out] aim_point Location of item.
* \param[out] target_node The node which item lied on.
*/
void ArenaAI::tryCollectItem(Vec3* aim_point, int* target_node) const
{
float distance = 999999.9f;
Item* selected = (*target_node == Graph::UNKNOWN_SECTOR ? NULL :
@ -619,18 +628,15 @@ void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const
*aim_point = m_closest_kart_point;
*target_node = m_closest_kart_node;
}
} // collectItemInArena
} // tryCollectItem
//-----------------------------------------------------------------------------
/** Determine if AI should skid: When it's close to target, but not straight
* ahead, in front of it, same steering side and with suitable difficulties
* which are in expert and supertux only.
*/
void ArenaAI::doSkiddingTest()
{
m_mini_skid = false;
// No skidding when u-turn
if (m_is_uturn) return;
// Skid when close to target, but not straight ahead, in front of it, same
// steering side and with suitable difficulties.
const float abs_angle = atan2f(fabsf(m_target_point_lc.x()),
fabsf(m_target_point_lc.z()));
if ((m_cur_difficulty == RaceManager::DIFFICULTY_HARD ||
@ -649,7 +655,7 @@ void ArenaAI::doSkiddingTest()
/** Determine if the path to target needs to be changed to avoid bad items, it
* will also set the turn radius based on the new path if necessary.
* \param forward Forward node of current AI position.
* \param path Default path to target.
* \param[in,out] path Default path to follow, will be changed if needed.
*/
void ArenaAI::determinePath(int forward, std::vector<int>* path)
{

View File

@ -32,7 +32,6 @@ class ArenaGraph;
namespace irr
{
namespace scene { class ISceneNode; }
namespace video { class ITexture; }
}
/** A base class for AI that use navmesh to work.
@ -41,12 +40,16 @@ namespace irr
class ArenaAI : public AIBaseController
{
protected:
/** Pointer to the \ref ArenaGraph. */
ArenaGraph* m_graph;
/** Pointer to the closest kart around this kart. */
AbstractKart *m_closest_kart;
/** The \ref ArenaNode at which the closest kart located on. */
int m_closest_kart_node;
/** The closest kart location. */
Vec3 m_closest_kart_point;
/** Holds the current difficulty. */
@ -55,26 +58,39 @@ protected:
/** For debugging purpose: a sphere indicating where the AI
* is targeting at. */
irr::scene::ISceneNode *m_debug_sphere;
/** For debugging purpose: a sphere indicating where the first
* turning corner is located. */
irr::scene::ISceneNode *m_debug_sphere_next;
/** The node(quad) at which the target point lies in. */
/** The \ref ArenaNode at which the target point located on. */
int m_target_node;
/** The target point. */
/** The coordinates of target point. */
Vec3 m_target_point;
/** True if AI can skid, currently only do when close to target, see
* \ref doSkiddingTest(). */
bool m_mini_skid;
void collectItemInArena(Vec3*, int*) const;
private:
/** Used by handleArenaUTurn, it tells whether to do left or right
* turning when steering is overridden. */
bool m_adjusting_side;
// ------------------------------------------------------------------------
void tryCollectItem(Vec3* aim_point, int* target_node) const;
// ------------------------------------------------------------------------
/** Find the closest kart around this AI, implemented by sub-class.
* \param consider_difficulty If take current difficulty into account.
* \param find_sta If find \ref SpareTireAI only. */
virtual void findClosestKart(bool consider_difficulty, bool find_sta) = 0;
private:
/** Local coordinates of current target point. */
Vec3 m_target_point_lc;
/** Indicates that the kart is currently stuck, and m_time_since_reversing is
* counting down. */
/** Save the last target point before reversing, so AI will end reversing
* until facing in front of it. */
Vec3 m_reverse_point;
/** Indicates that the kart is currently stuck, and m_time_since_reversing
* is counting down. */
bool m_is_stuck;
/** Indicates that the kart need a uturn to reach a node behind, and
@ -100,40 +116,79 @@ private:
/** This is a timer that counts when the kart start going off road. */
float m_time_since_off_road;
/** Used to determine braking and nitro usage. */
float m_turn_radius;
/** Used to determine if skidding can be done. */
float m_steering_angle;
/** The point in front of the AI which distance is \ref m_kart_length, used
* to compensate the time difference between steering when finding next
* node. */
Vec3 m_current_forward_point;
/** The \ref ArenaNode at which the forward point located on. */
int m_current_forward_node;
void configSpeed();
// ------------------------------------------------------------------------
void configSteering();
// ------------------------------------------------------------------------
void checkIfStuck(const float dt);
// ------------------------------------------------------------------------
void determinePath(int forward, std::vector<int>* path);
// ------------------------------------------------------------------------
void doSkiddingTest();
// ------------------------------------------------------------------------
void doUTurn(const float dt);
// ------------------------------------------------------------------------
bool gettingUnstuck(const float dt);
// ------------------------------------------------------------------------
bool updateAimingPosition(Vec3* target_point);
// ------------------------------------------------------------------------
void useItems(const float dt);
// ------------------------------------------------------------------------
virtual bool canSkid(float steer_fraction) OVERRIDE
{ return m_mini_skid; }
virtual void findClosestKart(bool use_difficulty) = 0;
{ return m_mini_skid; }
// ------------------------------------------------------------------------
/** Find a suitable target for this frame, implemented by sub-class. */
virtual void findTarget() = 0;
virtual bool forceBraking() { return false; }
// ------------------------------------------------------------------------
/** If true, AI will always try to brake for this frame. */
virtual bool forceBraking() { return false; }
// ------------------------------------------------------------------------
/** Return the current \ref ArenaNode the AI located on. */
virtual int getCurrentNode() const = 0;
// ------------------------------------------------------------------------
/** Return the distance based on graph distance matrix to any kart.
* \param kart \ref AbstractKart to check. */
virtual float getKartDistance(const AbstractKart* kart) const = 0;
virtual bool ignorePathFinding() { return false; }
// ------------------------------------------------------------------------
/** If true, AI will drive directly to target without path finding. */
virtual bool ignorePathFinding() { return false; }
// ------------------------------------------------------------------------
/** If true, AI will stop moving. */
virtual bool isWaiting() const = 0;
// ------------------------------------------------------------------------
/** If true, AI stays on the \ref ArenaNode correctly, otherwise
* \ref RescueAnimation will be done after sometime. */
virtual bool isKartOnRoad() const = 0;
virtual void resetAfterStop() {};
// ------------------------------------------------------------------------
/** Overridden if any action is needed to be done when AI stopped
* moving or changed driving direction. */
virtual void resetAfterStop() {}
public:
ArenaAI(AbstractKart *kart);
virtual ~ArenaAI() {};
// ------------------------------------------------------------------------
virtual ~ArenaAI() {}
// ------------------------------------------------------------------------
virtual void update (float delta) OVERRIDE;
// ------------------------------------------------------------------------
virtual void reset () OVERRIDE;
// ------------------------------------------------------------------------
virtual void newLap (int lap) OVERRIDE {}
};
#endif

View File

@ -24,6 +24,7 @@
#include "items/powerup.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/kart_control.hpp"
#include "karts/controller/spare_tire_ai.hpp"
#include "modes/three_strikes_battle.hpp"
#include "tracks/arena_graph.hpp"
@ -55,7 +56,6 @@ BattleAI::BattleAI(AbstractKart *kart)
} // BattleAI
//-----------------------------------------------------------------------------
BattleAI::~BattleAI()
{
#ifdef AI_DEBUG
@ -65,50 +65,48 @@ BattleAI::~BattleAI()
} // ~BattleAI
//-----------------------------------------------------------------------------
/** Resets the AI when a race is restarted.
/** Find the closest kart around this AI, if consider_difficulty is true, AI
* will try to follow human players more or less depends on difficulty.
* \param consider_difficulty If take current difficulty into account.
* \param find_sta If find \ref SpareTireAI only.
*/
void BattleAI::reset()
{
ArenaAI::reset();
} // reset
//-----------------------------------------------------------------------------
void BattleAI::update(float dt)
{
ArenaAI::update(dt);
} // update
//-----------------------------------------------------------------------------
void BattleAI::findClosestKart(bool use_difficulty)
void BattleAI::findClosestKart(bool consider_difficulty, bool find_sta)
{
float distance = 99999.9f;
const unsigned int n = m_world->getNumKarts();
int closest_kart_num = 0;
const int end = m_world->getNumKarts();
for (unsigned int i = 0; i < n; i++)
for (int start_id =
find_sta ? end - race_manager->getNumSpareTireKarts() : 0;
start_id < end; start_id++)
{
const AbstractKart* kart = m_world->getKart(i);
if (kart->isEliminated()) continue;
const AbstractKart* kart = m_world->getKart(start_id);
const SpareTireAI* sta =
dynamic_cast<const SpareTireAI*>(kart->getController());
if (kart->isEliminated() && !(find_sta && sta && sta->isMoving()))
continue;
if (kart->getWorldKartId() == m_kart->getWorldKartId())
continue; // Skip the same kart
// Test whether takes current difficulty into account for closest kart
// Notice: it don't affect aiming, this function will be called once
// more in handleArenaItems, which ignore difficulty.
if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && use_difficulty)
// more when use items, which ignore difficulty.
if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY &&
consider_difficulty)
{
// Skip human players for novice mode unless only human players left
const AbstractKart* temp = m_world->getKart(i);
// Skip human players for novice mode unless only they are left
const AbstractKart* temp = m_world->getKart(start_id);
if (temp->getController()->isPlayerController() &&
(m_world->getCurrentNumKarts() -
m_world->getCurrentNumPlayers()) > 1)
continue;
}
else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && use_difficulty)
else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST &&
consider_difficulty)
{
// Skip AI players for supertux mode
const AbstractKart* temp = m_world->getKart(i);
const AbstractKart* temp = m_world->getKart(start_id);
if (!(temp->getController()->isPlayerController()))
continue;
}
@ -118,7 +116,7 @@ void BattleAI::findClosestKart(bool use_difficulty)
if (dist_to_kart <= distance)
{
distance = dist_to_kart;
closest_kart_num = i;
closest_kart_num = start_id;
}
}
@ -129,12 +127,44 @@ void BattleAI::findClosestKart(bool use_difficulty)
} // findClosestKart
//-----------------------------------------------------------------------------
/** Find a suitable target to follow, it will find the closest kart first, it's
* used as fallback if no item is found. It takes the current difficulty into
* account, also collect life from \ref SpareTireAI depends on current
* difficulty if actually they are spawned:
* \li Novice and intermediate - collect them only AI has 1 life only.
* \li Expert and supertux - collect them if AI dones't have 3 lives.
*/
void BattleAI::findTarget()
{
bool find_sta = false;
if (m_world->spareTireKartsSpawned())
{
switch (m_cur_difficulty)
{
case RaceManager::DIFFICULTY_EASY:
case RaceManager::DIFFICULTY_MEDIUM:
{
find_sta = m_world->getKartLife(m_kart->getWorldKartId()) == 1;
break;
}
case RaceManager::DIFFICULTY_HARD:
case RaceManager::DIFFICULTY_BEST:
{
find_sta = m_world->getKartLife(m_kart->getWorldKartId()) != 3;
break;
}
default: assert(false);
}
}
bool consider_difficulty = !find_sta;
findClosestKart(consider_difficulty, find_sta);
// Find a suitable target to drive to, either powerup or kart
if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING &&
m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER)
collectItemInArena(&m_target_point , &m_target_node);
m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER &&
!find_sta)
tryCollectItem(&m_target_point , &m_target_node);
else
{
m_target_point = m_closest_kart_point;

View File

@ -30,21 +30,31 @@ class ThreeStrikesBattle;
*/
class BattleAI : public ArenaAI
{
private:
protected:
/** Keep a pointer to world. */
ThreeStrikesBattle *m_world;
virtual void findClosestKart(bool use_difficulty) OVERRIDE;
virtual void findTarget() OVERRIDE;
// ------------------------------------------------------------------------
virtual void findClosestKart(bool consider_difficulty,
bool find_sta) OVERRIDE;
// ------------------------------------------------------------------------
virtual int getCurrentNode() const OVERRIDE;
private:
// ------------------------------------------------------------------------
virtual void findTarget() OVERRIDE;
// ------------------------------------------------------------------------
virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE;
// ------------------------------------------------------------------------
virtual bool isKartOnRoad() const OVERRIDE;
// ------------------------------------------------------------------------
virtual bool isWaiting() const OVERRIDE;
public:
BattleAI(AbstractKart *kart);
~BattleAI();
virtual void update (float delta) OVERRIDE;
virtual void reset () OVERRIDE;
BattleAI(AbstractKart *kart);
// ------------------------------------------------------------------------
~BattleAI();
};
#endif

View File

@ -71,7 +71,6 @@ SoccerAI::SoccerAI(AbstractKart *kart)
} // SoccerAI
//-----------------------------------------------------------------------------
SoccerAI::~SoccerAI()
{
#ifdef AI_DEBUG
@ -103,6 +102,10 @@ void SoccerAI::reset()
} // reset
//-----------------------------------------------------------------------------
/** Update \ref m_front_transform for ball aiming functions, also make AI stop
* after goal.
* \param dt Time step size.
*/
void SoccerAI::update(float dt)
{
#ifdef BALL_AIM_DEBUG
@ -129,7 +132,12 @@ void SoccerAI::update(float dt)
} // update
//-----------------------------------------------------------------------------
void SoccerAI::findClosestKart(bool use_difficulty)
/** Find the closest kart around this AI, it won't find the kart with same
* team, consider_difficulty and find_sta are not used here.
* \param consider_difficulty If take current difficulty into account.
* \param find_sta If find \ref SpareTireAI only.
*/
void SoccerAI::findClosestKart(bool consider_difficulty, bool find_sta)
{
float distance = 99999.9f;
const unsigned int n = m_world->getNumKarts();
@ -163,8 +171,16 @@ void SoccerAI::findClosestKart(bool use_difficulty)
} // findClosestKart
//-----------------------------------------------------------------------------
/** Find a suitable target to follow, it will first call
* \ref SoccerWorld::getBallChaser to check if this AI should go chasing the
* ball and try to score, otherwise it will call \ref tryCollectItem if
* needed. After that it will call \ref SoccerWorld::getAttacker to see if
* this AI should attack the kart in opposite team which is chasing the ball,
* if not go for the closest kart found by \ref findClosestKart.
*/
void SoccerAI::findTarget()
{
findClosestKart(true/*consider_difficulty*/, false/*find_sta*/);
// Check if this AI kart is the one who will chase the ball
if (m_world->getBallChaser(m_cur_team) == (signed)m_kart->getWorldKartId())
{
@ -180,7 +196,7 @@ void SoccerAI::findTarget()
if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING &&
m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER)
{
collectItemInArena(&m_target_point , &m_target_node);
tryCollectItem(&m_target_point , &m_target_node);
}
else if (m_world->getAttacker(m_cur_team) == (signed)m_kart
->getWorldKartId())
@ -200,6 +216,11 @@ void SoccerAI::findTarget()
} // findTarget
//-----------------------------------------------------------------------------
/** Determine the point for aiming when try to steer or overtake the ball.
* AI will overtake the ball if the aiming position calculated by world is
* non-reachable.
* \return The coordinates to aim at.
*/
Vec3 SoccerAI::determineBallAimingPosition()
{
#ifdef BALL_AIM_DEBUG
@ -275,6 +296,11 @@ Vec3 SoccerAI::determineBallAimingPosition()
} // determineBallAimingPosition
//-----------------------------------------------------------------------------
/** Used in \ref determineBallAimingPosition to test if AI can overtake the
* ball by testing distance.
* \param ball_lc Local coordinates of the ball.
* \return False if the kart is too close to the ball which can't overtake
*/
bool SoccerAI::isOvertakable(const Vec3& ball_lc)
{
// No overtake if ball is behind
@ -297,6 +323,13 @@ bool SoccerAI::isOvertakable(const Vec3& ball_lc)
} // isOvertakable
//-----------------------------------------------------------------------------
/** Used in \ref determineBallAimingPosition to pick a correct point to
* overtake the ball
* \param ball_lc Local coordinates of the ball.
* \param aim_lc Local coordinates of the aiming position.
* \param[out] overtake_lc Local coordinates of the overtaking position.
* \return True if overtaking is possible.
*/
bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
const Vec3& aim_lc,
Vec3* overtake_lc)
@ -433,6 +466,12 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
} // determineOvertakePosition
//-----------------------------------------------------------------------------
/** Used in \ref determineOvertakePosition to adjust the overtake position
* which is calculated by slope of line if it's too close.
* \param old_slope Old slope calculated.
* \param rotate_up If adjust the slope upwards.
* \return A newly calculated slope.
*/
float SoccerAI::rotateSlope(float old_slope, bool rotate_up)
{
const float theta = atan(old_slope) + (old_slope < 0 ? M_PI : 0);

View File

@ -45,41 +45,68 @@ private:
/** Keep a pointer to world. */
SoccerWorld *m_world;
/** Save the team this AI belongs to. */
SoccerTeam m_cur_team;
/** Save the opposite team of this AI team. */
SoccerTeam m_opp_team;
/** Define which way to handle to ball, either steer with it,
* or overtake it (Denfense).
*/
* or overtake it (Defense). */
bool m_overtake_ball;
/** True if \ref forceBraking() is needed to be called. */
bool m_force_brake;
/** True if AI should steer with the ball. */
bool m_chasing_ball;
/** The front point of kart with the same rotation of center mass, used
* to determine point for aiming with ball */
btTransform m_front_transform;
// ------------------------------------------------------------------------
Vec3 determineBallAimingPosition();
// ------------------------------------------------------------------------
bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc,
Vec3* overtake_lc);
// ------------------------------------------------------------------------
bool isOvertakable(const Vec3& ball_lc);
// ------------------------------------------------------------------------
float rotateSlope(float old_slope, bool rotate_up);
// ------------------------------------------------------------------------
virtual bool canSkid(float steer_fraction) OVERRIDE
{ return m_mini_skid && !(m_overtake_ball || m_chasing_ball); }
virtual void findClosestKart(bool use_difficulty) OVERRIDE;
{ return m_mini_skid && !(m_overtake_ball || m_chasing_ball); }
// ------------------------------------------------------------------------
virtual void findClosestKart(bool consider_difficulty,
bool find_sta) OVERRIDE;
// ------------------------------------------------------------------------
virtual void findTarget() OVERRIDE;
virtual bool forceBraking() OVERRIDE { return m_force_brake; }
// ------------------------------------------------------------------------
virtual bool forceBraking() OVERRIDE { return m_force_brake; }
// ------------------------------------------------------------------------
virtual int getCurrentNode() const OVERRIDE;
// ------------------------------------------------------------------------
virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE;
// ------------------------------------------------------------------------
virtual bool ignorePathFinding() OVERRIDE
{ return m_overtake_ball || m_chasing_ball; }
{ return m_overtake_ball || m_chasing_ball; }
// ------------------------------------------------------------------------
virtual bool isKartOnRoad() const OVERRIDE;
// ------------------------------------------------------------------------
virtual bool isWaiting() const OVERRIDE;
virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; }
// ------------------------------------------------------------------------
virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; }
public:
SoccerAI(AbstractKart *kart);
// ------------------------------------------------------------------------
~SoccerAI();
// ------------------------------------------------------------------------
virtual void update (float delta) OVERRIDE;
// ------------------------------------------------------------------------
virtual void reset () OVERRIDE;
};
#endif

View File

@ -0,0 +1,156 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program 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.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/controller/spare_tire_ai.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/max_speed.hpp"
#include "modes/three_strikes_battle.hpp"
#include "states_screens/race_gui.hpp"
#include "tracks/arena_graph.hpp"
#include "tracks/arena_node.hpp"
#include "physics/physics.hpp"
#include "utils/random_generator.hpp"
#include <algorithm>
SpareTireAI::SpareTireAI(AbstractKart *kart)
: BattleAI(kart)
{
reset();
// Don't call our own setControllerName, since this will add a
// billboard showing 'AIBaseController' to the kart.
Controller::setControllerName("SpareTireAI");
// Pre-load the 4 nodes of bounding box defined by battle world
memcpy(m_fixed_target_nodes, m_graph->getBBNodes(), 4 * sizeof(int));
// Reverse the order depends on world ID, so not all spare tire karts go
// the same way
if (m_kart->getWorldKartId() % 2 != 0)
{
std::reverse(std::begin(m_fixed_target_nodes),
std::end(m_fixed_target_nodes));
}
} // SpareTireAI
//-----------------------------------------------------------------------------
/** Resets the AI when a race is restarted.
*/
void SpareTireAI::reset()
{
BattleAI::reset();
m_idx = 0;
m_timer = 0.0f;
} // reset
//-----------------------------------------------------------------------------
/** Besides calling update from parent class, it will auto \ref unspawn if
* \ref m_timer reaches zero which it will be decreased here.
* \param dt Time step size.
*/
void SpareTireAI::update(float dt)
{
BattleAI::update(dt);
m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI, 0.5f, /*fade_in_time*/0.0f);
m_timer -= dt;
if (m_timer < 0.0f)
unspawn();
} // update
//-----------------------------------------------------------------------------
/** Randomly find a start node for spare tire kart to move, called after \ref
* spawn.
*/
void SpareTireAI::findDefaultPath()
{
assert(m_idx == -1);
RandomGenerator random;
m_idx = random.get(4);
m_target_node = m_fixed_target_nodes[m_idx];
} // findDefaultPath
//-----------------------------------------------------------------------------
/** For SpareTireAI, it will pick next node in \ref m_fixed_target_nodes after
* reach the one in \ref m_idx, or the first one if it's the last.
*/
void SpareTireAI::findTarget()
{
assert(m_idx != -1 && m_idx < 4);
if (getCurrentNode() == m_fixed_target_nodes[m_idx])
m_idx = m_idx == 3 ? 0 : m_idx + 1;
const int chosen_node = m_fixed_target_nodes[m_idx];
m_target_node = chosen_node;
m_target_point = m_graph->getNode(chosen_node)->getCenter();
} // findTarget
//-----------------------------------------------------------------------------
/** Spawn the SpareTireAI, it will start appearing in the battle mode and
* moving around.
* \param time_to_last Time before calling \ref unspawn.
*/
void SpareTireAI::spawn(float time_to_last)
{
findDefaultPath();
m_timer = time_to_last;
World::getWorld()->getPhysics()->addKart(m_kart);
m_kart->startEngineSFX();
m_kart->getKartGFX()->reset();
m_kart->getNode()->setVisible(true);
} // spawn
//-----------------------------------------------------------------------------
/** Unspawn the SpareTireAI, it will be hidden in the battle mode but not
* deleted, so it can be called with \ref spawn again later.
*/
void SpareTireAI::unspawn()
{
m_idx = -1;
m_kart->eliminate();
} // unspawn
//-----------------------------------------------------------------------------
/** Callback function when a kart crashes into the SpareTireAI, the kart will
* increase one life if its life is not equal 3. A message will be shown too.
* \param k \ref AbstractKart this SpareTireAI crashed.
*/
void SpareTireAI::crashed(const AbstractKart *k)
{
// Nothing happen when two spare tire karts crash each other
if (dynamic_cast<const SpareTireAI*>(k->getController()) != NULL) return;
// Tell player that they have max 3 lives only
if (m_world->getKartLife(k->getWorldKartId()) == 3)
{
World::getWorld()->getRaceGUI()->addMessage
(_("You can have at most 3 lives!"), k, 2.0f);
return;
}
// Otherwise increase one life for that kart and unspawn
m_world->addKartLife(k->getWorldKartId());
World::getWorld()->getRaceGUI()->addMessage(_("+1 life."), k, 2.0f);
unspawn();
} // crashed

View File

@ -0,0 +1,64 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program 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.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_SPARE_TIRE_AI_HPP
#define HEADER_SPARE_TIRE_AI_HPP
#include "karts/controller/battle_ai.hpp"
/** The AI for spare tire karts in battle mode, allowing kart to gain life.
* \ingroup controller
*/
class SpareTireAI : public BattleAI
{
private:
/** The 4 bounding boxes \ref ArenaNode to follow. */
int m_fixed_target_nodes[4];
/** The current index of \ref ArenaNode in \ref m_fixed_target_nodes to
* follow, if it's -1, \ref update is not needed to be called. */
int m_idx;
/** Store the time before calling \ref unspawn. */
float m_timer;
// ------------------------------------------------------------------------
virtual void findTarget() OVERRIDE;
// ------------------------------------------------------------------------
void findDefaultPath();
public:
SpareTireAI(AbstractKart *kart);
// ------------------------------------------------------------------------
virtual void crashed(const AbstractKart *k) OVERRIDE;
// ------------------------------------------------------------------------
virtual void update(float delta) OVERRIDE;
// ------------------------------------------------------------------------
virtual void reset() OVERRIDE;
// ------------------------------------------------------------------------
void spawn(float time_to_last);
// ------------------------------------------------------------------------
void unspawn();
// ------------------------------------------------------------------------
/** Return true if this AI needed to be called \ref update by \ref World,
* ie it is spawned. */
bool isMoving() const { return m_idx != -1; }
};
#endif

View File

@ -50,6 +50,7 @@
#include "karts/abstract_kart_animation.hpp"
#include "karts/cached_characteristic.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/controller/spare_tire_ai.hpp"
#include "karts/explosion_animation.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/kart_model.hpp"
@ -90,13 +91,6 @@
#include <iostream>
#include <cmath>
#include <math.h>
#include <iostream>
#include <algorithm> // for min and max
#include <ICameraSceneNode.h>
#include <ISceneManager.h>
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
// Disable warning for using 'this' in base member initializer list
@ -332,6 +326,7 @@ void Kart::reset()
// restore the original controller.
if(m_saved_controller)
{
delete m_controller;
m_controller = m_saved_controller;
m_saved_controller = NULL;
}
@ -868,6 +863,9 @@ void Kart::finishedRace(float time, bool from_server)
m_kart_model->finishedRace();
race_manager->kartFinishedRace(this, time);
// If this is spare tire kart, end now
if (dynamic_cast<SpareTireAI*>(m_controller) != NULL) return;
if ((race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
@ -2888,12 +2886,14 @@ void Kart::setOnScreenText(const wchar_t *text)
if (CVS->isGLSL())
{
new STKTextBillboard(text, bold_face,
scene::ISceneNode* tb =
new STKTextBillboard(text, bold_face,
GUIEngine::getSkin()->getColor("font::bottom"),
GUIEngine::getSkin()->getColor("font::top"),
getNode(), irr_driver->getSceneManager(), -1,
core::vector3df(0.0f, 1.5f, 0.0f),
core::vector3df(1.0f, 1.0f, 1.0f));
tb->drop();
}
else
{

View File

@ -80,6 +80,13 @@ KartGFX::KartGFX(const AbstractKart *kart)
m_skidding_light_2->setName( ("skidding emitter 2 (" + m_kart->getIdent()
+ ")").c_str() );
if (CVS->isGLSL())
{
m_nitro_light->grab();
m_skidding_light_1->grab();
m_skidding_light_2->grab();
}
// Create particle effects
Vec3 rear_left(kart->getWheelGraphicsPosition(3).getX(), 0.05f,
kart->getWheelGraphicsPosition(3).getZ()-0.1f );
@ -142,7 +149,8 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name,
const Vec3 &position, bool important)
{
if (!UserConfigParams::m_graphical_effects &&
(!important || m_kart->getType() == RaceManager::KT_AI))
(!important || m_kart->getType() == RaceManager::KT_AI ||
m_kart->getType() == RaceManager::KT_SPARE_TIRE))
{
m_all_emitters.push_back(NULL);
return;

View File

@ -131,6 +131,7 @@ KartModel::KartModel(bool is_master)
m_min_suspension[i] = -0.07f;
m_max_suspension[i] = 0.20f;
m_dampen_suspension_amplitude[i] = 2.5f;
m_default_physics_suspension[i] = 0.25f;
}
m_wheel_filename[0] = "";
m_wheel_filename[1] = "";
@ -521,6 +522,13 @@ bool KartModel::loadModels(const KartProperties &kart_properties)
full_path.c_str(), kart_properties.getIdent().c_str());
return false;
}
scene::ISkinnedMesh* sm = dynamic_cast<scene::ISkinnedMesh*>(m_mesh);
if (sm)
{
MeshTools::createSkinnedMeshWithTangents(sm, &MeshTools::isNormalMap);
}
m_mesh->grab();
irr_driver->grabAllTextures(m_mesh);
@ -569,6 +577,14 @@ bool KartModel::loadModels(const KartProperties &kart_properties)
// Grab all textures. This is done for the master only, so
// the destructor will only free the textures if a master
// copy is freed.
scene::ISkinnedMesh* sm =
dynamic_cast<scene::ISkinnedMesh*>(obj.m_model);
if (sm)
{
MeshTools::createSkinnedMeshWithTangents(sm,
&MeshTools::isNormalMap);
}
irr_driver->grabAllTextures(obj.m_model);
// Update min/max
@ -611,6 +627,8 @@ bool KartModel::loadModels(const KartProperties &kart_properties)
std::string full_wheel =
kart_properties.getKartDir()+m_wheel_filename[i];
m_wheel_model[i] = irr_driver->getMesh(full_wheel);
m_wheel_model[i] = MeshTools::createMeshWithTangents(m_wheel_model[i],
&MeshTools::isNormalMap);
// Grab all textures. This is done for the master only, so
// the destructor will only free the textures if a master
// copy is freed.
@ -861,17 +879,18 @@ void KartModel::update(float dt, float distance, float steer, float speed,
float suspension_length = 0.0f;
GhostKart* gk = dynamic_cast<GhostKart*>(m_kart);
// Prevent using m_default_physics_suspension uninitialized
if (gk && gt_replay_index == -1) break;
if (gk)
// Prevent using suspension length uninitialized
if (dt != 0.0f && !(gk && gt_replay_index == -1))
{
suspension_length = gk->getSuspensionLength(gt_replay_index, i);
}
else
{
suspension_length = m_kart->getVehicle()->getWheelInfo(i).
m_raycastInfo.m_suspensionLength;
if (gk)
{
suspension_length = gk->getSuspensionLength(gt_replay_index, i);
}
else
{
suspension_length = m_kart->getVehicle()->getWheelInfo(i).
m_raycastInfo.m_suspensionLength;
}
}
// Check documentation of Kart::updateGraphics for the following line

View File

@ -494,7 +494,7 @@ const std::vector<int> KartPropertiesManager::getKartsInGroup(
* to this list.
*/
void KartPropertiesManager::getRandomKartList(int count,
RemoteKartInfoList& existing_karts,
RemoteKartInfoList* existing_karts,
std::vector<std::string> *ai_list)
{
// First: set up flags (based on global kart
@ -504,18 +504,22 @@ void KartPropertiesManager::getRandomKartList(int count,
used.resize(getNumberOfKarts(), false);
std::vector<std::string> random_kart_queue;
for (unsigned int i=0; i<existing_karts.size(); i++)
if (existing_karts != NULL)
{
try
for (unsigned int i=0; i<existing_karts->size(); i++)
{
int id = getKartId(existing_karts[i].getKartName());
used[id] = true;
}
catch (std::runtime_error& ex)
{
(void)ex;
Log::error("[KartPropertiesManager]", "getRandomKartList : WARNING, "
"can't find kart '%s'", existing_karts[i].getKartName().c_str());
try
{
int id = getKartId((*existing_karts)[i].getKartName());
used[id] = true;
}
catch (std::runtime_error& ex)
{
(void)ex;
Log::error("[KartPropertiesManager]", "getRandomKartList : "
"WARNING, can't find kart '%s'",
(*existing_karts)[i].getKartName().c_str());
}
}
}
for(unsigned int i=0; i<ai_list->size(); i++)

View File

@ -94,7 +94,7 @@ public:
void selectKartName(const std::string &kart_name);
bool testAndSetKart(int kartid);
void getRandomKartList(int count,
RemoteKartInfoList& existing_karts,
RemoteKartInfoList* existing_karts,
std::vector<std::string> *ai_list);
void setHatMeshName(const std::string &hat_name);
// ------------------------------------------------------------------------

View File

@ -222,11 +222,14 @@ void MainLoop::run()
m_prev_time = m_curr_time;
float dt = getLimitedDt();
// Render the previous frame, and also handle all user input.
PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F);
irr_driver->update(dt);
PROFILER_POP_CPU_MARKER();
if (!m_abort && !ProfileWorld::isNoGraphics())
{
// Render the previous frame, and also handle all user input.
PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F);
irr_driver->update(dt);
PROFILER_POP_CPU_MARKER();
}
if (World::getWorld()) // race is active if world exists
{

View File

@ -620,7 +620,17 @@ unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart)
// Setting XYZ for the kart is important since otherwise the kart
// will not detect the right material again when doing the next
// raycast to detect where it is driving on (--> potential rescue loop)
return getTrackSector(kart_id)->getCurrentGraphNode();
int index = getTrackSector(kart_id)->getCurrentGraphNode();
// Do not rescue to an ignored quad, find another (non-ignored) quad
if (Graph::get()->getQuad(index)->isIgnored())
{
Vec3 pos = kart->getFrontXYZ();
int sector = Graph::get()->findOutOfRoadSector(pos);
return sector;
}
return index;
} // getRescuePositionIndex
// ------------------------------------------------------------------------

View File

@ -30,6 +30,7 @@
#include "karts/kart_properties.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/controller/local_player_controller.hpp"
#include "karts/controller/network_player_controller.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/track.hpp"
@ -421,11 +422,9 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index,
m_num_players ++;
break;
case RaceManager::KT_NETWORK_PLAYER:
break; // Avoid compiler warning about enum not handled.
//controller = new NetworkController(kart_ident, position, init_pos,
// global_player_id);
//m_num_players++;
//break;
controller = new NetworkPlayerController(new_kart);
m_num_players++;
break;
case RaceManager::KT_AI:
controller = loadAIController(new_kart);
break;
@ -433,6 +432,8 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index,
break;
case RaceManager::KT_LEADER:
break;
case RaceManager::KT_SPARE_TIRE:
break;
}
new_kart->setController(controller);

View File

@ -23,15 +23,20 @@
#include "graphics/camera.hpp"
#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart.hpp"
#include "karts/controller/spare_tire_ai.hpp"
#include "karts/kart_model.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/arena_graph.hpp"
#include "tracks/arena_node.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "utils/constants.hpp"
#include <algorithm>
#include <string>
#include <IMeshSceneNode.h>
@ -73,8 +78,9 @@ void ThreeStrikesBattle::init()
ThreeStrikesBattle::~ThreeStrikesBattle()
{
m_tires.clearWithoutDeleting();
m_spare_tire_karts.clear();
irr_driver->grabAllTextures(m_tire);
irr_driver->dropAllTextures(m_tire);
// Remove the mesh from the cache so that the mesh is properly
// freed once all refernces to it (which will happen once all
// karts are being freed, which would have a pointer to this mesh)
@ -88,11 +94,25 @@ void ThreeStrikesBattle::reset()
{
WorldWithRank::reset();
m_next_sta_spawn_time =
race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ?
25.0f : 20.0f;
const unsigned int kart_amount = (unsigned int)m_karts.size();
for(unsigned int n=0; n<kart_amount; n++)
{
m_kart_info[n].m_lives = 3;
if (dynamic_cast<SpareTireAI*>(m_karts[n]->getController()) != NULL)
{
// STA has no life
m_kart_info[n].m_lives = 0;
}
else
{
m_kart_info[n].m_lives = 3;
}
// no positions in this mode
m_karts[n]->setPosition(-1);
@ -132,6 +152,18 @@ void ThreeStrikesBattle::reset()
m_track->getTrackObjectManager()->removeObject(obj);
}
m_tires.clearWithoutDeleting();
// Finish all spare tire karts first
if (!m_spare_tire_karts.empty())
{
updateKartRanks();
for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++)
{
m_spare_tire_karts[i]->finishedRace(0.0f);
m_spare_tire_karts[i]->getNode()->setVisible(false);
m_eliminated_karts++;
}
}
} // reset
//-----------------------------------------------------------------------------
@ -142,6 +174,20 @@ void ThreeStrikesBattle::reset()
*/
void ThreeStrikesBattle::kartAdded(AbstractKart* kart, scene::ISceneNode* node)
{
if (kart->getType() == RaceManager::KartType::KT_SPARE_TIRE)
{
// Add heart billboard above it
video::ITexture *heart =
irr_driver->getTexture(FileManager::GUI, "heart.png");
float height = kart->getKartHeight() + 0.5f;
scene::ISceneNode* billboard = irr_driver->addBillboard
(core::dimension2d<irr::f32>(0.8f, 0.8f), heart, kart->getNode(),
true);
billboard->setPosition(core::vector3df(0, height, 0));
return;
}
float coord = -kart->getKartLength()*0.5f;
scene::IMeshSceneNode* tire_node = irr_driver->addMesh(m_tire, "3strikestire", node);
@ -165,6 +211,15 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
{
if (isRaceOver()) return;
SpareTireAI* sta =
dynamic_cast<SpareTireAI*>(m_karts[kart_id]->getController());
if (sta)
{
// Unspawn the spare tire kart if it get hit
sta->unspawn();
return;
}
assert(kart_id < m_karts.size());
// make kart lose a life, ignore if in profiling mode
if (!UserConfigParams::m_arena_ai_stats)
@ -305,6 +360,7 @@ void ThreeStrikesBattle::update(float dt)
WorldWithRank::update(dt);
WorldWithRank::updateTrack(dt);
spawnSpareTireKarts();
if (m_track->hasNavMesh())
updateSectorForKarts();
@ -446,7 +502,7 @@ bool ThreeStrikesBattle::isRaceOver()
return (irr_driver->getRealTime()-m_start_time)*0.001f > 20.0f;
// for tests : never over when we have a single player there :)
if (race_manager->getNumberOfKarts()==1 &&
if (race_manager->getNumberOfKarts() - m_spare_tire_karts.size () ==1 &&
getCurrentNumKarts()==1 &&
UserConfigParams::m_artist_debug_mode)
{
@ -509,6 +565,16 @@ void ThreeStrikesBattle::enterRaceOverState()
{
WorldWithRank::enterRaceOverState();
// Unspawn all spare tire karts if neccesary
for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++)
{
SpareTireAI* sta =
dynamic_cast<SpareTireAI*>(m_spare_tire_karts[i]->getController());
assert(sta);
if (sta->isMoving())
sta->unspawn();
}
if (UserConfigParams::m_arena_ai_stats)
{
float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f;
@ -521,3 +587,164 @@ void ThreeStrikesBattle::enterRaceOverState()
}
} // enterRaceOverState
//-----------------------------------------------------------------------------
bool ThreeStrikesBattle::spareTireKartsSpawned() const
{
if (m_spare_tire_karts.empty()) return false;
// Spare tire karts are spawned if at least 1 of them needs update
SpareTireAI* sta =
dynamic_cast<SpareTireAI*>(m_spare_tire_karts[0]->getController());
assert(sta);
return sta->isMoving();
} // spareTireKartsSpawned
//-----------------------------------------------------------------------------
void ThreeStrikesBattle::addKartLife(unsigned int id)
{
m_kart_info[id].m_lives++;
updateKartRanks();
scene::ISceneNode* kart_node = m_karts[id]->getNode();
core::list<scene::ISceneNode*>& children =
const_cast<core::list<scene::ISceneNode*>&>(kart_node->getChildren());
for (core::list<scene::ISceneNode*>::Iterator it = children.begin();
it != children.end(); it++)
{
scene::ISceneNode* curr = *it;
if (core::stringc(curr->getName()) == "tire1")
{
curr->setVisible(m_kart_info[id].m_lives >= 3);
}
else if (core::stringc(curr->getName()) == "tire2")
{
curr->setVisible(m_kart_info[id].m_lives >= 2);
}
}
} // addKartLife
//-----------------------------------------------------------------------------
void ThreeStrikesBattle::spawnSpareTireKarts()
{
if (m_spare_tire_karts.empty() ||
getTimeSinceStart() < m_next_sta_spawn_time)
return;
const float period =
race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ?
25.0f : 20.0f;
const float inc_factor =
race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 0.7f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 0.65f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ?
0.6f : 0.55f;
// Spawn spare tire kart when necessary
// The lifespan for sta: inc_factor / period * 1000 / 2
// So in easier mode the sta lasts longer than spawn period
const float lifespan = inc_factor / period * 1000;
m_next_sta_spawn_time = lifespan + (getTimeSinceStart() * inc_factor) +
getTimeSinceStart();
int kart_has_few_lives = 0;
for (unsigned int i = 0; i < m_kart_info.size(); i++)
{
if (m_kart_info[i].m_lives > 0 && m_kart_info[i].m_lives < 3)
kart_has_few_lives++;
}
float ratio = kart_has_few_lives / (inc_factor * 2);
if (ratio < 1.5f) return;
unsigned int spawn_sta = unsigned(ratio);
if (spawn_sta > m_spare_tire_karts.size())
spawn_sta = m_spare_tire_karts.size();
m_race_gui->addMessage(_P("%i spare tire kart has been spawned!",
"%i spare tire karts have been spawned!",
spawn_sta), NULL, 2.0f);
for (unsigned int i = 0; i < spawn_sta; i++)
{
SpareTireAI* sta = dynamic_cast<SpareTireAI*>
(m_spare_tire_karts[i]->getController());
assert(sta);
sta->spawn(lifespan);
}
} // spawnSpareTireKarts
//-----------------------------------------------------------------------------
void ThreeStrikesBattle::loadCustomModels()
{
// Pre-add spare tire karts if there are more than certain number of karts
ArenaGraph* ag = ArenaGraph::get();
if (ag && m_karts.size() > 4)
{
// Spare tire karts only added with large arena
const int all_nodes = ag->getNumNodes();
if (all_nodes > 500)
{
// Don't create too many spare tire karts
const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f);
unsigned int pos_created = 0;
std::vector<int> used;
std::vector<btTransform> pos;
// Fill all current starting position into used first
for (unsigned int i = 0; i < getNumberOfRescuePositions(); i++)
{
int node = -1;
ag->findRoadSector(getRescueTransform(i).getOrigin(), &node,
NULL, true);
assert(node != -1);
used.push_back(node);
}
// Find random nodes to pre-spawn spare tire karts
RandomGenerator random;
while (true)
{
const int node = random.get(all_nodes);
if (std::find(used.begin(), used.end(), node) != used.end())
continue;
const ArenaNode* n = ag->getNode(node);
btTransform t;
t.setOrigin(n->getCenter());
t.setRotation(Track::createRotationFromNormal(n->getNormal()));
pos.push_back(t);
pos_created++;
used.push_back(node);
if (pos_created >= max_sta_num) break;
}
// Compute a random kart list
std::vector<std::string> sta_list;
kart_properties_manager->getRandomKartList(pos.size(), NULL,
&sta_list);
assert(sta_list.size() == pos.size());
// Now add them
for (unsigned int i = 0; i < pos.size(); i++)
{
AbstractKart* sta = new Kart(sta_list[i], m_karts.size(),
m_karts.size() + 1, pos[i], PLAYER_DIFFICULTY_NORMAL,
KRT_RED);
sta->init(RaceManager::KartType::KT_SPARE_TIRE);
sta->setController(new SpareTireAI(sta));
m_karts.push_back(sta);
race_manager->addSpareTireKart(sta_list[i]);
m_track->adjustForFog(sta->getNode());
// Copy STA pointer to m_spare_tire_karts array, allowing them
// to respawn easily
m_spare_tire_karts.push_back(sta);
}
unsigned int sta_num = race_manager->getNumSpareTireKarts();
assert(m_spare_tire_karts.size() == sta_num);
Log::info("ThreeStrikesBattle","%d spare tire kart(s) created.",
sta_num);
}
}
} // loadCustomModels

View File

@ -77,44 +77,63 @@ private:
int m_start_time;
int m_total_hit;
public:
std::vector<AbstractKart*> m_spare_tire_karts;
float m_next_sta_spawn_time;
public:
/** Used to show a nice graph when battle is over */
struct BattleEvent
{
float m_time;
std::vector<BattleInfo> m_kart_info;
};
// ------------------------------------------------------------------------
std::vector<BattleEvent> m_battle_events;
// ------------------------------------------------------------------------
ThreeStrikesBattle();
// ------------------------------------------------------------------------
virtual ~ThreeStrikesBattle();
// ------------------------------------------------------------------------
virtual void init() OVERRIDE;
// ------------------------------------------------------------------------
// clock events
virtual bool isRaceOver() OVERRIDE;
// ------------------------------------------------------------------------
virtual void terminateRace() OVERRIDE;
// ------------------------------------------------------------------------
// overriding World methods
virtual void reset() OVERRIDE;
//virtual void getDefaultCollectibles(int& collectible_type, int& amount);
virtual bool useFastMusicNearEnd() const OVERRIDE { return false; }
// ------------------------------------------------------------------------
virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;
virtual bool raceHasLaps() OVERRIDE { return false; }
// ------------------------------------------------------------------------
virtual bool raceHasLaps() OVERRIDE { return false; }
// ------------------------------------------------------------------------
virtual const std::string& getIdent() const OVERRIDE;
// ------------------------------------------------------------------------
virtual void kartHit(const unsigned int kart_id) OVERRIDE;
// ------------------------------------------------------------------------
virtual void update(float dt) OVERRIDE;
virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) OVERRIDE;
// ------------------------------------------------------------------------
virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node)
OVERRIDE;
// ------------------------------------------------------------------------
virtual void enterRaceOverState() OVERRIDE;
// ------------------------------------------------------------------------
virtual void loadCustomModels() OVERRIDE;
// ------------------------------------------------------------------------
void updateKartRanks();
void increaseRescueCount() { m_total_rescue++; }
// ------------------------------------------------------------------------
void increaseRescueCount() { m_total_rescue++; }
// ------------------------------------------------------------------------
void addKartLife(unsigned int id);
// ------------------------------------------------------------------------
int getKartLife(unsigned int id) const { return m_kart_info[id].m_lives; }
// ------------------------------------------------------------------------
bool spareTireKartsSpawned() const;
// ------------------------------------------------------------------------
void spawnSpareTireKarts();
}; // ThreeStrikesBattles

View File

@ -38,6 +38,7 @@
#include "karts/controller/end_controller.hpp"
#include "karts/controller/local_player_controller.hpp"
#include "karts/controller/skidding_ai.hpp"
#include "karts/controller/spare_tire_ai.hpp"
#include "karts/controller/test_ai.hpp"
#include "karts/controller/network_player_controller.hpp"
#include "karts/kart.hpp"
@ -220,13 +221,16 @@ void World::init()
} // for i
// Load other custom models if needed
loadCustomModels();
// Now that all models are loaded, apply the overrides
irr_driver->applyObjectPassShader();
// Must be called after all karts are created
m_race_gui->init();
powerup_manager->updateWeightsForRace(num_karts);
powerup_manager->updateWeightsForRace(race_manager->getNumberOfKarts());
if (UserConfigParams::m_weather_effects)
{
@ -368,6 +372,8 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index,
break;
case RaceManager::KT_LEADER:
break;
case RaceManager::KT_SPARE_TIRE:
break;
}
new_kart->setController(controller);
@ -477,6 +483,7 @@ World::~World()
race_manager->setRecordRace(false);
race_manager->setWatchingReplay(false);
race_manager->setTimeTarget(0.0f);
race_manager->setSpareTireKartNum(0);
Camera::removeAllCameras();
@ -485,6 +492,8 @@ World::~World()
if(m_physics)
delete m_physics;
delete m_script_engine;
m_world = NULL;
irr_driver->getSceneManager()->clear();
@ -971,8 +980,11 @@ void World::update(float dt)
const int kart_amount = (int)m_karts.size();
for (int i = 0 ; i < kart_amount; ++i)
{
SpareTireAI* sta =
dynamic_cast<SpareTireAI*>(m_karts[i]->getController());
// Update all karts that are not eliminated
if(!m_karts[i]->isEliminated()) m_karts[i]->update(dt) ;
if(!m_karts[i]->isEliminated() || (sta && sta->isMoving()))
m_karts[i]->update(dt);
}
PROFILER_POP_CPU_MARKER();

View File

@ -360,6 +360,8 @@ public:
void delayedSelfDestruct();
// ------------------------------------------------------------------------
virtual void escapePressed();
// ------------------------------------------------------------------------
virtual void loadCustomModels() {}
/** Set the network mode (true if networked) */
void setNetworkWorld(bool is_networked) { m_is_network_world = is_networked; }

View File

@ -199,7 +199,8 @@ void WorldStatus::updateTime(const float dt)
m_auxiliary_timer += dt;
if (UserConfigParams::m_artist_debug_mode &&
race_manager->getNumberOfKarts() == 1 &&
race_manager->getNumberOfKarts() -
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt * 6;
@ -276,7 +277,8 @@ void WorldStatus::updateTime(const float dt)
// In artist debug mode, when without opponents, skip the
// ready/set/go counter faster
if (UserConfigParams::m_artist_debug_mode &&
race_manager->getNumberOfKarts() == 1 &&
race_manager->getNumberOfKarts() -
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt*6;
@ -302,7 +304,8 @@ void WorldStatus::updateTime(const float dt)
// In artist debug mode, when without opponents,
// skip the ready/set/go counter faster
if (UserConfigParams::m_artist_debug_mode &&
race_manager->getNumberOfKarts() == 1 &&
race_manager->getNumberOfKarts() -
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt*6;
@ -327,7 +330,8 @@ void WorldStatus::updateTime(const float dt)
// In artist debug mode, when without opponents,
// skip the ready/set/go counter faster
if (UserConfigParams::m_artist_debug_mode &&
race_manager->getNumberOfKarts() == 1 &&
race_manager->getNumberOfKarts() -
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt*6;
@ -379,11 +383,16 @@ void WorldStatus::updateTime(const float dt)
default: break;
}
IrrlichtDevice *device = irr_driver->getDevice();
switch (m_clock_mode)
{
case CLOCK_CHRONO:
m_time += dt;
m_count_up_timer += dt;
if (!device->getTimer()->isStopped())
{
m_time += dt;
m_count_up_timer += dt;
}
break;
case CLOCK_COUNTDOWN:
// stop countdown when race is over
@ -394,8 +403,11 @@ void WorldStatus::updateTime(const float dt)
break;
}
m_time -= dt;
m_count_up_timer += dt;
if (!device->getTimer()->isStopped())
{
m_time -= dt;
m_count_up_timer += dt;
}
if(m_time <= 0.0)
{

View File

@ -18,6 +18,7 @@
#include "modes/world_with_rank.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/spare_tire_ai.hpp"
#include "karts/kart_properties.hpp"
#include "race/history.hpp"
#include "tracks/graph.hpp"
@ -251,7 +252,9 @@ void WorldWithRank::updateSectorForKarts()
assert(n == m_kart_track_sector.size());
for (unsigned int i = 0; i < n; i++)
{
if (m_karts[i]->isEliminated()) continue;
getTrackSector(i)->update(m_karts[i]->getXYZ());
SpareTireAI* sta =
dynamic_cast<SpareTireAI*>(m_karts[i]->getController());
if (!m_karts[i]->isEliminated() || (sta && sta->isMoving()))
getTrackSector(i)->update(m_karts[i]->getXYZ());
}
} // updateSectorForKarts

View File

@ -84,6 +84,7 @@ RaceManager::RaceManager()
setTrack("jungle");
m_default_ai_list.clear();
setNumPlayers(0);
setSpareTireKartNum(0);
} // RaceManager
//-----------------------------------------------------------------------------
@ -288,7 +289,7 @@ void RaceManager::computeRandomKartList()
}
if(n>0)
kart_properties_manager->getRandomKartList(n, m_player_karts,
kart_properties_manager->getRandomKartList(n, &m_player_karts,
&m_ai_kart_list );
if (m_ai_kart_override != "")

View File

@ -236,9 +236,10 @@ public:
DIFFICULTY_COUNT};
/** Different kart types: A local player, a player connected via network,
* an AI kart, the leader kart (currently not used), a ghost kart. */
* an AI kart, the leader kart (currently not used), a ghost kart and
* spare tire karts which allow gain life in battle mode */
enum KartType { KT_PLAYER, KT_NETWORK_PLAYER, KT_AI, KT_LEADER,
KT_GHOST };
KT_GHOST, KT_SPARE_TIRE };
private:
bool m_started_from_overworld;
@ -329,6 +330,7 @@ private:
GrandPrixData m_grand_prix;
SavedGrandPrix* m_saved_gp;
int m_num_karts;
unsigned int m_num_spare_tire_karts;
unsigned int m_num_finished_karts;
unsigned int m_num_finished_players;
int m_coin_target;
@ -751,6 +753,24 @@ public:
{
return m_watching_replay;
} // isWatchingReplay
// ------------------------------------------------------------------------
void addSpareTireKart(const std::string& name)
{
m_kart_status.push_back(KartStatus(name, 0, -1, -1,
-1, KT_SPARE_TIRE, PLAYER_DIFFICULTY_NORMAL));
m_num_spare_tire_karts++;
m_num_karts++;
} // addSpareTireKart
// ------------------------------------------------------------------------
void setSpareTireKartNum(unsigned int i)
{
m_num_spare_tire_karts = i;
} // setSpareTireKartNum
// ------------------------------------------------------------------------
unsigned int getNumSpareTireKarts() const
{
return m_num_spare_tire_karts;
} // getNumSpareTireKarts
}; // RaceManager

View File

@ -77,6 +77,8 @@ namespace Scripting
ScriptEngine::~ScriptEngine()
{
// Release the engine
m_pending_timeouts.clearAndDeleteAll();
m_engine->DiscardModule(MODULE_ID_MAIN_SCRIPT_FILE);
m_engine->Release();
}

View File

@ -39,7 +39,7 @@ using namespace irr;
#include "items/attachment_manager.hpp"
#include "items/powerup_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/controller/spare_tire_ai.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/follow_the_leader.hpp"
@ -104,6 +104,18 @@ RaceGUI::RaceGUI()
else
m_lap_width = font->getDimension(L"9/9").Width;
} // RaceGUI
//-----------------------------------------------------------------------------
RaceGUI::~RaceGUI()
{
} // ~Racegui
//-----------------------------------------------------------------------------
void RaceGUI::init()
{
RaceGUIBase::init();
// Technically we only need getNumLocalPlayers, but using the
// global kart id to find the data for a specific kart.
int n = race_manager->getNumberOfKarts();
@ -111,12 +123,7 @@ RaceGUI::RaceGUI()
m_animation_states.resize(n);
m_rank_animation_duration.resize(n);
m_last_ranks.resize(n);
} // RaceGUI
//-----------------------------------------------------------------------------
RaceGUI::~RaceGUI()
{
} // ~Racegui
} // init
//-----------------------------------------------------------------------------
/** Reset the gui before a race. It initialised all rank animation related
@ -369,13 +376,18 @@ void RaceGUI::drawGlobalMiniMap()
for(unsigned int i=0; i<world->getNumKarts(); i++)
{
const AbstractKart *kart = world->getKart(i);
if(kart->isEliminated()) continue; // don't draw eliminated kart
const SpareTireAI* sta =
dynamic_cast<const SpareTireAI*>(kart->getController());
// don't draw eliminated kart
if(kart->isEliminated() && !(sta && sta->isMoving())) continue;
const Vec3& xyz = kart->getXYZ();
Vec3 draw_at;
world->getTrack()->mapPoint2MiniMap(xyz, &draw_at);
draw_at *= UserConfigParams::m_scale_rtts_factor;
video::ITexture* icon = kart->getKartProperties()->getMinimapIcon();
video::ITexture* icon = sta ?
irr_driver->getTexture(FileManager::GUI, "heart.png") :
kart->getKartProperties()->getMinimapIcon();
// int marker_height = m_marker->getSize().Height;
core::rect<s32> source(core::position2di(0, 0), icon->getSize());

View File

@ -119,6 +119,7 @@ public:
RaceGUI();
~RaceGUI();
virtual void init();
virtual void reset();
virtual void renderGlobal(float dt);
virtual void renderPlayerView(const Camera *camera, float dt);

View File

@ -638,8 +638,11 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
y_space = irr_driver->getActualScreenSize().Height - y_base;
}
unsigned int sta = race_manager->getNumSpareTireKarts();
const unsigned int num_karts = race_manager->getNumberOfKarts() - sta;
// -2 because that's the spacing further on
int ICON_PLAYER_WIDTH = y_space / race_manager->getNumberOfKarts() - 2;
int ICON_PLAYER_WIDTH = y_space / num_karts - 2;
int icon_width_max = (int)(50*(irr_driver->getActualScreenSize().Width/800.0f));
int icon_width_min = (int)(35*(irr_driver->getActualScreenSize().Height/600.0f));
@ -664,10 +667,11 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
int ICON_WIDTH = ICON_PLAYER_WIDTH * 4 / 5;
WorldWithRank *world = (WorldWithRank*)(World::getWorld());
//initialize m_previous_icons_position
if(m_previous_icons_position.size()==0)
{
for(unsigned int i=0; i<race_manager->getNumberOfKarts(); i++)
for(unsigned int i=0; i<num_karts; i++)
{
const AbstractKart *kart = world->getKart(i);
int position = kart->getPosition();
@ -686,7 +690,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
int previous_y=y_base-ICON_PLAYER_WIDTH-2;
gui::ScalableFont* font = GUIEngine::getFont();
const unsigned int kart_amount = world->getNumKarts();
const unsigned int kart_amount = world->getNumKarts() - sta;
//where is the limit to hide last icons
int y_icons_limit=irr_driver->getActualScreenSize().Height-bottom_margin-ICON_PLAYER_WIDTH;

View File

@ -460,17 +460,17 @@ void RaceResultGUI::backToLobby()
WorldWithRank *rank_world = (WorldWithRank*)World::getWorld();
unsigned int first_position = 1;
unsigned int sta = race_manager->getNumSpareTireKarts();
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
first_position = 2;
// Use only the karts that are supposed to be displayed (and
// ignore e.g. the leader in a FTL race).
unsigned int num_karts = race_manager->getNumberOfKarts() - first_position + 1;
unsigned int num_karts = race_manager->getNumberOfKarts() - first_position + 1 - sta;
// In FTL races the leader kart is not displayed
m_all_row_infos.resize(num_karts);
// Determine the kart to display in the right order,
// and the maximum width for the kart name column
// -------------------------------------------------
@ -478,7 +478,7 @@ void RaceResultGUI::backToLobby()
float max_finish_time = 0;
for (unsigned int position = first_position;
position <= race_manager->getNumberOfKarts(); position++)
position <= race_manager->getNumberOfKarts() - sta; position++)
{
const AbstractKart *kart = rank_world->getKartAtPosition(position);

View File

@ -44,6 +44,8 @@ ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node)
if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
loadGoalNodes(node);
loadBoundingBoxNodes();
} // ArenaGraph
// -----------------------------------------------------------------------------
@ -148,7 +150,8 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh)
createQuad(all_vertices[quad_index[0]],
all_vertices[quad_index[1]], all_vertices[quad_index[2]],
all_vertices[quad_index[3]], m_all_nodes.size(),
false/*invisible*/, false/*ai_ignore*/, true/*is_arena*/);
false/*invisible*/, false/*ai_ignore*/, true/*is_arena*/,
false/*ignore*/);
ArenaNode* cur_node = getNode(m_all_nodes.size() - 1);
cur_node->setAdjacentNodes(adjacent_quad_index);

View File

@ -98,13 +98,13 @@ void DriveGraph::load(const std::string &quad_file_name,
}
// Each quad is part of the graph exactly once now.
for(unsigned int i=0; i<quad->getNumNodes(); i++)
for (unsigned int i = 0; i < quad->getNumNodes(); i++)
{
const XMLNode *xml_node = quad->getNode(i);
if(xml_node->getName()!="quad")
if (xml_node->getName() != "quad")
{
Log::warn("DriveGraph: Unsupported node type '%s' found in '%s' - ignored.",
xml_node->getName().c_str(), filename.c_str());
xml_node->getName().c_str(), filename.c_str());
continue;
}
@ -117,12 +117,29 @@ void DriveGraph::load(const std::string &quad_file_name,
getPoint(xml_node, "p1", &p1);
getPoint(xml_node, "p2", &p2);
getPoint(xml_node, "p3", &p3);
bool invisible=false;
bool invisible = false;
xml_node->get("invisible", &invisible);
bool ai_ignore=false;
bool ai_ignore = false;
xml_node->get("ai-ignore", &ai_ignore);
bool ignored = false;
std::string direction;
xml_node->get("direction", &direction);
if (direction == "forward" && race_manager->getReverseTrack())
{
ignored = true;
invisible = true;
ai_ignore = true;
}
else if (direction == "reverse" && !race_manager->getReverseTrack())
{
ignored = true;
invisible = true;
ai_ignore = true;
}
createQuad(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore,
false/*is_arena*/);
false/*is_arena*/, ignored);
}
delete quad;
@ -156,7 +173,7 @@ void DriveGraph::load(const std::string &quad_file_name,
const XMLNode *xml_node = xml->getNode(node_index);
// Load the definition of edges between the graph nodes:
// -----------------------------------------------------
if(xml_node->getName()=="node-list")
if (xml_node->getName() == "node-list")
{
// Each quad is part of the graph exactly once now.
unsigned int to = 0;
@ -223,6 +240,8 @@ void DriveGraph::load(const std::string &quad_file_name,
m_lap_length = l;
}
loadBoundingBoxNodes();
} // load
// ----------------------------------------------------------------------------

View File

@ -28,8 +28,8 @@
DriveNode::DriveNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
const Vec3 &p3, const Vec3 &normal,
unsigned int node_index, bool invisible,
bool ai_ignore)
:Quad(p0, p1, p2, p3, normal, node_index, invisible)
bool ai_ignore, bool ignored)
:Quad(p0, p1, p2, p3, normal, node_index, invisible, ignored)
{
m_ai_ignore = ai_ignore;
m_distance_from_start = -1.0f;

View File

@ -96,6 +96,7 @@ private:
*/
std::vector< int > m_checkline_requirements;
// ------------------------------------------------------------------------
void markAllSuccessorsToUse(unsigned int n,
PathToNodeVector *m_path_to_node);
@ -104,7 +105,7 @@ public:
DriveNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
const Vec3 &p3, const Vec3 &normal,
unsigned int node_index, bool invisible,
bool ai_ignore);
bool ai_ignore, bool ignored);
// ------------------------------------------------------------------------
virtual ~DriveNode() {}
// ------------------------------------------------------------------------

View File

@ -22,9 +22,9 @@
DriveNode2D::DriveNode2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
const Vec3 &p3, const Vec3 &normal,
unsigned int node_index, bool invisible,
bool ai_ignore)
bool ai_ignore, bool ignored)
: DriveNode(p0, p1, p2, p3, normal, node_index, invisible,
ai_ignore)
ai_ignore, ignored)
{
m_line = core::line2df(m_upper_center.getX(), m_upper_center.getZ(),
m_lower_center.getX(), m_lower_center.getZ());

View File

@ -45,7 +45,7 @@ private:
public:
DriveNode2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
const Vec3 &normal, unsigned int node_index, bool invisible,
bool ai_ignore);
bool ai_ignore, bool ignored);
// ------------------------------------------------------------------------
virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE;
// ------------------------------------------------------------------------

View File

@ -22,9 +22,9 @@
DriveNode3D::DriveNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
const Vec3 &p3, const Vec3 &normal,
unsigned int node_index, bool invisible,
bool ai_ignore)
bool ai_ignore, bool ignored)
: DriveNode(p0, p1, p2, p3, normal, node_index, invisible,
ai_ignore), BoundingBox3D(p0, p1, p2, p3, normal)
ai_ignore, ignored), BoundingBox3D(p0, p1, p2, p3, normal)
{
m_line = core::line3df(m_lower_center.toIrrVector(),
m_upper_center.toIrrVector());

View File

@ -38,7 +38,7 @@ private:
public:
DriveNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
const Vec3 &normal, unsigned int node_index, bool invisible,
bool ai_ignore);
bool ai_ignore, bool ignored);
// ------------------------------------------------------------------------
virtual bool pointInside(const Vec3& p,
bool ignore_vertical = false) const OVERRIDE

View File

@ -46,6 +46,7 @@ Graph::Graph()
m_new_rtt = NULL;
m_bb_min = Vec3( 99999, 99999, 99999);
m_bb_max = Vec3(-99999, -99999, -99999);
memset(m_bb_nodes, 0, 4 * sizeof(int));
} // Graph
// -----------------------------------------------------------------------------
@ -399,7 +400,7 @@ void Graph::mapPoint2MiniMap(const Vec3 &xyz,Vec3 *draw_at) const
// -----------------------------------------------------------------------------
void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
const Vec3 &p3, unsigned int node_index,
bool invisible, bool ai_ignore, bool is_arena)
bool invisible, bool ai_ignore, bool is_arena, bool ignored)
{
// Find the normal of this quad by computing the normal of two triangles
// and taking their average.
@ -427,7 +428,7 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
else
{
q = new DriveNode3D(p0, p1, p2, p3, normal, node_index, invisible,
ai_ignore);
ai_ignore, ignored);
}
}
else
@ -441,7 +442,7 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
else
{
q = new DriveNode2D(p0, p1, p2, p3, normal, node_index, invisible,
ai_ignore);
ai_ignore, ignored);
}
}
m_all_nodes.push_back(q);
@ -499,7 +500,7 @@ void Graph::findRoadSector(const Vec3& xyz, int *sector,
else
indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0;
const Quad* q = getQuad(indx);
if(q->pointInside(xyz, ignore_vertical))
if (q->pointInside(xyz, ignore_vertical))
{
*sector = indx;
return;
@ -585,24 +586,27 @@ int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector,
? 0
: current_sector+1;
// A first simple test uses the 2d distance to the center of the
// quad.
float dist_2 =
m_all_nodes[next_sector]->getDistance2FromPoint(xyz);
if(dist_2<min_dist_2)
const Quad* q = getQuad(next_sector);
if (!q->isIgnored())
{
const Quad* q = getQuad(next_sector);
float dist = xyz.getY() - q->getMinHeight();
// While negative distances are unlikely, we allow some small
// negative numbers in case that the kart is partly in the
// track. Only do the height test in phase==0, in phase==1
// accept any point, independent of height, or this node is 3d
// which already takes height into account
if(phase==1 || (dist < 5.0f && dist>-1.0f) ||
q->is3DQuad() || ignore_vertical)
// A first simple test uses the 2d distance to the center of the
// quad.
float dist_2 =
m_all_nodes[next_sector]->getDistance2FromPoint(xyz);
if (dist_2 < min_dist_2)
{
min_dist_2 = dist_2;
min_sector = next_sector;
float dist = xyz.getY() - q->getMinHeight();
// While negative distances are unlikely, we allow some small
// negative numbers in case that the kart is partly in the
// track. Only do the height test in phase==0, in phase==1
// accept any point, independent of height, or this node is 3d
// which already takes height into account
if (phase == 1 || (dist < 5.0f && dist>-1.0f) ||
q->is3DQuad() || ignore_vertical)
{
min_dist_2 = dist_2;
min_sector = next_sector;
}
}
}
current_sector = next_sector;
@ -618,3 +622,16 @@ int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector,
}
return min_sector;
} // findOutOfRoadSector
//-----------------------------------------------------------------------------
void Graph::loadBoundingBoxNodes()
{
m_bb_nodes[0] = findOutOfRoadSector(Vec3(m_bb_min.x(), 0, m_bb_min.z()),
-1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/);
m_bb_nodes[1] = findOutOfRoadSector(Vec3(m_bb_min.x(), 0, m_bb_max.z()),
-1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/);
m_bb_nodes[2] = findOutOfRoadSector(Vec3(m_bb_max.x(), 0, m_bb_min.z()),
-1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/);
m_bb_nodes[3] = findOutOfRoadSector(Vec3(m_bb_max.x(), 0, m_bb_max.z()),
-1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/);
} // loadBoundingBoxNodes

View File

@ -61,13 +61,19 @@ protected:
* graph. */
void createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
const Vec3 &p3, unsigned int node_index,
bool invisible, bool ai_ignore, bool is_arena);
bool invisible, bool ai_ignore, bool is_arena, bool ignore);
// ------------------------------------------------------------------------
/** Map 4 bounding box points to 4 closest graph nodes. */
void loadBoundingBoxNodes();
private:
/** The 2d bounding box, used for hashing. */
Vec3 m_bb_min;
Vec3 m_bb_max;
/** The 4 closest graph nodes to the bounding box. */
int m_bb_nodes[4];
RTT* m_new_rtt;
/** The node of the graph mesh. */
@ -149,6 +155,12 @@ public:
const int curr_sector = UNKNOWN_SECTOR,
std::vector<int> *all_sectors = NULL,
bool ignore_vertical = false) const;
// ------------------------------------------------------------------------
const Vec3& getBBMin() const { return m_bb_min; }
// ------------------------------------------------------------------------
const Vec3& getBBMax() const { return m_bb_max; }
// ------------------------------------------------------------------------
const int* getBBNodes() const { return m_bb_nodes; }
}; // Graph

View File

@ -82,7 +82,13 @@ LODNode* ModelDefinitionLoader::instanciateAsLOD(const XMLNode* node, scene::ISc
continue;
}
irr_driver->setAllMaterialFlags(a_mesh);
scene::ISkinnedMesh* sm =
dynamic_cast<scene::ISkinnedMesh*>(a_mesh);
if (sm)
{
MeshTools::createSkinnedMeshWithTangents(sm,
&MeshTools::isNormalMap);
}
a_mesh->grab();
//cache.push_back(a_mesh);

View File

@ -25,16 +25,17 @@
/** Constructor, takes 4 points. */
Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
const Vec3 &normal, int index, bool invisible)
const Vec3 &normal, int index, bool invisible, bool ignored)
: m_index(index), m_normal(normal), m_invisible(invisible)
{
m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3;
m_center = 0.25f*(p0+p1+p2+p3);
m_min_height = std::min ( std::min(p0.getY(), p1.getY()),
std::min(p2.getY(), p3.getY()) );
m_max_height = std::max ( std::max(p0.getY(), p1.getY()),
std::max(p2.getY(), p3.getY()) );
m_is_ignored = ignored;
m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3;
m_center = 0.25f*(p0+p1+p2+p3);
m_min_height = std::min ( std::min(p0.getY(), p1.getY()),
std::min(p2.getY(), p3.getY()) );
m_max_height = std::max ( std::max(p0.getY(), p1.getY()),
std::max(p2.getY(), p3.getY()) );
} // Quad
// ----------------------------------------------------------------------------

View File

@ -54,6 +54,8 @@ private:
/** Set to true if this quad should not be shown in the minimap. */
bool m_invisible;
bool m_is_ignored;
/** The minimum height of the quad, used in case that several quads
* are on top of each other when determining the sector a kart is on. */
float m_min_height;
@ -67,7 +69,7 @@ public:
// ------------------------------------------------------------------------
Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
const Vec3 & normal = Vec3(0, 1, 0), int index = -1,
bool invisible = false);
bool invisible = false, bool ignored = false);
// ------------------------------------------------------------------------
virtual ~Quad() {}
// ------------------------------------------------------------------------
@ -93,6 +95,8 @@ public:
/** Returns true of this quad is invisible, i.e. not to be shown in
* the minimap. */
bool isInvisible() const { return m_invisible; }
// ------------------------------------------------------------------------
bool isIgnored() const { return m_is_ignored; }
// ------------------------------------------------------------------------
/** Returns the normal of this quad. */
const Vec3& getNormal() const { return m_normal; }

View File

@ -702,11 +702,7 @@ btQuaternion Track::getArenaStartRotation(const Vec3& xyz, float heading)
}
const Vec3& normal = Graph::get()->getQuad(node)->getNormal();
Vec3 axis = -normal.cross(Vec3(0, 1, 0));
if (axis.length() == 0)
axis = Vec3(0, 0, 1);
btQuaternion q(axis, normal.angle(Vec3(0, 1, 0)));
btQuaternion q = createRotationFromNormal(normal);
btMatrix3x3 m;
m.setRotation(q);
return btQuaternion(m.getColumn(1), heading * DEGREE_TO_RAD) * q;
@ -1174,7 +1170,7 @@ bool Track::loadMainTrack(const XMLNode &root)
handleAnimatedTextures(scene_node, *track_node);
m_all_nodes.push_back(scene_node);
MeshTools::minMax3D(merged_mesh, &m_aabb_min, &m_aabb_max);
MeshTools::minMax3D(tangent_mesh, &m_aabb_min, &m_aabb_max);
// Increase the maximum height of the track: since items that fly
// too high explode, e.g. cakes can not be show when being at the
// top of the track (since they will explode when leaving the AABB
@ -1868,8 +1864,10 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
sun->getLightData().SpecularColor = m_sun_specular_color;
}
else
{
irr_driver->createSunInterposer();
m_sun->grab();
}
createPhysicsModel(main_track_count);

View File

@ -397,6 +397,14 @@ public:
static const float NOHIT;
static btQuaternion createRotationFromNormal(const Vec3& normal)
{
Vec3 axis = -normal.cross(Vec3(0, 1, 0));
if (axis.length() == 0)
axis = Vec3(0, 0, 1);
return btQuaternion(axis, normal.angle(Vec3(0, 1, 0)));
} // createRotationFromNormal
Track (const std::string &filename);
~Track ();
void cleanup ();

View File

@ -368,6 +368,16 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(
m_mesh = MeshTools::createMeshWithTangents(m_mesh,
&MeshTools::isNormalMap);
}
else
{
scene::ISkinnedMesh* sm =
dynamic_cast<scene::ISkinnedMesh*>(m_mesh);
if (sm)
{
MeshTools::createSkinnedMeshWithTangents(sm,
&MeshTools::isNormalMap);
}
}
init(&xml_node, parent, enabled);
} // TrackObjectPresentationMesh
@ -409,9 +419,21 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(
if (file_manager->fileExists(model_file))
{
if (animated)
{
m_mesh = irr_driver->getAnimatedMesh(model_file);
scene::ISkinnedMesh* sm =
dynamic_cast<scene::ISkinnedMesh*>(m_mesh);
if (sm)
{
MeshTools::createSkinnedMeshWithTangents(sm,
&MeshTools::isNormalMap);
}
}
else
m_mesh = irr_driver->getMesh(model_file);
{
m_mesh = MeshTools::createMeshWithTangents(
irr_driver->getMesh(model_file), &MeshTools::isNormalMap);
}
}
if (!m_mesh)
@ -959,6 +981,7 @@ TrackObjectPresentationLight::TrackObjectPresentationLight(
scene::ISceneNode* parent)
: TrackObjectPresentationSceneNode(xml_node)
{
m_color.set(0);
xml_node.get("color", &m_color);
const video::SColorf colorf(m_color);

View File

@ -182,12 +182,9 @@ void changeCameraTarget(u32 num)
{
AbstractKart* kart = world->getKart(num - 1);
if (kart == NULL) return;
if (kart->isEliminated()) return;
cam->setMode(Camera::CM_NORMAL);
cam->setKart(kart);
}
else
return;
} // changeCameraTarget