Line data Source code
1 0 : /*
2 :
3 : OOPlanetDrawable.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 "OOStellarBody.h"
26 : #if NEW_PLANETS
27 :
28 :
29 : #import "OOPlanetDrawable.h"
30 : #import "OOPlanetData.h"
31 : #import "OOSingleTextureMaterial.h"
32 : #import "OOOpenGL.h"
33 : #import "OOMacroOpenGL.h"
34 : #import "Universe.h"
35 : #import "MyOpenGLView.h"
36 :
37 : #ifndef NDEBUG
38 : #import "Entity.h"
39 : #import "OODebugGLDrawing.h"
40 : #import "OODebugFlags.h"
41 : #endif
42 :
43 :
44 : #define LOD_GRANULARITY ((float)(kOOPlanetDataLevels - 1))
45 : #define LOD_FACTOR (1.0 / 4.0)
46 :
47 :
48 : @interface OOPlanetDrawable (Private)
49 :
50 : - (void) recalculateTransform;
51 :
52 : - (void) debugDrawNormals;
53 :
54 : - (void) renderCommonParts;
55 :
56 : @end
57 :
58 :
59 : @implementation OOPlanetDrawable
60 :
61 : + (instancetype) planetWithTextureName:(NSString *)textureName radius:(float)radius
62 : {
63 : OOPlanetDrawable *result = [[[self alloc] init] autorelease];
64 : [result setTextureName:textureName];
65 : [result setRadius:radius];
66 :
67 : return result;
68 : }
69 :
70 :
71 : + (instancetype) atmosphereWithRadius:(float)radius
72 : {
73 : OOPlanetDrawable *result = [[[self alloc] initAsAtmosphere] autorelease];
74 : [result setRadius:radius];
75 :
76 : return result;
77 : }
78 :
79 :
80 : - (id) init
81 : {
82 : if ((self = [super init]))
83 : {
84 : _radius = 1.0f;
85 : [self recalculateTransform];
86 : [self setLevelOfDetail:0.5f];
87 : }
88 :
89 : return self;
90 : }
91 :
92 :
93 : - (id) initAsAtmosphere
94 : {
95 : if ((self = [self init]))
96 : {
97 : _isAtmosphere = YES;
98 : }
99 :
100 : return self;
101 : }
102 :
103 :
104 : - (void) dealloc
105 : {
106 : DESTROY(_material);
107 :
108 : [super dealloc];
109 : }
110 :
111 :
112 : - (id) copyWithZone:(NSZone *)zone
113 : {
114 : OOPlanetDrawable *copy = [[[self class] allocWithZone:zone] init];
115 : [copy setMaterial:[self material]];
116 : copy->_isAtmosphere = _isAtmosphere;
117 : copy->_radius = _radius;
118 : copy->_transform = _transform;
119 : copy->_lod = _lod;
120 :
121 : return copy;
122 : }
123 :
124 :
125 : - (OOMaterial *) material
126 : {
127 : return _material;
128 : }
129 :
130 :
131 : - (void) setMaterial:(OOMaterial *)material
132 : {
133 : [_material autorelease];
134 : _material = [material retain];
135 : }
136 :
137 :
138 : - (NSString *) textureName
139 : {
140 : return [_material name];
141 : }
142 :
143 :
144 : - (void) setTextureName:(NSString *)textureName
145 : {
146 : if (![textureName isEqual:[self textureName]])
147 : {
148 : [_material release];
149 : NSDictionary *spec = [@"{diffuse_map={repeat_s=yes;cube_map=yes};}" propertyList];
150 : _material = [[OOSingleTextureMaterial alloc] initWithName:textureName configuration:spec];
151 : }
152 : }
153 :
154 :
155 : - (float) radius
156 : {
157 : return _radius;
158 : }
159 :
160 :
161 : - (void) setRadius:(float)radius
162 : {
163 : _radius = fabsf(radius);
164 : [self recalculateTransform];
165 : }
166 :
167 :
168 : - (float) levelOfDetail
169 : {
170 : return (float)_lod / LOD_GRANULARITY;
171 : }
172 :
173 :
174 : - (void) setLevelOfDetail:(float)lod
175 : {
176 : _lod = roundf(OOClamp_0_1_f(lod) * LOD_GRANULARITY);
177 : }
178 :
179 :
180 : - (void) calculateLevelOfDetailForViewDistance:(float)distance
181 : {
182 : BOOL simple = [UNIVERSE reducedDetail];
183 : float drawFactor = [[UNIVERSE gameView] viewSize].width / (simple ? 100.0 : 40.0);
184 : float drawRatio2 = drawFactor * _radius / sqrtf(distance); // proportional to size on screen in pixels
185 :
186 : float lod = sqrtf(drawRatio2 * LOD_FACTOR);
187 : if (simple)
188 : {
189 : lod -= 0.5f / LOD_GRANULARITY; // Make LOD transitions earlier.
190 : lod = OOClamp_0_max_f(lod, (LOD_GRANULARITY - 1) / LOD_GRANULARITY); // Don't use highest LOD.
191 : }
192 : [self setLevelOfDetail:lod];
193 : }
194 :
195 :
196 : - (void) renderOpaqueParts
197 : {
198 : assert(_lod < kOOPlanetDataLevels);
199 :
200 : OOSetOpenGLState(OPENGL_STATE_OPAQUE);
201 :
202 : [self renderCommonParts];
203 :
204 : OOVerifyOpenGLState();
205 :
206 : }
207 :
208 :
209 : - (void) renderTranslucentParts
210 : {
211 : assert(_lod < kOOPlanetDataLevels);
212 :
213 : // yes, opaque - necessary changes made later
214 : OOSetOpenGLState(OPENGL_STATE_OPAQUE);
215 :
216 : [self renderCommonParts];
217 :
218 : OOVerifyOpenGLState();
219 :
220 : }
221 :
222 :
223 : - (void) renderTranslucentPartsOnOpaquePass
224 : {
225 : assert(_lod < kOOPlanetDataLevels);
226 :
227 : OO_ENTER_OPENGL();
228 :
229 : // yes, opaque - necessary changes made later
230 : OOSetOpenGLState(OPENGL_STATE_OPAQUE);
231 :
232 : OOGL(glDisable(GL_DEPTH_TEST));
233 : [self renderCommonParts];
234 : OOGL(glEnable(GL_DEPTH_TEST));
235 :
236 : OOVerifyOpenGLState();
237 :
238 : }
239 :
240 :
241 : - (void) renderCommonParts
242 : {
243 : const OOPlanetDataLevel *data = &kPlanetData[_lod];
244 :
245 : OO_ENTER_OPENGL();
246 :
247 : OOGL(glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT));
248 : OOGL(glShadeModel(GL_SMOOTH));
249 :
250 : if (_isAtmosphere)
251 : {
252 : OOGL(glEnable(GL_BLEND));
253 : OOGL(glDepthMask(GL_FALSE));
254 : }
255 : else
256 : {
257 : OOGL(glDisable(GL_BLEND));
258 : }
259 :
260 : // Scale the ball.
261 : OOGLPushModelView();
262 : OOGLMultModelView(_transform);
263 :
264 : [_material apply];
265 :
266 : OOGL(glEnable(GL_LIGHTING));
267 : OOGL(glEnable(GL_TEXTURE_2D));
268 :
269 : #if OO_TEXTURE_CUBE_MAP
270 : if ([_material wantsNormalsAsTextureCoordinates])
271 : {
272 : OOGL(glDisable(GL_TEXTURE_2D));
273 : OOGL(glEnable(GL_TEXTURE_CUBE_MAP));
274 : }
275 : #endif
276 :
277 : OOGL(glDisableClientState(GL_COLOR_ARRAY));
278 :
279 : OOGL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
280 :
281 : OOGL(glVertexPointer(3, GL_FLOAT, 0, kOOPlanetVertices));
282 : if ([_material wantsNormalsAsTextureCoordinates])
283 : {
284 : OOGL(glTexCoordPointer(3, GL_FLOAT, 0, kOOPlanetVertices));
285 : }
286 : else
287 : {
288 : OOGL(glTexCoordPointer(2, GL_FLOAT, 0, kOOPlanetTexCoords));
289 : }
290 :
291 : // FIXME: instead of GL_RESCALE_NORMAL, consider copying and transforming the vertex array for each planet.
292 : OOGL(glEnable(GL_RESCALE_NORMAL));
293 : OOGL(glNormalPointer(GL_FLOAT, 0, kOOPlanetVertices));
294 :
295 : OOGL(glDrawElements(GL_TRIANGLES, data->faceCount*3, data->type, data->indices));
296 :
297 : #ifndef NDEBUG
298 : if ([UNIVERSE wireframeGraphics])
299 : {
300 : OODebugDrawBasisAtOrigin(1.5);
301 : }
302 : #endif
303 :
304 : #if OO_TEXTURE_CUBE_MAP
305 : if ([_material wantsNormalsAsTextureCoordinates])
306 : {
307 : OOGL(glEnable(GL_TEXTURE_2D));
308 : OOGL(glDisable(GL_TEXTURE_CUBE_MAP));
309 : }
310 : #endif
311 :
312 :
313 : OOGLPopModelView();
314 : #ifndef NDEBUG
315 : if (gDebugFlags & DEBUG_DRAW_NORMALS) [self debugDrawNormals];
316 : #endif
317 :
318 : [OOMaterial applyNone];
319 : OOGL(glPopAttrib());
320 :
321 : OOGL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
322 :
323 : }
324 :
325 :
326 : - (BOOL) hasOpaqueParts
327 : {
328 : return !_isAtmosphere;
329 : }
330 :
331 :
332 : - (BOOL) hasTranslucentParts
333 : {
334 : return _isAtmosphere;
335 : }
336 :
337 :
338 : - (GLfloat) collisionRadius
339 : {
340 : return _radius;
341 : }
342 :
343 :
344 : - (GLfloat) maxDrawDistance
345 : {
346 : // FIXME
347 : return INFINITY;
348 : }
349 :
350 :
351 : - (BoundingBox) boundingBox
352 : {
353 : return (BoundingBox){{ -_radius, -_radius, -_radius }, { _radius, _radius, _radius }};
354 : }
355 :
356 :
357 : - (void) setBindingTarget:(id<OOWeakReferenceSupport>)target
358 : {
359 : [_material setBindingTarget:target];
360 : }
361 :
362 :
363 : - (void) dumpSelfState
364 : {
365 : [super dumpSelfState];
366 : OOLog(@"dumpState.planetDrawable", @"radius: %g", [self radius]);
367 : OOLog(@"dumpState.planetDrawable", @"LOD: %g", [self levelOfDetail]);
368 : }
369 :
370 :
371 : - (void) recalculateTransform
372 : {
373 : _transform = OOMatrixForScaleUniform(_radius);
374 : }
375 :
376 :
377 : #ifndef NDEBUG
378 :
379 : - (void) debugDrawNormals
380 : {
381 : OODebugWFState state;
382 :
383 : OO_ENTER_OPENGL();
384 :
385 : state = OODebugBeginWireframe(NO);
386 :
387 : const OOPlanetDataLevel *data = &kPlanetData[_lod];
388 : unsigned i;
389 :
390 : OOGLBEGIN(GL_LINES);
391 : for (i = 0; i < data->vertexCount; i++)
392 : {
393 : /* Fun sphere facts: the normalized coordinates of a point on a sphere at the origin
394 : is equal to the object-space normal of the surface at that point.
395 : Furthermore, we can construct the binormal (a vector pointing westward along the
396 : surface) as the cross product of the normal with the Y axis. (This produces
397 : singularities at the pole, but there have to be singularities according to the
398 : Hairy Ball Theorem.) The tangent (a vector north along the surface) is then the
399 : inverse of the cross product of the normal and binormal.
400 :
401 : (This comment courtesy of the in-development planet shader.)
402 : */
403 : Vector v = make_vector(kOOPlanetVertices[i * 3], kOOPlanetVertices[i * 3 + 1], kOOPlanetVertices[i * 3 + 2]);
404 : Vector n = v;
405 : v = OOVectorMultiplyMatrix(v, _transform);
406 :
407 : glColor3f(0.0f, 1.0f, 1.0f);
408 : GLVertexOOVector(v);
409 : GLVertexOOVector(vector_add(v, vector_multiply_scalar(n, _radius * 0.05)));
410 :
411 : Vector b = cross_product(n, kBasisYVector);
412 : Vector t = vector_flip(true_cross_product(n, b));
413 :
414 : glColor3f(1.0f, 1.0f, 0.0f);
415 : GLVertexOOVector(v);
416 : GLVertexOOVector(vector_add(v, vector_multiply_scalar(t, _radius * 0.03)));
417 :
418 : glColor3f(0.0f, 1.0f, 0.0f);
419 : GLVertexOOVector(v);
420 : GLVertexOOVector(vector_add(v, vector_multiply_scalar(b, _radius * 0.03)));
421 : }
422 : OOGLEND();
423 :
424 : OODebugEndWireframe(state);
425 : }
426 :
427 :
428 : - (NSSet *) allTextures
429 : {
430 : return [[self material] allTextures];
431 : }
432 :
433 : #endif
434 :
435 : @end
436 :
437 : #endif /* NEW_PLANETS */
|