1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <sprites/MissileActionRenderer.h>
22 #include <sprites/ExplosionTextures.h>
23 #include <GLEXT/GLCameraFrustum.h>
24 #include <weapons/Accessory.h>
25 #include <actions/ShotProjectile.h>
26 #include <landscape/Landscape.h>
27 #include <landscapemap/LandscapeMaps.h>
28 #include <landscape/ShadowMap.h>
29 #include <graph/OptionsDisplay.h>
30 #include <common/Defines.h>
31 #include <target/TargetContainer.h>
32 #include <client/ScorchedClient.h>
33 #include <engine/ScorchedContext.h>
34 #include <graph/ParticleEngine.h>
35 #include <sound/Sound.h>
36 #include <tank/Tank.h>
37 
MissileActionRenderer(int flareType,float scale,float spinSpeed,const Vector & rotationAxis)38 MissileActionRenderer::MissileActionRenderer(int flareType, float scale, float spinSpeed, const Vector &rotationAxis) :
39 	flareType_(flareType), counter_(0.05f, 0.05f),
40 	mesh_(0), scale_(scale), rotation_(180.0f),
41 	flameemitter_(0), smokeemitter_(0), sound_(0),
42 	rotationAxis_(rotationAxis),
43 	spinSpeed_(spinSpeed)
44 {
45 	frame_ = (float) rand();
46 }
47 
~MissileActionRenderer()48 MissileActionRenderer::~MissileActionRenderer()
49 {
50 	delete flameemitter_;
51 	delete smokeemitter_;
52 	delete sound_;
53 }
54 
simulate(Action * action,float timepassed,bool & remove)55 void MissileActionRenderer::simulate(Action *action, float timepassed, bool &remove)
56 {
57 	ShotProjectile *shot = (ShotProjectile *) action;
58 	if (!flameemitter_)
59 	{
60 		flameTextureSet_ = ExplosionTextures::instance()->getTextureSetByName(
61 			shot->getWeapon()->getFlameTexture());
62 		flameemitter_ = new ParticleEmitter;
63 		flameemitter_->setAttributes(
64 			shot->getWeapon()->getFlameLife() / 2.0f, shot->getWeapon()->getFlameLife(), // Life
65 			0.5f, 1.0f, // Mass
66 			0.01f, 0.02f, // Friction
67 			Vector(-0.05f, -0.1f, 0.3f), Vector(0.05f, 0.1f, 0.9f), // Velocity
68 			shot->getWeapon()->getFlameStartColor1(), 0.9f, // StartColor1
69 			shot->getWeapon()->getFlameStartColor2(), 1.0f, // StartColor2
70 			shot->getWeapon()->getFlameEndColor1(), 0.0f, // EndColor1
71 			shot->getWeapon()->getFlameEndColor2(), 0.1f, // EndColor2
72 			shot->getWeapon()->getFlameStartSize() / 2.0f, shot->getWeapon()->getFlameStartSize() / 2.0f, shot->getWeapon()->getFlameStartSize(), shot->getWeapon()->getFlameStartSize(), // Start Size
73 			shot->getWeapon()->getFlameEndSize() / 2.0f, shot->getWeapon()->getFlameEndSize() / 2.0f, shot->getWeapon()->getFlameEndSize(), shot->getWeapon()->getFlameEndSize(), // EndSize
74 			Vector(0.0f, 0.0f, 10.0f), // Gravity
75 			true,
76 			true);
77 	}
78 	if (!smokeemitter_)
79 	{
80 		smokeTextureSet_ = ExplosionTextures::instance()->getTextureSetByName(
81 			shot->getWeapon()->getSmokeTexture());
82 		smokeemitter_ = new ParticleEmitter;
83 		smokeemitter_->setAttributes(
84 			shot->getWeapon()->getSmokeLife() / 2.0f, shot->getWeapon()->getSmokeLife(), // Life
85 			0.2f, 0.5f, // Mass
86 			0.01f, 0.02f, // Friction
87 			Vector(-0.05f, -0.1f, 0.3f), Vector(0.05f, 0.1f, 0.9f), // Velocity
88 			Vector(0.7f, 0.7f, 0.7f), 0.3f, // StartColor1
89 			Vector(0.7f, 0.7f, 0.7f), 0.3f, // StartColor2
90 			Vector(0.7f, 0.7f, 0.7f), 0.0f, // EndColor1
91 			Vector(0.8f, 0.8f, 0.8f), 0.1f, // EndColor2
92 			shot->getWeapon()->getSmokeStartSize() / 2.0f, shot->getWeapon()->getSmokeStartSize() / 2.0f, shot->getWeapon()->getSmokeStartSize(), shot->getWeapon()->getSmokeStartSize(), // Start Size
93 			shot->getWeapon()->getSmokeEndSize() / 2.0f, shot->getWeapon()->getSmokeEndSize() / 2.0f, shot->getWeapon()->getSmokeEndSize(), shot->getWeapon()->getSmokeEndSize(), // EndSize
94 			Vector(0.0f, 0.0f, 100.0f), // Gravity
95 			false,
96 			true);
97 	}
98 	if (!sound_)
99 	{
100 		const char *engineSound = shot->getWeapon()->getEngineSound();
101 		if (0 != strcmp("none", engineSound))
102 		{
103 			SoundBuffer *rocket = Sound::instance()->fetchOrCreateBuffer(
104 				S3D::getModFile(engineSound));
105 			sound_ = new VirtualSoundSource(VirtualSoundPriority::eMissile, true, false);
106 			sound_->setPosition(shot->getCurrentPosition().asVector());
107 			sound_->setGain(0.25f);
108 			sound_->play(rocket);
109 		}
110 	}
111 	if (sound_)
112 	{
113 		sound_->setPosition(shot->getCurrentPosition().asVector());
114 		sound_->setVelocity(shot->getCurrentVelocity().asVector());
115 	}
116 
117 	Vector &actualPos = shot->getCurrentPosition().asVector();
118 	Vector actualPos1;
119 	actualPos1[0] = actualPos[0] - 0.25f;
120 	actualPos1[1] = actualPos[1] - 0.25f;
121 	actualPos1[2] = actualPos[2] - 0.25f;
122 	Vector actualPos2;
123 	actualPos2[0] = actualPos[0] + 0.25f;
124 	actualPos2[1] = actualPos[1] + 0.25f;
125 	actualPos2[2] = actualPos[2] + 0.25f;
126 
127 	// Rotate the shot
128 	frame_ += timepassed * 20.0f;
129 	rotation_ += shot->getCurrentVelocity().Magnitude().asFloat() * spinSpeed_;
130 
131 	// Add flame trail
132 	if (shot->getWeapon()->getCreateFlame())
133 	{
134 		flameemitter_->emitLinear(2, actualPos1, actualPos2,
135 			ScorchedClient::instance()->getParticleEngine(),
136 			ParticleRendererQuads::getInstance(),
137 			flameTextureSet_,
138 			shot->getWeapon()->getAnimateFlameTexture());
139 	}
140 
141 	// Add the smoke trail
142 	if (shot->getWeapon()->getCreateSmoke())
143 	{
144 		if (counter_.nextDraw(timepassed))
145 		{
146 			Vector vel1 = shot->getCurrentVelocity().asVector();
147 			Vector vel2;
148 			vel1 *= -0.4f;
149 			vel2 = vel1 * 0.7f;
150 
151 			actualPos1 -= shot->getCurrentVelocity().asVector() * 0.2f;
152 			actualPos2 -= shot->getCurrentVelocity().asVector() * 0.2f;
153 
154 			smokeemitter_->setVelocity(vel1, vel2);
155 			smokeemitter_->emitLinear(3, actualPos1, actualPos2,
156 				ScorchedClient::instance()->getParticleEngine(),
157 				ParticleRendererQuads::getInstance(),
158 				smokeTextureSet_,
159 				shot->getWeapon()->getAnimateSmokeTexture());
160 		}
161 	}
162 }
163 
draw(Action * action)164 void MissileActionRenderer::draw(Action *action)
165 {
166 	ShotProjectile *shot = (ShotProjectile *) action;
167 	Vector &actualPos = shot->getCurrentPosition().asVector();
168 	Vector &actualdir = shot->getCurrentVelocity().asVector();
169 
170 	if (shot->getWeapon()->getShowShotPath())
171 	{
172 		Tank *current =
173 			action->getScorchedContext()->getTargetContainer().
174 			getTankById(shot->getPlayerId());
175 		if (current)
176 		{
177 			glColor3fv(current->getColor());
178 			RenderTracer::instance()->drawSmokeTracer(
179 				shot->getPositions());
180 		}
181 	}
182 
183 	// Check we can see the missile
184 	if (!GLCameraFrustum::instance()->sphereInFrustum(actualPos, 1.0f))
185 	{
186 		return;
187 	}
188 
189 	// Do we have a loaded mesh
190 	if (!mesh_)
191 	{
192 		Tank *currentPlayer = action->
193 			getScorchedContext()->getTargetContainer().getTankById(
194 			shot->getPlayerId());
195 		mesh_ = Accessory::getWeaponMesh(
196 			shot->getWeapon()->getModelID(), currentPlayer);
197 	}
198 
199 	// Draw the missile
200 	mesh_->setScale(scale_);
201 	mesh_->draw(actualPos, actualdir, flareType_, rotation_, rotationAxis_, frame_);
202 
203 	// Draw the missile shadow
204 	float aboveGround =
205 		actualPos[2] - ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().
206 		getHeight((int) actualPos[0], (int) actualPos[1]).asFloat();
207 	Landscape::instance()->getShadowMap().
208 		addCircle(actualPos[0], actualPos[1], aboveGround / 10.0f);
209 }
210