Merge remote-tracking branch 'origin/master' into rewind
Fixed conflicts.
This commit is contained in:
@@ -67,6 +67,7 @@ set(ANGELSCRIPT_SOURCE
|
||||
../../source/as_builder.cpp
|
||||
../../source/as_bytecode.cpp
|
||||
../../source/as_callfunc.cpp
|
||||
../../source/as_callfunc_mips.cpp
|
||||
../../source/as_callfunc_x86.cpp
|
||||
../../source/as_callfunc_x64_gcc.cpp
|
||||
../../source/as_callfunc_x64_msvc.cpp
|
||||
|
||||
@@ -844,7 +844,7 @@
|
||||
#define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
|
||||
#define AS_X86
|
||||
#undef AS_NO_THISCALL_FUNCTOR_METHOD
|
||||
#elif defined(__LP64__) && !defined(__arm64__)
|
||||
#elif defined(__x86_64__)
|
||||
#define AS_X64_GCC
|
||||
#undef AS_NO_THISCALL_FUNCTOR_METHOD
|
||||
#define HAS_128_BIT_PRIMITIVES
|
||||
|
||||
@@ -16,3 +16,5 @@ add_library(glew STATIC
|
||||
include/GL/glew.h include/GL/glxew.h include/GL/wglew.h
|
||||
src/glew.c src/glewinfo.c
|
||||
)
|
||||
|
||||
target_link_libraries(glew ${OPENGL_LIBRARIES})
|
||||
|
||||
@@ -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/*")
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
|
||||
#include "ICameraSceneNode.h"
|
||||
|
||||
@@ -62,7 +62,7 @@ void CameraEnd::readEndCamera(const XMLNode &root)
|
||||
unsigned int index = i;
|
||||
// In reverse mode, reverse the order in which the
|
||||
// end cameras are read.
|
||||
if(QuadGraph::get()->isReverse())
|
||||
if(DriveGraph::get() != NULL && DriveGraph::get()->isReverse())
|
||||
index = root.getNumNodes() - 1 - i;
|
||||
const XMLNode *node = root.getNode(index);
|
||||
EndCameraInformation eci;
|
||||
|
||||
@@ -101,8 +101,7 @@ void CameraNormal::smoothMoveCamera(float dt)
|
||||
Vec3 m_kart_camera_position_with_offset = m_kart->getTrans()(camera_offset);
|
||||
|
||||
// next target
|
||||
core::vector3df current_target = m_kart->getXYZ().toIrrVector();
|
||||
current_target.Y += 0.5f;
|
||||
Vec3 current_target = m_kart->getTrans()(Vec3(0, 0.5f, 0));
|
||||
// new required position of camera
|
||||
core::vector3df wanted_position = m_kart_camera_position_with_offset.toIrrVector();
|
||||
|
||||
@@ -128,7 +127,7 @@ void CameraNormal::smoothMoveCamera(float dt)
|
||||
|
||||
if(getMode()!=CM_FALLING)
|
||||
m_camera->setPosition(current_position);
|
||||
m_camera->setTarget(current_target);//set new target
|
||||
m_camera->setTarget(current_target.toIrrVector());//set new target
|
||||
|
||||
assert(!std::isnan(m_camera->getPosition().X));
|
||||
assert(!std::isnan(m_camera->getPosition().Y));
|
||||
@@ -252,8 +251,7 @@ void CameraNormal::positionCamera(float dt, float above_kart, float cam_angle,
|
||||
float side_way, float distance, float smoothing)
|
||||
{
|
||||
Vec3 wanted_position;
|
||||
Vec3 wanted_target = m_kart->getXYZ();
|
||||
wanted_target.setY(wanted_target.getY() + above_kart);
|
||||
Vec3 wanted_target = m_kart->getTrans()(Vec3(0, above_kart, 0));
|
||||
|
||||
float tan_up = tan(cam_angle);
|
||||
Vec3 relative_position(side_way,
|
||||
|
||||
@@ -355,7 +355,7 @@ void IrrDriver::createListOfVideoModes()
|
||||
void IrrDriver::initDevice()
|
||||
{
|
||||
SIrrlichtCreationParameters params;
|
||||
|
||||
|
||||
// If --no-graphics option was used, the null device can still be used.
|
||||
if (!ProfileWorld::isNoGraphics())
|
||||
{
|
||||
@@ -438,7 +438,7 @@ void IrrDriver::initDevice()
|
||||
m_device->drop();
|
||||
m_device = NULL;
|
||||
|
||||
params.ForceLegacyDevice = (UserConfigParams::m_force_legacy_device ||
|
||||
params.ForceLegacyDevice = (UserConfigParams::m_force_legacy_device ||
|
||||
UserConfigParams::m_gamepad_visualisation);
|
||||
|
||||
// Try 32 and, upon failure, 24 then 16 bit per pixels
|
||||
@@ -463,9 +463,9 @@ void IrrDriver::initDevice()
|
||||
core::dimension2du(UserConfigParams::m_width,
|
||||
UserConfigParams::m_height);
|
||||
params.HandleSRGB = true;
|
||||
params.ShadersPath = (file_manager->getShadersDir() +
|
||||
params.ShadersPath = (file_manager->getShadersDir() +
|
||||
"irrlicht/").c_str();
|
||||
|
||||
|
||||
/*
|
||||
switch ((int)UserConfigParams::m_antialiasing)
|
||||
{
|
||||
@@ -526,13 +526,13 @@ void IrrDriver::initDevice()
|
||||
{
|
||||
Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n");
|
||||
}
|
||||
|
||||
|
||||
CVS->init();
|
||||
|
||||
|
||||
bool recreate_device = false;
|
||||
|
||||
|
||||
// Some drivers are able to create OpenGL 3.1 context, but shader-based
|
||||
// pipeline doesn't work for them. For example some radeon drivers
|
||||
// pipeline doesn't work for them. For example some radeon drivers
|
||||
// support only GLSL 1.3 and it causes STK to crash. We should force to use
|
||||
// fixed pipeline in this case.
|
||||
if (!ProfileWorld::isNoGraphics() &&
|
||||
@@ -542,13 +542,13 @@ void IrrDriver::initDevice()
|
||||
"Re-creating device to workaround the issue.");
|
||||
|
||||
params.ForceLegacyDevice = true;
|
||||
recreate_device = true;
|
||||
recreate_device = true;
|
||||
}
|
||||
|
||||
|
||||
// This is the ugly hack for intel driver on linux, which doesn't
|
||||
// use sRGB-capable visual, even if we request it. This causes
|
||||
// the screen to be darker than expected. It affects mesa 10.6 and newer.
|
||||
// Though we are able to force to use the proper format on mesa side by
|
||||
// the screen to be darker than expected. It affects mesa 10.6 and newer.
|
||||
// Though we are able to force to use the proper format on mesa side by
|
||||
// setting WithAlphaChannel parameter.
|
||||
else if (CVS->needsSRGBCapableVisualWorkaround())
|
||||
{
|
||||
@@ -558,19 +558,21 @@ void IrrDriver::initDevice()
|
||||
params.WithAlphaChannel = true;
|
||||
recreate_device = true;
|
||||
}
|
||||
|
||||
|
||||
if (!ProfileWorld::isNoGraphics() && recreate_device)
|
||||
{
|
||||
m_device->closeDevice();
|
||||
m_device->drop();
|
||||
|
||||
m_device->clearSystemMessages();
|
||||
m_device->run();
|
||||
m_device->drop();
|
||||
|
||||
m_device = createDeviceEx(params);
|
||||
|
||||
|
||||
if(!m_device)
|
||||
{
|
||||
Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n");
|
||||
}
|
||||
|
||||
|
||||
CVS->init();
|
||||
}
|
||||
|
||||
@@ -587,7 +589,7 @@ void IrrDriver::initDevice()
|
||||
(UserConfigParams::m_shadows_resolution < 512 ||
|
||||
UserConfigParams::m_shadows_resolution > 2048))
|
||||
{
|
||||
Log::warn("irr_driver",
|
||||
Log::warn("irr_driver",
|
||||
"Invalid value for UserConfigParams::m_shadows_resolution : %i",
|
||||
(int)UserConfigParams::m_shadows_resolution);
|
||||
UserConfigParams::m_shadows_resolution = 0;
|
||||
@@ -697,7 +699,7 @@ void IrrDriver::setMaxTextureSize()
|
||||
{
|
||||
io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
|
||||
att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(
|
||||
UserConfigParams::m_max_texture_size,
|
||||
UserConfigParams::m_max_texture_size,
|
||||
UserConfigParams::m_max_texture_size));
|
||||
}
|
||||
} // setMaxTextureSize
|
||||
@@ -725,7 +727,7 @@ void IrrDriver::createSunInterposer()
|
||||
mb->getMaterial().setTexture(7,
|
||||
getUnicolorTexture(video::SColor(0, 0, 0, 0)));
|
||||
}
|
||||
m_sun_interposer = new STKMeshSceneNode(sphere,
|
||||
m_sun_interposer = new STKMeshSceneNode(sphere,
|
||||
m_scene_manager->getRootSceneNode(),
|
||||
NULL, -1, "sun_interposer");
|
||||
|
||||
@@ -1176,7 +1178,7 @@ scene::IMeshSceneNode *IrrDriver::addSphere(float radius,
|
||||
{
|
||||
scene::IMesh *mesh = m_scene_manager->getGeometryCreator()
|
||||
->createSphereMesh(radius);
|
||||
|
||||
|
||||
mesh->setMaterialFlag(video::EMF_COLOR_MATERIAL, true);
|
||||
video::SMaterial &m = mesh->getMeshBuffer(0)->getMaterial();
|
||||
m.AmbientColor = color;
|
||||
@@ -1227,7 +1229,7 @@ scene::IMeshSceneNode *IrrDriver::addMesh(scene::IMesh *mesh,
|
||||
if (!parent)
|
||||
parent = m_scene_manager->getRootSceneNode();
|
||||
|
||||
scene::IMeshSceneNode* node = new STKMeshSceneNode(mesh, parent,
|
||||
scene::IMeshSceneNode* node = new STKMeshSceneNode(mesh, parent,
|
||||
m_scene_manager, -1,
|
||||
debug_name,
|
||||
core::vector3df(0, 0, 0),
|
||||
@@ -1266,7 +1268,7 @@ scene::ISceneNode *IrrDriver::addBillboard(const core::dimension2d< f32 > size,
|
||||
if (!parent)
|
||||
parent = m_scene_manager->getRootSceneNode();
|
||||
|
||||
node = new STKBillboard(parent, m_scene_manager, -1,
|
||||
node = new STKBillboard(parent, m_scene_manager, -1,
|
||||
vector3df(0., 0., 0.), size);
|
||||
node->drop();
|
||||
}
|
||||
@@ -1487,7 +1489,7 @@ scene::ISceneNode *IrrDriver::addSkyBox(const std::vector<video::ITexture*> &tex
|
||||
{
|
||||
m_spherical_harmonics->setTextures(spherical_harmonics_textures);
|
||||
}
|
||||
|
||||
|
||||
return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
|
||||
texture[2], texture[3],
|
||||
texture[4], texture[5]);
|
||||
@@ -2249,13 +2251,13 @@ void IrrDriver::update(float dt)
|
||||
renderGLSL(dt);
|
||||
else
|
||||
renderFixed(dt);
|
||||
|
||||
|
||||
GUIEngine::Screen* current_screen = GUIEngine::getCurrentScreen();
|
||||
if (current_screen != NULL && current_screen->needs3D())
|
||||
{
|
||||
GUIEngine::render(dt);
|
||||
}
|
||||
|
||||
|
||||
if (world->getPhysics() != NULL)
|
||||
{
|
||||
IrrDebugDrawer* debug_drawer = world->getPhysics()->getDebugDrawer();
|
||||
@@ -2274,7 +2276,7 @@ void IrrDriver::update(float dt)
|
||||
|
||||
m_video_driver->endScene();
|
||||
}
|
||||
|
||||
|
||||
if (m_request_screenshot) doScreenShot();
|
||||
|
||||
// Enable this next print statement to get render information printed
|
||||
@@ -2448,7 +2450,7 @@ void IrrDriver::RTTProvider::setupRTTScene(PtrVector<scene::IMesh, REF>& mesh,
|
||||
}
|
||||
|
||||
irr_driver->getSceneManager()->setAmbientLight(video::SColor(255, 35, 35, 35) );
|
||||
|
||||
|
||||
const core::vector3df &spot_pos = core::vector3df(0, 30, 40);
|
||||
m_light = irr_driver->getSceneManager()
|
||||
->addLightSceneNode(NULL, spot_pos, video::SColorf(1.0f,1.0f,1.0f),
|
||||
@@ -2564,9 +2566,9 @@ void IrrDriver::applyObjectPassShader(scene::ISceneNode * const node, bool rimli
|
||||
|
||||
const u32 mcount = node->getMaterialCount();
|
||||
u32 i;
|
||||
const video::E_MATERIAL_TYPE ref =
|
||||
const video::E_MATERIAL_TYPE ref =
|
||||
Shaders::getShader(rimlit ? ES_OBJECTPASS_RIMLIT : ES_OBJECTPASS_REF);
|
||||
const video::E_MATERIAL_TYPE pass =
|
||||
const video::E_MATERIAL_TYPE pass =
|
||||
Shaders::getShader(rimlit ? ES_OBJECTPASS_RIMLIT : ES_OBJECTPASS);
|
||||
|
||||
const video::E_MATERIAL_TYPE origref = Shaders::getShader(ES_OBJECTPASS_REF);
|
||||
@@ -2593,7 +2595,7 @@ void IrrDriver::applyObjectPassShader(scene::ISceneNode * const node, bool rimli
|
||||
for (i = 0; i < mcount; i++)
|
||||
{
|
||||
video::SMaterial &nodemat = node->getMaterial(i);
|
||||
video::SMaterial &mbmat = mesh ? mesh->getMeshBuffer(i)->getMaterial()
|
||||
video::SMaterial &mbmat = mesh ? mesh->getMeshBuffer(i)->getMaterial()
|
||||
: nodemat;
|
||||
video::SMaterial *mat = &nodemat;
|
||||
|
||||
|
||||
@@ -153,9 +153,9 @@ void SkidMarks::update(float dt, bool force_skid_marks,
|
||||
float distance = (newPoint - start).length();
|
||||
|
||||
m_left [m_current]->add(raycast_left-delta, raycast_left+delta,
|
||||
distance);
|
||||
m_kart.getNormal(), distance);
|
||||
m_right[m_current]->add(raycast_right-delta, raycast_right+delta,
|
||||
distance);
|
||||
m_kart.getNormal(), distance);
|
||||
// Adjust the boundary box of the mesh to include the
|
||||
// adjusted aabb of its buffers.
|
||||
core::aabbox3df aabb=m_nodes[m_current]->getMesh()
|
||||
@@ -180,14 +180,16 @@ void SkidMarks::update(float dt, bool force_skid_marks,
|
||||
delta *= m_width*0.5f;
|
||||
|
||||
SkidMarkQuads *smq_left =
|
||||
new SkidMarkQuads(raycast_left-delta, raycast_left+delta ,
|
||||
m_material, m_avoid_z_fighting, custom_color);
|
||||
new SkidMarkQuads(raycast_left-delta, raycast_left+delta,
|
||||
m_kart.getNormal(), m_material, m_avoid_z_fighting,
|
||||
custom_color);
|
||||
scene::SMesh *new_mesh = new scene::SMesh();
|
||||
new_mesh->addMeshBuffer(smq_left);
|
||||
|
||||
SkidMarkQuads *smq_right =
|
||||
new SkidMarkQuads(raycast_right-delta, raycast_right+delta,
|
||||
m_material, m_avoid_z_fighting, custom_color);
|
||||
m_kart.getNormal(), m_material, m_avoid_z_fighting,
|
||||
custom_color);
|
||||
new_mesh->addMeshBuffer(smq_right);
|
||||
scene::IMeshSceneNode *new_node = irr_driver->addMesh(new_mesh, "skidmark");
|
||||
if (STKMeshSceneNode* stkm = dynamic_cast<STKMeshSceneNode*>(new_node))
|
||||
@@ -233,6 +235,7 @@ void SkidMarks::update(float dt, bool force_skid_marks,
|
||||
//=============================================================================
|
||||
SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left,
|
||||
const Vec3 &right,
|
||||
const Vec3 &normal,
|
||||
video::SMaterial *material,
|
||||
float z_offset,
|
||||
video::SColor* custom_color)
|
||||
@@ -250,7 +253,7 @@ SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left,
|
||||
|
||||
Material = *material;
|
||||
m_aabb = core::aabbox3df(left.toIrrVector());
|
||||
add(left, right, 0.0f);
|
||||
add(left, right, normal, 0.0f);
|
||||
|
||||
|
||||
} // SkidMarkQuads
|
||||
@@ -261,6 +264,7 @@ SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left,
|
||||
*/
|
||||
void SkidMarks::SkidMarkQuads::add(const Vec3 &left,
|
||||
const Vec3 &right,
|
||||
const Vec3 &normal,
|
||||
float distance)
|
||||
{
|
||||
// The skid marks must be raised slightly higher, otherwise it blends
|
||||
@@ -280,13 +284,11 @@ void SkidMarks::SkidMarkQuads::add(const Vec3 &left,
|
||||
Vertices[n - 2].Color.setAlpha(m_start_alpha);
|
||||
}
|
||||
|
||||
v.Pos = left.toIrrVector();
|
||||
v.Pos.Y += m_z_offset;
|
||||
v.Normal = core::vector3df(0, 1, 0);
|
||||
v.Pos = Vec3(left + normal * m_z_offset).toIrrVector();
|
||||
v.Normal = normal.toIrrVector();
|
||||
v.TCoords = core::vector2df(0.0f, distance*0.5f);
|
||||
Vertices.push_back(v);
|
||||
v.Pos = right.toIrrVector();
|
||||
v.Pos.Y += m_z_offset;
|
||||
v.Pos = Vec3(right + normal * m_z_offset).toIrrVector();
|
||||
v.TCoords = core::vector2df(1.0f, distance*0.5f);
|
||||
Vertices.push_back(v);
|
||||
// Out of the box Irrlicht only supports triangle meshes and not
|
||||
|
||||
@@ -85,10 +85,11 @@ private:
|
||||
|
||||
public:
|
||||
SkidMarkQuads (const Vec3 &left, const Vec3 &right,
|
||||
video::SMaterial *material, float z_offset,
|
||||
video::SColor* custom_color = NULL);
|
||||
const Vec3 &normal, video::SMaterial *material,
|
||||
float z_offset, video::SColor* custom_color = NULL);
|
||||
void add (const Vec3 &left,
|
||||
const Vec3 &right,
|
||||
const Vec3 &normal,
|
||||
float distance);
|
||||
void fade (float f);
|
||||
/** Returns the aabb of this skid mark quads. */
|
||||
|
||||
@@ -77,7 +77,6 @@ SlipStream::SlipStream(AbstractKart* kart) : MovingTexture(0, 0), m_kart(kart)
|
||||
p[1]=Vec3(-ew*0.5f, 0, -kl*0.5f-length);
|
||||
p[2]=Vec3( ew*0.5f, 0, -kl*0.5f-length);
|
||||
p[3]=Vec3( kw*0.5f, 0, -kl*0.5f );
|
||||
m_slipstream_original_quad = new Quad(p[0], p[1], p[2], p[3]);
|
||||
m_slipstream_quad = new Quad(p[0], p[1], p[2], p[3]);
|
||||
if(UserConfigParams::m_slipstream_debug)
|
||||
{
|
||||
@@ -127,7 +126,6 @@ SlipStream::~SlipStream()
|
||||
m_debug_node->drop();
|
||||
m_debug_mesh->drop();
|
||||
}
|
||||
delete m_slipstream_original_quad;
|
||||
delete m_slipstream_quad;
|
||||
|
||||
} // ~SlipStream
|
||||
@@ -369,12 +367,6 @@ void SlipStream::update(float dt)
|
||||
|
||||
MovingTexture::update(dt);
|
||||
|
||||
// Update this karts slipstream quad (even for low level AI which don't
|
||||
// use slipstream, since even then player karts can get slipstream,
|
||||
// and so have to compare with the modified slipstream quad.
|
||||
m_slipstream_original_quad->transform(m_kart->getTrans(),
|
||||
m_slipstream_quad);
|
||||
|
||||
if(m_slipstream_mode==SS_USE)
|
||||
{
|
||||
m_slipstream_time -= dt;
|
||||
@@ -418,12 +410,13 @@ void SlipStream::update(float dt)
|
||||
m_target_kart->getKartAnimation() ||
|
||||
m_target_kart->isEliminated() ) continue;
|
||||
|
||||
float diff = fabsf(m_target_kart->getXYZ().getY()
|
||||
- m_kart->getXYZ().getY() );
|
||||
// Transform this kart location into target kart point of view
|
||||
Vec3 lc = m_target_kart->getTrans().inverse()(m_kart->getXYZ());
|
||||
|
||||
// If the kart is 'on top' of this kart (e.g. up on a bridge),
|
||||
// don't consider it for slipstreaming.
|
||||
if (fabsf(lc.y()) > 6.0f) continue;
|
||||
|
||||
if(diff>6.0f) continue;
|
||||
// If the kart we are testing against is too slow, no need to test
|
||||
// slipstreaming. Note: We compare the speed of the other kart
|
||||
// against the minimum slipstream speed kart of this kart - not
|
||||
@@ -447,7 +440,7 @@ void SlipStream::update(float dt)
|
||||
float l = kp->getSlipstreamLength()
|
||||
+ 0.5f*( m_target_kart->getKartLength()
|
||||
+m_kart->getKartLength() );
|
||||
if(delta.length2_2d() > l*l)
|
||||
if(delta.length2() > l*l)
|
||||
{
|
||||
if(UserConfigParams::m_slipstream_debug &&
|
||||
m_kart->getController()->isLocalPlayerController())
|
||||
@@ -457,7 +450,7 @@ void SlipStream::update(float dt)
|
||||
}
|
||||
// Real test: if in slipstream quad of other kart
|
||||
if(m_target_kart->getSlipstream()->m_slipstream_quad
|
||||
->pointInQuad(m_kart->getXYZ()))
|
||||
->pointInside(lc))
|
||||
{
|
||||
is_sstreaming = true;
|
||||
break;
|
||||
|
||||
@@ -66,14 +66,9 @@ private:
|
||||
* 'slipstream credits', or the kart is using accumulated credits. */
|
||||
enum {SS_NONE, SS_COLLECT, SS_USE} m_slipstream_mode;
|
||||
|
||||
/** The quad inside which another kart is considered to be slipstreaming.
|
||||
* This value is current area, i.e. takes the kart position into account. */
|
||||
/** This is slipstream area if the kart is at 0,0,0 without rotation. */
|
||||
Quad *m_slipstream_quad;
|
||||
|
||||
/** This is slipstream area if the kart is at 0,0,0 without rotation. From
|
||||
* this value m_slipstream_area is computed by applying the kart transform. */
|
||||
Quad *m_slipstream_original_quad;
|
||||
|
||||
/** The kart from which this kart gets slipstream. Used by the AI to
|
||||
** overtake the right kart. */
|
||||
AbstractKart* m_target_kart;
|
||||
|
||||
@@ -572,19 +572,17 @@ void Attachment::update(float dt)
|
||||
Vec3 hit_point;
|
||||
Vec3 normal;
|
||||
const Material* material_hit;
|
||||
Vec3 pos = m_kart->getXYZ();
|
||||
Vec3 to=pos+Vec3(0, -10000, 0);
|
||||
World* world = World::getWorld();
|
||||
world->getTrack()->getTriangleMesh().castRay(pos, to, &hit_point,
|
||||
&material_hit, &normal);
|
||||
world->getTrack()->getTriangleMesh().castRay(m_kart->getXYZ(),
|
||||
m_kart->getTrans().getBasis() * Vec3(0, -10000, 0), &hit_point,
|
||||
&material_hit, &normal);
|
||||
// This can happen if the kart is 'over nothing' when dropping
|
||||
// the bubble gum
|
||||
if(material_hit)
|
||||
{
|
||||
normal.normalize();
|
||||
|
||||
pos.setY(hit_point.getY()-0.05f);
|
||||
|
||||
Vec3 pos = hit_point + m_kart->getTrans().getBasis() * Vec3(0, -0.05f, 0);
|
||||
ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_kart);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "graphics/material.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
#include "utils/log.hpp" //TODO: remove after debugging is done
|
||||
@@ -55,10 +56,11 @@ Bowling::Bowling(AbstractKart *kart)
|
||||
if(m_speed < min_speed) m_speed = min_speed;
|
||||
}
|
||||
|
||||
const Vec3& normal = kart->getNormal();
|
||||
createPhysics(y_offset, btVector3(0.0f, 0.0f, m_speed*2),
|
||||
new btSphereShape(0.5f*m_extend.getY()),
|
||||
0.8f /*restitution*/,
|
||||
-70.0f /*gravity*/,
|
||||
0.4f /*restitution*/,
|
||||
-70.0f*normal /*gravity*/,
|
||||
true /*rotates*/);
|
||||
// Even if the ball is fired backwards, m_speed must be positive,
|
||||
// otherwise the ball can start to vibrate when energy is added.
|
||||
@@ -136,14 +138,15 @@ bool Bowling::updateAndDelete(float dt)
|
||||
m_body->applyCentralForce(direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Bowling balls lose energy (e.g. when hitting the track), so increase
|
||||
// the speed if the ball is too slow, but only if it's not too high (if
|
||||
// the ball is too high, it is 'pushed down', which can reduce the
|
||||
// speed, which causes the speed to increase, which in turn causes
|
||||
// the ball to fly higher and higher.
|
||||
//btTransform trans = getTrans();
|
||||
float hat = getXYZ().getY()-getHoT();
|
||||
float hat = (getXYZ() - getHitPoint()).length();
|
||||
if(hat-0.5f*m_extend.getY()<0.01f)
|
||||
{
|
||||
const Material *material = getMaterial();
|
||||
|
||||
@@ -35,6 +35,12 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE)
|
||||
{
|
||||
m_target = NULL;
|
||||
|
||||
setDoTerrainInfo(false);
|
||||
|
||||
btVector3 gravity_vector;
|
||||
btQuaternion q = kart->getTrans().getRotation();
|
||||
gravity_vector = Vec3(0, -1, 0).rotate(q.getAxis(), q.getAngle());
|
||||
gravity_vector = gravity_vector.normalize() * m_gravity;
|
||||
// A bit of a hack: the mass of this kinematic object is still 1.0
|
||||
// (see flyable), which enables collisions. I tried setting
|
||||
// collisionFilterGroup/mask, but still couldn't get this object to
|
||||
@@ -85,13 +91,14 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE)
|
||||
&fire_angle, &up_velocity);
|
||||
|
||||
// apply transformation to the bullet object (without pitch)
|
||||
trans.setRotation(btQuaternion(btVector3(0,1,0), fire_angle));
|
||||
|
||||
btQuaternion q;
|
||||
q = trans.getRotation() * btQuaternion(btVector3(0, 1, 0), fire_angle);
|
||||
trans.setRotation(q);
|
||||
m_initial_velocity = Vec3(0.0f, up_velocity, m_speed);
|
||||
|
||||
createPhysics(forward_offset, m_initial_velocity,
|
||||
new btCylinderShape(0.5f*m_extend),
|
||||
0.5f /* restitution */, -m_gravity,
|
||||
0.5f /* restitution */, gravity_vector,
|
||||
true /* rotation */, false /* backwards */, &trans);
|
||||
}
|
||||
else
|
||||
@@ -105,7 +112,7 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE)
|
||||
|
||||
createPhysics(forward_offset, m_initial_velocity,
|
||||
new btCylinderShape(0.5f*m_extend),
|
||||
0.5f /* restitution */, -m_gravity,
|
||||
0.5f /* restitution */, gravity_vector,
|
||||
true /* rotation */, backwards, &trans);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,14 @@
|
||||
|
||||
#include "graphics/explosion.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
#include "graphics/mesh_tools.hpp"
|
||||
#include "graphics/stars.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "items/projectile_manager.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/explosion_animation.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "modes/soccer_world.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
@@ -101,7 +102,7 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type,
|
||||
*/
|
||||
void Flyable::createPhysics(float forw_offset, const Vec3 &velocity,
|
||||
btCollisionShape *shape,
|
||||
float restitution, const float gravity,
|
||||
float restitution, const btVector3& gravity,
|
||||
const bool rotates, const bool turn_around,
|
||||
const btTransform* custom_direction)
|
||||
{
|
||||
@@ -133,7 +134,7 @@ void Flyable::createPhysics(float forw_offset, const Vec3 &velocity,
|
||||
m_user_pointer.set(this);
|
||||
World::getWorld()->getPhysics()->addBody(getBody());
|
||||
|
||||
m_body->setGravity(btVector3(0.0f, gravity, 0));
|
||||
m_body->setGravity(gravity);
|
||||
|
||||
// Rotate velocity to point in the right direction
|
||||
btVector3 v=trans.getBasis()*velocity;
|
||||
@@ -296,65 +297,59 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin,
|
||||
float *fire_angle,
|
||||
float *up_velocity)
|
||||
{
|
||||
Vec3 relative_target_kart_loc = target_kart->getXYZ() - origin;
|
||||
|
||||
// Transform the target into the firing kart's frame of reference
|
||||
btTransform inv_trans = m_owner->getTrans().inverse();
|
||||
|
||||
Vec3 relative_target_kart_loc = inv_trans(target_kart->getXYZ());
|
||||
|
||||
// Find the direction target is moving in
|
||||
btTransform trans = target_kart->getTrans();
|
||||
Vec3 target_direction(trans.getBasis().getColumn(2));
|
||||
|
||||
float dx = relative_target_kart_loc.getX();
|
||||
float dy = relative_target_kart_loc.getY();
|
||||
float dz = relative_target_kart_loc.getZ();
|
||||
// Now rotate it to the firing kart's frame of reference
|
||||
btQuaternion inv_rotate = inv_trans.getRotation();
|
||||
target_direction =
|
||||
target_direction.rotate(inv_rotate.getAxis(), inv_rotate.getAngle());
|
||||
|
||||
// Now we try to find the angle to aim at to hit the target.
|
||||
// Warning : Funky math stuff going on below. To understand, see answer by
|
||||
// Jeffrey Hantin here :
|
||||
// http://stackoverflow.com/questions/2248876/2d-game-fire-at-a-moving-target-by-predicting-intersection-of-projectile-and-u
|
||||
|
||||
float target_x_speed = target_direction.getX()*target_kart->getSpeed();
|
||||
float target_z_speed = target_direction.getZ()*target_kart->getSpeed();
|
||||
float target_y_speed = target_direction.getY()*target_kart->getSpeed();
|
||||
|
||||
float gy = target_direction.getY();
|
||||
float a = (target_x_speed*target_x_speed) + (target_z_speed*target_z_speed) -
|
||||
(item_XZ_speed*item_XZ_speed);
|
||||
float b = 2 * (target_x_speed * (relative_target_kart_loc.getX())
|
||||
+ target_z_speed * (relative_target_kart_loc.getZ()));
|
||||
float c = relative_target_kart_loc.getX()*relative_target_kart_loc.getX()
|
||||
+ relative_target_kart_loc.getZ()*relative_target_kart_loc.getZ();
|
||||
|
||||
float discriminant = b*b - 4 * a*c;
|
||||
if (discriminant < 0) discriminant = 0;
|
||||
|
||||
//Projected onto X-Z plane
|
||||
float target_kart_speed = target_direction.length_2d()
|
||||
* target_kart->getSpeed();
|
||||
|
||||
float target_kart_heading = target_kart->getHeading();
|
||||
|
||||
float dist = -(target_kart_speed / item_XZ_speed)
|
||||
* (dx * cosf(target_kart_heading) -
|
||||
dz * sinf(target_kart_heading) );
|
||||
|
||||
float f = dx*dx + dz*dz - dist*dist;
|
||||
// Avoid negative square root
|
||||
if(f>0)
|
||||
f = sqrtf(f);
|
||||
else
|
||||
f = 0.0f;
|
||||
|
||||
float fire_th = (dx*dist - dz * f)
|
||||
/ (dx*dx + dz*dz);
|
||||
if(fire_th>1)
|
||||
fire_th = 1.0f;
|
||||
else if (fire_th<-1.0f)
|
||||
fire_th = -1.0f;
|
||||
fire_th = (((dist - dx*fire_th) / dz > 0) ? -acosf(fire_th)
|
||||
: acosf(fire_th));
|
||||
|
||||
float time = 0.0f;
|
||||
float a = item_XZ_speed * sinf (fire_th)
|
||||
+ target_kart_speed * sinf (target_kart_heading);
|
||||
float b = item_XZ_speed * cosf (fire_th)
|
||||
+ target_kart_speed * cosf (target_kart_heading);
|
||||
|
||||
if (fabsf(a) > fabsf(b)) time = fabsf (dx / a);
|
||||
else if (b != 0.0f) time = fabsf(dz / b);
|
||||
|
||||
if (fire_th > M_PI)
|
||||
fire_th -= M_PI;
|
||||
else
|
||||
fire_th += M_PI;
|
||||
float t1 = (-b + sqrt(discriminant)) / (2 * a);
|
||||
float t2 = (-b - sqrt(discriminant)) / (2 * a);
|
||||
float time;
|
||||
if (t1 >= 0 && t1<t2) time = t1;
|
||||
else time = t2;
|
||||
|
||||
//createPhysics offset
|
||||
assert(sqrt(a*a+b*b)!=0);
|
||||
time -= forw_offset / sqrt(a*a+b*b);
|
||||
time -= forw_offset / item_XZ_speed;
|
||||
|
||||
float aimX = time*target_x_speed + relative_target_kart_loc.getX();
|
||||
float aimZ = time*target_z_speed + relative_target_kart_loc.getZ();
|
||||
|
||||
assert(time!=0);
|
||||
*fire_angle = fire_th;
|
||||
*up_velocity = (0.5f * time * gravity) + (dy / time)
|
||||
+ (gy * target_kart->getSpeed());
|
||||
float angle = atan2f(aimX, aimZ);
|
||||
|
||||
*fire_angle = angle;
|
||||
|
||||
// Now find the up_velocity. This is an application of newton's equation.
|
||||
*up_velocity = (0.5f * time * gravity) + (relative_target_kart_loc.getY() / time)
|
||||
+ ( target_y_speed);
|
||||
} // getLinearKartItemIntersection
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -396,15 +391,32 @@ bool Flyable::updateAndDelete(float dt)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add the position offset so that the flyable can adjust its position
|
||||
// (usually to do the raycast from a slightly higher position to avoid
|
||||
// problems finding the terrain in steep uphill sections).
|
||||
if(m_do_terrain_info)
|
||||
TerrainInfo::update(xyz+m_position_offset);
|
||||
if (m_do_terrain_info)
|
||||
{
|
||||
Vec3 towards = getBody()->getGravity();
|
||||
towards.normalize();
|
||||
// Add the position offset so that the flyable can adjust its position
|
||||
// (usually to do the raycast from a slightly higher position to avoid
|
||||
// problems finding the terrain in steep uphill sections).
|
||||
// Towards is a unit vector. so we can multiply -towards to offset the
|
||||
// position by one unit.
|
||||
TerrainInfo::update(xyz + m_position_offset*(-towards), towards);
|
||||
|
||||
// Make flyable anti-gravity when the it's projected on such surface
|
||||
const Material* m = TerrainInfo::getMaterial();
|
||||
if (m && m->hasGravity())
|
||||
{
|
||||
getBody()->setGravity(TerrainInfo::getNormal() * -70.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
getBody()->setGravity(Vec3(0, 1, 0) * -70.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_adjust_up_velocity)
|
||||
{
|
||||
float hat = xyz.getY()-getHoT();
|
||||
float hat = (xyz - getHitPoint()).length();
|
||||
|
||||
// Use the Height Above Terrain to set the Z velocity.
|
||||
// HAT is clamped by min/max height. This might be somewhat
|
||||
|
||||
@@ -149,7 +149,7 @@ protected:
|
||||
const Vec3 &velocity,
|
||||
btCollisionShape *shape,
|
||||
float restitution,
|
||||
const float gravity=0.0f,
|
||||
const btVector3& gravity=btVector3(0.0f,0.0f,0.0f),
|
||||
const bool rotates=false,
|
||||
const bool turn_around=false,
|
||||
const btTransform* customDirection=NULL);
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "modes/easter_egg_hunt.hpp"
|
||||
#include "modes/three_strikes_battle.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
@@ -37,10 +40,14 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
|
||||
{
|
||||
assert(type != ITEM_TRIGGER); // use other constructor for that
|
||||
|
||||
m_distance_2 = 0.8f;
|
||||
m_distance_2 = 1.2f;
|
||||
initItem(type, xyz);
|
||||
// Sets heading to 0, and sets pitch and roll depending on the normal. */
|
||||
m_original_hpr = Vec3(0, normal);
|
||||
|
||||
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_rotation_angle = 0.0f;
|
||||
m_original_mesh = mesh;
|
||||
m_original_lowmesh = lowres_mesh;
|
||||
m_listener = NULL;
|
||||
@@ -76,7 +83,9 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
|
||||
World::getWorld()->getTrack()->adjustForFog(m_node);
|
||||
m_node->setAutomaticCulling(scene::EAC_FRUSTUM_BOX);
|
||||
m_node->setPosition(xyz.toIrrVector());
|
||||
m_node->setRotation(m_original_hpr.toIrrHPR());
|
||||
Vec3 hpr;
|
||||
hpr.setHPR(m_original_rotation);
|
||||
m_node->setRotation(hpr.toIrrHPR());
|
||||
m_node->grab();
|
||||
} // Item(type, xyz, normal, mesh, lowres_mesh)
|
||||
|
||||
@@ -90,8 +99,8 @@ Item::Item(const Vec3& xyz, float distance, TriggerItemListener* trigger)
|
||||
{
|
||||
m_distance_2 = distance*distance;
|
||||
initItem(ITEM_TRIGGER, xyz);
|
||||
// Sets heading to 0, and sets pitch and roll depending on the normal. */
|
||||
m_original_hpr = Vec3(0, 0, 0);
|
||||
m_original_rotation = btQuaternion(0, 0, 0, 1);
|
||||
m_rotation_angle = 0.0f;
|
||||
m_original_mesh = NULL;
|
||||
m_original_lowmesh = NULL;
|
||||
m_node = NULL;
|
||||
@@ -126,31 +135,25 @@ void Item::initItem(ItemType type, const Vec3 &xyz)
|
||||
}
|
||||
// Now determine in which quad this item is, and its distance
|
||||
// from the center within this quad.
|
||||
m_graph_node = QuadGraph::UNKNOWN_SECTOR;
|
||||
QuadGraph* currentQuadGraph = QuadGraph::get();
|
||||
m_graph_node = Graph::UNKNOWN_SECTOR;
|
||||
m_distance_from_center = 9999.9f;
|
||||
m_avoidance_points[0] = NULL;
|
||||
m_avoidance_points[1] = NULL;
|
||||
|
||||
// Check that QuadGraph exist (it might not in battle mode for eg)
|
||||
if (currentQuadGraph != NULL)
|
||||
// Check that Graph exist (it might not in battle mode without navmesh)
|
||||
if (Graph::get())
|
||||
{
|
||||
QuadGraph::get()->findRoadSector(xyz, &m_graph_node);
|
||||
Graph::get()->findRoadSector(xyz, &m_graph_node);
|
||||
}
|
||||
|
||||
if(m_graph_node==QuadGraph::UNKNOWN_SECTOR)
|
||||
if (DriveGraph::get() && m_graph_node != Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
m_graph_node = -1;
|
||||
m_distance_from_center = 9999.9f; // is not used
|
||||
m_avoidance_points[0] = NULL;
|
||||
m_avoidance_points[1] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item is on quad graph. Pre-compute the distance from center
|
||||
// Item is on drive graph. Pre-compute the distance from center
|
||||
// of this item, which is used by the AI (mostly for avoiding items)
|
||||
Vec3 distances;
|
||||
QuadGraph::get()->spatialToTrack(&distances, m_xyz, m_graph_node);
|
||||
DriveGraph::get()->spatialToTrack(&distances, m_xyz, m_graph_node);
|
||||
m_distance_from_center = distances.getX();
|
||||
const GraphNode &gn = QuadGraph::get()->getNode(m_graph_node);
|
||||
const Vec3 right = gn.getRightUnitVector();
|
||||
const DriveNode* dn = DriveGraph::get()->getNode(m_graph_node);
|
||||
const Vec3& right = dn->getRightUnitVector();
|
||||
// Give it 10% more space, since the kart will not always come
|
||||
// parallel to the drive line.
|
||||
Vec3 delta = right * sqrt(m_distance_2) * 1.3f;
|
||||
@@ -223,7 +226,9 @@ void Item::switchBack()
|
||||
}
|
||||
|
||||
World::getWorld()->getTrack()->adjustForFog(m_node);
|
||||
m_node->setRotation(m_original_hpr.toIrrHPR());
|
||||
Vec3 hpr;
|
||||
hpr.setHPR(m_original_rotation);
|
||||
m_node->setRotation(hpr.toIrrHPR());
|
||||
} // switchBack
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -320,12 +325,17 @@ void Item::update(float dt)
|
||||
|
||||
if(!m_rotate || m_node == NULL) return;
|
||||
// have it rotate
|
||||
Vec3 rotation(0, dt*M_PI, 0);
|
||||
core::vector3df r = m_node->getRotation();
|
||||
r.Y += dt*180.0f;
|
||||
if(r.Y>360.0f) r.Y -= 360.0f;
|
||||
m_rotation_angle += dt * M_PI ;
|
||||
if (m_rotation_angle > M_PI * 2) m_rotation_angle -= M_PI * 2;
|
||||
|
||||
m_node->setRotation(r);
|
||||
btMatrix3x3 m;
|
||||
m.setRotation(m_original_rotation);
|
||||
btQuaternion r = btQuaternion(m.getColumn(1), m_rotation_angle) *
|
||||
m_original_rotation;
|
||||
|
||||
Vec3 hpr;
|
||||
hpr.setHPR(r);
|
||||
m_node->setRotation(hpr.toIrrHPR());
|
||||
return;
|
||||
} // not m_collected
|
||||
} // update
|
||||
|
||||
@@ -102,7 +102,10 @@ private:
|
||||
* (bubble gums don't rotate, but it will be replaced with
|
||||
* a nitro which rotates, and so overwrites the original
|
||||
* rotation). */
|
||||
Vec3 m_original_hpr;
|
||||
btQuaternion m_original_rotation;
|
||||
|
||||
/** Used when rotating the item */
|
||||
float m_rotation_angle;
|
||||
|
||||
/** True if item was collected & is not displayed. */
|
||||
bool m_collected;
|
||||
@@ -181,7 +184,6 @@ public:
|
||||
|
||||
const AbstractKart* getEmitter() const { return m_emitter; }
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the Kart is close enough to hit this item, the item is
|
||||
* not deactivated anymore, and it wasn't placed by this kart (this is
|
||||
@@ -190,31 +192,12 @@ public:
|
||||
* \param xyz Location of kart (avoiding to use kart->getXYZ() so that
|
||||
* kart.hpp does not need to be included here).
|
||||
*/
|
||||
bool hitKart (const Vec3 &xyz, const AbstractKart *kart=NULL) const
|
||||
bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const
|
||||
{
|
||||
return (m_event_handler!=kart || m_deactive_time <=0) &&
|
||||
(xyz-m_xyz).length2()<m_distance_2;
|
||||
} // hitKart
|
||||
|
||||
private:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the Kart is close enough to hit this item, the item is
|
||||
* not deactivated anymore, and it wasn't placed by this kart (this is
|
||||
* e.g. used to avoid that a kart hits a bubble gum it just dropped).
|
||||
* This function only uses the 2d coordinates, and it used by the AI only.
|
||||
* \param kart Kart to test.
|
||||
* \param xyz Location of kart (avoiding to use kart->getXYZ() so that
|
||||
* kart.hpp does not need to be included here).
|
||||
*/
|
||||
bool hitKart (const core::vector2df &xyz,
|
||||
const AbstractKart *kart=NULL) const
|
||||
{
|
||||
if(m_event_handler==kart && m_deactive_time >0) return false;
|
||||
float d2 = (m_xyz.getX()-xyz.X)*(m_xyz.getX()-xyz.X)
|
||||
+ (m_xyz.getZ()-xyz.Y)*(m_xyz.getZ()-xyz.Y);
|
||||
return d2 < m_distance_2;
|
||||
} // hitKart
|
||||
|
||||
protected:
|
||||
// ------------------------------------------------------------------------
|
||||
// Some convenient functions for the AI only
|
||||
@@ -225,12 +208,12 @@ protected:
|
||||
* \param line The line segment which is tested if it is close enough
|
||||
* to this item so that this item would be collected.
|
||||
*/
|
||||
bool hitLine(const core::line2df &line,
|
||||
bool hitLine(const core::line3df &line,
|
||||
const AbstractKart *kart=NULL) const
|
||||
{
|
||||
if(m_event_handler==kart && m_deactive_time >0) return false;
|
||||
core::vector2df p2d = m_xyz.toIrrVector2d();
|
||||
core::vector2df closest = line.getClosestPoint(p2d);
|
||||
|
||||
Vec3 closest = line.getClosestPoint(m_xyz.toIrrVector());
|
||||
return hitKart(closest, kart);
|
||||
} // hitLine
|
||||
|
||||
|
||||
@@ -32,8 +32,9 @@
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/race_event_manager.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
#include "tracks/arena_node.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
@@ -157,12 +158,12 @@ ItemManager::ItemManager()
|
||||
m_switch_to.push_back((Item::ItemType)i);
|
||||
setSwitchItems(stk_config->m_switch_items);
|
||||
|
||||
if(QuadGraph::get())
|
||||
if(Graph::get())
|
||||
{
|
||||
m_items_in_quads = new std::vector<AllItemTypes>;
|
||||
// Entries 0 to n-1 are for the quads, entry
|
||||
// n is for all items that are not on a quad.
|
||||
m_items_in_quads->resize(QuadSet::get()->getNumberOfQuads()+1);
|
||||
m_items_in_quads->resize(Graph::get()->getNumNodes()+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -224,11 +225,10 @@ void ItemManager::insertItem(Item *item)
|
||||
if(m_items_in_quads)
|
||||
{
|
||||
int graph_node = item->getGraphNode();
|
||||
// If the item is on the driveline, store it at the appropriate index
|
||||
// If the item is on the graph, store it at the appropriate index
|
||||
if(graph_node > -1)
|
||||
{
|
||||
int sector = QuadGraph::get()->getNode(graph_node).getQuadIndex();
|
||||
(*m_items_in_quads)[sector].push_back(item);
|
||||
(*m_items_in_quads)[graph_node].push_back(item);
|
||||
}
|
||||
else // otherwise store it in the 'outside' index
|
||||
(*m_items_in_quads)[m_items_in_quads->size()-1].push_back(item);
|
||||
@@ -425,10 +425,8 @@ void ItemManager::deleteItem(Item *item)
|
||||
// First check if the item needs to be removed from the items-in-quad list
|
||||
if(m_items_in_quads)
|
||||
{
|
||||
const Vec3 &xyz = item->getXYZ();
|
||||
int sector = QuadGraph::UNKNOWN_SECTOR;
|
||||
QuadGraph::get()->findRoadSector(xyz, §or);
|
||||
unsigned int indx = sector==QuadGraph::UNKNOWN_SECTOR
|
||||
int sector = item->getGraphNode();
|
||||
unsigned int indx = sector==Graph::UNKNOWN_SECTOR
|
||||
? (unsigned int) m_items_in_quads->size()-1
|
||||
: sector;
|
||||
AllItemTypes &items = (*m_items_in_quads)[indx];
|
||||
@@ -480,40 +478,41 @@ void ItemManager::switchItems()
|
||||
bool ItemManager::randomItemsForArena(const AlignedArray<btTransform>& pos)
|
||||
{
|
||||
if (!UserConfigParams::m_random_arena_item) return false;
|
||||
if (!BattleGraph::get()) return false;
|
||||
if (!ArenaGraph::get()) return false;
|
||||
|
||||
const ArenaGraph* ag = ArenaGraph::get();
|
||||
std::vector<int> used_location;
|
||||
std::vector<int> invalid_location;
|
||||
for (unsigned int i = 0; i < pos.size(); i++)
|
||||
{
|
||||
// Load all starting positions of arena, so no items will be near them
|
||||
int node = BattleGraph::get()->pointToNode(/*cur_node*/-1,
|
||||
Vec3(pos[i].getOrigin()), /*ignore_vertical*/true);
|
||||
int node = -1;
|
||||
ag->findRoadSector(pos[i].getOrigin(), &node, NULL, true);
|
||||
assert(node != -1);
|
||||
used_location.push_back(node);
|
||||
invalid_location.push_back(node);
|
||||
}
|
||||
|
||||
RandomGenerator random;
|
||||
const unsigned int MIN_DIST = int(sqrt(BattleGraph::get()->getNumNodes()));
|
||||
const unsigned int ALL_NODES = ag->getNumNodes();
|
||||
const unsigned int MIN_DIST = int(sqrt(ALL_NODES));
|
||||
const unsigned int TOTAL_ITEM = MIN_DIST / 2;
|
||||
|
||||
Log::info("[ItemManager]","Creating %d random items for arena", TOTAL_ITEM);
|
||||
for (unsigned int i = 0; i < TOTAL_ITEM; i++)
|
||||
{
|
||||
int chosen_node = -1;
|
||||
const unsigned int total_node = BattleGraph::get()->getNumNodes();
|
||||
while(true)
|
||||
{
|
||||
if (used_location.size() - pos.size() +
|
||||
invalid_location.size() == total_node)
|
||||
invalid_location.size() == ALL_NODES)
|
||||
{
|
||||
Log::warn("[ItemManager]","Can't place more random items! "
|
||||
"Use default item location.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int node = random.get(total_node);
|
||||
const int node = random.get(ALL_NODES);
|
||||
|
||||
// Check if tried
|
||||
std::vector<int>::iterator it = std::find(invalid_location.begin(),
|
||||
@@ -522,7 +521,7 @@ bool ItemManager::randomItemsForArena(const AlignedArray<btTransform>& pos)
|
||||
continue;
|
||||
|
||||
// Check if near edge
|
||||
if (BattleGraph::get()->isNearEdge(node))
|
||||
if (ag->getNode(node)->isNearEdge())
|
||||
{
|
||||
invalid_location.push_back(node);
|
||||
continue;
|
||||
@@ -532,8 +531,7 @@ bool ItemManager::randomItemsForArena(const AlignedArray<btTransform>& pos)
|
||||
for (unsigned int j = 0; j < used_location.size(); j++)
|
||||
{
|
||||
if (!found) continue;
|
||||
float test_distance = BattleGraph::get()
|
||||
->getDistance(used_location[j], node);
|
||||
float test_distance = ag->getDistance(used_location[j], node);
|
||||
found = test_distance > MIN_DIST;
|
||||
}
|
||||
if (found)
|
||||
@@ -566,10 +564,31 @@ bool ItemManager::randomItemsForArena(const AlignedArray<btTransform>& pos)
|
||||
Item::ItemType type = (j > BONUS_BOX ? Item::ITEM_BONUS_BOX :
|
||||
j > NITRO_BIG ? Item::ITEM_NITRO_BIG :
|
||||
j > NITRO_SMALL ? Item::ITEM_NITRO_SMALL : Item::ITEM_BANANA);
|
||||
Vec3 loc = BattleGraph::get()
|
||||
->getQuadOfNode(used_location[i]).getCenter();
|
||||
Item* item = newItem(type, loc, Vec3(0, 1, 0));
|
||||
BattleGraph::get()->insertItems(item, used_location[i]);
|
||||
|
||||
ArenaNode* an = ag->getNode(used_location[i]);
|
||||
Vec3 loc = an->getCenter();
|
||||
Vec3 quad_normal = an->getNormal();
|
||||
loc += quad_normal;
|
||||
|
||||
// Do a raycast to help place it fully on the surface
|
||||
const Material* m;
|
||||
Vec3 normal;
|
||||
Vec3 hit_point;
|
||||
const TriangleMesh& tm =
|
||||
World::getWorld()->getTrack()->getTriangleMesh();
|
||||
bool success = tm.castRay(loc, an->getCenter() + (-10000*quad_normal),
|
||||
&hit_point, &m, &normal);
|
||||
|
||||
if (success)
|
||||
{
|
||||
newItem(type, hit_point, normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::warn("[ItemManager]","Raycast to surface failed"
|
||||
"from node %d", used_location[i]);
|
||||
newItem(type, an->getCenter(), quad_normal);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -82,7 +82,7 @@ private:
|
||||
|
||||
/** Stores which items are on which quad. m_items_in_quads[#quads]
|
||||
* contains all items that are not on a quad. Note that this
|
||||
* field is undefined if no QuadGraph exist, e.g. in battle mode. */
|
||||
* field is undefined if no Graph exist, e.g. arena without navmesh. */
|
||||
std::vector< AllItemTypes > *m_items_in_quads;
|
||||
|
||||
/** What item this item is switched to. */
|
||||
@@ -132,6 +132,16 @@ public:
|
||||
assert(n<(*m_items_in_quads).size());
|
||||
return (*m_items_in_quads)[n];
|
||||
} // getItemsInQuads
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the first item (NULL if none) on the specified quad
|
||||
*/
|
||||
Item* getFirstItemInQuad(unsigned int n) const
|
||||
{
|
||||
assert(m_items_in_quads);
|
||||
assert(n < m_items_in_quads->size());
|
||||
return ((*m_items_in_quads)[n]).empty() ? NULL :
|
||||
(*m_items_in_quads)[n].front();
|
||||
} // getFirstItemInQuad
|
||||
}; // ItemManager
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,6 +41,8 @@ Plunger::Plunger(AbstractKart *kart)
|
||||
{
|
||||
const float gravity = 0.0f;
|
||||
|
||||
setDoTerrainInfo(false);
|
||||
|
||||
float forward_offset = 0.5f*kart->getKartLength()+0.5f*m_extend.getZ();
|
||||
float up_velocity = 0.0f;
|
||||
float plunger_speed = 2 * m_speed;
|
||||
@@ -56,7 +58,6 @@ Plunger::Plunger(AbstractKart *kart)
|
||||
kart /* search in front of this kart */, m_reverse_mode);
|
||||
|
||||
btTransform kart_transform = kart->getAlignedTransform();
|
||||
btMatrix3x3 kart_rotation = kart_transform.getBasis();
|
||||
|
||||
float heading =kart->getHeading();
|
||||
float pitch = kart->getTerrainPitch(heading);
|
||||
@@ -70,21 +71,22 @@ Plunger::Plunger(AbstractKart *kart)
|
||||
&fire_angle, &up_velocity);
|
||||
|
||||
btTransform trans = kart->getTrans();
|
||||
|
||||
trans.setRotation(btQuaternion(btVector3(0, 1, 0), fire_angle));
|
||||
btQuaternion q;
|
||||
q = trans.getRotation()*(btQuaternion(btVector3(0, 1, 0), fire_angle));
|
||||
trans.setRotation(q);
|
||||
|
||||
m_initial_velocity = btVector3(0.0f, up_velocity, plunger_speed);
|
||||
|
||||
createPhysics(forward_offset, m_initial_velocity,
|
||||
new btCylinderShape(0.5f*m_extend),
|
||||
0.5f /* restitution */ , gravity,
|
||||
0.5f /* restitution */ , btVector3(.0f,gravity,.0f),
|
||||
/* rotates */false , /*turn around*/false, &trans);
|
||||
}
|
||||
else
|
||||
{
|
||||
createPhysics(forward_offset, btVector3(pitch, 0.0f, plunger_speed),
|
||||
new btCylinderShape(0.5f*m_extend),
|
||||
0.5f /* restitution */, gravity,
|
||||
0.5f /* restitution */, btVector3(.0f,gravity,.0f),
|
||||
false /* rotates */, m_reverse_mode, &kart_transform);
|
||||
}
|
||||
|
||||
|
||||
@@ -278,9 +278,9 @@ void Powerup::use()
|
||||
Vec3 normal;
|
||||
const Material* material_hit;
|
||||
Vec3 pos = m_kart->getXYZ();
|
||||
Vec3 to=pos+Vec3(0, -10000, 0);
|
||||
Vec3 to = pos+ m_kart->getTrans().getBasis() * Vec3(0, -10000, 0);
|
||||
world->getTrack()->getTriangleMesh().castRay(pos, to, &hit_point,
|
||||
&material_hit, &normal);
|
||||
&material_hit, &normal);
|
||||
// This can happen if the kart is 'over nothing' when dropping
|
||||
// the bubble gum
|
||||
if(!material_hit)
|
||||
@@ -290,8 +290,7 @@ void Powerup::use()
|
||||
Powerup::adjustSound();
|
||||
m_sound_use->play();
|
||||
|
||||
pos.setY(hit_point.getY()-0.05f);
|
||||
|
||||
pos = hit_point + m_kart->getTrans().getBasis() * Vec3(0, -0.05f, 0);
|
||||
ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_kart);
|
||||
}
|
||||
else // if the kart is looking forward, use the bubblegum as a shield
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "physics/btKart.hpp"
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
|
||||
#include "utils/log.hpp" //TODO: remove after debugging is done
|
||||
@@ -66,8 +68,8 @@ RubberBall::RubberBall(AbstractKart *kart)
|
||||
float forw_offset = 0.5f*kart->getKartLength() + m_extend.getZ()*0.5f+5.0f;
|
||||
|
||||
createPhysics(forw_offset, btVector3(0.0f, 0.0f, m_speed*2),
|
||||
new btSphereShape(0.5f*m_extend.getY()),
|
||||
-70.0f /*gravity*/,
|
||||
new btSphereShape(0.5f*m_extend.getY()), -70.0f,
|
||||
btVector3(.0f,.0f,.0f) /*gravity*/,
|
||||
true /*rotates*/);
|
||||
|
||||
// Do not adjust the up velocity
|
||||
@@ -97,7 +99,9 @@ RubberBall::RubberBall(AbstractKart *kart)
|
||||
|
||||
// initialises the current graph node
|
||||
TrackSector::update(getXYZ());
|
||||
TerrainInfo::update(getXYZ());
|
||||
const Vec3& normal =
|
||||
DriveGraph::get()->getNode(getCurrentGraphNode())->getNormal();
|
||||
TerrainInfo::update(getXYZ(), -normal);
|
||||
initializeControlPoints(m_owner->getXYZ());
|
||||
|
||||
} // RubberBall
|
||||
@@ -132,7 +136,7 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz)
|
||||
// left or right when firing the ball off track.
|
||||
getNextControlPoint();
|
||||
m_control_points[2] =
|
||||
QuadGraph::get()->getQuadOfNode(m_last_aimed_graph_node).getCenter();
|
||||
DriveGraph::get()->getNode(m_last_aimed_graph_node)->getCenter();
|
||||
|
||||
// This updates m_last_aimed_graph_node, and sets m_control_points[3]
|
||||
getNextControlPoint();
|
||||
@@ -197,12 +201,12 @@ unsigned int RubberBall::getSuccessorToHitTarget(unsigned int node_index,
|
||||
|
||||
unsigned int sect =
|
||||
lin_world->getSectorForKart(m_target);
|
||||
succ = QuadGraph::get()->getNode(node_index).getSuccessorToReach(sect);
|
||||
succ = DriveGraph::get()->getNode(node_index)->getSuccessorToReach(sect);
|
||||
|
||||
if(dist)
|
||||
*dist += QuadGraph::get()->getNode(node_index)
|
||||
.getDistanceToSuccessor(succ);
|
||||
return QuadGraph::get()->getNode(node_index).getSuccessor(succ);
|
||||
*dist += DriveGraph::get()->getNode(node_index)
|
||||
->getDistanceToSuccessor(succ);
|
||||
return DriveGraph::get()->getNode(node_index)->getSuccessor(succ);
|
||||
} // getSuccessorToHitTarget
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -220,21 +224,21 @@ void RubberBall::getNextControlPoint()
|
||||
// spline between the control points.
|
||||
float dist=0;
|
||||
|
||||
float f = QuadGraph::get()->getDistanceFromStart(m_last_aimed_graph_node);
|
||||
float f = DriveGraph::get()->getDistanceFromStart(m_last_aimed_graph_node);
|
||||
|
||||
int next = getSuccessorToHitTarget(m_last_aimed_graph_node, &dist);
|
||||
float d = QuadGraph::get()->getDistanceFromStart(next)-f;
|
||||
float d = DriveGraph::get()->getDistanceFromStart(next)-f;
|
||||
while(d<m_st_min_interpolation_distance && d>=0)
|
||||
{
|
||||
next = getSuccessorToHitTarget(next, &dist);
|
||||
d = QuadGraph::get()->getDistanceFromStart(next)-f;
|
||||
d = DriveGraph::get()->getDistanceFromStart(next)-f;
|
||||
}
|
||||
|
||||
m_last_aimed_graph_node = next;
|
||||
m_length_cp_2_3 = dist;
|
||||
const Quad &quad =
|
||||
QuadGraph::get()->getQuadOfNode(m_last_aimed_graph_node);
|
||||
m_control_points[3] = quad.getCenter();
|
||||
const DriveNode* dn =
|
||||
DriveGraph::get()->getNode(m_last_aimed_graph_node);
|
||||
m_control_points[3] = dn->getCenter();
|
||||
} // getNextControlPoint
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -342,39 +346,42 @@ bool RubberBall::updateAndDelete(float dt)
|
||||
bool close_to_ground = 2.0*m_previous_height < m_current_max_height;
|
||||
|
||||
float vertical_offset = close_to_ground ? 4.0f : 2.0f;
|
||||
// Note that at this stage getHoT still reports the height at
|
||||
// the previous location (since TerrainInfo wasn't updated). On
|
||||
// the other hand, we can't update TerrainInfo without having
|
||||
// at least a good estimation of the height.
|
||||
next_xyz.setY(getHoT() + vertical_offset);
|
||||
|
||||
// Update height of terrain (which isn't done as part of
|
||||
// Flyable::update for rubber balls.
|
||||
TerrainInfo::update(next_xyz);
|
||||
TerrainInfo::update(next_xyz + getNormal()*vertical_offset, -getNormal());
|
||||
|
||||
m_height_timer += dt;
|
||||
float height = updateHeight()+m_extend.getY()*0.5f;
|
||||
float new_y = getHoT()+height;
|
||||
|
||||
|
||||
if(UserConfigParams::logFlyable())
|
||||
Log::debug("[RubberBall]", "ball %d: %f %f %f height %f new_y %f gethot %f ",
|
||||
m_id, next_xyz.getX(), next_xyz.getY(), next_xyz.getZ(), height, new_y, getHoT());
|
||||
Log::debug("[RubberBall]", "ball %d: %f %f %f height %f gethot %f ",
|
||||
m_id, next_xyz.getX(), next_xyz.getY(), next_xyz.getZ(), height, getHoT());
|
||||
|
||||
// No need to check for terrain height if the ball is low to the ground
|
||||
if(height > 0.5f)
|
||||
{
|
||||
float terrain_height = getMaxTerrainHeight(vertical_offset)
|
||||
float tunnel_height = getTunnelHeight(next_xyz, vertical_offset)
|
||||
- m_extend.getY();
|
||||
if(new_y>terrain_height)
|
||||
new_y = terrain_height;
|
||||
// If the current height of ball (above terrain) is higher than the
|
||||
// tunnel height then set adjust max height and compute new height again.
|
||||
// Else reset the max height.
|
||||
if (height > tunnel_height)
|
||||
{
|
||||
m_max_height = tunnel_height;
|
||||
height = updateHeight();
|
||||
}
|
||||
else
|
||||
m_max_height = m_st_max_height[m_type];
|
||||
}
|
||||
|
||||
if(UserConfigParams::logFlyable())
|
||||
Log::verbose("RubberBall", "newy2 %f gmth %f", new_y,
|
||||
getMaxTerrainHeight(vertical_offset));
|
||||
Log::verbose("RubberBall", "newy2 %f gmth %f", height,
|
||||
getTunnelHeight(next_xyz,vertical_offset));
|
||||
|
||||
next_xyz.setY(new_y);
|
||||
next_xyz = next_xyz + getNormal()*(height);
|
||||
m_previous_xyz = getXYZ();
|
||||
m_previous_height = next_xyz.getY()-getHoT();
|
||||
m_previous_height = (getXYZ() - getHitPoint()).length();
|
||||
setXYZ(next_xyz);
|
||||
|
||||
if(checkTunneling())
|
||||
@@ -405,39 +412,33 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt)
|
||||
// If the rubber ball is already close to a target, i.e. aiming
|
||||
// at it directly, stop interpolating, instead fly straight
|
||||
// towards it.
|
||||
Vec3 diff = m_target->getXYZ()-getXYZ();
|
||||
Vec3 diff = m_target->getXYZ() - getXYZ();
|
||||
diff = diff - diff.dot(getNormal())*getNormal();
|
||||
// Avoid potential division by zero
|
||||
if(diff.length2()==0)
|
||||
*next_xyz = getXYZ();
|
||||
*next_xyz = getXYZ() - getNormal()*m_previous_height;
|
||||
else
|
||||
*next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff;
|
||||
*next_xyz = getXYZ() - getNormal()*m_previous_height +(dt*m_speed / diff.length())*diff;
|
||||
|
||||
Vec3 old_vec = getXYZ()-m_previous_xyz;
|
||||
Vec3 new_vec = *next_xyz - getXYZ();
|
||||
float angle = atan2(new_vec.getZ(), new_vec.getX())
|
||||
- atan2(old_vec.getZ(), old_vec.getX());
|
||||
//float angle = atan2(new_vec.getZ(), new_vec.getX())
|
||||
// - atan2(old_vec.getZ(), old_vec.getX());
|
||||
float angle = new_vec.angle(old_vec);
|
||||
|
||||
// Adjust angle to be between -180 and 180 degrees
|
||||
if(angle < -M_PI)
|
||||
angle += 2*M_PI;
|
||||
else if(angle > M_PI)
|
||||
angle -= 2*M_PI;
|
||||
|
||||
// If the angle is too large, adjust next xyz
|
||||
if(fabsf(angle)>m_st_target_max_angle*dt)
|
||||
{
|
||||
core::vector2df old_2d(old_vec.getX(), old_vec.getZ());
|
||||
if(old_2d.getLengthSQ()==0.0f) old_2d.Y = 1.0f;
|
||||
old_2d.normalize();
|
||||
old_2d.rotateBy( RAD_TO_DEGREE * dt
|
||||
* (angle > 0 ? m_st_target_max_angle
|
||||
: -m_st_target_max_angle));
|
||||
next_xyz->setX(getXYZ().getX() + old_2d.X*dt*m_speed);
|
||||
next_xyz->setZ(getXYZ().getZ() + old_2d.Y*dt*m_speed);
|
||||
} // if fabsf(angle) > m_st_target_angle_max*dt
|
||||
// If ball is close to the target, then explode
|
||||
if (diff.length() < m_target->getKartLength())
|
||||
hit((AbstractKart*)m_target);
|
||||
|
||||
assert(!std::isnan((*next_xyz)[0]));
|
||||
assert(!std::isnan((*next_xyz)[1]));
|
||||
assert(!std::isnan((*next_xyz)[2]));
|
||||
|
||||
} // moveTowardsTarget
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -585,29 +586,26 @@ float RubberBall::updateHeight()
|
||||
} // updateHeight
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the maximum height of the terrain at the current point. While
|
||||
* generall the height is arbitrary (a skybox is not part of the physics and
|
||||
* will therefore not be detected), it is important that a rubber ball does
|
||||
* not end up on top of a tunnel.
|
||||
/** When the ball is in a tunnel, this will return the tunnel height.
|
||||
* NOTE: When this function is called next_xyz is usually the interpolated point
|
||||
* on the track and not the ball's current location. Look at updateAndDelete().
|
||||
*
|
||||
* \param vertical_offset A vertical offset which is added to the current
|
||||
* position of the kart in order to avoid tunneling effects (it could
|
||||
* happen that the raycast down find the track since it uses the
|
||||
* vertical offset, while the raycast up would hit under the track
|
||||
* if the vertical offset is not used).
|
||||
* \returns The height (Y coordinate) of the next terrain element found by
|
||||
* a raycast up. If no terrain is found, it returns 99990
|
||||
* position in order to avoid hitting the track when doing a raycast up.
|
||||
* \returns The distance to the terrain element found by raycast in the up
|
||||
direction. If no terrain found, it returns 99990
|
||||
*/
|
||||
float RubberBall::getMaxTerrainHeight(const Vec3 &vertical_offset) const
|
||||
float RubberBall::getTunnelHeight(const Vec3 &next_xyz, const float vertical_offset) const
|
||||
{
|
||||
const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh();
|
||||
Vec3 to(getXYZ());
|
||||
to.setY(10000.0f);
|
||||
Vec3 from(next_xyz + vertical_offset*getNormal());
|
||||
Vec3 to(next_xyz + 10000.0f*getNormal());
|
||||
Vec3 hit_point;
|
||||
const Material *material;
|
||||
tm.castRay(getXYZ()+vertical_offset, to, &hit_point, &material);
|
||||
tm.castRay(from, to, &hit_point, &material);
|
||||
|
||||
return (material) ? hit_point.getY() : 99999.f;
|
||||
} // getMaxTerrainHeight
|
||||
return (material) ? (hit_point - next_xyz).length() : 99999.f;
|
||||
} // getTunnelHeight
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determines the distance to the target kart. If the target is close, the
|
||||
@@ -633,7 +631,7 @@ void RubberBall::updateDistanceToTarget()
|
||||
m_target->getXYZ().getZ(),m_distance_to_target
|
||||
);
|
||||
|
||||
float height_diff = fabsf(m_target->getXYZ().getY() - getXYZ().getY());
|
||||
float height_diff = fabsf((m_target->getXYZ() - getXYZ()).dot(getNormal().normalized()));
|
||||
|
||||
if(m_distance_to_target < m_st_fast_ping_distance &&
|
||||
height_diff < m_st_max_height_difference)
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "tracks/track_sector.hpp"
|
||||
|
||||
class AbstractKart;
|
||||
class QuadGraph;
|
||||
class SFXBase;
|
||||
|
||||
/**
|
||||
@@ -196,7 +195,8 @@ private:
|
||||
void interpolate(Vec3 *next_xyz, float dt);
|
||||
void moveTowardsTarget(Vec3 *next_xyz, float dt);
|
||||
void initializeControlPoints(const Vec3 &xyz);
|
||||
float getMaxTerrainHeight(const Vec3 &vertical_offset) const;
|
||||
float getTunnelHeight(const Vec3 &next_xyz,
|
||||
const float vertical_offset) const;
|
||||
bool checkTunneling();
|
||||
public:
|
||||
RubberBall (AbstractKart* kart);
|
||||
|
||||
@@ -280,13 +280,11 @@ void Swatter::pointToTarget()
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3 swatter_to_target = m_target->getXYZ()
|
||||
-Vec3(m_scene_node->getAbsolutePosition());
|
||||
Vec3 swatter_to_target =
|
||||
m_kart->getTrans().inverse()(m_target->getXYZ());
|
||||
float dy = -swatter_to_target.getZ();
|
||||
float dx = swatter_to_target.getX();
|
||||
float angle = SWAT_ANGLE_OFFSET + (atan2(dy, dx)-m_kart->getHeading())
|
||||
* 180.0f/M_PI;
|
||||
|
||||
float angle = SWAT_ANGLE_OFFSET + atan2f(dy, dx) * 180 / M_PI;
|
||||
m_scene_node->setRotation(core::vector3df(0.0, angle, 0.0));
|
||||
}
|
||||
} // pointToTarget
|
||||
|
||||
@@ -441,6 +441,10 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void crashed(const Material *m, const Vec3 &normal) = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the normal of the terrain the kart is over atm. This is
|
||||
* defined even if the kart is flying. */
|
||||
virtual const Vec3& getNormal() const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the height of the terrain. we're currently above */
|
||||
virtual float getHoT() const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -86,9 +86,7 @@ float AIBaseController::steerToPoint(const Vec3 &point)
|
||||
|
||||
// First translate and rotate the point the AI is aiming
|
||||
// at into the kart's local coordinate system.
|
||||
btQuaternion q(btVector3(0,1,0), -m_kart->getHeading());
|
||||
Vec3 p = point - m_kart->getXYZ();
|
||||
Vec3 lc = quatRotate(q, p);
|
||||
Vec3 lc = m_kart->getTrans().inverse()(point);
|
||||
|
||||
// The point the kart is aiming at can be reached 'incorrectly' if the
|
||||
// point is below the y=x line: Instead of aiming at that point directly
|
||||
@@ -282,34 +280,50 @@ void AIBaseController::crashed(const Material *m)
|
||||
|
||||
} // crashed(Material)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void AIBaseController::checkPosition(const Vec3 &point, posData *pos_data,
|
||||
Vec3 *lc, bool use_front_xyz) const
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determine the center point and radius of a circle given two points on
|
||||
* the circle and the tangent at the first point. This is done as follows:
|
||||
* 1. Determine the line going through the center point start+end, which is
|
||||
* orthogonal to the vector from start to end. This line goes through the
|
||||
* center of the circle.
|
||||
* 2. Determine the line going through the first point and is orthogonal
|
||||
* to the given tangent.
|
||||
* 3. The intersection of these two lines is the center of the circle.
|
||||
* \param[in] end Second point on circle.
|
||||
* \param[out] center Center point of the circle (local coordinate).
|
||||
* \param[out] radius Radius of the circle.
|
||||
*/
|
||||
void AIBaseController::determineTurnRadius(const Vec3 &end, Vec3 *center,
|
||||
float *radius) const
|
||||
{
|
||||
// Convert to local coordinates from the point of view of current kart
|
||||
btQuaternion q(btVector3(0, 1, 0), -m_kart->getHeading());
|
||||
Vec3 p = point -
|
||||
(use_front_xyz ? m_kart->getFrontXYZ() : m_kart->getXYZ());
|
||||
Vec3 local_coordinates = quatRotate(q, p);
|
||||
// Convert end point to local coordinate, so start will be 0, 0, 0
|
||||
Vec3 lc = m_kart->getTrans().inverse()(end);
|
||||
|
||||
// Save local coordinates for later use if needed
|
||||
if (lc) *lc = local_coordinates;
|
||||
// 1) Line through middle of start+end
|
||||
Vec3 mid = 0.5f * lc;
|
||||
Vec3 direction = lc;
|
||||
|
||||
if (pos_data == NULL) return;
|
||||
// lhs: tell whether it's left or right hand side
|
||||
if (local_coordinates.getX() < 0)
|
||||
pos_data->lhs = true;
|
||||
Vec3 orthogonal(direction.getZ(), 0, -direction.getX());
|
||||
Vec3 q1 = mid + orthogonal;
|
||||
irr::core::line2df line1(mid.getX(), mid.getZ(),
|
||||
q1.getX(), q1.getZ() );
|
||||
|
||||
irr::core::line2df line2(0, 0, 1, 0);
|
||||
irr::core::vector2df result;
|
||||
if (line1.intersectWith(line2, result, /*checkOnlySegments*/false))
|
||||
{
|
||||
Vec3 lc_center(result.X, 0, result.Y);
|
||||
if (center)
|
||||
*center = lc_center;
|
||||
*radius = lc_center.length();
|
||||
}
|
||||
else
|
||||
pos_data->lhs = false;
|
||||
{
|
||||
// No intersection. In this case assume that the two points are
|
||||
// on a semicircle, in which case the center is at 0.5*(start+end):
|
||||
if (center)
|
||||
*center = mid;
|
||||
*radius = 0.5f*(lc).length();
|
||||
}
|
||||
|
||||
// behind: tell whether it's behind or not
|
||||
if (local_coordinates.getZ() < 0)
|
||||
pos_data->behind = true;
|
||||
else
|
||||
pos_data->behind = false;
|
||||
|
||||
pos_data->angle = atan2(fabsf(local_coordinates.getX()),
|
||||
fabsf(local_coordinates.getZ()));
|
||||
pos_data->distance = p.length();
|
||||
|
||||
} // checkPosition
|
||||
} // determineTurnRadius
|
||||
|
||||
@@ -64,22 +64,18 @@ protected:
|
||||
* for AI testing only. */
|
||||
static int m_test_ai;
|
||||
|
||||
/** Position info structure of targets. */
|
||||
struct posData {bool behind; bool lhs; float angle; float distance;};
|
||||
|
||||
void setControllerName(const std::string &name);
|
||||
float steerToPoint(const Vec3 &point);
|
||||
float normalizeAngle(float angle);
|
||||
virtual void update (float delta);
|
||||
virtual void setSteering (float angle, float dt);
|
||||
virtual bool canSkid(float steer_fraction) = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
/** This can be called to detect if the kart is stuck (i.e. repeatedly
|
||||
* hitting part of the track). */
|
||||
bool isStuck() const { return m_stuck; }
|
||||
void checkPosition(const Vec3&, posData*,
|
||||
Vec3* lc = NULL,
|
||||
bool use_front_xyz = false) const;
|
||||
void determineTurnRadius(const Vec3 &end, Vec3 *center,
|
||||
float *radius) const;
|
||||
virtual void update (float delta);
|
||||
virtual void setSteering (float angle, float dt);
|
||||
virtual bool canSkid(float steer_fraction) = 0;
|
||||
|
||||
public:
|
||||
AIBaseController(AbstractKart *kart);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/controller/ai_properties.hpp"
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ I.e. the controller that takes over from a player (or AI) when the race is
|
||||
finished.
|
||||
|
||||
This base class defines some basic operations:
|
||||
- It takes care on which part of the QuadGraph the AI currently is.
|
||||
- It takes care on which part of the DriveGraph the AI currently is.
|
||||
- It determines which path the AI should take (in case of shortcuts
|
||||
or forks in the road).
|
||||
|
||||
@@ -139,14 +139,14 @@ void AIBaseLapController::newLap(int lap)
|
||||
*/
|
||||
void AIBaseLapController::computePath()
|
||||
{
|
||||
m_next_node_index.resize(QuadGraph::get()->getNumNodes());
|
||||
m_successor_index.resize(QuadGraph::get()->getNumNodes());
|
||||
m_next_node_index.resize(DriveGraph::get()->getNumNodes());
|
||||
m_successor_index.resize(DriveGraph::get()->getNumNodes());
|
||||
std::vector<unsigned int> next;
|
||||
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
|
||||
for(unsigned int i=0; i<DriveGraph::get()->getNumNodes(); i++)
|
||||
{
|
||||
next.clear();
|
||||
// Get all successors the AI is allowed to take.
|
||||
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true);
|
||||
DriveGraph::get()->getSuccessors(i, next, /*for_ai*/true);
|
||||
// In case of short cuts hidden for the AI it can be that a node
|
||||
// might not have a successor (since the first and last edge of
|
||||
// a hidden shortcut is ignored). Since in the case that the AI
|
||||
@@ -154,7 +154,7 @@ void AIBaseLapController::computePath()
|
||||
// allowed way to drive, it should still be able to drive, so add
|
||||
// the non-AI successors of that node in this case.
|
||||
if(next.size()==0)
|
||||
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false);
|
||||
DriveGraph::get()->getSuccessors(i, next, /*for_ai*/false);
|
||||
// For now pick one part on random, which is not adjusted during the
|
||||
// race. Long term statistics might be gathered to determine the
|
||||
// best way, potentially depending on race position etc.
|
||||
@@ -171,12 +171,12 @@ void AIBaseLapController::computePath()
|
||||
// Now compute for each node in the graph the list of the next 'look_ahead'
|
||||
// graph nodes. This is the list of node that is tested in checkCrashes.
|
||||
// If the look_ahead is too big, the AI can skip loops (see
|
||||
// QuadGraph::findRoadSector for details), if it's too short the AI won't
|
||||
// Graph::findRoadSector for details), if it's too short the AI won't
|
||||
// find too good a driveline. Note that in general this list should
|
||||
// be computed recursively, but since the AI for now is using only
|
||||
// (randomly picked) path this is fine
|
||||
m_all_look_aheads.resize(QuadGraph::get()->getNumNodes());
|
||||
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
|
||||
m_all_look_aheads.resize(DriveGraph::get()->getNumNodes());
|
||||
for(unsigned int i=0; i<DriveGraph::get()->getNumNodes(); i++)
|
||||
{
|
||||
std::vector<int> l;
|
||||
int current = i;
|
||||
@@ -199,24 +199,24 @@ void AIBaseLapController::computePath()
|
||||
void AIBaseLapController::update(float dt)
|
||||
{
|
||||
AIBaseController::update(dt);
|
||||
if(QuadGraph::get())
|
||||
if(DriveGraph::get())
|
||||
{
|
||||
// Update the current node:
|
||||
int old_node = m_track_node;
|
||||
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
|
||||
if(m_track_node!=Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node,
|
||||
DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node,
|
||||
&m_all_look_aheads[m_track_node]);
|
||||
}
|
||||
// If we can't find a proper place on the track, to a broader search
|
||||
// on off-track locations.
|
||||
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
|
||||
if(m_track_node==Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
}
|
||||
// IF the AI is off track (or on a branch of the track it did not
|
||||
// select to be on), keep the old position.
|
||||
if(m_track_node==QuadGraph::UNKNOWN_SECTOR ||
|
||||
if(m_track_node==Graph::UNKNOWN_SECTOR ||
|
||||
m_next_node_index[m_track_node]==-1)
|
||||
m_track_node = old_node;
|
||||
}
|
||||
@@ -233,7 +233,7 @@ void AIBaseLapController::update(float dt)
|
||||
unsigned int AIBaseLapController::getNextSector(unsigned int index)
|
||||
{
|
||||
std::vector<unsigned int> successors;
|
||||
QuadGraph::get()->getSuccessors(index, successors);
|
||||
DriveGraph::get()->getSuccessors(index, successors);
|
||||
return successors[0];
|
||||
} // getNextSector
|
||||
|
||||
@@ -245,8 +245,8 @@ unsigned int AIBaseLapController::getNextSector(unsigned int index)
|
||||
float AIBaseLapController::steerToAngle(const unsigned int sector,
|
||||
const float add_angle)
|
||||
{
|
||||
float angle = QuadGraph::get()->getAngleToNext(sector,
|
||||
getNextSector(sector));
|
||||
float angle = DriveGraph::get()->getAngleToNext(sector,
|
||||
getNextSector(sector));
|
||||
|
||||
//Desired angle minus current angle equals how many angles to turn
|
||||
float steer_angle = angle - m_kart->getHeading();
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
class AIProperties;
|
||||
class LinearWorld;
|
||||
class QuadGraph;
|
||||
class Track;
|
||||
class Vec3;
|
||||
|
||||
|
||||
@@ -23,27 +23,21 @@
|
||||
#include "items/powerup.hpp"
|
||||
#include "items/projectile_manager.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/controller/player_controller.hpp"
|
||||
#include "karts/controller/ai_properties.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/quad.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "karts/rescue_animation.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
#include "tracks/arena_node.hpp"
|
||||
|
||||
int ArenaAI::m_test_node_for_banana = BattleGraph::UNKNOWN_POLY;
|
||||
|
||||
bool isNodeWithBanana(const std::pair<const Item*, int>& item_pair)
|
||||
{
|
||||
return item_pair.second == ArenaAI::m_test_node_for_banana &&
|
||||
item_pair.first->getType() == Item::ITEM_BANANA &&
|
||||
!item_pair.first->wasCollected();
|
||||
}
|
||||
#include <algorithm>
|
||||
|
||||
ArenaAI::ArenaAI(AbstractKart *kart)
|
||||
: AIBaseController(kart)
|
||||
{
|
||||
m_debug_sphere = NULL;
|
||||
m_debug_sphere_next = NULL;
|
||||
m_graph = ArenaGraph::get();
|
||||
assert(m_graph != NULL);
|
||||
} // ArenaAI
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -51,28 +45,26 @@ ArenaAI::ArenaAI(AbstractKart *kart)
|
||||
*/
|
||||
void ArenaAI::reset()
|
||||
{
|
||||
m_target_node = BattleGraph::UNKNOWN_POLY;
|
||||
m_current_forward_node = BattleGraph::UNKNOWN_POLY;
|
||||
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 = BattleGraph::UNKNOWN_POLY;
|
||||
m_closest_kart_node = Graph::UNKNOWN_SECTOR;
|
||||
m_closest_kart_point = Vec3(0, 0, 0);
|
||||
m_closest_kart_pos_data = {0};
|
||||
m_cur_kart_pos_data = {0};
|
||||
m_is_stuck = false;
|
||||
m_is_uturn = false;
|
||||
m_avoiding_banana = false;
|
||||
m_mini_skid = false;
|
||||
m_target_point = Vec3(0, 0, 0);
|
||||
m_target_point_lc = Vec3(0, 0, 0);
|
||||
m_time_since_last_shot = 0.0f;
|
||||
m_time_since_driving = 0.0f;
|
||||
m_time_since_off_road = 0.0f;
|
||||
m_time_since_reversing = 0.0f;
|
||||
m_time_since_uturn = 0.0f;
|
||||
m_turn_radius = 0.0f;
|
||||
m_turn_angle = 0.0f;
|
||||
m_steering_angle = 0.0f;
|
||||
m_on_node.clear();
|
||||
m_aiming_points.clear();
|
||||
m_aiming_nodes.clear();
|
||||
|
||||
m_cur_difficulty = race_manager->getDifficulty();
|
||||
AIBaseController::reset();
|
||||
@@ -88,7 +80,6 @@ void ArenaAI::update(float dt)
|
||||
// This is used to enable firing an item backwards.
|
||||
m_controls->setLookBack(false);
|
||||
m_controls->setNitro(false);
|
||||
m_avoiding_banana = false;
|
||||
|
||||
// Don't do anything if there is currently a kart animations shown.
|
||||
if (m_kart->getKartAnimation())
|
||||
@@ -97,6 +88,24 @@ void ArenaAI::update(float dt)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isKartOnRoad() && m_kart->isOnGround())
|
||||
{
|
||||
m_time_since_off_road += dt;
|
||||
}
|
||||
else if (m_time_since_off_road != 0.0f)
|
||||
{
|
||||
m_time_since_off_road = 0.0f;
|
||||
}
|
||||
|
||||
// If the kart needs to be rescued, do it now (and nothing else)
|
||||
if (m_time_since_off_road > 5.0f && m_kart->isOnGround())
|
||||
{
|
||||
m_time_since_off_road = 0.0f;
|
||||
new RescueAnimation(m_kart);
|
||||
AIBaseController::update(dt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWaiting())
|
||||
{
|
||||
AIBaseController::update(dt);
|
||||
@@ -104,14 +113,21 @@ void ArenaAI::update(float dt)
|
||||
}
|
||||
|
||||
checkIfStuck(dt);
|
||||
if (handleArenaUnstuck(dt))
|
||||
if (gettingUnstuck(dt))
|
||||
return;
|
||||
|
||||
findClosestKart(true);
|
||||
findTarget();
|
||||
handleArenaItems(dt);
|
||||
|
||||
if (m_kart->getSpeed() > 15.0f && m_turn_angle < 20)
|
||||
// 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();
|
||||
useItems(dt);
|
||||
|
||||
if (m_kart->getSpeed() > 15.0f && !m_is_uturn && m_turn_radius > 30.0f &&
|
||||
!ignorePathFinding())
|
||||
{
|
||||
// Only use nitro when turn angle is big (180 - angle)
|
||||
m_controls->setNitro(true);
|
||||
@@ -120,13 +136,12 @@ void ArenaAI::update(float dt)
|
||||
if (m_is_uturn)
|
||||
{
|
||||
resetAfterStop();
|
||||
handleArenaUTurn(dt);
|
||||
doUTurn(dt);
|
||||
}
|
||||
else
|
||||
{
|
||||
handleArenaAcceleration(dt);
|
||||
handleArenaSteering(dt);
|
||||
handleArenaBraking();
|
||||
configSpeed();
|
||||
setSteering(m_steering_angle, dt);
|
||||
}
|
||||
|
||||
AIBaseController::update(dt);
|
||||
@@ -134,127 +149,139 @@ void ArenaAI::update(float dt)
|
||||
} // update
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ArenaAI::updateAimingPosition()
|
||||
/** Update aiming position, use path finding if necessary.
|
||||
* \param[out] target_point Suitable target point.
|
||||
* \return True if found a suitable target point.
|
||||
*/
|
||||
bool ArenaAI::updateAimingPosition(Vec3* target_point)
|
||||
{
|
||||
#ifdef AI_DEBUG
|
||||
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_node = BattleGraph::get()->pointToNode
|
||||
(m_current_forward_node, m_current_forward_point,
|
||||
false/*ignore_vertical*/);
|
||||
|
||||
m_turn_radius = 0.0f;
|
||||
std::vector<int>* test_nodes = NULL;
|
||||
if (m_current_forward_node != Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
test_nodes =
|
||||
m_graph->getNode(m_current_forward_node)->getNearbyNodes();
|
||||
}
|
||||
m_graph->findRoadSector(m_current_forward_point, &m_current_forward_node,
|
||||
test_nodes);
|
||||
|
||||
// Use current node if forward node is unknown, or near the target
|
||||
const int forward = (m_current_forward_node == BattleGraph::UNKNOWN_POLY ||
|
||||
const int forward =
|
||||
m_current_forward_node == Graph::UNKNOWN_SECTOR ||
|
||||
m_current_forward_node == m_target_node ||
|
||||
getCurrentNode() == m_target_node ? getCurrentNode() :
|
||||
m_current_forward_node);
|
||||
getCurrentNode() == m_target_node ?
|
||||
getCurrentNode() : m_current_forward_node;
|
||||
|
||||
if (forward == BattleGraph::UNKNOWN_POLY ||
|
||||
m_target_node == BattleGraph::UNKNOWN_POLY)
|
||||
if (forward == Graph::UNKNOWN_SECTOR ||
|
||||
m_target_node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::error("ArenaAI", "Next node is unknown, path finding failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (forward == m_target_node)
|
||||
{
|
||||
m_aiming_points.push_back(BattleGraph::get()
|
||||
->getQuadOfNode(forward).getCenter());
|
||||
m_aiming_points.push_back(m_target_point);
|
||||
|
||||
m_aiming_nodes.insert(forward);
|
||||
m_aiming_nodes.insert(getCurrentNode());
|
||||
determineTurnRadius(m_target_point, NULL, &m_turn_radius);
|
||||
*target_point = m_target_point;
|
||||
return true;
|
||||
}
|
||||
|
||||
const int next_node = BattleGraph::get()
|
||||
->getNextShortestPathPoly(forward, m_target_node);
|
||||
if (next_node == BattleGraph::UNKNOWN_POLY)
|
||||
std::vector<int> path;
|
||||
int next_node = m_graph->getNextNode(forward, m_target_node);
|
||||
|
||||
if (next_node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::error("ArenaAI", "Next node is unknown, did you forget to link"
|
||||
"adjacent face in navmesh?");
|
||||
" adjacent face in navmesh?");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_aiming_points.push_back(BattleGraph::get()
|
||||
->getQuadOfNode(forward).getCenter());
|
||||
m_aiming_points.push_back(BattleGraph::get()
|
||||
->getQuadOfNode(next_node).getCenter());
|
||||
path.push_back(next_node);
|
||||
while (m_target_node != next_node)
|
||||
{
|
||||
int previous_node = next_node;
|
||||
next_node = m_graph->getNextNode(previous_node, m_target_node);
|
||||
if (next_node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::error("ArenaAI", "Next node is unknown, did you forget to"
|
||||
" link adjacent face in navmesh?");
|
||||
return false;
|
||||
}
|
||||
path.push_back(next_node);
|
||||
}
|
||||
|
||||
m_aiming_nodes.insert(forward);
|
||||
m_aiming_nodes.insert(next_node);
|
||||
m_aiming_nodes.insert(getCurrentNode());
|
||||
determinePath(forward, &path);
|
||||
*target_point = m_graph->getNode(path.front())->getCenter();
|
||||
|
||||
return true;
|
||||
|
||||
} // updateAimingPosition
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This function sets the steering.
|
||||
* \param dt Time step size.
|
||||
/** This function config the steering of AI.
|
||||
*/
|
||||
void ArenaAI::handleArenaSteering(const float dt)
|
||||
void ArenaAI::configSteering()
|
||||
{
|
||||
m_steering_angle = 0.0f;
|
||||
const int current_node = getCurrentNode();
|
||||
|
||||
if (current_node == BattleGraph::UNKNOWN_POLY ||
|
||||
m_target_node == BattleGraph::UNKNOWN_POLY)
|
||||
if (current_node == Graph::UNKNOWN_SECTOR ||
|
||||
m_target_node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_aiming_points.clear();
|
||||
m_aiming_nodes.clear();
|
||||
const bool found_position = updateAimingPosition();
|
||||
if (ignorePathFinding())
|
||||
{
|
||||
// Steer directly
|
||||
checkPosition(m_target_point, &m_cur_kart_pos_data);
|
||||
// Steer directly, don't brake
|
||||
m_turn_radius = 100.0f;
|
||||
#ifdef AI_DEBUG
|
||||
m_debug_sphere->setPosition(m_target_point.toIrrVector());
|
||||
#endif
|
||||
if (m_cur_kart_pos_data.behind)
|
||||
if (m_target_point_lc.z() < 0)
|
||||
{
|
||||
m_adjusting_side = m_cur_kart_pos_data.lhs;
|
||||
// Local coordinate z < 0 == target point is behind
|
||||
m_adjusting_side = m_target_point_lc.x() < 0;
|
||||
m_is_uturn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
float target_angle = steerToPoint(m_target_point);
|
||||
setSteering(target_angle, dt);
|
||||
m_steering_angle = steerToPoint(m_target_point);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (found_position)
|
||||
|
||||
// Otherwise use path finding to get target point
|
||||
Vec3 target_point;
|
||||
const bool found_position = updateAimingPosition(&target_point);
|
||||
if (found_position)
|
||||
{
|
||||
updateBananaLocation();
|
||||
assert(m_aiming_points.size() == 2);
|
||||
updateTurnRadius(m_kart->getXYZ(), m_aiming_points[0],
|
||||
m_aiming_points[1]);
|
||||
m_target_point = m_aiming_points[1];
|
||||
checkPosition(m_target_point, &m_cur_kart_pos_data);
|
||||
m_target_point = target_point;
|
||||
m_target_point_lc = m_kart->getTrans().inverse()(m_target_point);
|
||||
#ifdef AI_DEBUG
|
||||
m_debug_sphere->setVisible(true);
|
||||
m_debug_sphere_next->setVisible(true);
|
||||
m_debug_sphere->setPosition(m_aiming_points[0].toIrrVector());
|
||||
m_debug_sphere_next->setPosition(m_aiming_points[1].toIrrVector());
|
||||
m_debug_sphere->setPosition(m_target_point.toIrrVector());
|
||||
#endif
|
||||
if (m_cur_kart_pos_data.behind)
|
||||
if (m_target_point_lc.z() < 0)
|
||||
{
|
||||
m_adjusting_side = m_cur_kart_pos_data.lhs;
|
||||
m_adjusting_side = m_target_point_lc.x() < 0;
|
||||
m_is_uturn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
float target_angle = steerToPoint(m_target_point);
|
||||
setSteering(target_angle, dt);
|
||||
m_steering_angle = steerToPoint(m_target_point);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do nothing (go straight) if no targets found
|
||||
setSteering(0.0f, dt);
|
||||
return;
|
||||
}
|
||||
} // handleSteering
|
||||
} // configSteering
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ArenaAI::checkIfStuck(const float dt)
|
||||
@@ -292,26 +319,37 @@ void ArenaAI::checkIfStuck(const float dt)
|
||||
} // checkIfStuck
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Handles acceleration.
|
||||
* \param dt Time step size.
|
||||
/** Configure a suitable speed depends on current turn radius.
|
||||
*/
|
||||
void ArenaAI::handleArenaAcceleration(const float dt)
|
||||
void ArenaAI::configSpeed()
|
||||
{
|
||||
m_controls->setAccel(0.0f);
|
||||
m_controls->setBrake(false);
|
||||
|
||||
if (m_controls->getBrake())
|
||||
{
|
||||
m_controls->setAccel(0.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
const float handicap =
|
||||
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 0.7f : 1.0f);
|
||||
// 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.
|
||||
const float MIN_SPEED = 5.0f;
|
||||
const float handicap = (m_cur_difficulty == RaceManager::DIFFICULTY_EASY
|
||||
? 0.7f : 1.0f );
|
||||
m_controls->setAccel(stk_config->m_ai_acceleration * handicap);
|
||||
|
||||
} // handleArenaAcceleration
|
||||
const float max_turn_speed = m_kart->getSpeedForTurnRadius(m_turn_radius);
|
||||
if ((m_kart->getSpeed() > max_turn_speed || forceBraking()) &&
|
||||
m_kart->getSpeed() > MIN_SPEED * handicap)
|
||||
{
|
||||
// Brake if necessary
|
||||
m_controls->setBrake(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise accelerate
|
||||
m_controls->setAccel(stk_config->m_ai_acceleration * handicap);
|
||||
}
|
||||
} // configSpeed
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ArenaAI::handleArenaUTurn(const float dt)
|
||||
void ArenaAI::doUTurn(const float dt)
|
||||
{
|
||||
const float turn_side = (m_adjusting_side ? 1.0f : -1.0f);
|
||||
|
||||
@@ -329,19 +367,20 @@ void ArenaAI::handleArenaUTurn(const float dt)
|
||||
setSteering(turn_side, dt);
|
||||
m_time_since_uturn += dt;
|
||||
|
||||
checkPosition(m_target_point, &m_cur_kart_pos_data);
|
||||
if (!m_cur_kart_pos_data.behind || m_time_since_uturn >
|
||||
if (m_target_point_lc.z() > 0 || m_time_since_uturn >
|
||||
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 3.5f : 3.0f))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
m_is_uturn = true;
|
||||
} // handleArenaUTurn
|
||||
} // doUTurn
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ArenaAI::handleArenaUnstuck(const float dt)
|
||||
bool ArenaAI::gettingUnstuck(const float dt)
|
||||
{
|
||||
if (!m_is_stuck || m_is_uturn) return false;
|
||||
|
||||
@@ -365,142 +404,10 @@ bool ArenaAI::handleArenaUnstuck(const float dt)
|
||||
AIBaseController::update(dt);
|
||||
return true;
|
||||
|
||||
} // handleArenaUnstuck
|
||||
} // gettingUnstuck
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ArenaAI::updateBananaLocation()
|
||||
{
|
||||
std::vector<std::pair<const Item*, int>>& item_list =
|
||||
BattleGraph::get()->getItemList();
|
||||
|
||||
std::set<int>::iterator node = m_aiming_nodes.begin();
|
||||
while (node != m_aiming_nodes.end())
|
||||
{
|
||||
m_test_node_for_banana = *node;
|
||||
std::vector<std::pair<const Item*, int>>::iterator it =
|
||||
std::find_if(item_list.begin(), item_list.end(), isNodeWithBanana);
|
||||
|
||||
if (it != item_list.end())
|
||||
{
|
||||
Vec3 banana_lc;
|
||||
checkPosition(it->first->getXYZ(), NULL, &banana_lc,
|
||||
true/*use_front_xyz*/);
|
||||
|
||||
// If satisfy the below condition, AI should not eat banana:
|
||||
// banana_lc.z() < 0.0f, behind the kart
|
||||
if (banana_lc.z() < 0.0f)
|
||||
{
|
||||
node++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the node AI will pass has a banana, adjust the aim position
|
||||
banana_lc = (banana_lc.x() < 0 ? banana_lc + Vec3(5, 0, 0) :
|
||||
banana_lc - Vec3(5, 0, 0));
|
||||
m_aiming_points[1] = m_kart->getTrans()(banana_lc);
|
||||
m_avoiding_banana = true;
|
||||
// Handle one banana only
|
||||
return;
|
||||
}
|
||||
node++;
|
||||
}
|
||||
|
||||
} // updateBananaLocation
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This function handles braking. It used the turn radius found by
|
||||
* updateTurnRadius(). Depending on the turn radius, it finds out the maximum
|
||||
* speed. If the current speed is greater than the max speed and a set minimum
|
||||
* speed, brakes are applied.
|
||||
*/
|
||||
void ArenaAI::handleArenaBraking()
|
||||
{
|
||||
// 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.
|
||||
const float MIN_SPEED = 5.0f;
|
||||
|
||||
if (forceBraking() && m_kart->getSpeed() > MIN_SPEED)
|
||||
{
|
||||
// Brake now
|
||||
m_controls->setBrake(true);
|
||||
return;
|
||||
}
|
||||
|
||||
m_controls->setBrake(false);
|
||||
|
||||
if (getCurrentNode() == BattleGraph::UNKNOWN_POLY ||
|
||||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
|
||||
|
||||
if (m_aiming_points.empty()) return;
|
||||
|
||||
const float max_turn_speed = m_kart->getSpeedForTurnRadius(m_turn_radius);
|
||||
|
||||
if (m_kart->getSpeed() > 1.25f * max_turn_speed &&
|
||||
fabsf(m_controls->getSteer()) > 0.95f &&
|
||||
m_kart->getSpeed() > MIN_SPEED)
|
||||
{
|
||||
m_controls->setBrake(true);
|
||||
}
|
||||
|
||||
} // handleArenaBraking
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ArenaAI::updateTurnRadius(const Vec3& p1, const Vec3& p2,
|
||||
const Vec3& p3)
|
||||
{
|
||||
// First use cosine formula to find out the angle made by the distance
|
||||
// between kart (point one) to point two and point two between point three
|
||||
const float a = (p1 - p2).length();
|
||||
const float b = (p2 - p3).length();
|
||||
const float c = (p1 - p3).length();
|
||||
const float angle = 180 - findAngleFrom3Edges(a, b, c);
|
||||
|
||||
// Only calculate radius if not almost straight line
|
||||
if (angle > 1 && angle < 179)
|
||||
{
|
||||
// angle
|
||||
// ^
|
||||
// a / \ b
|
||||
// 90/\ /\90
|
||||
// \ / \ /
|
||||
// \ /
|
||||
// \ /
|
||||
// \ /
|
||||
// |
|
||||
// Try to estimate the turn radius with the help of a kite-like
|
||||
// polygon as shown, find out the lowest angle which is
|
||||
// (4 - 2) * 180 - 90 - 90 - angle (180 - angle from above)
|
||||
// Then we use this value as the angle of a sector of circle,
|
||||
// a + b as the arc length, then the radius can be calculated easily
|
||||
m_turn_radius = ((a + b) / (angle / 360)) / M_PI / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return large radius so no braking is needed otherwise
|
||||
m_turn_radius = 45.0f;
|
||||
}
|
||||
m_turn_angle = angle;
|
||||
|
||||
} // updateTurnRadius
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float ArenaAI::findAngleFrom3Edges(float a, float b, float c)
|
||||
{
|
||||
// Cosine forumla : c2 = a2 + b2 - 2ab cos C
|
||||
float test_value = ((c * c) - (a * a) - (b * b)) / (-(2 * a * b));
|
||||
// Prevent error
|
||||
if (test_value < -1)
|
||||
test_value = -1;
|
||||
else if (test_value > 1)
|
||||
test_value = 1;
|
||||
|
||||
return acosf(test_value) * RAD_TO_DEGREE;
|
||||
|
||||
} // findAngleFrom3Edges
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ArenaAI::handleArenaItems(const float dt)
|
||||
void ArenaAI::useItems(const float dt)
|
||||
{
|
||||
m_controls->setFire(false);
|
||||
if (m_kart->getKartAnimation() ||
|
||||
@@ -509,18 +416,27 @@ void ArenaAI::handleArenaItems(const float dt)
|
||||
|
||||
// Find a closest kart again, this time we ignore difficulty
|
||||
findClosestKart(false);
|
||||
|
||||
if (!m_closest_kart) return;
|
||||
|
||||
Vec3 closest_kart_point_lc =
|
||||
m_kart->getTrans().inverse()(m_closest_kart_point);
|
||||
|
||||
m_time_since_last_shot += dt;
|
||||
|
||||
float min_bubble_time = 2.0f;
|
||||
const bool difficulty = m_cur_difficulty == RaceManager::DIFFICULTY_EASY ||
|
||||
m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM;
|
||||
|
||||
const bool fire_behind = m_closest_kart_pos_data.behind && !difficulty;
|
||||
const bool fire_behind = closest_kart_point_lc.z() < 0 && !difficulty;
|
||||
|
||||
const bool perfect_aim = m_closest_kart_pos_data.angle < 0.2f;
|
||||
const float abs_angle = atan2f(fabsf(closest_kart_point_lc.x()),
|
||||
fabsf(closest_kart_point_lc.z()));
|
||||
const bool perfect_aim = abs_angle < 0.2f;
|
||||
|
||||
// Compensate the distance because this distance is straight to straight
|
||||
// in graph node, so if kart to kart are not facing like so as, their real
|
||||
// distance maybe smaller
|
||||
const float dist_to_kart = getKartDistance(m_closest_kart) * 0.8f;
|
||||
|
||||
switch(m_kart->getPowerup()->getType())
|
||||
{
|
||||
@@ -535,14 +451,11 @@ void ArenaAI::handleArenaItems(const float dt)
|
||||
// has a swatter attachment. If so, use bubblegum
|
||||
// as shield
|
||||
if ( (!m_kart->isShielded() &&
|
||||
projectile_manager->projectileIsClose(m_kart,
|
||||
m_ai_properties->m_shield_incoming_radius)
|
||||
) ||
|
||||
(m_closest_kart_pos_data.distance < 15.0f &&
|
||||
m_closest_kart->getAttachment()->getType() ==
|
||||
Attachment::ATTACH_SWATTER
|
||||
)
|
||||
)
|
||||
projectile_manager->projectileIsClose(m_kart,
|
||||
m_ai_properties->m_shield_incoming_radius) ) ||
|
||||
(dist_to_kart < 15.0f &&
|
||||
(m_closest_kart->getAttachment()->
|
||||
getType() == Attachment::ATTACH_SWATTER) ) )
|
||||
{
|
||||
m_controls->setFire(true);
|
||||
m_controls->setLookBack(false);
|
||||
@@ -552,11 +465,9 @@ void ArenaAI::handleArenaItems(const float dt)
|
||||
// Avoid dropping all bubble gums one after another
|
||||
if (m_time_since_last_shot < 3.0f) break;
|
||||
|
||||
// Use bubblegum if the next kart behind is 'close' but not too close,
|
||||
// Use bubblegum if the kart around is close,
|
||||
// or can't find a close kart for too long time
|
||||
if ((m_closest_kart_pos_data.distance < 15.0f &&
|
||||
m_closest_kart_pos_data.distance > 3.0f) ||
|
||||
m_time_since_last_shot > 15.0f)
|
||||
if (dist_to_kart < 15.0f || m_time_since_last_shot > 15.0f)
|
||||
{
|
||||
m_controls->setFire(true);
|
||||
m_controls->setLookBack(true);
|
||||
@@ -574,7 +485,7 @@ void ArenaAI::handleArenaItems(const float dt)
|
||||
// Leave some time between shots
|
||||
if (m_time_since_last_shot < 1.0f) break;
|
||||
|
||||
if (m_closest_kart_pos_data.distance < 25.0f &&
|
||||
if (dist_to_kart < 25.0f &&
|
||||
!m_closest_kart->isInvulnerable())
|
||||
{
|
||||
m_controls->setFire(true);
|
||||
@@ -594,7 +505,7 @@ void ArenaAI::handleArenaItems(const float dt)
|
||||
// Leave some time between shots
|
||||
if (m_time_since_last_shot < 1.0f) break;
|
||||
|
||||
if (m_closest_kart_pos_data.distance < 6.0f &&
|
||||
if (dist_to_kart < 6.0f &&
|
||||
(difficulty || perfect_aim) &&
|
||||
!m_closest_kart->isInvulnerable())
|
||||
{
|
||||
@@ -615,7 +526,7 @@ void ArenaAI::handleArenaItems(const float dt)
|
||||
break;
|
||||
|
||||
if (!m_closest_kart->isSquashed() &&
|
||||
m_closest_kart_pos_data.distance < d2 &&
|
||||
dist_to_kart * dist_to_kart < d2 &&
|
||||
m_closest_kart->getSpeed() < m_kart->getSpeed())
|
||||
{
|
||||
m_controls->setFire(true);
|
||||
@@ -653,60 +564,55 @@ void ArenaAI::handleArenaItems(const float dt)
|
||||
}
|
||||
if (m_controls->getFire())
|
||||
m_time_since_last_shot = 0.0f;
|
||||
} // handleArenaItems
|
||||
} // useItems
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const
|
||||
{
|
||||
float distance = 99999.9f;
|
||||
const std::vector< std::pair<const Item*, int> >& item_list =
|
||||
BattleGraph::get()->getItemList();
|
||||
const unsigned int items_count = item_list.size();
|
||||
float distance = 999999.9f;
|
||||
Item* selected = (*target_node == Graph::UNKNOWN_SECTOR ? NULL :
|
||||
ItemManager::get()->getFirstItemInQuad(*target_node));
|
||||
|
||||
if (item_list.empty())
|
||||
// Don't look for a new item unless it's collected or swapped
|
||||
if (selected && !(selected->wasCollected() ||
|
||||
selected->getType() == Item::ITEM_BANANA ||
|
||||
selected->getType() == Item::ITEM_BUBBLEGUM ||
|
||||
selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK))
|
||||
{
|
||||
// Notice: this should not happen, as it makes no sense
|
||||
// for an arean without items, if so how can attack happen?
|
||||
Log::fatal ("ArenaAI",
|
||||
"AI can't find any items in the arena, "
|
||||
"maybe there is something wrong with the navmesh, "
|
||||
"make sure it lies closely to the ground.");
|
||||
*aim_point = selected->getXYZ();
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int closest_item_num = 0;
|
||||
|
||||
for (unsigned int i = 0; i < items_count; ++i)
|
||||
for (unsigned int i = 0; i < m_graph->getNumNodes(); i++)
|
||||
{
|
||||
const Item* item = item_list[i].first;
|
||||
Item* cur_item = ItemManager::get()->getFirstItemInQuad(i);
|
||||
if (cur_item == NULL) continue;
|
||||
if (cur_item->wasCollected() ||
|
||||
cur_item->getType() == Item::ITEM_BANANA ||
|
||||
cur_item->getType() == Item::ITEM_BUBBLEGUM ||
|
||||
cur_item->getType() == Item::ITEM_BUBBLEGUM_NOLOK)
|
||||
continue;
|
||||
|
||||
if (item->wasCollected()) continue;
|
||||
|
||||
if ((item->getType() == Item::ITEM_NITRO_BIG ||
|
||||
item->getType() == Item::ITEM_NITRO_SMALL) &&
|
||||
if ((cur_item->getType() == Item::ITEM_NITRO_BIG ||
|
||||
cur_item->getType() == Item::ITEM_NITRO_SMALL) &&
|
||||
(m_kart->getEnergy() >
|
||||
m_kart->getKartProperties()->getNitroSmallContainer()))
|
||||
continue; // Ignore nitro when already has some
|
||||
|
||||
float test_distance = BattleGraph::get()
|
||||
->getDistance(item_list[i].second, getCurrentNode());
|
||||
if (test_distance <= distance &&
|
||||
(item->getType() == Item::ITEM_BONUS_BOX ||
|
||||
item->getType() == Item::ITEM_NITRO_BIG ||
|
||||
item->getType() == Item::ITEM_NITRO_SMALL))
|
||||
const int cur_node = cur_item->getGraphNode();
|
||||
assert(cur_node != Graph::UNKNOWN_SECTOR);
|
||||
float test_distance = m_graph->getDistance(cur_node, getCurrentNode());
|
||||
if (test_distance <= distance)
|
||||
{
|
||||
closest_item_num = i;
|
||||
selected = cur_item;
|
||||
distance = test_distance;
|
||||
}
|
||||
}
|
||||
|
||||
const Item *item_selected = item_list[closest_item_num].first;
|
||||
if (item_selected->getType() == Item::ITEM_BONUS_BOX ||
|
||||
item_selected->getType() == Item::ITEM_NITRO_BIG ||
|
||||
item_selected->getType() == Item::ITEM_NITRO_SMALL)
|
||||
if (selected != NULL)
|
||||
{
|
||||
*aim_point = item_selected->getXYZ();
|
||||
*target_node = item_list[closest_item_num].second;
|
||||
*aim_point = selected->getXYZ();
|
||||
*target_node = selected->getGraphNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -715,3 +621,121 @@ void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const
|
||||
*target_node = m_closest_kart_node;
|
||||
}
|
||||
} // collectItemInArena
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
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 ||
|
||||
m_cur_difficulty == RaceManager::DIFFICULTY_BEST) &&
|
||||
m_target_point_lc.z() > 0 && abs_angle > 0.15f &&
|
||||
m_target_point_lc.length() < 10.0f &&
|
||||
((m_steering_angle < 0 && m_target_point_lc.x() < 0) ||
|
||||
(m_steering_angle > 0 && m_target_point_lc.x() > 0)))
|
||||
{
|
||||
m_mini_skid = true;
|
||||
}
|
||||
|
||||
} // 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.
|
||||
*/
|
||||
void ArenaAI::determinePath(int forward, std::vector<int>* path)
|
||||
{
|
||||
std::vector<int> bad_item_nodes;
|
||||
// First, test if the nodes AI will cross contain bad item
|
||||
for (unsigned int i = 0; i < path->size(); i++)
|
||||
{
|
||||
// Only test few nodes ahead
|
||||
if (i == 6) break;
|
||||
const int node = (*path)[i];
|
||||
Item* selected = ItemManager::get()->getFirstItemInQuad(node);
|
||||
|
||||
if (selected && !selected->wasCollected() &&
|
||||
(selected->getType() == Item::ITEM_BANANA ||
|
||||
selected->getType() == Item::ITEM_BUBBLEGUM ||
|
||||
selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK))
|
||||
{
|
||||
bad_item_nodes.push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
// If so try to avoid
|
||||
if (!bad_item_nodes.empty())
|
||||
{
|
||||
bool failed_avoid = false;
|
||||
for (unsigned int i = 0; i < path->size(); i++)
|
||||
{
|
||||
if (failed_avoid) break;
|
||||
if (i == 6) break;
|
||||
// Choose any adjacent node that is in front of the AI to prevent
|
||||
// hitting bad item
|
||||
ArenaNode* cur_node =
|
||||
m_graph->getNode(i == 0 ? forward : (*path)[i - 1]);
|
||||
float dist = 99999.9f;
|
||||
const std::vector<int>& adj_nodes = cur_node->getAdjacentNodes();
|
||||
int chosen_node = Graph::UNKNOWN_SECTOR;
|
||||
for (const int& adjacent : adj_nodes)
|
||||
{
|
||||
if (std::find(bad_item_nodes.begin(), bad_item_nodes.end(),
|
||||
adjacent) != bad_item_nodes.end())
|
||||
continue;
|
||||
|
||||
Vec3 lc = m_kart->getTrans().inverse()
|
||||
(m_graph->getNode(adjacent)->getCenter());
|
||||
const float dist_to_target =
|
||||
m_graph->getDistance(adjacent, m_target_node);
|
||||
if (lc.z() > 0 && dist > dist_to_target)
|
||||
{
|
||||
chosen_node = adjacent;
|
||||
dist = dist_to_target;
|
||||
}
|
||||
if (chosen_node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::debug("ArenaAI", "Too many bad items to avoid!");
|
||||
failed_avoid = true;
|
||||
break;
|
||||
}
|
||||
(*path)[i] = chosen_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now find the first turning corner to determine turn radius
|
||||
for (unsigned int i = 0; i < path->size() - 1; i++)
|
||||
{
|
||||
const Vec3& p1 = m_kart->getXYZ();
|
||||
const Vec3& p2 = m_graph->getNode((*path)[i])->getCenter();
|
||||
const Vec3& p3 = m_graph->getNode((*path)[i + 1])->getCenter();
|
||||
float edge1 = (p1 - p2).length();
|
||||
float edge2 = (p2 - p3).length();
|
||||
float to_target = (p1 - p3).length();
|
||||
|
||||
// Triangle test
|
||||
if (fabsf(edge1 + edge2 - to_target) > 0.1f)
|
||||
{
|
||||
determineTurnRadius(p3, NULL, &m_turn_radius);
|
||||
#ifdef AI_DEBUG
|
||||
m_debug_sphere_next->setVisible(true);
|
||||
m_debug_sphere_next->setPosition(p3.toIrrVector());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback calculation
|
||||
determineTurnRadius(m_target_point, NULL, &m_turn_radius);
|
||||
|
||||
} // determinePath
|
||||
|
||||
@@ -21,14 +21,13 @@
|
||||
|
||||
#include "karts/controller/ai_base_controller.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
#undef AI_DEBUG
|
||||
#ifdef AI_DEBUG
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#endif
|
||||
|
||||
class Vec3;
|
||||
class ArenaGraph;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@@ -42,14 +41,14 @@ namespace irr
|
||||
class ArenaAI : public AIBaseController
|
||||
{
|
||||
protected:
|
||||
ArenaGraph* m_graph;
|
||||
|
||||
/** Pointer to the closest kart around this kart. */
|
||||
AbstractKart *m_closest_kart;
|
||||
|
||||
int m_closest_kart_node;
|
||||
Vec3 m_closest_kart_point;
|
||||
|
||||
posData m_closest_kart_pos_data;
|
||||
|
||||
/** Holds the current difficulty. */
|
||||
RaceManager::Difficulty m_cur_difficulty;
|
||||
|
||||
@@ -58,22 +57,21 @@ protected:
|
||||
irr::scene::ISceneNode *m_debug_sphere;
|
||||
irr::scene::ISceneNode *m_debug_sphere_next;
|
||||
|
||||
/** The node(poly) at which the target point lies in. */
|
||||
/** The node(quad) at which the target point lies in. */
|
||||
int m_target_node;
|
||||
|
||||
/** The target point. */
|
||||
Vec3 m_target_point;
|
||||
|
||||
bool m_avoiding_banana;
|
||||
bool m_mini_skid;
|
||||
|
||||
void collectItemInArena(Vec3*, int*) const;
|
||||
float findAngleFrom3Edges(float a, float b, float c);
|
||||
private:
|
||||
/** Used by handleArenaUTurn, it tells whether to do left or right
|
||||
* turning when steering is overridden. */
|
||||
bool m_adjusting_side;
|
||||
|
||||
posData m_cur_kart_pos_data;
|
||||
Vec3 m_target_point_lc;
|
||||
|
||||
/** Indicates that the kart is currently stuck, and m_time_since_reversing is
|
||||
* counting down. */
|
||||
@@ -99,40 +97,43 @@ private:
|
||||
/** This is a timer that counts down when the kart is doing u-turn. */
|
||||
float m_time_since_uturn;
|
||||
|
||||
/** This is a timer that counts when the kart start going off road. */
|
||||
float m_time_since_off_road;
|
||||
|
||||
float m_turn_radius;
|
||||
float m_turn_angle;
|
||||
|
||||
float m_steering_angle;
|
||||
|
||||
Vec3 m_current_forward_point;
|
||||
|
||||
int m_current_forward_node;
|
||||
|
||||
std::set<int> m_aiming_nodes;
|
||||
std::vector<Vec3> m_aiming_points;
|
||||
|
||||
void checkIfStuck(const float dt);
|
||||
void handleArenaAcceleration(const float dt);
|
||||
void handleArenaBraking();
|
||||
void handleArenaItems(const float dt);
|
||||
void handleArenaSteering(const float dt);
|
||||
void handleArenaUTurn(const float dt);
|
||||
bool handleArenaUnstuck(const float dt);
|
||||
bool updateAimingPosition();
|
||||
void updateBananaLocation();
|
||||
void updateTurnRadius(const Vec3& p1, const Vec3& p2,
|
||||
const Vec3& p3);
|
||||
virtual int getCurrentNode() const = 0;
|
||||
virtual bool isWaiting() const = 0;
|
||||
virtual void resetAfterStop() {};
|
||||
virtual void findClosestKart(bool use_difficulty) = 0;
|
||||
virtual void findTarget() = 0;
|
||||
virtual bool forceBraking() { return m_avoiding_banana; }
|
||||
virtual bool ignorePathFinding() { return false; }
|
||||
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;
|
||||
virtual void findTarget() = 0;
|
||||
virtual bool forceBraking() { return false; }
|
||||
virtual int getCurrentNode() const = 0;
|
||||
virtual float getKartDistance(const AbstractKart* kart) const = 0;
|
||||
virtual bool ignorePathFinding() { return false; }
|
||||
virtual bool isWaiting() const = 0;
|
||||
virtual bool isKartOnRoad() const = 0;
|
||||
virtual void resetAfterStop() {};
|
||||
public:
|
||||
static int m_test_node_for_banana;
|
||||
ArenaAI(AbstractKart *kart);
|
||||
virtual ~ArenaAI() {};
|
||||
virtual void update (float delta);
|
||||
virtual void reset ();
|
||||
virtual void newLap(int lap) {};
|
||||
virtual void update (float delta) OVERRIDE;
|
||||
virtual void reset () OVERRIDE;
|
||||
virtual void newLap (int lap) OVERRIDE {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,12 +25,10 @@
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/controller/kart_control.hpp"
|
||||
#include "modes/three_strikes_battle.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
#include "irrlicht.h"
|
||||
#include <iostream>
|
||||
using namespace irr;
|
||||
using namespace std;
|
||||
#endif
|
||||
|
||||
BattleAI::BattleAI(AbstractKart *kart)
|
||||
@@ -72,14 +70,11 @@ BattleAI::~BattleAI()
|
||||
void BattleAI::reset()
|
||||
{
|
||||
ArenaAI::reset();
|
||||
AIBaseController::reset();
|
||||
m_mini_skid = false;
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void BattleAI::update(float dt)
|
||||
{
|
||||
m_mini_skid = false;
|
||||
ArenaAI::update(dt);
|
||||
} // update
|
||||
|
||||
@@ -118,33 +113,19 @@ void BattleAI::findClosestKart(bool use_difficulty)
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec3 d = kart->getXYZ() - m_kart->getXYZ();
|
||||
if (d.length() <= distance)
|
||||
float dist_to_kart = m_graph->getDistance(getCurrentNode(),
|
||||
m_world->getSectorForKart(kart));
|
||||
if (dist_to_kart <= distance)
|
||||
{
|
||||
distance = d.length();
|
||||
distance = dist_to_kart;
|
||||
closest_kart_num = i;
|
||||
}
|
||||
}
|
||||
|
||||
const AbstractKart* closest_kart = m_world->getKart(closest_kart_num);
|
||||
m_closest_kart_node = m_world->getKartNode(closest_kart_num);
|
||||
m_closest_kart_point = closest_kart->getXYZ();
|
||||
m_closest_kart = m_world->getKart(closest_kart_num);
|
||||
m_closest_kart_node = m_world->getSectorForKart(m_closest_kart);
|
||||
m_closest_kart_point = m_closest_kart->getXYZ();
|
||||
|
||||
if (!use_difficulty)
|
||||
{
|
||||
m_closest_kart = m_world->getKart(closest_kart_num);
|
||||
checkPosition(m_closest_kart_point, &m_closest_kart_pos_data);
|
||||
|
||||
// Do a mini-skid to closest kart only when firing target,
|
||||
// not straight ahead, not too far, in front of it
|
||||
// and with suitable difficulties.
|
||||
if (m_closest_kart_pos_data.angle > 0.2f &&
|
||||
m_closest_kart_pos_data.distance < 20.0f &&
|
||||
!m_closest_kart_pos_data.behind &&
|
||||
(m_cur_difficulty == RaceManager::DIFFICULTY_HARD ||
|
||||
m_cur_difficulty == RaceManager::DIFFICULTY_BEST))
|
||||
m_mini_skid = true;
|
||||
}
|
||||
} // findClosestKart
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -164,10 +145,24 @@ void BattleAI::findTarget()
|
||||
//-----------------------------------------------------------------------------
|
||||
int BattleAI::getCurrentNode() const
|
||||
{
|
||||
return m_world->getKartNode(m_kart->getWorldKartId());
|
||||
return m_world->getSectorForKart(m_kart);
|
||||
} // getCurrentNode
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool BattleAI::isWaiting() const
|
||||
{
|
||||
return m_world->isStartPhase();
|
||||
} // isWaiting
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float BattleAI::getKartDistance(const AbstractKart* kart) const
|
||||
{
|
||||
return m_graph->getDistance(getCurrentNode(),
|
||||
m_world->getSectorForKart(kart));
|
||||
} // getKartDistance
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool BattleAI::isKartOnRoad() const
|
||||
{
|
||||
return m_world->isOnRoad(m_kart->getWorldKartId());
|
||||
} // isKartOnRoad
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#include "karts/controller/arena_ai.hpp"
|
||||
|
||||
class ThreeStrikesBattle;
|
||||
class Vec3;
|
||||
class Item;
|
||||
|
||||
/** The actual battle AI.
|
||||
* \ingroup controller
|
||||
@@ -36,18 +34,17 @@ private:
|
||||
/** Keep a pointer to world. */
|
||||
ThreeStrikesBattle *m_world;
|
||||
|
||||
bool m_mini_skid;
|
||||
|
||||
virtual void findClosestKart(bool use_difficulty);
|
||||
virtual void findTarget();
|
||||
virtual int getCurrentNode() const;
|
||||
virtual bool isWaiting() const;
|
||||
virtual bool canSkid(float steer_fraction) { return m_mini_skid; }
|
||||
virtual void findClosestKart(bool use_difficulty) OVERRIDE;
|
||||
virtual void findTarget() OVERRIDE;
|
||||
virtual int getCurrentNode() const 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);
|
||||
virtual void reset ();
|
||||
virtual void update (float delta) OVERRIDE;
|
||||
virtual void reset () OVERRIDE;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "states_screens/race_result_gui.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
@@ -62,14 +63,14 @@ EndController::EndController(AbstractKart *kart,
|
||||
// with a path that always picks the first branch (i.e. it follows
|
||||
// the main driveline).
|
||||
std::vector<unsigned int> next;
|
||||
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
|
||||
for(unsigned int i=0; i<DriveGraph::get()->getNumNodes(); i++)
|
||||
{
|
||||
// 0 is always a valid successor - so even if the kart should end
|
||||
// up by accident on a non-selected path, it will keep on working.
|
||||
m_successor_index[i] = 0;
|
||||
|
||||
next.clear();
|
||||
QuadGraph::get()->getSuccessors(i, next);
|
||||
DriveGraph::get()->getSuccessors(i, next);
|
||||
m_next_node_index[i] = next[0];
|
||||
}
|
||||
|
||||
@@ -77,11 +78,11 @@ EndController::EndController(AbstractKart *kart,
|
||||
// Now compute for each node in the graph the list of the next 'look_ahead'
|
||||
// graph nodes. This is the list of node that is tested in checkCrashes.
|
||||
// If the look_ahead is too big, the AI can skip loops (see
|
||||
// QuadGraph::findRoadSector for details), if it's too short the AI won't
|
||||
// DriveGraph::findRoadSector for details), if it's too short the AI won't
|
||||
// find too good a driveline. Note that in general this list should
|
||||
// be computed recursively, but since the AI for now is using only
|
||||
// (randomly picked) path this is fine
|
||||
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
|
||||
for(unsigned int i=0; i<DriveGraph::get()->getNumNodes(); i++)
|
||||
{
|
||||
std::vector<int> l;
|
||||
int current = i;
|
||||
@@ -94,7 +95,7 @@ EndController::EndController(AbstractKart *kart,
|
||||
}
|
||||
} // if not battle mode
|
||||
|
||||
// Reset must be called after QuadGraph::get() etc. is set up
|
||||
// Reset must be called after DriveGraph::get() etc. is set up
|
||||
reset();
|
||||
|
||||
m_max_handicap_accel = 1.0f;
|
||||
@@ -129,18 +130,18 @@ void EndController::reset()
|
||||
m_crash_time = 0.0f;
|
||||
m_time_since_stuck = 0.0f;
|
||||
|
||||
m_track_node = QuadGraph::UNKNOWN_SECTOR;
|
||||
m_track_node = Graph::UNKNOWN_SECTOR;
|
||||
// In battle mode there is no quad graph, so nothing to do in this case
|
||||
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
|
||||
DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
|
||||
|
||||
// Node that this can happen quite easily, e.g. an AI kart is
|
||||
// taken over by the end controller while it is off track.
|
||||
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
|
||||
if(m_track_node==Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
}
|
||||
}
|
||||
} // reset
|
||||
@@ -211,10 +212,10 @@ void EndController::handleSteering(float dt)
|
||||
*/
|
||||
//Reaction to being outside of the road
|
||||
if( fabsf(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )) >
|
||||
0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f )
|
||||
0.5f* DriveGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f )
|
||||
{
|
||||
const int next = m_next_node_index[m_track_node];
|
||||
target_point = QuadGraph::get()->getQuadOfNode(next).getCenter();
|
||||
target_point = DriveGraph::get()->getNode(next)->getCenter();
|
||||
#ifdef AI_DEBUG
|
||||
Log::debug("end_controller.cpp", "- Outside of road: steer to center point.");
|
||||
#endif
|
||||
@@ -274,10 +275,10 @@ void EndController::findNonCrashingPoint(Vec3 *result)
|
||||
target_sector = m_next_node_index[sector];
|
||||
|
||||
//direction is a vector from our kart to the sectors we are testing
|
||||
direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter()
|
||||
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
|
||||
- m_kart->getXYZ();
|
||||
|
||||
float len=direction.length_2d();
|
||||
float len=direction.length();
|
||||
steps = int( len / m_kart_length );
|
||||
if( steps < 3 ) steps = 3;
|
||||
|
||||
@@ -292,16 +293,16 @@ void EndController::findNonCrashingPoint(Vec3 *result)
|
||||
{
|
||||
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
|
||||
|
||||
QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
sector );
|
||||
|
||||
distance = fabsf(step_track_coord[0]);
|
||||
|
||||
//If we are outside, the previous sector is what we are looking for
|
||||
if ( distance + m_kart_width * 0.5f
|
||||
> QuadGraph::get()->getNode(sector).getPathWidth()*0.5f )
|
||||
> DriveGraph::get()->getNode(sector)->getPathWidth()*0.5f )
|
||||
{
|
||||
*result = QuadGraph::get()->getQuadOfNode(sector).getCenter();
|
||||
*result = DriveGraph::get()->getNode(sector)->getCenter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
class Camera;
|
||||
class LinearWorld;
|
||||
class QuadGraph;
|
||||
class Track;
|
||||
class Vec3;
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "modes/world.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
@@ -171,22 +171,23 @@ void SkiddingAI::reset()
|
||||
m_distance_behind = 0.0f;
|
||||
m_current_curve_radius = 0.0f;
|
||||
m_curve_center = Vec3(0,0,0);
|
||||
m_current_track_direction = GraphNode::DIR_STRAIGHT;
|
||||
m_current_track_direction = DriveNode::DIR_STRAIGHT;
|
||||
m_item_to_collect = NULL;
|
||||
m_last_direction_node = 0;
|
||||
m_avoid_item_close = false;
|
||||
m_skid_probability_state = SKID_PROBAB_NOT_YET;
|
||||
m_last_item_random = NULL;
|
||||
|
||||
AIBaseLapController::reset();
|
||||
m_track_node = QuadGraph::UNKNOWN_SECTOR;
|
||||
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
|
||||
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
|
||||
m_track_node = Graph::UNKNOWN_SECTOR;
|
||||
DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
|
||||
if(m_track_node==Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::error(getControllerName().c_str(),
|
||||
"Invalid starting position for '%s' - not on track"
|
||||
" - can be ignored.",
|
||||
m_kart->getIdent().c_str());
|
||||
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
}
|
||||
|
||||
AIBaseLapController::reset();
|
||||
@@ -414,7 +415,7 @@ void SkiddingAI::handleBraking()
|
||||
|
||||
// If the kart is not facing roughly in the direction of the track, brake
|
||||
// so that it is easier for the kart to turn in the right direction.
|
||||
if(m_current_track_direction==GraphNode::DIR_UNDEFINED &&
|
||||
if(m_current_track_direction==DriveNode::DIR_UNDEFINED &&
|
||||
m_kart->getSpeed() > MIN_SPEED)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@@ -426,8 +427,8 @@ void SkiddingAI::handleBraking()
|
||||
m_controls->setBrake(true);
|
||||
return;
|
||||
}
|
||||
if(m_current_track_direction==GraphNode::DIR_LEFT ||
|
||||
m_current_track_direction==GraphNode::DIR_RIGHT )
|
||||
if(m_current_track_direction==DriveNode::DIR_LEFT ||
|
||||
m_current_track_direction==DriveNode::DIR_RIGHT )
|
||||
{
|
||||
float max_turn_speed =
|
||||
m_kart->getSpeedForTurnRadius(m_current_curve_radius);
|
||||
@@ -476,14 +477,14 @@ void SkiddingAI::handleSteering(float dt)
|
||||
m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() );
|
||||
|
||||
if( fabsf(side_dist) >
|
||||
0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f )
|
||||
0.5f* DriveGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f )
|
||||
{
|
||||
steer_angle = steerToPoint(QuadGraph::get()->getQuadOfNode(next)
|
||||
.getCenter());
|
||||
steer_angle = steerToPoint(DriveGraph::get()->getNode(next)
|
||||
->getCenter());
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
m_debug_sphere[0]->setPosition(QuadGraph::get()->getQuadOfNode(next)
|
||||
.getCenter().toIrrVector());
|
||||
m_debug_sphere[0]->setPosition(DriveGraph::get()->getNode(next)
|
||||
->getCenter().toIrrVector());
|
||||
Log::debug(getControllerName().c_str(),
|
||||
"Outside of road: steer to center point.");
|
||||
#endif
|
||||
@@ -530,7 +531,7 @@ void SkiddingAI::handleSteering(float dt)
|
||||
{
|
||||
m_start_kart_crash_direction = 0;
|
||||
Vec3 aim_point;
|
||||
int last_node = QuadGraph::UNKNOWN_SECTOR;
|
||||
int last_node = Graph::UNKNOWN_SECTOR;
|
||||
|
||||
switch(m_point_selection_algorithm)
|
||||
{
|
||||
@@ -619,12 +620,11 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
#ifdef AI_DEBUG
|
||||
m_item_sphere->setVisible(false);
|
||||
#endif
|
||||
// Angle of line from kart to aim_point
|
||||
float kart_aim_angle = atan2(aim_point->getX()-m_kart->getXYZ().getX(),
|
||||
aim_point->getZ()-m_kart->getXYZ().getZ());
|
||||
// Angle to aim_point
|
||||
Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ();
|
||||
|
||||
// Make sure we have a valid last_node
|
||||
if(last_node==QuadGraph::UNKNOWN_SECTOR)
|
||||
if(last_node==Graph::UNKNOWN_SECTOR)
|
||||
last_node = m_next_node_index[m_track_node];
|
||||
|
||||
int node = m_track_node;
|
||||
@@ -637,15 +637,15 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
const float max_item_lookahead_distance = 30.f;
|
||||
while(distance < max_item_lookahead_distance)
|
||||
{
|
||||
int q_index= QuadGraph::get()->getNode(node).getQuadIndex();
|
||||
int n_index= DriveGraph::get()->getNode(node)->getIndex();
|
||||
const std::vector<Item *> &items_ahead =
|
||||
ItemManager::get()->getItemsInQuads(q_index);
|
||||
ItemManager::get()->getItemsInQuads(n_index);
|
||||
for(unsigned int i=0; i<items_ahead.size(); i++)
|
||||
{
|
||||
evaluateItems(items_ahead[i], kart_aim_angle,
|
||||
evaluateItems(items_ahead[i], kart_aim_direction,
|
||||
&items_to_avoid, &items_to_collect);
|
||||
} // for i<items_ahead;
|
||||
distance += QuadGraph::get()->getDistanceToNext(node,
|
||||
distance += DriveGraph::get()->getDistanceToNext(node,
|
||||
m_successor_index[node]);
|
||||
node = m_next_node_index[node];
|
||||
// Stop when we have reached the last quad
|
||||
@@ -654,10 +654,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
|
||||
m_avoid_item_close = items_to_avoid.size()>0;
|
||||
|
||||
core::line2df line_to_target(aim_point->getX(),
|
||||
aim_point->getZ(),
|
||||
m_kart->getXYZ().getX(),
|
||||
m_kart->getXYZ().getZ());
|
||||
core::line3df line_to_target_3d((*aim_point).toIrrVector(),
|
||||
m_kart->getXYZ().toIrrVector());
|
||||
|
||||
// 2) If the kart is aiming for an item, but (suddenly) detects
|
||||
// some close-by items to avoid (e.g. behind the item, which was too
|
||||
@@ -670,7 +668,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
for(unsigned int i=0; i< items_to_avoid.size(); i++)
|
||||
{
|
||||
Vec3 d = items_to_avoid[i]->getXYZ()-m_item_to_collect->getXYZ();
|
||||
if( d.length2_2d()>m_ai_properties->m_bad_item_closeness_2)
|
||||
if( d.length2()>m_ai_properties->m_bad_item_closeness_2)
|
||||
continue;
|
||||
// It could make sense to also test if the bad item would
|
||||
// actually be hit, not only if it is close (which can result
|
||||
@@ -690,7 +688,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
// -------------------------------------
|
||||
if(m_item_to_collect)
|
||||
{
|
||||
if(handleSelectedItem(kart_aim_angle, aim_point))
|
||||
if(handleSelectedItem(kart_aim_direction, aim_point))
|
||||
{
|
||||
// Still aim at the previsouly selected item.
|
||||
*aim_point = m_item_to_collect->getXYZ();
|
||||
@@ -715,7 +713,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
{
|
||||
// If we need to steer to avoid an item, this takes priority,
|
||||
// ignore items to collect and return the new aim_point.
|
||||
if(steerToAvoid(items_to_avoid, line_to_target, aim_point))
|
||||
if(steerToAvoid(items_to_avoid, line_to_target_3d, aim_point))
|
||||
{
|
||||
#ifdef AI_DEBUG
|
||||
m_item_sphere->setVisible(true);
|
||||
@@ -764,7 +762,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
// it's on a good enough driveline, so make this item a permanent
|
||||
// target. Otherwise only try to get closer (till hopefully this
|
||||
// item s on our driveline)
|
||||
if(item_to_collect->hitLine(line_to_target, m_kart))
|
||||
if(item_to_collect->hitLine(line_to_target_3d, m_kart))
|
||||
{
|
||||
#ifdef AI_DEBUG
|
||||
m_item_sphere->setVisible(true);
|
||||
@@ -782,10 +780,10 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
{
|
||||
// Kart will not hit item, try to get closer to this item
|
||||
// so that it can potentially become a permanent target.
|
||||
Vec3 xyz = item_to_collect->getXYZ();
|
||||
float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(),
|
||||
xyz.getZ() - m_kart->getXYZ().getZ());
|
||||
float angle = normalizeAngle(kart_aim_angle - item_angle);
|
||||
const Vec3& xyz = item_to_collect->getXYZ();
|
||||
float angle_to_item = (xyz - m_kart->getXYZ())
|
||||
.angle(kart_aim_direction);
|
||||
float angle = normalizeAngle(angle_to_item);
|
||||
|
||||
if(fabsf(angle) < 0.3)
|
||||
{
|
||||
@@ -826,8 +824,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
bool SkiddingAI::hitBadItemWhenAimAt(const Item *item,
|
||||
const std::vector<const Item *> &items_to_avoid)
|
||||
{
|
||||
core::line2df to_item(m_kart->getXYZ().getX(), m_kart->getXYZ().getZ(),
|
||||
item->getXYZ().getX(), item->getXYZ().getZ() );
|
||||
core::line3df to_item(m_kart->getXYZ().toIrrVector(),
|
||||
item->getXYZ().toIrrVector());
|
||||
for(unsigned int i=0; i<items_to_avoid.size(); i++)
|
||||
{
|
||||
if(items_to_avoid[i]->hitLine(to_item, m_kart))
|
||||
@@ -848,7 +846,7 @@ bool SkiddingAI::hitBadItemWhenAimAt(const Item *item,
|
||||
* \param last_node
|
||||
* \return True if th AI should still aim for the pre-selected item.
|
||||
*/
|
||||
bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
|
||||
bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point)
|
||||
{
|
||||
// If the item is unavailable keep on testing. It is not necessary
|
||||
// to test if an item has turned bad, this was tested before this
|
||||
@@ -857,10 +855,9 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
|
||||
return false;
|
||||
|
||||
const Vec3 &xyz = m_item_to_collect->getXYZ();
|
||||
float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(),
|
||||
xyz.getZ() - m_kart->getXYZ().getZ());
|
||||
float angle_to_item = (xyz - m_kart->getXYZ()).angle(kart_aim_direction);
|
||||
float angle = normalizeAngle(angle_to_item);
|
||||
|
||||
float angle = normalizeAngle(kart_aim_angle - item_angle);
|
||||
if(fabsf(angle)>1.5)
|
||||
{
|
||||
// We (likely) have passed the item we were aiming for
|
||||
@@ -885,7 +882,7 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
|
||||
* \return True if steering is necessary to avoid an item.
|
||||
*/
|
||||
bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
const core::line2df &line_to_target,
|
||||
const core::line3df &line_to_target,
|
||||
Vec3 *aim_point)
|
||||
{
|
||||
// First determine the left-most and right-most item.
|
||||
@@ -911,14 +908,19 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
|
||||
// Check if we would drive left of the leftmost or right of the
|
||||
// rightmost point - if so, nothing to do.
|
||||
core::vector2df left(items_to_avoid[index_left_most]->getXYZ().getX(),
|
||||
items_to_avoid[index_left_most]->getXYZ().getZ());
|
||||
const Vec3& left = items_to_avoid[index_left_most]->getXYZ();
|
||||
int node_index = items_to_avoid[index_left_most]->getGraphNode();
|
||||
const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal();
|
||||
Vec3 p1 = line_to_target.start,
|
||||
p2 = line_to_target.getMiddle() + normal.toIrrVector(),
|
||||
p3 = line_to_target.end;
|
||||
|
||||
int item_index = -1;
|
||||
bool is_left = false;
|
||||
|
||||
// >=0 means the point is to the right of the line, or the line is
|
||||
// to the left of the point.
|
||||
if(line_to_target.getPointOrientation(left)>=0)
|
||||
if(left.sideofPlane(p1,p2,p3) <= 0)
|
||||
{
|
||||
// Left of leftmost point
|
||||
item_index = index_left_most;
|
||||
@@ -926,9 +928,14 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
}
|
||||
else
|
||||
{
|
||||
core::vector2df right(items_to_avoid[index_right_most]->getXYZ().getX(),
|
||||
items_to_avoid[index_right_most]->getXYZ().getZ());
|
||||
if(line_to_target.getPointOrientation(right)<=0)
|
||||
const Vec3& right = items_to_avoid[index_right_most]->getXYZ();
|
||||
int node_index = items_to_avoid[index_right_most]->getGraphNode();
|
||||
const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal();
|
||||
Vec3 p1 = line_to_target.start,
|
||||
p2 = line_to_target.getMiddle() + normal.toIrrVector(),
|
||||
p3 = line_to_target.end;
|
||||
|
||||
if (right.sideofPlane(p1, p2, p3) >= 0)
|
||||
{
|
||||
// Right of rightmost point
|
||||
item_index = index_right_most;
|
||||
@@ -969,20 +976,19 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
|
||||
float min_distance[2] = {99999.9f, 99999.9f};
|
||||
int index[2] = {-1, -1};
|
||||
core::vector2df closest2d[2];
|
||||
core::vector3df closest3d[2];
|
||||
for(unsigned int i=0; i<items_to_avoid.size(); i++)
|
||||
{
|
||||
const Vec3 &xyz = items_to_avoid[i]->getXYZ();
|
||||
core::vector2df item2d = xyz.toIrrVector2d();
|
||||
core::vector2df point2d = line_to_target.getClosestPoint(item2d);
|
||||
float d = (xyz.toIrrVector2d() - point2d).getLengthSQ();
|
||||
float direction = line_to_target.getPointOrientation(item2d);
|
||||
int ind = direction<0 ? 0 : 1;
|
||||
core::vector3df point3d = line_to_target.getClosestPoint(xyz.toIrrVector());
|
||||
float d = (xyz.toIrrVector() - point3d).getLengthSQ();
|
||||
float direction = xyz.sideofPlane(p1,p2,p3);
|
||||
int ind = direction<0 ? 1 : 0;
|
||||
if(d<min_distance[ind])
|
||||
{
|
||||
min_distance[ind] = d;
|
||||
index[ind] = i;
|
||||
closest2d[ind] = point2d;
|
||||
closest3d[ind] = point3d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,8 +998,8 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
|
||||
// We are driving between item_to_avoid[index[0]] and ...[1].
|
||||
// If we don't hit any of them, just keep on driving as normal
|
||||
bool hit_left = items_to_avoid[index[0]]->hitKart(closest2d[0], m_kart);
|
||||
bool hit_right = items_to_avoid[index[1]]->hitKart(closest2d[1], m_kart);
|
||||
bool hit_left = items_to_avoid[index[0]]->hitKart(closest3d[0], m_kart);
|
||||
bool hit_right = items_to_avoid[index[1]]->hitKart(closest3d[1], m_kart);
|
||||
if( !hit_left && !hit_right)
|
||||
return false;
|
||||
|
||||
@@ -1027,7 +1033,7 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
* (NULL if no item was avoided so far).
|
||||
* \param item_to_collect A pointer to a previously selected item to collect.
|
||||
*/
|
||||
void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
|
||||
void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction,
|
||||
std::vector<const Item *> *items_to_avoid,
|
||||
std::vector<const Item *> *items_to_collect)
|
||||
{
|
||||
@@ -1075,13 +1081,10 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
|
||||
// to avoid are collected).
|
||||
if(!avoid)
|
||||
{
|
||||
// item_angle The angle of the item (relative to the forward axis,
|
||||
// so 0 means straight ahead in world coordinates!).
|
||||
const Vec3 &xyz = item->getXYZ();
|
||||
float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(),
|
||||
xyz.getZ() - m_kart->getXYZ().getZ());
|
||||
|
||||
float diff = normalizeAngle(kart_aim_angle-item_angle);
|
||||
float angle_to_item =
|
||||
(xyz - m_kart->getXYZ()).angle(kart_aim_direction);
|
||||
float diff = normalizeAngle(angle_to_item);
|
||||
|
||||
// The kart is driving at high speed, when the current max speed
|
||||
// is higher than the max speed of the kart (which is caused by
|
||||
@@ -1110,14 +1113,14 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
|
||||
else
|
||||
list = items_to_collect;
|
||||
|
||||
float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2_2d();
|
||||
float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2();
|
||||
|
||||
// This list is usually very short, so use a simple bubble sort
|
||||
list->push_back(item);
|
||||
int i;
|
||||
for(i=(int)list->size()-2; i>=0; i--)
|
||||
{
|
||||
float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2_2d();
|
||||
float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2();
|
||||
if(d<=new_distance)
|
||||
{
|
||||
break;
|
||||
@@ -1299,15 +1302,19 @@ void SkiddingAI::handleItems(const float dt)
|
||||
bool straight_ahead = false;
|
||||
if (m_kart_behind)
|
||||
{
|
||||
posData behind_pos = {0};
|
||||
checkPosition(m_kart_behind->getXYZ(), &behind_pos);
|
||||
if (behind_pos.angle < 0.2f) straight_behind = true;
|
||||
Vec3 behind_lc = m_kart->getTrans().inverse()
|
||||
(m_kart_behind->getXYZ());
|
||||
const float abs_angle =
|
||||
atan2f(fabsf(behind_lc.x()), fabsf(behind_lc.z()));
|
||||
if (abs_angle < 0.2f) straight_behind = true;
|
||||
}
|
||||
if (m_kart_ahead)
|
||||
{
|
||||
posData ahead_pos = {0};
|
||||
checkPosition(m_kart_ahead->getXYZ(), &ahead_pos);
|
||||
if (ahead_pos.angle < 0.2f) straight_ahead = true;
|
||||
Vec3 ahead_lc = m_kart->getTrans().inverse()
|
||||
(m_kart_ahead->getXYZ());
|
||||
const float abs_angle =
|
||||
atan2f(fabsf(ahead_lc.x()), fabsf(ahead_lc.z()));
|
||||
if (abs_angle < 0.2f) straight_ahead = true;
|
||||
}
|
||||
|
||||
// Bowling balls are slower, so only fire on closer karts - but when
|
||||
@@ -1679,14 +1686,14 @@ void SkiddingAI::handleNitroAndZipper()
|
||||
m_kart->getSpeed()>1.0f &&
|
||||
m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0)
|
||||
{
|
||||
GraphNode::DirectionType dir;
|
||||
DriveNode::DirectionType dir;
|
||||
unsigned int last;
|
||||
const GraphNode &gn = QuadGraph::get()->getNode(m_track_node);
|
||||
gn.getDirectionData(m_successor_index[m_track_node], &dir, &last);
|
||||
if(dir==GraphNode::DIR_STRAIGHT)
|
||||
const DriveNode* dn = DriveGraph::get()->getNode(m_track_node);
|
||||
dn->getDirectionData(m_successor_index[m_track_node], &dir, &last);
|
||||
if(dir==DriveNode::DIR_STRAIGHT)
|
||||
{
|
||||
float diff = QuadGraph::get()->getDistanceFromStart(last)
|
||||
- QuadGraph::get()->getDistanceFromStart(m_track_node);
|
||||
float diff = DriveGraph::get()->getDistanceFromStart(last)
|
||||
- DriveGraph::get()->getDistanceFromStart(m_track_node);
|
||||
if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength();
|
||||
if(diff>m_ai_properties->m_straight_length_for_zipper)
|
||||
m_controls->setFire(true);
|
||||
@@ -1730,16 +1737,14 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
|
||||
|
||||
const size_t NUM_KARTS = m_world->getNumKarts();
|
||||
|
||||
//Protection against having vel_normal with nan values
|
||||
const Vec3 &VEL = m_kart->getVelocity();
|
||||
Vec3 vel_normal(VEL.getX(), 0.0, VEL.getZ());
|
||||
float speed=vel_normal.length();
|
||||
float speed = m_kart->getVelocity().length();
|
||||
// If the velocity is zero, no sense in checking for crashes in time
|
||||
if(speed==0) return;
|
||||
|
||||
Vec3 vel_normal = m_kart->getVelocity().normalized();
|
||||
|
||||
// Time it takes to drive for m_kart_length units.
|
||||
float dt = m_kart_length / speed;
|
||||
vel_normal/=speed;
|
||||
|
||||
int current_node = m_track_node;
|
||||
if(steps<1 || steps>1000)
|
||||
@@ -1769,7 +1774,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
|
||||
continue;
|
||||
Vec3 other_kart_xyz = other_kart->getXYZ()
|
||||
+ other_kart->getVelocity()*(i*dt);
|
||||
float kart_distance = (step_coord - other_kart_xyz).length_2d();
|
||||
float kart_distance = (step_coord - other_kart_xyz).length();
|
||||
|
||||
if( kart_distance < m_kart_length)
|
||||
m_crashes.m_kart = j;
|
||||
@@ -1777,12 +1782,12 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
|
||||
}
|
||||
|
||||
/*Find if we crash with the drivelines*/
|
||||
if(current_node!=QuadGraph::UNKNOWN_SECTOR &&
|
||||
if(current_node!=Graph::UNKNOWN_SECTOR &&
|
||||
m_next_node_index[current_node]!=-1)
|
||||
QuadGraph::get()->findRoadSector(step_coord, ¤t_node,
|
||||
DriveGraph::get()->findRoadSector(step_coord, ¤t_node,
|
||||
/* sectors to test*/ &m_all_look_aheads[current_node]);
|
||||
|
||||
if( current_node == QuadGraph::UNKNOWN_SECTOR)
|
||||
if( current_node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
m_crashes.m_road = true;
|
||||
return;
|
||||
@@ -1830,23 +1835,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
*last_node = m_next_node_index[m_track_node];
|
||||
const core::vector2df xz = m_kart->getXYZ().toIrrVector2d();
|
||||
|
||||
const Quad &q = QuadGraph::get()->getQuadOfNode(*last_node);
|
||||
const DriveNode* dn = DriveGraph::get()->getNode(*last_node);
|
||||
|
||||
// Index of the left and right end of a quad.
|
||||
const unsigned int LEFT_END_POINT = 0;
|
||||
const unsigned int RIGHT_END_POINT = 1;
|
||||
core::line2df left (xz, q[LEFT_END_POINT ].toIrrVector2d());
|
||||
core::line2df right(xz, q[RIGHT_END_POINT].toIrrVector2d());
|
||||
core::line2df left (xz, (*dn)[LEFT_END_POINT ].toIrrVector2d());
|
||||
core::line2df right(xz, (*dn)[RIGHT_END_POINT].toIrrVector2d());
|
||||
|
||||
#if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
|
||||
const Vec3 eps1(0,0.5f,0);
|
||||
m_curve[CURVE_LEFT]->clear();
|
||||
m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
m_curve[CURVE_LEFT]->addPoint(q[LEFT_END_POINT]+eps1);
|
||||
m_curve[CURVE_LEFT]->addPoint((*dn)[LEFT_END_POINT]+eps1);
|
||||
m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
m_curve[CURVE_RIGHT]->clear();
|
||||
m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
m_curve[CURVE_RIGHT]->addPoint(q[RIGHT_END_POINT]+eps1);
|
||||
m_curve[CURVE_RIGHT]->addPoint((*dn)[RIGHT_END_POINT]+eps1);
|
||||
m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
#endif
|
||||
#if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
|
||||
@@ -1859,13 +1864,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
while(1)
|
||||
{
|
||||
unsigned int next_sector = m_next_node_index[*last_node];
|
||||
const Quad &q_next = QuadGraph::get()->getQuadOfNode(next_sector);
|
||||
const DriveNode* dn_next = DriveGraph::get()->getNode(next_sector);
|
||||
// Test if the next left point is to the right of the left
|
||||
// line. If so, a new left line is defined.
|
||||
if(left.getPointOrientation(q_next[LEFT_END_POINT].toIrrVector2d())
|
||||
if(left.getPointOrientation((*dn_next)[LEFT_END_POINT].toIrrVector2d())
|
||||
< 0 )
|
||||
{
|
||||
core::vector2df p = q_next[LEFT_END_POINT].toIrrVector2d();
|
||||
core::vector2df p = (*dn_next)[LEFT_END_POINT].toIrrVector2d();
|
||||
// Stop if the new point is to the right of the right line
|
||||
if(right.getPointOrientation(p)<0)
|
||||
break;
|
||||
@@ -1881,10 +1886,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
|
||||
// Test if new right point is to the left of the right line. If
|
||||
// so, a new right line is defined.
|
||||
if(right.getPointOrientation(q_next[RIGHT_END_POINT].toIrrVector2d())
|
||||
if(right.getPointOrientation((*dn_next)[RIGHT_END_POINT].toIrrVector2d())
|
||||
> 0 )
|
||||
{
|
||||
core::vector2df p = q_next[RIGHT_END_POINT].toIrrVector2d();
|
||||
core::vector2df p = (*dn_next)[RIGHT_END_POINT].toIrrVector2d();
|
||||
// Break if new point is to the left of left line
|
||||
if(left.getPointOrientation(p)>0)
|
||||
break;
|
||||
@@ -1906,7 +1911,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
// 0.5f*(left.end.Y+right.end.Y));
|
||||
//*result = ppp;
|
||||
|
||||
*result = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
|
||||
*result = DriveGraph::get()->getNode(*last_node)->getCenter();
|
||||
} // findNonCrashingPointNew
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1943,10 +1948,10 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
target_sector = m_next_node_index[*last_node];
|
||||
|
||||
//direction is a vector from our kart to the sectors we are testing
|
||||
direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter()
|
||||
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
|
||||
- m_kart->getXYZ();
|
||||
|
||||
float len=direction.length_2d();
|
||||
float len=direction.length();
|
||||
unsigned int steps = (unsigned int)( len / m_kart_length );
|
||||
if( steps < 3 ) steps = 3;
|
||||
|
||||
@@ -1966,23 +1971,23 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
{
|
||||
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
|
||||
|
||||
QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
*last_node );
|
||||
|
||||
float distance = fabsf(step_track_coord[0]);
|
||||
|
||||
//If we are outside, the previous node is what we are looking for
|
||||
if ( distance + m_kart_width * 0.5f
|
||||
> QuadGraph::get()->getNode(*last_node).getPathWidth()*0.5f )
|
||||
> DriveGraph::get()->getNode(*last_node)->getPathWidth()*0.5f )
|
||||
{
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node)
|
||||
.getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)
|
||||
->getCenter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
*last_node = target_sector;
|
||||
} // for i<100
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)->getCenter();
|
||||
} // findNonCrashingPointFixed
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1990,14 +1995,14 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
* 1. the test:
|
||||
*
|
||||
* distance + m_kart_width * 0.5f
|
||||
* > QuadGraph::get()->getNode(*last_node).getPathWidth() )
|
||||
* > DriveGraph::get()->getNode(*last_node)->getPathWidth() )
|
||||
*
|
||||
* is incorrect, it should compare with getPathWith*0.5f (since distance
|
||||
* is the distance from the center, i.e. it is half the path width if
|
||||
* the point is at the edge).
|
||||
* 2. the test:
|
||||
*
|
||||
* QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
* DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
* *last_node );
|
||||
* in the for loop tests always against distance from the same
|
||||
* graph node (*last_node), while de-fact the loop will test points
|
||||
@@ -2022,7 +2027,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
|
||||
#endif
|
||||
*last_node = m_next_node_index[m_track_node];
|
||||
float angle = QuadGraph::get()->getAngleToNext(m_track_node,
|
||||
float angle = DriveGraph::get()->getAngleToNext(m_track_node,
|
||||
m_successor_index[m_track_node]);
|
||||
int target_sector;
|
||||
|
||||
@@ -2038,7 +2043,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
// target_sector is the sector at the longest distance that we can
|
||||
// drive to without crashing with the track.
|
||||
target_sector = m_next_node_index[*last_node];
|
||||
angle1 = QuadGraph::get()->getAngleToNext(target_sector,
|
||||
angle1 = DriveGraph::get()->getAngleToNext(target_sector,
|
||||
m_successor_index[target_sector]);
|
||||
// In very sharp turns this algorithm tends to aim at off track points,
|
||||
// resulting in hitting a corner. So test for this special case and
|
||||
@@ -2046,16 +2051,16 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
float diff = normalizeAngle(angle1-angle);
|
||||
if(fabsf(diff)>1.5f)
|
||||
{
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(target_sector)
|
||||
.getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(target_sector)
|
||||
->getCenter();
|
||||
return;
|
||||
}
|
||||
|
||||
//direction is a vector from our kart to the sectors we are testing
|
||||
direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter()
|
||||
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
|
||||
- m_kart->getXYZ();
|
||||
|
||||
float len=direction.length_2d();
|
||||
float len=direction.length();
|
||||
unsigned int steps = (unsigned int)( len / m_kart_length );
|
||||
if( steps < 3 ) steps = 3;
|
||||
|
||||
@@ -2075,24 +2080,24 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
{
|
||||
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
|
||||
|
||||
QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
*last_node );
|
||||
|
||||
float distance = fabsf(step_track_coord[0]);
|
||||
|
||||
//If we are outside, the previous node is what we are looking for
|
||||
if ( distance + m_kart_width * 0.5f
|
||||
> QuadGraph::get()->getNode(*last_node).getPathWidth() )
|
||||
> DriveGraph::get()->getNode(*last_node)->getPathWidth() )
|
||||
{
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node)
|
||||
.getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)
|
||||
->getCenter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
angle = angle1;
|
||||
*last_node = target_sector;
|
||||
} // for i<100
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)->getCenter();
|
||||
} // findNonCrashingPoint
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -2101,10 +2106,17 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
*/
|
||||
void SkiddingAI::determineTrackDirection()
|
||||
{
|
||||
const QuadGraph *qg = QuadGraph::get();
|
||||
unsigned int succ = m_successor_index[m_track_node];
|
||||
float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ)
|
||||
- m_kart->getHeading();
|
||||
const DriveGraph *dg = DriveGraph::get();
|
||||
unsigned int succ = m_successor_index[m_track_node];
|
||||
unsigned int next = dg->getNode(m_track_node)->getSuccessor(succ);
|
||||
float angle_to_track = 0.0f;
|
||||
if (m_kart->getVelocity().length() > 0.0f)
|
||||
{
|
||||
Vec3 track_direction = -dg->getNode(m_track_node)->getCenter()
|
||||
+ dg->getNode(next)->getCenter();
|
||||
angle_to_track =
|
||||
track_direction.angle(m_kart->getVelocity().normalized());
|
||||
}
|
||||
angle_to_track = normalizeAngle(angle_to_track);
|
||||
|
||||
// In certain circumstances (esp. S curves) it is possible that the
|
||||
@@ -2120,26 +2132,24 @@ void SkiddingAI::determineTrackDirection()
|
||||
// quicker be aligned with the track again).
|
||||
if(fabsf(angle_to_track) > 0.22222f * M_PI)
|
||||
{
|
||||
m_current_track_direction = GraphNode::DIR_UNDEFINED;
|
||||
m_current_track_direction = DriveNode::DIR_UNDEFINED;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int next = qg->getNode(m_track_node).getSuccessor(succ);
|
||||
|
||||
qg->getNode(next).getDirectionData(m_successor_index[next],
|
||||
&m_current_track_direction,
|
||||
&m_last_direction_node);
|
||||
dg->getNode(next)->getDirectionData(m_successor_index[next],
|
||||
&m_current_track_direction,
|
||||
&m_last_direction_node);
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
m_curve[CURVE_QG]->clear();
|
||||
for(unsigned int i=m_track_node; i<=m_last_direction_node; i++)
|
||||
{
|
||||
m_curve[CURVE_QG]->addPoint(qg->getNode(i).getCenter());
|
||||
m_curve[CURVE_QG]->addPoint(dg->getNode(i)->getCenter());
|
||||
}
|
||||
#endif
|
||||
|
||||
if(m_current_track_direction==GraphNode::DIR_LEFT ||
|
||||
m_current_track_direction==GraphNode::DIR_RIGHT )
|
||||
if(m_current_track_direction==DriveNode::DIR_LEFT ||
|
||||
m_current_track_direction==DriveNode::DIR_RIGHT )
|
||||
{
|
||||
handleCurve();
|
||||
} // if(m_current_track_direction == DIR_LEFT || DIR_RIGHT )
|
||||
@@ -2164,13 +2174,10 @@ void SkiddingAI::handleCurve()
|
||||
// kart will already point towards the direction of the circle), and
|
||||
// the case that the kart is facing wrong was already tested for before
|
||||
|
||||
const QuadGraph *qg = QuadGraph::get();
|
||||
Vec3 xyz = m_kart->getXYZ();
|
||||
Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz;
|
||||
Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter();
|
||||
const DriveGraph *dg = DriveGraph::get();
|
||||
const Vec3& last_xyz = dg->getNode(m_last_direction_node)->getCenter();
|
||||
|
||||
determineTurnRadius(xyz, tangent, last_xyz,
|
||||
&m_curve_center, &m_current_curve_radius);
|
||||
determineTurnRadius(last_xyz, &m_curve_center, &m_current_curve_radius);
|
||||
assert(!std::isnan(m_curve_center.getX()));
|
||||
assert(!std::isnan(m_curve_center.getY()));
|
||||
assert(!std::isnan(m_curve_center.getZ()));
|
||||
@@ -2184,13 +2191,14 @@ void SkiddingAI::handleCurve()
|
||||
{
|
||||
i = m_next_node_index[i];
|
||||
// Pick either the lower left or right point:
|
||||
int index = m_current_track_direction==GraphNode::DIR_LEFT
|
||||
int index = m_current_track_direction==DriveNode::DIR_LEFT
|
||||
? 0 : 1;
|
||||
float r = (m_curve_center - qg->getQuadOfNode(i)[index]).length();
|
||||
Vec3 curve_center_wc = m_kart->getTrans()(m_curve_center);
|
||||
float r = (curve_center_wc - *(dg->getNode(i))[index]).length();
|
||||
if(m_current_curve_radius < r)
|
||||
{
|
||||
last_xyz = qg->getQuadOfNode(i)[index];
|
||||
determineTurnRadius(xyz, tangent, last_xyz,
|
||||
last_xyz = *(dg->getNode(i)[index]);
|
||||
determineTurnRadius(last_xyz,
|
||||
&m_curve_center, &m_current_curve_radius);
|
||||
m_last_direction_node = i;
|
||||
break;
|
||||
@@ -2200,11 +2208,11 @@ void SkiddingAI::handleCurve()
|
||||
}
|
||||
#endif
|
||||
#if defined(AI_DEBUG) && defined(AI_DEBUG_CIRCLES)
|
||||
m_curve[CURVE_PREDICT1]->makeCircle(m_curve_center,
|
||||
m_curve[CURVE_PREDICT1]->makeCircle(m_kart->getTrans()(m_curve_center),
|
||||
m_current_curve_radius);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(last_xyz);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(m_curve_center);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(xyz);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(m_kart->getTrans()(m_curve_center));
|
||||
m_curve[CURVE_PREDICT1]->addPoint(m_kart->getXYZ());
|
||||
#endif
|
||||
|
||||
} // handleCurve
|
||||
@@ -2243,8 +2251,8 @@ bool SkiddingAI::canSkid(float steer_fraction)
|
||||
}
|
||||
|
||||
// No skidding on straights
|
||||
if(m_current_track_direction==GraphNode::DIR_STRAIGHT ||
|
||||
m_current_track_direction==GraphNode::DIR_UNDEFINED )
|
||||
if(m_current_track_direction==DriveNode::DIR_STRAIGHT ||
|
||||
m_current_track_direction==DriveNode::DIR_UNDEFINED )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(m_controls->getSkidControl() && m_ai_debug)
|
||||
@@ -2258,18 +2266,19 @@ bool SkiddingAI::canSkid(float steer_fraction)
|
||||
}
|
||||
|
||||
const float MIN_SKID_SPEED = 5.0f;
|
||||
const QuadGraph *qg = QuadGraph::get();
|
||||
Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter();
|
||||
const DriveGraph *dg = DriveGraph::get();
|
||||
Vec3 last_xyz = m_kart->getTrans().inverse()
|
||||
(dg->getNode(m_last_direction_node)->getCenter());
|
||||
|
||||
// Only try skidding when a certain minimum speed is reached.
|
||||
if(m_kart->getSpeed()<MIN_SKID_SPEED) return false;
|
||||
|
||||
// Estimate how long it takes to finish the curve
|
||||
Vec3 diff_kart = m_kart->getXYZ() - m_curve_center;
|
||||
Vec3 diff_last = last_xyz - m_curve_center;
|
||||
Vec3 diff_kart = -m_curve_center;
|
||||
Vec3 diff_last = last_xyz - m_curve_center;
|
||||
float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ());
|
||||
float angle_last = atan2(diff_last.getX(), diff_last.getZ());
|
||||
float angle = m_current_track_direction == GraphNode::DIR_RIGHT
|
||||
float angle = m_current_track_direction == DriveNode::DIR_RIGHT
|
||||
? angle_last - angle_kart
|
||||
: angle_kart - angle_last;
|
||||
angle = normalizeAngle(angle);
|
||||
@@ -2296,9 +2305,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
|
||||
// left turn steer right to avoid getting too close to the left
|
||||
// vorder). In this case skidding will be useless.
|
||||
else if( (steer_fraction > 0 &&
|
||||
m_current_track_direction==GraphNode::DIR_LEFT) ||
|
||||
m_current_track_direction==DriveNode::DIR_LEFT) ||
|
||||
(steer_fraction < 0 &&
|
||||
m_current_track_direction==GraphNode::DIR_RIGHT) )
|
||||
m_current_track_direction==DriveNode::DIR_RIGHT) )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(m_controls->getSkidControl() && m_ai_debug)
|
||||
@@ -2450,55 +2459,3 @@ void SkiddingAI::setSteering(float angle, float dt)
|
||||
|
||||
|
||||
} // setSteering
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determine the center point and radius of a circle given two points on
|
||||
* the ccircle and the tangent at the first point. This is done as follows:
|
||||
* 1. Determine the line going through the center point start+end, which is
|
||||
* orthogonal to the vector from start to end. This line goes through the
|
||||
* center of the circle.
|
||||
* 2. Determine the line going through the first point and is orthogonal
|
||||
* to the given tangent.
|
||||
* 3. The intersection of these two lines is the center of the circle.
|
||||
* \param[in] start First point.
|
||||
* \param[in] tangent Tangent at first point.
|
||||
* \param[in] end Second point on circle.
|
||||
* \param[out] center Center point of the circle.
|
||||
* \param[out] radius Radius of the circle.
|
||||
*/
|
||||
void SkiddingAI::determineTurnRadius(const Vec3 &start,
|
||||
const Vec3 &tangent,
|
||||
const Vec3 &end,
|
||||
Vec3 *center,
|
||||
float *radius)
|
||||
{
|
||||
// 1) Line through middle of start+end
|
||||
Vec3 mid = 0.5f*(start+end);
|
||||
Vec3 direction = end-start;
|
||||
|
||||
Vec3 orthogonal(direction.getZ(), 0, -direction.getX());
|
||||
Vec3 q1 = mid + orthogonal;
|
||||
irr::core::line2df line1(mid.getX(), mid.getZ(),
|
||||
q1.getX(), q1.getZ() );
|
||||
|
||||
Vec3 ortho_tangent(tangent.getZ(), 0, -tangent.getX());
|
||||
Vec3 q2 = start + ortho_tangent;
|
||||
irr::core::line2df line2(start.getX(), start.getZ(),
|
||||
q2.getX(), q2.getZ());
|
||||
|
||||
|
||||
irr::core::vector2df result;
|
||||
if(line1.intersectWith(line2, result, /*checkOnlySegments*/false))
|
||||
{
|
||||
*center = Vec3(result.X, start.getY(), result.Y);
|
||||
*radius = (start - *center).length();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No intersection. In this case assume that the two points are
|
||||
// on a semicircle, in which case the center is at 0.5*(start+end):
|
||||
*center = 0.5f*(start+end);
|
||||
*radius = 0.5f*(end-start).length();
|
||||
}
|
||||
return;
|
||||
} // determineTurnRadius
|
||||
|
||||
@@ -45,11 +45,11 @@
|
||||
|
||||
#include "karts/controller/ai_base_lap_controller.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/graph_node.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
class LinearWorld;
|
||||
class QuadGraph;
|
||||
class DriveGraph;
|
||||
class ShowCurve;
|
||||
class Track;
|
||||
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
int m_start_kart_crash_direction;
|
||||
|
||||
/** The direction of the track where the kart is on atm. */
|
||||
GraphNode::DirectionType m_current_track_direction;
|
||||
DriveNode::DirectionType m_current_track_direction;
|
||||
|
||||
/** The radius of the curve the kart is currently driving. Undefined
|
||||
* when being on a straigt section. */
|
||||
@@ -249,13 +249,13 @@ private:
|
||||
void computeNearestKarts();
|
||||
void handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
int last_node);
|
||||
bool handleSelectedItem(float kart_aim_angle, Vec3 *aim_point);
|
||||
bool handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point);
|
||||
bool steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
const core::line2df &line_to_target,
|
||||
const core::line3df &line_to_target,
|
||||
Vec3 *aim_point);
|
||||
bool hitBadItemWhenAimAt(const Item *item,
|
||||
const std::vector<const Item *> &items_to_avoid);
|
||||
void evaluateItems(const Item *item, float kart_aim_angle,
|
||||
void evaluateItems(const Item *item, Vec3 kart_aim_direction,
|
||||
std::vector<const Item *> *items_to_avoid,
|
||||
std::vector<const Item *> *items_to_collect);
|
||||
|
||||
@@ -265,11 +265,6 @@ private:
|
||||
void findNonCrashingPoint(Vec3 *result, int *last_node);
|
||||
|
||||
void determineTrackDirection();
|
||||
void determineTurnRadius(const Vec3 &start,
|
||||
const Vec3 &start_direction,
|
||||
const Vec3 &end,
|
||||
Vec3 *center,
|
||||
float *radius);
|
||||
virtual bool canSkid(float steer_fraction);
|
||||
virtual void setSteering(float angle, float dt);
|
||||
void handleCurve();
|
||||
|
||||
@@ -24,13 +24,10 @@
|
||||
#include "karts/controller/kart_control.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "modes/soccer_world.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
#include "irrlicht.h"
|
||||
#include <iostream>
|
||||
using namespace irr;
|
||||
using namespace std;
|
||||
#endif
|
||||
|
||||
#ifdef BALL_AIM_DEBUG
|
||||
@@ -95,12 +92,14 @@ SoccerAI::~SoccerAI()
|
||||
void SoccerAI::reset()
|
||||
{
|
||||
ArenaAI::reset();
|
||||
AIBaseController::reset();
|
||||
|
||||
m_overtake_ball = false;
|
||||
m_force_brake = false;
|
||||
m_chasing_ball = false;
|
||||
|
||||
m_front_transform.setOrigin(m_kart->getFrontXYZ());
|
||||
m_front_transform.setBasis(m_kart->getTrans().getBasis());
|
||||
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -114,6 +113,8 @@ void SoccerAI::update(float dt)
|
||||
#endif
|
||||
m_force_brake = false;
|
||||
m_chasing_ball = false;
|
||||
m_front_transform.setOrigin(m_kart->getFrontXYZ());
|
||||
m_front_transform.setBasis(m_kart->getTrans().getBasis());
|
||||
|
||||
if (m_world->getPhase() == World::GOAL_PHASE)
|
||||
{
|
||||
@@ -155,11 +156,9 @@ void SoccerAI::findClosestKart(bool use_difficulty)
|
||||
}
|
||||
}
|
||||
|
||||
const AbstractKart* closest_kart = m_world->getKart(closest_kart_num);
|
||||
m_closest_kart_node = m_world->getKartNode(closest_kart_num);
|
||||
m_closest_kart_point = closest_kart->getXYZ();
|
||||
m_closest_kart = m_world->getKart(closest_kart_num);
|
||||
checkPosition(m_closest_kart_point, &m_closest_kart_pos_data);
|
||||
m_closest_kart_node = m_world->getSectorForKart(m_closest_kart);
|
||||
m_closest_kart_point = m_closest_kart->getXYZ();
|
||||
|
||||
} // findClosestKart
|
||||
|
||||
@@ -188,8 +187,9 @@ void SoccerAI::findTarget()
|
||||
{
|
||||
// This AI will attack the other team ball chaser
|
||||
int id = m_world->getBallChaser(m_opp_team);
|
||||
m_target_point = m_world->getKart(id)->getXYZ();
|
||||
m_target_node = m_world->getKartNode(id);
|
||||
const AbstractKart* kart = m_world->getKart(id);
|
||||
m_target_point = kart->getXYZ();
|
||||
m_target_node = m_world->getSectorForKart(kart);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -215,10 +215,8 @@ Vec3 SoccerAI::determineBallAimingPosition()
|
||||
const Vec3& ball_aim_pos = m_world->getBallAimPosition(m_opp_team);
|
||||
const Vec3& orig_pos = m_world->getBallPosition();
|
||||
|
||||
Vec3 ball_lc;
|
||||
Vec3 aim_lc;
|
||||
checkPosition(orig_pos, NULL, &ball_lc, true/*use_front_xyz*/);
|
||||
checkPosition(ball_aim_pos, NULL, &aim_lc, true/*use_front_xyz*/);
|
||||
Vec3 ball_lc = m_front_transform.inverse()(orig_pos);
|
||||
Vec3 aim_lc = m_front_transform.inverse()(ball_aim_pos);
|
||||
|
||||
// Too far from the ball,
|
||||
// use path finding from arena ai to get close
|
||||
@@ -236,7 +234,7 @@ Vec3 SoccerAI::determineBallAimingPosition()
|
||||
return ball_aim_pos;
|
||||
}
|
||||
else
|
||||
return m_kart->getTrans()(Vec3(overtake_lc));
|
||||
return m_front_transform(overtake_lc);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,10 +454,24 @@ float SoccerAI::rotateSlope(float old_slope, bool rotate_up)
|
||||
//-----------------------------------------------------------------------------
|
||||
int SoccerAI::getCurrentNode() const
|
||||
{
|
||||
return m_world->getKartNode(m_kart->getWorldKartId());
|
||||
return m_world->getSectorForKart(m_kart);
|
||||
} // getCurrentNode
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SoccerAI::isWaiting() const
|
||||
{
|
||||
return m_world->isStartPhase();
|
||||
} // isWaiting
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float SoccerAI::getKartDistance(const AbstractKart* kart) const
|
||||
{
|
||||
return m_graph->getDistance(getCurrentNode(),
|
||||
m_world->getSectorForKart(kart));
|
||||
} // getKartDistance
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SoccerAI::isKartOnRoad() const
|
||||
{
|
||||
return m_world->isOnRoad(m_kart->getWorldKartId());
|
||||
} // isKartOnRoad
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
|
||||
#include "karts/controller/arena_ai.hpp"
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
#undef BALL_AIM_DEBUG
|
||||
#ifdef BALL_AIM_DEBUG
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#endif
|
||||
|
||||
class SoccerWorld;
|
||||
class Vec3;
|
||||
|
||||
/** The actual soccer AI.
|
||||
* \ingroup controller
|
||||
@@ -54,27 +55,31 @@ private:
|
||||
bool m_force_brake;
|
||||
bool m_chasing_ball;
|
||||
|
||||
Vec3 determineBallAimingPosition();
|
||||
bool isOvertakable(const Vec3& ball_lc);
|
||||
bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc,
|
||||
Vec3* overtake_lc);
|
||||
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 void findClosestKart(bool use_difficulty);
|
||||
virtual void findTarget();
|
||||
virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; }
|
||||
virtual int getCurrentNode() const;
|
||||
virtual bool isWaiting() const;
|
||||
virtual bool canSkid(float steer_fraction) { return false; }
|
||||
virtual bool forceBraking() OVERRIDE
|
||||
{ return m_avoiding_banana || m_force_brake; }
|
||||
virtual bool ignorePathFinding() OVERRIDE
|
||||
virtual bool canSkid(float steer_fraction) OVERRIDE
|
||||
{ return m_mini_skid && !(m_overtake_ball || m_chasing_ball); }
|
||||
virtual void findClosestKart(bool use_difficulty) OVERRIDE;
|
||||
virtual void findTarget() OVERRIDE;
|
||||
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; }
|
||||
virtual bool isKartOnRoad() const OVERRIDE;
|
||||
virtual bool isWaiting() const OVERRIDE;
|
||||
virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; }
|
||||
public:
|
||||
SoccerAI(AbstractKart *kart);
|
||||
~SoccerAI();
|
||||
virtual void update (float delta);
|
||||
virtual void reset ();
|
||||
virtual void update (float delta) OVERRIDE;
|
||||
virtual void reset () OVERRIDE;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
@@ -177,22 +177,23 @@ void SkiddingAI::reset()
|
||||
m_distance_behind = 0.0f;
|
||||
m_current_curve_radius = 0.0f;
|
||||
m_curve_center = Vec3(0,0,0);
|
||||
m_current_track_direction = GraphNode::DIR_STRAIGHT;
|
||||
m_current_track_direction = DriveNode::DIR_STRAIGHT;
|
||||
m_item_to_collect = NULL;
|
||||
m_last_direction_node = 0;
|
||||
m_avoid_item_close = false;
|
||||
m_skid_probability_state = SKID_PROBAB_NOT_YET;
|
||||
m_last_item_random = NULL;
|
||||
|
||||
AIBaseLapController::reset();
|
||||
m_track_node = QuadGraph::UNKNOWN_SECTOR;
|
||||
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
|
||||
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
|
||||
m_track_node = Graph::UNKNOWN_SECTOR;
|
||||
DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
|
||||
if(m_track_node==Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::error(getControllerName().c_str(),
|
||||
"Invalid starting position for '%s' - not on track"
|
||||
" - can be ignored.",
|
||||
m_kart->getIdent().c_str());
|
||||
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
}
|
||||
|
||||
AIBaseLapController::reset();
|
||||
@@ -420,7 +421,7 @@ void SkiddingAI::handleBraking()
|
||||
|
||||
// If the kart is not facing roughly in the direction of the track, brake
|
||||
// so that it is easier for the kart to turn in the right direction.
|
||||
if(m_current_track_direction==GraphNode::DIR_UNDEFINED &&
|
||||
if(m_current_track_direction==DriveNode::DIR_UNDEFINED &&
|
||||
m_kart->getSpeed() > MIN_SPEED)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@@ -432,8 +433,8 @@ void SkiddingAI::handleBraking()
|
||||
m_controls->setBrake(true);
|
||||
return;
|
||||
}
|
||||
if(m_current_track_direction==GraphNode::DIR_LEFT ||
|
||||
m_current_track_direction==GraphNode::DIR_RIGHT )
|
||||
if(m_current_track_direction==DriveNode::DIR_LEFT ||
|
||||
m_current_track_direction==DriveNode::DIR_RIGHT )
|
||||
{
|
||||
float max_turn_speed =
|
||||
m_kart->getSpeedForTurnRadius(m_current_curve_radius);
|
||||
@@ -482,14 +483,14 @@ void SkiddingAI::handleSteering(float dt)
|
||||
m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() );
|
||||
|
||||
if( fabsf(side_dist) >
|
||||
0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f )
|
||||
0.5f* DriveGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f )
|
||||
{
|
||||
steer_angle = steerToPoint(QuadGraph::get()->getQuadOfNode(next)
|
||||
.getCenter());
|
||||
steer_angle = steerToPoint(DriveGraph::get()->getNode(next)
|
||||
->getCenter());
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
m_debug_sphere[0]->setPosition(QuadGraph::get()->getQuadOfNode(next)
|
||||
.getCenter().toIrrVector());
|
||||
m_debug_sphere[0]->setPosition(DriveGraph::get()->getNode(next)
|
||||
->getCenter().toIrrVector());
|
||||
Log::debug(getControllerName().c_str(),
|
||||
"Outside of road: steer to center point.");
|
||||
#endif
|
||||
@@ -536,7 +537,7 @@ void SkiddingAI::handleSteering(float dt)
|
||||
{
|
||||
m_start_kart_crash_direction = 0;
|
||||
Vec3 aim_point;
|
||||
int last_node = QuadGraph::UNKNOWN_SECTOR;
|
||||
int last_node = Graph::UNKNOWN_SECTOR;
|
||||
|
||||
switch(m_point_selection_algorithm)
|
||||
{
|
||||
@@ -625,12 +626,11 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
#ifdef AI_DEBUG
|
||||
m_item_sphere->setVisible(false);
|
||||
#endif
|
||||
// Angle of line from kart to aim_point
|
||||
float kart_aim_angle = atan2(aim_point->getX()-m_kart->getXYZ().getX(),
|
||||
aim_point->getZ()-m_kart->getXYZ().getZ());
|
||||
// Angle to aim_point
|
||||
Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ();
|
||||
|
||||
// Make sure we have a valid last_node
|
||||
if(last_node==QuadGraph::UNKNOWN_SECTOR)
|
||||
if(last_node==Graph::UNKNOWN_SECTOR)
|
||||
last_node = m_next_node_index[m_track_node];
|
||||
|
||||
int node = m_track_node;
|
||||
@@ -643,15 +643,15 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
const float max_item_lookahead_distance = 30.f;
|
||||
while(distance < max_item_lookahead_distance)
|
||||
{
|
||||
int q_index= QuadGraph::get()->getNode(node).getQuadIndex();
|
||||
int n_index= DriveGraph::get()->getNode(node)->getIndex();
|
||||
const std::vector<Item *> &items_ahead =
|
||||
ItemManager::get()->getItemsInQuads(q_index);
|
||||
ItemManager::get()->getItemsInQuads(n_index);
|
||||
for(unsigned int i=0; i<items_ahead.size(); i++)
|
||||
{
|
||||
evaluateItems(items_ahead[i], kart_aim_angle,
|
||||
evaluateItems(items_ahead[i], kart_aim_direction,
|
||||
&items_to_avoid, &items_to_collect);
|
||||
} // for i<items_ahead;
|
||||
distance += QuadGraph::get()->getDistanceToNext(node,
|
||||
distance += DriveGraph::get()->getDistanceToNext(node,
|
||||
m_successor_index[node]);
|
||||
node = m_next_node_index[node];
|
||||
// Stop when we have reached the last quad
|
||||
@@ -660,10 +660,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
|
||||
m_avoid_item_close = items_to_avoid.size()>0;
|
||||
|
||||
core::line2df line_to_target(aim_point->getX(),
|
||||
aim_point->getZ(),
|
||||
m_kart->getXYZ().getX(),
|
||||
m_kart->getXYZ().getZ());
|
||||
core::line3df line_to_target_3d((*aim_point).toIrrVector(),
|
||||
m_kart->getXYZ().toIrrVector());
|
||||
|
||||
// 2) If the kart is aiming for an item, but (suddenly) detects
|
||||
// some close-by items to avoid (e.g. behind the item, which was too
|
||||
@@ -676,7 +674,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
for(unsigned int i=0; i< items_to_avoid.size(); i++)
|
||||
{
|
||||
Vec3 d = items_to_avoid[i]->getXYZ()-m_item_to_collect->getXYZ();
|
||||
if( d.length2_2d()>m_ai_properties->m_bad_item_closeness_2)
|
||||
if( d.length2()>m_ai_properties->m_bad_item_closeness_2)
|
||||
continue;
|
||||
// It could make sense to also test if the bad item would
|
||||
// actually be hit, not only if it is close (which can result
|
||||
@@ -696,7 +694,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
// -------------------------------------
|
||||
if(m_item_to_collect)
|
||||
{
|
||||
if(handleSelectedItem(kart_aim_angle, aim_point))
|
||||
if(handleSelectedItem(kart_aim_direction, aim_point))
|
||||
{
|
||||
// Still aim at the previsouly selected item.
|
||||
*aim_point = m_item_to_collect->getXYZ();
|
||||
@@ -721,7 +719,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
{
|
||||
// If we need to steer to avoid an item, this takes priority,
|
||||
// ignore items to collect and return the new aim_point.
|
||||
if(steerToAvoid(items_to_avoid, line_to_target, aim_point))
|
||||
if(steerToAvoid(items_to_avoid, line_to_target_3d, aim_point))
|
||||
{
|
||||
#ifdef AI_DEBUG
|
||||
m_item_sphere->setVisible(true);
|
||||
@@ -770,7 +768,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
// it's on a good enough driveline, so make this item a permanent
|
||||
// target. Otherwise only try to get closer (till hopefully this
|
||||
// item s on our driveline)
|
||||
if(item_to_collect->hitLine(line_to_target, m_kart))
|
||||
if(item_to_collect->hitLine(line_to_target_3d, m_kart))
|
||||
{
|
||||
#ifdef AI_DEBUG
|
||||
m_item_sphere->setVisible(true);
|
||||
@@ -788,10 +786,10 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
{
|
||||
// Kart will not hit item, try to get closer to this item
|
||||
// so that it can potentially become a permanent target.
|
||||
Vec3 xyz = item_to_collect->getXYZ();
|
||||
float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(),
|
||||
xyz.getZ() - m_kart->getXYZ().getZ());
|
||||
float angle = normalizeAngle(kart_aim_angle - item_angle);
|
||||
const Vec3& xyz = item_to_collect->getXYZ();
|
||||
float angle_to_item = (xyz - m_kart->getXYZ())
|
||||
.angle(kart_aim_direction);
|
||||
float angle = normalizeAngle(angle_to_item);
|
||||
|
||||
if(fabsf(angle) < 0.3)
|
||||
{
|
||||
@@ -832,8 +830,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
bool SkiddingAI::hitBadItemWhenAimAt(const Item *item,
|
||||
const std::vector<const Item *> &items_to_avoid)
|
||||
{
|
||||
core::line2df to_item(m_kart->getXYZ().getX(), m_kart->getXYZ().getZ(),
|
||||
item->getXYZ().getX(), item->getXYZ().getZ() );
|
||||
core::line3df to_item(m_kart->getXYZ().toIrrVector(),
|
||||
item->getXYZ().toIrrVector());
|
||||
for(unsigned int i=0; i<items_to_avoid.size(); i++)
|
||||
{
|
||||
if(items_to_avoid[i]->hitLine(to_item, m_kart))
|
||||
@@ -854,7 +852,7 @@ bool SkiddingAI::hitBadItemWhenAimAt(const Item *item,
|
||||
* \param last_node
|
||||
* \return True if th AI should still aim for the pre-selected item.
|
||||
*/
|
||||
bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
|
||||
bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point)
|
||||
{
|
||||
// If the item is unavailable keep on testing. It is not necessary
|
||||
// to test if an item has turned bad, this was tested before this
|
||||
@@ -863,10 +861,9 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
|
||||
return false;
|
||||
|
||||
const Vec3 &xyz = m_item_to_collect->getXYZ();
|
||||
float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(),
|
||||
xyz.getZ() - m_kart->getXYZ().getZ());
|
||||
float angle_to_item = (xyz - m_kart->getXYZ()).angle(kart_aim_direction);
|
||||
float angle = normalizeAngle(angle_to_item);
|
||||
|
||||
float angle = normalizeAngle(kart_aim_angle - item_angle);
|
||||
if(fabsf(angle)>1.5)
|
||||
{
|
||||
// We (likely) have passed the item we were aiming for
|
||||
@@ -891,7 +888,7 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point)
|
||||
* \return True if steering is necessary to avoid an item.
|
||||
*/
|
||||
bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
const core::line2df &line_to_target,
|
||||
const core::line3df &line_to_target,
|
||||
Vec3 *aim_point)
|
||||
{
|
||||
// First determine the left-most and right-most item.
|
||||
@@ -917,14 +914,19 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
|
||||
// Check if we would drive left of the leftmost or right of the
|
||||
// rightmost point - if so, nothing to do.
|
||||
core::vector2df left(items_to_avoid[index_left_most]->getXYZ().getX(),
|
||||
items_to_avoid[index_left_most]->getXYZ().getZ());
|
||||
const Vec3& left = items_to_avoid[index_left_most]->getXYZ();
|
||||
int node_index = items_to_avoid[index_left_most]->getGraphNode();
|
||||
const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal();
|
||||
Vec3 p1 = line_to_target.start,
|
||||
p2 = line_to_target.getMiddle() + normal.toIrrVector(),
|
||||
p3 = line_to_target.end;
|
||||
|
||||
int item_index = -1;
|
||||
bool is_left = false;
|
||||
|
||||
// >=0 means the point is to the right of the line, or the line is
|
||||
// to the left of the point.
|
||||
if(line_to_target.getPointOrientation(left)>=0)
|
||||
if(left.sideofPlane(p1,p2,p3) <= 0)
|
||||
{
|
||||
// Left of leftmost point
|
||||
item_index = index_left_most;
|
||||
@@ -932,9 +934,14 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
}
|
||||
else
|
||||
{
|
||||
core::vector2df right(items_to_avoid[index_right_most]->getXYZ().getX(),
|
||||
items_to_avoid[index_right_most]->getXYZ().getZ());
|
||||
if(line_to_target.getPointOrientation(right)<=0)
|
||||
const Vec3& right = items_to_avoid[index_right_most]->getXYZ();
|
||||
int node_index = items_to_avoid[index_right_most]->getGraphNode();
|
||||
const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal();
|
||||
Vec3 p1 = line_to_target.start,
|
||||
p2 = line_to_target.getMiddle() + normal.toIrrVector(),
|
||||
p3 = line_to_target.end;
|
||||
|
||||
if (right.sideofPlane(p1, p2, p3) >= 0)
|
||||
{
|
||||
// Right of rightmost point
|
||||
item_index = index_right_most;
|
||||
@@ -975,20 +982,19 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
|
||||
float min_distance[2] = {99999.9f, 99999.9f};
|
||||
int index[2] = {-1, -1};
|
||||
core::vector2df closest2d[2];
|
||||
core::vector3df closest3d[2];
|
||||
for(unsigned int i=0; i<items_to_avoid.size(); i++)
|
||||
{
|
||||
const Vec3 &xyz = items_to_avoid[i]->getXYZ();
|
||||
core::vector2df item2d = xyz.toIrrVector2d();
|
||||
core::vector2df point2d = line_to_target.getClosestPoint(item2d);
|
||||
float d = (xyz.toIrrVector2d() - point2d).getLengthSQ();
|
||||
float direction = line_to_target.getPointOrientation(item2d);
|
||||
int ind = direction<0 ? 0 : 1;
|
||||
core::vector3df point3d = line_to_target.getClosestPoint(xyz.toIrrVector());
|
||||
float d = (xyz.toIrrVector() - point3d).getLengthSQ();
|
||||
float direction = xyz.sideofPlane(p1,p2,p3);
|
||||
int ind = direction<0 ? 1 : 0;
|
||||
if(d<min_distance[ind])
|
||||
{
|
||||
min_distance[ind] = d;
|
||||
index[ind] = i;
|
||||
closest2d[ind] = point2d;
|
||||
closest3d[ind] = point3d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,8 +1004,8 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
|
||||
// We are driving between item_to_avoid[index[0]] and ...[1].
|
||||
// If we don't hit any of them, just keep on driving as normal
|
||||
bool hit_left = items_to_avoid[index[0]]->hitKart(closest2d[0], m_kart);
|
||||
bool hit_right = items_to_avoid[index[1]]->hitKart(closest2d[1], m_kart);
|
||||
bool hit_left = items_to_avoid[index[0]]->hitKart(closest3d[0], m_kart);
|
||||
bool hit_right = items_to_avoid[index[1]]->hitKart(closest3d[1], m_kart);
|
||||
if( !hit_left && !hit_right)
|
||||
return false;
|
||||
|
||||
@@ -1033,7 +1039,7 @@ bool SkiddingAI::steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
* (NULL if no item was avoided so far).
|
||||
* \param item_to_collect A pointer to a previously selected item to collect.
|
||||
*/
|
||||
void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
|
||||
void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction,
|
||||
std::vector<const Item *> *items_to_avoid,
|
||||
std::vector<const Item *> *items_to_collect)
|
||||
{
|
||||
@@ -1081,13 +1087,10 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
|
||||
// to avoid are collected).
|
||||
if(!avoid)
|
||||
{
|
||||
// item_angle The angle of the item (relative to the forward axis,
|
||||
// so 0 means straight ahead in world coordinates!).
|
||||
const Vec3 &xyz = item->getXYZ();
|
||||
float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(),
|
||||
xyz.getZ() - m_kart->getXYZ().getZ());
|
||||
|
||||
float diff = normalizeAngle(kart_aim_angle-item_angle);
|
||||
float angle_to_item =
|
||||
(xyz - m_kart->getXYZ()).angle(kart_aim_direction);
|
||||
float diff = normalizeAngle(angle_to_item);
|
||||
|
||||
// The kart is driving at high speed, when the current max speed
|
||||
// is higher than the max speed of the kart (which is caused by
|
||||
@@ -1116,14 +1119,14 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
|
||||
else
|
||||
list = items_to_collect;
|
||||
|
||||
float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2_2d();
|
||||
float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2();
|
||||
|
||||
// This list is usually very short, so use a simple bubble sort
|
||||
list->push_back(item);
|
||||
int i;
|
||||
for(i=(int)list->size()-2; i>=0; i--)
|
||||
{
|
||||
float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2_2d();
|
||||
float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2();
|
||||
if(d<=new_distance)
|
||||
{
|
||||
break;
|
||||
@@ -1305,15 +1308,19 @@ void SkiddingAI::handleItems(const float dt)
|
||||
bool straight_ahead = false;
|
||||
if (m_kart_behind)
|
||||
{
|
||||
posData behind_pos = {0};
|
||||
checkPosition(m_kart_behind->getXYZ(), &behind_pos);
|
||||
if (behind_pos.angle < 0.2f) straight_behind = true;
|
||||
Vec3 behind_lc = m_kart->getTrans().inverse()
|
||||
(m_kart_behind->getXYZ());
|
||||
const float abs_angle =
|
||||
atan2f(fabsf(behind_lc.x()), fabsf(behind_lc.z()));
|
||||
if (abs_angle < 0.2f) straight_behind = true;
|
||||
}
|
||||
if (m_kart_ahead)
|
||||
{
|
||||
posData ahead_pos = {0};
|
||||
checkPosition(m_kart_ahead->getXYZ(), &ahead_pos);
|
||||
if (ahead_pos.angle < 0.2f) straight_ahead = true;
|
||||
Vec3 ahead_lc = m_kart->getTrans().inverse()
|
||||
(m_kart_ahead->getXYZ());
|
||||
const float abs_angle =
|
||||
atan2f(fabsf(ahead_lc.x()), fabsf(ahead_lc.z()));
|
||||
if (abs_angle < 0.2f) straight_ahead = true;
|
||||
}
|
||||
|
||||
// Bowling balls are slower, so only fire on closer karts - but when
|
||||
@@ -1728,14 +1735,14 @@ void SkiddingAI::handleNitroAndZipper()
|
||||
m_kart->getSpeed()>1.0f &&
|
||||
m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0)
|
||||
{
|
||||
GraphNode::DirectionType dir;
|
||||
DriveNode::DirectionType dir;
|
||||
unsigned int last;
|
||||
const GraphNode &gn = QuadGraph::get()->getNode(m_track_node);
|
||||
gn.getDirectionData(m_successor_index[m_track_node], &dir, &last);
|
||||
if(dir==GraphNode::DIR_STRAIGHT)
|
||||
const DriveNode* dn = DriveGraph::get()->getNode(m_track_node);
|
||||
dn->getDirectionData(m_successor_index[m_track_node], &dir, &last);
|
||||
if(dir==DriveNode::DIR_STRAIGHT)
|
||||
{
|
||||
float diff = QuadGraph::get()->getDistanceFromStart(last)
|
||||
- QuadGraph::get()->getDistanceFromStart(m_track_node);
|
||||
float diff = DriveGraph::get()->getDistanceFromStart(last)
|
||||
- DriveGraph::get()->getDistanceFromStart(m_track_node);
|
||||
if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength();
|
||||
if(diff>m_ai_properties->m_straight_length_for_zipper)
|
||||
m_controls->setFire(true);
|
||||
@@ -1779,16 +1786,14 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
|
||||
|
||||
const size_t NUM_KARTS = m_world->getNumKarts();
|
||||
|
||||
//Protection against having vel_normal with nan values
|
||||
const Vec3 &VEL = m_kart->getVelocity();
|
||||
Vec3 vel_normal(VEL.getX(), 0.0, VEL.getZ());
|
||||
float speed=vel_normal.length();
|
||||
float speed = m_kart->getVelocity().length();
|
||||
// If the velocity is zero, no sense in checking for crashes in time
|
||||
if(speed==0) return;
|
||||
|
||||
Vec3 vel_normal = m_kart->getVelocity().normalized();
|
||||
|
||||
// Time it takes to drive for m_kart_length units.
|
||||
float dt = m_kart_length / speed;
|
||||
vel_normal/=speed;
|
||||
|
||||
int current_node = m_track_node;
|
||||
if(steps<1 || steps>1000)
|
||||
@@ -1818,7 +1823,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
|
||||
continue;
|
||||
Vec3 other_kart_xyz = other_kart->getXYZ()
|
||||
+ other_kart->getVelocity()*(i*dt);
|
||||
float kart_distance = (step_coord - other_kart_xyz).length_2d();
|
||||
float kart_distance = (step_coord - other_kart_xyz).length();
|
||||
|
||||
if( kart_distance < m_kart_length)
|
||||
m_crashes.m_kart = j;
|
||||
@@ -1826,12 +1831,12 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
|
||||
}
|
||||
|
||||
/*Find if we crash with the drivelines*/
|
||||
if(current_node!=QuadGraph::UNKNOWN_SECTOR &&
|
||||
if(current_node!=Graph::UNKNOWN_SECTOR &&
|
||||
m_next_node_index[current_node]!=-1)
|
||||
QuadGraph::get()->findRoadSector(step_coord, ¤t_node,
|
||||
DriveGraph::get()->findRoadSector(step_coord, ¤t_node,
|
||||
/* sectors to test*/ &m_all_look_aheads[current_node]);
|
||||
|
||||
if( current_node == QuadGraph::UNKNOWN_SECTOR)
|
||||
if( current_node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
m_crashes.m_road = true;
|
||||
return;
|
||||
@@ -1879,23 +1884,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
*last_node = m_next_node_index[m_track_node];
|
||||
const core::vector2df xz = m_kart->getXYZ().toIrrVector2d();
|
||||
|
||||
const Quad &q = QuadGraph::get()->getQuadOfNode(*last_node);
|
||||
const DriveNode* dn = DriveGraph::get()->getNode(*last_node);
|
||||
|
||||
// Index of the left and right end of a quad.
|
||||
const unsigned int LEFT_END_POINT = 0;
|
||||
const unsigned int RIGHT_END_POINT = 1;
|
||||
core::line2df left (xz, q[LEFT_END_POINT ].toIrrVector2d());
|
||||
core::line2df right(xz, q[RIGHT_END_POINT].toIrrVector2d());
|
||||
core::line2df left (xz, (*dn)[LEFT_END_POINT ].toIrrVector2d());
|
||||
core::line2df right(xz, (*dn)[RIGHT_END_POINT].toIrrVector2d());
|
||||
|
||||
#if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
|
||||
const Vec3 eps1(0,0.5f,0);
|
||||
m_curve[CURVE_LEFT]->clear();
|
||||
m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
m_curve[CURVE_LEFT]->addPoint(q[LEFT_END_POINT]+eps1);
|
||||
m_curve[CURVE_LEFT]->addPoint((*dn)[LEFT_END_POINT]+eps1);
|
||||
m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
m_curve[CURVE_RIGHT]->clear();
|
||||
m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
m_curve[CURVE_RIGHT]->addPoint(q[RIGHT_END_POINT]+eps1);
|
||||
m_curve[CURVE_RIGHT]->addPoint((*dn)[RIGHT_END_POINT]+eps1);
|
||||
m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1);
|
||||
#endif
|
||||
#if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
|
||||
@@ -1908,13 +1913,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
while(1)
|
||||
{
|
||||
unsigned int next_sector = m_next_node_index[*last_node];
|
||||
const Quad &q_next = QuadGraph::get()->getQuadOfNode(next_sector);
|
||||
const DriveNode* dn_next = DriveGraph::get()->getNode(next_sector);
|
||||
// Test if the next left point is to the right of the left
|
||||
// line. If so, a new left line is defined.
|
||||
if(left.getPointOrientation(q_next[LEFT_END_POINT].toIrrVector2d())
|
||||
if(left.getPointOrientation((*dn_next)[LEFT_END_POINT].toIrrVector2d())
|
||||
< 0 )
|
||||
{
|
||||
core::vector2df p = q_next[LEFT_END_POINT].toIrrVector2d();
|
||||
core::vector2df p = (*dn_next)[LEFT_END_POINT].toIrrVector2d();
|
||||
// Stop if the new point is to the right of the right line
|
||||
if(right.getPointOrientation(p)<0)
|
||||
break;
|
||||
@@ -1930,10 +1935,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
|
||||
// Test if new right point is to the left of the right line. If
|
||||
// so, a new right line is defined.
|
||||
if(right.getPointOrientation(q_next[RIGHT_END_POINT].toIrrVector2d())
|
||||
if(right.getPointOrientation((*dn_next)[RIGHT_END_POINT].toIrrVector2d())
|
||||
> 0 )
|
||||
{
|
||||
core::vector2df p = q_next[RIGHT_END_POINT].toIrrVector2d();
|
||||
core::vector2df p = (*dn_next)[RIGHT_END_POINT].toIrrVector2d();
|
||||
// Break if new point is to the left of left line
|
||||
if(left.getPointOrientation(p)>0)
|
||||
break;
|
||||
@@ -1955,7 +1960,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
|
||||
// 0.5f*(left.end.Y+right.end.Y));
|
||||
//*result = ppp;
|
||||
|
||||
*result = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
|
||||
*result = DriveGraph::get()->getNode(*last_node)->getCenter();
|
||||
} // findNonCrashingPointNew
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1992,10 +1997,10 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
target_sector = m_next_node_index[*last_node];
|
||||
|
||||
//direction is a vector from our kart to the sectors we are testing
|
||||
direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter()
|
||||
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
|
||||
- m_kart->getXYZ();
|
||||
|
||||
float len=direction.length_2d();
|
||||
float len=direction.length();
|
||||
unsigned int steps = (unsigned int)( len / m_kart_length );
|
||||
if( steps < 3 ) steps = 3;
|
||||
|
||||
@@ -2015,23 +2020,23 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
{
|
||||
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
|
||||
|
||||
QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
*last_node );
|
||||
|
||||
float distance = fabsf(step_track_coord[0]);
|
||||
|
||||
//If we are outside, the previous node is what we are looking for
|
||||
if ( distance + m_kart_width * 0.5f
|
||||
> QuadGraph::get()->getNode(*last_node).getPathWidth()*0.5f )
|
||||
> DriveGraph::get()->getNode(*last_node)->getPathWidth()*0.5f )
|
||||
{
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node)
|
||||
.getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)
|
||||
->getCenter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
*last_node = target_sector;
|
||||
} // for i<100
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)->getCenter();
|
||||
} // findNonCrashingPointFixed
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -2039,14 +2044,14 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
* 1. the test:
|
||||
*
|
||||
* distance + m_kart_width * 0.5f
|
||||
* > QuadGraph::get()->getNode(*last_node).getPathWidth() )
|
||||
* > DriveGraph::get()->getNode(*last_node)->getPathWidth() )
|
||||
*
|
||||
* is incorrect, it should compare with getPathWith*0.5f (since distance
|
||||
* is the distance from the center, i.e. it is half the path width if
|
||||
* the point is at the edge).
|
||||
* 2. the test:
|
||||
*
|
||||
* QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
* DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
* *last_node );
|
||||
* in the for loop tests always against distance from the same
|
||||
* graph node (*last_node), while de-fact the loop will test points
|
||||
@@ -2071,7 +2076,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
|
||||
#endif
|
||||
*last_node = m_next_node_index[m_track_node];
|
||||
float angle = QuadGraph::get()->getAngleToNext(m_track_node,
|
||||
float angle = DriveGraph::get()->getAngleToNext(m_track_node,
|
||||
m_successor_index[m_track_node]);
|
||||
int target_sector;
|
||||
|
||||
@@ -2087,7 +2092,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
// target_sector is the sector at the longest distance that we can
|
||||
// drive to without crashing with the track.
|
||||
target_sector = m_next_node_index[*last_node];
|
||||
angle1 = QuadGraph::get()->getAngleToNext(target_sector,
|
||||
angle1 = DriveGraph::get()->getAngleToNext(target_sector,
|
||||
m_successor_index[target_sector]);
|
||||
// In very sharp turns this algorithm tends to aim at off track points,
|
||||
// resulting in hitting a corner. So test for this special case and
|
||||
@@ -2095,16 +2100,16 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
float diff = normalizeAngle(angle1-angle);
|
||||
if(fabsf(diff)>1.5f)
|
||||
{
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(target_sector)
|
||||
.getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(target_sector)
|
||||
->getCenter();
|
||||
return;
|
||||
}
|
||||
|
||||
//direction is a vector from our kart to the sectors we are testing
|
||||
direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter()
|
||||
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
|
||||
- m_kart->getXYZ();
|
||||
|
||||
float len=direction.length_2d();
|
||||
float len=direction.length();
|
||||
unsigned int steps = (unsigned int)( len / m_kart_length );
|
||||
if( steps < 3 ) steps = 3;
|
||||
|
||||
@@ -2124,24 +2129,24 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
{
|
||||
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
|
||||
|
||||
QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
|
||||
*last_node );
|
||||
|
||||
float distance = fabsf(step_track_coord[0]);
|
||||
|
||||
//If we are outside, the previous node is what we are looking for
|
||||
if ( distance + m_kart_width * 0.5f
|
||||
> QuadGraph::get()->getNode(*last_node).getPathWidth() )
|
||||
> DriveGraph::get()->getNode(*last_node)->getPathWidth() )
|
||||
{
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node)
|
||||
.getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)
|
||||
->getCenter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
angle = angle1;
|
||||
*last_node = target_sector;
|
||||
} // for i<100
|
||||
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
|
||||
*aim_position = DriveGraph::get()->getNode(*last_node)->getCenter();
|
||||
} // findNonCrashingPoint
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -2150,10 +2155,17 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
|
||||
*/
|
||||
void SkiddingAI::determineTrackDirection()
|
||||
{
|
||||
const QuadGraph *qg = QuadGraph::get();
|
||||
unsigned int succ = m_successor_index[m_track_node];
|
||||
float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ)
|
||||
- m_kart->getHeading();
|
||||
const DriveGraph *dg = DriveGraph::get();
|
||||
unsigned int succ = m_successor_index[m_track_node];
|
||||
unsigned int next = dg->getNode(m_track_node)->getSuccessor(succ);
|
||||
float angle_to_track = 0.0f;
|
||||
if (m_kart->getVelocity().length() > 0.0f)
|
||||
{
|
||||
Vec3 track_direction = -dg->getNode(m_track_node)->getCenter()
|
||||
+ dg->getNode(next)->getCenter();
|
||||
angle_to_track =
|
||||
track_direction.angle(m_kart->getVelocity().normalized());
|
||||
}
|
||||
angle_to_track = normalizeAngle(angle_to_track);
|
||||
|
||||
// In certain circumstances (esp. S curves) it is possible that the
|
||||
@@ -2169,26 +2181,24 @@ void SkiddingAI::determineTrackDirection()
|
||||
// quicker be aligned with the track again).
|
||||
if(fabsf(angle_to_track) > 0.22222f * M_PI)
|
||||
{
|
||||
m_current_track_direction = GraphNode::DIR_UNDEFINED;
|
||||
m_current_track_direction = DriveNode::DIR_UNDEFINED;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int next = qg->getNode(m_track_node).getSuccessor(succ);
|
||||
|
||||
qg->getNode(next).getDirectionData(m_successor_index[next],
|
||||
&m_current_track_direction,
|
||||
&m_last_direction_node);
|
||||
dg->getNode(next)->getDirectionData(m_successor_index[next],
|
||||
&m_current_track_direction,
|
||||
&m_last_direction_node);
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
m_curve[CURVE_QG]->clear();
|
||||
for(unsigned int i=m_track_node; i<=m_last_direction_node; i++)
|
||||
{
|
||||
m_curve[CURVE_QG]->addPoint(qg->getNode(i).getCenter());
|
||||
m_curve[CURVE_QG]->addPoint(dg->getNode(i)->getCenter());
|
||||
}
|
||||
#endif
|
||||
|
||||
if(m_current_track_direction==GraphNode::DIR_LEFT ||
|
||||
m_current_track_direction==GraphNode::DIR_RIGHT )
|
||||
if(m_current_track_direction==DriveNode::DIR_LEFT ||
|
||||
m_current_track_direction==DriveNode::DIR_RIGHT )
|
||||
{
|
||||
handleCurve();
|
||||
} // if(m_current_track_direction == DIR_LEFT || DIR_RIGHT )
|
||||
@@ -2213,13 +2223,10 @@ void SkiddingAI::handleCurve()
|
||||
// kart will already point towards the direction of the circle), and
|
||||
// the case that the kart is facing wrong was already tested for before
|
||||
|
||||
const QuadGraph *qg = QuadGraph::get();
|
||||
Vec3 xyz = m_kart->getXYZ();
|
||||
Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz;
|
||||
Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter();
|
||||
const DriveGraph *dg = DriveGraph::get();
|
||||
const Vec3& last_xyz = dg->getNode(m_last_direction_node)->getCenter();
|
||||
|
||||
determineTurnRadius(xyz, tangent, last_xyz,
|
||||
&m_curve_center, &m_current_curve_radius);
|
||||
determineTurnRadius(last_xyz, &m_curve_center, &m_current_curve_radius);
|
||||
assert(!std::isnan(m_curve_center.getX()));
|
||||
assert(!std::isnan(m_curve_center.getY()));
|
||||
assert(!std::isnan(m_curve_center.getZ()));
|
||||
@@ -2233,13 +2240,14 @@ void SkiddingAI::handleCurve()
|
||||
{
|
||||
i = m_next_node_index[i];
|
||||
// Pick either the lower left or right point:
|
||||
int index = m_current_track_direction==GraphNode::DIR_LEFT
|
||||
int index = m_current_track_direction==DriveNode::DIR_LEFT
|
||||
? 0 : 1;
|
||||
float r = (m_curve_center - qg->getQuadOfNode(i)[index]).length();
|
||||
Vec3 curve_center_wc = m_kart->getTrans()(m_curve_center);
|
||||
float r = (curve_center_wc - *(dg->getNode(i))[index]).length();
|
||||
if(m_current_curve_radius < r)
|
||||
{
|
||||
last_xyz = qg->getQuadOfNode(i)[index];
|
||||
determineTurnRadius(xyz, tangent, last_xyz,
|
||||
last_xyz = *(dg->getNode(i)[index]);
|
||||
determineTurnRadius(last_xyz,
|
||||
&m_curve_center, &m_current_curve_radius);
|
||||
m_last_direction_node = i;
|
||||
break;
|
||||
@@ -2249,11 +2257,11 @@ void SkiddingAI::handleCurve()
|
||||
}
|
||||
#endif
|
||||
#if defined(AI_DEBUG) && defined(AI_DEBUG_CIRCLES)
|
||||
m_curve[CURVE_PREDICT1]->makeCircle(m_curve_center,
|
||||
m_curve[CURVE_PREDICT1]->makeCircle(m_kart->getTrans()(m_curve_center),
|
||||
m_current_curve_radius);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(last_xyz);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(m_curve_center);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(xyz);
|
||||
m_curve[CURVE_PREDICT1]->addPoint(m_kart->getTrans()(m_curve_center));
|
||||
m_curve[CURVE_PREDICT1]->addPoint(m_kart->getXYZ());
|
||||
#endif
|
||||
|
||||
} // handleCurve
|
||||
@@ -2292,8 +2300,8 @@ bool SkiddingAI::canSkid(float steer_fraction)
|
||||
}
|
||||
|
||||
// No skidding on straights
|
||||
if(m_current_track_direction==GraphNode::DIR_STRAIGHT ||
|
||||
m_current_track_direction==GraphNode::DIR_UNDEFINED )
|
||||
if(m_current_track_direction==DriveNode::DIR_STRAIGHT ||
|
||||
m_current_track_direction==DriveNode::DIR_UNDEFINED )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(m_controls->getSkidControl() && m_ai_debug)
|
||||
@@ -2307,18 +2315,19 @@ bool SkiddingAI::canSkid(float steer_fraction)
|
||||
}
|
||||
|
||||
const float MIN_SKID_SPEED = 5.0f;
|
||||
const QuadGraph *qg = QuadGraph::get();
|
||||
Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter();
|
||||
const DriveGraph *dg = DriveGraph::get();
|
||||
Vec3 last_xyz = m_kart->getTrans().inverse()
|
||||
(dg->getNode(m_last_direction_node)->getCenter());
|
||||
|
||||
// Only try skidding when a certain minimum speed is reached.
|
||||
if(m_kart->getSpeed()<MIN_SKID_SPEED) return false;
|
||||
|
||||
// Estimate how long it takes to finish the curve
|
||||
Vec3 diff_kart = m_kart->getXYZ() - m_curve_center;
|
||||
Vec3 diff_last = last_xyz - m_curve_center;
|
||||
Vec3 diff_kart = -m_curve_center;
|
||||
Vec3 diff_last = last_xyz - m_curve_center;
|
||||
float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ());
|
||||
float angle_last = atan2(diff_last.getX(), diff_last.getZ());
|
||||
float angle = m_current_track_direction == GraphNode::DIR_RIGHT
|
||||
float angle = m_current_track_direction == DriveNode::DIR_RIGHT
|
||||
? angle_last - angle_kart
|
||||
: angle_kart - angle_last;
|
||||
angle = normalizeAngle(angle);
|
||||
@@ -2345,9 +2354,9 @@ bool SkiddingAI::canSkid(float steer_fraction)
|
||||
// left turn steer right to avoid getting too close to the left
|
||||
// vorder). In this case skidding will be useless.
|
||||
else if( (steer_fraction > 0 &&
|
||||
m_current_track_direction==GraphNode::DIR_LEFT) ||
|
||||
m_current_track_direction==DriveNode::DIR_LEFT) ||
|
||||
(steer_fraction < 0 &&
|
||||
m_current_track_direction==GraphNode::DIR_RIGHT) )
|
||||
m_current_track_direction==DriveNode::DIR_RIGHT) )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(m_controls->getSkidControl() && m_ai_debug)
|
||||
@@ -2499,55 +2508,3 @@ void SkiddingAI::setSteering(float angle, float dt)
|
||||
|
||||
|
||||
} // setSteering
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determine the center point and radius of a circle given two points on
|
||||
* the ccircle and the tangent at the first point. This is done as follows:
|
||||
* 1. Determine the line going through the center point start+end, which is
|
||||
* orthogonal to the vector from start to end. This line goes through the
|
||||
* center of the circle.
|
||||
* 2. Determine the line going through the first point and is orthogonal
|
||||
* to the given tangent.
|
||||
* 3. The intersection of these two lines is the center of the circle.
|
||||
* \param[in] start First point.
|
||||
* \param[in] tangent Tangent at first point.
|
||||
* \param[in] end Second point on circle.
|
||||
* \param[out] center Center point of the circle.
|
||||
* \param[out] radius Radius of the circle.
|
||||
*/
|
||||
void SkiddingAI::determineTurnRadius(const Vec3 &start,
|
||||
const Vec3 &tangent,
|
||||
const Vec3 &end,
|
||||
Vec3 *center,
|
||||
float *radius)
|
||||
{
|
||||
// 1) Line through middle of start+end
|
||||
Vec3 mid = 0.5f*(start+end);
|
||||
Vec3 direction = end-start;
|
||||
|
||||
Vec3 orthogonal(direction.getZ(), 0, -direction.getX());
|
||||
Vec3 q1 = mid + orthogonal;
|
||||
irr::core::line2df line1(mid.getX(), mid.getZ(),
|
||||
q1.getX(), q1.getZ() );
|
||||
|
||||
Vec3 ortho_tangent(tangent.getZ(), 0, -tangent.getX());
|
||||
Vec3 q2 = start + ortho_tangent;
|
||||
irr::core::line2df line2(start.getX(), start.getZ(),
|
||||
q2.getX(), q2.getZ());
|
||||
|
||||
|
||||
irr::core::vector2df result;
|
||||
if(line1.intersectWith(line2, result, /*checkOnlySegments*/false))
|
||||
{
|
||||
*center = Vec3(result.X, start.getY(), result.Y);
|
||||
*radius = (start - *center).length();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No intersection. In this case assume that the two points are
|
||||
// on a semicircle, in which case the center is at 0.5*(start+end):
|
||||
*center = 0.5f*(start+end);
|
||||
*radius = 0.5f*(end-start).length();
|
||||
}
|
||||
return;
|
||||
} // determineTurnRadius
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
#include "karts/controller/ai_base_lap_controller.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/graph_node.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
@@ -111,7 +111,7 @@ private:
|
||||
int m_start_kart_crash_direction;
|
||||
|
||||
/** The direction of the track where the kart is on atm. */
|
||||
GraphNode::DirectionType m_current_track_direction;
|
||||
DriveNode::DirectionType m_current_track_direction;
|
||||
|
||||
/** The radius of the curve the kart is currently driving. Undefined
|
||||
* when being on a straigt section. */
|
||||
@@ -205,13 +205,13 @@ private:
|
||||
void computeNearestKarts();
|
||||
void handleItemCollectionAndAvoidance(Vec3 *aim_point,
|
||||
int last_node);
|
||||
bool handleSelectedItem(float kart_aim_angle, Vec3 *aim_point);
|
||||
bool handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point);
|
||||
bool steerToAvoid(const std::vector<const Item *> &items_to_avoid,
|
||||
const core::line2df &line_to_target,
|
||||
const core::line3df &line_to_target,
|
||||
Vec3 *aim_point);
|
||||
bool hitBadItemWhenAimAt(const Item *item,
|
||||
const std::vector<const Item *> &items_to_avoid);
|
||||
void evaluateItems(const Item *item, float kart_aim_angle,
|
||||
void evaluateItems(const Item *item, Vec3 kart_aim_direction,
|
||||
std::vector<const Item *> *items_to_avoid,
|
||||
std::vector<const Item *> *items_to_collect);
|
||||
|
||||
@@ -221,11 +221,6 @@ private:
|
||||
void findNonCrashingPoint(Vec3 *result, int *last_node);
|
||||
|
||||
void determineTrackDirection();
|
||||
void determineTurnRadius(const Vec3 &start,
|
||||
const Vec3 &start_direction,
|
||||
const Vec3 &end,
|
||||
Vec3 *center,
|
||||
float *radius);
|
||||
virtual bool canSkid(float steer_fraction);
|
||||
virtual void setSteering(float angle, float dt);
|
||||
void handleCurve();
|
||||
|
||||
@@ -76,7 +76,8 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
|
||||
: AbstractKartAnimation(kart, "ExplosionAnimation")
|
||||
{
|
||||
m_xyz = m_kart->getXYZ();
|
||||
m_orig_y = m_xyz.getY();
|
||||
m_orig_xyz = m_xyz;
|
||||
m_normal = m_kart->getNormal();
|
||||
m_kart->playCustomSFX(SFXManager::CUSTOM_EXPLODE);
|
||||
m_timer = m_kart->getKartProperties()->getExplosionDuration();
|
||||
|
||||
@@ -141,13 +142,12 @@ ExplosionAnimation::~ExplosionAnimation()
|
||||
void ExplosionAnimation::update(float dt)
|
||||
{
|
||||
m_velocity -= dt*World::getWorld()->getTrack()->getGravity();
|
||||
|
||||
m_xyz.setY(m_xyz.getY() + dt*m_velocity);
|
||||
m_xyz = m_xyz + dt*m_velocity*m_normal;
|
||||
|
||||
// Make sure the kart does not end up under the track
|
||||
if(m_xyz.getY()<m_orig_y)
|
||||
if ((m_xyz - m_orig_xyz).dot(m_normal)<0)
|
||||
{
|
||||
m_xyz.setY(m_orig_y);
|
||||
m_xyz = m_orig_xyz;
|
||||
// This will trigger the end of the animations
|
||||
m_timer = -1;
|
||||
}
|
||||
|
||||
@@ -37,13 +37,17 @@
|
||||
class ExplosionAnimation: public AbstractKartAnimation
|
||||
{
|
||||
protected:
|
||||
/** The coordinates where the kart was hit originally. */
|
||||
/** The coordinates where the kart was hit originally, it will be increased
|
||||
* later. */
|
||||
Vec3 m_xyz;
|
||||
|
||||
/** The original Y coordinate. The kart needs to be restored accurately
|
||||
/** The original coordinates. The kart needs to be restored accurately
|
||||
* otherwise due to floating point errors, time step size variations,
|
||||
* a kart can be restarted under the track. */
|
||||
float m_orig_y;
|
||||
Vec3 m_orig_xyz;
|
||||
|
||||
/** The normal of kart when it started to explode. */
|
||||
Vec3 m_normal;
|
||||
|
||||
/** The kart's current rotation. */
|
||||
Vec3 m_curr_rotation;
|
||||
|
||||
@@ -73,8 +73,11 @@
|
||||
#include "physics/physics.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "tracks/terrain_info.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "tracks/track_sector.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp" //TODO: remove after debugging is done
|
||||
#include "utils/vs.hpp"
|
||||
@@ -310,7 +313,10 @@ void Kart::reset()
|
||||
// mode) - but only if they actually have a body (e.g. ghost karts
|
||||
// don't have one).
|
||||
if(m_body)
|
||||
{
|
||||
World::getWorld()->getPhysics()->removeKart(this);
|
||||
World::getWorld()->getPhysics()->addKart(this);
|
||||
}
|
||||
|
||||
m_min_nitro_time = 0.0f;
|
||||
|
||||
@@ -404,7 +410,10 @@ void Kart::reset()
|
||||
Vec3 front(0, 0, getKartLength()*0.5f);
|
||||
m_xyz_front = getTrans()(front);
|
||||
|
||||
m_terrain_info->update(getTrans().getBasis());
|
||||
// Base on update() below, require if starting point of kart is not near
|
||||
// 0, 0, 0 (like in battle arena)
|
||||
m_terrain_info->update(getTrans().getBasis(),
|
||||
getTrans().getOrigin() + getTrans().getBasis() * Vec3(0, 0.3f, 0));
|
||||
|
||||
// Reset is also called when the kart is created, at which time
|
||||
// m_controller is not yet defined, so this has to be tested here.
|
||||
@@ -541,7 +550,7 @@ void Kart::blockViewWithPlunger()
|
||||
btTransform Kart::getAlignedTransform(const float custom_pitch)
|
||||
{
|
||||
btTransform trans = getTrans();
|
||||
|
||||
/*
|
||||
float pitch = (custom_pitch == -1 ? getTerrainPitch(getHeading())
|
||||
: custom_pitch);
|
||||
|
||||
@@ -549,7 +558,12 @@ btTransform Kart::getAlignedTransform(const float custom_pitch)
|
||||
m.setEulerZYX(pitch, getHeading()+m_skidding->getVisualSkidRotation(),
|
||||
0.0f);
|
||||
trans.setBasis(m);
|
||||
|
||||
*/
|
||||
btTransform trans2;
|
||||
trans2.setIdentity();
|
||||
trans2.setRotation(btQuaternion(m_skidding->getVisualSkidRotation(), 0, 0));
|
||||
trans *= trans2;
|
||||
|
||||
return trans;
|
||||
} // getAlignedTransform
|
||||
|
||||
@@ -695,10 +709,8 @@ void Kart::createPhysics()
|
||||
wheel.m_frictionSlip = m_kart_properties->getFrictionSlip();
|
||||
wheel.m_rollInfluence = m_kart_properties->getStabilityRollInfluence();
|
||||
}
|
||||
// Obviously these allocs have to be properly managed/freed
|
||||
btTransform t;
|
||||
t.setIdentity();
|
||||
World::getWorld()->getPhysics()->addKart(this);
|
||||
// Body to be added in reset() which allows complete reset kart when
|
||||
// restarting the race
|
||||
|
||||
} // createPhysics
|
||||
|
||||
@@ -1070,7 +1082,7 @@ bool Kart::isOnGround() const
|
||||
*/
|
||||
bool Kart::isNearGround() const
|
||||
{
|
||||
if(m_terrain_info->getHoT()==Track::NOHIT)
|
||||
if((m_terrain_info->getHitPoint() - getXYZ()).length() ==Track::NOHIT)
|
||||
return false;
|
||||
else
|
||||
return ((getXYZ().getY() - m_terrain_info->getHoT())
|
||||
@@ -1207,6 +1219,8 @@ void Kart::update(float dt)
|
||||
// Update the position and other data taken from the physics
|
||||
Moveable::update(dt);
|
||||
|
||||
Vec3 front(0, 0, getKartLength()*0.5f);
|
||||
m_xyz_front = getTrans()(front);
|
||||
// Update the locally maintained speed of the kart (m_speed), which
|
||||
// is used furthermore for engine power, camera distance etc
|
||||
updateSpeed();
|
||||
@@ -1284,7 +1298,10 @@ void Kart::update(float dt)
|
||||
m_kart_properties->getStabilityChassisAngularDamping());
|
||||
}
|
||||
|
||||
if(m_kart_animation)
|
||||
// Used to prevent creating a rescue animation after an explosion animation
|
||||
// got deleted
|
||||
const bool has_animation_before = m_kart_animation!= NULL;
|
||||
if(has_animation_before)
|
||||
m_kart_animation->update(dt);
|
||||
|
||||
m_attachment->update(dt);
|
||||
@@ -1325,13 +1342,31 @@ void Kart::update(float dt)
|
||||
// But only do this if auto-rescue is enabled (i.e. it will be disabled in
|
||||
// battle mode), and the material the kart is driving on does not have
|
||||
// gravity (which atm affects the roll angle).
|
||||
if(World::getWorld()->getTrack()->isAutoRescueEnabled() &&
|
||||
(!m_terrain_info->getMaterial() ||
|
||||
!m_terrain_info->getMaterial()->hasGravity()) &&
|
||||
!getKartAnimation() && fabs(getRoll())>60*DEGREE_TO_RAD &&
|
||||
fabs(getSpeed())<3.0f )
|
||||
|
||||
// To be used later
|
||||
float dist_to_sector = 0.0f;
|
||||
LinearWorld* lw = dynamic_cast<LinearWorld*>(World::getWorld());
|
||||
if (lw && DriveGraph::get())
|
||||
{
|
||||
new RescueAnimation(this, /*is_auto_rescue*/true);
|
||||
const int sector =
|
||||
lw->getTrackSector(getWorldKartId())->getCurrentGraphNode();
|
||||
dist_to_sector = getXYZ().distance
|
||||
(DriveGraph::get()->getNode(sector)->getCenter());
|
||||
|
||||
const Vec3& quad_normal = DriveGraph::get()->getNode(sector)
|
||||
->getNormal();
|
||||
const btQuaternion& q = getTrans().getRotation();
|
||||
const float roll = quad_normal.angle
|
||||
((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle())));
|
||||
|
||||
if (World::getWorld()->getTrack()->isAutoRescueEnabled() &&
|
||||
(!m_terrain_info->getMaterial() ||
|
||||
!m_terrain_info->getMaterial()->hasGravity()) &&
|
||||
!has_animation_before && fabs(roll) > 60 * DEGREE_TO_RAD &&
|
||||
fabs(getSpeed()) < 3.0f)
|
||||
{
|
||||
new RescueAnimation(this, /*is_auto_rescue*/true);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that the ray doesn't hit the kart. This is done by
|
||||
@@ -1344,12 +1379,9 @@ void Kart::update(float dt)
|
||||
m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0;
|
||||
}
|
||||
|
||||
Vec3 front(0, 0, getKartLength()*0.5f);
|
||||
m_xyz_front = getTrans()(front);
|
||||
|
||||
// After the physics step was done, the position of the wheels (as stored
|
||||
// in wheelInfo) is actually outdated, since the chassis was moved
|
||||
// according to the force acting from the wheels. So the cnter of the
|
||||
// according to the force acting from the wheels. So the center of the
|
||||
// chassis is not at the center of the wheels anymore, it is somewhat
|
||||
// moved forward (depending on speed and fps). In very extreme cases
|
||||
// (see bug 2246) the center of the chassis can actually be ahead of the
|
||||
@@ -1371,7 +1403,7 @@ void Kart::update(float dt)
|
||||
// partly tunnels through the track). While tunneling should not be
|
||||
// happening (since Z velocity is clamped), the epsilon is left in place
|
||||
// just to be on the safe side (it will not hit the chassis itself).
|
||||
from = from/4 + Vec3(0,0.3f,0);
|
||||
from = from/4 + (getTrans().getBasis() * Vec3(0,0.3f,0));
|
||||
|
||||
m_terrain_info->update(getTrans().getBasis(), from);
|
||||
|
||||
@@ -1395,7 +1427,8 @@ void Kart::update(float dt)
|
||||
// let kart fall a bit before rescuing
|
||||
const Vec3 *min, *max;
|
||||
World::getWorld()->getTrack()->getAABB(&min, &max);
|
||||
if(min->getY() - getXYZ().getY() > 17 && !m_flying &&
|
||||
|
||||
if((min->getY() - getXYZ().getY() > 17 || dist_to_sector > 25) && !m_flying &&
|
||||
!getKartAnimation())
|
||||
new RescueAnimation(this);
|
||||
}
|
||||
@@ -1995,13 +2028,13 @@ void Kart::crashed(const Material *m, const Vec3 &normal)
|
||||
World::getWorld()->getTrack()->isPushBackEnabled())
|
||||
{
|
||||
int sector = lw->getSectorForKart(this);
|
||||
if(sector!=QuadGraph::UNKNOWN_SECTOR)
|
||||
if(sector!=Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
// Use the first predecessor node, which is the most
|
||||
// natural one (i.e. the one on the main driveline).
|
||||
const GraphNode &gn = QuadGraph::get()->getNode(
|
||||
QuadGraph::get()->getNode(sector).getPredecessor(0));
|
||||
Vec3 impulse = gn.getCenter() - getXYZ();
|
||||
const DriveNode* dn = DriveGraph::get()->getNode(
|
||||
DriveGraph::get()->getNode(sector)->getPredecessor(0));
|
||||
Vec3 impulse = dn->getCenter() - getXYZ();
|
||||
impulse.setY(0);
|
||||
if(impulse.getX() || impulse.getZ())
|
||||
impulse.normalize();
|
||||
@@ -2881,4 +2914,13 @@ void Kart::setOnScreenText(const wchar_t *text)
|
||||
// when the parent is deleted.
|
||||
} // setOnScreenText
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the normal of the terrain the kart is over atm. This is
|
||||
* defined even if the kart is flying.
|
||||
*/
|
||||
const Vec3& Kart::getNormal() const
|
||||
{
|
||||
return m_terrain_info->getNormal();
|
||||
} // getNormal
|
||||
|
||||
/* EOF */
|
||||
|
||||
@@ -401,7 +401,7 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the kart is close to the ground, used to dis/enable
|
||||
* the upright constraint to allow for more realistic explosions. */
|
||||
bool isNearGround () const;
|
||||
bool isNearGround() const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the kart is eliminated. */
|
||||
virtual bool isEliminated() const { return m_eliminated; }
|
||||
@@ -446,6 +446,10 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void setOnScreenText(const wchar_t *text);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the normal of the terrain the kart is over atm. This is
|
||||
* defined even if the kart is flying. */
|
||||
virtual const Vec3& getNormal() const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** For debugging only: check if a kart is flying. */
|
||||
bool isFlying() const { return m_flying; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -140,8 +140,8 @@ void Moveable::update(float dt)
|
||||
*/
|
||||
void Moveable::updatePosition()
|
||||
{
|
||||
Vec3 forw_vec = m_transform.getBasis().getColumn(0);
|
||||
m_heading = -atan2f(forw_vec.getZ(), forw_vec.getX());
|
||||
Vec3 forw_vec = m_transform.getBasis().getColumn(2);
|
||||
m_heading = atan2f(forw_vec.getX(), forw_vec.getZ());
|
||||
|
||||
// The pitch in hpr is in between -pi and pi. But for the camera it
|
||||
// must be restricted to -pi/2 and pi/2 - so recompute it by restricting
|
||||
|
||||
@@ -89,15 +89,23 @@ public:
|
||||
m_transform.setOrigin(a);
|
||||
if(m_motion_state)
|
||||
m_motion_state->setWorldTransform(m_transform);
|
||||
}
|
||||
} // setXYZ
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the rotation of this moveable. */
|
||||
void setRotation(const btQuaternion&a)
|
||||
/** Sets the rotation of the physical body this moveable. */
|
||||
void setRotation(const btMatrix3x3 &m)
|
||||
{
|
||||
m_transform.setRotation(a);
|
||||
m_transform.setBasis(m);
|
||||
if(m_motion_state)
|
||||
m_motion_state->setWorldTransform(m_transform);
|
||||
}
|
||||
} // setRotation(btMatrix3x3)
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the rotation of the physical body this moveable. */
|
||||
void setRotation(const btQuaternion &q)
|
||||
{
|
||||
m_transform.setRotation(q);
|
||||
if(m_motion_state)
|
||||
m_motion_state->setWorldTransform(m_transform);
|
||||
} // setRotation(btQuaternion)
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void updateGraphics(float dt, const Vec3& off_xyz,
|
||||
const btQuaternion& off_rotation);
|
||||
|
||||
@@ -45,11 +45,20 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
|
||||
|
||||
m_kart->getAttachment()->clear();
|
||||
|
||||
m_curr_rotation.setPitch(m_kart->getPitch());
|
||||
m_curr_rotation.setRoll(m_kart->getRoll() );
|
||||
m_curr_rotation.setHeading(0);
|
||||
m_add_rotation = -m_curr_rotation/m_timer;
|
||||
m_curr_rotation.setHeading(m_kart->getHeading());
|
||||
// Get the current rotation of the kart
|
||||
m_curr_rotation = m_kart->getNode()->getRotation() * DEGREE_TO_RAD;
|
||||
|
||||
// Determine the rotation that will rotate the kart from the current
|
||||
// up direction to the right up direction it should have according to
|
||||
// the normal at the kart's location
|
||||
Vec3 up = m_kart->getTrans().getBasis().getColumn(1);
|
||||
btQuaternion q = shortestArcQuat(up, m_kart->getNormal());
|
||||
|
||||
// Store this rotation as 'delta HPR', which is added over time to the
|
||||
// current rotation to end up (after m_timer seconds) with the right up
|
||||
// rotation
|
||||
m_add_rotation.setHPR(q);
|
||||
m_add_rotation /= m_timer;
|
||||
|
||||
// Add a hit unless it was auto-rescue
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
@@ -98,13 +107,13 @@ RescueAnimation::~RescueAnimation()
|
||||
*/
|
||||
void RescueAnimation::update(float dt)
|
||||
{
|
||||
|
||||
m_xyz.setY(m_xyz.getY() + dt*m_velocity);
|
||||
m_xyz += dt*m_velocity * m_kart->getNormal();
|
||||
m_kart->setXYZ(m_xyz);
|
||||
m_curr_rotation += dt*m_add_rotation;
|
||||
btQuaternion q(m_curr_rotation.getHeading(), m_curr_rotation.getPitch(),
|
||||
btMatrix3x3 m;
|
||||
m.setEulerZYX(m_curr_rotation.getPitch(), m_curr_rotation.getHeading(),
|
||||
m_curr_rotation.getRoll());
|
||||
m_kart->setRotation(q);
|
||||
m_kart->setRotation(m);
|
||||
|
||||
AbstractKartAnimation::update(dt);
|
||||
|
||||
|
||||
11
src/main.cpp
11
src/main.cpp
@@ -195,7 +195,7 @@
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "states_screens/user_screen.hpp"
|
||||
#include "states_screens/dialogs/message_dialog.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "utils/command_line.hpp"
|
||||
@@ -902,13 +902,16 @@ int handleCmdLine()
|
||||
}
|
||||
if(CommandLine::has("--battle-ai-stats"))
|
||||
{
|
||||
std::string track;
|
||||
if (!CommandLine::has("--track", &track))
|
||||
track = "temple";
|
||||
UserConfigParams::m_arena_ai_stats=true;
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES);
|
||||
std::vector<std::string> l;
|
||||
for (int i = 0; i < 8; i++)
|
||||
l.push_back("tux");
|
||||
race_manager->setDefaultAIKartList(l);
|
||||
race_manager->setTrack("temple");
|
||||
race_manager->setTrack(track);
|
||||
race_manager->setNumKarts(8);
|
||||
race_manager->setDifficulty(RaceManager::Difficulty(3));
|
||||
UserConfigParams::m_no_start_screen = true;
|
||||
@@ -1887,8 +1890,8 @@ void runUnitTests()
|
||||
Log::info("UnitTest", "Kart characteristics");
|
||||
CombinedCharacteristic::unitTesting();
|
||||
|
||||
Log::info("UnitTest", "Battle Graph");
|
||||
BattleGraph::unitTesting();
|
||||
Log::info("UnitTest", "Arena Graph");
|
||||
ArenaGraph::unitTesting();
|
||||
|
||||
Log::info("UnitTest", "Fonts for translation");
|
||||
font_manager->unitTesting();
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/controller/controller.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "tracks/track_sector.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
@@ -91,7 +94,6 @@ void LinearWorld::reset()
|
||||
for(unsigned int i=0; i<kart_amount; i++)
|
||||
{
|
||||
m_kart_info[i].reset();
|
||||
m_kart_info[i].getTrackSector()->update(m_karts[i]->getXYZ());
|
||||
m_karts[i]->setWrongwayCounter(0);
|
||||
} // next kart
|
||||
|
||||
@@ -178,12 +180,12 @@ void LinearWorld::update(float dt)
|
||||
// in the position of the kart (e.g. while falling the kart
|
||||
// might get too close to another part of the track, shortly
|
||||
// jump to position one, then on reset fall back to last)
|
||||
if ((!kart_info.getTrackSector()->isOnRoad() &&
|
||||
if ((!getTrackSector(n)->isOnRoad() &&
|
||||
(!kart->getMaterial() ||
|
||||
kart->getMaterial()->isDriveReset())) &&
|
||||
!kart->isGhostKart())
|
||||
continue;
|
||||
kart_info.getTrackSector()->update(kart->getFrontXYZ());
|
||||
getTrackSector(n)->update(kart->getFrontXYZ());
|
||||
kart_info.m_overall_distance = kart_info.m_race_lap
|
||||
* m_track->getTrackLength()
|
||||
+ getDistanceDownTrackForKart(kart->getWorldKartId());
|
||||
@@ -377,24 +379,6 @@ void LinearWorld::newLap(unsigned int kart_index)
|
||||
kart->getController()->newLap(kart_info.m_race_lap);
|
||||
} // newLap
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Gets the sector a kart is on. This function returns UNKNOWN_SECTOR if the
|
||||
* kart_id is larger than the current kart info. This is necessary in the case
|
||||
* that a collision with the track happens during resetAllKarts: at this time
|
||||
* m_kart_info is not initialised (and has size 0), so it would trigger this
|
||||
* assert. While this normally does not happen, it is useful for track
|
||||
* designers that STK does not crash.
|
||||
* \param kart_id The world kart id of the kart for which to return
|
||||
* the sector.
|
||||
*/
|
||||
int LinearWorld::getSectorForKart(const AbstractKart *kart) const
|
||||
{
|
||||
if(kart->getWorldKartId()>=m_kart_info.size())
|
||||
return QuadGraph::UNKNOWN_SECTOR;
|
||||
return m_kart_info[kart->getWorldKartId()].getTrackSector()
|
||||
->getCurrentGraphNode();
|
||||
} // getSectorForKart
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Returns the distance the kart has travelled along the track since
|
||||
* crossing the start line..
|
||||
@@ -402,8 +386,7 @@ int LinearWorld::getSectorForKart(const AbstractKart *kart) const
|
||||
*/
|
||||
float LinearWorld::getDistanceDownTrackForKart(const int kart_id) const
|
||||
{
|
||||
assert(kart_id < (int)m_kart_info.size());
|
||||
return m_kart_info[kart_id].getTrackSector()->getDistanceFromStart();
|
||||
return getTrackSector(kart_id)->getDistanceFromStart();
|
||||
} // getDistanceDownTrackForKart
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -413,8 +396,7 @@ float LinearWorld::getDistanceDownTrackForKart(const int kart_id) const
|
||||
*/
|
||||
float LinearWorld::getDistanceToCenterForKart(const int kart_id) const
|
||||
{
|
||||
assert(kart_id < (int)m_kart_info.size());
|
||||
return m_kart_info[kart_id].getTrackSector()->getDistanceToCenter();
|
||||
return getTrackSector(kart_id)->getDistanceToCenter();
|
||||
} // getDistanceToCenterForKart
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -626,29 +608,41 @@ float LinearWorld::estimateFinishTimeForKart(AbstractKart* kart)
|
||||
*/
|
||||
unsigned int LinearWorld::getNumberOfRescuePositions() const
|
||||
{
|
||||
return QuadGraph::get()->getNumNodes();
|
||||
return DriveGraph::get()->getNumNodes();
|
||||
} // getNumberOfRescuePositions
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart)
|
||||
{
|
||||
KartInfo& info = m_kart_info[kart->getWorldKartId()];
|
||||
const unsigned int kart_id = kart->getWorldKartId();
|
||||
|
||||
info.getTrackSector()->rescue();
|
||||
getTrackSector(kart_id)->rescue();
|
||||
// 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 info.getTrackSector()->getCurrentGraphNode();
|
||||
return getTrackSector(kart_id)->getCurrentGraphNode();
|
||||
} // getRescuePositionIndex
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
btTransform LinearWorld::getRescueTransform(unsigned int index) const
|
||||
{
|
||||
const Vec3 &xyz = QuadGraph::get()->getQuadOfNode(index).getCenter();
|
||||
const Vec3 &xyz = DriveGraph::get()->getNode(index)->getCenter();
|
||||
const Vec3 &normal = DriveGraph::get()->getNode(index)->getNormal();
|
||||
btTransform pos;
|
||||
pos.setOrigin(xyz);
|
||||
pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f),
|
||||
m_track->getAngle(index)));
|
||||
|
||||
// First rotate into the quad's plane (q1), then rotate so that the kart points in the
|
||||
// right direction (q2).
|
||||
btQuaternion q1;
|
||||
if (normal.cross(Vec3(0, 1, 0)).length() > 0)
|
||||
{
|
||||
q1 = btQuaternion(-normal.cross(Vec3(0, 1, 0)), normal.angle(Vec3(0, 1, 0)));
|
||||
}
|
||||
else q1 = btQuaternion(Vec3(0,1,0),0);
|
||||
|
||||
// First apply the heading change, than the 'parallelisation' to the plane
|
||||
btQuaternion q2(btVector3(0,1,0), m_track->getAngle(index));
|
||||
pos.setRotation(q1*q2);
|
||||
return pos;
|
||||
} // getRescueTransform
|
||||
|
||||
@@ -849,19 +843,21 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt)
|
||||
// If the kart can go in more than one directions from the current track
|
||||
// don't do any reverse message handling, since it is likely that there
|
||||
// will be one direction in which it isn't going backwards anyway.
|
||||
int sector = m_kart_info[i].getTrackSector()->getCurrentGraphNode();
|
||||
int sector = getTrackSector(i)->getCurrentGraphNode();
|
||||
|
||||
if (QuadGraph::get()->getNumberOfSuccessors(sector) > 1)
|
||||
if (DriveGraph::get()->getNumberOfSuccessors(sector) > 1)
|
||||
return;
|
||||
|
||||
// check if the player is going in the wrong direction
|
||||
float angle_diff = kart->getHeading() - m_track->getAngle(sector);
|
||||
|
||||
if (angle_diff > M_PI)
|
||||
const DriveNode* node = DriveGraph::get()->getNode(sector);
|
||||
Vec3 center_line = node->getUpperCenter() - node->getLowerCenter();
|
||||
float angle_diff = kart->getVelocity().angle(center_line);
|
||||
|
||||
if (angle_diff > M_PI)
|
||||
angle_diff -= 2*M_PI;
|
||||
else if (angle_diff < -M_PI)
|
||||
else if (angle_diff < -M_PI)
|
||||
angle_diff += 2*M_PI;
|
||||
|
||||
|
||||
// Display a warning message if the kart is going back way (unless
|
||||
// the kart has already finished the race).
|
||||
if ((angle_diff > DEGREE_TO_RAD * 120.0f ||
|
||||
@@ -898,3 +894,8 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt)
|
||||
} // checkForWrongDirection
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void LinearWorld::setLastTriggeredCheckline(unsigned int kart_index, int index)
|
||||
{
|
||||
if (m_kart_info.size() == 0) return;
|
||||
getTrackSector(kart_index)->setLastTriggeredCheckline(index);
|
||||
} // setLastTriggeredCheckline
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "modes/world_with_rank.hpp"
|
||||
#include "tracks/track_sector.hpp"
|
||||
#include "utils/aligned_array.hpp"
|
||||
|
||||
class SFXBase;
|
||||
@@ -78,9 +77,6 @@ private:
|
||||
* track-length plus distance-along-track). */
|
||||
float m_overall_distance;
|
||||
|
||||
/** Stores the current graph node and track coordinates etc. */
|
||||
TrackSector m_track_sector;
|
||||
|
||||
/** Initialises all fields. */
|
||||
KartInfo() { reset(); }
|
||||
// --------------------------------------------------------------------
|
||||
@@ -92,14 +88,7 @@ private:
|
||||
m_time_at_last_lap = 99999.9f;
|
||||
m_estimated_finish = -1.0f;
|
||||
m_overall_distance = 0.0f;
|
||||
m_track_sector.reset();
|
||||
} // reset
|
||||
// --------------------------------------------------------------------
|
||||
/** Returns a pointer to the current node object. */
|
||||
TrackSector *getTrackSector() {return &m_track_sector; }
|
||||
// --------------------------------------------------------------------
|
||||
/** Returns a pointer to the current node object. */
|
||||
const TrackSector *getTrackSector() const {return &m_track_sector; }
|
||||
};
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -124,7 +113,6 @@ public:
|
||||
virtual ~LinearWorld();
|
||||
|
||||
virtual void update(float delta) OVERRIDE;
|
||||
int getSectorForKart(const AbstractKart *kart) const;
|
||||
float getDistanceDownTrackForKart(const int kart_id) const;
|
||||
float getDistanceToCenterForKart(const int kart_id) const;
|
||||
float getEstimatedFinishTime(const int kart_id) const;
|
||||
@@ -149,14 +137,6 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Override settings from base class */
|
||||
virtual bool useChecklineRequirements() const OVERRIDE { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the kart is on a valid driveline quad.
|
||||
* \param kart_index Index of the kart. */
|
||||
bool isOnRoad(unsigned int kart_index) const
|
||||
{
|
||||
return m_kart_info[kart_index].getTrackSector()->isOnRoad();
|
||||
} // isOnRoad
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of laps a kart has completed.
|
||||
* \param kart_index World index of the kart. */
|
||||
@@ -165,21 +145,8 @@ public:
|
||||
assert(kart_index < m_kart_info.size());
|
||||
return m_kart_info[kart_index].m_race_lap;
|
||||
} // getkartLap
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the track_sector object for the specified kart.
|
||||
* \param kart_index World index of the kart. */
|
||||
TrackSector& getTrackSector(unsigned int kart_index)
|
||||
{
|
||||
return m_kart_info[kart_index].m_track_sector;
|
||||
} // getTrackSector
|
||||
// ------------------------------------------------------------------------
|
||||
void setLastTriggeredCheckline(unsigned int kart_index, int index)
|
||||
{
|
||||
if (m_kart_info.size() == 0)
|
||||
return;
|
||||
m_kart_info[kart_index].m_track_sector.setLastTriggeredCheckline(index);
|
||||
}
|
||||
void setLastTriggeredCheckline(unsigned int kart_index, int index);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns how far the kart has driven so far (i.e.
|
||||
* number-of-laps-finished times track-length plus distance-on-track.
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
OverWorld::OverWorld() : WorldWithRank()
|
||||
OverWorld::OverWorld() : World()
|
||||
{
|
||||
m_return_to_garage = false;
|
||||
m_stop_music_when_dialog_open = false;
|
||||
@@ -118,8 +118,8 @@ void OverWorld::update(float dt)
|
||||
music_manager->startMusic();
|
||||
m_karts[0]->startEngineSFX();
|
||||
}
|
||||
WorldWithRank::update(dt);
|
||||
WorldWithRank::updateTrack(dt);
|
||||
World::update(dt);
|
||||
World::updateTrack(dt);
|
||||
const unsigned int kart_amount = (unsigned int)m_karts.size();
|
||||
|
||||
// isn't it cool, on the overworld nitro is free!
|
||||
@@ -269,7 +269,7 @@ void OverWorld::onMouseClick(int x, int y)
|
||||
if(challenge)
|
||||
{
|
||||
// Use the 'get closest start point' rescue function
|
||||
// from WorldWithRank by setting the kart's position to
|
||||
// from World by setting the kart's position to
|
||||
// be the location of the challenge bubble.
|
||||
AbstractKart* kart = getKart(0);
|
||||
kart->setXYZ(challenge->m_position);
|
||||
|
||||
@@ -20,19 +20,18 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "modes/world_with_rank.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "utils/aligned_array.hpp"
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
/*
|
||||
* The overworld map where challenges are played.
|
||||
* \note This mode derives from LinearWorld to get support for drivelines,
|
||||
* minimap and rescue, even though this world is not technically
|
||||
* linear.
|
||||
* \note Extends world to make a simple world where karts can drive around,
|
||||
* it adds challenges and starting of races.
|
||||
* \ingroup modes
|
||||
*/
|
||||
class OverWorld : public WorldWithRank
|
||||
class OverWorld : public World
|
||||
{
|
||||
protected:
|
||||
|
||||
|
||||
@@ -25,18 +25,16 @@
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/render_info.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/kart.hpp"
|
||||
#include "karts/kart_model.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/rescue_animation.hpp"
|
||||
#include "karts/controller/local_player_controller.hpp"
|
||||
#include "karts/controller/soccer_ai.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
#include "tracks/track_sector.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
|
||||
#include <IMeshSceneNode.h>
|
||||
@@ -61,6 +59,7 @@ SoccerWorld::SoccerWorld() : WorldWithRank()
|
||||
m_use_highscores = false;
|
||||
m_red_ai = 0;
|
||||
m_blue_ai = 0;
|
||||
m_ball_track_sector = NULL;
|
||||
} // SoccerWorld
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -69,6 +68,9 @@ SoccerWorld::SoccerWorld() : WorldWithRank()
|
||||
SoccerWorld::~SoccerWorld()
|
||||
{
|
||||
m_goal_sound->deleteSFX();
|
||||
|
||||
delete m_ball_track_sector;
|
||||
m_ball_track_sector = NULL;
|
||||
} // ~SoccerWorld
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -88,6 +90,12 @@ void SoccerWorld::init()
|
||||
m_goal_target = race_manager->getMaxGoal();
|
||||
m_goal_sound = SFXManager::get()->createSoundSource("goal_scored");
|
||||
|
||||
if (m_track->hasNavMesh())
|
||||
{
|
||||
// Init track sector for ball if navmesh is found
|
||||
m_ball_track_sector = new TrackSector();
|
||||
}
|
||||
|
||||
TrackObjectManager* tom = getTrack()->getTrackObjectManager();
|
||||
assert(tom);
|
||||
PtrVector<TrackObject>& objects = tom->getObjects();
|
||||
@@ -141,10 +149,15 @@ void SoccerWorld::reset()
|
||||
m_goal_sound->stop();
|
||||
}
|
||||
|
||||
if (m_track->hasNavMesh())
|
||||
{
|
||||
m_ball_track_sector->reset();
|
||||
}
|
||||
|
||||
initKartList();
|
||||
resetAllPosition();
|
||||
m_ball->reset();
|
||||
m_bgd.reset();
|
||||
|
||||
// Make the player kart in profiling mode up
|
||||
// ie make this kart less likely to affect gaming result
|
||||
if (UserConfigParams::m_arena_ai_stats)
|
||||
@@ -169,7 +182,7 @@ void SoccerWorld::update(float dt)
|
||||
updateBallPosition(dt);
|
||||
if (m_track->hasNavMesh())
|
||||
{
|
||||
updateKartNodes();
|
||||
updateSectorForKarts();
|
||||
updateAIData();
|
||||
}
|
||||
|
||||
@@ -427,24 +440,6 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index,
|
||||
return new_kart;
|
||||
} // createKart
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Updates the m_kart_on_node value of each kart to localize it
|
||||
* on the navigation mesh.
|
||||
*/
|
||||
void SoccerWorld::updateKartNodes()
|
||||
{
|
||||
if (isRaceOver()) return;
|
||||
|
||||
const unsigned int n = getNumKarts();
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
{
|
||||
if (m_karts[i]->isEliminated()) continue;
|
||||
|
||||
m_kart_on_node[i] = BattleGraph::get()->pointToNode(m_kart_on_node[i],
|
||||
m_karts[i]->getXYZ(), false/*ignore_vertical*/);
|
||||
}
|
||||
} // updateKartNodes
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Localize the ball on the navigation mesh.
|
||||
*/
|
||||
@@ -461,11 +456,9 @@ void SoccerWorld::updateBallPosition(float dt)
|
||||
|
||||
if (m_track->hasNavMesh())
|
||||
{
|
||||
m_ball_on_node = BattleGraph::get()->pointToNode(m_ball_on_node,
|
||||
getBallPosition(), true/*ignore_vertical*/);
|
||||
|
||||
if (m_ball_on_node == BattleGraph::UNKNOWN_POLY &&
|
||||
getPhase() == RACE_PHASE)
|
||||
m_ball_track_sector
|
||||
->update(getBallPosition(), true/*ignore_vertical*/);
|
||||
if (!m_ball_track_sector->isOnRoad() && getPhase() == RACE_PHASE)
|
||||
{
|
||||
m_ball_invalid_timer += dt;
|
||||
// Reset the ball and karts if out of navmesh after 2 seconds
|
||||
@@ -486,12 +479,12 @@ void SoccerWorld::updateBallPosition(float dt)
|
||||
} // updateBallPosition
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void SoccerWorld::resetAllPosition()
|
||||
int SoccerWorld::getBallNode() const
|
||||
{
|
||||
m_kart_on_node.clear();
|
||||
m_kart_on_node.resize(m_karts.size(), BattleGraph::UNKNOWN_POLY);
|
||||
m_ball_on_node = BattleGraph::UNKNOWN_POLY;
|
||||
} // resetAllPosition
|
||||
assert(m_ball_track_sector != NULL);
|
||||
return m_ball_track_sector->getCurrentGraphNode();
|
||||
} // getBallNode
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
SoccerTeam SoccerWorld::getKartTeam(unsigned int kart_id) const
|
||||
{
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
class AbstractKart;
|
||||
class Controller;
|
||||
class TrackObject;
|
||||
class TrackSector;
|
||||
|
||||
/** An implementation of World, to provide the soccer game mode
|
||||
/** \brief An implementation of WorldWithRank, to provide the soccer game mode
|
||||
* Notice: In soccer world, true goal means blue, false means red.
|
||||
* \ingroup modes
|
||||
*/
|
||||
@@ -279,8 +280,7 @@ private:
|
||||
std::map<int, unsigned int> m_kart_position_map;
|
||||
|
||||
/** Data generated from navmesh */
|
||||
std::vector<int> m_kart_on_node;
|
||||
int m_ball_on_node;
|
||||
TrackSector* m_ball_track_sector;
|
||||
|
||||
int m_red_ai;
|
||||
int m_blue_ai;
|
||||
@@ -289,12 +289,8 @@ private:
|
||||
|
||||
/** Set the team for the karts */
|
||||
void initKartList();
|
||||
/** Function to update the locations of all karts on the polygon map */
|
||||
void updateKartNodes();
|
||||
/** Function to update the location the ball on the polygon map */
|
||||
void updateBallPosition(float dt);
|
||||
/** Clean up */
|
||||
void resetAllPosition();
|
||||
/** Function to update data for AI usage. */
|
||||
void updateAIData();
|
||||
/** Get number of teammates in a team, used by starting position assign. */
|
||||
@@ -357,11 +353,7 @@ public:
|
||||
m_blue_score_times : m_red_score_times);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
int getKartNode(unsigned int kart_id) const
|
||||
{ return m_kart_on_node[kart_id]; }
|
||||
// ------------------------------------------------------------------------
|
||||
int getBallNode() const
|
||||
{ return m_ball_on_node; }
|
||||
int getBallNode() const;
|
||||
// ------------------------------------------------------------------------
|
||||
const Vec3& getBallPosition() const
|
||||
{ return (Vec3&)m_ball_body->getCenterOfMassTransform().getOrigin(); }
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
@@ -93,8 +92,7 @@ void ThreeStrikesBattle::reset()
|
||||
|
||||
for(unsigned int n=0; n<kart_amount; n++)
|
||||
{
|
||||
m_kart_info[n].m_lives = 3;
|
||||
m_kart_info[n].m_on_node = BattleGraph::UNKNOWN_POLY;
|
||||
m_kart_info[n].m_lives = 3;
|
||||
|
||||
// no positions in this mode
|
||||
m_karts[n]->setPosition(-1);
|
||||
@@ -308,7 +306,7 @@ void ThreeStrikesBattle::update(float dt)
|
||||
WorldWithRank::updateTrack(dt);
|
||||
|
||||
if (m_track->hasNavMesh())
|
||||
updateKartNodes();
|
||||
updateSectorForKarts();
|
||||
|
||||
// insert blown away tire(s) now if was requested
|
||||
while (m_insert_tire > 0)
|
||||
@@ -458,33 +456,6 @@ bool ThreeStrikesBattle::isRaceOver()
|
||||
return getCurrentNumKarts()==1 || getCurrentNumPlayers()==0;
|
||||
} // isRaceOver
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Updates the m_on_node value of each kart to localize it
|
||||
* on the navigation mesh.
|
||||
*/
|
||||
void ThreeStrikesBattle::updateKartNodes()
|
||||
{
|
||||
if (isRaceOver()) return;
|
||||
|
||||
const unsigned int n = getNumKarts();
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
{
|
||||
if (m_karts[i]->isEliminated()) continue;
|
||||
|
||||
m_kart_info[i].m_on_node = BattleGraph::get()
|
||||
->pointToNode(m_kart_info[i].m_on_node,
|
||||
m_karts[i]->getXYZ(), false/*ignore_vertical*/);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Get the which node the kart located in navigation mesh.
|
||||
*/
|
||||
int ThreeStrikesBattle::getKartNode(unsigned int kart_id) const
|
||||
{
|
||||
return m_kart_info[kart_id].m_on_node;
|
||||
} // getKartNode
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when the race finishes, i.e. after playing (if necessary) an
|
||||
* end of race animation. It updates the time for all karts still racing,
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
class PhysicalObject;
|
||||
|
||||
/**
|
||||
* \brief An implementation of World, to provide the 3 strikes battle game mode
|
||||
* \brief An implementation of WorldWithRank, to provide the 3 strikes battle
|
||||
* game mode
|
||||
* \ingroup modes
|
||||
*/
|
||||
class ThreeStrikesBattle : public WorldWithRank
|
||||
@@ -40,7 +41,6 @@ private:
|
||||
struct BattleInfo
|
||||
{
|
||||
int m_lives;
|
||||
int m_on_node;
|
||||
};
|
||||
|
||||
/** This vector contains an 'BattleInfo' struct for every kart in the race.
|
||||
@@ -71,9 +71,6 @@ private:
|
||||
|
||||
PtrVector<TrackObject, REF> m_tires;
|
||||
|
||||
/** Function to update the locations of all karts on the polygon map */
|
||||
void updateKartNodes();
|
||||
|
||||
/** Profiling usage */
|
||||
int m_total_rescue;
|
||||
int m_frame_count;
|
||||
@@ -93,31 +90,29 @@ public:
|
||||
ThreeStrikesBattle();
|
||||
virtual ~ThreeStrikesBattle();
|
||||
|
||||
virtual void init();
|
||||
virtual void init() OVERRIDE;
|
||||
|
||||
// clock events
|
||||
virtual bool isRaceOver();
|
||||
virtual void terminateRace();
|
||||
virtual bool isRaceOver() OVERRIDE;
|
||||
virtual void terminateRace() OVERRIDE;
|
||||
|
||||
// overriding World methods
|
||||
virtual void reset();
|
||||
virtual void reset() OVERRIDE;
|
||||
|
||||
//virtual void getDefaultCollectibles(int& collectible_type, int& amount);
|
||||
virtual bool useFastMusicNearEnd() const { return false; }
|
||||
virtual bool useFastMusicNearEnd() const OVERRIDE { return false; }
|
||||
virtual void getKartsDisplayInfo(
|
||||
std::vector<RaceGUIBase::KartIconDisplayInfo> *info);
|
||||
virtual bool raceHasLaps(){ return false; }
|
||||
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;
|
||||
virtual bool raceHasLaps() OVERRIDE { return false; }
|
||||
|
||||
virtual const std::string& getIdent() const;
|
||||
virtual const std::string& getIdent() const OVERRIDE;
|
||||
|
||||
virtual void kartHit(const unsigned int kart_id);
|
||||
virtual void update(float dt);
|
||||
virtual void kartHit(const unsigned int kart_id) OVERRIDE;
|
||||
virtual void update(float dt) OVERRIDE;
|
||||
|
||||
virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node);
|
||||
virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) OVERRIDE;
|
||||
virtual void enterRaceOverState() OVERRIDE;
|
||||
|
||||
int getKartNode(unsigned int kart_id) const;
|
||||
|
||||
void updateKartRanks();
|
||||
void increaseRescueCount() { m_total_rescue++; }
|
||||
}; // ThreeStrikesBattles
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/camera.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
#include "graphics/render_info.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "input/device_manager.hpp"
|
||||
@@ -164,11 +165,11 @@ void World::init()
|
||||
// constructor is called, so the wrong race gui would be created.
|
||||
createRaceGUI();
|
||||
|
||||
RewindManager::create();
|
||||
RewindManager::create();
|
||||
|
||||
// Grab the track file
|
||||
m_track = track_manager->getTrack(race_manager->getTrackName());
|
||||
m_script_engine = new Scripting::ScriptEngine();
|
||||
m_script_engine = new Scripting::ScriptEngine();
|
||||
if(!m_track)
|
||||
{
|
||||
std::ostringstream msg;
|
||||
@@ -241,7 +242,7 @@ void World::init()
|
||||
*/
|
||||
void World::reset()
|
||||
{
|
||||
RewindManager::get()->reset();
|
||||
RewindManager::get()->reset();
|
||||
|
||||
// If m_saved_race_gui is set, it means that the restart was done
|
||||
// when the race result gui was being shown. In this case restore the
|
||||
@@ -706,69 +707,16 @@ void World::resetAllKarts()
|
||||
}
|
||||
}
|
||||
|
||||
bool all_finished=false;
|
||||
// kart->isInRest() is not fully correct, since it only takes the
|
||||
// velocity in count, which might be close to zero when the kart
|
||||
// is just hitting the floor, before being pushed up again by
|
||||
// the suspension. So we just do a longer initial simulation,
|
||||
// which should be long enough for all karts to be firmly on ground.
|
||||
for(int i=0; i<60; i++) m_physics->update(1.f/60.f);
|
||||
|
||||
// Stil wait will all karts are in rest (and handle the case that a kart
|
||||
// fell through the ground, which can happen if a kart falls for a long
|
||||
// time, therefore having a high speed when hitting the ground.
|
||||
int count = 0;
|
||||
while(!all_finished)
|
||||
// Do a longer initial simulation, which should be long enough for all
|
||||
// karts to be firmly on ground.
|
||||
float g = World::getWorld()->getTrack()->getGravity();
|
||||
for (KartList::iterator i = m_karts.begin(); i != m_karts.end(); i++)
|
||||
{
|
||||
if (count++ > 100)
|
||||
{
|
||||
Log::error("World", "Infinite loop waiting for all_finished?");
|
||||
break;
|
||||
}
|
||||
m_physics->update(1.f/60.f);
|
||||
all_finished=true;
|
||||
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
|
||||
{
|
||||
if ((*i)->isGhostKart()) continue;
|
||||
if(!(*i)->isInRest())
|
||||
{
|
||||
Vec3 normal;
|
||||
Vec3 hit_point;
|
||||
const Material *material;
|
||||
// We can't use (*i)->getXYZ(), since this is only defined
|
||||
// after update() was called. Instead we have to get the
|
||||
// real position of the rigid body.
|
||||
btTransform t;
|
||||
(*i)->getBody()->getMotionState()->getWorldTransform(t);
|
||||
// This test can not be done only once before the loop, since
|
||||
// it can happen that the kart falls through the track later!
|
||||
Vec3 to = t.getOrigin()+Vec3(0, -10000, 0);
|
||||
m_track->getTriangleMesh().castRay(t.getOrigin(), to,
|
||||
&hit_point, &material,
|
||||
&normal);
|
||||
if(!material)
|
||||
{
|
||||
Log::error("World",
|
||||
"No valid starting position for kart %d "
|
||||
"on track %s.",
|
||||
(int)(i-m_karts.begin()),
|
||||
m_track->getIdent().c_str());
|
||||
if (UserConfigParams::m_artist_debug_mode)
|
||||
{
|
||||
Log::warn("World", "Activating fly mode.");
|
||||
(*i)->flyUp();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
all_finished=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // while
|
||||
if ((*i)->isGhostKart()) continue;
|
||||
(*i)->getBody()->setGravity((*i)->getMaterial()->hasGravity() ?
|
||||
(*i)->getNormal() * -g : Vec3(0, -g, 0));
|
||||
}
|
||||
for(int i=0; i<60; i++) m_physics->update(1.f/60.f);
|
||||
|
||||
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
|
||||
{
|
||||
@@ -806,8 +754,8 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform)
|
||||
btTransform pos(transform);
|
||||
|
||||
// Move the kart
|
||||
Vec3 xyz = pos.getOrigin() + btVector3(0, 0.5f*kart->getKartHeight(),0.0f);
|
||||
|
||||
Vec3 xyz = pos.getOrigin() +
|
||||
pos.getBasis() * Vec3(0, 0.5f*kart->getKartHeight(), 0);
|
||||
pos.setOrigin(xyz);
|
||||
kart->setXYZ(xyz);
|
||||
kart->setRotation(pos.getRotation());
|
||||
@@ -1321,20 +1269,36 @@ void World::unpause()
|
||||
void World::delayedSelfDestruct()
|
||||
{
|
||||
m_self_destruct = true;
|
||||
}
|
||||
} // delayedSelfDestruct
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void World::escapePressed()
|
||||
{
|
||||
new RacePausedDialog(0.8f, 0.6f);
|
||||
}
|
||||
} // escapePressed
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool World::isFogEnabled() const
|
||||
{
|
||||
return !m_force_disable_fog && (m_track != NULL && m_track->isFogEnabled());
|
||||
}
|
||||
} // isFogEnabled
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the start transform with the give index.
|
||||
* \param rescue_pos Index of the start position to be returned.
|
||||
* \returns The transform of the corresponding start position.
|
||||
*/
|
||||
btTransform World::getRescueTransform(unsigned int rescue_pos) const
|
||||
{
|
||||
return m_track->getStartTransform(rescue_pos);
|
||||
} // getRescueTransform
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Uses the start position as rescue positions, override if necessary
|
||||
*/
|
||||
unsigned int World::getNumberOfRescuePositions() const
|
||||
{
|
||||
return m_track->getNumberOfStartPositions();
|
||||
} // getNumberOfRescuePositions
|
||||
|
||||
/* EOF */
|
||||
|
||||
@@ -221,13 +221,13 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of rescue positions on a given track and game
|
||||
* mode. */
|
||||
virtual unsigned int getNumberOfRescuePositions() const = 0;
|
||||
virtual unsigned int getNumberOfRescuePositions() const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Determines the rescue position index of the specified kart. */
|
||||
virtual unsigned int getRescuePositionIndex(AbstractKart *kart) = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the bullet transformation for the specified rescue index. */
|
||||
virtual btTransform getRescueTransform(unsigned int index) const = 0;
|
||||
virtual btTransform getRescueTransform(unsigned int index) const;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void moveKartAfterRescue(AbstractKart* kart);
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -20,11 +20,23 @@
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "tracks/graph.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_sector.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
WorldWithRank::~WorldWithRank()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_kart_track_sector.size(); i++)
|
||||
{
|
||||
delete m_kart_track_sector[i];
|
||||
}
|
||||
m_kart_track_sector.clear();
|
||||
} // ~WorldWithRank
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void WorldWithRank::init()
|
||||
{
|
||||
@@ -39,8 +51,26 @@ void WorldWithRank::init()
|
||||
#endif
|
||||
stk_config->getAllScores(&m_score_for_position, getNumKarts());
|
||||
|
||||
// Don't init track sector if navmesh is not found in arena
|
||||
if ((m_track->isArena() || m_track->isSoccer()) && !m_track->hasNavMesh())
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < m_karts.size(); i++)
|
||||
m_kart_track_sector.push_back(new TrackSector());
|
||||
|
||||
} // init
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void WorldWithRank::reset()
|
||||
{
|
||||
World::reset();
|
||||
for (unsigned int i = 0; i < m_kart_track_sector.size(); i++)
|
||||
{
|
||||
getTrackSector(i)->reset();
|
||||
getTrackSector(i)->update(m_karts[i]->getXYZ());
|
||||
}
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Returns the kart with a given position.
|
||||
* \param p The position of the kart, 1<=p<=num_karts).
|
||||
@@ -124,16 +154,6 @@ void WorldWithRank::endSetKartPositions()
|
||||
#endif
|
||||
} // endSetKartPositions
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** WorldWithRank uses the start position as rescue positions. So return
|
||||
* the number of start positions.
|
||||
*/
|
||||
unsigned int WorldWithRank::getNumberOfRescuePositions() const
|
||||
{
|
||||
return getTrack()->getNumberOfStartPositions();
|
||||
} // getNumberOfRescuePositions
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Determines the rescue position for a kart. The rescue position is the
|
||||
* start position which is has the biggest accumulated distance to all other
|
||||
@@ -162,7 +182,7 @@ unsigned int WorldWithRank::getRescuePositionIndex(AbstractKart *kart)
|
||||
for(unsigned int k=0; k<getCurrentNumKarts(); k++)
|
||||
{
|
||||
if(kart->getWorldKartId()==k) continue;
|
||||
float abs_distance2 = (getKart(k)->getXYZ()-v).length2_2d();
|
||||
float abs_distance2 = (getKart(k)->getXYZ()-v).length2();
|
||||
const float CLEAR_SPAWN_RANGE2 = 5*5;
|
||||
if( abs_distance2 < CLEAR_SPAWN_RANGE2)
|
||||
{
|
||||
@@ -184,16 +204,6 @@ unsigned int WorldWithRank::getRescuePositionIndex(AbstractKart *kart)
|
||||
return furthest_id_found;
|
||||
} // getRescuePositionIndex
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the start transform with the give index.
|
||||
* \param rescue_pos Index of the start position to be returned.
|
||||
* \returns The transform of the corresponding start position.
|
||||
*/
|
||||
btTransform WorldWithRank::getRescueTransform(unsigned int rescue_pos) const
|
||||
{
|
||||
return getTrack()->getStartTransform(rescue_pos);
|
||||
} // getRescueTransform
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Returns the number of points for a kart at a specified position.
|
||||
* \param p Position (starting with 1).
|
||||
@@ -204,3 +214,44 @@ int WorldWithRank::getScoreForPosition(int p)
|
||||
assert(p - 1 <(int) m_score_for_position.size());
|
||||
return m_score_for_position[p - 1];
|
||||
} // getScoreForPosition
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Returns true if the kart is on a valid graph quad.
|
||||
* \param kart_index Index of the kart.
|
||||
*/
|
||||
bool WorldWithRank::isOnRoad(unsigned int kart_index) const
|
||||
{
|
||||
return getTrackSector(kart_index)->isOnRoad();
|
||||
} // isOnRoad
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Gets the sector a kart is on. This function returns UNKNOWN_SECTOR if the
|
||||
* kart_id is larger than the current kart sector. This is necessary in the
|
||||
* case that a collision with the track happens during resetAllKarts: at this
|
||||
* time m_kart_track_sector is not initialised (and has size 0), so it would
|
||||
* trigger this assert. While this normally does not happen, it is useful for
|
||||
* track designers that STK does not crash.
|
||||
* \param kart Kart for which to return the sector.
|
||||
*/
|
||||
int WorldWithRank::getSectorForKart(const AbstractKart *kart) const
|
||||
{
|
||||
if (kart->getWorldKartId() >= m_kart_track_sector.size())
|
||||
return Graph::UNKNOWN_SECTOR;
|
||||
return getTrackSector(kart->getWorldKartId())->getCurrentGraphNode();
|
||||
} // getSectorForKart
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Localize each kart on the graph using its center xyz.
|
||||
*/
|
||||
void WorldWithRank::updateSectorForKarts()
|
||||
{
|
||||
if (isRaceOver()) return;
|
||||
|
||||
const unsigned int n = getNumKarts();
|
||||
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());
|
||||
}
|
||||
} // updateSectorForKarts
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "modes/world.hpp"
|
||||
|
||||
class AbstractKart;
|
||||
class TrackSector;
|
||||
|
||||
/**
|
||||
* A WorldWithRank is a world where the karts are ranked. This is the base
|
||||
@@ -60,28 +61,42 @@ protected:
|
||||
|
||||
unsigned int getClosestStartPoint(AbstractKart *kart);
|
||||
|
||||
/** Stores the current graph node and track coordinates for each kart. */
|
||||
std::vector<TrackSector*> m_kart_track_sector;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void updateSectorForKarts();
|
||||
|
||||
public:
|
||||
WorldWithRank() : World() {}
|
||||
virtual ~WorldWithRank();
|
||||
/** call just after instanciating. can't be moved to the contructor as child
|
||||
classes must be instanciated, otherwise polymorphism will fail and the
|
||||
results will be incorrect */
|
||||
virtual void init() OVERRIDE;
|
||||
virtual void reset() OVERRIDE;
|
||||
|
||||
bool displayRank() const { return m_display_rank; }
|
||||
|
||||
void beginSetKartPositions();
|
||||
bool setKartPosition(unsigned int kart_id,
|
||||
unsigned int position);
|
||||
unsigned int position);
|
||||
void endSetKartPositions();
|
||||
AbstractKart* getKartAtPosition(unsigned int p) const;
|
||||
virtual int getScoreForPosition(int p);
|
||||
|
||||
|
||||
virtual unsigned int getNumberOfRescuePositions() const OVERRIDE;
|
||||
virtual unsigned int getRescuePositionIndex(AbstractKart *kart) OVERRIDE;
|
||||
virtual btTransform getRescueTransform(unsigned int index) const OVERRIDE;
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the track_sector object for the specified kart.
|
||||
* \param kart_index World index of the kart. */
|
||||
TrackSector* getTrackSector(unsigned int kart_index) const
|
||||
{
|
||||
assert(kart_index < m_kart_track_sector.size());
|
||||
return m_kart_track_sector[kart_index];
|
||||
} // getTrackSector
|
||||
// ------------------------------------------------------------------------
|
||||
bool isOnRoad(unsigned int kart_index) const;
|
||||
// ------------------------------------------------------------------------
|
||||
int getSectorForKart(const AbstractKart *kart) const;
|
||||
|
||||
}; // WorldWithRank
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "LinearMath/btIDebugDraw.h"
|
||||
#include "BulletDynamics/ConstraintSolver/btContactConstraint.h"
|
||||
|
||||
#include "graphics/material.hpp"
|
||||
#include "karts/kart.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
@@ -444,7 +445,9 @@ void btKart::updateVehicle( btScalar step )
|
||||
if(m_num_wheels_on_ground==0)
|
||||
{
|
||||
btVector3 kart_up = getChassisWorldTransform().getBasis().getColumn(1);
|
||||
btVector3 terrain_up(0,1,0);
|
||||
btVector3 terrain_up =
|
||||
m_kart->getMaterial() && m_kart->getMaterial()->hasGravity() ?
|
||||
m_kart->getNormal() : Vec3(0, 1, 0);
|
||||
// Length of axis depends on the angle - i.e. the further awat
|
||||
// the kart is from being upright, the larger the applied impulse
|
||||
// will be, resulting in fast changes when the kart is on its
|
||||
@@ -521,9 +524,9 @@ void btKart::updateVehicle( btScalar step )
|
||||
for (int i=0;i<m_wheelInfo.size();i++)
|
||||
{
|
||||
btWheelInfo& wheel = m_wheelInfo[i];
|
||||
btVector3 relpos = wheel.m_raycastInfo.m_hardPointWS
|
||||
- getRigidBody()->getCenterOfMassPosition();
|
||||
btVector3 vel = getRigidBody()->getVelocityInLocalPoint(relpos);
|
||||
//btVector3 relpos = wheel.m_raycastInfo.m_hardPointWS
|
||||
// - getRigidBody()->getCenterOfMassPosition();
|
||||
//btVector3 vel = getRigidBody()->getVelocityInLocalPoint(relpos);
|
||||
|
||||
if (wheel.m_raycastInfo.m_isInContact)
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
using namespace irr;
|
||||
|
||||
#include "graphics/material.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
#include "graphics/mesh_tools.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
#include <string>
|
||||
#include <angelscript.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include "scriptengine/script_utils.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/ptr_vector.hpp"
|
||||
|
||||
class TrackObjectPresentation;
|
||||
|
||||
@@ -23,13 +23,13 @@
|
||||
#include "input/input_device.hpp"
|
||||
#include "input/input_manager.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "scriptengine/script_engine.hpp"
|
||||
#include "states_screens/dialogs/tutorial_message_dialog.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_object.hpp"
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
|
||||
#include <angelscript.h>
|
||||
#include "scriptarray.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <iostream> //debug
|
||||
|
||||
@@ -137,11 +137,14 @@ void RaceGUIBase::reset()
|
||||
{
|
||||
const AbstractKart *kart = World::getWorld()->getKart(i);
|
||||
m_referee_pos[i] = kart->getTrans()(Referee::getStartOffset());
|
||||
m_referee_rotation[i] = Referee::getStartRotation()
|
||||
+ Vec3(0, kart->getHeading()*RAD_TO_DEGREE, 0);
|
||||
Vec3 hpr;
|
||||
btQuaternion q = btQuaternion(kart->getTrans().getBasis().getColumn(1),
|
||||
Referee::getStartRotation().getY() * DEGREE_TO_RAD) *
|
||||
kart->getTrans().getRotation();
|
||||
hpr.setHPR(q);
|
||||
m_referee_rotation[i] = hpr.toIrrHPR();
|
||||
}
|
||||
|
||||
|
||||
m_referee_height = 10.0f;
|
||||
m_referee->attachToSceneNode();
|
||||
m_plunger_move_time = 0;
|
||||
@@ -419,8 +422,8 @@ void RaceGUIBase::preRenderCallback(const Camera *camera)
|
||||
if(m_referee && camera->getKart())
|
||||
{
|
||||
unsigned int world_id = camera->getKart()->getWorldKartId();
|
||||
Vec3 xyz = m_referee_pos[world_id];
|
||||
xyz.setY(xyz.getY()+m_referee_height);
|
||||
Vec3 xyz = m_referee_pos[world_id] +
|
||||
camera->getKart()->getNormal() * m_referee_height;
|
||||
m_referee->setPosition(xyz);
|
||||
m_referee->setRotation(m_referee_rotation[world_id]);
|
||||
}
|
||||
|
||||
441
src/tracks/arena_graph.cpp
Normal file
441
src/tracks/arena_graph.cpp
Normal file
@@ -0,0 +1,441 @@
|
||||
//
|
||||
// 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 "tracks/arena_graph.hpp"
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/arena_node.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node)
|
||||
: Graph()
|
||||
{
|
||||
loadNavmesh(navmesh);
|
||||
buildGraph();
|
||||
// Compute shortest distance from all nodes
|
||||
for (unsigned int i = 0; i < getNumNodes(); i++)
|
||||
computeDijkstra(i);
|
||||
|
||||
setNearbyNodesOfAllNodes();
|
||||
if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
loadGoalNodes(node);
|
||||
|
||||
} // ArenaGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
ArenaNode* ArenaGraph::getNode(unsigned int i) const
|
||||
{
|
||||
assert(i < m_all_nodes.size());
|
||||
ArenaNode* n = dynamic_cast<ArenaNode*>(m_all_nodes[i]);
|
||||
assert(n != NULL);
|
||||
return n;
|
||||
} // getNode
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void ArenaGraph::differentNodeColor(int n, video::SColor* c) const
|
||||
{
|
||||
std::set<int>::iterator it;
|
||||
it = m_red_node.find(n);
|
||||
if (it != m_red_node.end())
|
||||
{
|
||||
*c = video::SColor(255, 255, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
it = m_blue_node.find(n);
|
||||
if (it != m_blue_node.end())
|
||||
{
|
||||
*c = video::SColor(255, 0, 0, 255);
|
||||
return;
|
||||
}
|
||||
|
||||
if (UserConfigParams::m_track_debug)
|
||||
{
|
||||
if (m_all_nodes[n]->is3DQuad())
|
||||
*c = video::SColor(255, 0, 255, 0);
|
||||
else
|
||||
*c = video::SColor(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
} // differentNodeColor
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void ArenaGraph::loadNavmesh(const std::string &navmesh)
|
||||
{
|
||||
XMLNode *xml = file_manager->createXMLTree(navmesh);
|
||||
if (xml->getName() != "navmesh")
|
||||
{
|
||||
Log::error("ArenaGraph", "NavMesh is invalid.");
|
||||
delete xml;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Vec3> all_vertices;
|
||||
for (unsigned int i = 0; i < xml->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
if (xml_node->getName() == "vertices")
|
||||
{
|
||||
for (unsigned int i = 0; i < xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if (!(xml_node_node->getName() == "vertex"))
|
||||
{
|
||||
Log::error("ArenaGraph", "Unsupported type '%s' found"
|
||||
"in '%s' - ignored.",
|
||||
xml_node_node->getName().c_str(), navmesh.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reading vertices
|
||||
float x, y, z;
|
||||
xml_node_node->get("x", &x);
|
||||
xml_node_node->get("y", &y);
|
||||
xml_node_node->get("z", &z);
|
||||
Vec3 p(x, y, z);
|
||||
all_vertices.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (xml_node->getName() == "faces")
|
||||
{
|
||||
for (unsigned int i = 0; i < xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if (xml_node_node->getName() != "face")
|
||||
{
|
||||
Log::error("ArenaGraph", "Unsupported type '%s'"
|
||||
" found in '%s' - ignored.",
|
||||
xml_node_node->getName().c_str(), navmesh.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reading quads
|
||||
std::vector<int> quad_index;
|
||||
std::vector<int> adjacent_quad_index;
|
||||
xml_node_node->get("indices", &quad_index);
|
||||
xml_node_node->get("adjacents", &adjacent_quad_index);
|
||||
if (quad_index.size() != 4)
|
||||
{
|
||||
Log::error("ArenaGraph", "A Node in navmesh is not made"
|
||||
" of quad, will only use the first 4 vertices");
|
||||
}
|
||||
|
||||
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*/);
|
||||
|
||||
ArenaNode* cur_node = getNode(m_all_nodes.size() - 1);
|
||||
cur_node->setAdjacentNodes(adjacent_quad_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete xml;
|
||||
|
||||
} // loadNavmesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ArenaGraph::buildGraph()
|
||||
{
|
||||
const unsigned int n_nodes = getNumNodes();
|
||||
|
||||
m_distance_matrix = std::vector<std::vector<float>>
|
||||
(n_nodes, std::vector<float>(n_nodes, 9999.9f));
|
||||
for (unsigned int i = 0; i < n_nodes; i++)
|
||||
{
|
||||
ArenaNode* cur_node = getNode(i);
|
||||
for (const int& adjacent : cur_node->getAdjacentNodes())
|
||||
{
|
||||
Vec3 diff = getNode(adjacent)->getCenter() - cur_node->getCenter();
|
||||
float distance = diff.length();
|
||||
m_distance_matrix[i][adjacent] = distance;
|
||||
}
|
||||
m_distance_matrix[i][i] = 0.0f;
|
||||
}
|
||||
|
||||
// Allocate and initialise the previous node data structure:
|
||||
m_parent_node = std::vector<std::vector<int16_t>>
|
||||
(n_nodes, std::vector<int16_t>(n_nodes, Graph::UNKNOWN_SECTOR));
|
||||
for (unsigned int i = 0; i < n_nodes; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < n_nodes; j++)
|
||||
{
|
||||
if (i == j || m_distance_matrix[i][j] >= 9899.9f)
|
||||
m_parent_node[i][j] = -1;
|
||||
else
|
||||
m_parent_node[i][j] = i;
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
} // buildGraph
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Dijkstra shortest path computation. It computes the shortest distance from
|
||||
* the specified node 'source' to all other nodes. At the end of the
|
||||
* computation, m_distance_matrix[i][j] stores the shortest path distance from
|
||||
* source to j and m_parent_node[source][j] stores the last vertex visited on
|
||||
* the shortest path from i to j before visiting j. Suppose the shortest path
|
||||
* from i to j is i->......->k->j then m_parent_node[i][j] = k
|
||||
*/
|
||||
void ArenaGraph::computeDijkstra(int source)
|
||||
{
|
||||
// Stores the distance (float) to 'source' from a specified node (int)
|
||||
typedef std::pair<int, float> IndDistPair;
|
||||
|
||||
class Shortest
|
||||
{
|
||||
public:
|
||||
bool operator()(const IndDistPair &p1, const IndDistPair &p2)
|
||||
{
|
||||
return p1.second > p2.second;
|
||||
}
|
||||
};
|
||||
|
||||
std::priority_queue<IndDistPair, std::vector<IndDistPair>, Shortest> queue;
|
||||
IndDistPair begin(source, 0.0f);
|
||||
queue.push(begin);
|
||||
const unsigned int n = getNumNodes();
|
||||
std::vector<bool> visited;
|
||||
visited.resize(n, false);
|
||||
while (!queue.empty())
|
||||
{
|
||||
// Get element with shortest path
|
||||
IndDistPair current = queue.top();
|
||||
queue.pop();
|
||||
int cur_index = current.first;
|
||||
if (visited[cur_index]) continue;
|
||||
visited[cur_index] = true;
|
||||
|
||||
for (const int& adjacent : getNode(cur_index)->getAdjacentNodes())
|
||||
{
|
||||
// Distance already computed, can be ignored
|
||||
if (visited[adjacent]) continue;
|
||||
|
||||
float new_dist =
|
||||
current.second + m_distance_matrix[cur_index][adjacent];
|
||||
if (new_dist < m_distance_matrix[source][adjacent])
|
||||
{
|
||||
m_distance_matrix[source][adjacent] = new_dist;
|
||||
m_parent_node[source][adjacent] = cur_index;
|
||||
}
|
||||
IndDistPair pair(adjacent, new_dist);
|
||||
queue.push(pair);
|
||||
}
|
||||
}
|
||||
} // computeDijkstra
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** THIS FUNCTION IS ONLY USED FOR UNIT-TESTING, to verify that the new
|
||||
* Dijkstra algorithm gives the same results.
|
||||
* computeFloydWarshall() computes the shortest distance between any two
|
||||
* nodes. At the end of the computation, m_distance_matrix[i][j] stores the
|
||||
* shortest path distance from i to j and m_parent_node[i][j] stores the last
|
||||
* vertex visited on the shortest path from i to j before visiting j. Suppose
|
||||
* the shortest path from i to j is i->......->k->j then
|
||||
* m_parent_node[i][j] = k
|
||||
*/
|
||||
void ArenaGraph::computeFloydWarshall()
|
||||
{
|
||||
unsigned int n = getNumNodes();
|
||||
|
||||
for (unsigned int k = 0; k < n; k++)
|
||||
{
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < n; j++)
|
||||
{
|
||||
if ((m_distance_matrix[i][k] + m_distance_matrix[k][j]) <
|
||||
m_distance_matrix[i][j])
|
||||
{
|
||||
m_distance_matrix[i][j] =
|
||||
m_distance_matrix[i][k] + m_distance_matrix[k][j];
|
||||
m_parent_node[i][j] = m_parent_node[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // computeFloydWarshall
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void ArenaGraph::loadGoalNodes(const XMLNode *node)
|
||||
{
|
||||
const XMLNode *check_node = node->getNode("checks");
|
||||
for (unsigned int i = 0; i < check_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *goal = check_node->getNode(i);
|
||||
if (goal->getName() =="goal")
|
||||
{
|
||||
Vec3 p1, p2;
|
||||
bool first_goal = false;
|
||||
goal->get("first_goal", &first_goal);
|
||||
goal->get("p1", &p1);
|
||||
goal->get("p2", &p2);
|
||||
|
||||
int first = Graph::UNKNOWN_SECTOR;
|
||||
findRoadSector(p1, &first, NULL, true);
|
||||
int last = Graph::UNKNOWN_SECTOR;
|
||||
findRoadSector(p2, &last, NULL, true);
|
||||
|
||||
first_goal ? m_blue_node.insert(first) : m_red_node.insert(first);
|
||||
first_goal ? m_blue_node.insert(last) : m_red_node.insert(last);
|
||||
while (first != last)
|
||||
{
|
||||
// Find all the nodes which connect the two points of
|
||||
// goal, notice: only work if it's a straight line
|
||||
first = getNextNode(first, last);
|
||||
first_goal ? m_blue_node.insert(first) :
|
||||
m_red_node.insert(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // loadGoalNodes
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ArenaGraph::setNearbyNodesOfAllNodes()
|
||||
{
|
||||
// Only save the nearby 8 nodes
|
||||
const unsigned int try_count = 8;
|
||||
for (unsigned int i = 0; i < getNumNodes(); i++)
|
||||
{
|
||||
// Get the distance to all nodes at i
|
||||
ArenaNode* cur_node = getNode(i);
|
||||
std::vector<int> nearby_nodes;
|
||||
std::vector<float> dist = m_distance_matrix[i];
|
||||
|
||||
// Skip the same node
|
||||
dist[i] = 999999.0f;
|
||||
for (unsigned int j = 0; j < try_count; j++)
|
||||
{
|
||||
std::vector<float>::iterator it =
|
||||
std::min_element(dist.begin(), dist.end());
|
||||
const int pos = it - dist.begin();
|
||||
nearby_nodes.push_back(pos);
|
||||
dist[pos] = 999999.0f;
|
||||
}
|
||||
cur_node->setNearbyNodes(nearby_nodes);
|
||||
}
|
||||
|
||||
} // setNearbyNodesOfAllNodes
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determines the full path from 'from' to 'to' and returns it in a
|
||||
* std::vector (in reverse order). Used only for unit testing.
|
||||
*/
|
||||
std::vector<int16_t> ArenaGraph::getPathFromTo(int from, int to,
|
||||
const std::vector< std::vector< int16_t > >& parent_node)
|
||||
{
|
||||
std::vector<int16_t> path;
|
||||
path.push_back(to);
|
||||
while(from!=to)
|
||||
{
|
||||
to = parent_node[from][to];
|
||||
path.push_back(to);
|
||||
}
|
||||
return path;
|
||||
} // getPathFromTo
|
||||
|
||||
// ============================================================================
|
||||
/** Unit testing for arena graph distance and parent node computation.
|
||||
* Instead of using hand-tuned test cases we use the tested, verified and
|
||||
* easier to understand Floyd-Warshall algorithm to compute the distances,
|
||||
* and check if the (significanty faster) Dijkstra algorithm gives the same
|
||||
* results. For now we use the cave mesh as test case.
|
||||
*/
|
||||
void ArenaGraph::unitTesting()
|
||||
{
|
||||
Track *track = track_manager->getTrack("cave");
|
||||
std::string navmesh_file_name=track->getTrackFile("navmesh.xml");
|
||||
|
||||
double s = StkTime::getRealTime();
|
||||
ArenaGraph* ag = new ArenaGraph(navmesh_file_name);
|
||||
double e = StkTime::getRealTime();
|
||||
Log::error("Time", "Dijkstra %lf", e-s);
|
||||
|
||||
// Save the Dijkstra results
|
||||
std::vector< std::vector< float > > distance_matrix = ag->m_distance_matrix;
|
||||
std::vector< std::vector< int16_t > > parent_node = ag->m_parent_node;
|
||||
ag->buildGraph();
|
||||
|
||||
// Now compute results with Floyd-Warshall
|
||||
s = StkTime::getRealTime();
|
||||
ag->computeFloydWarshall();
|
||||
e = StkTime::getRealTime();
|
||||
Log::error("Time", "Floyd-Warshall %lf", e-s);
|
||||
|
||||
int error_count = 0;
|
||||
for(unsigned int i=0; i<ag->m_distance_matrix.size(); i++)
|
||||
{
|
||||
for(unsigned int j=0; j<ag->m_distance_matrix[i].size(); j++)
|
||||
{
|
||||
if(ag->m_distance_matrix[i][j] - distance_matrix[i][j] > 0.001f)
|
||||
{
|
||||
Log::error("ArenaGraph",
|
||||
"Incorrect distance %d, %d: Dijkstra: %f F.W.: %f",
|
||||
i, j, distance_matrix[i][j], ag->m_distance_matrix[i][j]);
|
||||
error_count++;
|
||||
} // if distance is too different
|
||||
|
||||
// Unortunately it happens frequently that there are different
|
||||
// shortest path with the same length. And Dijkstra might find
|
||||
// a different path then Floyd-Warshall. So the test for parent
|
||||
// polygon often results in false positives, so it is disabled,
|
||||
// but I leave the code in place in case it is useful for some
|
||||
// debugging in the feature
|
||||
#undef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
|
||||
#ifdef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
|
||||
if(ag->m_parent_node[i][j] != parent_node[i][j])
|
||||
{
|
||||
error_count++;
|
||||
std::vector<int16_t> dijkstra_path = getPathFromTo(i, j, parent_node);
|
||||
std::vector<int16_t> floyd_path = getPathFromTo(i, j, ag->m_parent_node);
|
||||
if(dijkstra_path.size()!=floyd_path.size())
|
||||
{
|
||||
Log::error("ArenaGraph",
|
||||
"Incorrect path length %d, %d: Dijkstra: %d F.W.: %d",
|
||||
i, j, parent_node[i][j], ag->m_parent_node[i][j]);
|
||||
continue;
|
||||
}
|
||||
Log::error("ArenaGraph", "Path problems from %d to %d:",
|
||||
i, j);
|
||||
for (unsigned k = 0; k < dijkstra_path.size(); k++)
|
||||
{
|
||||
if(dijkstra_path[k]!=floyd_path[k])
|
||||
Log::error("ArenaGraph", "%d/%d dijkstra: %d floyd %d",
|
||||
k, dijkstra_path.size(), dijkstra_path[k],
|
||||
floyd_path[k]);
|
||||
} // for k<dijkstra_path.size()
|
||||
|
||||
} // if dijkstra parent_node != floyd parent node
|
||||
#endif
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
delete ag;
|
||||
|
||||
} // unitTesting
|
||||
100
src/tracks/arena_graph.hpp
Normal file
100
src/tracks/arena_graph.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// 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_ARENA_GRAPH_HPP
|
||||
#define HEADER_ARENA_GRAPH_HPP
|
||||
|
||||
#include "tracks/graph.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
class ArenaNode;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \brief A graph made from navmesh
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class ArenaGraph : public Graph
|
||||
{
|
||||
private:
|
||||
/** The actual graph data structure, it is an adjacency matrix. */
|
||||
std::vector<std::vector<float>> m_distance_matrix;
|
||||
|
||||
/** The matrix that is used to store computed shortest paths. */
|
||||
std::vector<std::vector<int16_t>> m_parent_node;
|
||||
|
||||
/** Used in soccer mode to colorize the goal lines in minimap. */
|
||||
std::set<int> m_red_node;
|
||||
|
||||
std::set<int> m_blue_node;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void loadGoalNodes(const XMLNode *node);
|
||||
// ------------------------------------------------------------------------
|
||||
void loadNavmesh(const std::string &navmesh);
|
||||
// ------------------------------------------------------------------------
|
||||
void buildGraph();
|
||||
// ------------------------------------------------------------------------
|
||||
void setNearbyNodesOfAllNodes();
|
||||
// ------------------------------------------------------------------------
|
||||
void computeDijkstra(int n);
|
||||
// ------------------------------------------------------------------------
|
||||
void computeFloydWarshall();
|
||||
// ------------------------------------------------------------------------
|
||||
static std::vector<int16_t> getPathFromTo(int from, int to,
|
||||
const std::vector< std::vector< int16_t > >& parent_node);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool hasLapLine() const OVERRIDE { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void differentNodeColor(int n, video::SColor* c) const OVERRIDE;
|
||||
|
||||
public:
|
||||
static ArenaGraph* get() { return dynamic_cast<ArenaGraph*>(m_graph); }
|
||||
// ------------------------------------------------------------------------
|
||||
static void unitTesting();
|
||||
// ------------------------------------------------------------------------
|
||||
ArenaGraph(const std::string &navmesh, const XMLNode *node = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~ArenaGraph() {}
|
||||
// ------------------------------------------------------------------------
|
||||
ArenaNode* getNode(unsigned int i) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the next node on the shortest path from i to j.
|
||||
* Note: m_parent_node[j][i] contains the parent of i on path from j to i,
|
||||
* which is the next node on the path from i to j (undirected graph)
|
||||
*/
|
||||
int getNextNode(int i, int j) const
|
||||
{
|
||||
if (i == Graph::UNKNOWN_SECTOR || j == Graph::UNKNOWN_SECTOR)
|
||||
return Graph::UNKNOWN_SECTOR;
|
||||
return (int)(m_parent_node[j][i]);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the distance between any two nodes */
|
||||
float getDistance(int from, int to) const
|
||||
{
|
||||
if (from == Graph::UNKNOWN_SECTOR || to == Graph::UNKNOWN_SECTOR)
|
||||
return 99999.0f;
|
||||
return m_distance_matrix[from][to];
|
||||
}
|
||||
|
||||
}; // ArenaGraph
|
||||
|
||||
#endif
|
||||
45
src/tracks/arena_node.cpp
Normal file
45
src/tracks/arena_node.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// 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 "tracks/arena_node.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
ArenaNode::ArenaNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
|
||||
const Vec3 &p3, const Vec3 &normal,
|
||||
unsigned int node_index)
|
||||
: Quad(p0, p1, p2, p3, normal, node_index)
|
||||
{
|
||||
Vec3 lower_center = (p0 + p1) * 0.5f;
|
||||
Vec3 upper_center = (p2 + p3) * 0.5f;
|
||||
|
||||
m_line = core::line3df(lower_center.toIrrVector(),
|
||||
upper_center.toIrrVector());
|
||||
} // ArenaNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the square of the distance between the given point and any point
|
||||
* on the 'centre' line, i.e. the finite line from the middle point of the
|
||||
* lower end of the node to the middle point of the upper end of the node
|
||||
* which belongs to this node.
|
||||
* \param xyz The point for which the distance to the line is computed.
|
||||
*/
|
||||
float ArenaNode::getDistance2FromPoint(const Vec3 &xyz) const
|
||||
{
|
||||
core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector());
|
||||
return (closest-xyz.toIrrVector()).getLengthSQ();
|
||||
} // getDistance2FromPoint
|
||||
68
src/tracks/arena_node.hpp
Normal file
68
src/tracks/arena_node.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// 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_ARENA_NODE_HPP
|
||||
#define HEADER_ARENA_NODE_HPP
|
||||
|
||||
#include "tracks/quad.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class ArenaNode : public Quad
|
||||
{
|
||||
private:
|
||||
core::line3df m_line;
|
||||
|
||||
std::vector<int> m_adjacent_nodes;
|
||||
|
||||
std::vector<int> m_nearby_nodes;
|
||||
|
||||
public:
|
||||
ArenaNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
const Vec3 &normal, unsigned int node_index);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~ArenaNode() {}
|
||||
// ------------------------------------------------------------------------
|
||||
const std::vector<int>& getAdjacentNodes() { return m_adjacent_nodes; }
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<int>* getNearbyNodes() { return &m_nearby_nodes; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setAdjacentNodes(const std::vector<int>& nodes)
|
||||
{
|
||||
m_adjacent_nodes = nodes;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setNearbyNodes(const std::vector<int>& nodes)
|
||||
{
|
||||
m_nearby_nodes = nodes;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the quad lies near the edge, which means it doesn't
|
||||
* have 4 adjacent quads.
|
||||
*/
|
||||
bool isNearEdge() const { return m_adjacent_nodes.size() != 4; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
47
src/tracks/arena_node_3d.hpp
Normal file
47
src/tracks/arena_node_3d.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// 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_ARENA_NODE_3D_HPP
|
||||
#define HEADER_ARENA_NODE_3D_HPP
|
||||
|
||||
#include "tracks/arena_node.hpp"
|
||||
#include "tracks/bounding_box_3d.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class ArenaNode3D : public ArenaNode,
|
||||
public BoundingBox3D
|
||||
{
|
||||
public:
|
||||
ArenaNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
const Vec3 &normal, unsigned int node_index)
|
||||
: ArenaNode(p0, p1, p2, p3, normal, node_index),
|
||||
BoundingBox3D(p0, p1, p2, p3, normal) {}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool pointInside(const Vec3& p,
|
||||
bool ignore_vertical = false) const OVERRIDE
|
||||
{
|
||||
return BoundingBox3D::pointInside(p);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool is3DQuad() const OVERRIDE { return true; }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,473 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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, B
|
||||
|
||||
#include "tracks/battle_graph.hpp"
|
||||
|
||||
#include <IMesh.h>
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IMeshSceneNode.h>
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "items/item_manager.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/navmesh.hpp"
|
||||
#include "tracks/quad.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
const int BattleGraph::UNKNOWN_POLY = -1;
|
||||
BattleGraph * BattleGraph::m_battle_graph = NULL;
|
||||
|
||||
/** Constructor, Creates a navmesh, builds a graph from the navmesh and
|
||||
* then runs shortest path algorithm to find and store paths to be used
|
||||
* by the AI. */
|
||||
BattleGraph::BattleGraph(const std::string &navmesh_file_name,
|
||||
const XMLNode *node)
|
||||
{
|
||||
m_items_on_graph.clear();
|
||||
|
||||
NavMesh::create(navmesh_file_name);
|
||||
m_navmesh_file = navmesh_file_name;
|
||||
buildGraph(NavMesh::get());
|
||||
|
||||
// Compute shortest distance from all nodes
|
||||
for(unsigned int i=0; i < NavMesh::get()->getNumberOfQuads(); i++)
|
||||
computeDijkstra(i);
|
||||
|
||||
sortNearbyQuad();
|
||||
if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
loadGoalNodes(node);
|
||||
|
||||
} // BattleGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Destructor, destroys NavMesh and the debug mesh if it exists */
|
||||
BattleGraph::~BattleGraph(void)
|
||||
{
|
||||
NavMesh::destroy();
|
||||
|
||||
if(UserConfigParams::m_track_debug)
|
||||
cleanupDebugMesh();
|
||||
GraphStructure::destroyRTT();
|
||||
} // ~BattleGraph
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Builds a graph from an existing NavMesh. The graph is stored as an
|
||||
* adjacency matrix. */
|
||||
void BattleGraph::buildGraph(NavMesh* navmesh)
|
||||
{
|
||||
const unsigned int n_quads = navmesh->getNumberOfQuads();
|
||||
|
||||
m_distance_matrix = std::vector<std::vector<float>>
|
||||
(n_quads, std::vector<float>(n_quads, 9999.9f));
|
||||
for(unsigned int i = 0; i < n_quads; i++)
|
||||
{
|
||||
const Quad& cur_quad = navmesh->getQuad(i);
|
||||
for (const int& adjacent : navmesh->getAdjacentQuads(i))
|
||||
{
|
||||
Vec3 diff = navmesh->getQuad(adjacent).getCenter()
|
||||
- cur_quad.getCenter();
|
||||
float distance = diff.length();
|
||||
m_distance_matrix[i][adjacent] = distance;
|
||||
}
|
||||
m_distance_matrix[i][i] = 0.0f;
|
||||
}
|
||||
|
||||
// Allocate and initialise the previous node data structure:
|
||||
m_parent_poly = std::vector<std::vector<int>>
|
||||
(n_quads, std::vector<int>(n_quads, BattleGraph::UNKNOWN_POLY));
|
||||
for (unsigned int i = 0; i < n_quads; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < n_quads; j++)
|
||||
{
|
||||
if(i == j || m_distance_matrix[i][j] >= 9899.9f)
|
||||
m_parent_poly[i][j] = -1;
|
||||
else
|
||||
m_parent_poly[i][j] = i;
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
} // buildGraph
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Dijkstra shortest path computation. It computes the shortest distance from
|
||||
* the specified node 'source' to all other nodes. At the end of the
|
||||
* computation, m_distance_matrix[i][j] stores the shortest path distance from
|
||||
* source to j and m_parent_poly[source][j] stores the last vertex visited on
|
||||
* the shortest path from i to j before visiting j. Suppose the shortest path
|
||||
* from i to j is i->......->k->j then m_parent_poly[i][j] = k
|
||||
*/
|
||||
void BattleGraph::computeDijkstra(int source)
|
||||
{
|
||||
// Stores the distance (float) to 'source' from a specified node (int)
|
||||
typedef std::pair<int, float> IndDistPair;
|
||||
|
||||
class Shortest
|
||||
{
|
||||
public:
|
||||
bool operator()(const IndDistPair &p1, const IndDistPair &p2)
|
||||
{
|
||||
return p1.second > p2.second;
|
||||
}
|
||||
};
|
||||
std::priority_queue<IndDistPair, std::vector<IndDistPair>, Shortest> queue;
|
||||
IndDistPair begin(source, 0.0f);
|
||||
queue.push(begin);
|
||||
const unsigned int n=getNumNodes();
|
||||
std::vector<bool> visited;
|
||||
visited.resize(n, false);
|
||||
NavMesh *navmesh = NavMesh::get();
|
||||
while(!queue.empty())
|
||||
{
|
||||
// Get element with shortest path
|
||||
IndDistPair current = queue.top();
|
||||
queue.pop();
|
||||
int cur_index = current.first;
|
||||
if(visited[cur_index]) continue;
|
||||
visited[cur_index] = true;
|
||||
|
||||
for (const int& adjacent : navmesh->getAdjacentQuads(cur_index))
|
||||
{
|
||||
// Distance already computed, can be ignored
|
||||
if(visited[adjacent]) continue;
|
||||
|
||||
float new_dist = current.second + m_distance_matrix[cur_index][adjacent];
|
||||
if(new_dist < m_distance_matrix[source][adjacent])
|
||||
{
|
||||
m_distance_matrix[source][adjacent] = new_dist;
|
||||
m_parent_poly[source][adjacent] = cur_index;
|
||||
}
|
||||
IndDistPair pair(adjacent, new_dist);
|
||||
queue.push(pair);
|
||||
}
|
||||
}
|
||||
} // computeDijkstra
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** THIS FUNCTION IS ONLY USED FOR UNIT-TESTING, to verify that the new
|
||||
* Dijkstra algorithm gives the same results.
|
||||
* computeFloydWarshall() computes the shortest distance between any two
|
||||
* nodes. At the end of the computation, m_distance_matrix[i][j] stores the
|
||||
* shortest path distance from i to j and m_parent_poly[i][j] stores the last
|
||||
* vertex visited on the shortest path from i to j before visiting j. Suppose
|
||||
* the shortest path from i to j is i->......->k->j then
|
||||
* m_parent_poly[i][j] = k
|
||||
*/
|
||||
void BattleGraph::computeFloydWarshall()
|
||||
{
|
||||
unsigned int n = getNumNodes();
|
||||
|
||||
// initialize m_parent_poly with unknown_poly so that if no path is found b/w i and j
|
||||
// then m_parent_poly[i][j] = -1 (UNKNOWN_POLY)
|
||||
// AI must check this
|
||||
m_parent_poly = std::vector< std::vector<int> > (n, std::vector<int>(n,BattleGraph::UNKNOWN_POLY));
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
{
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if(i == j || m_distance_matrix[i][j]>=9899.9f) m_parent_poly[i][j]=-1;
|
||||
else m_parent_poly[i][j] = i;
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int k=0; k<n; k++)
|
||||
{
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
{
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if( (m_distance_matrix[i][k] + m_distance_matrix[k][j]) < m_distance_matrix[i][j])
|
||||
{
|
||||
m_distance_matrix[i][j] = m_distance_matrix[i][k] + m_distance_matrix[k][j];
|
||||
m_parent_poly[i][j] = m_parent_poly[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // computeFloydWarshall
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Maps items on battle graph */
|
||||
void BattleGraph::findItemsOnGraphNodes()
|
||||
{
|
||||
const ItemManager* item_manager = ItemManager::get();
|
||||
unsigned int item_count = item_manager->getNumberOfItems();
|
||||
|
||||
for (unsigned int i = 0; i < item_count; ++i)
|
||||
{
|
||||
const Item* item = item_manager->getItem(i);
|
||||
Vec3 xyz = item->getXYZ();
|
||||
int polygon = BattleGraph::UNKNOWN_POLY;
|
||||
|
||||
for (unsigned int j = 0; j < this->getNumNodes(); ++j)
|
||||
{
|
||||
if (getQuadOfNode(j).pointInQuad(xyz, false))
|
||||
polygon = j;
|
||||
}
|
||||
|
||||
if (polygon != BattleGraph::UNKNOWN_POLY)
|
||||
{
|
||||
m_items_on_graph.push_back(std::make_pair(item, polygon));
|
||||
Log::debug("BattleGraph","item number %d is on polygon %d", i, polygon);
|
||||
}
|
||||
else
|
||||
Log::debug("BattleGraph","Can't map item number %d with a suitable polygon", i);
|
||||
}
|
||||
} // findItemsOnGraphNodes
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
int BattleGraph::pointToNode(const int cur_node,
|
||||
const Vec3& cur_point,
|
||||
bool ignore_vertical) const
|
||||
{
|
||||
if (cur_node == BattleGraph::UNKNOWN_POLY)
|
||||
{
|
||||
// Try all nodes in the battle graph
|
||||
for (unsigned int node = 0; node < this->getNumNodes(); node++)
|
||||
{
|
||||
const Quad& quad = this->getQuadOfNode(node);
|
||||
if (quad.pointInQuad(cur_point, ignore_vertical))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the point is still on the same node
|
||||
const Quad& cur_quad = this->getQuadOfNode(cur_node);
|
||||
if (cur_quad.pointInQuad(cur_point, ignore_vertical)) return cur_node;
|
||||
|
||||
// If not then check all nearby quads (8 quads)
|
||||
// Skip the same node
|
||||
assert(cur_node == m_nearby_quads[cur_node][0]);
|
||||
for (unsigned int i = 1; i < m_nearby_quads[0].size(); i++)
|
||||
{
|
||||
const int test_node = m_nearby_quads[cur_node][i];
|
||||
const Quad& quad = this->getQuadOfNode(test_node);
|
||||
if (quad.pointInQuad(cur_point, ignore_vertical))
|
||||
{
|
||||
return test_node;
|
||||
}
|
||||
}
|
||||
|
||||
// Current node is still unkown:
|
||||
// Calculated distance from saved node to current position,
|
||||
// if it's close enough than use the saved node anyway, it
|
||||
// may happen when the kart stays on the edge of obstacles
|
||||
Vec3 diff = (cur_quad.getCenter() - cur_point);
|
||||
float dist = diff.length();
|
||||
|
||||
if (dist < 3.0f)
|
||||
return cur_node;
|
||||
}
|
||||
|
||||
return BattleGraph::UNKNOWN_POLY;
|
||||
} // pointToNode
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
const bool BattleGraph::differentNodeColor(int n, NodeColor* c) const
|
||||
{
|
||||
std::set<int>::iterator it;
|
||||
it = m_red_node.find(n);
|
||||
if (it != m_red_node.end())
|
||||
{
|
||||
*c = COLOR_RED;
|
||||
return true;
|
||||
}
|
||||
|
||||
it = m_blue_node.find(n);
|
||||
if (it != m_blue_node.end())
|
||||
{
|
||||
*c = COLOR_BLUE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} // differentNodeColor
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void BattleGraph::loadGoalNodes(const XMLNode *node)
|
||||
{
|
||||
m_red_node.clear();
|
||||
m_blue_node.clear();
|
||||
|
||||
const XMLNode *check_node = node->getNode("checks");
|
||||
for (unsigned int i = 0; i < check_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *goal = check_node->getNode(i);
|
||||
if (goal->getName() =="goal")
|
||||
{
|
||||
Vec3 p1, p2;
|
||||
bool first_goal = false;
|
||||
goal->get("first_goal", &first_goal);
|
||||
goal->get("p1", &p1);
|
||||
goal->get("p2", &p2);
|
||||
|
||||
int first = pointToNode(/*cur_node*/-1, p1, true);
|
||||
int last = pointToNode(/*cur_node*/-1, p2, true);
|
||||
|
||||
first_goal ? m_blue_node.insert(first) : m_red_node.insert(first);
|
||||
first_goal ? m_blue_node.insert(last) : m_red_node.insert(last);
|
||||
while (first != last)
|
||||
{
|
||||
// Find all the nodes which connect the two points of
|
||||
// goal, notice: only work if it's a straight line
|
||||
first = getNextShortestPathPoly(first, last);
|
||||
first_goal ? m_blue_node.insert(first) :
|
||||
m_red_node.insert(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // loadGoalNodes
|
||||
|
||||
// ============================================================================
|
||||
/** Unit testing for battle graph distance and parent node computation.
|
||||
* Instead of using hand-tuned test cases we use the tested, verified and
|
||||
* easier to understand Floyd-Warshall algorithm to compute the distances,
|
||||
* and check if the (significanty faster) Dijkstra algorithm gives the same
|
||||
* results. For now we use the cave mesh as test case.
|
||||
*/
|
||||
void BattleGraph::unitTesting()
|
||||
{
|
||||
Track *track = track_manager->getTrack("cave");
|
||||
std::string navmesh_file_name=track->getTrackFile("navmesh.xml");
|
||||
|
||||
double s = StkTime::getRealTime();
|
||||
BattleGraph *bg = new BattleGraph(navmesh_file_name);
|
||||
double e = StkTime::getRealTime();
|
||||
Log::error("Time", "Dijkstra %lf", e-s);
|
||||
|
||||
// Save the Dijkstra results
|
||||
std::vector< std::vector< float > > distance_matrix = bg->m_distance_matrix;
|
||||
std::vector< std::vector< int > > parent_poly = bg->m_parent_poly;
|
||||
bg->buildGraph(NavMesh::get());
|
||||
|
||||
// Now compute results with Floyd-Warshall
|
||||
s = StkTime::getRealTime();
|
||||
bg->computeFloydWarshall();
|
||||
e = StkTime::getRealTime();
|
||||
Log::error("Time", "Floyd-Warshall %lf", e-s);
|
||||
|
||||
int error_count = 0;
|
||||
for(unsigned int i=0; i<bg->m_distance_matrix.size(); i++)
|
||||
{
|
||||
for(unsigned int j=0; j<bg->m_distance_matrix[i].size(); j++)
|
||||
{
|
||||
if(bg->m_distance_matrix[i][j] - distance_matrix[i][j] > 0.001f)
|
||||
{
|
||||
Log::error("BattleGraph",
|
||||
"Incorrect distance %d, %d: Dijkstra: %f F.W.: %f",
|
||||
i, j, distance_matrix[i][j], bg->m_distance_matrix[i][j]);
|
||||
error_count++;
|
||||
} // if distance is too different
|
||||
|
||||
// Unortunately it happens frequently that there are different
|
||||
// shortest path with the same length. And Dijkstra might find
|
||||
// a different path then Floyd-Warshall. So the test for parent
|
||||
// polygon often results in false positives, so it is disabled,
|
||||
// but I leave the code in place in case it is useful for some
|
||||
// debugging in the feature
|
||||
#undef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
|
||||
#ifdef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
|
||||
if(bg->m_parent_poly[i][j] != parent_poly[i][j])
|
||||
{
|
||||
error_count++;
|
||||
std::vector<int> dijkstra_path = getPathFromTo(i, j, parent_poly);
|
||||
std::vector<int> floyd_path = getPathFromTo(i, j, bg->m_parent_poly);
|
||||
if(dijkstra_path.size()!=floyd_path.size())
|
||||
{
|
||||
Log::error("BattleGraph",
|
||||
"Incorrect path length %d, %d: Dijkstra: %d F.W.: %d",
|
||||
i, j, parent_poly[i][j], bg->m_parent_poly[i][j]);
|
||||
continue;
|
||||
}
|
||||
Log::error("BattleGraph", "Path problems from %d to %d:",
|
||||
i, j);
|
||||
for (unsigned k = 0; k < dijkstra_path.size(); k++)
|
||||
{
|
||||
if(dijkstra_path[k]!=floyd_path[k])
|
||||
Log::error("BattleGraph", "%d/%d dijkstra: %d floyd %d",
|
||||
k, dijkstra_path.size(), dijkstra_path[k],
|
||||
floyd_path[k]);
|
||||
} // for k<dijkstra_path.size()
|
||||
|
||||
} // if dijkstra parent_poly != floyd parent poly
|
||||
#endif
|
||||
} // for j
|
||||
} // for i
|
||||
} // unitTesting
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determines the full path from 'from' to 'to' and returns it in a
|
||||
* std::vector (in reverse order). Used only for unit testing.
|
||||
*/
|
||||
std::vector<int> BattleGraph::getPathFromTo(int from, int to,
|
||||
const std::vector< std::vector< int > > parent_poly)
|
||||
{
|
||||
std::vector<int> path;
|
||||
path.push_back(to);
|
||||
while(from!=to)
|
||||
{
|
||||
to = parent_poly[from][to];
|
||||
path.push_back(to);
|
||||
}
|
||||
return path;
|
||||
} // getPathFromTo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void BattleGraph::sortNearbyQuad()
|
||||
{
|
||||
// Only try the nearby 8 quads
|
||||
const unsigned int n = 8;
|
||||
m_nearby_quads = std::vector< std::vector<int> >
|
||||
(this->getNumNodes(), std::vector<int>(n, BattleGraph::UNKNOWN_POLY));
|
||||
|
||||
for (unsigned int i = 0; i < this->getNumNodes(); i++)
|
||||
{
|
||||
// Get the distance to all nodes at i
|
||||
std::vector<float> dist = m_distance_matrix[i];
|
||||
for (unsigned int j = 0; j < n; j++)
|
||||
{
|
||||
std::vector<float>::iterator it =
|
||||
std::min_element(dist.begin(), dist.end());
|
||||
const int pos = it - dist.begin();
|
||||
m_nearby_quads[i][j] = pos;
|
||||
dist[pos] = 999999.0f;
|
||||
}
|
||||
}
|
||||
} // sortNearbyQuad
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void BattleGraph::set3DVerticesOfGraph(int i, video::S3DVertex *v,
|
||||
const video::SColor &color) const
|
||||
{
|
||||
NavMesh::get()->getQuad(i).getVertices(v, color);
|
||||
} // set3DVerticesOfGraph
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
const bool BattleGraph::isNodeInvisible(int n) const
|
||||
{
|
||||
return NavMesh::get()->getQuad(n).isInvisible();
|
||||
} // isNodeInvisible
|
||||
@@ -1,166 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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, B
|
||||
|
||||
#ifndef HEADER_BATTLE_GRAPH_HPP
|
||||
#define HEADER_BATTLE_GRAPH_HPP
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "tracks/graph_structure.hpp"
|
||||
#include "tracks/navmesh.hpp"
|
||||
|
||||
class Item;
|
||||
class ItemManager;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*
|
||||
* \brief This class stores a graph constructed from the navigatoin mesh.
|
||||
* It uses a 'simplified singleton' design pattern: it has a static create
|
||||
* function to create exactly one instance, a destroy function, and a get
|
||||
* function (that does not have the side effect of the 'normal singleton'
|
||||
* design pattern to create an instance).
|
||||
\ingroup tracks
|
||||
*/
|
||||
class BattleGraph : public GraphStructure
|
||||
{
|
||||
|
||||
private:
|
||||
static BattleGraph *m_battle_graph;
|
||||
|
||||
/** The actual graph data structure, it is an adjacency matrix */
|
||||
std::vector< std::vector< float > > m_distance_matrix;
|
||||
/** The matrix that is used to store computed shortest paths */
|
||||
std::vector< std::vector< int > > m_parent_poly;
|
||||
|
||||
std::vector< std::vector< int > > m_nearby_quads;
|
||||
|
||||
/** Stores the name of the file containing the NavMesh data */
|
||||
std::string m_navmesh_file;
|
||||
|
||||
std::vector< std::pair<const Item*, int> > m_items_on_graph;
|
||||
|
||||
std::set<int> m_red_node;
|
||||
std::set<int> m_blue_node;
|
||||
|
||||
void buildGraph(NavMesh*);
|
||||
void computeFloydWarshall();
|
||||
void loadGoalNodes(const XMLNode *node);
|
||||
void sortNearbyQuad();
|
||||
|
||||
BattleGraph(const std::string &navmesh_file_name, const XMLNode *node=NULL);
|
||||
~BattleGraph(void);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v,
|
||||
const video::SColor &color) const;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const
|
||||
{ NavMesh::get()->getBoundingBox(min, max); }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const bool isNodeInvisible(int n) const;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const bool hasLapLine() const
|
||||
{ return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const bool differentNodeColor(int n, NodeColor* c) const;
|
||||
void computeDijkstra(int n);
|
||||
static std::vector<int> getPathFromTo(int from, int to,
|
||||
const std::vector< std::vector< int > > parent_poly);
|
||||
|
||||
public:
|
||||
static const int UNKNOWN_POLY;
|
||||
|
||||
void findItemsOnGraphNodes();
|
||||
// ----------------------------------------------------------------------
|
||||
int pointToNode(const int cur_node,
|
||||
const Vec3& cur_point,
|
||||
bool ignore_vertical) const;
|
||||
// ------------------------------------------------------------------------
|
||||
static void unitTesting();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the one instance of this object. */
|
||||
static BattleGraph *get() { return m_battle_graph; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Asserts that no BattleGraph instance exists. Then
|
||||
* creates a BattleGraph instance. */
|
||||
static void create(const std::string &navmesh_file_name,
|
||||
const XMLNode *node)
|
||||
{
|
||||
assert(m_battle_graph==NULL);
|
||||
m_battle_graph = new BattleGraph(navmesh_file_name, node);
|
||||
} // create
|
||||
// ------------------------------------------------------------------------
|
||||
/** Cleans up the BattleGraph instance if it exists */
|
||||
static void destroy()
|
||||
{
|
||||
if(m_battle_graph)
|
||||
{
|
||||
delete m_battle_graph;
|
||||
m_battle_graph = NULL;
|
||||
}
|
||||
} // destroy
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of nodes in the BattleGraph (equal to the number
|
||||
* of quads in the NavMesh
|
||||
*/
|
||||
virtual const unsigned int getNumNodes() const
|
||||
{ return NavMesh::get()->getNumberOfQuads(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the distance between any two nodes */
|
||||
float getDistance(int from, int to) const
|
||||
{
|
||||
if (from == BattleGraph::UNKNOWN_POLY ||
|
||||
to == BattleGraph::UNKNOWN_POLY)
|
||||
return 0.0f;
|
||||
return m_distance_matrix[from][to];
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the next polygon on the shortest path from i to j.
|
||||
* Note: m_parent_poly[j][i] contains the parent of i on path from j to i,
|
||||
* which is the next node on the path from i to j (undirected graph)
|
||||
*/
|
||||
int getNextShortestPathPoly(int i, int j) const
|
||||
{
|
||||
if (i == BattleGraph::UNKNOWN_POLY || j == BattleGraph::UNKNOWN_POLY)
|
||||
return BattleGraph::UNKNOWN_POLY;
|
||||
return m_parent_poly[j][i];
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<std::pair<const Item*, int>>& getItemList()
|
||||
{ return m_items_on_graph; }
|
||||
// ------------------------------------------------------------------------
|
||||
void insertItems(Item* item, int polygon)
|
||||
{ m_items_on_graph.push_back(std::make_pair(item, polygon)); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the quad that belongs to a node. */
|
||||
const Quad& getQuadOfNode(unsigned int n) const
|
||||
{ return NavMesh::get()->getQuad(n); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the quad lies near the edge, which means it doesn't
|
||||
* have 4 adjacent quads.
|
||||
*/
|
||||
bool isNearEdge(unsigned int n) const
|
||||
{ return NavMesh::get()->getAdjacentQuads(n).size() != 4; }
|
||||
// ------------------------------------------------------------------------
|
||||
}; //BattleGraph
|
||||
|
||||
#endif
|
||||
84
src/tracks/bounding_box_3d.hpp
Normal file
84
src/tracks/bounding_box_3d.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// 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_NODE_BOUNDING_BOX_3D_HPP
|
||||
#define HEADER_NODE_BOUNDING_BOX_3D_HPP
|
||||
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class BoundingBox3D
|
||||
{
|
||||
private:
|
||||
/** A 3D box using to check if a point lies inside a quad.
|
||||
*/
|
||||
Vec3 m_box_faces[6][4];
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
BoundingBox3D(const Vec3& p0, const Vec3& p1, const Vec3& p2,
|
||||
const Vec3& p3, const Vec3& normal)
|
||||
{
|
||||
// Compute the node bounding box used by pointInside
|
||||
Vec3 box_corners[8];
|
||||
float box_high = 5.0f;
|
||||
float box_low = 1.0f;
|
||||
box_corners[0] = p0 + box_high * normal;
|
||||
box_corners[1] = p1 + box_high * normal;
|
||||
box_corners[2] = p2 + box_high * normal;
|
||||
box_corners[3] = p3 + box_high * normal;
|
||||
box_corners[4] = p0 - box_low * normal;
|
||||
box_corners[5] = p1 - box_low * normal;
|
||||
box_corners[6] = p2 - box_low * normal;
|
||||
box_corners[7] = p3 - box_low * normal;
|
||||
|
||||
Vec3 box_faces[6][4] =
|
||||
{
|
||||
{ box_corners[0], box_corners[1], box_corners[2], box_corners[3] },
|
||||
{ box_corners[3], box_corners[2], box_corners[6], box_corners[7] },
|
||||
{ box_corners[7], box_corners[6], box_corners[5], box_corners[4] },
|
||||
{ box_corners[1], box_corners[0], box_corners[4], box_corners[5] },
|
||||
{ box_corners[4], box_corners[0], box_corners[3], box_corners[7] },
|
||||
{ box_corners[1], box_corners[5], box_corners[6], box_corners[2] }
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < 6 ; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < 4; j++)
|
||||
m_box_faces[i][j] = box_faces[i][j];
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
bool pointInside(const Vec3& p, bool ignore_vertical = false) const
|
||||
{
|
||||
float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1],
|
||||
m_box_faces[0][2]);
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1],
|
||||
m_box_faces[i][2]) < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -28,7 +28,8 @@
|
||||
#include "tracks/check_lap.hpp"
|
||||
#include "tracks/check_line.hpp"
|
||||
#include "tracks/check_structure.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
CheckManager *CheckManager::m_check_manager = NULL;
|
||||
|
||||
@@ -84,7 +85,7 @@ void CheckManager::load(const XMLNode &node)
|
||||
for(it=check_structures_to_change_state.begin();
|
||||
it != check_structures_to_change_state.end(); it++)
|
||||
{
|
||||
if(QuadGraph::get()->isReverse())
|
||||
if(DriveGraph::get()->isReverse())
|
||||
m_all_checks[*it]->addSuccessor(i);
|
||||
else
|
||||
m_all_checks[i]->addSuccessor(*it);
|
||||
|
||||
@@ -16,98 +16,133 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, B
|
||||
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
#include <IMesh.h>
|
||||
#include <ICameraSceneNode.h>
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/callbacks.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/screen_quad.hpp"
|
||||
#include "graphics/shaders.hpp"
|
||||
#include "graphics/rtts.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "tracks/check_lap.hpp"
|
||||
#include "tracks/check_line.hpp"
|
||||
#include "tracks/check_manager.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "graphics/glwrap.hpp"
|
||||
|
||||
const int QuadGraph::UNKNOWN_SECTOR = -1;
|
||||
QuadGraph *QuadGraph::m_quad_graph = NULL;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor, loads the graph information for a given set of quads
|
||||
* from a graph file.
|
||||
* \param quad_file_name Name of the file of all quads
|
||||
* \param graph_file_name Name of the file describing the actual graph
|
||||
*/
|
||||
QuadGraph::QuadGraph(const std::string &quad_file_name,
|
||||
const std::string &graph_file_name,
|
||||
const bool reverse) : m_reverse(reverse)
|
||||
DriveGraph::DriveGraph(const std::string &quad_file_name,
|
||||
const std::string &graph_file_name,
|
||||
const bool reverse) : m_reverse(reverse)
|
||||
{
|
||||
m_lap_length = 0;
|
||||
QuadSet::create();
|
||||
QuadSet::get()->init(quad_file_name);
|
||||
m_quad_filename = quad_file_name;
|
||||
m_quad_graph = this;
|
||||
load(graph_file_name);
|
||||
} // QuadGraph
|
||||
m_lap_length = 0;
|
||||
m_quad_filename = quad_file_name;
|
||||
Graph::setGraph(this);
|
||||
load(quad_file_name, graph_file_name);
|
||||
} // DriveGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Destructor, removes all nodes of the graph. */
|
||||
QuadGraph::~QuadGraph()
|
||||
// ----------------------------------------------------------------------------
|
||||
void DriveGraph::addSuccessor(unsigned int from, unsigned int to)
|
||||
{
|
||||
QuadSet::destroy();
|
||||
for(unsigned int i=0; i<m_all_nodes.size(); i++) {
|
||||
delete m_all_nodes[i];
|
||||
}
|
||||
if(UserConfigParams::m_track_debug)
|
||||
cleanupDebugMesh();
|
||||
GraphStructure::destroyRTT();
|
||||
} // ~QuadGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void QuadGraph::addSuccessor(unsigned int from, unsigned int to) {
|
||||
if(m_reverse)
|
||||
m_all_nodes[to]->addSuccessor(from);
|
||||
getNode(to)->addSuccessor(from);
|
||||
else
|
||||
m_all_nodes[from]->addSuccessor(to);
|
||||
getNode(from)->addSuccessor(to);
|
||||
|
||||
} // addSuccessor
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Loads a quad graph from a file.
|
||||
* \param filename Name of the file to load.
|
||||
*/
|
||||
void QuadGraph::load(const std::string &filename)
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function interprets a point specification as an attribute in the
|
||||
xml quad file. It understands two different specifications:
|
||||
p1="n:p" : get point p from square n (n, p integers)
|
||||
p1="p1,p2,p3" : make a 3d point out of these 3 floating point values
|
||||
*/
|
||||
void DriveGraph::getPoint(const XMLNode *xml,
|
||||
const std::string &attribute_name,
|
||||
Vec3* result) const
|
||||
{
|
||||
std::string s;
|
||||
xml->get(attribute_name, &s);
|
||||
int pos=(int)s.find_first_of(":");
|
||||
if(pos>0) // n:p specification
|
||||
{
|
||||
std::vector<std::string> l = StringUtils::split(s, ':');
|
||||
int n=atoi(l[0].c_str());
|
||||
int p=atoi(l[1].c_str());
|
||||
*result=(*m_all_nodes[n])[p];
|
||||
}
|
||||
else
|
||||
{
|
||||
xml->get(attribute_name, result);
|
||||
}
|
||||
|
||||
} // getPoint
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Loads a drive graph from a file.
|
||||
* \param filename Name of the quad file to load.
|
||||
* \param filename Name of the graph file to load.
|
||||
*/
|
||||
void DriveGraph::load(const std::string &quad_file_name,
|
||||
const std::string &filename)
|
||||
{
|
||||
XMLNode *quad = file_manager->createXMLTree(quad_file_name);
|
||||
if (!quad || quad->getName() != "quads")
|
||||
{
|
||||
Log::error("DriveGraph : Quad xml '%s' not found.", filename.c_str());
|
||||
delete quad;
|
||||
return;
|
||||
}
|
||||
|
||||
// Each quad is part of the graph exactly once now.
|
||||
for(unsigned int i=0; i<quad->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node = quad->getNode(i);
|
||||
if(xml_node->getName()!="quad")
|
||||
{
|
||||
Log::warn("DriveGraph: Unsupported node type '%s' found in '%s' - ignored.",
|
||||
xml_node->getName().c_str(), filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that it's not easy to do the reading of the parameters here
|
||||
// in quad, since the specification in the xml can contain references
|
||||
// to previous points. E.g.:
|
||||
// <quad p0="40:3" p1="40:2" p2="25.396030 0.770338 64.796539" ...
|
||||
Vec3 p0, p1, p2, p3;
|
||||
getPoint(xml_node, "p0", &p0);
|
||||
getPoint(xml_node, "p1", &p1);
|
||||
getPoint(xml_node, "p2", &p2);
|
||||
getPoint(xml_node, "p3", &p3);
|
||||
bool invisible=false;
|
||||
xml_node->get("invisible", &invisible);
|
||||
bool ai_ignore=false;
|
||||
xml_node->get("ai-ignore", &ai_ignore);
|
||||
createQuad(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore,
|
||||
false/*is_arena*/);
|
||||
}
|
||||
delete quad;
|
||||
|
||||
const XMLNode *xml = file_manager->createXMLTree(filename);
|
||||
|
||||
if(!xml)
|
||||
{
|
||||
// No graph file exist, assume a default loop X -> X+1
|
||||
// i.e. each quad is part of the graph exactly once.
|
||||
// First create an empty graph node for each quad:
|
||||
for(unsigned int i=0; i<QuadSet::get()->getNumberOfQuads(); i++)
|
||||
m_all_nodes.push_back(new GraphNode(i, (unsigned int) m_all_nodes.size()));
|
||||
// Then set the default loop:
|
||||
// Set the default loop:
|
||||
setDefaultSuccessors();
|
||||
computeDirectionData();
|
||||
|
||||
if (m_all_nodes.size() > 0)
|
||||
{
|
||||
m_lap_length = m_all_nodes[m_all_nodes.size()-1]->getDistanceFromStart()
|
||||
+ m_all_nodes[m_all_nodes.size()-1]->getDistanceToSuccessor(0);
|
||||
m_lap_length = getNode(m_all_nodes.size()-1)->getDistanceFromStart()
|
||||
+ getNode(m_all_nodes.size()-1)->getDistanceToSuccessor(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("Quad Graph", "No node in driveline graph.");
|
||||
Log::error("DriveGraph", "No node in driveline graph.");
|
||||
m_lap_length = 10.0f;
|
||||
}
|
||||
|
||||
@@ -119,29 +154,16 @@ void QuadGraph::load(const std::string &filename)
|
||||
for(unsigned int node_index=0; node_index<xml->getNumNodes(); node_index++)
|
||||
{
|
||||
const XMLNode *xml_node = xml->getNode(node_index);
|
||||
// First graph node definitions:
|
||||
// -----------------------------
|
||||
// Load the definition of edges between the graph nodes:
|
||||
// -----------------------------------------------------
|
||||
if(xml_node->getName()=="node-list")
|
||||
{
|
||||
// A list of quads is connected to a list of graph nodes:
|
||||
unsigned int from, to;
|
||||
xml_node->get("from-quad", &from);
|
||||
// Each quad is part of the graph exactly once now.
|
||||
unsigned int to = 0;
|
||||
xml_node->get("to-quad", &to);
|
||||
for(unsigned int i=from; i<=to; i++)
|
||||
{
|
||||
m_all_nodes.push_back(new GraphNode(i, (unsigned int) m_all_nodes.size()));
|
||||
}
|
||||
assert(to + 1 == m_all_nodes.size());
|
||||
continue;
|
||||
}
|
||||
else if(xml_node->getName()=="node")
|
||||
{
|
||||
// A single quad is connected to a single graph node.
|
||||
unsigned int id;
|
||||
xml_node->get("quad", &id);
|
||||
m_all_nodes.push_back(new GraphNode(id, (unsigned int) m_all_nodes.size()));
|
||||
}
|
||||
|
||||
// Then the definition of edges between the graph nodes:
|
||||
// -----------------------------------------------------
|
||||
else if(xml_node->getName()=="edge-loop")
|
||||
{
|
||||
// A closed loop:
|
||||
@@ -179,7 +201,7 @@ void QuadGraph::load(const std::string &filename)
|
||||
} // edge
|
||||
else
|
||||
{
|
||||
Log::error("Quad Graph", "Incorrect specification in '%s': '%s' ignored.",
|
||||
Log::error("DriveGraph", "Incorrect specification in '%s': '%s' ignored.",
|
||||
filename.c_str(), xml_node->getName().c_str());
|
||||
continue;
|
||||
} // incorrect specification
|
||||
@@ -195,11 +217,12 @@ void QuadGraph::load(const std::string &filename)
|
||||
m_lap_length = -1;
|
||||
for(unsigned int i=0; i<m_all_nodes.size(); i++)
|
||||
{
|
||||
float l = m_all_nodes[i]->getDistanceFromStart()
|
||||
+ m_all_nodes[i]->getDistanceToSuccessor(0);
|
||||
float l = getNode(i)->getDistanceFromStart()
|
||||
+ getNode(i)->getDistanceToSuccessor(0);
|
||||
if(l > m_lap_length)
|
||||
m_lap_length = l;
|
||||
}
|
||||
|
||||
} // load
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -209,18 +232,18 @@ void QuadGraph::load(const std::string &filename)
|
||||
* but in reverse mode (where node 0 is actually the end of the track)
|
||||
* this is 0's successor.
|
||||
*/
|
||||
unsigned int QuadGraph::getStartNode() const
|
||||
unsigned int DriveGraph::getStartNode() const
|
||||
{
|
||||
return m_reverse ? m_all_nodes[0]->getSuccessor(0)
|
||||
return m_reverse ? getNode(0)->getSuccessor(0)
|
||||
: 0;
|
||||
} // getStartNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Sets the checkline requirements for all nodes in the graph.
|
||||
*/
|
||||
void QuadGraph::computeChecklineRequirements()
|
||||
void DriveGraph::computeChecklineRequirements()
|
||||
{
|
||||
computeChecklineRequirements(m_all_nodes[0],
|
||||
computeChecklineRequirements(getNode(0),
|
||||
CheckManager::get()->getLapLineIndex());
|
||||
} // computeChecklineRequirements
|
||||
|
||||
@@ -228,8 +251,8 @@ void QuadGraph::computeChecklineRequirements()
|
||||
/** Finds which checklines must be visited before driving on this quad
|
||||
* (useful for rescue)
|
||||
*/
|
||||
void QuadGraph::computeChecklineRequirements(GraphNode* node,
|
||||
int latest_checkline)
|
||||
void DriveGraph::computeChecklineRequirements(DriveNode* node,
|
||||
int latest_checkline)
|
||||
{
|
||||
for (unsigned int n=0; n<node->getNumberOfSuccessors(); n++)
|
||||
{
|
||||
@@ -238,7 +261,7 @@ void QuadGraph::computeChecklineRequirements(GraphNode* node,
|
||||
// warp-around
|
||||
if (succ_id == 0) break;
|
||||
|
||||
GraphNode* succ = m_all_nodes[succ_id];
|
||||
DriveNode* succ = getNode(succ_id);
|
||||
int new_latest_checkline =
|
||||
CheckManager::get()->getChecklineTriggering(node->getCenter(),
|
||||
succ->getCenter() );
|
||||
@@ -276,11 +299,11 @@ void QuadGraph::computeChecklineRequirements(GraphNode* node,
|
||||
* (since on other graph nodes only one path can be used anyway, this
|
||||
* saves some memory).
|
||||
*/
|
||||
void QuadGraph::setupPaths()
|
||||
void DriveGraph::setupPaths()
|
||||
{
|
||||
for(unsigned int i=0; i<getNumNodes(); i++)
|
||||
{
|
||||
m_all_nodes[i]->setupPathsToNode();
|
||||
getNode(i)->setupPathsToNode();
|
||||
}
|
||||
} // setupPaths
|
||||
|
||||
@@ -288,18 +311,18 @@ void QuadGraph::setupPaths()
|
||||
/** This function sets a default successor for all graph nodes that currently
|
||||
* don't have a successor defined. The default successor of node X is X+1.
|
||||
*/
|
||||
void QuadGraph::setDefaultSuccessors()
|
||||
void DriveGraph::setDefaultSuccessors()
|
||||
{
|
||||
for(unsigned int i=0; i<m_all_nodes.size(); i++) {
|
||||
if(m_all_nodes[i]->getNumberOfSuccessors()==0) {
|
||||
if(getNode(i)->getNumberOfSuccessors()==0) {
|
||||
addSuccessor(i,i+1>=m_all_nodes.size() ? 0 : i+1);
|
||||
//~ m_all_nodes[i]->addSuccessor(i+1>=m_all_nodes.size() ? 0 : i+1);
|
||||
//~ getNode(i)->addSuccessor(i+1>=m_all_nodes.size() ? 0 : i+1);
|
||||
} // if size==0
|
||||
} // for i<m_allNodes.size()
|
||||
} // setDefaultSuccessors
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Sets all start positions depending on the quad graph. The number of
|
||||
/** Sets all start positions depending on the drive graph. The number of
|
||||
* entries needed is defined by the size of the start_transform (though all
|
||||
* entries will be overwritten).
|
||||
* E.g. the karts will be placed as:
|
||||
@@ -319,17 +342,17 @@ void QuadGraph::setDefaultSuccessors()
|
||||
* \param sidewards_distance Distance in sidewards (X) direction between
|
||||
* karts.
|
||||
*/
|
||||
void QuadGraph::setDefaultStartPositions(AlignedArray<btTransform>
|
||||
void DriveGraph::setDefaultStartPositions(AlignedArray<btTransform>
|
||||
*start_transforms,
|
||||
unsigned int karts_per_row,
|
||||
float forwards_distance,
|
||||
float sidewards_distance,
|
||||
float upwards_distance) const
|
||||
unsigned int karts_per_row,
|
||||
float forwards_distance,
|
||||
float sidewards_distance,
|
||||
float upwards_distance) const
|
||||
{
|
||||
// We start just before the start node (which will trigger lap
|
||||
// counting when reached). The first predecessor is the one on
|
||||
// the main driveline.
|
||||
int current_node = m_all_nodes[getStartNode()]->getPredecessor(0);
|
||||
int current_node = getNode(getStartNode())->getPredecessor(0);
|
||||
|
||||
float distance_from_start = 0.1f+forwards_distance;
|
||||
|
||||
@@ -350,20 +373,20 @@ void QuadGraph::setDefaultStartPositions(AlignedArray<btTransform>
|
||||
else
|
||||
{
|
||||
// First find on which segment we have to start
|
||||
while(distance_from_start > getNode(current_node).getNodeLength())
|
||||
while(distance_from_start > getNode(current_node)->getNodeLength())
|
||||
{
|
||||
distance_from_start -= getNode(current_node).getNodeLength();
|
||||
distance_from_start -= getNode(current_node)->getNodeLength();
|
||||
// Only follow the main driveline, i.e. first predecessor
|
||||
current_node = getNode(current_node).getPredecessor(0);
|
||||
current_node = getNode(current_node)->getPredecessor(0);
|
||||
}
|
||||
const GraphNode &gn = getNode(current_node);
|
||||
Vec3 center_line = gn.getLowerCenter() - gn.getUpperCenter();
|
||||
const DriveNode* dn = getNode(current_node);
|
||||
Vec3 center_line = dn->getLowerCenter() - dn->getUpperCenter();
|
||||
center_line.normalize();
|
||||
|
||||
Vec3 horizontal_line = gn[2] - gn[3];
|
||||
Vec3 horizontal_line = (*dn)[2] - (*dn)[3];
|
||||
horizontal_line.normalize();
|
||||
|
||||
Vec3 start = gn.getUpperCenter()
|
||||
Vec3 start = dn->getUpperCenter()
|
||||
+ center_line * distance_from_start
|
||||
+ horizontal_line * x_pos;
|
||||
// Add a certain epsilon to the height in case that the
|
||||
@@ -371,7 +394,7 @@ void QuadGraph::setDefaultStartPositions(AlignedArray<btTransform>
|
||||
(*start_transforms)[i].setOrigin(start+Vec3(0,upwards_distance,0));
|
||||
(*start_transforms)[i].setRotation(
|
||||
btQuaternion(btVector3(0, 1, 0),
|
||||
gn.getAngleToSuccessor(0)));
|
||||
dn->getAngleToSuccessor(0)));
|
||||
if(x_pos >= max_x_dist-sidewards_distance*0.5f)
|
||||
{
|
||||
x_pos = -max_x_dist;
|
||||
@@ -395,17 +418,17 @@ void QuadGraph::setDefaultStartPositions(AlignedArray<btTransform>
|
||||
* \param succ A vector of ints to which the successors are added.
|
||||
* \param for_ai true if only quads accessible by the AI should be returned.
|
||||
*/
|
||||
void QuadGraph::getSuccessors(int node_number,
|
||||
std::vector<unsigned int>& succ,
|
||||
bool for_ai) const
|
||||
void DriveGraph::getSuccessors(int node_number,
|
||||
std::vector<unsigned int>& succ,
|
||||
bool for_ai) const
|
||||
{
|
||||
const GraphNode *gn=m_all_nodes[node_number];
|
||||
for(unsigned int i=0; i<gn->getNumberOfSuccessors(); i++)
|
||||
const DriveNode *dn=getNode(node_number);
|
||||
for(unsigned int i=0; i<dn->getNumberOfSuccessors(); i++)
|
||||
{
|
||||
// If getSuccessor is called for the AI, only add
|
||||
// quads that are meant for the AI to be used.
|
||||
if(!for_ai || !gn->ignoreSuccessorForAI(i))
|
||||
succ.push_back(gn->getSuccessor(i));
|
||||
if(!for_ai || !dn->ignoreSuccessorForAI(i))
|
||||
succ.push_back(dn->getSuccessor(i));
|
||||
}
|
||||
} // getSuccessors
|
||||
|
||||
@@ -415,10 +438,10 @@ void QuadGraph::getSuccessors(int node_number,
|
||||
* \param node The node index for which to set the distance from start.
|
||||
* \param new_distance The new distance for the specified graph node.
|
||||
*/
|
||||
void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance)
|
||||
void DriveGraph::computeDistanceFromStart(unsigned int node, float new_distance)
|
||||
{
|
||||
GraphNode *gn = m_all_nodes[node];
|
||||
float current_distance = gn->getDistanceFromStart();
|
||||
DriveNode *dn = getNode(node);
|
||||
float current_distance = dn->getDistanceFromStart();
|
||||
|
||||
// If this node already has a distance defined, check if the new distance
|
||||
// is longer, and if so adjust all following nodes. Without this the
|
||||
@@ -432,25 +455,25 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance)
|
||||
if(current_distance<new_distance)
|
||||
{
|
||||
float delta = new_distance - current_distance;
|
||||
updateDistancesForAllSuccessors(gn->getQuadIndex(), delta, 0);
|
||||
updateDistancesForAllSuccessors(dn->getIndex(), delta, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise this node has no distance defined yet. Set the new
|
||||
// distance, and recursively update all following nodes.
|
||||
gn->setDistanceFromStart(new_distance);
|
||||
dn->setDistanceFromStart(new_distance);
|
||||
|
||||
for(unsigned int i=0; i<gn->getNumberOfSuccessors(); i++)
|
||||
for(unsigned int i=0; i<dn->getNumberOfSuccessors(); i++)
|
||||
{
|
||||
GraphNode *gn_next = m_all_nodes[gn->getSuccessor(i)];
|
||||
DriveNode *dn_next = getNode(dn->getSuccessor(i));
|
||||
// The start node (only node with distance 0) is reached again,
|
||||
// recursion can stop now
|
||||
if(gn_next->getDistanceFromStart()==0)
|
||||
if(dn_next->getDistanceFromStart()==0)
|
||||
continue;
|
||||
|
||||
computeDistanceFromStart(gn_next->getQuadIndex(),
|
||||
new_distance + gn->getDistanceToSuccessor(i));
|
||||
computeDistanceFromStart(dn_next->getIndex(),
|
||||
new_distance + dn->getDistanceToSuccessor(i));
|
||||
} // for i
|
||||
} // computeDistanceFromStart
|
||||
|
||||
@@ -464,35 +487,35 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance)
|
||||
* \param recursive_count Counts how often this function was called
|
||||
* recursively in order to catch incorrect graphs that contain loops.
|
||||
*/
|
||||
void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta,
|
||||
void DriveGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta,
|
||||
unsigned int recursive_count)
|
||||
{
|
||||
if(recursive_count>getNumNodes())
|
||||
{
|
||||
Log::error("QuadGraph",
|
||||
"Quad graph contains a loop (without start node).");
|
||||
Log::fatal("QuadGraph",
|
||||
Log::error("DriveGraph",
|
||||
"DriveGraph contains a loop (without start node).");
|
||||
Log::fatal("DriveGraph",
|
||||
"Fix graph, check for directions of all shortcuts etc.");
|
||||
}
|
||||
recursive_count++;
|
||||
|
||||
GraphNode &g=getNode(indx);
|
||||
g.setDistanceFromStart(g.getDistanceFromStart()+delta);
|
||||
for(unsigned int i=0; i<g.getNumberOfSuccessors(); i++)
|
||||
DriveNode* dn = getNode(indx);
|
||||
dn->setDistanceFromStart(dn->getDistanceFromStart()+delta);
|
||||
for(unsigned int i=0; i<dn->getNumberOfSuccessors(); i++)
|
||||
{
|
||||
GraphNode &g_next = getNode(g.getSuccessor(i));
|
||||
DriveNode* dn_next = getNode(dn->getSuccessor(i));
|
||||
// Stop when we reach the start node, i.e. the only node with a
|
||||
// distance of 0
|
||||
if(g_next.getDistanceFromStart()==0)
|
||||
if(dn_next->getDistanceFromStart()==0)
|
||||
continue;
|
||||
|
||||
// Only increase the distance from start of a successor node, if
|
||||
// this successor has a distance from start that is smaller then
|
||||
// the increased amount.
|
||||
if(g.getDistanceFromStart()+g.getDistanceToSuccessor(i) >
|
||||
g_next.getDistanceFromStart())
|
||||
if(dn->getDistanceFromStart()+dn->getDistanceToSuccessor(i) >
|
||||
dn_next->getDistanceFromStart())
|
||||
{
|
||||
updateDistancesForAllSuccessors(g.getSuccessor(i), delta,
|
||||
updateDistancesForAllSuccessors(dn->getSuccessor(i), delta,
|
||||
recursive_count);
|
||||
}
|
||||
}
|
||||
@@ -513,12 +536,12 @@ void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta,
|
||||
* its data constantly, i.e. if it takes a different turn, it will be using
|
||||
* the new data).
|
||||
*/
|
||||
void QuadGraph::computeDirectionData()
|
||||
void DriveGraph::computeDirectionData()
|
||||
{
|
||||
for(unsigned int i=0; i<m_all_nodes.size(); i++)
|
||||
{
|
||||
for(unsigned int succ_index=0;
|
||||
succ_index<m_all_nodes[i]->getNumberOfSuccessors();
|
||||
succ_index<getNode(i)->getNumberOfSuccessors();
|
||||
succ_index++)
|
||||
{
|
||||
determineDirection(i, succ_index);
|
||||
@@ -530,14 +553,14 @@ void QuadGraph::computeDirectionData()
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Adjust the given angle to be in [-PI, PI].
|
||||
*/
|
||||
float QuadGraph::normalizeAngle(float f)
|
||||
float DriveGraph::normalizeAngle(float f)
|
||||
{
|
||||
if(f>M_PI) f -= 2*M_PI;
|
||||
else if(f<-M_PI) f += 2*M_PI;
|
||||
return f;
|
||||
} // normalizeAngle
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Determines the direction of the quad graph when driving to the specified
|
||||
/** Determines the direction of the drive graph when driving to the specified
|
||||
* successor. It also determines the last graph node that is still following
|
||||
* the given direction. The computed data is saved in the corresponding
|
||||
* graph node.
|
||||
@@ -553,22 +576,22 @@ float QuadGraph::normalizeAngle(float f)
|
||||
* If there should be any other branches later, successor
|
||||
* 0 will always be tetsed.
|
||||
*/
|
||||
void QuadGraph::determineDirection(unsigned int current,
|
||||
unsigned int succ_index)
|
||||
void DriveGraph::determineDirection(unsigned int current,
|
||||
unsigned int succ_index)
|
||||
{
|
||||
// The maximum angle which is still considered to be straight
|
||||
const float max_straight_angle=0.1f;
|
||||
|
||||
// Compute the angle from n (=current) to n+1 (=next)
|
||||
float angle_current = getAngleToNext(current, succ_index);
|
||||
unsigned int next = getNode(current).getSuccessor(succ_index);
|
||||
unsigned int next = getNode(current)->getSuccessor(succ_index);
|
||||
float angle_next = getAngleToNext(next, 0);
|
||||
float rel_angle = normalizeAngle(angle_next-angle_current);
|
||||
// Small angles are considered to be straight
|
||||
if(fabsf(rel_angle)<max_straight_angle)
|
||||
rel_angle = 0;
|
||||
|
||||
next = getNode(next).getSuccessor(0); // next is now n+2
|
||||
next = getNode(next)->getSuccessor(0); // next is now n+2
|
||||
|
||||
// If the direction is still the same during a lap the last node
|
||||
// in the same direction is the previous node;
|
||||
@@ -591,14 +614,14 @@ void QuadGraph::determineDirection(unsigned int current,
|
||||
break;
|
||||
rel_angle = new_rel_angle;
|
||||
|
||||
next = getNode(next).getSuccessor(0);
|
||||
next = getNode(next)->getSuccessor(0);
|
||||
} // while(1)
|
||||
|
||||
GraphNode::DirectionType dir =
|
||||
rel_angle==0 ? GraphNode::DIR_STRAIGHT
|
||||
: (rel_angle>0) ? GraphNode::DIR_RIGHT
|
||||
: GraphNode::DIR_LEFT;
|
||||
m_all_nodes[current]->setDirectionData(succ_index, dir, next);
|
||||
DriveNode::DirectionType dir =
|
||||
rel_angle==0 ? DriveNode::DIR_STRAIGHT
|
||||
: (rel_angle>0) ? DriveNode::DIR_RIGHT
|
||||
: DriveNode::DIR_LEFT;
|
||||
getNode(current)->setDirectionData(succ_index, dir, next);
|
||||
} // determineDirection
|
||||
|
||||
|
||||
@@ -612,180 +635,60 @@ void QuadGraph::determineDirection(unsigned int current,
|
||||
* \param xyz The position of the kart.
|
||||
* \param sector The graph node the position is on.
|
||||
*/
|
||||
void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz,
|
||||
void DriveGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz,
|
||||
const int sector) const
|
||||
{
|
||||
if(sector == UNKNOWN_SECTOR )
|
||||
{
|
||||
Log::warn("Quad Graph", "UNKNOWN_SECTOR in spatialToTrack().");
|
||||
Log::warn("Drive Graph", "UNKNOWN_SECTOR in spatialToTrack().");
|
||||
return;
|
||||
}
|
||||
|
||||
getNode(sector).getDistances(xyz, dst);
|
||||
getNode(sector)->getDistances(xyz, dst);
|
||||
} // spatialToTrack
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** findRoadSector returns in which sector on the road the position
|
||||
* xyz is. If xyz is not on top of the road, it sets UNKNOWN_SECTOR as sector.
|
||||
*
|
||||
* \param xyz Position for which the segment should be determined.
|
||||
* \param sector Contains the previous sector (as a shortcut, since usually
|
||||
* the sector is the same as the last one), and on return the result
|
||||
* \param all_sectors If this is not NULL, it is a list of all sectors to
|
||||
* test. This is used by the AI to make sure that it ends up on the
|
||||
* selected way in case of a branch, and also to make sure that it
|
||||
* doesn't skip e.g. a loop (see explanation below for details).
|
||||
*/
|
||||
void QuadGraph::findRoadSector(const Vec3& xyz, int *sector,
|
||||
std::vector<int> *all_sectors) const
|
||||
float DriveGraph::getDistanceToNext(int n, int j) const
|
||||
{
|
||||
// Most likely the kart will still be on the sector it was before,
|
||||
// so this simple case is tested first.
|
||||
if(*sector!=UNKNOWN_SECTOR && getQuadOfNode(*sector).pointInQuad(xyz) )
|
||||
{
|
||||
return;
|
||||
} // if still on same quad
|
||||
|
||||
// Now we search through all graph nodes, starting with
|
||||
// the current one
|
||||
int indx = *sector;
|
||||
float min_dist = 999999.9f;
|
||||
|
||||
// If a current sector is given, and max_lookahead is specify, only test
|
||||
// the next max_lookahead graph nodes instead of testing the whole graph.
|
||||
// This is necessary for the AI: if the track contains a loop, e.g.:
|
||||
// -A--+---B---+----F--------
|
||||
// E C
|
||||
// +---D---+
|
||||
// and the track is supposed to be driven: ABCDEBF, the AI might find
|
||||
// the node on F, and then keep on going straight ahead instead of
|
||||
// using the loop at all.
|
||||
unsigned int max_count = (*sector!=UNKNOWN_SECTOR && all_sectors!=NULL)
|
||||
? (unsigned int)all_sectors->size()
|
||||
: (unsigned int)m_all_nodes.size();
|
||||
*sector = UNKNOWN_SECTOR;
|
||||
for(unsigned int i=0; i<max_count; i++)
|
||||
{
|
||||
if(all_sectors)
|
||||
indx = (*all_sectors)[i];
|
||||
else
|
||||
indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0;
|
||||
const Quad &q = getQuadOfNode(indx);
|
||||
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.
|
||||
if(q.pointInQuad(xyz) && dist < min_dist && dist>-1.0f)
|
||||
{
|
||||
min_dist = dist;
|
||||
*sector = indx;
|
||||
}
|
||||
} // for i<m_all_nodes.size()
|
||||
|
||||
return;
|
||||
} // findRoadSector
|
||||
return getNode(n)->getDistanceToSuccessor(j);
|
||||
} // getDistanceToNext
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** findOutOfRoadSector finds the sector where XYZ is, but as it name
|
||||
implies, it is more accurate for the outside of the track than the
|
||||
inside, and for STK's needs the accuracy on top of the track is
|
||||
unacceptable; but if this was a 2D function, the accuracy for out
|
||||
of road sectors would be perfect.
|
||||
|
||||
To find the sector we look for the closest line segment from the
|
||||
right and left drivelines, and the number of that segment will be
|
||||
the sector.
|
||||
|
||||
The SIDE argument is used to speed up the function only; if we know
|
||||
that XYZ is on the left or right side of the track, we know that
|
||||
the closest driveline must be the one that matches that condition.
|
||||
In reality, the side used in STK is the one from the previous frame,
|
||||
but in order to move from one side to another a point would go
|
||||
through the middle, that is handled by findRoadSector() which doesn't
|
||||
has speed ups based on the side.
|
||||
|
||||
NOTE: This method of finding the sector outside of the road is *not*
|
||||
perfect: if two line segments have a similar altitude (but enough to
|
||||
let a kart get through) and they are very close on a 2D system,
|
||||
if a kart is on the air it could be closer to the top line segment
|
||||
even if it is supposed to be on the sector of the lower line segment.
|
||||
Probably the best solution would be to construct a quad that reaches
|
||||
until the next higher overlapping line segment, and find the closest
|
||||
one to XYZ.
|
||||
*/
|
||||
int QuadGraph::findOutOfRoadSector(const Vec3& xyz,
|
||||
const int curr_sector,
|
||||
std::vector<int> *all_sectors) const
|
||||
float DriveGraph::getAngleToNext(int n, int j) const
|
||||
{
|
||||
int count = (all_sectors!=NULL) ? (int) all_sectors->size() : getNumNodes();
|
||||
int current_sector = 0;
|
||||
if(curr_sector != UNKNOWN_SECTOR && !all_sectors)
|
||||
return getNode(n)->getAngleToSuccessor(j);
|
||||
} // getAngleToNext
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int DriveGraph::getNumberOfSuccessors(int n) const
|
||||
{
|
||||
return getNode(n)->getNumberOfSuccessors();
|
||||
} // getNumberOfSuccessors
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float DriveGraph::getDistanceFromStart(int j) const
|
||||
{
|
||||
return getNode(j)->getDistanceFromStart();
|
||||
} // getDistanceFromStart
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void DriveGraph::differentNodeColor(int n, video::SColor* c) const
|
||||
{
|
||||
if (UserConfigParams::m_track_debug)
|
||||
{
|
||||
// We have to test all nodes here: reason is that on track with
|
||||
// shortcuts the n quads of the main drivelines is followed by
|
||||
// the quads of the shortcuts. So after quad n-1 (the last one
|
||||
// before the lap counting line) quad n will not be 0 (the first
|
||||
// quad after the lap counting line), but one of the quads on a
|
||||
// shortcut. If we only tested a limited number of quads to
|
||||
// improve the performance the crossing of a lap might not be
|
||||
// detected (because quad 0 is not tested, only quads on the
|
||||
// shortcuts are tested). If this should become a performance
|
||||
// bottleneck, we need to set up a graph of 'next' quads for each
|
||||
// quad (similar to what the AI does), and only test the quads
|
||||
// in this graph.
|
||||
const int LIMIT = getNumNodes();
|
||||
count = LIMIT;
|
||||
// Start 10 quads before the current quad, so the quads closest
|
||||
// to the current position are tested first.
|
||||
current_sector = curr_sector -10;
|
||||
if(current_sector<0) current_sector += getNumNodes();
|
||||
if (getNode(n)->is3DQuad())
|
||||
*c = video::SColor(255, 0, 255, 0);
|
||||
else
|
||||
*c = video::SColor(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
int min_sector = UNKNOWN_SECTOR;
|
||||
float min_dist_2 = 999999.0f*999999.0f;
|
||||
} // differentNodeColor
|
||||
|
||||
// If a kart is falling and in between (or too far below)
|
||||
// a driveline point it might not fulfill
|
||||
// the height condition. So we run the test twice: first with height
|
||||
// condition, then again without the height condition - just to make sure
|
||||
// it always comes back with some kind of quad.
|
||||
for(int phase=0; phase<2; phase++)
|
||||
{
|
||||
for(int j=0; j<count; j++)
|
||||
{
|
||||
int next_sector;
|
||||
if(all_sectors)
|
||||
next_sector = (*all_sectors)[j];
|
||||
else
|
||||
next_sector = current_sector+1 == (int)getNumNodes()
|
||||
? 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 = getQuadOfNode(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.
|
||||
if(phase==1 || (dist < 5.0f && dist>-1.0f) )
|
||||
{
|
||||
min_dist_2 = dist_2;
|
||||
min_sector = next_sector;
|
||||
}
|
||||
}
|
||||
current_sector = next_sector;
|
||||
} // for j
|
||||
// Leave in phase 0 if any sector was found.
|
||||
if(min_sector!=UNKNOWN_SECTOR)
|
||||
return min_sector;
|
||||
} // phase
|
||||
|
||||
if(min_sector==UNKNOWN_SECTOR )
|
||||
{
|
||||
Log::info("Quad Grap", "unknown sector found.");
|
||||
}
|
||||
return min_sector;
|
||||
} // findOutOfRoadSector
|
||||
// -----------------------------------------------------------------------------
|
||||
DriveNode* DriveGraph::getNode(unsigned int j) const
|
||||
{
|
||||
assert(j < m_all_nodes.size());
|
||||
DriveNode* n = dynamic_cast<DriveNode*>(m_all_nodes[j]);
|
||||
assert(n != NULL);
|
||||
return n;
|
||||
} // getNode
|
||||
125
src/tracks/drive_graph.hpp
Normal file
125
src/tracks/drive_graph.hpp
Normal file
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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, B
|
||||
|
||||
#ifndef HEADER_DRIVE_GRAPH_HPP
|
||||
#define HEADER_DRIVE_GRAPH_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "tracks/graph.hpp"
|
||||
#include "utils/aligned_array.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
class DriveNode;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \brief A graph made from driveline
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class DriveGraph : public Graph
|
||||
{
|
||||
private:
|
||||
/** The length of the first loop. */
|
||||
float m_lap_length;
|
||||
|
||||
/** Stores the filename - just used for error messages. */
|
||||
std::string m_quad_filename;
|
||||
|
||||
/** Wether the graph should be reverted or not */
|
||||
bool m_reverse;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void setDefaultSuccessors();
|
||||
// ------------------------------------------------------------------------
|
||||
void computeChecklineRequirements(DriveNode* node, int latest_checkline);
|
||||
// ------------------------------------------------------------------------
|
||||
void computeDirectionData();
|
||||
// ------------------------------------------------------------------------
|
||||
void determineDirection(unsigned int current, unsigned int succ_index);
|
||||
// ------------------------------------------------------------------------
|
||||
float normalizeAngle(float f);
|
||||
// ------------------------------------------------------------------------
|
||||
void addSuccessor(unsigned int from, unsigned int to);
|
||||
// ------------------------------------------------------------------------
|
||||
void load(const std::string &quad_file_name, const std::string &filename);
|
||||
// ------------------------------------------------------------------------
|
||||
void getPoint(const XMLNode *xml, const std::string &attribute_name,
|
||||
Vec3 *result) const;
|
||||
// ------------------------------------------------------------------------
|
||||
void computeDistanceFromStart(unsigned int start_node, float distance);
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getStartNode() const;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool hasLapLine() const OVERRIDE { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void differentNodeColor(int n, video::SColor* c) const OVERRIDE;
|
||||
|
||||
public:
|
||||
static DriveGraph* get() { return dynamic_cast<DriveGraph*>(m_graph); }
|
||||
// ------------------------------------------------------------------------
|
||||
DriveGraph(const std::string &quad_file_name,
|
||||
const std::string &graph_file_name, const bool reverse);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~DriveGraph() {}
|
||||
// ------------------------------------------------------------------------
|
||||
void getSuccessors(int node_number, std::vector<unsigned int>& succ,
|
||||
bool for_ai=false) const;
|
||||
// ------------------------------------------------------------------------
|
||||
void spatialToTrack(Vec3 *dst, const Vec3& xyz, const int sector) const;
|
||||
// ------------------------------------------------------------------------
|
||||
void setDefaultStartPositions(AlignedArray<btTransform> *start_transforms,
|
||||
unsigned int karts_per_row,
|
||||
float forwards_distance = 1.5f,
|
||||
float sidewards_distance = 1.5f,
|
||||
float upwards_distance=0.0f) const;
|
||||
// ------------------------------------------------------------------------
|
||||
void updateDistancesForAllSuccessors(unsigned int indx, float delta,
|
||||
unsigned int count);
|
||||
// ------------------------------------------------------------------------
|
||||
void setupPaths();
|
||||
// ------------------------------------------------------------------------
|
||||
void computeChecklineRequirements();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the distance to the j-th successor of node n. */
|
||||
float getDistanceToNext(int n, int j) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the angle of the line between node n and its j-th.
|
||||
* successor. */
|
||||
float getAngleToNext(int n, int j) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of successors of a node n. */
|
||||
int getNumberOfSuccessors(int n) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the quad that belongs to a graph node. */
|
||||
DriveNode* getNode(unsigned int j) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the distance from the start to the beginning of a quad. */
|
||||
float getDistanceFromStart(int j) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the length of the main driveline. */
|
||||
float getLapLength() const { return m_lap_length; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool isReverse() const { return m_reverse; }
|
||||
|
||||
}; // DriveGraph
|
||||
|
||||
#endif
|
||||
186
src/tracks/drive_node.cpp
Normal file
186
src/tracks/drive_node.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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, B
|
||||
|
||||
#include "tracks/drive_node.hpp"
|
||||
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "matrix4.h"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
m_ai_ignore = ai_ignore;
|
||||
m_distance_from_start = -1.0f;
|
||||
|
||||
// The following values should depend on the actual orientation
|
||||
// of the quad. ATM we always assume that indices 0,1 are the lower end,
|
||||
// and 2,3 are the upper end (or the reverse if reverse mode is selected).
|
||||
// The width is the average width at the beginning and at the end.
|
||||
m_right_unit_vector = ( m_p[0]-m_p[1]
|
||||
+m_p[3]-m_p[2]) * 0.5f;
|
||||
m_right_unit_vector.normalize();
|
||||
|
||||
m_width = ( (m_p[1]-m_p[0]).length()
|
||||
+ (m_p[3]-m_p[2]).length() ) * 0.5f;
|
||||
|
||||
if(DriveGraph::get()->isReverse())
|
||||
{
|
||||
m_lower_center = (m_p[2]+m_p[3]) * 0.5f;
|
||||
m_upper_center = (m_p[0]+m_p[1]) * 0.5f;
|
||||
m_right_unit_vector *= -1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lower_center = (m_p[0]+m_p[1]) * 0.5f;
|
||||
m_upper_center = (m_p[2]+m_p[3]) * 0.5f;
|
||||
}
|
||||
|
||||
} // DriveNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Adds a successor to a node. This function will also pre-compute certain
|
||||
* values (like distance from this node to the successor, angle (in world)
|
||||
* between this node and the successor.
|
||||
* \param to The index of the drive node of the successor.
|
||||
*/
|
||||
void DriveNode::addSuccessor(unsigned int to)
|
||||
{
|
||||
m_successor_nodes.push_back(to);
|
||||
// to is the drive node
|
||||
DriveNode* dn_to = DriveGraph::get()->getNode(to);
|
||||
|
||||
// Note that the first predecessor is (because of the way the drive graph
|
||||
// is exported) the most 'natural' one, i.e. the one on the main
|
||||
// driveline.
|
||||
dn_to->m_predecessor_nodes.push_back(m_index);
|
||||
|
||||
Vec3 d = m_lower_center - dn_to->m_lower_center;
|
||||
m_distance_to_next.push_back(d.length());
|
||||
|
||||
Vec3 diff = dn_to->getCenter() - getCenter();
|
||||
|
||||
core::CMatrix4<float> m;
|
||||
m.buildRotateFromTo(getNormal().toIrrVector(),
|
||||
Vec3(0, 1, 0).toIrrVector());
|
||||
core::vector3df diff_rotated;
|
||||
m.rotateVect(diff_rotated, diff.toIrrVector());
|
||||
|
||||
m_angle_to_next.push_back(atan2(diff_rotated.X, diff_rotated.Z));
|
||||
|
||||
} // addSuccessor
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** If this node has more than one successor, it will set up a vector that
|
||||
* contains the direction to use when a certain drive node X should be
|
||||
* reached.
|
||||
*/
|
||||
void DriveNode::setupPathsToNode()
|
||||
{
|
||||
if(m_successor_nodes.size()<2) return;
|
||||
|
||||
const unsigned int num_nodes = DriveGraph::get()->getNumNodes();
|
||||
m_path_to_node.resize(num_nodes);
|
||||
|
||||
// Initialise each drive node with -1, indicating that
|
||||
// it hasn't been reached yet.
|
||||
for(unsigned int i=0; i<num_nodes; i++)
|
||||
m_path_to_node[i] = -1;
|
||||
|
||||
// Indicate that this node can be reached from this node by following
|
||||
// successor 0 - just a dummy value that might only be used during the
|
||||
// recursion below.
|
||||
m_path_to_node[m_index] = 0;
|
||||
|
||||
// A simple depth first search is used to determine which successor to
|
||||
// use to reach a certain drive node. Using Dijkstra's algorithm would
|
||||
// give the shortest way to reach a certain node, but the shortest way
|
||||
// might involve some shortcuts which are hidden, and should therefore
|
||||
// not be used.
|
||||
for(unsigned int i=0; i<getNumberOfSuccessors(); i++)
|
||||
{
|
||||
DriveNode* dn = DriveGraph::get()->getNode(getSuccessor(i));
|
||||
dn->markAllSuccessorsToUse(i, &m_path_to_node);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
for(unsigned int i = 0; i < m_path_to_node.size(); ++i)
|
||||
{
|
||||
if(m_path_to_node[i] == -1)
|
||||
Log::warn("DriveNode", "No path to node %d found on drive node %d.",
|
||||
i, m_index);
|
||||
}
|
||||
#endif
|
||||
} // setupPathsToNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function marks that the successor n should be used to reach this
|
||||
* node. It then recursively (depth first) does the same for all its
|
||||
* successors. Depth-first
|
||||
* \param n The successor which should be used in m_path_node to reach
|
||||
* this node.
|
||||
* \param path_to_node The path-to-node data structure of the node for
|
||||
* which the paths are currently determined.
|
||||
*/
|
||||
void DriveNode::markAllSuccessorsToUse(unsigned int n,
|
||||
PathToNodeVector *path_to_node)
|
||||
{
|
||||
// End recursion if the path to this node has already been found.
|
||||
if( (*path_to_node)[m_index] >-1) return;
|
||||
|
||||
(*path_to_node)[m_index] = n;
|
||||
for(unsigned int i=0; i<getNumberOfSuccessors(); i++)
|
||||
{
|
||||
DriveNode* dn = DriveGraph::get()->getNode(getSuccessor(i));
|
||||
dn->markAllSuccessorsToUse(n, path_to_node);
|
||||
}
|
||||
} // markAllSuccesorsToUse
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void DriveNode::setDirectionData(unsigned int successor, DirectionType dir,
|
||||
unsigned int last_node_index)
|
||||
{
|
||||
if(m_direction.size()<successor+1)
|
||||
{
|
||||
m_direction.resize(successor+1);
|
||||
m_last_index_same_direction.resize(successor+1);
|
||||
}
|
||||
m_direction[successor] = dir;
|
||||
m_last_index_same_direction[successor] = last_node_index;
|
||||
} // setDirectionData
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void DriveNode::setChecklineRequirements(int latest_checkline)
|
||||
{
|
||||
m_checkline_requirements.push_back(latest_checkline);
|
||||
} // setChecklineRequirements
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns true if the index-successor of this node is one that the AI
|
||||
* is allowed to use.
|
||||
* \param index Index of the successor.
|
||||
*/
|
||||
bool DriveNode::ignoreSuccessorForAI(unsigned int i) const
|
||||
{
|
||||
return DriveGraph::get()->getNode(m_successor_nodes[i])->letAIIgnore();
|
||||
} // ignoreSuccessorForAI
|
||||
@@ -16,27 +16,19 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, B
|
||||
|
||||
#ifndef HEADER_GRAPH_NODE_HPP
|
||||
#define HEADER_GRAPH_NODE_HPP
|
||||
#ifndef HEADER_DRIVE_NODE_HPP
|
||||
#define HEADER_DRIVE_NODE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <vector2d.h>
|
||||
#include <dimension2d.h>
|
||||
#include <line2d.h>
|
||||
|
||||
#include "tracks/quad.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
class QuadGraph;
|
||||
|
||||
/**
|
||||
* \brief This class stores a node of the graph, i.e. a list of successor
|
||||
* edges.
|
||||
* \brief This class stores a node of the drive graph, i.e. a list of
|
||||
* successor edges, it can either be 2d or 3d.
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class GraphNode
|
||||
class DriveNode : public Quad
|
||||
{
|
||||
public:
|
||||
/** To indiciate in which direction the track is going:
|
||||
@@ -44,17 +36,21 @@ public:
|
||||
* AI only. */
|
||||
enum DirectionType {DIR_STRAIGHT, DIR_LEFT, DIR_RIGHT,
|
||||
DIR_UNDEFINED};
|
||||
protected:
|
||||
/** Lower center point of the drive node. */
|
||||
Vec3 m_lower_center;
|
||||
|
||||
/** Upper center point of the drive node. */
|
||||
Vec3 m_upper_center;
|
||||
|
||||
/** Distance from the start to the beginning of the drive node. */
|
||||
float m_distance_from_start;
|
||||
|
||||
private:
|
||||
/** Index of this node in the set of quads. Several graph nodes can use
|
||||
* the same quad, meaning it is possible to use a quad more than once,
|
||||
* e.g. a figure 8 like track. */
|
||||
unsigned int m_quad_index;
|
||||
/** Set to true if this drive node should not be used by the AI. */
|
||||
bool m_ai_ignore;
|
||||
|
||||
/** Index of this graph node. */
|
||||
unsigned int m_node_index;
|
||||
|
||||
/** The list of successor graph nodes. */
|
||||
/** The list of successor drive nodes. */
|
||||
std::vector<int> m_successor_nodes;
|
||||
|
||||
/** The list of predecessors of a node. */
|
||||
@@ -66,74 +62,58 @@ private:
|
||||
/** The angle of the line from this node to each neighbour. */
|
||||
std::vector<float> m_angle_to_next;
|
||||
|
||||
/** Distance from the start to the beginning of this quad. */
|
||||
float m_distance_from_start;
|
||||
|
||||
/** Width of the track, which is the average of the width at the
|
||||
* beginning and at the end. FIXME: for now the width is independent
|
||||
* of the orientation (e.g. a quad used more than once might once
|
||||
* be used from top to bottom, one from left to right, so it should
|
||||
* have a different width then). */
|
||||
float m_width;
|
||||
* beginning and at the end. */
|
||||
float m_width;
|
||||
|
||||
/** The center point of the lower two points (e.g. points 0 and 1).
|
||||
* This saves some computations in getDistances later. Only the
|
||||
* start point is needed, and only in 2d. */
|
||||
core::vector2df m_lower_center_2d;
|
||||
/** A vector from the center of the quad to the right edge. */
|
||||
Vec3 m_center_to_right;
|
||||
|
||||
/** Lower center point of the graph node. */
|
||||
Vec3 m_lower_center;
|
||||
typedef std::vector<int> PathToNodeVector;
|
||||
/** This vector is only used if the drive node has more than one
|
||||
* successor. In this case m_path_to_node[X] will contain the index
|
||||
* of the successor to use in order to reach drive node X for this
|
||||
* drive nodes. */
|
||||
PathToNodeVector m_path_to_node;
|
||||
|
||||
/** Upper center point of the graph node. */
|
||||
Vec3 m_upper_center;
|
||||
/** The direction for each of the successors. */
|
||||
std::vector<DirectionType> m_direction;
|
||||
|
||||
/** A vector from the center of the quad to the right edge. */
|
||||
Vec3 m_center_to_right;
|
||||
/** Stores for each successor the index of the last drive node that
|
||||
* has the same direction (i.e. if index 0 curves left, this vector
|
||||
* will store the index of the last drive node that is still turning
|
||||
* left. */
|
||||
std::vector<unsigned int> m_last_index_same_direction;
|
||||
|
||||
/** Line between lower and upper center, saves computation in
|
||||
* getDistanceFromLine() later. The line is 2d only since otherwise
|
||||
* taller karts would have a larger distance from the center. It also
|
||||
* saves computation, and it is only needed to determine the distance
|
||||
* from the center of the drivelines anyway. */
|
||||
core::line2df m_line;
|
||||
|
||||
typedef std::vector<int> PathToNodeVector;
|
||||
/** This vector is only used if the graph node has more than one
|
||||
* successor. In this case m_path_to_node[X] will contain the index
|
||||
* of the successor to use in order to reach graph node X for this
|
||||
* graph nodes. */
|
||||
PathToNodeVector m_path_to_node;
|
||||
|
||||
/** The direction for each of the successors. */
|
||||
std::vector<DirectionType> m_direction;
|
||||
|
||||
/** Stores for each successor the index of the last graph node that
|
||||
* has the same direction (i.e. if index 0 curves left, this vector
|
||||
* will store the index of the last graph node that is still turning
|
||||
* left. */
|
||||
std::vector<unsigned int> m_last_index_same_direction;
|
||||
|
||||
/** A unit vector pointing from the center to the right side, orthogonal
|
||||
* to the driving direction. */
|
||||
Vec3 m_right_unit_vector;
|
||||
/** A unit vector pointing from the center to the right side, orthogonal
|
||||
* to the driving direction. */
|
||||
Vec3 m_right_unit_vector;
|
||||
|
||||
/**
|
||||
* Sets of checklines you should have activated when you are driving on
|
||||
* this node (there is a possibility of more than one set because of
|
||||
* alternate ways)
|
||||
*/
|
||||
std::vector< int > m_checkline_requirements;
|
||||
* Sets of checklines you should have activated when you are driving on
|
||||
* this node (there is a possibility of more than one set because of
|
||||
* alternate ways)
|
||||
*/
|
||||
std::vector< int > m_checkline_requirements;
|
||||
|
||||
void markAllSuccessorsToUse(unsigned int n,
|
||||
// ------------------------------------------------------------------------
|
||||
void markAllSuccessorsToUse(unsigned int n,
|
||||
PathToNodeVector *m_path_to_node);
|
||||
|
||||
public:
|
||||
GraphNode(unsigned int quad_index, unsigned int node_index);
|
||||
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);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~DriveNode() {}
|
||||
// ------------------------------------------------------------------------
|
||||
void addSuccessor (unsigned int to);
|
||||
void getDistances(const Vec3 &xyz, Vec3 *result);
|
||||
float getDistance2FromPoint(const Vec3 &xyz);
|
||||
// ------------------------------------------------------------------------
|
||||
void setupPathsToNode();
|
||||
// ------------------------------------------------------------------------
|
||||
void setChecklineRequirements(int latest_checkline);
|
||||
// ------------------------------------------------------------------------
|
||||
void setDirectionData(unsigned int successor, DirectionType dir,
|
||||
unsigned int last_node_index);
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -143,7 +123,7 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the i-th successor node. */
|
||||
unsigned int getSuccessor(unsigned int i) const
|
||||
{ return m_successor_nodes[i]; }
|
||||
{ return m_successor_nodes[i]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of predecessors. */
|
||||
unsigned int getNumberOfPredecessors() const
|
||||
@@ -152,65 +132,41 @@ public:
|
||||
/** Returns a predecessor for this node. Note that the first predecessor
|
||||
* is the most 'natural' one, i.e. the one on the main driveline.
|
||||
*/
|
||||
int getPredecessor(unsigned int i) const {return m_predecessor_nodes[i]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the quad_index in the quad_set of this node. */
|
||||
int getQuadIndex() const { return m_quad_index; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the quad of this graph node. */
|
||||
const Quad& getQuad() const {return QuadSet::get()->getQuad(m_quad_index);}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the i-th. point of a quad. ATM this just returns the vertices
|
||||
* from the quads, but if necessary this method will also consider
|
||||
* rotated quads. So index 0 will always be lower left point, then
|
||||
* counterclockwise. */
|
||||
const Vec3& operator[](int i) const
|
||||
{return QuadSet::get()->getQuad(m_quad_index)[i];}
|
||||
int getPredecessor(unsigned int i) const { return m_predecessor_nodes[i]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the distance to the j-th. successor. */
|
||||
float getDistanceToSuccessor(unsigned int j) const
|
||||
{ return m_distance_to_next[j]; }
|
||||
|
||||
{ return m_distance_to_next[j]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the angle from this node to the j-th. successor. */
|
||||
float getAngleToSuccessor(unsigned int j) const
|
||||
{ return m_angle_to_next[j]; }
|
||||
{ return m_angle_to_next[j]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the distance from start. */
|
||||
float getDistanceFromStart() const
|
||||
{ return m_distance_from_start; }
|
||||
{ return m_distance_from_start; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the distance from start for this node. */
|
||||
void setDistanceFromStart(float d) {m_distance_from_start = d; }
|
||||
void setDistanceFromStart(float d) { m_distance_from_start = d; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the width of the part for this quad. */
|
||||
float getPathWidth() const { return m_width; }
|
||||
float getPathWidth() const { return m_width; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center point of the lower edge of this graph node. */
|
||||
const Vec3& getLowerCenter() const {return m_lower_center;}
|
||||
/** Returns the center point of the lower edge of this drive node. */
|
||||
const Vec3& getLowerCenter() const { return m_lower_center; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center point of the upper edge of this graph node. */
|
||||
const Vec3& getUpperCenter() const {return m_upper_center;}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center point of this graph node. */
|
||||
const Vec3 getCenter() const
|
||||
{return (m_upper_center + m_lower_center) / 2.0f;}
|
||||
/** Returns the center point of the upper edge of this drive node. */
|
||||
const Vec3& getUpperCenter() const { return m_upper_center; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the length of the quad of this node. */
|
||||
float getNodeLength() const
|
||||
{return (m_lower_center-m_upper_center).length();}
|
||||
{ return (m_lower_center-m_upper_center).length(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if the index-successor of this node is one that the AI
|
||||
* is allowed to use.
|
||||
* \param index Index of the successor. */
|
||||
bool ignoreSuccessorForAI(unsigned int i) const
|
||||
{
|
||||
return QuadSet::get()->getQuad(m_successor_nodes[i]).letAIIgnore();
|
||||
};
|
||||
bool ignoreSuccessorForAI(unsigned int i) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns which successor node to use in order to be able to reach the
|
||||
* given node n.
|
||||
* \param n Index of the graph node to reach.
|
||||
* \param n Index of the drive node to reach.
|
||||
*/
|
||||
int getSuccessorToReach(unsigned int n)
|
||||
{
|
||||
@@ -219,7 +175,7 @@ public:
|
||||
return m_path_to_node.size()>0 ? m_path_to_node[n] : 0;
|
||||
} // getSuccesorToReach
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the checkline requirements of this graph node. */
|
||||
/** Returns the checkline requirements of this drive node. */
|
||||
const std::vector<int>& getChecklineRequirements() const
|
||||
{ return m_checkline_requirements; }
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -231,7 +187,13 @@ public:
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns a unit vector pointing to the right side of the quad. */
|
||||
const Vec3 &getRightUnitVector() const { return m_right_unit_vector; }
|
||||
}; // GraphNode
|
||||
const Vec3 &getRightUnitVector() const { return m_right_unit_vector; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** True if this node should be ignored by the AI. */
|
||||
bool letAIIgnore() const { return m_ai_ignore; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getDistances(const Vec3 &xyz, Vec3 *result) const = 0;
|
||||
|
||||
}; // DriveNode
|
||||
|
||||
#endif
|
||||
71
src/tracks/drive_node_2d.cpp
Normal file
71
src/tracks/drive_node_2d.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// 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 "tracks/drive_node_2d.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
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)
|
||||
: DriveNode(p0, p1, p2, p3, normal, node_index, invisible,
|
||||
ai_ignore)
|
||||
{
|
||||
m_line = core::line2df(m_upper_center.getX(), m_upper_center.getZ(),
|
||||
m_lower_center.getX(), m_lower_center.getZ());
|
||||
|
||||
// Only this 2d point is needed later
|
||||
m_lower_center_2d = core::vector2df(m_lower_center.getX(),
|
||||
m_lower_center.getZ());
|
||||
} // DriveNode2D
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the distance a point has from this node in forward and sidewards
|
||||
* direction, i.e. how far forwards the point is from the beginning of the
|
||||
* node, and how far to the side from the line connecting the center points
|
||||
* is it. All these computations are done in 2D only.
|
||||
* \param xyz The coordinates of the point.
|
||||
* \param result The X coordinate contains the sidewards distance, the
|
||||
* Z coordinate the forward distance.
|
||||
*/
|
||||
void DriveNode2D::getDistances(const Vec3 &xyz, Vec3 *result) const
|
||||
{
|
||||
core::vector2df xyz2d(xyz.getX(), xyz.getZ());
|
||||
core::vector2df closest = m_line.getClosestPoint(xyz2d);
|
||||
if (m_line.getPointOrientation(xyz2d) > 0)
|
||||
result->setX( (closest-xyz2d).getLength()); // to the right
|
||||
else
|
||||
result->setX(-(closest-xyz2d).getLength()); // to the left
|
||||
|
||||
result->setZ(m_distance_from_start +
|
||||
(closest-m_lower_center_2d).getLength());
|
||||
} // getDistances
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the square of the distance between the given point and any point
|
||||
* on the 'centre' line, i.e. the finite line from the middle point of the
|
||||
* lower end of the node to the middle point of the upper end of the node
|
||||
* which belongs to this graph node. The value is computed in 2d only!
|
||||
* \param xyz The point for which the distance to the line is computed.
|
||||
*/
|
||||
float DriveNode2D::getDistance2FromPoint(const Vec3 &xyz) const
|
||||
{
|
||||
core::vector2df xyz2d(xyz.getX(), xyz.getZ());
|
||||
core::vector2df closest = m_line.getClosestPoint(xyz2d);
|
||||
return (closest-xyz2d).getLengthSQ();
|
||||
} // getDistance2FromPoint
|
||||
55
src/tracks/drive_node_2d.hpp
Normal file
55
src/tracks/drive_node_2d.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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_DRIVE_NODE_2D_HPP
|
||||
#define HEADER_DRIVE_NODE_2D_HPP
|
||||
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <line2d.h>
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class DriveNode2D : public DriveNode
|
||||
{
|
||||
private:
|
||||
/** The center point of the lower two points (e.g. points 0 and 1).
|
||||
* This saves some computations in getDistances later. Only the
|
||||
* start point is needed, and only in 2d. */
|
||||
core::vector2df m_lower_center_2d;
|
||||
|
||||
/** Line between lower and upper center, saves computation in
|
||||
* getDistance() later. The line is 2d only since otherwise taller karts
|
||||
* would have a larger distance from the center. It also saves
|
||||
* computation, and it is only needed to determine the distance from the
|
||||
* center of the drivelines anyway. */
|
||||
core::line2df m_line;
|
||||
|
||||
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);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE;
|
||||
|
||||
};
|
||||
#endif
|
||||
67
src/tracks/drive_node_3d.cpp
Normal file
67
src/tracks/drive_node_3d.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// 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 "tracks/drive_node_3d.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
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)
|
||||
: DriveNode(p0, p1, p2, p3, normal, node_index, invisible,
|
||||
ai_ignore), BoundingBox3D(p0, p1, p2, p3, normal)
|
||||
{
|
||||
m_line = core::line3df(m_lower_center.toIrrVector(),
|
||||
m_upper_center.toIrrVector());
|
||||
} // DriveNode3D
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the distance a point has from this node in forward and sidewards
|
||||
* direction, i.e. how far forwards the point is from the beginning of the
|
||||
* node, and how far to the side from the line connecting the center points
|
||||
* is it.
|
||||
* \param xyz The coordinates of the point.
|
||||
* \param result The X coordinate contains the sidewards distance, the
|
||||
* Z coordinate the forward distance.
|
||||
*/
|
||||
void DriveNode3D::getDistances(const Vec3 &xyz, Vec3 *result) const
|
||||
{
|
||||
core::vector3df xyz_irr = xyz.toIrrVector();
|
||||
core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector());
|
||||
core::vector3df normal = getNormal().toIrrVector();
|
||||
|
||||
if (xyz.sideofPlane(closest, closest + normal, m_line.end) < 0)
|
||||
result->setX( (closest-xyz_irr).getLength()); // to the right
|
||||
else
|
||||
result->setX(-(closest-xyz_irr).getLength()); // to the left
|
||||
result->setZ(m_distance_from_start +
|
||||
(closest-m_lower_center.toIrrVector()).getLength());
|
||||
} // getDistances
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the square of the distance between the given point and any point
|
||||
* on the 'centre' line, i.e. the finite line from the middle point of the
|
||||
* lower end of the node to the middle point of the upper end of the node
|
||||
* which belongs to this node.
|
||||
* \param xyz The point for which the distance to the line is computed.
|
||||
*/
|
||||
float DriveNode3D::getDistance2FromPoint(const Vec3 &xyz) const
|
||||
{
|
||||
core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector());
|
||||
return (closest-xyz.toIrrVector()).getLengthSQ();
|
||||
} // getDistance2FromPoint
|
||||
56
src/tracks/drive_node_3d.hpp
Normal file
56
src/tracks/drive_node_3d.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// 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_DRIVE_NODE_3D_HPP
|
||||
#define HEADER_DRIVE_NODE_3D_HPP
|
||||
|
||||
#include "tracks/bounding_box_3d.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class DriveNode3D : public DriveNode,
|
||||
public BoundingBox3D
|
||||
{
|
||||
private:
|
||||
/** Line between lower and upper center, saves computation in
|
||||
* getDistance() later.
|
||||
*/
|
||||
core::line3df m_line;
|
||||
|
||||
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);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool pointInside(const Vec3& p,
|
||||
bool ignore_vertical = false) const OVERRIDE
|
||||
{
|
||||
return BoundingBox3D::pointInside(p);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool is3DQuad() const OVERRIDE { return true; }
|
||||
|
||||
};
|
||||
#endif
|
||||
620
src/tracks/graph.cpp
Normal file
620
src/tracks/graph.cpp
Normal file
@@ -0,0 +1,620 @@
|
||||
//
|
||||
// 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 "tracks/graph.hpp"
|
||||
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IMesh.h>
|
||||
#include <IMeshSceneNode.h>
|
||||
#include <ISceneManager.h>
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/glwrap.hpp"
|
||||
#include "graphics/shaders.hpp"
|
||||
#include "graphics/rtts.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "tracks/arena_node_3d.hpp"
|
||||
#include "tracks/drive_node_2d.hpp"
|
||||
#include "tracks/drive_node_3d.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
const int Graph::UNKNOWN_SECTOR = -1;
|
||||
Graph *Graph::m_graph = NULL;
|
||||
// -----------------------------------------------------------------------------
|
||||
Graph::Graph()
|
||||
{
|
||||
m_scaling = 0;
|
||||
m_node = NULL;
|
||||
m_mesh = NULL;
|
||||
m_mesh_buffer = NULL;
|
||||
m_new_rtt = NULL;
|
||||
m_bb_min = Vec3( 99999, 99999, 99999);
|
||||
m_bb_max = Vec3(-99999, -99999, -99999);
|
||||
} // Graph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
Graph::~Graph()
|
||||
{
|
||||
if (m_new_rtt != NULL)
|
||||
{
|
||||
delete m_new_rtt;
|
||||
m_new_rtt = NULL;
|
||||
}
|
||||
|
||||
if (UserConfigParams::m_track_debug)
|
||||
cleanupDebugMesh();
|
||||
|
||||
for (unsigned int i = 0; i < m_all_nodes.size(); i++)
|
||||
{
|
||||
delete m_all_nodes[i];
|
||||
}
|
||||
m_all_nodes.clear();
|
||||
} // ~Graph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the debug mesh to display the graph on top of the track
|
||||
* model. */
|
||||
void Graph::createDebugMesh()
|
||||
{
|
||||
if (getNumNodes() <= 0) return; // no debug output if not graph
|
||||
|
||||
createMesh(/*show_invisible*/true,
|
||||
/*enable_transparency*/true);
|
||||
|
||||
video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices();
|
||||
for (unsigned int i = 0; i < m_mesh_buffer->getVertexCount(); i++)
|
||||
{
|
||||
// Swap the alpha and back
|
||||
v[i].Color.setAlpha((i%2) ? 64 : 255);
|
||||
}
|
||||
m_node = irr_driver->addMesh(m_mesh, "track-debug-mesh");
|
||||
#ifdef DEBUG
|
||||
m_node->setName("track-debug-mesh");
|
||||
#endif
|
||||
|
||||
} // createDebugMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Cleans up the debug mesh */
|
||||
void Graph::cleanupDebugMesh()
|
||||
{
|
||||
if (m_node != NULL)
|
||||
irr_driver->removeNode(m_node);
|
||||
|
||||
m_node = NULL;
|
||||
// No need to call irr_driber->removeMeshFromCache, since the mesh
|
||||
// was manually made and so never added to the mesh cache.
|
||||
m_mesh->drop();
|
||||
m_mesh = NULL;
|
||||
} // cleanupDebugMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap()
|
||||
*/
|
||||
void Graph::createMesh(bool show_invisible, bool enable_transparency,
|
||||
const video::SColor *track_color)
|
||||
{
|
||||
// The debug track will not be lighted or culled.
|
||||
video::SMaterial m;
|
||||
m.BackfaceCulling = false;
|
||||
m.Lighting = false;
|
||||
if (enable_transparency)
|
||||
m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
m.setTexture(0, getUnicolorTexture(video::SColor(255, 255, 255, 255)));
|
||||
m.setTexture(1, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
|
||||
m.setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
|
||||
m_mesh = irr_driver->createQuadMesh(&m);
|
||||
m_mesh_buffer = m_mesh->getMeshBuffer(0);
|
||||
assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD);
|
||||
|
||||
unsigned int n = 0;
|
||||
const unsigned int total_nodes = getNumNodes();
|
||||
|
||||
// Count the number of quads to display (some quads might be invisible)
|
||||
for (unsigned int i = 0; i < total_nodes; i++)
|
||||
{
|
||||
if (show_invisible || !m_all_nodes[i]->isInvisible())
|
||||
n++;
|
||||
}
|
||||
|
||||
// Four vertices for each of the n-1 remaining quads
|
||||
video::S3DVertex *new_v = new video::S3DVertex[4*n];
|
||||
// Each quad consists of 2 triangles with 3 elements, so
|
||||
// we need 2*3 indices for each quad.
|
||||
irr::u16 *ind = new irr::u16[6*n];
|
||||
video::SColor c(255, 255, 0, 0);
|
||||
|
||||
if (track_color)
|
||||
c = *track_color;
|
||||
|
||||
// Now add all quads
|
||||
int i = 0;
|
||||
for (unsigned int count = 0; count < total_nodes; count++)
|
||||
{
|
||||
// Ignore invisible quads
|
||||
if (!show_invisible && m_all_nodes[count]->isInvisible())
|
||||
continue;
|
||||
|
||||
// Swap the colours from red to blue and back
|
||||
if (!track_color)
|
||||
{
|
||||
c.setRed ((i%2) ? 255 : 0);
|
||||
c.setBlue((i%2) ? 0 : 255);
|
||||
}
|
||||
|
||||
video::SColor this_color = c;
|
||||
differentNodeColor(count, &this_color);
|
||||
// Transfer the 4 points of the current quad to the list of vertices
|
||||
m_all_nodes[count]->getVertices(new_v+4*i, this_color);
|
||||
|
||||
// Set up the indices for the triangles
|
||||
// (note, afaik with opengl we could use quads directly, but the code
|
||||
// would not be portable to directx anymore).
|
||||
ind[6*i ] = 4*i+2; // First triangle: vertex 0, 1, 2
|
||||
ind[6*i+1] = 4*i+1;
|
||||
ind[6*i+2] = 4*i;
|
||||
ind[6*i+3] = 4*i+3; // second triangle: vertex 0, 1, 3
|
||||
ind[6*i+4] = 4*i+2;
|
||||
ind[6*i+5] = 4*i;
|
||||
i++;
|
||||
}
|
||||
|
||||
m_mesh_buffer->append(new_v, n*4, ind, n*6);
|
||||
|
||||
if (hasLapLine())
|
||||
{
|
||||
video::S3DVertex lap_v[4];
|
||||
irr::u16 lap_ind[6];
|
||||
video::SColor lap_color(128, 255, 0, 0);
|
||||
m_all_nodes[0]->getVertices(lap_v, lap_color);
|
||||
|
||||
// Now scale the length (distance between vertix 0 and 3
|
||||
// and between 1 and 2) to be 'length':
|
||||
// Length of the lap line about 3% of the 'height'
|
||||
// of the track.
|
||||
const float length = (m_bb_max.getZ()-m_bb_min.getZ())*0.03f;
|
||||
|
||||
core::vector3df dl = lap_v[3].Pos-lap_v[0].Pos;
|
||||
float ll2 = dl.getLengthSQ();
|
||||
if (ll2 < 0.001)
|
||||
lap_v[3].Pos = lap_v[0].Pos+core::vector3df(0, 0, 1);
|
||||
else
|
||||
lap_v[3].Pos = lap_v[0].Pos+dl*length/sqrt(ll2);
|
||||
|
||||
core::vector3df dr = lap_v[2].Pos-lap_v[1].Pos;
|
||||
float lr2 = dr.getLengthSQ();
|
||||
if (lr2 < 0.001)
|
||||
lap_v[2].Pos = lap_v[1].Pos+core::vector3df(0, 0, 1);
|
||||
else
|
||||
lap_v[2].Pos = lap_v[1].Pos+dr*length/sqrt(lr2);
|
||||
lap_ind[0] = 2;
|
||||
lap_ind[1] = 1;
|
||||
lap_ind[2] = 0;
|
||||
lap_ind[3] = 3;
|
||||
lap_ind[4] = 2;
|
||||
lap_ind[5] = 0;
|
||||
// Set it a bit higher to avoid issued with z fighting,
|
||||
// i.e. part of the lap line might not be visible.
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
lap_v[i].Pos.Y += 0.1f;
|
||||
#ifndef USE_TEXTURED_LINE
|
||||
m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
|
||||
#else
|
||||
lap_v[0].TCoords = core::vector2df(0,0);
|
||||
lap_v[1].TCoords = core::vector2df(3,0);
|
||||
lap_v[2].TCoords = core::vector2df(3,1);
|
||||
lap_v[3].TCoords = core::vector2df(0,1);
|
||||
m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
|
||||
video::SMaterial &m = m_mesh_buffer->getMaterial();
|
||||
video::ITexture *t = irr_driver->getTexture("chess.png");
|
||||
m.setTexture(0, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Instead of setting the bounding boxes, we could just disable culling,
|
||||
// since the debug track should always be drawn.
|
||||
//m_node->setAutomaticCulling(scene::EAC_OFF);
|
||||
m_mesh_buffer->recalculateBoundingBox();
|
||||
m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox());
|
||||
|
||||
m_mesh_buffer->getMaterial().setTexture(0, irr_driver
|
||||
->getTexture("unlit.png"));
|
||||
|
||||
delete[] ind;
|
||||
delete[] new_v;
|
||||
} // createMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Takes a snapshot of the graph so they can be used as minimap.
|
||||
*/
|
||||
void Graph::makeMiniMap(const core::dimension2du &dimension,
|
||||
const std::string &name,
|
||||
const video::SColor &fill_color,
|
||||
video::ITexture** oldRttMinimap,
|
||||
FrameBuffer** newRttMinimap)
|
||||
{
|
||||
// Skip minimap when profiling
|
||||
if (ProfileWorld::isNoGraphics()) return;
|
||||
|
||||
const video::SColor oldClearColor = World::getWorld()->getClearColor();
|
||||
World::getWorld()
|
||||
->setClearbackBufferColor(video::SColor(0, 255, 255, 255));
|
||||
World::getWorld()->forceFogDisabled(true);
|
||||
*oldRttMinimap = NULL;
|
||||
*newRttMinimap = NULL;
|
||||
|
||||
RTT* newRttProvider = NULL;
|
||||
IrrDriver::RTTProvider* oldRttProvider = NULL;
|
||||
if (CVS->isGLSL())
|
||||
{
|
||||
m_new_rtt = newRttProvider =
|
||||
new RTT(dimension.Width, dimension.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldRttProvider = new IrrDriver::RTTProvider(dimension, name, true);
|
||||
}
|
||||
|
||||
irr_driver->getSceneManager()
|
||||
->setAmbientLight(video::SColor(255, 255, 255, 255));
|
||||
|
||||
createMesh(/*show_invisible part of the track*/ false,
|
||||
/*enable_transparency*/ false,
|
||||
/*track_color*/ &fill_color);
|
||||
|
||||
m_node = irr_driver->addMesh(m_mesh, "mini_map");
|
||||
#ifdef DEBUG
|
||||
m_node->setName("minimap-mesh");
|
||||
#endif
|
||||
|
||||
m_node->setAutomaticCulling(0);
|
||||
m_node->setMaterialFlag(video::EMF_LIGHTING, false);
|
||||
|
||||
// Add the camera:
|
||||
// ---------------
|
||||
scene::ICameraSceneNode *camera = irr_driver->addCameraSceneNode();
|
||||
Vec3 center = (m_bb_max+m_bb_min)*0.5f;
|
||||
|
||||
float dx = m_bb_max.getX()-m_bb_min.getX();
|
||||
float dz = m_bb_max.getZ()-m_bb_min.getZ();
|
||||
|
||||
// Set the scaling correctly. Also the center point (which is used
|
||||
// as the camera position) needs to be adjusted: the track must
|
||||
// be aligned to the left/top of the texture which is used (otherwise
|
||||
// mapPoint2MiniMap doesn't work), so adjust the camera position
|
||||
// that the track is properly aligned (view from the side):
|
||||
// c camera
|
||||
// / \ .
|
||||
// / \ <--- camera angle
|
||||
// / \ .
|
||||
// { [-] } <--- track flat (viewed from the side)
|
||||
// If [-] is the shorter side of the track, then the camera will
|
||||
// actually render the area in { } - which is the length of the
|
||||
// longer side of the track.
|
||||
// To align the [-] side to the left, the camera must be moved
|
||||
// the distance betwwen '{' and '[' to the right. This distance
|
||||
// is exacly (longer_side - shorter_side) / 2.
|
||||
// So, adjust the center point by this amount:
|
||||
if (dz > dx)
|
||||
{
|
||||
center.setX(center.getX() + (dz-dx)*0.5f);
|
||||
m_scaling = dimension.Width / dz;
|
||||
}
|
||||
else
|
||||
{
|
||||
center.setZ(center.getZ() + (dx-dz)*0.5f);
|
||||
m_scaling = dimension.Width / dx;
|
||||
}
|
||||
|
||||
float range = (dx>dz) ? dx : dz;
|
||||
|
||||
core::matrix4 projection;
|
||||
projection.buildProjectionMatrixOrthoLH
|
||||
(range /* width */, range /* height */, -1,
|
||||
m_bb_max.getY()-m_bb_min.getY()+1);
|
||||
camera->setProjectionMatrix(projection, true);
|
||||
|
||||
irr_driver->suppressSkyBox();
|
||||
irr_driver->clearLights();
|
||||
|
||||
// Adjust Y position by +1 for max, -1 for min - this helps in case that
|
||||
// the maximum Y coordinate is negative (otherwise the minimap is mirrored)
|
||||
// and avoids problems for tracks which have a flat (max Y=min Y) minimap.
|
||||
camera->setPosition(core::vector3df(center.getX(), m_bb_min.getY() + 1.0f,
|
||||
center.getZ()));
|
||||
//camera->setPosition(core::vector3df(center.getX() - 5.0f,
|
||||
// m_bb_min.getY() - 1 - 5.0f, center.getZ() - 15.0f));
|
||||
camera->setUpVector(core::vector3df(0, 0, 1));
|
||||
camera->setTarget(core::vector3df(center.getX(), m_bb_min.getY() - 1,
|
||||
center.getZ()));
|
||||
//camera->setAspectRatio(1.0f);
|
||||
camera->updateAbsolutePosition();
|
||||
|
||||
video::ITexture* texture = NULL;
|
||||
FrameBuffer* frame_buffer = NULL;
|
||||
|
||||
if (CVS->isGLSL())
|
||||
{
|
||||
frame_buffer = newRttProvider->render(camera,
|
||||
GUIEngine::getLatestDt());
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = oldRttProvider->renderToTexture();
|
||||
delete oldRttProvider;
|
||||
}
|
||||
|
||||
cleanupDebugMesh();
|
||||
irr_driver->removeCameraSceneNode(camera);
|
||||
|
||||
if (texture == NULL && frame_buffer == NULL)
|
||||
{
|
||||
Log::error("Graph", "[makeMiniMap] WARNING: RTT does not"
|
||||
"appear to work, mini-map will not be available.");
|
||||
}
|
||||
|
||||
*oldRttMinimap = texture;
|
||||
*newRttMinimap = frame_buffer;
|
||||
World::getWorld()->setClearbackBufferColor(oldClearColor);
|
||||
World::getWorld()->forceFogDisabled(false);
|
||||
|
||||
irr_driver->getSceneManager()->clear();
|
||||
VAOManager::kill();
|
||||
irr_driver->clearGlowingNodes();
|
||||
irr_driver->clearLights();
|
||||
irr_driver->clearForcedBloom();
|
||||
irr_driver->clearBackgroundNodes();
|
||||
} // makeMiniMap
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Returns the 2d coordinates of a point when drawn on the mini map
|
||||
* texture.
|
||||
* \param xyz Coordinates of the point to map.
|
||||
* \param draw_at The coordinates in pixel on the mini map of the point,
|
||||
* only the first two coordinates will be used.
|
||||
*/
|
||||
void Graph::mapPoint2MiniMap(const Vec3 &xyz,Vec3 *draw_at) const
|
||||
{
|
||||
draw_at->setX((xyz.getX()-m_bb_min.getX())*m_scaling);
|
||||
draw_at->setY((xyz.getZ()-m_bb_min.getZ())*m_scaling);
|
||||
|
||||
} // mapPoint
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
// Find the normal of this quad by computing the normal of two triangles
|
||||
// and taking their average.
|
||||
core::triangle3df tri1(p0.toIrrVector(), p1.toIrrVector(),
|
||||
p2.toIrrVector());
|
||||
core::triangle3df tri2(p0.toIrrVector(), p2.toIrrVector(),
|
||||
p3.toIrrVector());
|
||||
Vec3 normal1 = tri1.getNormal();
|
||||
Vec3 normal2 = tri2.getNormal();
|
||||
Vec3 normal = -0.5f * (normal1 + normal2);
|
||||
normal.normalize();
|
||||
|
||||
// Use the angle between the normal and an up vector to choose 3d/2d quad
|
||||
const float angle = normal.angle(Vec3(0, 1, 0));
|
||||
|
||||
Quad* q = NULL;
|
||||
if (angle > 0.5f)
|
||||
{
|
||||
Log::debug("Graph", "3d node created, normal: %f, %f, %f",
|
||||
normal.x(), normal.y(), normal.z());
|
||||
if (is_arena)
|
||||
{
|
||||
q = new ArenaNode3D(p0, p1, p2, p3, normal, node_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
q = new DriveNode3D(p0, p1, p2, p3, normal, node_index, invisible,
|
||||
ai_ignore);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::debug("Graph", "2d node created, normal: %f, %f, %f",
|
||||
normal.x(), normal.y(), normal.z());
|
||||
if (is_arena)
|
||||
{
|
||||
q = new ArenaNode(p0, p1, p2, p3, normal, node_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
q = new DriveNode2D(p0, p1, p2, p3, normal, node_index, invisible,
|
||||
ai_ignore);
|
||||
}
|
||||
}
|
||||
m_all_nodes.push_back(q);
|
||||
|
||||
m_bb_max.max(p0); m_bb_max.max(p1); m_bb_max.max(p2); m_bb_max.max(p3);
|
||||
m_bb_min.min(p0); m_bb_min.min(p1); m_bb_min.min(p2); m_bb_min.min(p3);
|
||||
|
||||
} // createQuad
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** findRoadSector returns in which sector on the road the position
|
||||
* xyz is. If xyz is not on top of the road, it sets UNKNOWN_SECTOR as sector.
|
||||
*
|
||||
* \param xyz Position for which the segment should be determined.
|
||||
* \param sector Contains the previous sector (as a shortcut, since usually
|
||||
* the sector is the same as the last one), and on return the result
|
||||
* \param all_sectors If this is not NULL, it is a list of all sectors to
|
||||
* test. This is used by the AI to make sure that it ends up on the
|
||||
* selected way in case of a branch, and also to make sure that it
|
||||
* doesn't skip e.g. a loop (see explanation below for details).
|
||||
*/
|
||||
void Graph::findRoadSector(const Vec3& xyz, int *sector,
|
||||
std::vector<int> *all_sectors,
|
||||
bool ignore_vertical) const
|
||||
{
|
||||
// Most likely the kart will still be on the sector it was before,
|
||||
// so this simple case is tested first.
|
||||
if (*sector!=UNKNOWN_SECTOR &&
|
||||
getQuad(*sector)->pointInside(xyz, ignore_vertical))
|
||||
{
|
||||
return;
|
||||
} // if still on same quad
|
||||
|
||||
// Now we search through all quads, starting with
|
||||
// the current one
|
||||
int indx = *sector;
|
||||
|
||||
// If a current sector is given, and max_lookahead is specify, only test
|
||||
// the next max_lookahead quads instead of testing the whole graph.
|
||||
// This is necessary for the AI: if the track contains a loop, e.g.:
|
||||
// -A--+---B---+----F--------
|
||||
// E C
|
||||
// +---D---+
|
||||
// and the track is supposed to be driven: ABCDEBF, the AI might find
|
||||
// the quad on F, and then keep on going straight ahead instead of
|
||||
// using the loop at all.
|
||||
unsigned int max_count = (*sector!=UNKNOWN_SECTOR && all_sectors!=NULL)
|
||||
? (unsigned int)all_sectors->size()
|
||||
: (unsigned int)m_all_nodes.size();
|
||||
*sector = UNKNOWN_SECTOR;
|
||||
for(unsigned int i=0; i<max_count; i++)
|
||||
{
|
||||
if(all_sectors)
|
||||
indx = (*all_sectors)[i];
|
||||
else
|
||||
indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0;
|
||||
const Quad* q = getQuad(indx);
|
||||
if(q->pointInside(xyz, ignore_vertical))
|
||||
{
|
||||
*sector = indx;
|
||||
return;
|
||||
}
|
||||
} // for i<m_all_nodes.size()
|
||||
|
||||
return;
|
||||
} // findRoadSector
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** findOutOfRoadSector finds the sector where XYZ is, but as it name
|
||||
implies, it is more accurate for the outside of the track than the
|
||||
inside, and for STK's needs the accuracy on top of the track is
|
||||
unacceptable; but if this was a 2D function, the accuracy for out
|
||||
of road sectors would be perfect.
|
||||
|
||||
To find the sector we look for the closest line segment from the
|
||||
right and left drivelines, and the number of that segment will be
|
||||
the sector.
|
||||
|
||||
The SIDE argument is used to speed up the function only; if we know
|
||||
that XYZ is on the left or right side of the track, we know that
|
||||
the closest driveline must be the one that matches that condition.
|
||||
In reality, the side used in STK is the one from the previous frame,
|
||||
but in order to move from one side to another a point would go
|
||||
through the middle, that is handled by findRoadSector() which doesn't
|
||||
has speed ups based on the side.
|
||||
|
||||
NOTE: This method of finding the sector outside of the road is *not*
|
||||
perfect: if two line segments have a similar altitude (but enough to
|
||||
let a kart get through) and they are very close on a 2D system,
|
||||
if a kart is on the air it could be closer to the top line segment
|
||||
even if it is supposed to be on the sector of the lower line segment.
|
||||
Probably the best solution would be to construct a quad that reaches
|
||||
until the next higher overlapping line segment, and find the closest
|
||||
one to XYZ.
|
||||
*/
|
||||
int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector,
|
||||
std::vector<int> *all_sectors,
|
||||
bool ignore_vertical) const
|
||||
{
|
||||
int count = (all_sectors!=NULL) ? (int)all_sectors->size() : getNumNodes();
|
||||
int current_sector = 0;
|
||||
if(curr_sector != UNKNOWN_SECTOR && !all_sectors)
|
||||
{
|
||||
// We have to test all quads here: reason is that on track with
|
||||
// shortcuts the n quads of the main drivelines is followed by
|
||||
// the quads of the shortcuts. So after quad n-1 (the last one
|
||||
// before the lap counting line) quad n will not be 0 (the first
|
||||
// quad after the lap counting line), but one of the quads on a
|
||||
// shortcut. If we only tested a limited number of quads to
|
||||
// improve the performance the crossing of a lap might not be
|
||||
// detected (because quad 0 is not tested, only quads on the
|
||||
// shortcuts are tested). If this should become a performance
|
||||
// bottleneck, we need to set up a graph of 'next' quads for each
|
||||
// quad (similar to what the AI does), and only test the quads
|
||||
// in this graph.
|
||||
const int LIMIT = getNumNodes();
|
||||
count = LIMIT;
|
||||
// Start 10 quads before the current quad, so the quads closest
|
||||
// to the current position are tested first.
|
||||
current_sector = curr_sector -10;
|
||||
if(current_sector<0) current_sector += getNumNodes();
|
||||
}
|
||||
|
||||
int min_sector = UNKNOWN_SECTOR;
|
||||
float min_dist_2 = 999999.0f*999999.0f;
|
||||
|
||||
// If a kart is falling and in between (or too far below)
|
||||
// a driveline point it might not fulfill
|
||||
// the height condition. So we run the test twice: first with height
|
||||
// condition, then again without the height condition - just to make sure
|
||||
// it always comes back with some kind of quad.
|
||||
for(int phase=0; phase<2; phase++)
|
||||
{
|
||||
for(int j=0; j<count; j++)
|
||||
{
|
||||
int next_sector;
|
||||
if(all_sectors)
|
||||
next_sector = (*all_sectors)[j];
|
||||
else
|
||||
next_sector = current_sector+1 == (int)getNumNodes()
|
||||
? 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);
|
||||
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;
|
||||
} // for j
|
||||
// Leave in phase 0 if any sector was found.
|
||||
if(min_sector!=UNKNOWN_SECTOR)
|
||||
return min_sector;
|
||||
} // phase
|
||||
|
||||
if(min_sector==UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::info("Graph", "unknown sector found.");
|
||||
}
|
||||
return min_sector;
|
||||
} // findOutOfRoadSector
|
||||
155
src/tracks/graph.hpp
Normal file
155
src/tracks/graph.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// 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_GRAPH_HPP
|
||||
#define HEADER_GRAPH_HPP
|
||||
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
#include <dimension2d.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; }
|
||||
namespace video { class ITexture; struct S3DVertex; class SColor; }
|
||||
}
|
||||
|
||||
using namespace irr;
|
||||
|
||||
class FrameBuffer;
|
||||
class Quad;
|
||||
class RTT;
|
||||
|
||||
/**
|
||||
* \brief This class stores a graph of quads. It uses a 'simplified singleton'
|
||||
* design pattern: it has a static create function to create exactly instance,
|
||||
* a destroy function, and a get function (that does not have the side effect
|
||||
* of the 'normal singleton' design pattern to create an instance). Besides
|
||||
* saving on the if statement in get(), this is necessary since certain race
|
||||
* modes might not have a quad graph at all (e.g. arena without navmesh). So
|
||||
* get() returns NULL in this case, and this is tested where necessary.
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class Graph : public NoCopy
|
||||
{
|
||||
protected:
|
||||
static Graph* m_graph;
|
||||
|
||||
std::vector<Quad*> m_all_nodes;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Factory method to dynamic create 2d / 3d quad for drive and arena
|
||||
* 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);
|
||||
|
||||
private:
|
||||
/** The 2d bounding box, used for hashing. */
|
||||
Vec3 m_bb_min;
|
||||
Vec3 m_bb_max;
|
||||
|
||||
RTT* m_new_rtt;
|
||||
|
||||
/** The node of the graph mesh. */
|
||||
scene::ISceneNode *m_node;
|
||||
|
||||
/** The mesh of the graph mesh. */
|
||||
scene::IMesh *m_mesh;
|
||||
|
||||
/** The actual mesh buffer storing the graph. */
|
||||
scene::IMeshBuffer *m_mesh_buffer;
|
||||
|
||||
/** Scaling for mini map. */
|
||||
float m_scaling;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void createMesh(bool show_invisible=true,
|
||||
bool enable_transparency=false,
|
||||
const video::SColor *track_color=NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
void cleanupDebugMesh();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool hasLapLine() const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void differentNodeColor(int n, video::SColor* c) const = 0;
|
||||
|
||||
public:
|
||||
static const int UNKNOWN_SECTOR;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the one instance of this object. It is possible that there
|
||||
* is no instance created (e.g. arena without navmesh) so we don't assert
|
||||
* that an instance exist. */
|
||||
static Graph* get() { return m_graph; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set the graph (either drive or arena graph for now). */
|
||||
static void setGraph(Graph* graph)
|
||||
{
|
||||
assert(m_graph == NULL);
|
||||
m_graph = graph;
|
||||
} // setGraph
|
||||
// ------------------------------------------------------------------------
|
||||
/** Cleans up the graph. It is possible that this function is called even
|
||||
* if no instance exists (e.g. arena without navmesh). So it is not an
|
||||
* error if there is no instance. */
|
||||
static void destroy()
|
||||
{
|
||||
if (m_graph)
|
||||
{
|
||||
delete m_graph;
|
||||
m_graph = NULL;
|
||||
}
|
||||
} // destroy
|
||||
// ------------------------------------------------------------------------
|
||||
Graph();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~Graph();
|
||||
// ------------------------------------------------------------------------
|
||||
void createDebugMesh();
|
||||
// ------------------------------------------------------------------------
|
||||
void makeMiniMap(const core::dimension2du &where, const std::string &name,
|
||||
const video::SColor &fill_color,
|
||||
video::ITexture** oldRttMinimap,
|
||||
FrameBuffer** newRttMinimap);
|
||||
// ------------------------------------------------------------------------
|
||||
void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const;
|
||||
// ------------------------------------------------------------------------
|
||||
Quad* getQuad(unsigned int i) const
|
||||
{
|
||||
assert(i < m_all_nodes.size());
|
||||
return m_all_nodes[i];
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getNumNodes() const { return m_all_nodes.size(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void findRoadSector(const Vec3& XYZ, int *sector,
|
||||
std::vector<int> *all_sectors = NULL,
|
||||
bool ignore_vertical = false) const;
|
||||
// ------------------------------------------------------------------------
|
||||
int findOutOfRoadSector(const Vec3& xyz,
|
||||
const int curr_sector = UNKNOWN_SECTOR,
|
||||
std::vector<int> *all_sectors = NULL,
|
||||
bool ignore_vertical = false) const;
|
||||
|
||||
}; // Graph
|
||||
|
||||
#endif
|
||||
@@ -1,217 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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, B
|
||||
|
||||
#include "tracks/quad_graph.hpp"
|
||||
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor. Saves the quad index which belongs to this graph node.
|
||||
* \param index Index of the quad to use for this node (in QuadSet).
|
||||
*/
|
||||
GraphNode::GraphNode(unsigned int quad_index, unsigned int node_index)
|
||||
{
|
||||
if (quad_index >= QuadSet::get()->getNumberOfQuads())
|
||||
Log::fatal("GraphNode", "No driveline found, or empty driveline.");
|
||||
|
||||
m_quad_index = quad_index;
|
||||
m_node_index = node_index;
|
||||
m_distance_from_start = -1.0f;
|
||||
|
||||
const Quad &quad = QuadSet::get()->getQuad(m_quad_index);
|
||||
// The following values should depend on the actual orientation
|
||||
// of the quad. ATM we always assume that indices 0,1 are the lower end,
|
||||
// and 2,3 are the upper end (or the reverse if reverse mode is selected).
|
||||
// The width is the average width at the beginning and at the end.
|
||||
m_right_unit_vector = ( quad[0]-quad[1]
|
||||
+quad[3]-quad[2]) * 0.5f;
|
||||
m_right_unit_vector.normalize();
|
||||
|
||||
m_width = ( (quad[1]-quad[0]).length()
|
||||
+ (quad[3]-quad[2]).length() ) * 0.5f;
|
||||
|
||||
if(QuadGraph::get()->isReverse())
|
||||
{
|
||||
m_lower_center = (quad[2]+quad[3]) * 0.5f;
|
||||
m_upper_center = (quad[0]+quad[1]) * 0.5f;
|
||||
m_right_unit_vector *= -1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lower_center = (quad[0]+quad[1]) * 0.5f;
|
||||
m_upper_center = (quad[2]+quad[3]) * 0.5f;
|
||||
}
|
||||
m_line = core::line2df(m_upper_center.getX(), m_upper_center.getZ(),
|
||||
m_lower_center.getX(), m_lower_center.getZ() );
|
||||
// Only this 2d point is needed later
|
||||
m_lower_center_2d = core::vector2df(m_lower_center.getX(),
|
||||
m_lower_center.getZ() );
|
||||
|
||||
} // GraphNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Adds a successor to a node. This function will also pre-compute certain
|
||||
* values (like distance from this node to the successor, angle (in world)
|
||||
* between this node and the successor.
|
||||
* \param to The index of the graph node of the successor.
|
||||
*/
|
||||
void GraphNode::addSuccessor(unsigned int to)
|
||||
{
|
||||
m_successor_nodes.push_back(to);
|
||||
// m_quad_index is the quad index
|
||||
const Quad &this_quad = QuadSet::get()->getQuad(m_quad_index);
|
||||
// to is the graph node
|
||||
GraphNode &gn = QuadGraph::get()->getNode(to);
|
||||
const Quad &next_quad = QuadGraph::get()->getQuadOfNode(to);
|
||||
|
||||
// Note that the first predecessor is (because of the way the quad graph
|
||||
// is exported) the most 'natural' one, i.e. the one on the main
|
||||
// driveline.
|
||||
gn.m_predecessor_nodes.push_back(m_node_index);
|
||||
|
||||
Vec3 d = m_lower_center - QuadGraph::get()->getNode(to).m_lower_center;
|
||||
m_distance_to_next.push_back(d.length());
|
||||
|
||||
Vec3 diff = next_quad.getCenter() - this_quad.getCenter();
|
||||
m_angle_to_next.push_back(atan2(diff.getX(), diff.getZ()));
|
||||
|
||||
} // addSuccessor
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** If this node has more than one successor, it will set up a vector that
|
||||
* contains the direction to use when a certain graph node X should be
|
||||
* reached.
|
||||
*/
|
||||
void GraphNode::setupPathsToNode()
|
||||
{
|
||||
if(m_successor_nodes.size()<2) return;
|
||||
|
||||
const unsigned int num_nodes = QuadGraph::get()->getNumNodes();
|
||||
m_path_to_node.resize(num_nodes);
|
||||
|
||||
// Initialise each graph node with -1, indicating that
|
||||
// it hasn't been reached yet.
|
||||
for(unsigned int i=0; i<num_nodes; i++)
|
||||
m_path_to_node[i] = -1;
|
||||
|
||||
// Indicate that this node can be reached from this node by following
|
||||
// successor 0 - just a dummy value that might only be used during the
|
||||
// recursion below.
|
||||
m_path_to_node[m_node_index] = 0;
|
||||
|
||||
// A simple depth first search is used to determine which successor to
|
||||
// use to reach a certain graph node. Using Dijkstra's algorithm would
|
||||
// give the shortest way to reach a certain node, but the shortest way
|
||||
// might involve some shortcuts which are hidden, and should therefore
|
||||
// not be used.
|
||||
for(unsigned int i=0; i<getNumberOfSuccessors(); i++)
|
||||
{
|
||||
GraphNode &gn = QuadGraph::get()->getNode(getSuccessor(i));
|
||||
gn.markAllSuccessorsToUse(i, &m_path_to_node);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
for(unsigned int i = 0; i < m_path_to_node.size(); ++i)
|
||||
{
|
||||
if(m_path_to_node[i] == -1)
|
||||
Log::warn("GraphNode", "No path to node %d found on graph node %d.",
|
||||
i, m_node_index);
|
||||
}
|
||||
#endif
|
||||
} // setupPathsToNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function marks that the successor n should be used to reach this
|
||||
* node. It then recursively (depth first) does the same for all its
|
||||
* successors. Depth-first
|
||||
* \param n The successor which should be used in m_path_node to reach
|
||||
* this node.
|
||||
* \param path_to_node The path-to-node data structure of the node for
|
||||
* which the paths are currently determined.
|
||||
*/
|
||||
void GraphNode::markAllSuccessorsToUse(unsigned int n,
|
||||
PathToNodeVector *path_to_node)
|
||||
{
|
||||
// End recursion if the path to this node has already been found.
|
||||
if( (*path_to_node)[m_node_index] >-1) return;
|
||||
|
||||
(*path_to_node)[m_node_index] = n;
|
||||
for(unsigned int i=0; i<getNumberOfSuccessors(); i++)
|
||||
{
|
||||
GraphNode &gn = QuadGraph::get()->getNode(getSuccessor(i));
|
||||
gn.markAllSuccessorsToUse(n, path_to_node);
|
||||
}
|
||||
} // markAllSuccesorsToUse
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void GraphNode::setDirectionData(unsigned int successor, DirectionType dir,
|
||||
unsigned int last_node_index)
|
||||
{
|
||||
if(m_direction.size()<successor+1)
|
||||
{
|
||||
m_direction.resize(successor+1);
|
||||
m_last_index_same_direction.resize(successor+1);
|
||||
}
|
||||
m_direction[successor] = dir;
|
||||
m_last_index_same_direction[successor] = last_node_index;
|
||||
} // setDirectionData
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the distance a point has from this quad in forward and sidewards
|
||||
* direction, i.e. how far forwards the point is from the beginning of the
|
||||
* quad, and how far to the side from the line connecting the center points
|
||||
* is it. All these computations are done in 2D only.
|
||||
* \param xyz The coordinates of the point.
|
||||
* \param result The X coordinate contains the sidewards distance, the
|
||||
* Z coordinate the forward distance.
|
||||
*/
|
||||
void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result)
|
||||
{
|
||||
core::vector2df xyz2d(xyz.getX(), xyz.getZ());
|
||||
core::vector2df closest = m_line.getClosestPoint(xyz2d);
|
||||
if(m_line.getPointOrientation(xyz2d)>0)
|
||||
result->setX( (closest-xyz2d).getLength()); // to the right
|
||||
else
|
||||
result->setX(-(closest-xyz2d).getLength()); // to the left
|
||||
result->setZ( m_distance_from_start +
|
||||
(closest-m_lower_center_2d).getLength());
|
||||
} // getDistances
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the square of the distance between the given point and any point
|
||||
* on the 'centre' line, i.e. the finite line from the middle point of the
|
||||
* lower end of the quad node to the middle point of the upper end of the
|
||||
* quad which belongs to this graph node. The value is computed in 2d only!
|
||||
* \param xyz The point for which the distance to the line is computed.
|
||||
*/
|
||||
float GraphNode::getDistance2FromPoint(const Vec3 &xyz)
|
||||
{
|
||||
core::vector2df xyz2d(xyz.getX(), xyz.getZ());
|
||||
core::vector2df closest = m_line.getClosestPoint(xyz2d);
|
||||
return (closest-xyz2d).getLengthSQ();
|
||||
} // getDistance2FromPoint
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void GraphNode::setChecklineRequirements(int latest_checkline)
|
||||
{
|
||||
m_checkline_requirements.push_back(latest_checkline);
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2015 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 "tracks/graph_structure.hpp"
|
||||
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IMesh.h>
|
||||
#include <IMeshSceneNode.h>
|
||||
#include <ISceneManager.h>
|
||||
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/glwrap.hpp"
|
||||
#include "graphics/shaders.hpp"
|
||||
#include "graphics/rtts.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
GraphStructure::GraphStructure()
|
||||
{
|
||||
m_min_coord = 0;
|
||||
m_scaling = 0;
|
||||
m_node = NULL;
|
||||
m_mesh = NULL;
|
||||
m_mesh_buffer = NULL;
|
||||
m_new_rtt = NULL;
|
||||
} // GraphStructure
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void GraphStructure::destroyRTT()
|
||||
{
|
||||
if (m_new_rtt != NULL)
|
||||
{
|
||||
delete m_new_rtt;
|
||||
m_new_rtt = NULL;
|
||||
}
|
||||
} // destroyRTT
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Cleans up the debug mesh */
|
||||
void GraphStructure::cleanupDebugMesh()
|
||||
{
|
||||
if (m_node != NULL)
|
||||
irr_driver->removeNode(m_node);
|
||||
|
||||
m_node = NULL;
|
||||
// No need to call irr_driber->removeMeshFromCache, since the mesh
|
||||
// was manually made and so never added to the mesh cache.
|
||||
m_mesh->drop();
|
||||
m_mesh = NULL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the debug mesh to display the graph on top of the track
|
||||
* model. */
|
||||
void GraphStructure::createDebugMesh()
|
||||
{
|
||||
if (getNumNodes() <= 0) return; // no debug output if not graph
|
||||
|
||||
createMesh(/*show_invisible*/true,
|
||||
/*enable_transparency*/true);
|
||||
|
||||
// Now colour the quads red/blue/red ...
|
||||
video::SColor c( 128, 255, 0, 0);
|
||||
video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices();
|
||||
for (unsigned int i = 0; i < m_mesh_buffer->getVertexCount(); i++)
|
||||
{
|
||||
// Swap the colours from red to blue and back
|
||||
c.setRed ((i%2) ? 255 : 0);
|
||||
c.setBlue((i%2) ? 0 : 255);
|
||||
v[i].Color = c;
|
||||
}
|
||||
m_node = irr_driver->addMesh(m_mesh, "track-debug-mesh");
|
||||
#ifdef DEBUG
|
||||
m_node->setName("track-debug-mesh");
|
||||
#endif
|
||||
|
||||
} // createDebugMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap() */
|
||||
void GraphStructure::createMesh(bool show_invisible,
|
||||
bool enable_transparency,
|
||||
const video::SColor *track_color)
|
||||
{
|
||||
// The debug track will not be lighted or culled.
|
||||
video::SMaterial m;
|
||||
m.BackfaceCulling = false;
|
||||
m.Lighting = false;
|
||||
if (enable_transparency)
|
||||
m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
m.setTexture(0, getUnicolorTexture(video::SColor(255, 255, 255, 255)));
|
||||
m.setTexture(1, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
|
||||
m.setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
|
||||
m_mesh = irr_driver->createQuadMesh(&m);
|
||||
m_mesh_buffer = m_mesh->getMeshBuffer(0);
|
||||
assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD);
|
||||
|
||||
unsigned int n = 0;
|
||||
const unsigned int total_nodes = getNumNodes();
|
||||
|
||||
// Count the number of quads to display (some quads might be invisible)
|
||||
for (unsigned int i = 0; i < total_nodes; i++)
|
||||
{
|
||||
if (show_invisible || !isNodeInvisible(i))
|
||||
n++;
|
||||
}
|
||||
|
||||
// Four vertices for each of the n-1 remaining quads
|
||||
video::S3DVertex *new_v = new video::S3DVertex[4*n];
|
||||
// Each quad consists of 2 triangles with 3 elements, so
|
||||
// we need 2*3 indices for each quad.
|
||||
irr::u16 *ind = new irr::u16[6*n];
|
||||
video::SColor c(255, 255, 0, 0);
|
||||
|
||||
if (track_color)
|
||||
c = *track_color;
|
||||
|
||||
// Now add all quads
|
||||
int i = 0;
|
||||
for (unsigned int count = 0; count < total_nodes; count++)
|
||||
{
|
||||
// Ignore invisible quads
|
||||
if (!show_invisible && isNodeInvisible(count))
|
||||
continue;
|
||||
|
||||
// Swap the colours from red to blue and back
|
||||
if (!track_color)
|
||||
{
|
||||
c.setRed ((i%2) ? 255 : 0);
|
||||
c.setBlue((i%2) ? 0 : 255);
|
||||
}
|
||||
|
||||
NodeColor nc = COLOR_RED;
|
||||
const bool different_color = differentNodeColor(count, &nc);
|
||||
// Transfer the 4 points of the current quad to the list of vertices
|
||||
set3DVerticesOfGraph(count, new_v+4*i, (different_color ?
|
||||
(nc == COLOR_RED ? video::SColor(255, 255, 0, 0) :
|
||||
video::SColor(255, 0, 0, 255)) : c));
|
||||
|
||||
// Set up the indices for the triangles
|
||||
// (note, afaik with opengl we could use quads directly, but the code
|
||||
// would not be portable to directx anymore).
|
||||
ind[6*i ] = 4*i+2; // First triangle: vertex 0, 1, 2
|
||||
ind[6*i+1] = 4*i+1;
|
||||
ind[6*i+2] = 4*i;
|
||||
ind[6*i+3] = 4*i+3; // second triangle: vertex 0, 1, 3
|
||||
ind[6*i+4] = 4*i+2;
|
||||
ind[6*i+5] = 4*i;
|
||||
i++;
|
||||
}
|
||||
|
||||
m_mesh_buffer->append(new_v, n*4, ind, n*6);
|
||||
|
||||
if (hasLapLine())
|
||||
{
|
||||
video::S3DVertex lap_v[4];
|
||||
irr::u16 lap_ind[6];
|
||||
video::SColor lap_color(128, 255, 0, 0);
|
||||
set3DVerticesOfGraph(0, lap_v, lap_color);
|
||||
|
||||
// Now scale the length (distance between vertix 0 and 3
|
||||
// and between 1 and 2) to be 'length':
|
||||
Vec3 bb_min, bb_max;
|
||||
getGraphBoundingBox(&bb_min, &bb_max);
|
||||
// Length of the lap line about 3% of the 'height'
|
||||
// of the track.
|
||||
const float length = (bb_max.getZ()-bb_min.getZ())*0.03f;
|
||||
|
||||
core::vector3df dl = lap_v[3].Pos-lap_v[0].Pos;
|
||||
float ll2 = dl.getLengthSQ();
|
||||
if (ll2 < 0.001)
|
||||
lap_v[3].Pos = lap_v[0].Pos+core::vector3df(0, 0, 1);
|
||||
else
|
||||
lap_v[3].Pos = lap_v[0].Pos+dl*length/sqrt(ll2);
|
||||
|
||||
core::vector3df dr = lap_v[2].Pos-lap_v[1].Pos;
|
||||
float lr2 = dr.getLengthSQ();
|
||||
if (lr2 < 0.001)
|
||||
lap_v[2].Pos = lap_v[1].Pos+core::vector3df(0, 0, 1);
|
||||
else
|
||||
lap_v[2].Pos = lap_v[1].Pos+dr*length/sqrt(lr2);
|
||||
lap_ind[0] = 2;
|
||||
lap_ind[1] = 1;
|
||||
lap_ind[2] = 0;
|
||||
lap_ind[3] = 3;
|
||||
lap_ind[4] = 2;
|
||||
lap_ind[5] = 0;
|
||||
// Set it a bit higher to avoid issued with z fighting,
|
||||
// i.e. part of the lap line might not be visible.
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
lap_v[i].Pos.Y += 0.1f;
|
||||
#ifndef USE_TEXTURED_LINE
|
||||
m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
|
||||
#else
|
||||
lap_v[0].TCoords = core::vector2df(0,0);
|
||||
lap_v[1].TCoords = core::vector2df(3,0);
|
||||
lap_v[2].TCoords = core::vector2df(3,1);
|
||||
lap_v[3].TCoords = core::vector2df(0,1);
|
||||
m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
|
||||
video::SMaterial &m = m_mesh_buffer->getMaterial();
|
||||
video::ITexture *t = irr_driver->getTexture("chess.png");
|
||||
m.setTexture(0, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Instead of setting the bounding boxes, we could just disable culling,
|
||||
// since the debug track should always be drawn.
|
||||
//m_node->setAutomaticCulling(scene::EAC_OFF);
|
||||
m_mesh_buffer->recalculateBoundingBox();
|
||||
m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox());
|
||||
|
||||
m_mesh_buffer->getMaterial().setTexture(0, irr_driver->getTexture("unlit.png"));
|
||||
|
||||
delete[] ind;
|
||||
delete[] new_v;
|
||||
} // createMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Takes a snapshot of the graph so they can be used as minimap.
|
||||
*/
|
||||
void GraphStructure::makeMiniMap(const core::dimension2du &dimension,
|
||||
const std::string &name,
|
||||
const video::SColor &fill_color,
|
||||
video::ITexture** oldRttMinimap,
|
||||
FrameBuffer** newRttMinimap)
|
||||
{
|
||||
// Skip minimap when profiling
|
||||
if (ProfileWorld::isNoGraphics()) return;
|
||||
|
||||
const video::SColor oldClearColor = World::getWorld()->getClearColor();
|
||||
World::getWorld()->setClearbackBufferColor(video::SColor(0, 255, 255, 255));
|
||||
World::getWorld()->forceFogDisabled(true);
|
||||
*oldRttMinimap = NULL;
|
||||
*newRttMinimap = NULL;
|
||||
|
||||
RTT* newRttProvider = NULL;
|
||||
IrrDriver::RTTProvider* oldRttProvider = NULL;
|
||||
if (CVS->isGLSL())
|
||||
{
|
||||
m_new_rtt = newRttProvider = new RTT(dimension.Width, dimension.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldRttProvider = new IrrDriver::RTTProvider(dimension, name, true);
|
||||
}
|
||||
|
||||
irr_driver->getSceneManager()->setAmbientLight(video::SColor(255, 255, 255, 255));
|
||||
|
||||
createMesh(/*show_invisible part of the track*/ false,
|
||||
/*enable_transparency*/ false,
|
||||
/*track_color*/ &fill_color);
|
||||
|
||||
m_node = irr_driver->addMesh(m_mesh, "mini_map");
|
||||
#ifdef DEBUG
|
||||
m_node->setName("minimap-mesh");
|
||||
#endif
|
||||
|
||||
m_node->setAutomaticCulling(0);
|
||||
m_node->setMaterialFlag(video::EMF_LIGHTING, false);
|
||||
|
||||
// Add the camera:
|
||||
// ---------------
|
||||
scene::ICameraSceneNode *camera = irr_driver->addCameraSceneNode();
|
||||
Vec3 bb_min, bb_max;
|
||||
getGraphBoundingBox(&bb_min, &bb_max);
|
||||
Vec3 center = (bb_max+bb_min)*0.5f;
|
||||
|
||||
float dx = bb_max.getX()-bb_min.getX();
|
||||
float dz = bb_max.getZ()-bb_min.getZ();
|
||||
|
||||
// Set the scaling correctly. Also the center point (which is used
|
||||
// as the camera position) needs to be adjusted: the track must
|
||||
// be aligned to the left/top of the texture which is used (otherwise
|
||||
// mapPoint2MiniMap doesn't work), so adjust the camera position
|
||||
// that the track is properly aligned (view from the side):
|
||||
// c camera
|
||||
// / \ .
|
||||
// / \ <--- camera angle
|
||||
// / \ .
|
||||
// { [-] } <--- track flat (viewed from the side)
|
||||
// If [-] is the shorter side of the track, then the camera will
|
||||
// actually render the area in { } - which is the length of the
|
||||
// longer side of the track.
|
||||
// To align the [-] side to the left, the camera must be moved
|
||||
// the distance betwwen '{' and '[' to the right. This distance
|
||||
// is exacly (longer_side - shorter_side) / 2.
|
||||
// So, adjust the center point by this amount:
|
||||
if (dz > dx)
|
||||
{
|
||||
center.setX(center.getX() + (dz-dx)*0.5f);
|
||||
m_scaling = dimension.Width / dz;
|
||||
}
|
||||
else
|
||||
{
|
||||
center.setZ(center.getZ() + (dx-dz)*0.5f);
|
||||
m_scaling = dimension.Width / dx;
|
||||
}
|
||||
|
||||
float range = (dx>dz) ? dx : dz;
|
||||
|
||||
core::matrix4 projection;
|
||||
projection.buildProjectionMatrixOrthoLH(range /* width */,
|
||||
range /* height */,
|
||||
-1, bb_max.getY()-bb_min.getY()+1);
|
||||
camera->setProjectionMatrix(projection, true);
|
||||
|
||||
irr_driver->suppressSkyBox();
|
||||
irr_driver->clearLights();
|
||||
|
||||
// Adjust Y position by +1 for max, -1 for min - this helps in case that
|
||||
// the maximum Y coordinate is negative (otherwise the minimap is mirrored)
|
||||
// and avoids problems for tracks which have a flat (max Y = min Y) minimap.
|
||||
camera->setPosition(core::vector3df(center.getX(), bb_min.getY() + 1.0f, center.getZ()));
|
||||
//camera->setPosition(core::vector3df(center.getX() - 5.0f, bb_min.getY() - 1 - 5.0f, center.getZ() - 15.0f));
|
||||
camera->setUpVector(core::vector3df(0, 0, 1));
|
||||
camera->setTarget(core::vector3df(center.getX(),bb_min.getY()-1,center.getZ()));
|
||||
//camera->setAspectRatio(1.0f);
|
||||
camera->updateAbsolutePosition();
|
||||
|
||||
video::ITexture* texture = NULL;
|
||||
FrameBuffer* frame_buffer = NULL;
|
||||
|
||||
if (CVS->isGLSL())
|
||||
{
|
||||
frame_buffer = newRttProvider->render(camera, GUIEngine::getLatestDt());
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = oldRttProvider->renderToTexture();
|
||||
delete oldRttProvider;
|
||||
}
|
||||
|
||||
cleanupDebugMesh();
|
||||
irr_driver->removeCameraSceneNode(camera);
|
||||
m_min_coord = bb_min;
|
||||
|
||||
|
||||
if (texture == NULL && frame_buffer == NULL)
|
||||
{
|
||||
Log::error("Graph Structure", "[makeMiniMap] WARNING: RTT does not"
|
||||
"appear to work, mini-map will not be available.");
|
||||
}
|
||||
|
||||
*oldRttMinimap = texture;
|
||||
*newRttMinimap = frame_buffer;
|
||||
World::getWorld()->setClearbackBufferColor(oldClearColor);
|
||||
World::getWorld()->forceFogDisabled(false);
|
||||
|
||||
irr_driver->getSceneManager()->clear();
|
||||
VAOManager::kill();
|
||||
irr_driver->clearGlowingNodes();
|
||||
irr_driver->clearLights();
|
||||
irr_driver->clearForcedBloom();
|
||||
irr_driver->clearBackgroundNodes();
|
||||
} // makeMiniMap
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Returns the 2d coordinates of a point when drawn on the mini map
|
||||
* texture.
|
||||
* \param xyz Coordinates of the point to map.
|
||||
* \param draw_at The coordinates in pixel on the mini map of the point,
|
||||
* only the first two coordinates will be used.
|
||||
*/
|
||||
void GraphStructure::mapPoint2MiniMap(const Vec3 &xyz,Vec3 *draw_at) const
|
||||
{
|
||||
draw_at->setX((xyz.getX()-m_min_coord.getX())*m_scaling);
|
||||
draw_at->setY((xyz.getZ()-m_min_coord.getZ())*m_scaling);
|
||||
|
||||
} // mapPoint
|
||||
@@ -1,101 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2015 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_GRAPH_STRUCTURE_HPP
|
||||
#define HEADER_GRAPH_STRUCTURE_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <dimension2d.h>
|
||||
#include <SColor.h>
|
||||
#include "utils/vec3.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; }
|
||||
namespace video { class ITexture; struct S3DVertex; }
|
||||
}
|
||||
using namespace irr;
|
||||
|
||||
class FrameBuffer;
|
||||
class RTT;
|
||||
|
||||
/**
|
||||
* \brief Virtual base class for a graph structure.
|
||||
* This is mainly used for drawing minimap in game.
|
||||
*
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class GraphStructure : public NoCopy
|
||||
{
|
||||
protected:
|
||||
|
||||
/** Used by soccer field with navmesh to draw goal line. */
|
||||
enum NodeColor
|
||||
{
|
||||
COLOR_BLUE,
|
||||
COLOR_RED
|
||||
};
|
||||
|
||||
void cleanupDebugMesh();
|
||||
void destroyRTT();
|
||||
|
||||
private:
|
||||
RTT* m_new_rtt;
|
||||
|
||||
/** The node of the graph mesh. */
|
||||
scene::ISceneNode *m_node;
|
||||
|
||||
/** The mesh of the graph mesh. */
|
||||
scene::IMesh *m_mesh;
|
||||
|
||||
/** The actual mesh buffer storing the graph. */
|
||||
scene::IMeshBuffer *m_mesh_buffer;
|
||||
|
||||
/** The minimum coordinates of the graph. */
|
||||
Vec3 m_min_coord;
|
||||
|
||||
/** Scaling for mini map. */
|
||||
float m_scaling;
|
||||
|
||||
void createMesh(bool show_invisible=true,
|
||||
bool enable_transparency=false,
|
||||
const video::SColor *track_color=NULL);
|
||||
|
||||
virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v,
|
||||
const video::SColor &color) const = 0;
|
||||
virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const = 0;
|
||||
virtual const bool isNodeInvisible(int n) const = 0;
|
||||
virtual const bool hasLapLine() const = 0;
|
||||
virtual const bool differentNodeColor(int n, NodeColor* c) const = 0;
|
||||
|
||||
public:
|
||||
GraphStructure();
|
||||
virtual ~GraphStructure() {};
|
||||
void createDebugMesh();
|
||||
void makeMiniMap(const core::dimension2du &where,
|
||||
const std::string &name,
|
||||
const video::SColor &fill_color,
|
||||
video::ITexture** oldRttMinimap,
|
||||
FrameBuffer** newRttMinimap);
|
||||
void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const;
|
||||
virtual const unsigned int getNumNodes() const = 0;
|
||||
}; // GraphStructure
|
||||
|
||||
#endif
|
||||
@@ -1,133 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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 "tracks/navmesh.hpp"
|
||||
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "tracks/quad.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
NavMesh *NavMesh::m_nav_mesh = NULL;
|
||||
|
||||
/** Constructor, loads the mesh information from a given set of polygons
|
||||
* from a navmesh.xml file.
|
||||
* \param filename Name of the file containing all polygons
|
||||
*/
|
||||
NavMesh::NavMesh(const std::string &filename)
|
||||
{
|
||||
|
||||
m_min = Vec3( 99999, 99999, 99999);
|
||||
m_max = Vec3(-99999, -99999, -99999);
|
||||
|
||||
XMLNode *xml = file_manager->createXMLTree(filename);
|
||||
if (xml->getName() != "navmesh")
|
||||
{
|
||||
Log::error("NavMesh", "NavMesh is invalid.");
|
||||
delete xml;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Vec3> all_vertices;
|
||||
for (unsigned int i = 0; i < xml->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
if (xml_node->getName() == "vertices")
|
||||
{
|
||||
for (unsigned int i = 0; i < xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if (!(xml_node_node->getName() == "vertex"))
|
||||
{
|
||||
Log::error("NavMesh", "Unsupported type '%s' found"
|
||||
"in '%s' - ignored.",
|
||||
xml_node_node->getName().c_str(), filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reading vertices
|
||||
Vec3 p;
|
||||
readVertex(xml_node_node, &p);
|
||||
m_max.max(p);
|
||||
m_min.min(p);
|
||||
all_vertices.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (xml_node->getName() == "faces")
|
||||
{
|
||||
for(unsigned int i = 0; i < xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if (xml_node_node->getName() != "face")
|
||||
{
|
||||
Log::error("NavMesh", "Unsupported type '%s' found in '%s'"
|
||||
" - ignored.",
|
||||
xml_node_node->getName().c_str(), filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reading quads
|
||||
std::vector<int> quad_index;
|
||||
std::vector<int> adjacent_quad_index;
|
||||
xml_node_node->get("indices", &quad_index);
|
||||
xml_node_node->get("adjacents", &adjacent_quad_index);
|
||||
assert(quad_index.size() == 4);
|
||||
|
||||
m_adjacent_quads.push_back(adjacent_quad_index);
|
||||
m_quads.push_back(new Quad(
|
||||
all_vertices[quad_index[0]], all_vertices[quad_index[1]],
|
||||
all_vertices[quad_index[2]], all_vertices[quad_index[3]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
delete xml;
|
||||
|
||||
} // NavMesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
NavMesh::~NavMesh()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_quads.size(); i++)
|
||||
{
|
||||
delete m_quads[i];
|
||||
}
|
||||
m_quads.clear();
|
||||
} // ~NavMesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Reads the vertex information from an XMLNode */
|
||||
void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const
|
||||
{
|
||||
float x, y, z;
|
||||
xml->get("x", &x);
|
||||
xml->get("y", &y);
|
||||
xml->get("z", &z);
|
||||
Vec3 temp(x, y, z);
|
||||
*result = temp;
|
||||
} // readVertex
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
const Vec3& NavMesh::getCenterOfQuad(unsigned int n) const
|
||||
{
|
||||
assert(m_quads.size() > 0 && n < m_quads.size());
|
||||
return m_quads[n]->getCenter();
|
||||
} // getCenterOfQuad
|
||||
@@ -1,121 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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, B
|
||||
|
||||
#ifndef HEADER_NAVMESH_HPP
|
||||
#define HEADER_NAVMESH_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
class Quad;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \brief This class stores a set of navigation quads. It uses a
|
||||
* 'simplified singleton' design pattern: it has a static create function
|
||||
* to create exactly one instance, a destroy function, and a get function
|
||||
* (that does not have the side effect of the 'normal singleton' design
|
||||
* pattern to create an instance). Besides saving on the if statement in
|
||||
* get(), this is necessary since certain race modes might not have a
|
||||
* navigation mesh at all (e.g. race mode). So get() returns NULL in this
|
||||
* case, and this is tested where necessary.
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class NavMesh
|
||||
{
|
||||
|
||||
private:
|
||||
static NavMesh *m_nav_mesh;
|
||||
|
||||
/** The 2d bounding box, used for hashing. */
|
||||
Vec3 m_min;
|
||||
Vec3 m_max;
|
||||
|
||||
/** The actual set of quads that constitute the nav mesh */
|
||||
std::vector<Quad*> m_quads;
|
||||
|
||||
std::vector<std::vector<int>> m_adjacent_quads;
|
||||
|
||||
void readVertex(const XMLNode *xml, Vec3* result) const;
|
||||
// ------------------------------------------------------------------------
|
||||
NavMesh(const std::string &filename);
|
||||
// ------------------------------------------------------------------------
|
||||
~NavMesh();
|
||||
|
||||
public:
|
||||
/** Creates a NavMesh instance. */
|
||||
static void create(const std::string &filename)
|
||||
{
|
||||
assert(m_nav_mesh == NULL);
|
||||
m_nav_mesh = new NavMesh(filename);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Cleans up the nav mesh. It is possible that this function is called
|
||||
* even if no instance exists (e.g. in race). So it is not an
|
||||
* error if there is no instance.
|
||||
*/
|
||||
static void destroy()
|
||||
{
|
||||
if (m_nav_mesh)
|
||||
{
|
||||
delete m_nav_mesh;
|
||||
m_nav_mesh = NULL;
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the one instance of this object. It is possible that there
|
||||
* is no instance created (e.g. in normal race, since it doesn't have
|
||||
* a nav mesh), so we don't assert that an instance exist, and we
|
||||
* also don't create one if it doesn't exists.
|
||||
*/
|
||||
static NavMesh *get() { return m_nav_mesh; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the minimum and maximum coordinates of this navmesh. */
|
||||
void getBoundingBox(Vec3 *min, Vec3 *max) { *min=m_min; *max=m_max; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns a const reference to a quad */
|
||||
const Quad& getQuad(unsigned int n) const
|
||||
{
|
||||
assert(m_quads.size() > 0 && n < m_quads.size());
|
||||
return *(m_quads[n]);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns a const referece to a vector containing the indices
|
||||
* of quads adjacent to a given quad
|
||||
*/
|
||||
const std::vector<int>& getAdjacentQuads(unsigned int n) const
|
||||
{
|
||||
assert(m_adjacent_quads.size() > 0 && n < m_adjacent_quads.size() &&
|
||||
m_quads.size() == m_adjacent_quads.size());
|
||||
return m_adjacent_quads[n];
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the total number of quads */
|
||||
unsigned int getNumberOfQuads() const
|
||||
{
|
||||
assert(m_quads.size() > 0);
|
||||
return m_quads.size();
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center of a quad */
|
||||
const Vec3& getCenterOfQuad(unsigned int n) const;
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -23,33 +23,18 @@
|
||||
#include <S3DVertex.h>
|
||||
#include <triangle3d.h>
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
/** Constructor, takes 4 points. */
|
||||
Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
bool invisible, bool ai_ignore)
|
||||
{
|
||||
if(p1.sideOfLine2D(p0, p2)>0 ||
|
||||
p3.sideOfLine2D(p0, p2)<0)
|
||||
{
|
||||
Log::warn("Quad", "Quad has wrong orientation: p0=%f %f %f p1=%f %f %f",
|
||||
p0.getX(), p0.getY(), p0.getZ(),p1.getX(), p1.getY(), p1.getZ());
|
||||
Log::warn("Quad", "The quad will be swapped, nevertheless test for correctness -");
|
||||
Log::warn("Quad", "quads must be counter-clockwise oriented.");
|
||||
m_p[0]=p1; m_p[1]=p0; m_p[2]=p3; m_p[3]=p2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3;
|
||||
}
|
||||
const Vec3 &normal, int index, bool invisible)
|
||||
: 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_invisible = invisible;
|
||||
m_ai_ignore = ai_ignore;
|
||||
|
||||
} // Quad
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -61,25 +46,17 @@ void Quad::getVertices(video::S3DVertex *v, const video::SColor &color) const
|
||||
{
|
||||
// Eps is used to raise the track debug quads a little bit higher than
|
||||
// the ground, so that they are actually visible.
|
||||
core::vector3df eps(0, 0.1f, 0);
|
||||
core::vector3df normal = getNormal().toIrrVector();
|
||||
core::vector3df eps = normal * 0.1f;
|
||||
v[0].Pos = m_p[0].toIrrVector()+eps;
|
||||
v[1].Pos = m_p[1].toIrrVector()+eps;
|
||||
v[2].Pos = m_p[2].toIrrVector()+eps;
|
||||
v[3].Pos = m_p[3].toIrrVector()+eps;
|
||||
|
||||
core::triangle3df tri(m_p[0].toIrrVector(), m_p[1].toIrrVector(),
|
||||
m_p[2].toIrrVector());
|
||||
core::vector3df normal = tri.getNormal();
|
||||
normal.normalize();
|
||||
v[0].Normal = normal;
|
||||
v[1].Normal = normal;
|
||||
v[2].Normal = normal;
|
||||
|
||||
core::triangle3df tri1(m_p[0].toIrrVector(), m_p[2].toIrrVector(),
|
||||
m_p[3].toIrrVector());
|
||||
core::vector3df normal1 = tri1.getNormal();
|
||||
normal1.normalize();
|
||||
v[3].Normal = normal1;
|
||||
v[3].Normal = normal;
|
||||
|
||||
v[0].Color = color;
|
||||
v[1].Color = color;
|
||||
@@ -88,7 +65,7 @@ void Quad::getVertices(video::S3DVertex *v, const video::SColor &color) const
|
||||
} // setVertices
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool Quad::pointInQuad(const Vec3& p, bool ignore_vertical) const
|
||||
bool Quad::pointInside(const Vec3& p, bool ignore_vertical) const
|
||||
{
|
||||
// In case that a kart can validly run too high over one driveline
|
||||
// and it should not be considered to be on that driveline. Example:
|
||||
@@ -116,26 +93,4 @@ bool Quad::pointInQuad(const Vec3& p, bool ignore_vertical) const
|
||||
return p.sideOfLine2D(m_p[2], m_p[3]) > 0.0 &&
|
||||
p.sideOfLine2D(m_p[3], m_p[0]) >= 0.0;
|
||||
}
|
||||
} // pointInQuad
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Transforms a quad by a given transform (i.e. translation+rotation). This
|
||||
* function does not modify this quad, the results are stored in the quad
|
||||
* specified as parameter. These functions are used for slipstreaming to
|
||||
* determine the slipstream area from the original value (kart at 0,0,0 and
|
||||
* no rotation) to the current value.
|
||||
* \param t The transform to apply.
|
||||
* \param result The quad which stores the result.
|
||||
*/
|
||||
void Quad::transform(const btTransform &t, Quad *result) const
|
||||
{
|
||||
result->m_p[0] = t(m_p[0]);
|
||||
result->m_p[1] = t(m_p[1]);
|
||||
result->m_p[2] = t(m_p[2]);
|
||||
result->m_p[3] = t(m_p[3]);
|
||||
result->m_min_height = std::min ( std::min(result->m_p[0].getY(),
|
||||
result->m_p[1].getY()),
|
||||
std::min(result->m_p[2].getY(),
|
||||
result->m_p[3].getY()) );
|
||||
} // transform
|
||||
|
||||
} // pointInside
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#define HEADER_QUAD_HPP
|
||||
|
||||
#include <SColor.h>
|
||||
|
||||
#include "utils/leak_check.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
namespace irr
|
||||
@@ -28,14 +31,12 @@ namespace irr
|
||||
}
|
||||
using namespace irr;
|
||||
|
||||
class btTransform;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class Quad
|
||||
class Quad : public NoCopy
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
/** The four points of a quad. */
|
||||
Vec3 m_p[4];
|
||||
|
||||
@@ -43,6 +44,16 @@ private:
|
||||
* This saves some computations at runtime. */
|
||||
Vec3 m_center;
|
||||
|
||||
/** Index of this quad, used only with graph. */
|
||||
int m_index;
|
||||
|
||||
/** Normal of the quad */
|
||||
Vec3 m_normal;
|
||||
|
||||
private:
|
||||
/** Set to true if this quad should not be shown in the minimap. */
|
||||
bool m_invisible;
|
||||
|
||||
/** 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;
|
||||
@@ -51,34 +62,55 @@ private:
|
||||
* to distinguish between quads which are on top of each other. */
|
||||
float m_max_height;
|
||||
|
||||
/** Set to true if this quad should not be shown in the minimap. */
|
||||
bool m_invisible;
|
||||
|
||||
/** Set if this quad should not be used by the AI. */
|
||||
bool m_ai_ignore;
|
||||
|
||||
public:
|
||||
Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
bool invis=false, bool ai_ignore=false);
|
||||
LEAK_CHECK()
|
||||
// ------------------------------------------------------------------------
|
||||
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);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~Quad() {}
|
||||
// ------------------------------------------------------------------------
|
||||
void getVertices(video::S3DVertex *v, const video::SColor &color) const;
|
||||
bool pointInQuad(const Vec3& p, bool ignore_vertical = false) const;
|
||||
void transform(const btTransform &t, Quad *result) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the i-th. point of a quad. */
|
||||
const Vec3& operator[](int i) const {return m_p[i]; }
|
||||
const Vec3& operator[](int i) const { return m_p[i]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center of a quad. */
|
||||
const Vec3& getCenter () const {return m_center; }
|
||||
const Vec3& getCenter () const { return m_center; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the minimum height of a quad. */
|
||||
float getMinHeight() const { return m_min_height; }
|
||||
float getMinHeight() const { return m_min_height; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the index of this quad. */
|
||||
int getIndex() const
|
||||
{
|
||||
// You should not call this if it has default value (like slipstream)
|
||||
assert(m_index != -1);
|
||||
return m_index;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true of this quad is invisible, i.e. not to be shown in
|
||||
* the minimap. */
|
||||
bool isInvisible() const { return m_invisible; }
|
||||
bool isInvisible() const { return m_invisible; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** True if this quad should be ignored by the AI. */
|
||||
bool letAIIgnore() const { return m_ai_ignore; }
|
||||
/** Returns the normal of this quad. */
|
||||
const Vec3& getNormal() const { return m_normal; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if a point is inside this quad. */
|
||||
virtual bool pointInside(const Vec3& p,
|
||||
bool ignore_vertical = false) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if this quad is 3D, which additional 3D testing is used in
|
||||
* pointInside. */
|
||||
virtual bool is3DQuad() const { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getDistance2FromPoint(const Vec3 &xyz) const
|
||||
{
|
||||
// You should not call this in a bare quad
|
||||
assert(false);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
}; // class Quad
|
||||
#endif
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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, B
|
||||
|
||||
#ifndef HEADER_QUAD_GRAPH_HPP
|
||||
#define HEADER_QUAD_GRAPH_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include "tracks/graph_node.hpp"
|
||||
#include "tracks/graph_structure.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
#include "utils/aligned_array.hpp"
|
||||
|
||||
class CheckLine;
|
||||
|
||||
/**
|
||||
* \brief This class stores a graph of quads. It uses a 'simplified singleton'
|
||||
* design pattern: it has a static create function to create exactly instance,
|
||||
* a destroy function, and a get function (that does not have the side effect
|
||||
* of the 'normal singleton' design pattern to create an instance). Besides
|
||||
* saving on the if statement in get(), this is necessary since certain race
|
||||
* modes might not have a quad graph at all (e.g. battle mode). So get()
|
||||
* returns NULL in this case, and this is tested where necessary.
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class QuadGraph : public GraphStructure
|
||||
{
|
||||
|
||||
private:
|
||||
static QuadGraph *m_quad_graph;
|
||||
|
||||
/** The actual graph data structure. */
|
||||
std::vector<GraphNode*> m_all_nodes;
|
||||
|
||||
/** The length of the first loop. */
|
||||
float m_lap_length;
|
||||
|
||||
/** Stores the filename - just used for error messages. */
|
||||
std::string m_quad_filename;
|
||||
|
||||
/** Wether the graph should be reverted or not */
|
||||
bool m_reverse;
|
||||
|
||||
void setDefaultSuccessors();
|
||||
void computeChecklineRequirements(GraphNode* node, int latest_checkline);
|
||||
void computeDirectionData();
|
||||
void determineDirection(unsigned int current, unsigned int succ_index);
|
||||
float normalizeAngle(float f);
|
||||
|
||||
void addSuccessor(unsigned int from, unsigned int to);
|
||||
void load (const std::string &filename);
|
||||
void computeDistanceFromStart(unsigned int start_node, float distance);
|
||||
unsigned int getStartNode() const;
|
||||
QuadGraph (const std::string &quad_file_name,
|
||||
const std::string &graph_file_name,
|
||||
const bool reverse);
|
||||
~QuadGraph ();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v,
|
||||
const video::SColor &color) const
|
||||
{ m_all_nodes[i]->getQuad().getVertices(v, color); }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const
|
||||
{ QuadSet::get()->getBoundingBox(min, max); }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const bool isNodeInvisible(int n) const
|
||||
{ return m_all_nodes[n]->getQuad().isInvisible(); }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const bool hasLapLine() const
|
||||
{ return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const bool differentNodeColor(int n, NodeColor* c) const
|
||||
{ return false; }
|
||||
|
||||
public:
|
||||
static const int UNKNOWN_SECTOR;
|
||||
|
||||
void getSuccessors(int node_number,
|
||||
std::vector<unsigned int>& succ,
|
||||
bool for_ai=false) const;
|
||||
void spatialToTrack(Vec3 *dst, const Vec3& xyz,
|
||||
const int sector) const;
|
||||
void findRoadSector(const Vec3& XYZ, int *sector,
|
||||
std::vector<int> *all_sectors=NULL) const;
|
||||
int findOutOfRoadSector(const Vec3& xyz,
|
||||
const int curr_sector=UNKNOWN_SECTOR,
|
||||
std::vector<int> *all_sectors=NULL
|
||||
) const;
|
||||
void setDefaultStartPositions(AlignedArray<btTransform>
|
||||
*start_transforms,
|
||||
unsigned int karts_per_row,
|
||||
float forwards_distance=1.5f,
|
||||
float sidewards_distance=1.5f,
|
||||
float upwards_distance=0.0f) const;
|
||||
void updateDistancesForAllSuccessors(unsigned int indx,
|
||||
float delta,
|
||||
unsigned int count);
|
||||
void setupPaths();
|
||||
void computeChecklineRequirements();
|
||||
// ----------------------------------------------------------------------======
|
||||
/** Returns the one instance of this object. It is possible that there
|
||||
* is no instance created (e.g. in battle mode, since it doesn't have
|
||||
* a quad graph), so we don't assert that an instance exist, and we
|
||||
* also don't create one if it doesn't exists. */
|
||||
static QuadGraph *get() { return m_quad_graph; }
|
||||
// ----------------------------------------------------------------------==
|
||||
/** Creates a QuadGraph instance. */
|
||||
static void create(const std::string &quad_file_name,
|
||||
const std::string &graph_file_name,
|
||||
const bool reverse)
|
||||
{
|
||||
assert(m_quad_graph==NULL);
|
||||
// assignment to m_quad_graph is done in the constructor, since
|
||||
// functions called from the constructor need it to be defined.
|
||||
new QuadGraph(quad_file_name, graph_file_name, reverse);
|
||||
} // create
|
||||
// ------------------------------------------------------------------------
|
||||
/** Cleans up the quad graph. It is possible that this function is called
|
||||
* even if no instance exists (e.g. in battle mode). So it is not an
|
||||
* error if there is no instance. */
|
||||
static void destroy()
|
||||
{
|
||||
if(m_quad_graph)
|
||||
{
|
||||
delete m_quad_graph;
|
||||
m_quad_graph = NULL;
|
||||
}
|
||||
} // destroy
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of nodes in the graph. */
|
||||
virtual const unsigned int getNumNodes() const
|
||||
{ return (unsigned int)m_all_nodes.size();}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the distance to the j-th successor of node n. */
|
||||
float getDistanceToNext(int n, int j) const
|
||||
{ return m_all_nodes[n]->getDistanceToSuccessor(j);}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the angle of the line between node n and its j-th.
|
||||
* successor. */
|
||||
float getAngleToNext(int n, int j) const
|
||||
{ return m_all_nodes[n]->getAngleToSuccessor(j); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of successors of a node n. */
|
||||
int getNumberOfSuccessors(int n) const
|
||||
{ return m_all_nodes[n]->getNumberOfSuccessors(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the quad that belongs to a graph node. */
|
||||
const Quad& getQuadOfNode(unsigned int j) const
|
||||
{ return QuadSet::get()->getQuad(m_all_nodes[j]->getQuadIndex()); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the quad that belongs to a graph node. */
|
||||
GraphNode& getNode(unsigned int j) const{ return *m_all_nodes[j]; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the distance from the start to the beginning of a quad. */
|
||||
float getDistanceFromStart(int j) const
|
||||
{ return m_all_nodes[j]->getDistanceFromStart(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the length of the main driveline. */
|
||||
float getLapLength() const {return m_lap_length; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool isReverse() const {return m_reverse; }
|
||||
}; // QuadGraph
|
||||
|
||||
#endif
|
||||
@@ -1,124 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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 "tracks/quad_set.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
QuadSet *QuadSet::m_quad_set = NULL;
|
||||
|
||||
/** Constructor, loads the quad set from a file. Assigns a pointer
|
||||
* to this instance to m_quad_set, so that it can be accessed using get().
|
||||
*/
|
||||
QuadSet::QuadSet()
|
||||
{
|
||||
m_quad_set = this;
|
||||
} // QuadSet
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Destructor, frees all memory.
|
||||
*/
|
||||
QuadSet::~QuadSet()
|
||||
{
|
||||
for(unsigned int i=0; i<m_all_quads.size(); i++)
|
||||
{
|
||||
delete m_all_quads[i];
|
||||
}
|
||||
m_all_quads.clear();
|
||||
m_quad_set = NULL;
|
||||
} // ~QuadSet
|
||||
|
||||
// -----------------------------------------------------------------------------}
|
||||
/** This function interprets a point specification as an attribute in the
|
||||
xml quadset file. It understands two different specifications:
|
||||
p1="n:p" : get point p from square n (n, p integers)
|
||||
p1="p1,p2,p3" : make a 3d point out of these 3 floating point values
|
||||
*/
|
||||
void QuadSet::getPoint(const XMLNode *xml, const std::string &attribute_name,
|
||||
Vec3* result) const
|
||||
{
|
||||
std::string s;
|
||||
xml->get(attribute_name, &s);
|
||||
int pos=(int)s.find_first_of(":");
|
||||
if(pos>0) // n:p specification
|
||||
{
|
||||
std::vector<std::string> l = StringUtils::split(s, ':');
|
||||
int n=atoi(l[0].c_str());
|
||||
int p=atoi(l[1].c_str());
|
||||
*result=(*m_all_quads[n])[p];
|
||||
}
|
||||
else
|
||||
{
|
||||
xml->get(attribute_name, result);
|
||||
}
|
||||
|
||||
} // getPoint
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Loads the set of all quads from the specified filename.
|
||||
* \param filename The absolute filename to load the quad file from.
|
||||
*/
|
||||
void QuadSet::init(const std::string &filename)
|
||||
{
|
||||
m_min = Vec3( 99999, 99999, 99999);
|
||||
m_max = Vec3(-99999, -99999, -99999);
|
||||
|
||||
XMLNode *xml = file_manager->createXMLTree(filename);
|
||||
if(!xml || xml->getName()!="quads")
|
||||
{
|
||||
Log::error("[QuadSet::load] ERROR : QuadSet '%s' not found.", filename.c_str());
|
||||
delete xml;
|
||||
return;
|
||||
}
|
||||
for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
if(xml_node->getName()!="quad")
|
||||
{
|
||||
Log::warn("[QuadSet::load] WARNING: Unsupported node type '%s' found in '%s' - ignored.",
|
||||
xml_node->getName().c_str(), filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that it's not easy to do the reading of the parameters here
|
||||
// in quad, since the specification in the xml can contain references
|
||||
// to previous points. E.g.:
|
||||
// <quad p0="40:3" p1="40:2" p2="25.396030 0.770338 64.796539" ...
|
||||
Vec3 p0, p1, p2, p3;
|
||||
getPoint(xml_node, "p0", &p0);
|
||||
getPoint(xml_node, "p1", &p1);
|
||||
getPoint(xml_node, "p2", &p2);
|
||||
getPoint(xml_node, "p3", &p3);
|
||||
bool invisible=false;
|
||||
xml_node->get("invisible", &invisible);
|
||||
bool ai_ignore=false;
|
||||
xml_node->get("ai-ignore", &ai_ignore);
|
||||
Quad* q=new Quad(p0,p1,p2,p3, invisible, ai_ignore);
|
||||
m_all_quads.push_back(q);
|
||||
m_max.max(p0);m_max.max(p1);m_max.max(p2);m_max.max(p3);
|
||||
m_min.min(p0);m_min.min(p1);m_min.min(p2);m_min.min(p3);
|
||||
|
||||
}
|
||||
delete xml;
|
||||
} // load
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -1,90 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 Joerg Henrichs
|
||||
//
|
||||
// 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_QUAD_SET_HPP
|
||||
#define HEADER_QUAD_SET_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "tracks/quad.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class QuadSet
|
||||
{
|
||||
private:
|
||||
/** The 2d bounding box, used for hashing. */
|
||||
Vec3 m_min;
|
||||
Vec3 m_max;
|
||||
|
||||
/** The list of all quads. */
|
||||
std::vector<Quad*> m_all_quads;
|
||||
|
||||
/** Pointer to the one instance of a quad set. */
|
||||
static QuadSet *m_quad_set;
|
||||
|
||||
void getPoint(const XMLNode *xml, const std::string &attribute_name,
|
||||
Vec3 *result) const;
|
||||
QuadSet();
|
||||
~QuadSet();
|
||||
|
||||
public:
|
||||
static const int QUAD_NONE=-1;
|
||||
|
||||
void init (const std::string &filename);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Creates one instance of the quad set. */
|
||||
static void create()
|
||||
{
|
||||
assert(m_quad_set==NULL);
|
||||
m_quad_set = new QuadSet();
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Destroys the one instance of a quad set. */
|
||||
static void destroy()
|
||||
{
|
||||
delete m_quad_set; m_quad_set = NULL;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Static member function to access the QuadSet instance. */
|
||||
static QuadSet *get() { return m_quad_set; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the quad with a given index number. */
|
||||
const Quad& getQuad(int n) const {return *(m_all_quads[n]); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the minimum and maximum coordinates of this quad set. */
|
||||
void getBoundingBox(Vec3 *min, Vec3 *max)
|
||||
{ *min=m_min; *max=m_max; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of quads. */
|
||||
unsigned int getNumberOfQuads() const
|
||||
{return (unsigned int)m_all_quads.size(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center of quad n. */
|
||||
const Vec3& getCenterOfQuad(int n) const
|
||||
{return m_all_quads[n]->getCenter(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the n-th. quad. */
|
||||
const Quad& getQuad(int n) {return *(m_all_quads[n]); }
|
||||
}; // QuadSet
|
||||
#endif
|
||||
@@ -93,6 +93,19 @@ void TerrainInfo::update(const btMatrix3x3 &rotation, const Vec3 &from)
|
||||
->castRay(from, to, &m_hit_point, &m_material,
|
||||
&m_normal, /*interpolate*/true);
|
||||
} // update
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Update the terrain information based on the latest position.
|
||||
* \param Position from which to start the rayast from.
|
||||
*/
|
||||
void TerrainInfo::update(const Vec3 &from, const Vec3 &towards)
|
||||
{
|
||||
m_last_material = m_material;
|
||||
Vec3 direction = towards.normalized();
|
||||
btVector3 to = from + 10000.0f*direction;
|
||||
|
||||
const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh();
|
||||
tm.castRay(from, to, &m_hit_point, &m_material, &m_normal);
|
||||
} // update
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Does a raycast upwards from the given position
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
const Material **m);
|
||||
virtual void update(const btMatrix3x3 &rotation, const Vec3 &from);
|
||||
virtual void update(const Vec3 &from);
|
||||
virtual void update(const Vec3 &from, const Vec3 &towards);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Simple wrapper with no offset. */
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "graphics/glwrap.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/lod_node.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
#include "graphics/mesh_tools.hpp"
|
||||
#include "graphics/moving_texture.hpp"
|
||||
@@ -51,13 +52,14 @@
|
||||
#include "physics/physics.hpp"
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "scriptengine/script_engine.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
#include "tracks/bezier_curve.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/check_manager.hpp"
|
||||
#include "tracks/drive_graph.hpp"
|
||||
#include "tracks/drive_node.hpp"
|
||||
#include "tracks/model_definition_loader.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "tracks/quad_graph.hpp"
|
||||
#include "tracks/quad_set.hpp"
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
@@ -275,8 +277,7 @@ void Track::reset()
|
||||
*/
|
||||
void Track::cleanup()
|
||||
{
|
||||
QuadGraph::destroy();
|
||||
BattleGraph::destroy();
|
||||
Graph::destroy();
|
||||
ItemManager::destroy();
|
||||
VAOManager::kill();
|
||||
|
||||
@@ -372,7 +373,7 @@ void Track::cleanup()
|
||||
}
|
||||
if (m_new_rtt_mini_map)
|
||||
{
|
||||
m_new_rtt_mini_map = NULL; // already deleted by QuadGraph::~QuadGraph
|
||||
m_new_rtt_mini_map = NULL; // already deleted by Graph::~Graph
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<m_sky_textures.size(); i++)
|
||||
@@ -665,14 +666,15 @@ void Track::startMusic() const
|
||||
} // startMusic
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Loads the polygon graph for battle, i.e. the definition of all polys, and the way
|
||||
* they are connected to each other. Input file name is hardcoded for now
|
||||
/** Loads the quad graph for arena, i.e. the definition of all quads, and the
|
||||
* way they are connected to each other. Input file name is hardcoded for now
|
||||
*/
|
||||
void Track::loadBattleGraph(const XMLNode &node)
|
||||
void Track::loadArenaGraph(const XMLNode &node)
|
||||
{
|
||||
BattleGraph::create(m_root+"navmesh.xml", &node);
|
||||
ArenaGraph* graph = new ArenaGraph(m_root+"navmesh.xml", &node);
|
||||
Graph::setGraph(graph);
|
||||
|
||||
if(BattleGraph::get()->getNumNodes()==0)
|
||||
if(Graph::get()->getNumNodes()==0)
|
||||
{
|
||||
Log::warn("track", "No graph nodes defined for track '%s'\n",
|
||||
m_filename.c_str());
|
||||
@@ -681,27 +683,56 @@ void Track::loadBattleGraph(const XMLNode &node)
|
||||
{
|
||||
loadMinimap();
|
||||
}
|
||||
} // loadBattleGraph
|
||||
} // loadArenaGraph
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Loads the quad graph, i.e. the definition of all quads, and the way
|
||||
btQuaternion Track::getArenaStartRotation(const Vec3& xyz, float heading)
|
||||
{
|
||||
btQuaternion def_pos(Vec3(0, 1, 0), heading * DEGREE_TO_RAD);
|
||||
if (!ArenaGraph::get())
|
||||
return def_pos;
|
||||
|
||||
// Set the correct axis based on normal of the starting position
|
||||
int node = Graph::UNKNOWN_SECTOR;
|
||||
Graph::get()->findRoadSector(xyz, &node);
|
||||
if (node == Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::warn("track", "Starting position is not on ArenaGraph");
|
||||
return def_pos;
|
||||
}
|
||||
|
||||
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)));
|
||||
btMatrix3x3 m;
|
||||
m.setRotation(q);
|
||||
return btQuaternion(m.getColumn(1), heading * DEGREE_TO_RAD) * q;
|
||||
|
||||
} // getArenaStartRotation
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Loads the drive graph, i.e. the definition of all quads, and the way
|
||||
* they are connected to each other.
|
||||
*/
|
||||
void Track::loadQuadGraph(unsigned int mode_id, const bool reverse)
|
||||
void Track::loadDriveGraph(unsigned int mode_id, const bool reverse)
|
||||
{
|
||||
QuadGraph::create(m_root+m_all_modes[mode_id].m_quad_name,
|
||||
m_root+m_all_modes[mode_id].m_graph_name,
|
||||
reverse);
|
||||
new DriveGraph(m_root+m_all_modes[mode_id].m_quad_name,
|
||||
m_root+m_all_modes[mode_id].m_graph_name, reverse);
|
||||
|
||||
QuadGraph::get()->setupPaths();
|
||||
// setGraph is done in DriveGraph constructor
|
||||
assert(DriveGraph::get());
|
||||
DriveGraph::get()->setupPaths();
|
||||
#ifdef DEBUG
|
||||
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
|
||||
for(unsigned int i=0; i<DriveGraph::get()->getNumNodes(); i++)
|
||||
{
|
||||
assert(QuadGraph::get()->getNode(i).getPredecessor(0)!=-1);
|
||||
assert(DriveGraph::get()->getNode(i)->getPredecessor(0)!=-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(QuadGraph::get()->getNumNodes()==0)
|
||||
if(DriveGraph::get()->getNumNodes()==0)
|
||||
{
|
||||
Log::warn("track", "No graph nodes defined for track '%s'\n",
|
||||
m_filename.c_str());
|
||||
@@ -715,16 +746,13 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse)
|
||||
{
|
||||
loadMinimap();
|
||||
}
|
||||
} // loadQuadGraph
|
||||
} // loadDriveGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const
|
||||
{
|
||||
if ((m_is_arena || m_is_soccer) && m_has_navmesh)
|
||||
BattleGraph::get()->mapPoint2MiniMap(xyz, draw_at);
|
||||
else
|
||||
QuadGraph::get()->mapPoint2MiniMap(xyz, draw_at);
|
||||
Graph::get()->mapPoint2MiniMap(xyz, draw_at);
|
||||
draw_at->setX(draw_at->getX() * m_minimap_x_scale);
|
||||
draw_at->setY(draw_at->getY() * m_minimap_y_scale);
|
||||
}
|
||||
@@ -1027,16 +1055,8 @@ void Track::loadMinimap()
|
||||
core::dimension2du size = m_mini_map_size
|
||||
.getOptimalSize(!nonpower,!nonsquare);
|
||||
|
||||
if ((m_is_arena || m_is_soccer) && m_has_navmesh)
|
||||
{
|
||||
BattleGraph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255),
|
||||
&m_old_rtt_mini_map, &m_new_rtt_mini_map);
|
||||
}
|
||||
else
|
||||
{
|
||||
QuadGraph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255),
|
||||
&m_old_rtt_mini_map, &m_new_rtt_mini_map);
|
||||
}
|
||||
Graph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255),
|
||||
&m_old_rtt_mini_map, &m_new_rtt_mini_map);
|
||||
|
||||
if (m_old_rtt_mini_map)
|
||||
{
|
||||
@@ -1655,9 +1675,9 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
// the race gui was created. The race gui is needed since it stores
|
||||
// the information about the size of the texture to render the mini
|
||||
// map to.
|
||||
if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadQuadGraph(mode_id, reverse_track);
|
||||
if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadDriveGraph(mode_id, reverse_track);
|
||||
else if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh)
|
||||
loadBattleGraph(*root);
|
||||
loadArenaGraph(*root);
|
||||
|
||||
ItemManager::create();
|
||||
|
||||
@@ -1687,7 +1707,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
}
|
||||
else
|
||||
m_start_transforms.resize(race_manager->getNumberOfKarts());
|
||||
QuadGraph::get()->setDefaultStartPositions(&m_start_transforms,
|
||||
DriveGraph::get()->setDefaultStartPositions(&m_start_transforms,
|
||||
karts_per_row,
|
||||
forwards_distance,
|
||||
sidewards_distance,
|
||||
@@ -1873,20 +1893,8 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
|
||||
delete root;
|
||||
|
||||
if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh && !arena_random_item_created)
|
||||
BattleGraph::get()->findItemsOnGraphNodes();
|
||||
|
||||
if (UserConfigParams::m_track_debug &&
|
||||
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
!m_is_cutscene)
|
||||
{
|
||||
QuadGraph::get()->createDebugMesh();
|
||||
}
|
||||
|
||||
if (UserConfigParams::m_track_debug && m_has_navmesh &&
|
||||
race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
!m_is_cutscene)
|
||||
BattleGraph::get()->createDebugMesh();
|
||||
if (UserConfigParams::m_track_debug && Graph::get() && !m_is_cutscene)
|
||||
Graph::get()->createDebugMesh();
|
||||
|
||||
// Only print warning if not in battle mode, since battle tracks don't have
|
||||
// any quads or check lines.
|
||||
@@ -1910,7 +1918,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
World *world = World::getWorld();
|
||||
if (world->useChecklineRequirements())
|
||||
{
|
||||
QuadGraph::get()->computeChecklineRequirements();
|
||||
DriveGraph::get()->computeChecklineRequirements();
|
||||
}
|
||||
|
||||
EasterEggHunt *easter_world = dynamic_cast<EasterEggHunt*>(world);
|
||||
@@ -1969,9 +1977,8 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin
|
||||
}
|
||||
|
||||
m_start_transforms[position].setOrigin(xyz);
|
||||
m_start_transforms[position].setRotation(
|
||||
btQuaternion(btVector3(0,1,0),
|
||||
h*DEGREE_TO_RAD ) );
|
||||
m_start_transforms[position]
|
||||
.setRotation(getArenaStartRotation(xyz, h));
|
||||
}
|
||||
else if (name == "camera")
|
||||
{
|
||||
@@ -2260,19 +2267,38 @@ void Track::itemCommand(const XMLNode *node)
|
||||
}
|
||||
|
||||
Vec3 loc(xyz);
|
||||
// if only 2d coordinates are given, let the item fall from very high
|
||||
if(drop)
|
||||
|
||||
// Test if the item lies on a 3d node, if so adjust the normal
|
||||
// Also do a raycast if drop item is given
|
||||
Vec3 normal(0, 1, 0);
|
||||
Vec3 quad_normal = normal;
|
||||
Vec3 hit_point = loc;
|
||||
if (Graph::get())
|
||||
{
|
||||
int road_sector = Graph::UNKNOWN_SECTOR;
|
||||
Graph::get()->findRoadSector(xyz, &road_sector);
|
||||
// Only do custom direction of raycast if item is on quad graph
|
||||
if (road_sector != Graph::UNKNOWN_SECTOR)
|
||||
{
|
||||
quad_normal = Graph::get()->getQuad(road_sector)->getNormal();
|
||||
}
|
||||
}
|
||||
|
||||
if (drop)
|
||||
{
|
||||
const Material *m;
|
||||
// If raycast is used, increase the start position slightly
|
||||
// in case that the point is too close to the actual surface
|
||||
// (e.g. floating point errors can cause a problem here).
|
||||
loc += Vec3(0,0.1f,0);
|
||||
loc += quad_normal * 0.1f;
|
||||
|
||||
#ifndef DEBUG
|
||||
// Avoid unused variable warning in case of non-debug compilation.
|
||||
setTerrainHeight(&loc);
|
||||
m_track_mesh->castRay(loc, loc + (-10000 * quad_normal), &hit_point,
|
||||
&m, &normal);
|
||||
#else
|
||||
bool drop_success = setTerrainHeight(&loc);
|
||||
if(!drop_success)
|
||||
bool drop_success = m_track_mesh->castRay(loc, loc +
|
||||
(-10000 * quad_normal), &hit_point, &m, &normal);
|
||||
if (!drop_success)
|
||||
{
|
||||
Log::warn("track",
|
||||
"Item at position (%f,%f,%f) can not be dropped",
|
||||
@@ -2282,38 +2308,9 @@ void Track::itemCommand(const XMLNode *node)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Don't tilt the items, since otherwise the rotation will look odd,
|
||||
// i.e. the items will not rotate around the normal, but 'wobble'
|
||||
// around.
|
||||
//Vec3 normal(0.7071f, 0, 0.7071f);
|
||||
Vec3 normal(0, 1, 0);
|
||||
ItemManager::get()->newItem(type, loc, normal);
|
||||
ItemManager::get()->newItem(type, drop ? hit_point : loc, normal);
|
||||
} // itemCommand
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Does a raycast from the given position, and if terrain was found
|
||||
* adjust the Y position of the given vector to the actual terrain
|
||||
* height. If no terrain is found, false is returned and the
|
||||
* y position is not modified.
|
||||
* \param pos Pointer to the position at which to determine the
|
||||
* height. If terrain is found, its Y position will be
|
||||
* set to the actual height.
|
||||
* \return True if terrain was found and the height was adjusted.
|
||||
*/
|
||||
bool Track::setTerrainHeight(Vec3 *pos) const
|
||||
{
|
||||
Vec3 hit_point;
|
||||
Vec3 normal;
|
||||
const Material *m;
|
||||
Vec3 to=*pos+Vec3(0,-10000,0);
|
||||
if(m_track_mesh->castRay(*pos, to, &hit_point, &m, &normal))
|
||||
{
|
||||
pos->setY(hit_point.getY());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} // setTerrainHeight
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
std::vector< std::vector<float> > Track::buildHeightMap()
|
||||
@@ -2368,15 +2365,14 @@ const core::vector3df& Track::getSunRotation()
|
||||
|
||||
bool Track::findGround(AbstractKart *kart)
|
||||
{
|
||||
btVector3 to(kart->getXYZ());
|
||||
to.setY(-100000.f);
|
||||
const Vec3 &xyz = kart->getXYZ();
|
||||
Vec3 down = kart->getTrans().getBasis() * Vec3(0, -10000.0f, 0);
|
||||
|
||||
// Material and hit point are not needed;
|
||||
const Material *m;
|
||||
Vec3 hit_point, normal;
|
||||
bool over_ground = m_track_mesh->castRay(kart->getXYZ(), to, &hit_point,
|
||||
bool over_ground = m_track_mesh->castRay(xyz, down, &hit_point,
|
||||
&m, &normal);
|
||||
const Vec3 &xyz = kart->getXYZ();
|
||||
if(!over_ground)
|
||||
{
|
||||
Log::warn("physics", "Kart at (%f %f %f) can not be dropped.",
|
||||
@@ -2405,7 +2401,6 @@ bool Track::findGround(AbstractKart *kart)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
btTransform t = kart->getBody()->getCenterOfMassTransform();
|
||||
// The computer offset is slightly too large, it should take
|
||||
// the default suspension rest instead of suspension rest (i.e. the
|
||||
@@ -2413,9 +2408,21 @@ bool Track::findGround(AbstractKart *kart)
|
||||
// it). On the other hand this initial bouncing looks nice imho
|
||||
// - so I'll leave it in for now.
|
||||
float offset = kart->getKartProperties()->getSuspensionRest();
|
||||
t.setOrigin(hit_point+Vec3(0, offset, 0) );
|
||||
t.setOrigin(hit_point + normal * offset);
|
||||
kart->getBody()->setCenterOfMassTransform(t);
|
||||
kart->setTrans(t);
|
||||
|
||||
return true;
|
||||
} // findGround
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float Track::getTrackLength() const
|
||||
{
|
||||
return DriveGraph::get()->getLapLength();
|
||||
} // getTrackLength
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float Track::getAngle(int n) const
|
||||
{
|
||||
return DriveGraph::get()->getAngleToNext(n, 0);
|
||||
} // getAngle
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user