Line data Source code
1 0 : /*
2 :
3 : OOExhaustPlumeEntity.m
4 :
5 :
6 : Oolite
7 : Copyright (C) 2004-2013 Giles C Williams and contributors
8 :
9 : This program is free software; you can redistribute it and/or
10 : modify it under the terms of the GNU General Public License
11 : as published by the Free Software Foundation; either version 2
12 : of the License, or (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program; if not, write to the Free Software
21 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 : MA 02110-1301, USA.
23 :
24 : */
25 :
26 :
27 : #import "OOExhaustPlumeEntity.h"
28 : #import "OOCollectionExtractors.h"
29 : #import "ShipEntity.h"
30 : #import "Universe.h"
31 : #import "OOMacroOpenGL.h"
32 : #import "PlayerEntity.h"
33 : #import "MyOpenGLView.h"
34 :
35 : #import "OOTexture.h"
36 : #import "OOGraphicsResetManager.h"
37 :
38 :
39 0 : #define kOverallAlpha 1.0f
40 0 : #define kTimeStep 0.05f
41 0 : #define kFadeLevel1 0.4f
42 0 : #define kFadeLevel2 0.2f
43 0 : #define kFadeLevel3 0.02f
44 0 : #define kScaleLevel1 1.0f
45 0 : #define kScaleLevel2 0.8f
46 0 : #define kScaleLevel3 0.6f
47 :
48 0 : static OOTexture *sPlumeTexture = nil;
49 :
50 :
51 : @interface OOExhaustPlumeEntity (Private)
52 :
53 0 : - (void) saveToLastFrame;
54 0 : - (Frame) frameAtTime:(double) t_frame fromFrame:(Frame) frame_zero; // t_frame is relative to now ie. -0.5 = half a second ago.
55 :
56 : @end
57 :
58 :
59 : @implementation OOExhaustPlumeEntity
60 :
61 : + (id) exhaustForShip:(ShipEntity *)ship withDefinition:(NSArray *)definition andScale:(float)scale
62 : {
63 : return [[[self alloc] initForShip:ship withDefinition:definition andScale:(float)scale] autorelease];
64 : }
65 :
66 :
67 : - (id) initForShip:(ShipEntity *)ship withDefinition:(NSArray *)definition andScale:(float)scaleFactor
68 : {
69 : if ([definition count] == 0)
70 : {
71 : [self release];
72 : return nil;
73 : }
74 :
75 : if ((self = [super init]))
76 : {
77 : [self setOwner:ship];
78 : HPVector pos = { [definition oo_floatAtIndex:0]*scaleFactor, [definition oo_floatAtIndex:1]*scaleFactor, [definition oo_floatAtIndex:2]*scaleFactor };
79 : [self setPosition:pos];
80 : // scale.z is special and *not* multiplied by scaleFactor
81 : Vector scale = { [definition oo_floatAtIndex:3]*scaleFactor, [definition oo_floatAtIndex:4]*scaleFactor, [definition oo_floatAtIndex:5] };
82 : [self setScale:scale];
83 : }
84 :
85 : return self;
86 : }
87 :
88 :
89 : - (Vector) scale
90 : {
91 : return _exhaustScale;
92 : }
93 :
94 :
95 : - (void) setScale:(Vector)scale
96 : {
97 : _exhaustScale = scale;
98 : if (scale.z < 0.5 || scale.z > 2.0)
99 : {
100 : _exhaustScale.z = 1.0;
101 : }
102 : }
103 :
104 :
105 0 : - (BOOL)isExhaust
106 : {
107 : return YES;
108 : }
109 :
110 :
111 0 : - (double)findCollisionRadius
112 : {
113 : return collision_radius;
114 : }
115 :
116 :
117 0 : - (void) update:(OOTimeDelta) delta_t
118 : {
119 : // Profiling: this function and subfunctions are expensive - CIM
120 :
121 : // don't draw if there's no ship, or if we're just jumping out of witchspace/docked at a station!
122 : ShipEntity *ship = [self owner];
123 : // also don't draw if the ship isn't visible
124 : if (EXPECT_NOT(ship == nil || ![ship isVisible] || ([ship isPlayer] && [ship suppressFlightNotifications]))) return;
125 :
126 : OOTimeAbsolute now = [UNIVERSE getTime];
127 : if ([UNIVERSE getTime] > _trackTime + kTimeStep)
128 : {
129 : [self saveToLastFrame];
130 : _trackTime = now;
131 : }
132 :
133 : //GLfloat ex_emissive[4] = {0.7f, 0.9, 1.0f, 0.9f * kOverallAlpha}; // pale blue - old definition
134 : collision_radius = 0;
135 : GLfloat length;
136 : HPVector vertex;
137 : GLfloat ex_emissive[4];
138 : [[ship exhaustEmissiveColor] getRed:&ex_emissive[0] green:&ex_emissive[1] blue:&ex_emissive[2] alpha:&ex_emissive[3]];
139 : const GLfloat s1[8] = { 0.0, M_SQRT1_2, 1.0, M_SQRT1_2, 0.0, -M_SQRT1_2, -1.0, -M_SQRT1_2};
140 : const GLfloat c1[8] = { 1.0, M_SQRT1_2, 0.0, -M_SQRT1_2, -1.0, -M_SQRT1_2, 0.0, M_SQRT1_2};
141 :
142 : Quaternion shipQrotation = [ship normalOrientation];
143 :
144 : Frame zero =
145 : {
146 : .timeframe = [UNIVERSE getTime],
147 : .orientation = shipQrotation,
148 : .k = [ship forwardVector]
149 : };
150 : int dam = [ship damage];
151 :
152 : GLfloat speed = [ship speedFactor];
153 : // don't draw if not moving.
154 : if (EXPECT_NOT(speed <= 0.001f)) return;
155 :
156 : GLfloat hyper_fade = 8.0f / (8.0f + speed * speed * speed);
157 :
158 : GLfloat flare_factor = fmaxf(speed,1.0) * ex_emissive[3] * hyper_fade;
159 : GLfloat red_factor = speed * ex_emissive[0] * (ranrot_rand() % 11) * 0.1f; // random fluctuations
160 : GLfloat green_factor = speed * ex_emissive[1] * hyper_fade;
161 :
162 : if (speed > 1.0f) // afterburner!
163 : {
164 : red_factor = 1.5;
165 : }
166 :
167 : if ((int)(ranrot_rand() % 50) < dam - 50) // flicker the damaged engines
168 : red_factor = 0.0;
169 : if ((int)(ranrot_rand() % 40) < dam - 60)
170 : green_factor = 0.0;
171 : if ((int)(ranrot_rand() % 25) < dam - 75)
172 : flare_factor = 0.0;
173 :
174 : HPVector currentPos = ship->position;
175 : Vector vfwd = [ship forwardVector];
176 : GLfloat spd = 0.5f * [ship flightSpeed];
177 : vfwd = vector_multiply_scalar(vfwd, spd);
178 : Vector master_i = [ship rightVector];
179 : Vector vi,vj,vk;
180 : vi = master_i;
181 : vj = [ship upVector];
182 : vk = [ship forwardVector];
183 : zero.position = make_HPvector(currentPos.x + vi.x * position.x + vj.x * position.y + vk.x * position.z,
184 : currentPos.y + vi.y * position.x + vj.y * position.y + vk.y * position.z,
185 : currentPos.z + vi.z * position.x + vj.z * position.y + vk.z * position.z);
186 :
187 : GLfloat speedScale = fminf(1.0f,speed * 5.0f);
188 :
189 : GLfloat exhaust_factor = _exhaustScale.z;
190 : GLfloat i01 = -0.00f * hyper_fade;
191 : GLfloat i03 = -0.12f * exhaust_factor;
192 : GLfloat i06 = -0.25f * exhaust_factor;
193 : GLfloat i08 = -0.32f * exhaust_factor;
194 : GLfloat i10 = -0.40f * exhaust_factor;
195 : GLfloat q01 = i01/i10; // factor for trail
196 : GLfloat q03 = i03/i10;
197 : GLfloat q06 = i06/i10;
198 : GLfloat q08 = i08/i10;
199 : GLfloat r01 = 1.0f - q01; // factor for jet
200 : GLfloat r03 = 1.0f - q03;
201 : GLfloat r06 = 1.0f - q06;
202 : GLfloat r08 = 1.0f - q08;
203 : Frame f01 = [self frameAtTime: i01 fromFrame: zero];
204 : Vector b01 = make_vector(r01 * i01 * vfwd.x, r01 * i01 * vfwd.y, r01 * i01 * vfwd.z);
205 : Frame f03 = [self frameAtTime: i03 fromFrame: zero];
206 : Vector b03 = make_vector(r03 * i03 * vfwd.x, r03 * i03 * vfwd.y, r03 * i03 * vfwd.z);
207 : Frame f06 = [self frameAtTime: i06 fromFrame: zero];
208 : Vector b06 = make_vector(r06 * i06 * vfwd.x, r06 * i06 * vfwd.y, r06 * i06 * vfwd.z);
209 : Frame f08 = [self frameAtTime: i08 fromFrame: zero];
210 : Vector b08 = make_vector(r08 * i08 * vfwd.x, r08 * i08 * vfwd.y, r08 * i08 * vfwd.z);
211 : Frame f10 = [self frameAtTime: i10 fromFrame: zero];
212 :
213 : int ci = 0;
214 : int iv = 0;
215 : int i;
216 : float r1;
217 :
218 : // f01.position = vector_subtract(zero.position, vk); // 1m out from zero
219 : f01.position = zero.position;
220 :
221 : ex_emissive[3] = flare_factor * kOverallAlpha; // fade alpha towards rear of exhaust
222 : ex_emissive[1] = green_factor; // diminish green part towards rear of exhaust
223 : ex_emissive[0] = red_factor; // diminish red part towards rear of exhaust
224 : vertex = HPvector_add(f01.position, vectorToHPVector(b01));
225 : collision_radius = HPmagnitude(HPvector_subtract(vertex, currentPos));
226 : _vertices[iv++] = vertex.x;// + zero.k.x * flare_factor * 4.0;
227 : _vertices[iv++] = vertex.y;// + zero.k.y * flare_factor * 4.0;
228 : _vertices[iv++] = vertex.z;// + zero.k.z * flare_factor * 4.0;
229 : _exhaustBaseColors[ci++] = ex_emissive[0];
230 : _exhaustBaseColors[ci++] = ex_emissive[1];
231 : _exhaustBaseColors[ci++] = ex_emissive[2];
232 : _exhaustBaseColors[ci++] = ex_emissive[3];
233 :
234 :
235 : Vector k1 = f01.k;
236 : Vector j1 = cross_product(master_i, k1);
237 : Vector i1 = vector_multiply_scalar(cross_product(j1, k1), _exhaustScale.x * speedScale);
238 : j1 = vector_multiply_scalar(j1, _exhaustScale.y * speedScale);
239 :
240 : for (i = 0; i < 8; i++)
241 : {
242 : vertex = HPvector_add(f01.position,
243 : vectorToHPVector(vector_add(b01,
244 : vector_add(vector_multiply_scalar(i1,s1[i]),
245 : vector_multiply_scalar(j1,c1[i])))));
246 : length = HPmagnitude(HPvector_subtract(vertex, currentPos));
247 : if (length > collision_radius)
248 : {
249 : collision_radius = length;
250 : }
251 : _vertices[iv++] = vertex.x;
252 : _vertices[iv++] = vertex.y;
253 : _vertices[iv++] = vertex.z;
254 : _exhaustBaseColors[ci++] = ex_emissive[0];
255 : _exhaustBaseColors[ci++] = ex_emissive[1];
256 : _exhaustBaseColors[ci++] = ex_emissive[2];
257 : _exhaustBaseColors[ci++] = ex_emissive[3];
258 : }
259 :
260 : ex_emissive[3] = kFadeLevel1 * flare_factor * kOverallAlpha; // fade alpha towards rear of exhaust
261 : ex_emissive[1] = kFadeLevel1 * green_factor; // diminish green part towards rear of exhaust
262 : ex_emissive[0] = kFadeLevel1 * red_factor; // diminish red part towards rear of exhaust
263 :
264 : k1 = f03.k;
265 : i1 = vector_multiply_scalar(cross_product(j1, k1), _exhaustScale.x * kScaleLevel1 * speedScale);
266 : j1 = vector_multiply_scalar(cross_product(master_i, k1), _exhaustScale.y * kScaleLevel1 * speedScale);
267 : for (i = 0; i < 8; i++)
268 : {
269 : r1 = randf();
270 : vertex = HPvector_add(f03.position,
271 : vectorToHPVector(vector_add(b03,
272 : vector_add(vector_multiply_scalar(i1,s1[i]),
273 : vector_add(vector_multiply_scalar(j1,c1[i]),
274 : vector_multiply_scalar(k1,r1))))));
275 : length = HPmagnitude(HPvector_subtract(vertex, currentPos));
276 : if (length > collision_radius)
277 : {
278 : collision_radius = length;
279 : }
280 : _vertices[iv++] = vertex.x;
281 : _vertices[iv++] = vertex.y;
282 : _vertices[iv++] = vertex.z;
283 : _exhaustBaseColors[ci++] = ex_emissive[0];
284 : _exhaustBaseColors[ci++] = ex_emissive[1];
285 : _exhaustBaseColors[ci++] = ex_emissive[2];
286 : _exhaustBaseColors[ci++] = ex_emissive[3];
287 : }
288 :
289 : ex_emissive[3] = kFadeLevel2 * flare_factor * kOverallAlpha; // fade alpha towards rear of exhaust
290 : ex_emissive[1] = kFadeLevel2 * green_factor; // diminish green part towards rear of exhaust
291 : ex_emissive[0] = kFadeLevel2 * red_factor; // diminish red part towards rear of exhaust
292 :
293 : k1 = f06.k;
294 : i1 = vector_multiply_scalar(cross_product(j1, k1), 0.8f * _exhaustScale.x * kScaleLevel2 * speedScale);
295 : j1 = vector_multiply_scalar(cross_product(master_i, k1), 0.8f * _exhaustScale.y * kScaleLevel2 * speedScale);
296 : for (i = 0; i < 8; i++)
297 : {
298 : r1 = randf();
299 : vertex = HPvector_add(f06.position,
300 : vectorToHPVector(vector_add(b06,
301 : vector_add(vector_multiply_scalar(i1,s1[i]),
302 : vector_add(vector_multiply_scalar(j1,c1[i]),
303 : vector_multiply_scalar(k1,r1))))));
304 : length = HPmagnitude(HPvector_subtract(vertex, currentPos));
305 : if (length > collision_radius)
306 : {
307 : collision_radius = length;
308 : }
309 : _vertices[iv++] = vertex.x;
310 : _vertices[iv++] = vertex.y;
311 : _vertices[iv++] = vertex.z;
312 : _exhaustBaseColors[ci++] = ex_emissive[0];
313 : _exhaustBaseColors[ci++] = ex_emissive[1];
314 : _exhaustBaseColors[ci++] = ex_emissive[2];
315 : _exhaustBaseColors[ci++] = ex_emissive[3];
316 : }
317 :
318 : ex_emissive[3] = kFadeLevel3 * flare_factor * kOverallAlpha; // fade alpha towards rear of exhaust
319 : ex_emissive[1] = kFadeLevel3 * green_factor; // diminish green part towards rear of exhaust
320 : ex_emissive[0] = kFadeLevel3 * red_factor; // diminish red part towards rear of exhaust
321 : k1 = f08.k;
322 : i1 = vector_multiply_scalar(cross_product(j1, k1), 0.5f * _exhaustScale.x * kScaleLevel3 * speedScale);
323 : j1 = vector_multiply_scalar(cross_product(master_i, k1), 0.5f * _exhaustScale.y * kScaleLevel3 * speedScale);
324 : for (i = 0; i < 8; i++)
325 : {
326 : r1 = randf();
327 : vertex = HPvector_add(f08.position,
328 : vectorToHPVector(vector_add(b08,
329 : vector_add(vector_multiply_scalar(i1,s1[i]),
330 : vector_add(vector_multiply_scalar(j1,c1[i]),
331 : vector_multiply_scalar(k1,r1))))));
332 : length = HPmagnitude(HPvector_subtract(vertex, currentPos));
333 : if (length > collision_radius)
334 : {
335 : collision_radius = length;
336 : }
337 : _vertices[iv++] = vertex.x;
338 : _vertices[iv++] = vertex.y;
339 : _vertices[iv++] = vertex.z;
340 : _exhaustBaseColors[ci++] = ex_emissive[0];
341 : _exhaustBaseColors[ci++] = ex_emissive[1];
342 : _exhaustBaseColors[ci++] = ex_emissive[2];
343 : _exhaustBaseColors[ci++] = ex_emissive[3];
344 : }
345 :
346 : ex_emissive[3] = 0.0; // fade alpha towards rear of exhaust
347 : ex_emissive[1] = 0.0; // diminish green part towards rear of exhaust
348 : ex_emissive[0] = 0.0; // diminish red part towards rear of exhaust
349 : length = HPmagnitude(HPvector_subtract(vertex, currentPos));
350 : if (length > collision_radius)
351 : {
352 : collision_radius = length;
353 : }
354 : _vertices[iv++] = f10.position.x;
355 : _vertices[iv++] = f10.position.y;
356 : _vertices[iv++] = f10.position.z;
357 : _exhaustBaseColors[ci++] = ex_emissive[0];
358 : _exhaustBaseColors[ci++] = ex_emissive[1];
359 : _exhaustBaseColors[ci++] = ex_emissive[2];
360 : _exhaustBaseColors[ci++] = ex_emissive[3];
361 :
362 : (void)iv; (void)ci; // Suppress Clang static analyzer warnings.
363 : }
364 :
365 0 : static GLuint tfan1[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 1 }; // initial fan 0..9
366 :
367 : // normal polys
368 0 : static GLuint tstr1[9] = { 1, 5, 9, 13, 17, 21, 25, 29, 33 };
369 0 : static GLuint tstr2[9] = { 2, 6, 10, 14, 18, 22, 26, 30, 33 };
370 0 : static GLuint tstr3[9] = { 3, 7, 11, 15, 19, 23, 27, 31, 33 };
371 0 : static GLuint tstr4[9] = { 4, 8, 12, 16, 20, 24, 28, 32, 33 };
372 :
373 : // aft-view special polys
374 0 : static GLuint afttstr1[4] = { 1, 5, 25, 29 };
375 0 : static GLuint afttstr2[4] = { 2, 6, 26, 30 };
376 0 : static GLuint afttstr3[4] = { 3, 7, 27, 31 };
377 0 : static GLuint afttstr4[4] = { 4, 8, 28, 32 };
378 :
379 :
380 0 : static GLfloat pA[6] = { 0.01, 0.0, 2.0, 4.0, 6.0, 10.0 }; // phase adjustments
381 :
382 :
383 0 : - (void) drawSubEntityImmediate:(bool)immediate translucent:(bool)translucent
384 : {
385 : if (!translucent) return;
386 :
387 : ShipEntity *ship = [self owner];
388 : if ([ship speedFactor] <= 0.001f) return; // don't draw if not moving according to 'update' calculation
389 :
390 : OO_ENTER_OPENGL();
391 : OOSetOpenGLState(OPENGL_STATE_ADDITIVE_BLENDING);
392 :
393 : OOGLPopModelView();
394 : OOGLPushModelView();
395 : // GLTranslateOOVector(vector_flip([self cameraRelativePosition]));
396 : HPVector cam = [PLAYER viewpointPosition];
397 : for (unsigned n=0;n<34*3;n++)
398 : {
399 : switch (n%3)
400 : {
401 : case 0: // x coordinates
402 : _glVertices[n] = (GLfloat)(_vertices[n] - cam.x);
403 : break;
404 : case 1: // y coordinates
405 : _glVertices[n] = (GLfloat)(_vertices[n] - cam.y);
406 : break;
407 : case 2: // z coordinates
408 : _glVertices[n] = (GLfloat)(_vertices[n] - cam.z);
409 : break;
410 : }
411 : }
412 :
413 : OOGL(glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT));
414 :
415 : OOGL(glDisable(GL_LIGHTING));
416 : OOGL(glEnable(GL_BLEND));
417 : OOGL(glDepthMask(GL_FALSE));
418 : OOGL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
419 : OOGL(glEnable(GL_TEXTURE_2D));
420 : [[self texture] apply];
421 :
422 : // OOGL(glDisable(GL_CULL_FACE)); // face culling
423 : OOGL(glShadeModel(GL_SMOOTH));
424 :
425 : OOGL(glEnableClientState(GL_COLOR_ARRAY));
426 : OOGL(glVertexPointer(3, GL_FLOAT, 0, _glVertices));
427 : OOGL(glColorPointer(4, GL_FLOAT, 0, _exhaustBaseColors));
428 :
429 : double intpart, dphase = 1.0-modf((double)[UNIVERSE getTime]*2.5,&intpart);
430 : GLfloat phase = (GLfloat)dphase;
431 :
432 : GLfloat texCoords[68] = {
433 : 0.5, phase+pA[0],
434 :
435 : 0.1, phase+pA[1], 0.1, phase+pA[1],
436 : 0.1, phase+pA[1], 0.1, phase+pA[1],
437 : 0.9, phase+pA[1], 0.9, phase+pA[1],
438 : 0.9, phase+pA[1], 0.9, phase+pA[1],
439 :
440 : 0.1, phase+pA[2], 0.1, phase+pA[2],
441 : 0.1, phase+pA[2], 0.1, phase+pA[2],
442 : 0.9, phase+pA[2], 0.9, phase+pA[2],
443 : 0.9, phase+pA[2], 0.9, phase+pA[2],
444 :
445 : 0.1, phase+pA[3], 0.1, phase+pA[3],
446 : 0.1, phase+pA[3], 0.1, phase+pA[3],
447 : 0.9, phase+pA[3], 0.9, phase+pA[3],
448 : 0.9, phase+pA[3], 0.9, phase+pA[3],
449 :
450 : 0.1, phase+pA[4], 0.1, phase+pA[4],
451 : 0.1, phase+pA[4], 0.1, phase+pA[4],
452 : 0.9, phase+pA[4], 0.9, phase+pA[4],
453 : 0.9, phase+pA[4], 0.9, phase+pA[4],
454 :
455 : 0.5, phase+pA[5],
456 : };
457 : OOGL(glTexCoordPointer(2, GL_FLOAT, 0, texCoords));
458 :
459 : // reduced detail for internal view to avoid rendering artefacts
460 : if ([[self owner] isPlayer] && [UNIVERSE viewDirection] != VIEW_CUSTOM)
461 : {
462 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, afttstr1));
463 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, afttstr2));
464 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, afttstr3));
465 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, afttstr4));
466 : }
467 : else
468 : {
469 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_INT, tstr1));
470 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_INT, tstr2));
471 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_INT, tstr3));
472 : OOGL(glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_INT, tstr4));
473 : }
474 :
475 : /* Need a different texture and color array for this segment */
476 : GLfloat fanTextures[18] = {
477 : 0.5f, 0.0f+phase,
478 : 0.2f, 0.0f+phase,
479 : 0.2f, 0.1f+phase,
480 : 0.2f, 0.2f+phase,
481 : 0.2f, 0.3f+phase,
482 : 0.2f, 0.4f+phase,
483 : 0.2f, 0.3f+phase,
484 : 0.2f, 0.2f+phase,
485 : 0.2f, 0.1f+phase
486 : };
487 : OOGL(glTexCoordPointer(2, GL_FLOAT, 0, fanTextures));
488 :
489 : GLfloat fanColors[36];
490 : GLfloat fr = _exhaustBaseColors[0], fg = _exhaustBaseColors[1], fb = _exhaustBaseColors[2];
491 : unsigned i = 0;
492 : fanColors[i++] = fr;
493 : fanColors[i++] = fg;
494 : fanColors[i++] = fb;
495 : fanColors[i++] = 1.0;
496 : for (;i<36;)
497 : {
498 : fanColors[i++] = fr;
499 : fanColors[i++] = fg;
500 : fanColors[i++] = fb;
501 : fanColors[i++] = 0.5;
502 : }
503 : OOGL(glColorPointer(4, GL_FLOAT, 0, fanColors));
504 :
505 : OOGL(glDrawElements(GL_TRIANGLE_FAN, 10, GL_UNSIGNED_INT, tfan1));
506 :
507 : OOGL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
508 : OOGL(glDisable(GL_TEXTURE_2D));
509 :
510 : OOGL(glDisableClientState(GL_COLOR_ARRAY));
511 :
512 :
513 : OOGL(glPopAttrib());
514 :
515 : OOVerifyOpenGLState();
516 : }
517 :
518 :
519 0 : #define PREV(n) ((n + kExhaustFrameCount - 1) % kExhaustFrameCount)
520 0 : #define NEXT(n) ((n + 1) % kExhaustFrameCount)
521 :
522 :
523 0 : - (void) saveToLastFrame
524 : {
525 : ShipEntity *ship = [self owner];
526 :
527 : // Absolute position of self
528 : // normally this would use the transformation matrix, but that
529 : // introduces inaccuracies
530 : // so just use the rotation matrix, then translate using HPVectors
531 : HPVector framePos = OOHPVectorMultiplyMatrix([self position], [ship drawRotationMatrix]);
532 : framePos = HPvector_add(framePos,[ship position]);
533 : Frame frame = { [UNIVERSE getTime], framePos, [ship normalOrientation], [ship upVector] };
534 :
535 : _track[_nextFrame] = frame;
536 : _nextFrame = (_nextFrame + 1) % kExhaustFrameCount;
537 : }
538 :
539 :
540 0 : - (Frame) frameAtTime:(double)t_frame fromFrame:(Frame) frame_zero // t_frame is relative to now ie. -0.5 = half a second ago.
541 : {
542 : if (t_frame >= 0.0) return frame_zero;
543 :
544 : Frame frame_one;
545 :
546 : int t1 = PREV(_nextFrame);
547 : double moment_in_time = frame_zero.timeframe + t_frame;
548 : double period, f0;
549 :
550 : if (moment_in_time > _trackTime) // between the last saved frame and now
551 : {
552 : frame_one = _track[t1]; // last saved moment
553 : period = (moment_in_time - t_frame) - _trackTime;
554 : f0 = 1.0 + t_frame/period;
555 : }
556 : else if (moment_in_time < _track[_nextFrame].timeframe) // more than kExhaustFrameCount frames back
557 : {
558 : return _track[_nextFrame];
559 : }
560 : else
561 : {
562 : while (moment_in_time < _track[t1].timeframe)
563 : {
564 : t1 = PREV(t1);
565 : }
566 : int t0 = NEXT(t1);
567 :
568 : frame_zero = _track[t0];
569 : frame_one = _track[t1];
570 : period = frame_zero.timeframe - frame_one.timeframe;
571 : f0 = (moment_in_time - _track[t1].timeframe)/period;
572 : }
573 :
574 : // interpolate
575 : double f1 = 1.0 - f0;
576 :
577 : HPVector posn;
578 : posn.x = f0 * frame_zero.position.x + f1 * frame_one.position.x;
579 : posn.y = f0 * frame_zero.position.y + f1 * frame_one.position.y;
580 : posn.z = f0 * frame_zero.position.z + f1 * frame_one.position.z;
581 : Quaternion qrot;
582 : qrot.w = (float)f0 * frame_zero.orientation.w + (float)f1 * frame_one.orientation.w;
583 : qrot.x = (float)f0 * frame_zero.orientation.x + (float)f1 * frame_one.orientation.x;
584 : qrot.y = (float)f0 * frame_zero.orientation.y + (float)f1 * frame_one.orientation.y;
585 : qrot.z = (float)f0 * frame_zero.orientation.z + (float)f1 * frame_one.orientation.z;
586 :
587 : Frame result;
588 : result.position = posn;
589 : result.orientation = qrot;
590 : result.timeframe = moment_in_time;
591 : result.k = vector_forward_from_quaternion(qrot);
592 : return result;
593 : }
594 :
595 :
596 : - (void) resetPlume
597 : {
598 : /*ShipEntity *ship = [self owner];
599 :
600 : // Absolute position of self
601 : Vector framePos = OOVectorMultiplyMatrix([self position], [ship drawTransformationMatrix]);
602 : Frame frame = { [UNIVERSE getTime], framePos, [ship normalOrientation], [ship upVector] };
603 :
604 : _track[_nextFrame] = frame;
605 : _nextFrame = (_nextFrame + 1) % kExhaustFrameCount;*/
606 : _nextFrame = 0;
607 : HPVector framePos = OOHPVectorMultiplyMatrix([self position], [[self owner] drawTransformationMatrix]);
608 : uint8_t i;
609 : for (i = 0; i < kExhaustFrameCount; i++)
610 : {
611 : _track[i].timeframe = 0.0;
612 : _track[i].position = framePos;
613 : _track[i].orientation = kIdentityQuaternion;
614 : _track[i].k = kZeroVector;
615 : }
616 : }
617 :
618 :
619 0 : - (void) rescaleBy:(GLfloat)factor
620 : {
621 : _exhaustScale = vector_multiply_scalar(_exhaustScale, factor);
622 : }
623 :
624 :
625 0 : - (void) rescaleBy:(GLfloat)factor writeToCache:(BOOL)writeToCache
626 : {
627 : /* Do nothing; this is only needed because of OOEntityWithDrawable
628 : implementation requirements */
629 : }
630 :
631 :
632 : - (OOTexture *) texture
633 : {
634 : return [OOExhaustPlumeEntity plumeTexture];
635 : }
636 :
637 :
638 : + (void) setUpTexture
639 : {
640 : if (sPlumeTexture == nil)
641 : {
642 : sPlumeTexture = [[OOTexture textureWithName:@"oolite-exhaust-blur.png"
643 : inFolder:@"Textures"
644 : options:kOOTextureMinFilterMipMap | kOOTextureMagFilterLinear | kOOTextureAlphaMask | kOOTextureRepeatT | kOOTextureRepeatS
645 : anisotropy:kOOTextureDefaultAnisotropy / 2.0
646 : lodBias:0.0] retain];
647 : [[OOGraphicsResetManager sharedManager] registerClient:(id<OOGraphicsResetClient>)[OOExhaustPlumeEntity class]];
648 :
649 : }
650 : }
651 :
652 :
653 : + (OOTexture *) plumeTexture
654 : {
655 : if (sPlumeTexture == nil) [self setUpTexture];
656 : return sPlumeTexture;
657 : }
658 :
659 :
660 : + (void) resetGraphicsState
661 : {
662 : [sPlumeTexture release];
663 : sPlumeTexture = nil;
664 : }
665 :
666 :
667 : @end
668 :
669 :
670 : @implementation Entity (OOExhaustPlume)
671 :
672 : - (BOOL)isExhaust
673 : {
674 : return NO;
675 : }
676 :
677 : @end
|