stk-code_catmod/src/graphics/referee.cpp
2018-01-25 15:38:10 +08:00

283 lines
10 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2011-2015 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "graphics/referee.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/light.hpp"
#include "graphics/material.hpp"
#include "graphics/mesh_tools.hpp"
#include "graphics/sp/sp_mesh_buffer.hpp"
#include "graphics/sp/sp_mesh_node.hpp"
#include "karts/abstract_kart.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "utils/constants.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"
int Referee::m_st_first_start_frame = 1;
int Referee::m_st_last_start_frame = 1;
int Referee::m_st_first_rescue_frame = 1;
int Referee::m_st_last_rescue_frame = 1;
int Referee::m_st_traffic_buffer = -1;
Vec3 Referee::m_st_start_offset = Vec3(-2, 2, 2);
Vec3 Referee::m_st_start_rotation = Vec3(0, 180, 0);
Vec3 Referee::m_st_scale = Vec3(1, 1, 1);
float Referee::m_height = 0.0f;
scene::IAnimatedMesh *Referee::m_st_referee_mesh = NULL;
// ----------------------------------------------------------------------------
/** Loads the static mesh.
*/
void Referee::init()
{
assert(!m_st_referee_mesh);
const std::string filename=file_manager->getAssetChecked(FileManager::MODEL,
"referee.xml", true);
XMLNode *node = file_manager->createXMLTree(filename);
if(!node)
{
Log::fatal("referee", "Can't read XML file referee.xml, aborting.");
}
if(node->getName()!="referee")
{
Log::fatal("referee", "The file referee.xml does not contain a referee"
"node, aborting.");
}
std::string model_filename;
node->get("model", &model_filename);
m_st_referee_mesh = irr_driver->getAnimatedMesh(
file_manager->getAsset(FileManager::MODEL,
model_filename) );
if(!m_st_referee_mesh)
{
Log::fatal("referee", "Can't find referee model '%s', aborting.",
model_filename.c_str());
}
node->get("first-rescue-frame", &m_st_first_rescue_frame);
node->get("last-rescue-frame", &m_st_last_rescue_frame );
node->get("first-start-frame", &m_st_first_start_frame );
node->get("last-start-frame", &m_st_last_start_frame );
node->get("start-offset", &m_st_start_offset );
node->get("scale", &m_st_scale );
node->get("start-rotation", &m_st_start_rotation );
float angle_to_kart = atan2(m_st_start_offset.getX(),
m_st_start_offset.getZ())
* RAD_TO_DEGREE;
m_st_start_rotation.setY(m_st_start_rotation.getY()+angle_to_kart);
for(unsigned int i=0; i<m_st_referee_mesh->getMeshBufferCount(); i++)
{
if (m_st_traffic_buffer != -1)
{
break;
}
scene::IMeshBuffer *mb = m_st_referee_mesh->getMeshBuffer(i);
SP::SPMeshBuffer* spmb = dynamic_cast<SP::SPMeshBuffer*>(mb);
if (spmb)
{
auto ret = spmb->getAllSTKMaterials();
for (unsigned j = 0; j < ret.size(); j++)
{
std::string name =
StringUtils::getBasename(ret[j]->getSamplerPath(0));
if (name == "traffic_light.png")
{
m_st_traffic_buffer = i;
spmb->enableTextureMatrix(j);
break;
}
}
continue;
}
video::SMaterial &irrMaterial = mb->getMaterial();
video::ITexture* t=irrMaterial.getTexture(0);
if(!t) continue;
std::string name=StringUtils::getBasename(t->getName()
.getInternalName().c_str());
if (name == "traffic_light.png")
{
m_st_traffic_buffer = i;
break;
}
else
{
irrMaterial.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
}
}
delete node;
} // init
// ----------------------------------------------------------------------------
/** Frees the static mesh.
*/
void Referee::cleanup()
{
irr_driver->removeMeshFromCache(m_st_referee_mesh);
m_st_referee_mesh = NULL;
m_st_traffic_buffer = -1;
} // cleanup
// ----------------------------------------------------------------------------
/** Creates an instance of the referee, using the static values to initialise
* it. This is the constructor used when a start referee is needed.
*/
Referee::Referee()
{
assert(m_st_referee_mesh);
// First add a NULL mesh, then set the material to be read only
// (this appears to be the only way to get read only materials).
// This way we only need to adjust the materials in the original
// mesh. ATM it doesn't make any difference, but if we ever should
// decide to use more than one referee model at startup we only
// have to change the textures once, and all models will be in synch.
m_scene_node = irr_driver->addAnimatedMesh(NULL, "referee");
m_scene_node->setReadOnlyMaterials(true);
m_scene_node->setMesh(m_st_referee_mesh);
m_scene_node->grab();
m_scene_node->setRotation(m_st_start_rotation.toIrrVector());
m_scene_node->setScale(m_st_scale.toIrrVector());
m_scene_node->setFrameLoop(m_st_first_start_frame,
m_st_last_start_frame);
#ifndef SERVER_ONLY
if (CVS->isGLSL() && CVS->isDeferredEnabled())
{
m_light = irr_driver->addLight(core::vector3df(0.0f, 0.0f, 0.6f), 0.7f, 2.0f,
0.7f /* r */, 0.0 /* g */, 0.0f /* b */, false /* sun */, m_scene_node);
}
else
#endif
{
m_light = NULL;
}
} // Referee
// ----------------------------------------------------------------------------
/** Creates an instance of the referee, using the static values to initialise
* it. This is the constructor used when a rescue referee is needed.
* \param kart The kart which the referee should rescue.
*/
Referee::Referee(const AbstractKart &kart)
{
assert(m_st_referee_mesh);
// First add a NULL mesh, then set the material to be read only
// (this appears to be the only way to get read only materials).
// This way we only need to adjust the materials in the original
// mesh. ATM it doesn't make any difference, but if we ever should
// decide to use more than one referee model at startup we only
// have to change the textures once, and all models will be in synch.
m_scene_node = irr_driver->addAnimatedMesh(NULL, "referee");
m_scene_node->setReadOnlyMaterials(true);
m_scene_node->setMesh(m_st_referee_mesh);
m_scene_node->grab();
m_scene_node->setScale(m_st_scale.toIrrVector());
m_scene_node->setPosition(core::vector3df(0, kart.getKartHeight() + 0.4f, 0));
m_scene_node->setFrameLoop(m_st_first_rescue_frame,
m_st_last_rescue_frame);
} // Referee
// ----------------------------------------------------------------------------
Referee::~Referee()
{
if(m_scene_node->getParent())
irr_driver->removeNode(m_scene_node);
m_scene_node->drop();
} // ~Referee
// ----------------------------------------------------------------------------
/** Make sure that this referee is attached to the scene graph. This is used
* for the start referee, which is removed from scene graph once the ready-
* set-go phase is over (it is kept in case of a restart of the race).
*/
void Referee::attachToSceneNode()
{
if(!m_scene_node->getParent())
m_scene_node->setParent(irr_driver->getSceneManager()
->getRootSceneNode());
if (m_light != NULL)
m_light->setVisible(true);
} // attachToSceneNode
// ----------------------------------------------------------------------------
/** Removes the referee's scene node from the scene graph, but still keeps
* the scene node in memory. This is used for the start referee, so that
* it is quickly available in case of a restart.
*/
void Referee::removeFromSceneGraph()
{
if(isAttached())
irr_driver->removeNode(m_scene_node);
if (m_light != NULL)
m_light->setVisible(false);
} // removeFromSceneGraph
// ----------------------------------------------------------------------------
/** Selects one of the states 'ready', 'set', or 'go' to be displayed by
* the referee.
* \param rsg 0=ready, 1=set, 2=go.
*/
void Referee::selectReadySetGo(int rsg)
{
if (m_st_traffic_buffer < 0)
return;
SP::SPMeshNode* spmn = dynamic_cast<SP::SPMeshNode*>(m_scene_node);
if (spmn)
{
spmn->setTextureMatrix(m_st_traffic_buffer, {{ 0.0f, rsg * 0.333f }});
}
else
{
video::SMaterial &m = m_scene_node->getMaterial(m_st_traffic_buffer);
core::matrix4* matrix = &m.getTextureMatrix(0);
matrix->setTextureTranslate(0.0f, rsg*0.333f);
// disable lighting, we need to see the traffic light even if facing away
// from the sun
m.AmbientColor = video::SColor(255, 255, 255, 255);
m.DiffuseColor = video::SColor(255, 255, 255, 255);
m.EmissiveColor = video::SColor(255, 255, 255, 255);
m.SpecularColor = video::SColor(255, 255, 255, 255);
}
if (m_light != NULL)
{
if (rsg == 0)
{
((LightNode*)m_light)->setColor(0.6f, 0.0f, 0.0f);
}
else if (rsg == 1)
{
((LightNode*)m_light)->setColor(0.7f, 0.23f, 0.0f);
}
else if (rsg == 2)
{
((LightNode*)m_light)->setColor(0.0f, 0.6f, 0.0f);
}
}
} // selectReadySetGo