1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2011-2015 Joerg Henrichs
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "graphics/referee.hpp"
20 #include "config/stk_config.hpp"
21 #include "graphics/central_settings.hpp"
22 #include "graphics/irr_driver.hpp"
23 #include "graphics/light.hpp"
24 #include "graphics/material.hpp"
25 #include "graphics/mesh_tools.hpp"
26 #include "graphics/sp/sp_mesh_buffer.hpp"
27 #include "graphics/sp/sp_mesh_node.hpp"
28 #include "karts/abstract_kart.hpp"
29 #include "io/file_manager.hpp"
30 #include "io/xml_node.hpp"
31 #include "modes/world.hpp"
32 #include "utils/constants.hpp"
33 #include "utils/log.hpp"
34 #include "utils/string_utils.hpp"
35
36 int Referee::m_st_first_start_frame = 1;
37 int Referee::m_st_last_start_frame = 1;
38 int Referee::m_st_first_rescue_frame = 1;
39 int Referee::m_st_last_rescue_frame = 1;
40 int Referee::m_st_traffic_buffer = -1;
41 Vec3 Referee::m_st_start_offset = Vec3(-2, 2, 2);
42 Vec3 Referee::m_st_start_rotation = Vec3(0, 180, 0);
43 Vec3 Referee::m_st_scale = Vec3(1, 1, 1);
44 float Referee::m_height = 0.0f;
45 scene::IAnimatedMesh *Referee::m_st_referee_mesh = NULL;
46
47 // ----------------------------------------------------------------------------
48 /** Loads the static mesh.
49 */
init()50 void Referee::init()
51 {
52 assert(!m_st_referee_mesh);
53 const std::string filename=file_manager->getAssetChecked(FileManager::MODEL,
54 "referee.xml", true);
55 XMLNode *node = file_manager->createXMLTree(filename);
56 if(!node)
57 {
58 Log::fatal("referee", "Can't read XML file referee.xml, aborting.");
59 }
60 if(node->getName()!="referee")
61 {
62 Log::fatal("referee", "The file referee.xml does not contain a referee"
63 "node, aborting.");
64 }
65 std::string model_filename;
66 node->get("model", &model_filename);
67
68 m_st_referee_mesh = irr_driver->getAnimatedMesh(
69 file_manager->getAsset(FileManager::MODEL,
70 model_filename) );
71 if(!m_st_referee_mesh)
72 {
73 Log::fatal("referee", "Can't find referee model '%s', aborting.",
74 model_filename.c_str());
75 }
76
77 node->get("first-rescue-frame", &m_st_first_rescue_frame);
78 node->get("last-rescue-frame", &m_st_last_rescue_frame );
79 node->get("first-start-frame", &m_st_first_start_frame );
80 node->get("last-start-frame", &m_st_last_start_frame );
81 node->get("start-offset", &m_st_start_offset );
82 node->get("scale", &m_st_scale );
83 node->get("start-rotation", &m_st_start_rotation );
84
85 float angle_to_kart = atan2(m_st_start_offset.getX(),
86 m_st_start_offset.getZ())
87 * RAD_TO_DEGREE;
88 m_st_start_rotation.setY(m_st_start_rotation.getY()+angle_to_kart);
89
90 for(unsigned int i=0; i<m_st_referee_mesh->getMeshBufferCount(); i++)
91 {
92 if (m_st_traffic_buffer != -1)
93 {
94 break;
95 }
96 scene::IMeshBuffer *mb = m_st_referee_mesh->getMeshBuffer(i);
97 SP::SPMeshBuffer* spmb = dynamic_cast<SP::SPMeshBuffer*>(mb);
98 if (spmb)
99 {
100 auto ret = spmb->getAllSTKMaterials();
101 for (unsigned j = 0; j < ret.size(); j++)
102 {
103 std::string name =
104 StringUtils::getBasename(ret[j]->getSamplerPath(0));
105 if (name == "traffic_light.png")
106 {
107 m_st_traffic_buffer = i;
108 spmb->enableTextureMatrix(j);
109 break;
110 }
111 }
112 continue;
113 }
114 video::SMaterial &irrMaterial = mb->getMaterial();
115 video::ITexture* t=irrMaterial.getTexture(0);
116 if(!t) continue;
117
118 std::string name=StringUtils::getBasename(t->getName()
119 .getInternalName().c_str());
120 if (name == "traffic_light.png")
121 {
122 m_st_traffic_buffer = i;
123 break;
124 }
125 else
126 {
127 irrMaterial.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
128 }
129
130 }
131
132 delete node;
133 } // init
134
135 // ----------------------------------------------------------------------------
136 /** Frees the static mesh.
137 */
cleanup()138 void Referee::cleanup()
139 {
140 irr_driver->removeMeshFromCache(m_st_referee_mesh);
141 m_st_referee_mesh = NULL;
142 m_st_traffic_buffer = -1;
143 } // cleanup
144
145 // ----------------------------------------------------------------------------
146 /** Creates an instance of the referee, using the static values to initialise
147 * it. This is the constructor used when a start referee is needed.
148 */
Referee()149 Referee::Referee()
150 {
151 assert(m_st_referee_mesh);
152 // First add a NULL mesh, then set the material to be read only
153 // (this appears to be the only way to get read only materials).
154 // This way we only need to adjust the materials in the original
155 // mesh. ATM it doesn't make any difference, but if we ever should
156 // decide to use more than one referee model at startup we only
157 // have to change the textures once, and all models will be in synch.
158 m_scene_node = irr_driver->addAnimatedMesh(NULL, "referee");
159 m_scene_node->setReadOnlyMaterials(true);
160 m_scene_node->setMesh(m_st_referee_mesh);
161 m_scene_node->grab();
162 m_scene_node->setRotation(m_st_start_rotation.toIrrVector());
163 m_scene_node->setScale(m_st_scale.toIrrVector());
164 m_scene_node->setFrameLoop(m_st_first_start_frame,
165 m_st_last_start_frame);
166 #ifndef SERVER_ONLY
167 if (CVS->isGLSL() && CVS->isDeferredEnabled())
168 {
169 m_light = irr_driver->addLight(core::vector3df(0.0f, 0.0f, 0.6f), 0.7f, 2.0f,
170 0.7f /* r */, 0.0 /* g */, 0.0f /* b */, false /* sun */, m_scene_node);
171 }
172 else
173 #endif
174 {
175 m_light = NULL;
176 }
177 } // Referee
178
179 // ----------------------------------------------------------------------------
180 /** Creates an instance of the referee, using the static values to initialise
181 * it. This is the constructor used when a rescue referee is needed.
182 * \param kart The kart which the referee should rescue.
183 */
Referee(const AbstractKart & kart)184 Referee::Referee(const AbstractKart &kart)
185 {
186 assert(m_st_referee_mesh);
187 // First add a NULL mesh, then set the material to be read only
188 // (this appears to be the only way to get read only materials).
189 // This way we only need to adjust the materials in the original
190 // mesh. ATM it doesn't make any difference, but if we ever should
191 // decide to use more than one referee model at startup we only
192 // have to change the textures once, and all models will be in synch.
193 m_scene_node = irr_driver->addAnimatedMesh(NULL, "referee");
194 m_scene_node->setReadOnlyMaterials(true);
195 m_scene_node->setMesh(m_st_referee_mesh);
196 m_scene_node->grab();
197 m_scene_node->setScale(m_st_scale.toIrrVector());
198 m_scene_node->setPosition(core::vector3df(0, kart.getKartHeight() + 0.4f, 0));
199
200 } // Referee
201
202 // ----------------------------------------------------------------------------
~Referee()203 Referee::~Referee()
204 {
205 if(m_scene_node->getParent())
206 irr_driver->removeNode(m_scene_node);
207 m_scene_node->drop();
208 } // ~Referee
209
210 // ----------------------------------------------------------------------------
211 /** Make sure that this referee is attached to the scene graph. This is used
212 * for the start referee, which is removed from scene graph once the ready-
213 * set-go phase is over (it is kept in case of a restart of the race).
214 */
attachToSceneNode()215 void Referee::attachToSceneNode()
216 {
217 if(!m_scene_node->getParent())
218 m_scene_node->setParent(irr_driver->getSceneManager()
219 ->getRootSceneNode());
220
221 if (m_light != NULL)
222 m_light->setVisible(true);
223 } // attachToSceneNode
224
225 // ----------------------------------------------------------------------------
226 /** Removes the referee's scene node from the scene graph, but still keeps
227 * the scene node in memory. This is used for the start referee, so that
228 * it is quickly available in case of a restart.
229 */
removeFromSceneGraph()230 void Referee::removeFromSceneGraph()
231 {
232 if(isAttached())
233 irr_driver->removeNode(m_scene_node);
234 if (m_light != NULL)
235 m_light->setVisible(false);
236 } // removeFromSceneGraph
237
238 // ----------------------------------------------------------------------------
239 /** Selects one of the states 'ready', 'set', or 'go' to be displayed by
240 * the referee.
241 * \param rsg 0=ready, 1=set, 2=go.
242 */
selectReadySetGo(int rsg)243 void Referee::selectReadySetGo(int rsg)
244 {
245 if (m_st_traffic_buffer < 0)
246 return;
247
248 SP::SPMeshNode* spmn = dynamic_cast<SP::SPMeshNode*>(m_scene_node);
249 if (spmn)
250 {
251 spmn->setTextureMatrix(m_st_traffic_buffer, {{ 0.0f, rsg * 0.333f }});
252 }
253 else
254 {
255 video::SMaterial &m = m_scene_node->getMaterial(m_st_traffic_buffer);
256 core::matrix4* matrix = &m.getTextureMatrix(0);
257 matrix->setTextureTranslate(0.0f, rsg*0.333f);
258 // disable lighting, we need to see the traffic light even if facing away
259 // from the sun
260 m.AmbientColor = video::SColor(255, 255, 255, 255);
261 m.DiffuseColor = video::SColor(255, 255, 255, 255);
262 m.EmissiveColor = video::SColor(255, 255, 255, 255);
263 m.SpecularColor = video::SColor(255, 255, 255, 255);
264 }
265
266 if (m_light != NULL)
267 {
268 if (rsg == 0)
269 {
270 ((LightNode*)m_light)->setColor(0.6f, 0.0f, 0.0f);
271 }
272 else if (rsg == 1)
273 {
274 ((LightNode*)m_light)->setColor(0.7f, 0.23f, 0.0f);
275 }
276 else if (rsg == 2)
277 {
278 ((LightNode*)m_light)->setColor(0.0f, 0.6f, 0.0f);
279 }
280 }
281 } // selectReadySetGo
282
283 // ----------------------------------------------------------------------------
284 /** Set the referee animation frame with created ticks of \ref RescueAnimation,
285 * so that it's synchronized with world ticks, and can be rewound easily.
286 */
setAnimationFrameWithCreatedTicks(int created_ticks)287 void Referee::setAnimationFrameWithCreatedTicks(int created_ticks)
288 {
289 float dur = stk_config->ticks2Time(
290 World::getWorld()->getTicksSinceStart() - created_ticks);
291 dur *= 25.0f;
292 float ref_dur = (float)(m_st_last_rescue_frame - m_st_first_rescue_frame);
293 float frame = std::fmod(dur, ref_dur);
294 frame += (float)m_st_first_rescue_frame;
295 m_scene_node->setCurrentFrame(frame);
296 } // setAnimationFrameWithCreatedTicks
297