Merge remote-tracking branch 'origin/master' into refactor_networking

This commit is contained in:
hiker 2016-01-06 17:53:45 +11:00
commit b8ed2025ed
40 changed files with 1144 additions and 965 deletions

View File

@ -4,6 +4,7 @@
<card is="Intel(R) HD Graphics 3000" os="windows" disable="AdvancedPipeline"/>
<card is="Intel(R) HD Graphics 3000" os="windows" disable="FramebufferSRGBWorking"/>
<card contains="Intel" os="osx" disable="GI"/>
<card contains="Intel" os="linux" version="<12.0" disable="ComputeShader"/>
<card contains="Intel" os="linux" version="<12.0" disable="FramebufferSRGBCapable"/>
<card contains="Intel" disable="TextureCompressionS3TC"/>
<card contains="Intel" os="windows" disable="HighDefinitionTextures"/>

View File

@ -40,9 +40,12 @@
<label id="highscore3" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="10%"/>
<spacer width="1" height="2%"/>
<label id="author" width="100%" text_align="center" word_wrap="true"/>
<spacer width="1" height="10%"/>
<label id="max-arena-players" width="100%" text_align="center" word_wrap="true"/>
</div>
</box>

View File

@ -99,8 +99,10 @@
#if !defined(_IRR_WINDOWS_API_) && !defined(_IRR_OSX_PLATFORM_)
#ifndef _IRR_SOLARIS_PLATFORM_
#if !defined(__linux__) && !defined(__FreeBSD__)
#define _IRR_LINUX_PLATFORM_
#endif
#endif
#define _IRR_POSIX_API_
#define _IRR_COMPILE_WITH_X11_DEVICE_
#endif

View File

@ -267,6 +267,36 @@ public:
*this = &tmpbuf[idx];
}
//! Constructs a string from an unsigned long long
explicit string(unsigned long long number)
: array(0), allocated(0), used(0)
{
// temporary buffer for 32 numbers
c8 tmpbuf[32]={0};
u32 idx = 31;
// special case '0'
if (!number)
{
tmpbuf[30] = '0';
*this = &tmpbuf[30];
return;
}
// add numbers
while(number && idx)
{
--idx;
tmpbuf[idx] = (c8)('0' + (number % 10));
number /= 10;
}
*this = &tmpbuf[idx];
}
//! Constructor for copying a string from a pointer with a given length

View File

@ -2028,7 +2028,7 @@ public:
{
u32 tmp;
sscanf(text, "0x%x", &tmp);
Value = (void *) tmp;
Value = reinterpret_cast<void *>(tmp);
}
virtual E_ATTRIBUTE_TYPE getType() const

View File

@ -39,7 +39,7 @@ extern bool GLContextDebugBit;
#ifdef __FreeBSD__
#include <sys/joystick.h>
#else
#elif defined(__linux__)
// linux/joystick.h includes linux/input.h, which #defines values for various KEY_FOO keys.
// These override the irr::KEY_FOO equivalents, which stops key handling from working.
@ -2185,7 +2185,7 @@ bool CIrrDeviceLinux::activateJoysticks(core::array<SJoystickInfo> & joystickInf
#ifdef __FreeBSD__
info.axes=2;
info.buttons=2;
#else
#elif defined(__linux__)
ioctl( info.fd, JSIOCGAXES, &(info.axes) );
ioctl( info.fd, JSIOCGBUTTONS, &(info.buttons) );
fcntl( info.fd, F_SETFL, O_NONBLOCK );
@ -2207,7 +2207,7 @@ bool CIrrDeviceLinux::activateJoysticks(core::array<SJoystickInfo> & joystickInf
returnInfo.Axes = info.axes;
returnInfo.Buttons = info.buttons;
#ifndef __FreeBSD__
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
char name[80];
ioctl( info.fd, JSIOCGNAME(80), name);
returnInfo.Name = name;
@ -2252,7 +2252,7 @@ void CIrrDeviceLinux::pollJoysticks()
info.persistentData.JoystickEvent.Axis[0] = js.x; /* X axis */
info.persistentData.JoystickEvent.Axis[1] = js.y; /* Y axis */
}
#else
#elif defined(__linux__)
struct js_event event;
while (sizeof(event) == read(info.fd, &event, sizeof(event)))
{

View File

@ -478,7 +478,7 @@ bool CIrrDeviceSDL::run()
joyevent.JoystickEvent.ButtonStates |= (SDL_JoystickGetButton(joystick, j)<<j);
// query all axes, already in correct range
const int numAxes = core::min_(SDL_JoystickNumAxes(joystick), SEvent::SJoystickEvent::NUMBER_OF_AXES);
const int numAxes = core::min_(SDL_JoystickNumAxes(joystick), static_cast<int>(SEvent::SJoystickEvent::NUMBER_OF_AXES));
joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X]=0;
joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y]=0;
joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z]=0;

View File

@ -12,6 +12,7 @@
#include <string.h>
#include <unistd.h>
#if !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__CYGWIN__)
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#endif

View File

@ -22,10 +22,14 @@
#include <libkern/OSByteOrder.h>
#define bswap_16(X) OSReadSwapInt16(&X,0)
#define bswap_32(X) OSReadSwapInt32(&X,0)
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#define bswap_16(X) bswap16(X)
#define bswap_32(X) bswap32(X)
#elif defined(__OpenBSD__)
#include <endian.h>
#define bswap_16(X) swap16(X)
#define bswap_32(X) swap32(X)
#elif !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__PPC__) && !defined(_IRR_WINDOWS_API_)
#include <byteswap.h>
#else

View File

@ -36,6 +36,7 @@ void CentralVideoSettings::init()
hasBuffserStorage = false;
hasDrawIndirect = false;
hasComputeShaders = false;
hasArraysOfArrays = false;
hasTextureStorage = false;
hasTextureView = false;
hasBindlessTexture = false;
@ -46,11 +47,11 @@ void CentralVideoSettings::init()
hasTextureCompression = false;
hasUBO = false;
hasGS = false;
hasSRGBCapableVisual = true;
m_GI_has_artifact = false;
m_GI_has_artifact = false;
m_need_rh_workaround = false;
m_need_srgb_workaround = false;
m_need_srgb_visual_workaround = false;
// Call to glGetIntegerv should not be made if --no-graphics is used
if (!ProfileWorld::isNoGraphics())
@ -103,6 +104,11 @@ void CentralVideoSettings::init()
hasComputeShaders = true;
Log::info("GLDriver", "ARB Compute Shader Present");
}
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_ARRAYS_OF_ARRAYS) &&
hasGLExtension("GL_ARB_arrays_of_arrays")) {
hasArraysOfArrays = true;
Log::info("GLDriver", "ARB Arrays of Arrays Present");
}
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_TEXTURE_STORAGE) &&
hasGLExtension("GL_ARB_texture_storage")) {
hasTextureStorage = true;
@ -188,7 +194,7 @@ void CentralVideoSettings::init()
GLint param = GL_SRGB;
glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT,
GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &param);
hasSRGBCapableVisual = (param == GL_SRGB);
m_need_srgb_visual_workaround = (param != GL_SRGB);
}
}
}
@ -220,7 +226,7 @@ bool CentralVideoSettings::needsRGBBindlessWorkaround() const
bool CentralVideoSettings::needsSRGBCapableVisualWorkaround() const
{
return !hasSRGBCapableVisual;
return m_need_srgb_visual_workaround;
}
bool CentralVideoSettings::isARBGeometryShader4Usable() const
@ -263,6 +269,11 @@ bool CentralVideoSettings::isARBComputeShaderUsable() const
return hasComputeShaders;
}
bool CentralVideoSettings::isARBArraysOfArraysUsable() const
{
return hasArraysOfArrays;
}
bool CentralVideoSettings::isARBTextureStorageUsable() const
{
return hasTextureStorage;
@ -315,7 +326,7 @@ bool CentralVideoSettings::supportsIndirectInstancingRendering() const
bool CentralVideoSettings::supportsComputeShadersFiltering() const
{
return isARBBufferStorageUsable() && isARBImageLoadStoreUsable() && isARBComputeShaderUsable();
return isARBBufferStorageUsable() && isARBImageLoadStoreUsable() && isARBComputeShaderUsable() && isARBArraysOfArraysUsable();
}
bool CentralVideoSettings::supportsAsyncInstanceUpload() const

View File

@ -30,6 +30,7 @@ private:
bool hasDrawIndirect;
bool hasBuffserStorage;
bool hasComputeShaders;
bool hasArraysOfArrays;
bool hasTextureStorage;
bool hasTextureView;
bool hasBindlessTexture;
@ -40,10 +41,10 @@ private:
bool hasSSBO;
bool hasImageLoadStore;
bool hasMultiDrawIndirect;
bool hasSRGBCapableVisual;
bool m_need_rh_workaround;
bool m_need_srgb_workaround;
bool m_need_srgb_visual_workaround;
bool m_GI_has_artifact;
public:
void init();
@ -63,6 +64,7 @@ public:
bool isARBTextureStorageUsable() const;
bool isAMDVertexShaderLayerUsable() const;
bool isARBComputeShaderUsable() const;
bool isARBArraysOfArraysUsable() const;
bool isARBBindlessTextureUsable() const;
bool isARBBufferStorageUsable() const;
bool isARBBaseInstanceUsable() const;

View File

@ -48,6 +48,7 @@ namespace GraphicsRestrictions
"ImageLoadStore",
"BaseInstance",
"ComputeShader",
"ArraysOfArrays",
"ShaderStorageBufferObject",
"MultiDrawIndirect",
"ShaderAtomicCounters",

View File

@ -42,6 +42,7 @@ namespace GraphicsRestrictions
GR_IMAGE_LOAD_STORE,
GR_BASE_INSTANCE,
GR_COMPUTE_SHADER,
GR_ARRAYS_OF_ARRAYS,
GR_SHADER_STORAGE_BUFFER_OBJECT,
GR_MULTI_DRAW_INDIRECT,
GR_SHADER_ATOMIC_COUNTERS,

View File

@ -66,8 +66,24 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type)
std::ostringstream code;
code << "#version " << CVS->getGLSLVersion()<<"\n";
// Some drivers report that the compute shaders extension is available,
// but they report only OpenGL 3.x version, and thus these extensions
// must be enabled manually. Otherwise the shaders compilation will fail
// because STK tries to use extensions which are available, but disabled
// by default.
if (type == GL_COMPUTE_SHADER)
{
if (CVS->isARBComputeShaderUsable())
code << "#extension GL_ARB_compute_shader : enable\n";
if (CVS->isARBImageLoadStoreUsable())
code << "#extension GL_ARB_shader_image_load_store : enable\n";
if (CVS->isARBArraysOfArraysUsable())
code << "#extension GL_ARB_arrays_of_arrays : enable\n";
}
if (CVS->isAMDVertexShaderLayerUsable())
code << "#extension GL_AMD_vertex_shader_layer : enable\n";
if (CVS->isAZDOEnabled())
{
code << "#extension GL_ARB_bindless_texture : enable\n";
@ -80,7 +96,7 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type)
code << "#define VSLayer\n";
if (CVS->needsRGBBindlessWorkaround())
code << "#define SRGBBindlessFix\n";
//shader compilation fails with some drivers if there is no precision qualifier
if (type == GL_FRAGMENT_SHADER)
code << "precision mediump float;\n";

