Line data Source code
1 0 : /*
2 :
3 : DustEntity.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "DustEntity.h"
26 :
27 : #import "OOMaths.h"
28 : #import "Universe.h"
29 : #import "MyOpenGLView.h"
30 : #import "OOGraphicsResetManager.h"
31 : #import "OODebugFlags.h"
32 : #import "OOMacroOpenGL.h"
33 :
34 :
35 : #if OO_SHADERS
36 : #import "OOMaterial.h" // For kTangentAttributeIndex
37 : #import "OOShaderProgram.h"
38 : #import "OOShaderUniform.h"
39 : #endif
40 :
41 : #import "PlayerEntity.h"
42 :
43 :
44 0 : #define FAR_PLANE (DUST_SCALE * 0.50f)
45 0 : #define NEAR_PLANE (DUST_SCALE * 0.25f)
46 :
47 :
48 : // Declare protocol conformance
49 : @interface DustEntity (Private) <OOGraphicsResetClient>
50 :
51 0 : - (void) checkShaderMode;
52 :
53 : @end
54 :
55 : #if OO_SHADERS
56 : enum
57 : {
58 : kShaderModeOff,
59 : kShaderModeOn,
60 : kShaderModeUnknown
61 : };
62 : #endif
63 :
64 :
65 : @implementation DustEntity
66 :
67 0 : - (id) init
68 : {
69 : int vi;
70 :
71 : // this should be unnecessary
72 : // ranrot_srand((uint32_t)[[NSDate date] timeIntervalSince1970]); // seed randomiser by time
73 :
74 : self = [super init];
75 :
76 : for (vi = 0; vi < DUST_N_PARTICLES; vi++)
77 : {
78 : vertices[vi].x = (ranrot_rand() % DUST_SCALE) - DUST_SCALE / 2;
79 : vertices[vi].y = (ranrot_rand() % DUST_SCALE) - DUST_SCALE / 2;
80 : vertices[vi].z = (ranrot_rand() % DUST_SCALE) - DUST_SCALE / 2;
81 :
82 : // Set up element index array for warp mode.
83 : indices[vi * 2] = vi;
84 : indices[vi * 2 + 1] = vi + DUST_N_PARTICLES;
85 :
86 : #if OO_SHADERS
87 : vertices[vi + DUST_N_PARTICLES] = vertices[vi];
88 : warpinessAttr[vi] = 0.0f;
89 : warpinessAttr[vi + DUST_N_PARTICLES] = 1.0f;
90 : #endif
91 : }
92 :
93 : #if OO_SHADERS
94 : shaderMode = kShaderModeUnknown;
95 : #endif
96 :
97 : drawDust = ![[[NSProcessInfo processInfo] arguments] containsObject:@"-nodust"];
98 :
99 : dust_color = [[OOColor colorWithRed:0.5 green:1.0 blue:1.0 alpha:1.0] retain];
100 : [self setStatus:STATUS_ACTIVE];
101 :
102 : hasPointSprites = [[OOOpenGLExtensionManager sharedManager] haveExtension:@"GL_ARB_point_sprite"];
103 :
104 : if (hasPointSprites)
105 : {
106 : texture = [[OOTexture textureWithName:@"oolite-particle-dust.png"
107 : inFolder:@"Textures"
108 : options:kOOTextureMinFilterMipMap | kOOTextureMagFilterLinear | kOOTextureAlphaMask
109 : anisotropy:kOOTextureDefaultAnisotropy / 2.0
110 : lodBias:0.0] retain];
111 : }
112 :
113 : collision_radius = DUST_SCALE; // for draw pass calculations
114 :
115 : [[OOGraphicsResetManager sharedManager] registerClient:self];
116 :
117 : return self;
118 : }
119 :
120 :
121 0 : - (void) dealloc
122 : {
123 : DESTROY(dust_color);
124 : [[OOGraphicsResetManager sharedManager] unregisterClient:self];
125 :
126 : DESTROY(texture);
127 : #if OO_SHADERS
128 : DESTROY(shader);
129 : DESTROY(uniforms);
130 : #endif
131 :
132 : [super dealloc];
133 : }
134 :
135 :
136 : - (void) setDustColor:(OOColor *) color
137 : {
138 : if (dust_color) [dust_color release];
139 : dust_color = [color retain];
140 : [dust_color getRed:&color_fv[0] green:&color_fv[1] blue:&color_fv[2] alpha:&color_fv[3]];
141 : }
142 :
143 :
144 : - (OOColor *) dustColor
145 : {
146 : return dust_color;
147 : }
148 :
149 :
150 0 : - (BOOL) canCollide
151 : {
152 : return NO;
153 : }
154 :
155 :
156 0 : - (void) updateCameraRelativePosition
157 : {
158 : HPVector c_pos = [PLAYER viewpointPosition];
159 : cameraRelativePosition = make_vector((OOScalar)-fmod(c_pos.x,DUST_SCALE),(OOScalar)-fmod(c_pos.y,DUST_SCALE),(OOScalar)-fmod(c_pos.z,DUST_SCALE));
160 : }
161 :
162 :
163 0 : - (void) update:(OOTimeDelta) delta_t
164 : {
165 : // [self setPosition:position];
166 : zero_distance = 0.0;
167 :
168 : #if OO_SHADERS
169 : if (EXPECT_NOT(shaderMode == kShaderModeUnknown)) [self checkShaderMode];
170 :
171 : // Shader takes care of repositioning.
172 : if (shaderMode == kShaderModeOn) return;
173 : #endif
174 :
175 : Vector offset = vector_flip(cameraRelativePosition);
176 : GLfloat half_scale = DUST_SCALE * 0.50;
177 : int vi;
178 : for (vi = 0; vi < DUST_N_PARTICLES; vi++)
179 : {
180 : while (vertices[vi].x - offset.x < -half_scale)
181 : vertices[vi].x += DUST_SCALE;
182 : while (vertices[vi].x - offset.x > half_scale)
183 : vertices[vi].x -= DUST_SCALE;
184 :
185 : while (vertices[vi].y - offset.y < -half_scale)
186 : vertices[vi].y += DUST_SCALE;
187 : while (vertices[vi].y - offset.y > half_scale)
188 : vertices[vi].y -= DUST_SCALE;
189 :
190 : while (vertices[vi].z - offset.z < -half_scale)
191 : vertices[vi].z += DUST_SCALE;
192 : while (vertices[vi].z - offset.z > half_scale)
193 : vertices[vi].z -= DUST_SCALE;
194 : }
195 : }
196 :
197 :
198 : #if OO_SHADERS
199 : - (OOShaderProgram *) shader
200 : {
201 : if (shader == nil)
202 : {
203 : NSString *prefix = [NSString stringWithFormat:
204 : @"#define OODUST_SCALE_MAX (float(%g))\n"
205 : "#define OODUST_SCALE_FACTOR (float(%g))\n"
206 : "#define OODUST_SIZE (float(%g))\n",
207 : FAR_PLANE / NEAR_PLANE,
208 : 1.0f / (FAR_PLANE - NEAR_PLANE),
209 : (float)DUST_SCALE];
210 :
211 : // Reuse tangent attribute ID for "warpiness", as we don't need a tangent.
212 : NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kTangentAttributeIndex]
213 : forKey:@"aWarpiness"];
214 :
215 : shader = [[OOShaderProgram shaderProgramWithVertexShaderName:@"oolite-dust.vertex"
216 : fragmentShaderName:@"oolite-dust.fragment"
217 : prefix:prefix
218 : attributeBindings:attributes] retain];
219 :
220 : DESTROY(uniforms);
221 : OOShaderUniform *uWarp = [[OOShaderUniform alloc] initWithName:@"uWarp"
222 : shaderProgram:shader
223 : boundToObject:self
224 : property:@selector(warpVector)
225 : convertOptions:0];
226 : OOShaderUniform *uOffsetPlayerPosition = [[OOShaderUniform alloc] initWithName:@"uOffsetPlayerPosition"
227 : shaderProgram:shader
228 : boundToObject:self
229 : property:@selector(offsetPlayerPosition)
230 : convertOptions:0];
231 :
232 : uniforms = [[NSArray alloc] initWithObjects:uWarp, uOffsetPlayerPosition, nil];
233 : [uWarp release];
234 : [uOffsetPlayerPosition release];
235 : }
236 :
237 : return shader;
238 : }
239 :
240 : - (Vector) offsetPlayerPosition
241 : {
242 : // used as shader uniform, so needs to be low precision
243 : HPVector c_pos = [PLAYER viewpointPosition];
244 : Vector offset = make_vector((OOScalar)fmod(c_pos.x,DUST_SCALE),(OOScalar)fmod(c_pos.y,DUST_SCALE),(OOScalar)fmod(c_pos.z,DUST_SCALE));
245 : return vector_subtract(offset, make_vector(DUST_SCALE * 0.5f, DUST_SCALE * 0.5f, DUST_SCALE * 0.5f));
246 : }
247 :
248 :
249 : - (void) checkShaderMode
250 : {
251 : shaderMode = kShaderModeOff;
252 : if ([UNIVERSE detailLevel] >= DETAIL_LEVEL_SHADERS)
253 : {
254 : if ([[OOOpenGLExtensionManager sharedManager] useDustShader])
255 : {
256 : shaderMode = kShaderModeOn;
257 : }
258 : }
259 : }
260 : #endif
261 :
262 :
263 0 : - (Vector) warpVector
264 : {
265 : return vector_multiply_scalar([PLAYER velocity], 1.0f / HYPERSPEED_FACTOR);
266 : }
267 :
268 :
269 0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
270 : {
271 : if (!drawDust || [UNIVERSE breakPatternHide] || !translucent) return; // DON'T DRAW
272 :
273 : PlayerEntity* player = PLAYER;
274 : assert(player != nil);
275 :
276 : #ifndef NDEBUG
277 : if (gDebugFlags & DEBUG_NO_DUST) return;
278 : #endif
279 :
280 : #if OO_SHADERS
281 : if (EXPECT_NOT(shaderMode == kShaderModeUnknown)) [self checkShaderMode];
282 : BOOL useShader = (shaderMode == kShaderModeOn);
283 : #endif
284 :
285 :
286 : GLfloat *fogcolor = [UNIVERSE skyClearColor];
287 : float idealDustSize = [[UNIVERSE gameView] viewSize].width / 800.0f;
288 :
289 : BOOL warp_stars = [player atHyperspeed];
290 : float dustIntensity;
291 :
292 : OO_ENTER_OPENGL();
293 : OOSetOpenGLState(OPENGL_STATE_OPAQUE);
294 : OOGL(glDisableClientState(GL_NORMAL_ARRAY));
295 :
296 : if (!warp_stars)
297 : {
298 : // Draw points.
299 : float dustPointSize = ceil(idealDustSize);
300 : if (dustPointSize < 1.0f) dustPointSize = 1.0f;
301 : OOGL(GLScaledPointSize(dustPointSize));
302 : dustIntensity = OOClamp_0_1_f(idealDustSize / dustPointSize);
303 : }
304 : else
305 : {
306 : // Draw lines.
307 : float idealLineSize = idealDustSize * 0.5f;
308 : float dustLineSize = ceil(idealLineSize);
309 : if (dustLineSize < 1.0f) dustLineSize = 1.0f;
310 : GLScaledLineWidth(dustLineSize);
311 : dustIntensity = OOClamp_0_1_f(idealLineSize / dustLineSize);
312 : }
313 : if (fogcolor[3] > 0.0)
314 : {
315 : // fade out dust when entering atmosphere (issue #100)
316 : dustIntensity = OOClamp_0_1_f(dustIntensity-(fogcolor[3]*3.0));
317 : }
318 :
319 :
320 : if (dustIntensity > 0.0)
321 : {
322 :
323 : float *color = NULL;
324 : if (player->isSunlit) color = color_fv;
325 : else color = UNIVERSE->stars_ambient;
326 : OOGL(glColor4f(color[0], color[1], color[2], dustIntensity));
327 :
328 : #if OO_SHADERS
329 : if (useShader)
330 : {
331 : [[self shader] apply];
332 : [uniforms makeObjectsPerformSelector:@selector(apply)];
333 : }
334 : else
335 : #endif
336 : {
337 : OOGL(glEnable(GL_FOG));
338 : OOGL(glFogi(GL_FOG_MODE, GL_LINEAR));
339 : OOGL(glFogfv(GL_FOG_COLOR, fogcolor));
340 : OOGL(glHint(GL_FOG_HINT, GL_NICEST));
341 : OOGL(glFogf(GL_FOG_START, NEAR_PLANE));
342 : OOGL(glFogf(GL_FOG_END, FAR_PLANE));
343 : }
344 :
345 : OOGL(glEnable(GL_BLEND));
346 : OOGL(glDepthMask(GL_FALSE));
347 :
348 : if (warp_stars)
349 : {
350 : OOGL(glDisable(GL_TEXTURE_2D));
351 : #if OO_SHADERS
352 : if (useShader)
353 : {
354 : OOGL(glEnableVertexAttribArrayARB(kTangentAttributeIndex));
355 : OOGL(glVertexAttribPointerARB(kTangentAttributeIndex, 1, GL_FLOAT, GL_FALSE, 0, warpinessAttr));
356 : }
357 : else
358 : #endif
359 : {
360 : Vector warpVector = [self warpVector];
361 : unsigned vi;
362 : for (vi = 0; vi < DUST_N_PARTICLES; vi++)
363 : {
364 : vertices[vi + DUST_N_PARTICLES] = vector_subtract(vertices[vi], warpVector);
365 : }
366 : }
367 :
368 : OOGL(glVertexPointer(3, GL_FLOAT, 0, vertices));
369 : OOGL(glDrawElements(GL_LINES, DUST_N_PARTICLES * 2, GL_UNSIGNED_SHORT, indices));
370 :
371 : #if OO_SHADERS
372 : if (useShader)
373 : {
374 : OOGL(glDisableVertexAttribArrayARB(kTangentAttributeIndex));
375 : }
376 : #endif
377 : OOGL(glEnable(GL_TEXTURE_2D));
378 :
379 : }
380 : else
381 : {
382 : if (hasPointSprites)
383 : {
384 : #if OO_SHADERS
385 : if (!useShader)
386 : #endif
387 : {
388 : OOGL(glBlendFunc(GL_SRC_ALPHA, GL_ONE));
389 : }
390 : OOGL(glEnable(GL_POINT_SPRITE_ARB));
391 : [texture apply];
392 : OOGL(glVertexPointer(3, GL_FLOAT, 0, vertices));
393 : OOGL(glDrawArrays(GL_POINTS, 0, DUST_N_PARTICLES));
394 : OOGL(glDisable(GL_POINT_SPRITE_ARB));
395 : }
396 : else
397 : {
398 : OOGL(glDisable(GL_TEXTURE_2D));
399 : OOGL(glVertexPointer(3, GL_FLOAT, 0, vertices));
400 : OOGL(glDrawArrays(GL_POINTS, 0, DUST_N_PARTICLES));
401 : OOGL(glEnable(GL_TEXTURE_2D));
402 : }
403 : }
404 :
405 : // reapply normal conditions
406 : #if OO_SHADERS
407 : if (useShader)
408 : {
409 : [OOShaderProgram applyNone];
410 : }
411 : else
412 : #endif
413 : {
414 : OOGL(glDisable(GL_FOG));
415 : OOGL(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
416 : }
417 :
418 : OOGL(glDisable(GL_BLEND));
419 : OOGL(glDepthMask(GL_TRUE));
420 :
421 : }
422 : OOGL(glEnableClientState(GL_NORMAL_ARRAY));
423 :
424 : OOVerifyOpenGLState();
425 : OOCheckOpenGLErrors(@"DustEntity after drawing %@", self);
426 : }
427 :
428 :
429 0 : - (void) resetGraphicsState
430 : {
431 : #if OO_SHADERS
432 : DESTROY(shader);
433 : DESTROY(uniforms);
434 :
435 : shaderMode = kShaderModeUnknown;
436 :
437 : /* Duplicate vertex data. This is only required if we're switching from
438 : non-shader mode to a shader mode, but let's KISS.
439 : */
440 : memcpy(vertices + DUST_N_PARTICLES, vertices, sizeof *vertices * DUST_N_PARTICLES);
441 : #endif
442 : }
443 :
444 :
445 : #ifndef NDEBUG
446 0 : - (NSString *) descriptionForObjDump
447 : {
448 : // Don't include range and visibility flag as they're irrelevant.
449 : return [self descriptionForObjDumpBasic];
450 : }
451 : #endif
452 :
453 : @end
|