1 // Description:
2 //   Particles are grouped into ParticleGroups.
3 //
4 // Copyright (C) 2008 Frank Becker
5 //
6 // This program is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free Software
8 // Foundation;  either version 2 of the License,  or (at your option) any  later
9 // version.
10 //
11 // This program is distributed in the hope that it will be useful,  but  WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
14 //
15 #include "ParticleGroup.hpp"
16 
17 #include <Trace.hpp>
18 #include <Particles.hpp>
19 #include <Constants.hpp>
20 #include <FindHash.hpp>
21 
22 using namespace std;
23 
24 hash_map< const string, ParticleType*, hash<const string>, std::equal_to<const string> >
25     ParticleGroup::_particleTypeMap;
26 
ParticleGroup(const string & groupName,int numParticles)27 ParticleGroup::ParticleGroup( const string &groupName, int numParticles):
28     _particles( 0),
29     _aliveCount( 0),
30     _groupName( groupName),
31     _numParticles( numParticles)
32 {
33     XTRACE();
34     _usedList.next = 0;
35 }
36 
~ParticleGroup()37 ParticleGroup::~ParticleGroup()
38 {
39     XTRACE();
40 
41     LOG_INFO << _groupName << " has " << _aliveCount << " particles still alive.\n";
42     ParticleInfo *p = _usedList.next;
43     while( p)
44     {
45 	p->tod = 0;
46 	p->particle->update( p);
47 	p = p->next;
48     }
49 
50     delete[] _particles;
51 }
52 
reset(void)53 void ParticleGroup::reset( void)
54 {
55     XTRACE();
56 
57     //trigger particles in use to die
58     ParticleInfo *p = _usedList.next;
59     while( p)
60     {
61 	p->tod = 0;
62 	p->particle->update( p);
63 	p = p->next;
64     }
65 
66     //reset free/used lists
67     _freeList.next = &_particles[0];
68     for( int i=0; i<_numParticles-1; i++)
69     {
70         _particles[i].next = &_particles[i+1];
71     }
72     _particles[_numParticles-1].next = 0;
73     _usedList.next = 0;
74     _aliveCount = 0;
75 }
76 
init(void)77 bool ParticleGroup::init( void)
78 {
79     XTRACE();
80 
81     //FIXME: do we really need to restrict size?
82     if( _numParticles > MAX_PARTICLES_PER_GROUP)
83     {
84         _numParticles = MAX_PARTICLES_PER_GROUP;
85     }
86 
87     _particles = new ParticleInfo[ _numParticles];
88     for( int i=0; i<_numParticles-1; i++)
89     {
90 	const vec3 zero(0,0,0);
91         _particles[i].position = zero;
92         _particles[i].velocity = zero;
93         _particles[i].color    = zero;
94         _particles[i].extra    = zero;
95 
96         _particles[i].prevPosition = zero;
97         _particles[i].prevVelocity = zero;
98         _particles[i].prevColor    = zero;
99         _particles[i].prevExtra    = zero;
100     }
101 
102     reset();
103 
104     Particles::Initialize();
105 
106     return true;
107 }
108 
newParticle(const string & name,const ParticleInfo & pi)109 ParticleInfo *ParticleGroup::newParticle(
110     const string & name, const ParticleInfo &pi)
111 {
112 //    XTRACE();
113     ParticleType *particleType = getParticleType( name);
114     if( !particleType)
115     {
116 	LOG_ERROR << "Unknown particleType [" << name << "]" << endl;
117 	return 0;
118     }
119     return newParticle( particleType, pi);
120 }
121 
newParticle(ParticleType * particleType,const ParticleInfo & pi)122 ParticleInfo *ParticleGroup::newParticle(
123     ParticleType *particleType, const ParticleInfo &pi)
124 {
125 //    XTRACE();
126     ParticleInfo *p = _freeList.next;
127     if( !p)
128     {
129 	LOG_ERROR << _groupName << " is out of particles!" << endl;
130 	return 0;
131     }
132 
133     _freeList.next = p->next;
134     p->next = _usedList.next;
135     _usedList.next = p;
136 
137     p->particle = particleType;
138     p->position = pi.position;
139     p->velocity = pi.velocity;
140     p->extra = pi.extra;
141     p->color = pi.color;
142     p->text = pi.text;
143     p->damage = pi.damage;
144     p->related = pi.related;
145 
146     //particle initializes particle info
147     particleType->init( p);
148 
149     _aliveCount++;
150 
151     return p;
152 }
153 
newParticle(const string & name,float x,float y,float z)154 ParticleInfo *ParticleGroup::newParticle(
155     const string & name, float x, float y, float z)
156 {
157 //    XTRACE();
158     ParticleType *particleType = getParticleType( name);
159     if( !particleType)
160     {
161 	LOG_ERROR << "Unknown particleType [" << name << "]" << endl;
162 	return 0;
163     }
164     return newParticle( particleType, x, y, z);
165 }
166 
newParticle(ParticleType * particleType,float x,float y,float z)167 ParticleInfo *ParticleGroup::newParticle(
168     ParticleType *particleType, float x, float y, float z)
169 {
170 //    XTRACE();
171     ParticleInfo *p = _freeList.next;
172     if( !p)
173     {
174 	LOG_ERROR << _groupName << " is out of particles!" << endl;
175 	return 0;
176     }
177 
178     _freeList.next = p->next;
179     p->next = _usedList.next;
180     _usedList.next = p;
181 
182     p->particle = particleType;
183     p->position.x = x;
184     p->position.y = y;
185     p->position.z = z;
186 
187     //particle initializes particle info
188     particleType->init( p);
189 
190     _aliveCount++;
191 
192     return p;
193 }
194 
195 static inline
hasRadiusCollision(const float & minDist,const vec3 & pos1,const vec3 & pos2)196 bool hasRadiusCollision( const float &minDist, const vec3 &pos1, const vec3 &pos2)
197 {
198     float d2 = minDist;
199     d2 *= d2;
200     float dx = (pos2.x-pos1.x);
201     float dy = (pos2.y-pos1.y);
202     float D2 = dx*dx + dy*dy;
203     if( D2 < d2)
204     {
205 	return true;
206     }
207     return false;
208 }
209 
detectCollisions(ParticleGroup * pg)210 void ParticleGroup::detectCollisions( ParticleGroup *pg)
211 {
212 //    XTRACE();
213     ParticleInfo *p1 = _usedList.next;
214     while( p1)
215     {
216 	ParticleInfo *p2 = pg->_usedList.next;
217 	while( p2)
218 	{
219 	    if( (p1->radius == 0.0f) || (p2->radius == 0.0f))
220 	    {
221 		//Bosses are made up of multiple radii
222 		for( int rc1=0; rc1 < p1->particle->getRadiiCount(); rc1++)
223 		{
224 		    float r1 = p1->particle->getRadius(rc1);
225 		    if( r1 == 0.0f) r1 = p1->radius;
226 		    vec3 offset1 = p1->particle->getOffset(rc1);
227 		    for( int rc2=0; rc2 < p2->particle->getRadiiCount(); rc2++)
228 		    {
229 			float r2 = p2->particle->getRadius(rc2);
230 			if( r2 == 0.0f) r2 = p2->radius;
231 			vec3 offset2 = p2->particle->getOffset(rc2);
232 			vec3 pos1 = p1->position+offset1;
233 			vec3 pos2 = p2->position+offset2;
234 //			LOG_INFO << "pos1 = " << pos1.x << " " << pos1.y << "\n";
235 //			LOG_INFO << "pos2 = " << pos2.x << " " << pos2.y << "\n";
236 			if( hasRadiusCollision( r1+r2, pos1, pos2))
237 			{
238 			    p1->particle->hit( p1, p2, rc1);
239 			    p2->particle->hit( p2, p1, rc2);
240 			}
241 		    }
242 		}
243 	    }
244 	    else
245 	    {
246 		if( hasRadiusCollision(
247 			p1->radius+p2->radius,
248 			p1->position, p2->position))
249 		{
250 		    p1->particle->hit( p1, p2);
251 		    p2->particle->hit( p2, p1);
252 		}
253 	    }
254 
255 	    p2 = p2->next;
256         }
257 
258         p1 = p1->next;
259     }
260 }
261 
addParticleType(ParticleType * particleType)262 void ParticleGroup::addParticleType( ParticleType *particleType)
263 {
264     XTRACE();
265     if( particleType)
266     {
267         LOG_INFO << "New Particle type: ["
268 	         << particleType->name() << "]" << endl;
269 
270         _particleTypeMap[ particleType->name()] = particleType;
271     }
272 }
273 
getParticleType(const string particleTypeName)274 ParticleType * ParticleGroup::getParticleType( const string particleTypeName)
275 {
276 //    XTRACE();
277     ParticleType * particleType = findHash<const string>( particleTypeName, _particleTypeMap);
278     if( !particleType)
279     {
280         LOG_ERROR << "ParticleGroup never heard of a " << particleTypeName
281                   << " before!" << endl;
282 
283         //trying first in list
284         particleType = _particleTypeMap.begin()->second;
285     }
286     return particleType;
287 }
288