Oolite 1.91.0.7644-241112-7f5034b
Loading...
Searching...
No Matches
OOParticleSystem.m
Go to the documentation of this file.
1/*
2
3OOParticleSystem.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25#import "OOParticleSystem.h"
26
27#import "Universe.h"
28#import "OOTexture.h"
29#import "PlayerEntity.h"
31#import "OOMacroOpenGL.h"
32#import "MyOpenGLView.h"
33
34
35// Testing toy: cause particle systems to stop after half a second.
36#define FREEZE_PARTICLES 0
37
38
39@implementation OOParticleSystem
40
41- (id) init
42{
43 [self release];
44 return nil;
45}
46
47
48/* Initialize shared aspects of the fragburst entities.
49 Also stashes generated particle speeds in _particleSize[] array.
50*/
51- (id) initWithPosition:(HPVector)pos
52 velocity:(Vector)vel
53 count:(unsigned)count
54 minSpeed:(float)minSpeed
55 maxSpeed:(float)maxSpeed
56 duration:(OOTimeDelta)duration
57 baseColor:(GLfloat[4])baseColor
58{
59 NSParameterAssert(count <= kFragmentBurstMaxParticles);
60
61 if ((self = [super init]))
62 {
63 _count = count;
64 [self setPosition:pos];
65
66 velocity = vel;
67 _duration = duration;
69
70 for (unsigned i = 0; i < count; i++)
71 {
72 GLfloat speed = minSpeed + 0.5f * (randf()+randf()) * (maxSpeed - minSpeed); // speed tends toward middle of range
73 _particleVelocity[i] = vector_multiply_scalar(OORandomUnitVector(), speed);
74
75 Vector color = make_vector(baseColor[0] * 0.1f * (9.5f + randf()), baseColor[1] * 0.1f * (9.5f + randf()), baseColor[2] * 0.1f * (9.5f + randf()));
76 color = vector_normal(color);
77 _particleColor[i][0] = color.x;
78 _particleColor[i][1] = color.y;
79 _particleColor[i][2] = color.z;
80 _particleColor[i][3] = baseColor[3];
81
83 }
84
85 [self setStatus:STATUS_EFFECT];
86 scanClass = CLASS_NO_DRAW;
87 }
88
89 return self;
90}
91
92
93- (NSString *) descriptionComponents
94{
95 return [NSString stringWithFormat:@"ttl: %.3fs", _duration - _timePassed];
96}
97
98
99- (BOOL) canCollide
100{
101 return NO;
102}
103
104
105- (BOOL) checkCloseCollisionWith:(Entity *)other
106{
107 if (other == [self owner]) return NO;
108 return ![other isEffect];
109}
110
111
112- (void) update:(OOTimeDelta) delta_t
113{
114 [super update:delta_t];
115
116 _timePassed += delta_t;
117 collision_radius += delta_t * _maxSpeed;
118
119 unsigned i, count = _count;
120 Vector *particlePosition = _particlePosition;
121 Vector *particleVelocity = _particleVelocity;
122
123 for (i = 0; i < count; i++)
124 {
125 particlePosition[i] = vector_add(particlePosition[i], vector_multiply_scalar(particleVelocity[i], delta_t));
126 }
127
128 // disappear eventually.
129 if (_timePassed > _duration) [UNIVERSE removeEntity:self];
130}
131
132
133#define DrawQuadForView(x, y, z, sz) \
134do { \
135 glTexCoord2f(0.0, 1.0); glVertex3f(x-sz, y-sz, z); \
136 glTexCoord2f(1.0, 1.0); glVertex3f(x+sz, y-sz, z); \
137 glTexCoord2f(1.0, 0.0); glVertex3f(x+sz, y+sz, z); \
138 glTexCoord2f(0.0, 0.0); glVertex3f(x-sz, y+sz, z); \
139} while (0)
140
141
142- (void) drawImmediate:(bool)immediate translucent:(bool)translucent
143{
144 if (!translucent || [UNIVERSE breakPatternHide]) return;
145
148
149 OOGL(glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT));
150
151 OOGL(glEnable(GL_TEXTURE_2D));
152 OOGL(glEnable(GL_BLEND));
153 OOGL(glBlendFunc(GL_SRC_ALPHA, GL_ONE));
154 [[self texture] apply];
155
156 HPVector viewPosition = [PLAYER viewpointPosition];
157 HPVector selfPosition = [self position];
158
159 unsigned i, count = _count;
160 Vector *particlePosition = _particlePosition;
161 GLfloat (*particleColor)[4] = _particleColor;
162 GLfloat *particleSize = _particleSize;
163
164 if ([UNIVERSE reducedDetail])
165 {
166 // Quick rendering - particle cloud is effectively a 2D billboard.
168 OOGLMultModelView(OOMatrixForBillboard(selfPosition, viewPosition));
169
170 OOGLBEGIN(GL_QUADS);
171 for (i = 0; i < count; i++)
172 {
173 glColor4fv(particleColor[i]);
174 DrawQuadForView(particlePosition[i].x, particlePosition[i].y, particlePosition[i].z, particleSize[i]);
175 }
176 OOGLEND();
177
179 }
180 else
181 {
182 float distanceThreshold = collision_radius * 2.0f; // Distance between player and middle of effect where we start to transition to "non-fast rendering."
183 float thresholdSq = distanceThreshold * distanceThreshold;
184 float distanceSq = cam_zero_distance;
185
186 if (distanceSq > thresholdSq)
187 {
188 /* Semi-quick rendering - particle positions are volumetric, but
189 orientation is shared. This can cause noticeable distortion
190 if the player is close to the centre of the cloud.
191 */
192 OOMatrix bbMatrix = OOMatrixForBillboard(selfPosition, viewPosition);
193
194 for (i = 0; i < count; i++)
195 {
197 OOGLTranslateModelView(particlePosition[i]);
198 OOGLMultModelView(bbMatrix);
199
200 glColor4fv(particleColor[i]);
201 OOGLBEGIN(GL_QUADS);
202 DrawQuadForView(0, 0, 0, particleSize[i]);
203 OOGLEND();
204
206 }
207 }
208 else
209 {
210 /* Non-fast rendering - each particle is billboarded individually.
211 The "individuality" factor interpolates between this behavior
212 and "semi-quick" to avoid jumping at the boundary.
213 */
214 float individuality = 3.0f * (1.0f - distanceSq / thresholdSq);
215 individuality = OOClamp_0_1_f(individuality);
216
217 for (i = 0; i < count; i++)
218 {
220 OOGLTranslateModelView(particlePosition[i]);
221 OOGLMultModelView(OOMatrixForBillboard(HPvector_add(selfPosition, vectorToHPVector(vector_multiply_scalar(particlePosition[i], individuality))), viewPosition));
222
223 glColor4fv(particleColor[i]);
224 OOGLBEGIN(GL_QUADS);
225 DrawQuadForView(0, 0, 0, particleSize[i]);
226 OOGLEND();
227
229 }
230 }
231
232 }
233
234 OOGL(glPopAttrib());
235
237 OOCheckOpenGLErrors(@"OOParticleSystem after drawing %@", self);
238}
239
240
241- (BOOL) isEffect
242{
243 return YES;
244}
245
246- (OOTexture *) texture
247{
249}
250
251#ifndef NDEBUG
252- (NSSet *) allTextures
253{
254 return [NSSet setWithObject:[OOLightParticleEntity defaultParticleTexture]];
255}
256#endif
257
258@end
259
260
262
263- (id) initFragmentBurstFrom:(HPVector)fragPosition velocity:(Vector)fragVelocity size:(GLfloat)size
264{
265 enum
266 {
267 kMinSpeed = 100, kMaxSpeed = 400
268 };
269
270 unsigned count = 0.4f * size;
271 count = MIN(count | 12, (unsigned)kFragmentBurstMaxParticles);
272
273 // Select base colour
274 // yellow/orange (0.12) through yellow (0.1667) to yellow/slightly green (0.20)
275 OOColor *hsvColor = [OOColor colorWithHue:0.12f + 0.08f * randf() saturation:1.0f brightness:1.0f alpha:1.0f];
276 GLfloat baseColor[4];
277 [hsvColor getRed:&baseColor[0] green:&baseColor[1] blue:&baseColor[2] alpha:&baseColor[3]];
278
279 if ((self = [super initWithPosition:fragPosition velocity:fragVelocity count:count minSpeed:kMinSpeed maxSpeed:kMaxSpeed duration:1.5 baseColor:baseColor]))
280 {
281 for (unsigned i = 0; i < count; i++)
282 {
283 // Note: initWithPosition:... stashes speeds in _particleSize[].
284 _particleSize[i] = 32.0f * kMinSpeed / _particleSize[i];
285 }
286 }
287
288 return self;
289}
290
291
292+ (id) fragmentBurstFromEntity:(Entity *)entity
293{
294 return [[[self alloc] initFragmentBurstFrom:[entity position] velocity:[entity velocity] size:[entity collisionRadius]] autorelease];
295}
296
297
298- (void) update:(OOTimeDelta) delta_t
299{
300#if FREEZE_PARTICLES
301 if (_timePassed + delta_t > 0.5) delta_t = 0.5 - _timePassed;
302#endif
303
304 [super update:delta_t];
305
306 unsigned i, count = _count;
307 GLfloat (*particleColor)[4] = _particleColor;
308 GLfloat timePassed = _timePassed;
309
310 for (i = 0; i < count; i++)
311 {
312 GLfloat du = 0.5f + (1.0f/32.0f) * (32 - i);
313 particleColor[i][3] = OOClamp_0_1_f(1.0f - timePassed / du);
314 }
315}
316
317@end
318
319
321
322- (id) initFragmentBurstFrom:(HPVector)fragPosition velocity:(Vector)fragVelocity size:(GLfloat)size
323{
324 float minSpeed = 1.0f + size * 0.5f;
325 float maxSpeed = minSpeed * 4.0f;
326
327 unsigned count = 0.2f * size;
329
330 GLfloat baseColor[4] = { 1.0f, 1.0f, 0.5f, 1.0f };
331
332 size *= 2.0f; // Account for margins in particle texture.
333 if ((self = [super initWithPosition:fragPosition velocity:fragVelocity count:count minSpeed:minSpeed maxSpeed:maxSpeed duration:1.0 baseColor:baseColor]))
334 {
335 _baseSize = size;
336
337 for (unsigned i = 0; i < count; i++)
338 {
339 _particleSize[i] = size;
340 }
341 }
342
343 return self;
344}
345
346
347+ (id) fragmentBurstFromEntity:(Entity *)entity
348{
349 return [[[self alloc] initFragmentBurstFrom:[entity position] velocity:vector_multiply_scalar([entity velocity], 0.85) size:[entity collisionRadius]] autorelease];
350}
351
352
353- (void) update:(double)delta_t
354{
355#if FREEZE_PARTICLES
356 if (_timePassed + delta_t > 0.5) delta_t = 0.5 - _timePassed;
357#endif
358
359 [super update:delta_t];
360
361 unsigned i, count = _count;
362 GLfloat (*particleColor)[4] = _particleColor;
363 GLfloat *particleSize = _particleSize;
364 GLfloat timePassed = _timePassed;
365 GLfloat duration = _duration;
366
367 GLfloat size = (1.0f + timePassed) * _baseSize;
368 GLfloat di = 1.0f / (count - 1);
369
370 for (i = 0; i < count; i++)
371 {
372 GLfloat du = duration * (0.5f + di * i);
373 particleColor[i][3] = OOClamp_0_1_f(1.0f - timePassed / du);
374
375 particleSize[i] = size;
376 }
377}
378
379@end
#define OO_ENTER_OPENGL()
#define MIN(A, B)
Definition OOMaths.h:111
OOMatrix OOMatrixForBillboard(HPVector bbPos, HPVector eyePos)
Definition OOMatrix.m:191
void OOGLPushModelView(void)
void OOGLTranslateModelView(Vector vector)
void OOGLMultModelView(OOMatrix matrix)
OOMatrix OOGLPopModelView(void)
#define OOGLBEGIN
Definition OOOpenGL.h:253
@ OPENGL_STATE_ADDITIVE_BLENDING
Definition OOOpenGL.h:125
#define OOVerifyOpenGLState()
Definition OOOpenGL.h:136
BOOL OOCheckOpenGLErrors(NSString *format,...)
Definition OOOpenGL.m:39
#define OOSetOpenGLState(STATE)
Definition OOOpenGL.h:135
#define OOGL(statement)
Definition OOOpenGL.h:251
#define OOGLEND
Definition OOOpenGL.h:254
@ kFragmentBurstMaxParticles
@ kBigFragmentBurstMaxParticles
float maxSpeed
GLfloat baseColor[4]
#define DrawQuadForView(x, y, z, sz)
unsigned count
OOColor * hsvColor
return nil
float y
float x
double OOTimeDelta
Definition OOTypes.h:224
Vector OORandomUnitVector(void)
Definition OOVector.m:83
#define UNIVERSE
Definition Universe.h:833
GLfloat collision_radius
Definition Entity.h:111
GLfloat collisionRadius()
Definition Entity.m:905
OOScanClass scanClass
Definition Entity.h:106
GLfloat cam_zero_distance
Definition Entity.h:109
BOOL isEffect()
Definition Entity.m:196
HPVector position
Definition Entity.h:112
Vector velocity
Definition Entity.h:140
double speed()
Definition Entity.m:769
id owner()
Definition Entity.m:583
void getRed:green:blue:alpha:(float *red,[green] float *green,[blue] float *blue,[alpha] float *alpha)
Definition OOColor.m:368
OOColor * colorWithHue:saturation:brightness:alpha:(float hue,[saturation] float saturation,[brightness] float brightness,[alpha] float alpha)
Definition OOColor.m:87
OOTimeDelta _timePassed
NSString * descriptionComponents()
Vector _particleVelocity[kFragmentBurstMaxParticles]
GLfloat _particleSize[kFragmentBurstMaxParticles]
GLfloat _particleColor[kFragmentBurstMaxParticles][4]
OOTimeDelta _duration
Vector _particlePosition[kFragmentBurstMaxParticles]
voidpf void uLong size
Definition ioapi.h:134
float randf(void)