Line data Source code
1 0 : /*
2 :
3 : OOLightParticleEntity.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 : #import "OOLightParticleEntity.h"
27 : #import "Universe.h"
28 : #import "PlayerEntity.h"
29 : #import "OOTexture.h"
30 : #import "OOColor.h"
31 : #import "OOCollectionExtractors.h"
32 : #import "OOFunctionAttributes.h"
33 : #import "OOMacroOpenGL.h"
34 : #import "OOGraphicsResetManager.h"
35 : #import "MyOpenGLView.h"
36 :
37 :
38 0 : #define PARTICLE_DISTANCE_SCALE_LOW 12.0
39 0 : #define PARTICLE_DISTANCE_SCALE_HIGH 36.0
40 :
41 :
42 0 : static OOTexture *sBlobTexture = nil;
43 :
44 :
45 : @interface OOLightParticleEntity (Private)
46 :
47 0 : + (void) resetGraphicsState;
48 :
49 : @end
50 :
51 :
52 : @implementation OOLightParticleEntity
53 :
54 : - (id) initWithDiameter:(float)diameter
55 : {
56 : if ((self = [super init]))
57 : {
58 : _diameter = diameter;
59 : no_draw_distance = pow(diameter / 2.0, M_SQRT2) * NO_DRAW_DISTANCE_FACTOR * NO_DRAW_DISTANCE_FACTOR;
60 : no_draw_distance *= [UNIVERSE reducedDetail] ? PARTICLE_DISTANCE_SCALE_LOW : PARTICLE_DISTANCE_SCALE_HIGH;
61 :
62 : _colorComponents[0] = 1.0f;
63 : _colorComponents[1] = 1.0f;
64 : _colorComponents[2] = 1.0f;
65 : _colorComponents[3] = 1.0f;
66 :
67 : [self setScanClass:CLASS_NO_DRAW];
68 : [self setStatus:STATUS_EFFECT];
69 : }
70 :
71 : return self;
72 : }
73 :
74 :
75 : - (float) diameter
76 : {
77 : return _diameter;
78 : }
79 :
80 :
81 : - (void) setDiameter:(float)diameter
82 : {
83 : _diameter = diameter;
84 : }
85 :
86 :
87 : - (void) setColor:(OOColor *)color
88 : {
89 : [color getRed:&_colorComponents[0] green:&_colorComponents[1] blue:&_colorComponents[2] alpha:&_colorComponents[3]];
90 : }
91 :
92 :
93 : - (void) setColor:(OOColor *)color alpha:(GLfloat)alpha
94 : {
95 : [self setColor:color];
96 : _colorComponents[3] = alpha;
97 : }
98 :
99 :
100 : - (void) drawSubEntityImmediate:(bool)immediate translucent:(bool)translucent
101 : {
102 : if (!translucent) return;
103 :
104 : /* TODO: someone will inevitably build a ship so big that individual
105 : zero_distances are necessary for flashers, if they haven't already.
106 : -- Ahruman 2009-09-20
107 : */
108 : cam_zero_distance = [[self owner] camZeroDistance];
109 : if (no_draw_distance <= cam_zero_distance) return;
110 :
111 : Entity *father = [self owner];
112 : Entity *last = nil;
113 : HPVector abspos = position;
114 :
115 : while (father != nil && father != last && father != NO_TARGET)
116 : {
117 : OOMatrix rM = [father drawRotationMatrix];
118 : abspos = HPvector_add(OOHPVectorMultiplyMatrix(abspos, rM), [father position]);
119 : last = father;
120 :
121 : if (![father isSubEntity]) break;
122 : father = [father owner];
123 : }
124 :
125 : OOMatrix temp_matrix = OOGLPopModelView();
126 : OOGLPushModelView();
127 :
128 : /* Flashers are drawn using the absolute view matrix */
129 : OOGLLoadModelView([UNIVERSE viewMatrix]);
130 : /* ...modified by the aggregate translation calculated above */
131 : OOGLTranslateModelView(HPVectorToVector(HPvector_subtract(abspos,[PLAYER viewpointPosition]))); // move to camera-relative position
132 : [self drawImmediate:immediate translucent:translucent];
133 :
134 : OOGLLoadModelView(temp_matrix);
135 : }
136 :
137 :
138 0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
139 : {
140 : if (!translucent) return;
141 : if ([UNIVERSE breakPatternHide] && ![self isImmuneToBreakPatternHide])
142 : {
143 : Entity *father = [self owner];
144 : while (father != nil && father != NO_TARGET)
145 : {
146 : if (![father isSubEntity]) break;
147 : father = [father owner];
148 : }
149 : if (![father isImmuneToBreakPatternHide])
150 : {
151 : return;
152 : }
153 : }
154 : if (no_draw_distance <= cam_zero_distance) return;
155 :
156 : OO_ENTER_OPENGL();
157 : OOSetOpenGLState(OPENGL_STATE_ADDITIVE_BLENDING);
158 :
159 : OOGL(glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT));
160 : OOGL(glEnable(GL_BLEND));
161 : OOGL(glBlendFunc(GL_SRC_ALPHA, GL_ONE));
162 :
163 : OOGL(glEnable(GL_TEXTURE_2D));
164 : OOGL(glDepthMask(GL_FALSE));
165 :
166 : GLfloat distanceAttenuation = cam_zero_distance / no_draw_distance;
167 : distanceAttenuation = 1.0 - distanceAttenuation;
168 : GLfloat components[4] = { _colorComponents[0], _colorComponents[1], _colorComponents[2], _colorComponents[3] * distanceAttenuation };
169 : OOGL(glColor4fv(components));
170 :
171 : OOGL(glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, components));
172 : OOGL(glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND));
173 :
174 : OOViewID viewDir = [UNIVERSE viewDirection];
175 : if (viewDir != VIEW_GUI_DISPLAY) OOGLMultModelView([PLAYER drawRotationMatrix]);
176 : [[self texture] apply];
177 :
178 : /* NOTE: nominal diameter is actual radius, because of the black border
179 : in the texture. However, the offset along the view axis is not
180 : affected by the border and needs to be scaled.
181 : -- Ahruman 2009-12-20
182 : */
183 : float viewOffset = _diameter * 0.5f;
184 :
185 : OOGLBEGIN(GL_QUADS);
186 : switch (viewDir)
187 : {
188 : case VIEW_FORWARD:
189 : case VIEW_GUI_DISPLAY:
190 : glTexCoord2f(0.0, 1.0);
191 : glVertex3f(-_diameter, -_diameter, -viewOffset);
192 :
193 : glTexCoord2f(1.0, 1.0);
194 : glVertex3f(_diameter, -_diameter, -viewOffset);
195 :
196 : glTexCoord2f(1.0, 0.0);
197 : glVertex3f(_diameter, _diameter, -viewOffset);
198 :
199 : glTexCoord2f(0.0, 0.0);
200 : glVertex3f(-_diameter, _diameter, -viewOffset);
201 : break;
202 :
203 : case VIEW_AFT:
204 : glTexCoord2f(0.0, 1.0);
205 : glVertex3f(_diameter, -_diameter, viewOffset);
206 :
207 : glTexCoord2f(1.0, 1.0);
208 : glVertex3f(-_diameter, -_diameter, viewOffset);
209 :
210 : glTexCoord2f(1.0, 0.0);
211 : glVertex3f(-_diameter, _diameter, viewOffset);
212 :
213 : glTexCoord2f(0.0, 0.0);
214 : glVertex3f(_diameter, _diameter, viewOffset);
215 : break;
216 :
217 : case VIEW_STARBOARD:
218 : glTexCoord2f(0.0, 1.0);
219 : glVertex3f(-viewOffset, -_diameter, _diameter);
220 :
221 : glTexCoord2f(1.0, 1.0);
222 : glVertex3f(-viewOffset, -_diameter, -_diameter);
223 :
224 : glTexCoord2f(1.0, 0.0);
225 : glVertex3f(-viewOffset, _diameter, -_diameter);
226 :
227 : glTexCoord2f(0.0, 0.0);
228 : glVertex3f(-viewOffset, _diameter, _diameter);
229 : break;
230 :
231 : case VIEW_PORT:
232 : glTexCoord2f(0.0, 1.0);
233 : glVertex3f(viewOffset, -_diameter, -_diameter);
234 :
235 : glTexCoord2f(1.0, 1.0);
236 : glVertex3f(viewOffset, -_diameter, _diameter);
237 :
238 : glTexCoord2f(1.0, 0.0);
239 : glVertex3f(viewOffset, _diameter, _diameter);
240 :
241 : glTexCoord2f(0.0, 0.0);
242 : glVertex3f(viewOffset, _diameter, -_diameter);
243 : break;
244 :
245 : case VIEW_CUSTOM:
246 : {
247 : PlayerEntity *player = PLAYER;
248 : Vector vi = [player customViewRightVector]; vi.x *= _diameter; vi.y *= _diameter; vi.z *= _diameter;
249 : Vector vj = [player customViewUpVector]; vj.x *= _diameter; vj.y *= _diameter; vj.z *= _diameter;
250 : Vector vk = [player customViewForwardVector]; vk.x *= viewOffset; vk.y *= viewOffset; vk.z *= viewOffset;
251 : glTexCoord2f(0.0, 1.0);
252 : glVertex3f(-vi.x -vj.x -vk.x, -vi.y -vj.y -vk.y, -vi.z -vj.z -vk.z);
253 : glTexCoord2f(1.0, 1.0);
254 : glVertex3f(+vi.x -vj.x -vk.x, +vi.y -vj.y -vk.y, +vi.z -vj.z -vk.z);
255 : glTexCoord2f(1.0, 0.0);
256 : glVertex3f(+vi.x +vj.x -vk.x, +vi.y +vj.y -vk.y, +vi.z +vj.z -vk.z);
257 : glTexCoord2f(0.0, 0.0);
258 : glVertex3f(-vi.x +vj.x -vk.x, -vi.y +vj.y -vk.y, -vi.z +vj.z -vk.z);
259 : }
260 : break;
261 :
262 : default:
263 : glTexCoord2f(0.0, 1.0);
264 : glVertex3f(-_diameter, -_diameter, -_diameter);
265 :
266 : glTexCoord2f(1.0, 1.0);
267 : glVertex3f(_diameter, -_diameter, -_diameter);
268 :
269 : glTexCoord2f(1.0, 0.0);
270 : glVertex3f(_diameter, _diameter, -_diameter);
271 :
272 : glTexCoord2f(0.0, 0.0);
273 : glVertex3f(-_diameter, _diameter, -_diameter);
274 : break;
275 : }
276 : OOGLEND();
277 :
278 : OOGL(glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE));
279 : OOGL(glPopAttrib());
280 :
281 : OOVerifyOpenGLState();
282 : }
283 :
284 :
285 : - (OOTexture *) texture
286 : {
287 : return [OOLightParticleEntity defaultParticleTexture];
288 : }
289 :
290 :
291 : + (void) setUpTexture
292 : {
293 : if (sBlobTexture == nil)
294 : {
295 : sBlobTexture = [[OOTexture textureWithName:@"oolite-particle-blur.png"
296 : inFolder:@"Textures"
297 : options:kOOTextureMinFilterMipMap | kOOTextureMagFilterLinear | kOOTextureAlphaMask
298 : anisotropy:kOOTextureDefaultAnisotropy / 2.0
299 : lodBias:0.0] retain];
300 : [[OOGraphicsResetManager sharedManager] registerClient:(id<OOGraphicsResetClient>)[OOLightParticleEntity class]];
301 : }
302 : }
303 :
304 :
305 : + (OOTexture *) defaultParticleTexture
306 : {
307 : if (sBlobTexture == nil) [self setUpTexture];
308 : return sBlobTexture;
309 : }
310 :
311 :
312 0 : + (void) resetGraphicsState
313 : {
314 : [sBlobTexture release];
315 : sBlobTexture = nil;
316 : }
317 :
318 :
319 0 : - (BOOL) isEffect
320 : {
321 : return YES;
322 : }
323 :
324 :
325 0 : - (BOOL) canCollide
326 : {
327 : return NO;
328 : }
329 :
330 :
331 :
332 : #ifndef NDEBUG
333 0 : - (NSSet *) allTextures
334 : {
335 : return [NSSet setWithObject:[self texture]];
336 : }
337 : #endif
338 :
339 : @end
|