View File

@ -491,6 +491,7 @@ std::set<wchar_t> ScalableFont::getPreloadCharacters(const GUIEngine::TTFLoading
preload_char.insert((wchar_t)i); //Include basic Latin too, starting from A (char code 65)
setlocale(LC_ALL, "en_US.UTF8");
std::set<wchar_t> upper;
std::set<wchar_t>::iterator it = preload_char.begin();
while (it != preload_char.end())
@ -498,7 +499,15 @@ std::set<wchar_t> ScalableFont::getPreloadCharacters(const GUIEngine::TTFLoading
//Only use all capital letter for bold char with latin (<640 of char code).
//Remove all characters (>char code 8191) not used by the title
if (((iswlower((wchar_t)*it) || !iswalpha((wchar_t)*it)) && *it < 640) || *it > 8191)
{
if (*it < 8192 && iswalpha((wchar_t)*it))
{
//Make sure we include all upper case letters,
//because the title font shows all characters as capital letters
upper.insert(towupper((wchar_t)*it));
}
it = preload_char.erase(it);
}
else
++it;
}
@ -507,6 +516,7 @@ std::set<wchar_t> ScalableFont::getPreloadCharacters(const GUIEngine::TTFLoading
for (u32 i = 32; i < 65; ++i)
preload_char.insert((wchar_t)i); //Include basic symbol (from space (char code 32) to @(char code 64))
preload_char.insert(upper.begin(), upper.end());
preload_char.insert((wchar_t)160); //Non-breaking space
//Remove Ordinal indicator (char code 170 and 186)

View File

@ -447,6 +447,9 @@ public:
/** Counter which is used for displaying wrong way message after a delay */
virtual float getWrongwayCounter() = 0;
virtual void setWrongwayCounter(float counter) = 0;
// ------------------------------------------------------------------------
/** Returns whether this kart wins or loses. */
virtual bool getRaceResult() const = 0;
}; // AbstractKart

View File

@ -23,10 +23,6 @@
#include "states_screens/state_manager.hpp"
class AIProperties;
class LinearWorld;
class ThreeStrikesBattle;
class QuadGraph;
class BattleGraph;
class Track;
class Vec3;

View File

@ -2,7 +2,7 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2008-2012 Joerg Henrichs
// Copyright (C) 2008-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
@ -33,8 +33,7 @@
#include "karts/rescue_animation.hpp"
#include "karts/skidding.hpp"
#include "modes/three_strikes_battle.hpp"
#include "tracks/nav_poly.hpp"
#include "tracks/navmesh.hpp"
#include "tracks/battle_graph.hpp"
#include "utils/log.hpp"
#ifdef AI_DEBUG
@ -96,19 +95,21 @@ BattleAI::~BattleAI()
*/
void BattleAI::reset()
{
m_current_node = BattleGraph::UNKNOWN_POLY;
m_target_node = BattleGraph::UNKNOWN_POLY;
m_adjusting_side = false;
m_closest_kart = NULL;
m_closest_kart_node = BattleGraph::UNKNOWN_POLY;
m_closest_kart_point = Vec3(0, 0, 0);
m_closest_kart_pos_data = {0};
m_cur_kart_pos_data = {0};
m_is_steering_overridden = false;
m_is_stuck = false;
m_is_uturn = false;
m_target_point = Vec3(0, 0, 0);
m_time_since_last_shot = 0.0f;
m_time_since_driving = 0.0f;
m_time_since_reversing = 0.0f;
m_time_since_steering_overridden = 0.0f;
m_time_since_uturn = 0.0f;
m_on_node.clear();
m_path_corners.clear();
@ -165,6 +166,7 @@ void BattleAI::update(float dt)
findClosestKart(true);
findTarget();
handleItems(dt);
handleBanana();
if (m_kart->getSpeed() > 15.0f && m_cur_kart_pos_data.angle < 0.2f)
{
@ -198,7 +200,7 @@ void BattleAI::checkIfStuck(const float dt)
m_time_since_driving = 0.0f;
}
m_on_node.insert(m_current_node);
m_on_node.insert(m_world->getKartNode(m_kart->getWorldKartId()));
m_time_since_driving += dt;
if ((m_time_since_driving >=
@ -291,19 +293,8 @@ void BattleAI::findClosestKart(bool difficulty)
}
const AbstractKart* closest_kart = m_world->getKart(closest_kart_num);
if (!closest_kart->getController()->isPlayerController())
{
BattleAI* controller = (BattleAI*)(closest_kart->getController());
m_closest_kart_node = controller->getCurrentNode();
m_closest_kart_point = closest_kart->getXYZ();
}
else if (closest_kart->getController()->isPlayerController())
{
PlayerController* controller = (PlayerController*)(closest_kart->getController());
m_closest_kart_node = controller->getCurrentNode();
m_closest_kart_point = closest_kart->getXYZ();
}
m_closest_kart_node = m_world->getKartNode(closest_kart_num);
m_closest_kart_point = closest_kart->getXYZ();
if (!difficulty)
{
@ -347,7 +338,7 @@ void BattleAI::handleAcceleration(const float dt)
//-----------------------------------------------------------------------------
void BattleAI::handleUTurn(const float dt)
{
const float turn_side = (m_cur_kart_pos_data.on_side ? 1.0f : -1.0f);
const float turn_side = (m_adjusting_side ? 1.0f : -1.0f);
if (fabsf(m_kart->getSpeed()) >
(m_kart->getKartProperties()->getEngineMaxSpeed() / 5)
@ -380,10 +371,29 @@ void BattleAI::handleUTurn(const float dt)
*/
void BattleAI::handleSteering(const float dt)
{
if (m_current_node == BattleGraph::UNKNOWN_POLY ||
const int current_node = m_world->getKartNode(m_kart->getWorldKartId());
if (current_node == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
if (m_target_node == m_current_node)
if (m_is_steering_overridden)
{
// Steering is overridden to avoid eating banana
const float turn_side = (m_adjusting_side ? 1.0f : -1.0f);
m_time_since_steering_overridden += dt;
if (m_time_since_steering_overridden > 0.35f)
setSteering(-(turn_side), dt);
else
setSteering(turn_side, dt);
if (m_time_since_steering_overridden > 0.7f)
{
m_is_steering_overridden = false;
m_time_since_steering_overridden = 0.0f;
}
return;
}
if (m_target_node == current_node)
{
// Very close to the item, steer directly
checkPosition(m_target_point, &m_cur_kart_pos_data);
@ -392,6 +402,7 @@ void BattleAI::handleSteering(const float dt)
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_is_uturn = true;
}
else
@ -402,9 +413,9 @@ void BattleAI::handleSteering(const float dt)
return;
}
else if (m_target_node != m_current_node)
else if (m_target_node != current_node)
{
findPortals(m_current_node, m_target_node);
findPortals(current_node, m_target_node);
stringPull(m_kart->getXYZ(), m_target_point);
if (m_path_corners.size() > 0)
m_target_point = m_path_corners[0];
@ -415,6 +426,7 @@ void BattleAI::handleSteering(const float dt)
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_is_uturn = true;
}
else
@ -433,6 +445,34 @@ void BattleAI::handleSteering(const float dt)
}
} // handleSteering
//-----------------------------------------------------------------------------
void BattleAI::handleBanana()
{
if (m_is_steering_overridden || m_is_uturn) return;
const std::vector< std::pair<const Item*, int> >& item_list =
BattleGraph::get()->getItemList();
const unsigned int items_count = item_list.size();
for (unsigned int i = 0; i < items_count; ++i)
{
const Item* item = item_list[i].first;
if (item->getType() == Item::ITEM_BANANA && !item->wasCollected())
{
posData banana_pos = {0};
checkPosition(item->getXYZ(), &banana_pos);
if (banana_pos.angle < 0.2f && banana_pos.distance < 7.5f &&
!banana_pos.behind)
{
// Check whether it's straight ahead towards a banana
// If so, try to do hard turn to avoid
m_adjusting_side = banana_pos.on_side;
m_is_steering_overridden = true;
}
}
}
} // handleBanana
//-----------------------------------------------------------------------------
/** This function finds the polyon edges(portals) that the AI will cross before
* reaching its destination. We start from the current polygon and call
@ -592,6 +632,11 @@ void BattleAI::handleBraking()
{
m_controls->m_brake = false;
if (m_world->getKartNode(m_kart->getWorldKartId())
== BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY ||
m_is_steering_overridden) return;
// 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.
@ -599,9 +644,6 @@ void BattleAI::handleBraking()
std::vector<Vec3> points;
if (m_current_node == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
points.push_back(m_kart->getXYZ());
points.push_back(m_path_corners[0]);
points.push_back((m_path_corners.size()>=2) ? m_path_corners[1] : m_path_corners[0]);
@ -835,6 +877,18 @@ void BattleAI::handleItemCollection(Vec3* aim_point, int* target_node)
const std::vector< std::pair<const Item*, int> >& item_list =
BattleGraph::get()->getItemList();
const unsigned int items_count = item_list.size();
if (item_list.empty())
{
// Notice: this should not happen, as it makes no sense
// for an arean without items, if so how can attack happen?
Log::fatal ("BattleAI",
"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.");
return;
}
unsigned int closest_item_num = 0;
for (unsigned int i = 0; i < items_count; ++i)

View File

@ -1,9 +1,8 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2010 Joerg Henrichs
// Copyright (C) 2010-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
@ -29,13 +28,9 @@
#include "karts/controller/ai_base_controller.hpp"
#include "race/race_manager.hpp"
#include "tracks/battle_graph.hpp"
#include "utils/random_generator.hpp"
class AIProperties;
class ThreeStrikesBattle;
class BattleGraph;
class Track;
class Vec3;
class Item;
@ -53,10 +48,9 @@ private:
/** Holds the position info of targets. */
struct posData {bool behind; bool on_side; float angle; float distance;};
/** Holds the current position of the AI on the battle graph. Sets to
* BattleGraph::UNKNOWN_POLY if the location is unknown. This variable is
* updated in ThreeStrikesBattle::updateKartNodes(). */
int m_current_node;
/** Used by handleBanana and UTurn, it tells whether to do left or right
* turning when steering is overridden. */
bool m_adjusting_side;
int m_closest_kart_node;
Vec3 m_closest_kart_point;
@ -70,6 +64,10 @@ private:
/** Holds the current difficulty. */
RaceManager::Difficulty m_cur_difficulty;
/** Indicates that the steering of kart is overridden, and
* m_time_since_steering_overridden is counting down. */
bool m_is_steering_overridden;
/** Indicates that the kart is currently stuck, and m_time_since_reversing is
* counting down. */
bool m_is_stuck;
@ -105,6 +103,9 @@ private:
/** This is a timer that counts down when the kart is starting to drive. */
float m_time_since_driving;
/** This is a timer that counts down when the steering of kart is overridden. */
float m_time_since_steering_overridden;
/** This is a timer that counts down when the kart is doing u-turn. */
float m_time_since_uturn;
@ -115,6 +116,7 @@ private:
void findPortals(int start, int end);
void findTarget();
void handleAcceleration(const float dt);
void handleBanana();
void handleBraking();
void handleItems(const float dt);
void handleItemCollection(Vec3*, int*);
@ -138,8 +140,6 @@ public:
BattleAI(AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);
~BattleAI();
unsigned int getCurrentNode() const { return m_current_node; }
void setCurrentNode(int i) { m_current_node = i; }
virtual void update (float delta);
virtual void reset ();

View File

@ -55,6 +55,7 @@ protected:
/** The name of the controller, mainly used for debugging purposes. */
std::string m_controller_name;
public:
Controller (AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);

View File

@ -67,7 +67,6 @@ void PlayerController::reset()
m_prev_accel = 0;
m_prev_nitro = false;
m_penalty_time = 0;
m_current_node = BattleGraph::UNKNOWN_POLY;
} // reset
// ----------------------------------------------------------------------------

View File

@ -113,6 +113,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
m_race_position = position;
m_collected_energy = 0;
m_finished_race = false;
m_race_result = false;
m_finish_time = 0.0f;
m_bubblegum_time = 0.0f;
m_bubblegum_torque = 0.0f;
@ -836,71 +837,102 @@ void Kart::finishedRace(float time)
m_kart_model->finishedRace();
race_manager->kartFinishedRace(this, time);
if ((race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
&& m_controller->isPlayerController())
{
RaceGUIBase* m = World::getWorld()->getRaceGUI();
if (m)
{
if (race_manager->
getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
getPosition() == 2)
m->addMessage(_("You won the race!"), this, 2.0f);
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
{
m->addMessage((getPosition() == 1 ?
_("You won the race!") : _("You finished the race!")) ,
this, 2.0f);
}
}
}
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG)
{
// Save for music handling in race result gui
setRaceResult();
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
// Skip animation if this kart is eliminated
if (m_eliminated) return;
m_kart_model->setAnimation(m_race_result ?
KartModel::AF_WIN_START : KartModel::AF_LOSE_START);
}
} // finishedRace
//-----------------------------------------------------------------------------
void Kart::setRaceResult()
{
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
{
// in modes that support it, start end animation
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
// TODO NetworkController?
if (m_controller->isLocalPlayerController()) // if player is on this computer
{
PlayerProfile *player = PlayerManager::getCurrentPlayer();
const ChallengeStatus *challenge = player->getCurrentChallengeStatus();
// In case of a GP challenge don't make the end animation depend
// on if the challenge is fulfilled
if(challenge && !challenge->getData()->isGrandPrix())
if (challenge && !challenge->getData()->isGrandPrix())
{
if(challenge->getData()->isChallengeFulfilled())
m_kart_model->setAnimation(KartModel::AF_WIN_START);
if (challenge->getData()->isChallengeFulfilled())
m_race_result = true;
else
m_kart_model->setAnimation(KartModel::AF_LOSE_START);
m_race_result = false;
}
else if(m_race_position<=0.5f*race_manager->getNumberOfKarts() ||
m_race_position==1)
m_kart_model->setAnimation(KartModel::AF_WIN_START);
else if (this->getPosition() <= 0.5f*race_manager->getNumberOfKarts() ||
this->getPosition() == 1)
m_race_result = true;
else
m_kart_model->setAnimation(KartModel::AF_LOSE_START);
RaceGUIBase* m = World::getWorld()->getRaceGUI();
if(m)
{
m->addMessage((getPosition() == 1 ? _("You won the race!") : _("You finished the race!")) ,
this, 2.0f);
}
m_race_result = false;
}
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
// start end animation
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
if(m_race_position<=2)
m_kart_model->setAnimation(KartModel::AF_WIN_START);
else if(m_race_position>=0.7f*race_manager->getNumberOfKarts())
m_kart_model->setAnimation(KartModel::AF_LOSE_START);
RaceGUIBase* m = World::getWorld()->getRaceGUI();
if(m)
else
{
if (getPosition() == 2)
m->addMessage(_("You won the race!"), this, 2.0f);
if (this->getPosition() <= 0.5f*race_manager->getNumberOfKarts() ||
this->getPosition() == 1)
m_race_result = true;
else
m_race_result = false;
}
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
{
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
// the kart wins if it isn't eliminated
m_race_result = !this->isEliminated();
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
{
// TODO complete together with soccer ai!
m_race_result = true;
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG)
{
m_kart_model->setAnimation(KartModel::AF_WIN_START);
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
// Easter egg mode only has one player, so always win
m_race_result = true;
}
else
Log::warn("Kart", "Unknown game mode given.");
} // finishedRace
} // setKartResult
//-----------------------------------------------------------------------------
/** Called when an item is collected. It will either adjust the collected

View File

@ -109,6 +109,9 @@ private:
/** Offset of the graphical kart chassis from the physical chassis. */
float m_graphical_y_offset;
/** True if the kart wins, false otherwise. */
bool m_race_result;
/** True if the kart is eliminated. */
bool m_eliminated;
@ -438,6 +441,12 @@ public:
float getWrongwayCounter() { return m_wrongway_counter; }
// ------------------------------------------------------------------------
void setWrongwayCounter(float counter) { m_wrongway_counter = counter; }
// ------------------------------------------------------------------------
/** Returns whether this kart wins or loses. */
virtual bool getRaceResult() const { return m_race_result; }
// ------------------------------------------------------------------------
/** Set this kart race result. */
void setRaceResult();
}; // Kart

View File

@ -26,8 +26,6 @@
#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/battle_ai.hpp"
#include "karts/controller/player_controller.hpp"
#include "karts/kart_model.hpp"
#include "karts/kart_properties.hpp"
#include "physics/physics.hpp"
@ -87,7 +85,8 @@ void ThreeStrikesBattle::reset()
for(unsigned int n=0; n<kart_amount; n++)
{
m_kart_info[n].m_lives = 3;
m_kart_info[n].m_lives = 3;
m_kart_info[n].m_on_node = BattleGraph::UNKNOWN_POLY;
// no positions in this mode
m_karts[n]->setPosition(-1);
@ -174,6 +173,8 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
// check if kart is 'dead'
if (m_kart_info[kart_id].m_lives < 1)
{
if (getCurrentNumPlayers())
eliminateKart(kart_id, /*notify_of_elimination*/ true);
m_karts[kart_id]->finishedRace(WorldStatus::getTime());
scene::ISceneNode** wheels = m_karts[kart_id]->getKartModel()
->getWheelNodes();
@ -181,8 +182,6 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
if(wheels[1]) wheels[1]->setVisible(false);
if(wheels[2]) wheels[2]->setVisible(false);
if(wheels[3]) wheels[3]->setVisible(false);
if (getCurrentNumPlayers())
eliminateKart(kart_id, /*notify_of_elimination*/ true);
// Find a camera of the kart with the most lives ("leader"), and
// attach all cameras for this kart to the leader.
int max_lives = 0;
@ -442,7 +441,7 @@ bool ThreeStrikesBattle::isRaceOver()
} // isRaceOver
//-----------------------------------------------------------------------------
/** Updates the m_current_node value of each kart controller to localize it
/** Updates the m_on_node value of each kart to localize it
* on the navigation mesh.
*/
void ThreeStrikesBattle::updateKartNodes()
@ -450,111 +449,84 @@ void ThreeStrikesBattle::updateKartNodes()
if (isRaceOver()) return;
const unsigned int n = getNumKarts();
for(unsigned int i=0; i<n; i++)
for (unsigned int i = 0; i < n; i++)
{
if(m_karts[i]->isEliminated()) continue;
if (m_karts[i]->isEliminated()) continue;
const AbstractKart* kart = m_karts[i];
const int saved_current_node = m_kart_info[i].m_on_node;
if(!kart->getController()->isPlayerController())
if (saved_current_node == BattleGraph::UNKNOWN_POLY)
{
BattleAI* controller = (BattleAI*)(kart->getController());
int saved_current_node = controller->getCurrentNode();
if (saved_current_node != BattleGraph::UNKNOWN_POLY)
// Try all nodes in the battle graph
bool found = false;
unsigned int node = 0;
while (!found && node < BattleGraph::get()->getNumNodes())
{
//check if the kart is still on the same node
const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode());
if(p.pointInPoly(kart->getXYZ())) continue;
//if not then check all adjacent polys
const std::vector<int>& adjacents =
NavMesh::get()->getAdjacentPolys(controller->getCurrentNode());
// Set m_current_node to unknown so that if no adjacent poly checks true
// we look everywhere the next time updateCurrentNode is called. This is
// useful in cases when you are "teleported" to some other poly, ex. rescue
controller->setCurrentNode(BattleGraph::UNKNOWN_POLY);
for(unsigned int i=0; i<adjacents.size(); i++)
const NavPoly& p_all = BattleGraph::get()->getPolyOfNode(node);
if ((p_all.pointInPoly(m_karts[i]->getXYZ())))
{
const NavPoly& p_temp =
BattleGraph::get()->getPolyOfNode(adjacents[i]);
if(p_temp.pointInPoly(kart->getXYZ()))
controller->setCurrentNode(adjacents[i]);
m_kart_info[i].m_on_node = node;
found = true;
}
node++;
}
//Current node is still unkown
if (saved_current_node == BattleGraph::UNKNOWN_POLY)
{
bool flag = 0;
unsigned int max_count = BattleGraph::get()->getNumNodes();
for(unsigned int i=0; i<max_count; i++)
{
const NavPoly& p = BattleGraph::get()->getPolyOfNode(i);
if((p.pointInPoly(kart->getXYZ())))
{
controller->setCurrentNode(i);
flag = 1;
}
}
if(flag == 0) controller->setCurrentNode(saved_current_node);
}
}
else
{
PlayerController* controller = (PlayerController*)(kart->getController());
// Check if the kart is still on the same node
const NavPoly& p_cur = BattleGraph::get()
->getPolyOfNode(saved_current_node);
if (p_cur.pointInPoly(m_karts[i]->getXYZ())) continue;
int saved_current_node = controller->getCurrentNode();
// If not then check all adjacent polys
const std::vector<int>& adjacents = NavMesh::get()
->getAdjacentPolys(saved_current_node);
if (saved_current_node != BattleGraph::UNKNOWN_POLY)
// Set current node to unknown so that if no adjacent polygons,
// we look everywhere the next time updateKartNodes is called.
// This is useful in cases when you are "teleported"
// to some other polygons, ex. rescue
m_kart_info[i].m_on_node = BattleGraph::UNKNOWN_POLY;
bool found = false;
unsigned int num = 0;
while (!found && num < adjacents.size())
{
//check if the kart is still on the same node
const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode());
if(p.pointInPoly(kart->getXYZ())) continue;
//if not then check all adjacent polys
const std::vector<int>& adjacents =
NavMesh::get()->getAdjacentPolys(controller->getCurrentNode());
// Set m_current_node to unknown so that if no adjacent poly checks true
// we look everywhere the next time updateCurrentNode is called. This is
// useful in cases when you are "teleported" to some other poly, ex. rescue
controller->setCurrentNode(BattleGraph::UNKNOWN_POLY);
for(unsigned int i=0; i<adjacents.size(); i++)
const NavPoly& p_temp =
BattleGraph::get()->getPolyOfNode(adjacents[num]);
if (p_temp.pointInPoly(m_karts[i]->getXYZ()))
{
const NavPoly& p_temp =
BattleGraph::get()->getPolyOfNode(adjacents[i]);
if(p_temp.pointInPoly(kart->getXYZ()))
controller->setCurrentNode(adjacents[i]);
m_kart_info[i].m_on_node = adjacents[num];
found = true;
}
num++;
}
if (saved_current_node == BattleGraph::UNKNOWN_POLY)
// Current node is still unkown
if (m_kart_info[i].m_on_node == BattleGraph::UNKNOWN_POLY)
{
bool flag = 0;
unsigned int max_count = BattleGraph::get()->getNumNodes();
for(unsigned int i =0; i<max_count; i++)
{
const NavPoly& p = BattleGraph::get()->getPolyOfNode(i);
if((p.pointInPoly(kart->getXYZ())))
{
controller->setCurrentNode(i);
flag = 1;
}
}
// 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
const NavPoly& p = BattleGraph::get()
->getPolyOfNode(saved_current_node);
const float dist = (p.getCenter() - m_karts[i]->getXYZ()).length_2d();
if(flag == 0) controller->setCurrentNode(saved_current_node);
if (dist < 3.0f)
m_kart_info[i].m_on_node = saved_current_node;
}
}
}
}
//-----------------------------------------------------------------------------
/** 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,

View File

@ -39,7 +39,8 @@ class ThreeStrikesBattle : public WorldWithRank
private:
struct BattleInfo
{
int m_lives;
int m_lives;
int m_on_node;
};
/** This vector contains an 'BattleInfo' struct for every kart in the race.
@ -108,6 +109,7 @@ public:
virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node);
int getKartNode(unsigned int kart_id) const;
void updateKartRanks();
}; // ThreeStrikesBattles

View File

@ -172,6 +172,99 @@ namespace Scripting
//-----------------------------------------------------------------------------
void ScriptEngine::runDelegate(asIScriptFunction* delegate)
{
asIScriptContext *ctx = m_engine->CreateContext();
if (ctx == NULL)
{
Log::error("Scripting", "runMethod: Failed to create the context.");
//m_engine->Release();
return;
}
int r = ctx->Prepare(delegate);
if (r < 0)
{
Log::error("Scripting", "runMethod: Failed to prepare the context.");
ctx->Release();
return;
}
// Execute the function
r = ctx->Execute();
if (r != asEXECUTION_FINISHED)
{
// The execution didn't finish as we had planned. Determine why.
if (r == asEXECUTION_ABORTED)
{
Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out.");
}
else if (r == asEXECUTION_EXCEPTION)
{
Log::error("Scripting", "The script ended with an exception : (line %i) %s",
ctx->GetExceptionLineNumber(),
ctx->GetExceptionString());
}
else
{
Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r);
}
}
ctx->Release();
}
//-----------------------------------------------------------------------------
/*
void ScriptEngine::runMethod(asIScriptObject* obj, std::string methodName)
{
asIObjectType* type = obj->GetObjectType();
asIScriptFunction* method = type->GetMethodByName(methodName.c_str());
if (method == NULL)
Log::error("Scripting", ("runMethod: object does not implement method " + methodName).c_str());
asIScriptContext *ctx = m_engine->CreateContext();
if (ctx == NULL)
{
Log::error("Scripting", "runMethod: Failed to create the context.");
//m_engine->Release();
return;
}
int r = ctx->Prepare(method);
if (r < 0)
{
Log::error("Scripting", "runMethod: Failed to prepare the context.");
ctx->Release();
return;
}
// Execute the function
r = ctx->Execute();
if (r != asEXECUTION_FINISHED)
{
// The execution didn't finish as we had planned. Determine why.
if (r == asEXECUTION_ABORTED)
{
Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out.");
}
else if (r == asEXECUTION_EXCEPTION)
{
Log::error("Scripting", "The script ended with an exception.");
}
else
{
Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r);
}
}
ctx->Release();
}
*/
//-----------------------------------------------------------------------------
/** runs the specified script
* \param string scriptName = name of script to run
*/
@ -415,9 +508,35 @@ namespace Scripting
//-----------------------------------------------------------------------------
PendingTimeout::PendingTimeout(double time, asIScriptFunction* callback_delegate)
{
m_time = time;
m_callback_delegate = callback_delegate;
}
//-----------------------------------------------------------------------------
PendingTimeout::~PendingTimeout()
{
if (m_callback_delegate != NULL)
{
asIScriptEngine* engine = World::getWorld()->getScriptEngine()->getEngine();
m_callback_delegate->Release();
}
}
//-----------------------------------------------------------------------------
void ScriptEngine::addPendingTimeout(double time, const std::string& callback_name)
{
m_pending_timeouts.push_back(PendingTimeout(time, callback_name));
m_pending_timeouts.push_back(new PendingTimeout(time, callback_name));
}
//-----------------------------------------------------------------------------
void ScriptEngine::addPendingTimeout(double time, asIScriptFunction* delegate)
{
m_pending_timeouts.push_back(new PendingTimeout(time, delegate));
}
//-----------------------------------------------------------------------------
@ -430,8 +549,16 @@ namespace Scripting
curr.m_time -= dt;
if (curr.m_time <= 0.0)
{
runFunction(true, "void " + curr.m_callback_name + "()");
m_pending_timeouts.erase(m_pending_timeouts.begin() + i);
if (curr.m_callback_delegate != NULL)
{
runDelegate(curr.m_callback_delegate);
}
else
{
runFunction(true, "void " + curr.m_callback_name + "()");
}
m_pending_timeouts.erase(i);
}
}
}

View File

@ -23,21 +23,34 @@
#include <angelscript.h>
#include <functional>
#include "scriptengine/script_utils.hpp"
#include "utils/ptr_vector.hpp"
class TrackObjectPresentation;
namespace Scripting
{
/** Represents a scripting function to execute after a given time */
struct PendingTimeout
struct PendingTimeout : NoCopy
{
double m_time;
/** We have two callback types: a string containing the name of the function
* to call (simple callback) or a "TimeoutBase" object (advanced callback)
*/
std::string m_callback_name;
asIScriptFunction* m_callback_delegate;
PendingTimeout(double time, const std::string& callback_name)
{
m_callback_delegate = NULL;
m_time = time;
m_callback_name = callback_name;
}
PendingTimeout(double time, asIScriptFunction* callback_delegate);
~PendingTimeout();
};
class ScriptEngine
@ -53,6 +66,7 @@ namespace Scripting
void runFunction(bool warn_if_not_found, std::string function_name,
std::function<void(asIScriptContext*)> callback,
std::function<void(asIScriptContext*)> get_return_value);
void runDelegate(asIScriptFunction* delegate_fn);
void evalScript(std::string script_fragment);
void cleanupCache();
@ -60,12 +74,15 @@ namespace Scripting
bool compileLoadedScripts();
void addPendingTimeout(double time, const std::string& callback_name);
void addPendingTimeout(double time, asIScriptFunction* delegate_fn);
void update(double dt);
asIScriptEngine* getEngine() { return m_engine; }
private:
asIScriptEngine *m_engine;
std::map<std::string, asIScriptFunction*> m_functions_cache;
std::vector<PendingTimeout> m_pending_timeouts;
PtrVector<PendingTimeout> m_pending_timeouts;
void configureEngine(asIScriptEngine *engine);
}; // class ScriptEngine

View File

@ -135,6 +135,16 @@ namespace Scripting
new RacePausedDialog(0.8f, 0.6f);
}
int getNumberOfKarts()
{
return race_manager->getNumberOfKarts();
}
int getNumLocalPlayers()
{
return race_manager->getNumLocalPlayers();
}
void setFog(float maxDensity, float start, float end, int r, int g, int b, float duration)
{
PropertyAnimator* animator = PropertyAnimator::get();
@ -365,6 +375,8 @@ namespace Scripting
r = engine->RegisterGlobalFunction("void exitRace()", asFUNCTION(exitRace), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("void pauseRace()", asFUNCTION(pauseRace), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("void setFog(float maxDensity, float start, float end, int r, int g, int b, float duration)", asFUNCTION(setFog), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("int getNumberOfKarts()", asFUNCTION(getNumberOfKarts), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("int getNumLocalPlayers()", asFUNCTION(getNumLocalPlayers), asCALL_CDECL); assert(r >= 0);
// TrackObject
r = engine->RegisterObjectMethod("TrackObject", "void setEnabled(bool status)", asMETHOD(::TrackObject, setEnabled), asCALL_THISCALL); assert(r >= 0);

View File

@ -16,7 +16,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "script_track.hpp"
#include "script_utils.hpp"
#include "animations/three_d_animation.hpp"
#include "input/device_manager.hpp"
@ -132,6 +132,12 @@ namespace Scripting
World::getWorld()->getScriptEngine()->addPendingTimeout(delay, *callback_name);
}
/** Call a method from the given object after the specified delay */
void setTimeoutDelegate(asIScriptFunction* obj, float delay)
{
World::getWorld()->getScriptEngine()->addPendingTimeout(delay, obj);
}
/** Log to the console */
void logInfo(std::string* log)
{
@ -180,6 +186,7 @@ namespace Scripting
{
int r; // of type asERetCodes
engine->SetDefaultNamespace("Utils");
r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(proxy_insertValues1), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues2), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues3), asCALL_CDECL); assert(r >= 0);
@ -190,6 +197,9 @@ namespace Scripting
r = engine->RegisterGlobalFunction("int randomInt(int, int)", asFUNCTION(randomInt), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("float randomFloat(int, int)", asFUNCTION(randomFloat), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("void setTimeout(const string &in, float)", asFUNCTION(setTimeout), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterFuncdef("void TimeoutCallback()"); assert(r >= 0);
r = engine->RegisterGlobalFunction("void setTimeoutDelegate(TimeoutCallback@, float)", asFUNCTION(setTimeoutDelegate), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("void logInfo(const string &in)", asFUNCTION(logInfo), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("void logWarning(const string &in)", asFUNCTION(logWarning), asCALL_CDECL); assert(r >= 0);

View File

@ -63,10 +63,6 @@ DEFINE_SCREEN_SINGLETON( RaceResultGUI );
RaceResultGUI::RaceResultGUI() : Screen("race_result.stkgui",
/*pause race*/ false)
{
std::string path = file_manager->getAsset(FileManager::MUSIC,
"race_summary.music");
m_race_over_music = music_manager->getMusicInformation(path);
} // RaceResultGUI
//-----------------------------------------------------------------------------
@ -87,7 +83,25 @@ void RaceResultGUI::init()
getWidget("bottom")->setVisible(false);
music_manager->stopMusic();
m_finish_sound = SFXManager::get()->quickSound("race_finish");
bool human_win = true;
unsigned int num_karts = race_manager->getNumberOfKarts();
for (unsigned int kart_id = 0; kart_id < num_karts; kart_id++)
{
const AbstractKart *kart = World::getWorld()->getKart(kart_id);
if (kart->getController()->isPlayerController())
human_win = human_win && kart->getRaceResult();
}
m_finish_sound = SFXManager::get()->quickSound(
human_win ? "gp_end" : "race_finish");
//std::string path = (human_win ? Different result music too later
// file_manager->getAsset(FileManager::MUSIC, "race_summary.music") :
// file_manager->getAsset(FileManager::MUSIC, "race_summary.music"));
std::string path = file_manager->getAsset(FileManager::MUSIC, "race_summary.music");
m_race_over_music = music_manager->getMusicInformation(path);
if (!m_finish_sound)
{
// If there is no finish sound (because sfx are disabled), start

View File

@ -97,8 +97,9 @@ void TrackInfoScreen::setTrack(Track *track)
*/
void TrackInfoScreen::init()
{
const bool has_laps = race_manager->modeHasLaps();
const bool has_highscores = race_manager->modeHasHighscores();
const int max_arena_players = m_track->getMaxArenaPlayers();
const bool has_laps = race_manager->modeHasLaps();
const bool has_highscores = race_manager->modeHasHighscores();
getWidget<LabelWidget>("name")->setText(translations->fribidize(m_track->getName()), false);
@ -107,6 +108,14 @@ void TrackInfoScreen::init()
getWidget<LabelWidget>("author")->setText( _("Track by %s", m_track->getDesigner()),
false );
LabelWidget* max_players = getWidget<LabelWidget>("max-arena-players");
max_players->setVisible(m_track->isArena());
if (m_track->isArena())
{
//I18N: the max players supported by an arena.
max_players->setText( _("Max players supported: %d", max_arena_players), false );
}
// ---- Track screenshot
GUIEngine::IconButtonWidget* screenshot = getWidget<IconButtonWidget>("screenshot");
@ -143,9 +152,11 @@ void TrackInfoScreen::init()
// Number of AIs
// -------------
const int local_players = race_manager->getNumLocalPlayers();
const bool has_AI =
(race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ?
m_track->hasNavMesh() : race_manager->hasAI());
m_track->hasNavMesh() && (max_arena_players - local_players) > 0 :
race_manager->hasAI());
m_ai_kart_spinner->setVisible(has_AI);
getWidget<LabelWidget>("ai-text")->setVisible(has_AI);
if (has_AI)
@ -154,25 +165,25 @@ void TrackInfoScreen::init()
// Avoid negative numbers (which can happen if e.g. the number of karts
// in a previous race was lower than the number of players now.
int num_ai = UserConfigParams::m_num_karts - race_manager->getNumLocalPlayers();
int num_ai = UserConfigParams::m_num_karts - local_players;
if (num_ai < 0) num_ai = 0;
m_ai_kart_spinner->setValue(num_ai);
race_manager->setNumKarts(num_ai + race_manager->getNumLocalPlayers());
// Currently battle arena only has 4 starting position
race_manager->setNumKarts(num_ai + local_players);
// Set the max karts supported based on the battle arena selected
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES)
{
m_ai_kart_spinner->setMax(4 - race_manager->getNumLocalPlayers());
m_ai_kart_spinner->setMax(max_arena_players - local_players);
}
else
m_ai_kart_spinner->setMax(stk_config->m_max_karts - race_manager->getNumLocalPlayers());
m_ai_kart_spinner->setMax(stk_config->m_max_karts - local_players);
// A ftl reace needs at least three karts to make any sense
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
m_ai_kart_spinner->setMin(3-race_manager->getNumLocalPlayers());
m_ai_kart_spinner->setMin(3 - local_players);
}
// Make sure in battle mode at least 1 ai for single player
else if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES &&
race_manager->getNumLocalPlayers() == 1 &&
local_players == 1 &&
!UserConfigParams::m_artist_debug_mode)
m_ai_kart_spinner->setMin(1);
else
@ -180,7 +191,7 @@ void TrackInfoScreen::init()
} // has_AI
else
race_manager->setNumKarts(race_manager->getNumLocalPlayers());
race_manager->setNumKarts(local_players);
// Reverse track
// -------------
@ -296,20 +307,21 @@ void TrackInfoScreen::onEnterPressedInternal()
race_manager->setReverseTrack(reverse_track);
// Avoid invaild Ai karts number during switching game modes
const int max_arena_players = m_track->getMaxArenaPlayers();
const int local_players = race_manager->getNumLocalPlayers();
const bool has_AI =
(race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ?
m_track->hasNavMesh() : race_manager->hasAI());
m_track->hasNavMesh() && (max_arena_players - local_players) > 0 :
race_manager->hasAI());
int num_ai = 0;
if (has_AI)
num_ai = m_ai_kart_spinner->getValue();
if (UserConfigParams::m_num_karts != (signed)(race_manager
->getNumLocalPlayers() + num_ai))
if (UserConfigParams::m_num_karts != (local_players + num_ai))
{
race_manager->setNumKarts(race_manager->getNumLocalPlayers() + num_ai);
UserConfigParams::m_num_karts = race_manager->getNumLocalPlayers() +
num_ai;
race_manager->setNumKarts(local_players + num_ai);
UserConfigParams::m_num_karts = local_players + num_ai;
}
// Disable accidentally unlocking of a challenge

View File

@ -23,12 +23,7 @@
#include <IMeshSceneNode.h>
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/glwrap.hpp"
#include "graphics/shaders.hpp"
#include "graphics/rtts.hpp"
#include "items/item_manager.hpp"
#include "modes/world.hpp"
#include "tracks/navmesh.hpp"
#include "utils/log.hpp"
@ -40,10 +35,6 @@ BattleGraph * BattleGraph::m_battle_graph = NULL;
* by the AI. */
BattleGraph::BattleGraph(const std::string &navmesh_file_name)
{
m_node = NULL;
m_mesh = NULL;
m_mesh_buffer = NULL;
m_new_rtt = NULL;
m_items_on_graph.clear();
NavMesh::create(navmesh_file_name);
@ -60,8 +51,7 @@ BattleGraph::~BattleGraph(void)
if(UserConfigParams::m_track_debug)
cleanupDebugMesh();
if (m_new_rtt != NULL)
delete m_new_rtt;
GraphStructure::destroyRTT();
} // ~BattleGraph
// -----------------------------------------------------------------------------
@ -132,266 +122,7 @@ void BattleGraph::computeFloydWarshall()
} // computeFloydWarshall
// -----------------------------------------------------------------------------
/** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap() */
void BattleGraph::createMesh(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_mesh = irr_driver->createQuadMesh(&m);
m_mesh_buffer = m_mesh->getMeshBuffer(0);
assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD);
const unsigned int num_nodes = getNumNodes();
// Four vertices for each of the n-1 remaining quads
video::S3DVertex *new_v = new video::S3DVertex[4*num_nodes];
// 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*num_nodes];
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<num_nodes; count++)
{
// There should not be a poly which isn't made of 4 vertices
if((NavMesh::get()->getNavPoly(count).getVerticesIndex()).size() !=4)
{
Log::warn("Battle Graph", "There is an invalid poly!");
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);
}
// Transfer the 4 points of the current quad to the list of vertices
NavMesh::get()->setVertices(count, new_v+4*i, 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++;
} // for i=1; i<QuadSet::get()
m_mesh_buffer->append(new_v, num_nodes*4, ind, num_nodes*6);
// 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"));
} // createMesh
// -----------------------------------------------------------------------------
/** Takes a snapshot of the navmesh so they can be used as minimap.
*/
void BattleGraph::makeMiniMap(const core::dimension2du &dimension,
const std::string &name,
const video::SColor &fill_color,
video::ITexture** oldRttMinimap,
FrameBuffer** newRttMinimap)
{
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(/*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;
NavMesh::get()->getBoundingBox(&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("BattleGraph", "[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 BattleGraph::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
// -----------------------------------------------------------------------------
/** Creates the debug mesh to display the quad graph on top of the track
* model. */
void BattleGraph::createDebugMesh()
{
if(getNumNodes()<=0) return; // no debug output if not graph
createMesh(/*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
// -----------------------------------------------------------------------------
/** Cleans up the debug mesh */
void BattleGraph::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;
}
// -----------------------------------------------------------------------------
/** Maps items on battle graph */
void BattleGraph::findItemsOnGraphNodes()
{
@ -407,11 +138,7 @@ void BattleGraph::findItemsOnGraphNodes()
for (unsigned int j = 0; j < this->getNumNodes(); ++j)
{
if (NavMesh::get()->getNavPoly(j).pointInPoly(xyz))
{
float dist = xyz.getY() - NavMesh::get()->getCenterOfPoly(j).getY();
if (fabsf(dist) < 1.0f )
polygon = j;
}
polygon = j;
}
if (polygon != BattleGraph::UNKNOWN_POLY)

View File

@ -23,21 +23,13 @@
#include <string>
#include <set>
#include <dimension2d.h>
#include "tracks/graph_structure.hpp"
#include "tracks/navmesh.hpp"
namespace irr
{
namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; }
namespace video { class ITexture; }
}
using namespace irr;
class FrameBuffer;
class GraphStructure;
class Item;
class ItemManager;
class Navmesh;
class RTT;
/**
* \ingroup tracks
@ -49,30 +41,16 @@ class RTT;
* design pattern to create an instance).
\ingroup tracks
*/
class BattleGraph
class BattleGraph : public GraphStructure
{
private:
static BattleGraph *m_battle_graph;
RTT* m_new_rtt;
/** 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;
/** For debug mode only: the node of the debug mesh. */
scene::ISceneNode *m_node;
/** For debug only: the mesh of the debug mesh. */
scene::IMesh *m_mesh;
/** For debug only: the actual mesh buffer storing the quads. */
scene::IMeshBuffer *m_mesh_buffer;
/** The minimum coordinates of the quad graph. */
Vec3 m_min_coord;
/** Scaling for mini map. */
float m_scaling;
/** Stores the name of the file containing the NavMesh data */
std::string m_navmesh_file;
@ -81,12 +59,27 @@ private:
void buildGraph(NavMesh*);
void computeFloydWarshall();
void createMesh(bool enable_transparency=false,
const video::SColor *track_color=NULL);
BattleGraph(const std::string &navmesh_file_name);
~BattleGraph(void);
// ------------------------------------------------------------------------
virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v,
const video::SColor &color) const
{ NavMesh::get()->setVertices(i, v, color); }
// ------------------------------------------------------------------------
virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const
{ NavMesh::get()->getBoundingBox(min, max); }
// ------------------------------------------------------------------------
virtual const bool isNodeInvisible(int n) const
{ return false; }
// ------------------------------------------------------------------------
virtual const bool isNodeInvalid(int n) const
{ return (NavMesh::get()->getNavPoly(n).getVerticesIndex()).size()!=4; }
// ------------------------------------------------------------------------
virtual const bool hasLapLine() const
{ return false; }
public:
static const int UNKNOWN_POLY;
@ -115,12 +108,13 @@ public:
// ----------------------------------------------------------------------
/** Returns the number of nodes in the BattleGraph (equal to the number of
* polygons in the NavMesh */
unsigned int getNumNodes() const { return m_distance_matrix.size(); }
virtual const unsigned int getNumNodes() const
{ return m_distance_matrix.size(); }
// ----------------------------------------------------------------------
/** Returns the NavPoly corresponding to the i-th node of the BattleGraph */
const NavPoly& getPolyOfNode(int i) const
{ return NavMesh::get()->getNavPoly(i); }
{ return NavMesh::get()->getNavPoly(i); }
// ----------------------------------------------------------------------
/** Returns the next polygon on the shortest path from i to j.
@ -129,17 +123,9 @@ public:
const int & getNextShortestPathPoly(int i, int j) const;
const std::vector < std::pair<const Item*, int> >& getItemList()
{ return m_items_on_graph; }
{ return m_items_on_graph; }
void createDebugMesh();
void cleanupDebugMesh();
void findItemsOnGraphNodes();
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;
}; //BattleGraph
#endif

View File

@ -0,0 +1,384 @@
//
// 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 <IMesh.h>
#include <ICameraSceneNode.h>
#include <IMeshSceneNode.h>
#include "graphics/irr_driver.hpp"
#include "graphics/glwrap.hpp"
#include "graphics/shaders.hpp"
#include "graphics/rtts.hpp"
#include "modes/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_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;
else if (isNodeInvalid(count))
{
// There should not be a node which isn't made of 4 vertices
Log::warn("Graph Structure", "There is an invalid node!");
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);
}
// Transfer the 4 points of the current quad to the list of vertices
set3DVerticesOfGraph(count, new_v+4*i, 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)
{
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

View File

@ -0,0 +1,94 @@
//
// 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:
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 isNodeInvalid(int n) const = 0;
virtual const bool hasLapLine() 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

View File

@ -70,6 +70,10 @@ bool NavPoly::pointInPoly(const Vec3& p) const
return false;
}
// Check for vertical distance too
const float dist = p.getY() - m_center.getY();
if (fabsf(dist) > 1.0f )
return false;
return true;
}

View File

@ -51,11 +51,7 @@ QuadGraph::QuadGraph(const std::string &quad_file_name,
const std::string &graph_file_name,
const bool reverse) : m_reverse(reverse)
{
m_node = NULL;
m_mesh = NULL;
m_mesh_buffer = NULL;
m_lap_length = 0;
m_new_rtt = NULL;
QuadSet::create();
QuadSet::get()->init(quad_file_name);
m_quad_filename = quad_file_name;
@ -73,8 +69,7 @@ QuadGraph::~QuadGraph()
}
if(UserConfigParams::m_track_debug)
cleanupDebugMesh();
if (m_new_rtt != NULL)
delete m_new_rtt;
GraphStructure::destroyRTT();
} // ~QuadGraph
// -----------------------------------------------------------------------------
@ -394,186 +389,6 @@ void QuadGraph::setDefaultStartPositions(AlignedArray<btTransform>
} // for i<stk_config->m_max_karts
} // setStartPositions
// -----------------------------------------------------------------------------
/** Creates a mesh for this graph. The mesh is not added to a scene node and
* is stored in m_mesh.
* \param show_invisble If true, also create a mesh for parts of the
* driveline that are invisible.
* \param enable_transparency Enable alpha blending to make the mesh
* semi transparent.
* \param track_color Colour of the actual quads.
* \param lap_color If defined, show the lap counting line in that colour.
*/
void QuadGraph::createMesh(bool show_invisible,
bool enable_transparency,
const video::SColor *track_color,
const video::SColor *lap_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(SColor(255, 255, 255, 255)));
m.setTexture(1, getUnicolorTexture(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);
// Count the number of quads to display (some quads might be invisible
unsigned int n = 0;
for(unsigned int i=0; i<m_all_nodes.size(); i++)
{
if(show_invisible || !m_all_nodes[i]->getQuad().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<m_all_nodes.size(); count++)
{
// Ignore invisible quads
if(!show_invisible && m_all_nodes[count]->getQuad().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);
}
// Transfer the 4 points of the current quad to the list of vertices
m_all_nodes[count]->getQuad().getVertices(new_v+4*i, 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++;
} // for i=1; i<QuadSet::get()
m_mesh_buffer->append(new_v, n*4, ind, n*6);
if(lap_color)
{
video::S3DVertex lap_v[4];
irr::u16 lap_ind[6];
video::SColor c(128, 255, 0, 0);
m_all_nodes[0]->getQuad().getVertices(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;
QuadSet::get()->getBoundingBox(&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
// -----------------------------------------------------------------------------
/** Creates the debug mesh to display the quad graph on top of the track
* model. */
void QuadGraph::createDebugMesh()
{
if(m_all_nodes.size()<=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, "quad_graph_debug");
#ifdef DEBUG
m_node->setName("track-debug-mesh");
#endif
} // createDebugMesh
// -----------------------------------------------------------------------------
/** Removes the debug mesh from the scene.
*/
void QuadGraph::cleanupDebugMesh()
{
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
// -----------------------------------------------------------------------------
/** Returns the list of successors or a node.
* \param node_number The number of the node.
@ -974,155 +789,3 @@ int QuadGraph::findOutOfRoadSector(const Vec3& xyz,
}
return min_sector;
} // findOutOfRoadSector
//-----------------------------------------------------------------------------
/** Takes a snapshot of the driveline quads so they can be used as minimap.
*/
void QuadGraph::makeMiniMap(const core::dimension2du &dimension,
const std::string &name,
const video::SColor &fill_color,
video::ITexture** oldRttMinimap,
FrameBuffer** newRttMinimap)
{
const SColor oldClearColor = World::getWorld()->getClearColor();
World::getWorld()->setClearbackBufferColor(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));
video::SColor red(128, 255, 0, 0);
createMesh(/*show_invisible part of the track*/ false,
/*enable_transparency*/ false,
/*track_color*/ &fill_color,
/*lap line color*/ &red );
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;
QuadSet::get()->getBoundingBox(&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("Quad 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 QuadGraph::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

View File

@ -24,21 +24,12 @@
#include <set>
#include "tracks/graph_node.hpp"
#include "tracks/graph_structure.hpp"
#include "tracks/quad_set.hpp"
#include "utils/aligned_array.hpp"
#include "utils/no_copy.hpp"
#include <dimension2d.h>
namespace irr
{
namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; }
namespace video { class ITexture; }
}
using namespace irr;
class CheckLine;
class RTT;
class FrameBuffer;
class GraphStructure;
/**
* \brief This class stores a graph of quads. It uses a 'simplified singleton'
@ -50,32 +41,18 @@ class FrameBuffer;
* returns NULL in this case, and this is tested where necessary.
* \ingroup tracks
*/
class QuadGraph : public NoCopy
class QuadGraph : public GraphStructure
{
private:
static QuadGraph *m_quad_graph;
RTT* m_new_rtt;
/** The actual graph data structure. */
std::vector<GraphNode*> m_all_nodes;
/** For debug mode only: the node of the debug mesh. */
scene::ISceneNode *m_node;
/** For debug only: the mesh of the debug mesh. */
scene::IMesh *m_mesh;
/** For debug only: the actual mesh buffer storing the quads. */
scene::IMeshBuffer *m_mesh_buffer;
/** The length of the first loop. */
float m_lap_length;
/** The minimum coordinates of the quad graph. */
Vec3 m_min_coord;
/** Scaling for mini map. */
float m_scaling;
/** Stores the filename - just used for error messages. */
std::string m_quad_filename;
@ -91,20 +68,32 @@ private:
void addSuccessor(unsigned int from, unsigned int to);
void load (const std::string &filename);
void computeDistanceFromStart(unsigned int start_node, float distance);
void createMesh(bool show_invisible=true,
bool enable_transparency=false,
const video::SColor *track_color=NULL,
const video::SColor *lap_color=NULL);
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 isNodeInvalid(int n) const
{ return false; }
// ------------------------------------------------------------------------
virtual const bool hasLapLine() const
{ return true; }
public:
static const int UNKNOWN_SECTOR;
void createDebugMesh();
void cleanupDebugMesh();
void getSuccessors(int node_number,
std::vector<unsigned int>& succ,
bool for_ai=false) const;
@ -122,12 +111,6 @@ public:
float forwards_distance=1.5f,
float sidewards_distance=1.5f,
float upwards_distance=0.0f) const;
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;
void updateDistancesForAllSuccessors(unsigned int indx,
float delta,
unsigned int count);
@ -164,7 +147,8 @@ public:
} // destroy
// ------------------------------------------------------------------------
/** Returns the number of nodes in the graph. */
unsigned int getNumNodes() const { return (unsigned int)m_all_nodes.size();}
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
@ -193,9 +177,7 @@ public:
/** Returns the length of the main driveline. */
float getLapLength() const {return m_lap_length; }
// ------------------------------------------------------------------------
/** Returns true if the graph is to be reversed. */
bool isReverse() const {return m_reverse; }
}; // QuadGraph
#endif

View File

@ -115,6 +115,7 @@ Track::Track(const std::string &filename)
m_enable_push_back = true;
m_reverse_available = false;
m_is_arena = false;
m_max_arena_players = 0;
m_has_easter_eggs = false;
m_has_navmesh = false;
m_is_soccer = false;
@ -510,6 +511,7 @@ void Track::loadTrackInfo()
root->get("gravity", &m_gravity);
root->get("soccer", &m_is_soccer);
root->get("arena", &m_is_arena);
root->get("max-arena-players", &m_max_arena_players);
root->get("cutscene", &m_is_cutscene);
root->get("groups", &m_groups);
root->get("internal", &m_internal);
@ -672,35 +674,7 @@ void Track::loadBattleGraph()
}
else
{
//Check whether the hardware can do nonsquare or
// non power-of-two textures
video::IVideoDriver* const video_driver = irr_driver->getVideoDriver();
bool nonpower = false; //video_driver->queryFeature(video::EVDF_TEXTURE_NPOT);
bool nonsquare =
video_driver->queryFeature(video::EVDF_TEXTURE_NSQUARE);
//Create the minimap resizing it as necessary.
m_mini_map_size = World::getWorld()->getRaceGUI()->getMiniMapSize();
core::dimension2du size = m_mini_map_size
.getOptimalSize(!nonpower,!nonsquare);
BattleGraph::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)
{
m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_old_rtt_mini_map->getSize().Width);
m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_old_rtt_mini_map->getSize().Height);
}
else if (m_new_rtt_mini_map)
{
m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_new_rtt_mini_map->getWidth());
m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_new_rtt_mini_map->getHeight());
}
else
{
m_minimap_x_scale = 0;
m_minimap_y_scale = 0;
}
loadMinimap();
}
} // loadBattleGraph
@ -734,35 +708,7 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse)
}
else
{
//Check whether the hardware can do nonsquare or
// non power-of-two textures
video::IVideoDriver* const video_driver = irr_driver->getVideoDriver();
bool nonpower = false; //video_driver->queryFeature(video::EVDF_TEXTURE_NPOT);
bool nonsquare =
video_driver->queryFeature(video::EVDF_TEXTURE_NSQUARE);
//Create the minimap resizing it as necessary.
m_mini_map_size = World::getWorld()->getRaceGUI()->getMiniMapSize();
core::dimension2du size = m_mini_map_size
.getOptimalSize(!nonpower,!nonsquare);
QuadGraph::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)
{
m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_old_rtt_mini_map->getSize().Width);
m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_old_rtt_mini_map->getSize().Height);
}
else if (m_new_rtt_mini_map)
{
m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_new_rtt_mini_map->getWidth());
m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_new_rtt_mini_map->getHeight());
}
else
{
m_minimap_x_scale = 0;
m_minimap_y_scale = 0;
}
loadMinimap();
}
} // loadQuadGraph
@ -1070,6 +1016,50 @@ void Track::convertTrackToBullet(scene::ISceneNode *node)
} // convertTrackToBullet
// ----------------------------------------------------------------------------
void Track::loadMinimap()
{
//Check whether the hardware can do nonsquare or
// non power-of-two textures
video::IVideoDriver* const video_driver = irr_driver->getVideoDriver();
bool nonpower = false; //video_driver->queryFeature(video::EVDF_TEXTURE_NPOT);
bool nonsquare =
video_driver->queryFeature(video::EVDF_TEXTURE_NSQUARE);
//Create the minimap resizing it as necessary.
m_mini_map_size = World::getWorld()->getRaceGUI()->getMiniMapSize();
core::dimension2du size = m_mini_map_size
.getOptimalSize(!nonpower,!nonsquare);
if (m_is_arena && 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);
}
if (m_old_rtt_mini_map)
{
m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_old_rtt_mini_map->getSize().Width);
m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_old_rtt_mini_map->getSize().Height);
}
else if (m_new_rtt_mini_map)
{
m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_new_rtt_mini_map->getWidth());
m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_new_rtt_mini_map->getHeight());
}
else
{
m_minimap_x_scale = 0;
m_minimap_y_scale = 0;
}
} // loadMinimap
// ----------------------------------------------------------------------------
/** Loads the main track model (i.e. all other objects contained in the
* scene might use raycast on this track model to determine the actual

View File

@ -204,6 +204,8 @@ private:
Vec3 m_aabb_max;
/** True if this track is an arena. */
bool m_is_arena;
/** Max players supported by an arena. */
unsigned int m_max_arena_players;
/** True if this track has easter eggs. */
bool m_has_easter_eggs;
/** True if this track has navmesh. */
@ -382,6 +384,7 @@ private:
void loadBattleGraph();
void convertTrackToBullet(scene::ISceneNode *node);
bool loadMainTrack(const XMLNode &node);
void loadMinimap();
void createWater(const XMLNode &node);
void getMusicInformation(std::vector<std::string>& filenames,
std::vector<MusicInformation*>& m_music );
@ -543,6 +546,10 @@ public:
/** Returns the graphical effect mesh for this track. */
const TriangleMesh& getGFXEffectMesh() const {return *m_gfx_effect_mesh;}
// ------------------------------------------------------------------------
/** Get the max players supported for this track, for arena only. */
unsigned int getMaxArenaPlayers() const
{ return m_max_arena_players; }
// ------------------------------------------------------------------------
/** Get the number of start positions defined in the scene file. */
unsigned int getNumberOfStartPositions() const
{ return (unsigned int)m_start_transforms.size(); }