Line data Source code
1 0 : /*
2 :
3 : OOPlanetEntity.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 "OOPlanetEntity.h"
26 :
27 : #if NEW_PLANETS
28 :
29 : #define NEW_ATMOSPHERE 1
30 :
31 : #import "OOPlanetDrawable.h"
32 :
33 : #import "AI.h"
34 : #import "Universe.h"
35 : #import "ShipEntity.h"
36 : #import "PlayerEntity.h"
37 : #import "ShipEntityAI.h"
38 : #import "OOCharacter.h"
39 :
40 : #import "OOMaths.h"
41 : #import "ResourceManager.h"
42 : #import "OOStringParsing.h"
43 : #import "OOCollectionExtractors.h"
44 : #import "OOSystemDescriptionManager.h"
45 :
46 : #import "OOPlanetTextureGenerator.h"
47 : #import "OOStandaloneAtmosphereGenerator.h"
48 : #import "OOSingleTextureMaterial.h"
49 : #import "OOShaderMaterial.h"
50 : #import "OOEntityFilterPredicate.h"
51 : #import "OOGraphicsResetManager.h"
52 : #import "OOStringExpander.h"
53 : #import "OOOpenGLMatrixManager.h"
54 :
55 :
56 : #define OO_TERMINATOR_THRESHOLD_VECTOR_DEFAULT (make_vector(0.105, 0.18, 0.28)) // used to be (0.1, 0.105, 0.12);
57 :
58 :
59 : @interface OOPlanetEntity (Private) <OOGraphicsResetClient>
60 :
61 : - (void) setUpTerrainParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo;
62 : - (void) setUpLandParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo;
63 : - (void) setUpAtmosphereParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo;
64 : - (void) setUpColorParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo isAtmosphere:(BOOL)isAtmosphere;
65 : - (void) setUpTypeParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo;
66 :
67 : @end
68 :
69 :
70 : @implementation OOPlanetEntity
71 :
72 : // this is exclusively called to initialise the main planet.
73 : - (id) initAsMainPlanetForSystem:(OOSystemID)s
74 : {
75 : NSMutableDictionary *planetInfo = [[UNIVERSE generateSystemData:s] mutableCopy];
76 : [planetInfo autorelease];
77 :
78 : [planetInfo oo_setBool:YES forKey:@"mainForLocalSystem"];
79 : if (s != [PLAYER systemID])
80 : {
81 : [planetInfo oo_setBool:YES forKey:@"isMiniature"];
82 : }
83 : return [self initFromDictionary:planetInfo withAtmosphere:[planetInfo oo_boolForKey:@"has_atmosphere" defaultValue:YES] andSeed:[[UNIVERSE systemManager] getRandomSeedForSystem:s inGalaxy:[PLAYER galaxyNumber]] forSystem:s];
84 : }
85 :
86 :
87 : static const double kMesosphere = 10.0 * ATMOSPHERE_DEPTH; // atmosphere effect starts at 10x the height of the clouds
88 :
89 :
90 : - (id) initFromDictionary:(NSDictionary *)dict withAtmosphere:(BOOL)atmosphere andSeed:(Random_Seed)seed forSystem:(OOSystemID)systemID
91 : {
92 : if (dict == nil) dict = [NSDictionary dictionary];
93 : RANROTSeed savedRanrotSeed = RANROTGetFullSeed();
94 :
95 : self = [self init];
96 : if (self == nil) return nil;
97 :
98 : scanClass = CLASS_NO_DRAW;
99 :
100 : NSMutableDictionary *planetInfo = [[UNIVERSE generateSystemData:systemID] mutableCopy];
101 : [planetInfo autorelease];
102 :
103 : [self setUpTypeParametersWithSourceInfo:dict targetInfo:planetInfo];
104 :
105 : [self setUpTerrainParametersWithSourceInfo:dict targetInfo:planetInfo];
106 :
107 :
108 : // Load random seed override.
109 : NSString *seedStr = [dict oo_stringForKey:@"seed"];
110 : if (seedStr != nil)
111 : {
112 : Random_Seed overrideSeed = RandomSeedFromString(seedStr);
113 : if (!is_nil_seed(overrideSeed)) seed = overrideSeed;
114 : else OOLogERR(@"planet.fromDict", @"could not interpret \"%@\" as planet seed, using default.", seedStr);
115 : }
116 :
117 : // Generate various planet info.
118 : seed_for_planet_description(seed);
119 :
120 : _name = nil;
121 : [self setName:OOExpand([dict oo_stringForKey:KEY_PLANETNAME defaultValue:[planetInfo oo_stringForKey:KEY_PLANETNAME defaultValue:@"%H"]])];
122 :
123 : int radius_km = [dict oo_intForKey:KEY_RADIUS defaultValue:[planetInfo oo_intForKey:KEY_RADIUS]];
124 : collision_radius = radius_km * 10.0; // Scale down by a factor of 100
125 : OOTechLevelID techLevel = [dict oo_intForKey:KEY_TECHLEVEL defaultValue:[planetInfo oo_intForKey:KEY_TECHLEVEL]];
126 :
127 : if (techLevel > 14) techLevel = 14;
128 : _shuttlesOnGround = 1 + techLevel / 2;
129 : _shuttleLaunchInterval = 3600.0 / (double)_shuttlesOnGround; // All are launched in one hour.
130 : _lastLaunchTime = [UNIVERSE getTime] + 30.0 - _shuttleLaunchInterval; // launch 30s after player enters universe.
131 : // make delay > 0 to allow scripts adding a station nearby.
132 :
133 : int percent_land = [planetInfo oo_intForKey:@"percent_land" defaultValue:24 + (gen_rnd_number() % 48)];
134 : [planetInfo setObject:[NSNumber numberWithFloat:0.01 * percent_land] forKey:@"land_fraction"];
135 :
136 : int percent_ice = [planetInfo oo_intForKey:@"percent_ice" defaultValue:5];
137 : [planetInfo setObject:[NSNumber numberWithFloat:0.01 * percent_ice] forKey:@"polar_fraction"];
138 :
139 :
140 : RNG_Seed savedRndSeed = currentRandomSeed();
141 :
142 : _planetDrawable = [[OOPlanetDrawable alloc] init];
143 :
144 : _terminatorThresholdVector = [planetInfo oo_vectorForKey:@"terminator_threshold_vector" defaultValue:OO_TERMINATOR_THRESHOLD_VECTOR_DEFAULT];
145 :
146 : // Load material parameters, including atmosphere.
147 : RANROTSeed planetNoiseSeed = RANROTGetFullSeed();
148 : [planetInfo setObject:[NSValue valueWithBytes:&planetNoiseSeed objCType:@encode(RANROTSeed)] forKey:@"noise_map_seed"];
149 : [self setUpLandParametersWithSourceInfo:dict targetInfo:planetInfo];
150 :
151 : _airColor = nil; // default to no air
152 : _airColorMixRatio = 0.5f;
153 : _airDensity = 0.75f;
154 :
155 : _illuminationColor = [[planetInfo objectForKey:@"illumination_color"] retain];
156 :
157 : #if NEW_ATMOSPHERE
158 : if (atmosphere)
159 : {
160 : // shader atmosphere always has a radius of collision_radius + ATMOSPHERE_DEPTH. For texture atmosphere, we need to check
161 : // if a shader atmosphere is also used. If yes, set its radius to just cover the planet so that it doesn't conflict with
162 : // the shader atmosphere at the planet edges. If no shader atmosphere is used, then set it to the standard radius
163 : double atmosphereRadius = [UNIVERSE detailLevel] >= DETAIL_LEVEL_SHADERS ? collision_radius : collision_radius + ATMOSPHERE_DEPTH;
164 : _atmosphereDrawable = [[OOPlanetDrawable atmosphereWithRadius:atmosphereRadius] retain];
165 : _atmosphereShaderDrawable = [[OOPlanetDrawable atmosphereWithRadius:collision_radius + ATMOSPHERE_DEPTH] retain];
166 :
167 : // convert the atmosphere settings to generic 'material parameters'
168 : percent_land = 100 - [dict oo_intForKey:@"percent_cloud" defaultValue:100 - (3 + (gen_rnd_number() & 31)+(gen_rnd_number() & 31))];
169 : [planetInfo setObject:[NSNumber numberWithFloat:0.01 * percent_land] forKey:@"cloud_fraction"];
170 : [self setUpAtmosphereParametersWithSourceInfo:dict targetInfo:planetInfo];
171 : // planetInfo now contains a valid air_color
172 : _airColor = [[planetInfo objectForKey:@"air_color"] retain];
173 : _airColorMixRatio = [planetInfo oo_floatForKey:@"air_color_mix_ratio"];
174 :
175 : _airDensity = OOClamp_0_1_f([planetInfo oo_floatForKey:@"air_density"]);
176 : // OOLog (@"planet.debug",@" translated air colour:%@ cloud colour:%@ polar cloud color:%@", [_airColor rgbaDescription],[(OOColor *)[planetInfo objectForKey:@"cloud_color"] rgbaDescription],[(OOColor *)[planetInfo objectForKey:@"polar_cloud_color"] rgbaDescription]);
177 :
178 : _materialParameters = [planetInfo dictionaryWithValuesForKeys:[NSArray arrayWithObjects:@"cloud_fraction", @"air_color", @"air_color_mix_ratio", @"air_density", @"cloud_color", @"polar_cloud_color", @"cloud_alpha", @"land_fraction", @"land_color", @"sea_color", @"polar_land_color", @"polar_sea_color", @"noise_map_seed", @"economy", @"polar_fraction", @"isMiniature", @"perlin_3d", @"terminator_threshold_vector", nil]];
179 : }
180 : else
181 : #else
182 : // NEW_ATMOSPHERE is 0? still differentiate between normal planets and moons.
183 : if (atmosphere)
184 : {
185 : _atmosphereDrawable = [[OOPlanetDrawable atmosphereWithRadius:collision_radius + ATMOSPHERE_DEPTH] retain];
186 : _airColor = [[OOColor colorWithRed:0.8f green:0.8f blue:0.9f alpha:1.0f] retain];
187 : }
188 : if (YES) // create _materialParameters when NEW_ATMOSPHERE is set to 0
189 : #endif
190 : {
191 : _materialParameters = [planetInfo dictionaryWithValuesForKeys:[NSArray arrayWithObjects:@"land_fraction", @"land_color", @"sea_color", @"polar_land_color", @"polar_sea_color", @"noise_map_seed", @"economy", @"polar_fraction", @"isMiniature", @"perlin_3d", @"terminator_threshold_vector", @"illumination_color", nil]];
192 : }
193 : [_materialParameters retain];
194 :
195 : _mesopause2 = (atmosphere) ? (kMesosphere + collision_radius) * (kMesosphere + collision_radius) : 0.0;
196 :
197 : _normSpecMapName = [[dict oo_stringForKey:@"texture_normspec"] retain]; // must be set up before _textureName
198 :
199 : _textureName = [[dict oo_stringForKey:@"texture"] retain];
200 : [self setUpPlanetFromTexture:_textureName];
201 : [_planetDrawable setRadius:collision_radius];
202 :
203 : // Orientation should be handled by the code that calls this planetEntity. Starting with a default value anyway.
204 : orientation = (Quaternion){ M_SQRT1_2, M_SQRT1_2, 0, 0 };
205 : _atmosphereOrientation = kIdentityQuaternion;
206 : _rotationAxis = vector_up_from_quaternion(orientation);
207 :
208 : // set speed of rotation.
209 : if ([dict objectForKey:@"rotational_velocity"])
210 : {
211 : _rotationalVelocity = [dict oo_floatForKey:@"rotational_velocity" defaultValue:0.01f * randf()]; // 0.0 .. 0.01 avr 0.005
212 : }
213 : else
214 : {
215 : _rotationalVelocity = [planetInfo oo_floatForKey:@"rotation_speed" defaultValue:0.005f * randf()]; // 0.0 .. 0.005 avr 0.0025
216 : _rotationalVelocity *= [planetInfo oo_floatForKey:@"rotation_speed_factor" defaultValue:1.0f];
217 : }
218 :
219 : _atmosphereRotationalVelocity = [dict oo_floatForKey:@"atmosphere_rotational_velocity" defaultValue:0.01f * randf()];
220 :
221 : // set energy
222 : energy = collision_radius * 1000.0f;
223 :
224 : setRandomSeed(savedRndSeed);
225 : RANROTSetFullSeed(savedRanrotSeed);
226 :
227 : // rotate planet based on current time, needs to be done here - backported from PlanetEntity.
228 : int deltaT = floor(fmod([PLAYER clockTimeAdjusted], 86400));
229 : quaternion_rotate_about_axis(&orientation, _rotationAxis, _rotationalVelocity * deltaT);
230 : quaternion_rotate_about_axis(&_atmosphereOrientation, kBasisYVector, _atmosphereRotationalVelocity * deltaT);
231 :
232 :
233 : #ifdef OO_DUMP_PLANETINFO
234 : #define CPROP(PROP) OOLog(@"planetinfo.record",@#PROP " = %@;",[(OOColor *)[planetInfo objectForKey:@#PROP] descriptionComponents]);
235 : #define FPROP(PROP) OOLog(@"planetinfo.record",@#PROP " = %f;",[planetInfo oo_floatForKey:@"" #PROP]);
236 : CPROP(air_color);
237 : CPROP(illumination_color);
238 : FPROP(air_color_mix_ratio);
239 : FPROP(cloud_alpha);
240 : CPROP(cloud_color);
241 : FPROP(cloud_fraction);
242 : CPROP(land_color);
243 : FPROP(land_fraction);
244 : CPROP(polar_cloud_color);
245 : CPROP(polar_land_color);
246 : CPROP(polar_sea_color);
247 : CPROP(sea_color);
248 : OOLog(@"planetinfo.record",@"rotation_speed = %f",_rotationalVelocity);
249 : #endif
250 :
251 : [self setStatus:STATUS_ACTIVE];
252 :
253 : [[OOGraphicsResetManager sharedManager] registerClient:self];
254 :
255 : return self;
256 : }
257 :
258 :
259 : static Vector RandomHSBColor(void)
260 : {
261 : return (Vector)
262 : {
263 : gen_rnd_number() / 256.0,
264 : gen_rnd_number() / 256.0,
265 : 0.5 + gen_rnd_number() / 512.0
266 : };
267 : }
268 :
269 :
270 : static Vector LighterHSBColor(Vector c)
271 : {
272 : return (Vector)
273 : {
274 : c.x,
275 : c.y * 0.25f,
276 : 1.0f - (c.z * 0.1f)
277 : };
278 : }
279 :
280 :
281 : static Vector HSBColorWithColor(OOColor *color)
282 : {
283 : OOHSBAComponents c = [color hsbaComponents];
284 : return (Vector){ c.h/360, c.s, c.b };
285 : }
286 :
287 :
288 : static OOColor *ColorWithHSBColor(Vector c)
289 : {
290 : return [OOColor colorWithHue:c.x saturation:c.y brightness:c.z alpha:1.0];
291 : }
292 :
293 :
294 : - (void) setUpTypeParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo
295 : {
296 : [targetInfo oo_setBool:[sourceInfo oo_boolForKey:@"mainForLocalSystem"] forKey:@"mainForLocalSystem"];
297 : [targetInfo oo_setBool:[sourceInfo oo_boolForKey:@"isMiniature"] forKey:@"isMiniature"];
298 :
299 : }
300 :
301 :
302 : - (void) setUpTerrainParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo
303 : {
304 : NSArray *keys = [NSArray arrayWithObjects:@"atmosphere_rotational_velocity",@"rotational_velocity",@"cloud_alpha",@"has_atmosphere",@"percent_cloud",@"percent_ice",@"percent_land",@"radius",@"seed",nil];
305 : NSString *key = nil;
306 : foreach (key, keys) {
307 : id sval = [sourceInfo objectForKey:key];
308 : if (sval != nil) {
309 : [targetInfo setObject:sval forKey:key];
310 : }
311 : }
312 :
313 : }
314 :
315 :
316 : - (void) setUpLandParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo
317 : {
318 : [self setUpColorParametersWithSourceInfo:sourceInfo targetInfo:targetInfo isAtmosphere:NO];
319 : }
320 :
321 :
322 : - (void) setUpAtmosphereParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo
323 : {
324 : [self setUpColorParametersWithSourceInfo:sourceInfo targetInfo:targetInfo isAtmosphere:YES];
325 : }
326 :
327 :
328 : - (void) setUpColorParametersWithSourceInfo:(NSDictionary *)sourceInfo targetInfo:(NSMutableDictionary *)targetInfo isAtmosphere:(BOOL)isAtmosphere
329 : {
330 : // Stir the PRNG fourteen times for backwards compatibility.
331 : unsigned i;
332 : for (i = 0; i < 14; i++)
333 : {
334 : gen_rnd_number();
335 : }
336 :
337 : Vector landHSB, seaHSB, landPolarHSB, seaPolarHSB, illumHSB;
338 : Vector terminatorThreshold;
339 : OOColor *color;
340 :
341 : landHSB = RandomHSBColor();
342 :
343 : if (!isAtmosphere)
344 : {
345 : do
346 : {
347 : seaHSB = RandomHSBColor();
348 : }
349 : while (dot_product(landHSB, seaHSB) > .80f); // make sure land and sea colors differ significantly
350 :
351 : // saturation bias - avoids really grey oceans
352 : if (seaHSB.y < 0.22f) seaHSB.y = seaHSB.y * 0.3f + 0.2f;
353 : // brightness bias - avoids really bright landmasses
354 : if (landHSB.z > 0.66f) landHSB.z = 0.66f;
355 :
356 : // planetinfo.plist overrides
357 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"land_color"]];
358 : if (color != nil) landHSB = HSBColorWithColor(color);
359 : else ScanVectorFromString([sourceInfo oo_stringForKey:@"land_hsb_color"], &landHSB);
360 :
361 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"sea_color"]];
362 : if (color != nil) seaHSB = HSBColorWithColor(color);
363 : else ScanVectorFromString([sourceInfo oo_stringForKey:@"sea_hsb_color"], &seaHSB);
364 :
365 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"illumination_color"]];
366 : if (color != nil) illumHSB = HSBColorWithColor(color);
367 : else
368 : {
369 : NSString *illumHSBColorString = [sourceInfo oo_stringForKey:@"illumination_hsb_color"];
370 : if (illumHSBColorString) ScanVectorFromString(illumHSBColorString, &illumHSB);
371 : else illumHSB = HSBColorWithColor([OOColor colorWithRed:0.8f green:0.8f blue:0.4f alpha:1.0f]);
372 : }
373 :
374 : // polar areas are brighter but have less colour (closer to white)
375 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"polar_land_color"]];
376 : if (color != nil)
377 : {
378 : landPolarHSB = HSBColorWithColor(color);
379 : }
380 : else
381 : {
382 : landPolarHSB = LighterHSBColor(landHSB);
383 : }
384 :
385 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"polar_sea_color"]];
386 : if (color != nil)
387 : {
388 : seaPolarHSB = HSBColorWithColor(color);
389 : }
390 : else
391 : {
392 : seaPolarHSB = LighterHSBColor(seaHSB);
393 : }
394 :
395 : [targetInfo setObject:ColorWithHSBColor(landHSB) forKey:@"land_color"];
396 : [targetInfo setObject:ColorWithHSBColor(seaHSB) forKey:@"sea_color"];
397 : [targetInfo setObject:ColorWithHSBColor(landPolarHSB) forKey:@"polar_land_color"];
398 : [targetInfo setObject:ColorWithHSBColor(seaPolarHSB) forKey:@"polar_sea_color"];
399 : [targetInfo setObject:ColorWithHSBColor(illumHSB) forKey:@"illumination_color"];
400 : }
401 : else
402 : {
403 : landHSB = RandomHSBColor(); // NB: randomcolor is called twice to make the cloud colour similar to the old one.
404 :
405 : // add a cloud_color tinge to sky blue({0.66, 0.3, 1}).
406 : seaHSB = vector_add(landHSB,((Vector){1.333, 0.6, 2})); // 1 part cloud, 2 parts sky blue
407 : scale_vector(&seaHSB, 0.333);
408 :
409 : float cloudAlpha = OOClamp_0_1_f([sourceInfo oo_floatForKey:@"cloud_alpha" defaultValue:1.0f]);
410 : [targetInfo setObject:[NSNumber numberWithFloat:cloudAlpha] forKey:@"cloud_alpha"];
411 :
412 : // planetinfo overrides
413 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"air_color"]];
414 : if (color != nil) seaHSB = HSBColorWithColor(color);
415 :
416 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"cloud_color"]];
417 : if (color != nil) landHSB = HSBColorWithColor(color);
418 :
419 : // polar areas: brighter, less saturation
420 : landPolarHSB = vector_add(landHSB,LighterHSBColor(landHSB));
421 : scale_vector(&landPolarHSB, 0.5);
422 :
423 : color = [OOColor colorWithDescription:[sourceInfo objectForKey:@"polar_cloud_color"]];
424 : if (color != nil) landPolarHSB = HSBColorWithColor(color);
425 :
426 : [targetInfo setObject:ColorWithHSBColor(seaHSB) forKey:@"air_color"];
427 : [targetInfo setObject:ColorWithHSBColor(landHSB) forKey:@"cloud_color"];
428 : [targetInfo setObject:ColorWithHSBColor(landPolarHSB) forKey:@"polar_cloud_color"];
429 : [targetInfo setObject:[NSNumber numberWithFloat:[sourceInfo oo_floatForKey:@"air_color_mix_ratio"]] forKey:@"air_color_mix_ratio"];
430 : }
431 : terminatorThreshold = [sourceInfo oo_vectorForKey:@"terminator_threshold_vector" defaultValue:OO_TERMINATOR_THRESHOLD_VECTOR_DEFAULT];
432 : [targetInfo setObject:[NSString stringWithFormat:@"%f %f %f", terminatorThreshold.x, terminatorThreshold.y, terminatorThreshold.z] forKey:@"terminator_threshold_vector"];
433 : }
434 :
435 :
436 : - (id) initAsMiniatureVersionOfPlanet:(OOPlanetEntity *)planet
437 : {
438 : // Nasty, nasty. I'd really prefer to have a separate entity class for this.
439 : if (planet == nil)
440 : {
441 : [self release];
442 : return nil;
443 : }
444 :
445 : self = [self init];
446 : if (self == nil) return nil;
447 :
448 : scanClass = CLASS_NO_DRAW;
449 : [self setStatus:STATUS_COCKPIT_DISPLAY];
450 :
451 : collision_radius = planet->collision_radius * PLANET_MINIATURE_FACTOR;
452 : orientation = planet->orientation;
453 : _rotationAxis = planet->_rotationAxis;
454 : _atmosphereOrientation = planet->_atmosphereOrientation;
455 : _rotationalVelocity = 0.04;
456 :
457 : _terminatorThresholdVector = planet->_terminatorThresholdVector;
458 :
459 : _miniature = YES;
460 :
461 : _planetDrawable = [planet->_planetDrawable copy];
462 : [_planetDrawable setRadius:collision_radius];
463 :
464 : // FIXME: in old planet code, atmosphere (if textured) is set to 0.6 alpha.
465 : _atmosphereDrawable = [planet->_atmosphereDrawable copy];
466 : _atmosphereShaderDrawable = [planet->_atmosphereShaderDrawable copy];
467 : [_atmosphereDrawable setRadius:collision_radius + ATMOSPHERE_DEPTH * PLANET_MINIATURE_FACTOR * 2.0]; //not to scale: invisible otherwise
468 : if (_atmosphereShaderDrawable) [_atmosphereShaderDrawable setRadius:collision_radius + ATMOSPHERE_DEPTH * PLANET_MINIATURE_FACTOR * 2.0];
469 :
470 : [_planetDrawable setLevelOfDetail:0.8f];
471 : [_atmosphereDrawable setLevelOfDetail:0.8f];
472 : if (_atmosphereShaderDrawable) [_atmosphereShaderDrawable setLevelOfDetail:0.8f];
473 :
474 : return self;
475 : }
476 :
477 :
478 : - (void) dealloc
479 : {
480 : DESTROY(_name);
481 : DESTROY(_planetDrawable);
482 : DESTROY(_atmosphereDrawable);
483 : DESTROY(_atmosphereShaderDrawable);
484 : DESTROY(_airColor);
485 : DESTROY(_illuminationColor);
486 : DESTROY(_materialParameters);
487 : DESTROY(_textureName);
488 : DESTROY(_normSpecMapName);
489 :
490 : [[OOGraphicsResetManager sharedManager] unregisterClient:self];
491 :
492 : [super dealloc];
493 : }
494 :
495 :
496 : - (NSString*) descriptionComponents
497 : {
498 : return [NSString stringWithFormat:@"position: %@ radius: %g m", HPVectorDescription([self position]), [self radius]];
499 : }
500 :
501 :
502 : - (void) setOrientation:(Quaternion) quat
503 : {
504 : [super setOrientation: quat];
505 : _rotationAxis = vector_up_from_quaternion(quat);
506 : }
507 :
508 :
509 : - (double) radius
510 : {
511 : return collision_radius;
512 : }
513 :
514 :
515 : - (OOStellarBodyType) planetType
516 : {
517 : if (_miniature) return STELLAR_TYPE_MINIATURE;
518 : if (_atmosphereDrawable != nil) return STELLAR_TYPE_NORMAL_PLANET;
519 : return STELLAR_TYPE_MOON;
520 : }
521 :
522 :
523 : - (instancetype) miniatureVersion
524 : {
525 : return [[[[self class] alloc] initAsMiniatureVersionOfPlanet:self] autorelease];
526 : }
527 :
528 :
529 : - (void) update:(OOTimeDelta) delta_t
530 : {
531 : [super update:delta_t];
532 :
533 : if (EXPECT(!_miniature))
534 : {
535 : BOOL canDrawShaderAtmosphere = _atmosphereShaderDrawable && [UNIVERSE detailLevel] >= DETAIL_LEVEL_SHADERS;
536 : if (EXPECT_NOT(_atmosphereDrawable && cam_zero_distance < _mesopause2))
537 : {
538 : NSAssert(_airColor != nil, @"Expected a non-nil air colour for normal planet. Exiting.");
539 : double alt = (sqrt(cam_zero_distance) - collision_radius) / kMesosphere; // the viewpoint altitude
540 : double trueAlt = (sqrt(zero_distance) - collision_radius) / kMesosphere; // the actual ship altitude
541 : // if at long distance external view, rotating the camera could potentially end up with it being
542 : // at negative altitude. Since we know we are already inside the atmosphere at this point, just make sure
543 : // that altitude is kept to a minimum positive value to avoid sudden black skies
544 : if (alt <= 0.0) alt = 1e-4;
545 : if (EXPECT_NOT(alt > 0 && alt <= 1.0)) // ensure aleph is clamped between 0 and 1
546 : {
547 : double aleph = 1.0 - alt;
548 : double aleph2 = aleph * aleph;
549 :
550 : // night sky, reddish flash on entering the atmosphere, low light pollution otherwhise
551 : OOColor *mixColor = [OOColor colorWithRed:(EXPECT_NOT(alt > 0.98) ? 30.0f : 0.1f)
552 : green:0.1f
553 : blue:0.1f
554 : alpha:aleph];
555 :
556 : // occlusion rate: .9 is 18 degrees after the terminus, where twilight ends.
557 : // 1 is the terminus, 1.033 is 6 degrees before the terminus, where the sky begins to redden
558 : double rate = ([PLAYER occlusionLevel] - 0.97)/0.06; // from 0.97 to 1.03
559 :
560 : if (EXPECT(rate <= 1.0 && rate > 0.0))
561 : {
562 : mixColor = [mixColor blendedColorWithFraction:rate ofColor:_airColor];
563 : // TODO: properly calculated pink sky - needs to depend on sun's angular size,
564 : // and its angular height on the horizon.
565 : /*
566 : rate -= 0.7;
567 : if (rate >= 0.0) // pink sky!
568 : {
569 : rate = 0.5 - (fabs(rate - 0.15) / 0.3); // at most a 50% blend!
570 : mixColor = [mixColor blendedColorWithFraction:rate ofColor:[OOColor colorWithRed:0.6
571 : green:0.1
572 : blue:0.0
573 : alpha:aleph]];
574 : }
575 : */
576 : }
577 : else
578 : {
579 : if (PLAYER->isSunlit && _airColor != nil) mixColor = _airColor;
580 : }
581 : [UNIVERSE setSkyColorRed:[mixColor redComponent] * aleph2
582 : green:[mixColor greenComponent] * aleph2
583 : blue:[mixColor blueComponent] * aleph
584 : alpha:aleph];
585 : double atmosphereRadius = canDrawShaderAtmosphere ? collision_radius : collision_radius + (ATMOSPHERE_DEPTH * alt);
586 : [_atmosphereDrawable setRadius:atmosphereRadius];
587 : if (_atmosphereShaderDrawable) [_atmosphereShaderDrawable setRadius:collision_radius + (ATMOSPHERE_DEPTH * alt)];
588 : // apply air resistance for the ship, not the camera. Although setSkyColorRed
589 : // has already set the air resistance to aleph, override it immediately
590 : [UNIVERSE setAirResistanceFactor:OOClamp_0_1_f(1.0 - trueAlt)];
591 : }
592 : }
593 : else
594 : {
595 : if (EXPECT_NOT([_atmosphereDrawable radius] < collision_radius + ATMOSPHERE_DEPTH))
596 : {
597 : [_atmosphereDrawable setRadius:collision_radius + ATMOSPHERE_DEPTH];
598 : if (_atmosphereShaderDrawable) [_atmosphereShaderDrawable setRadius:collision_radius + ATMOSPHERE_DEPTH];
599 : }
600 : if (canDrawShaderAtmosphere && [_atmosphereDrawable radius] != collision_radius)
601 : {
602 : // if shader atmo is in use, force texture atmo radius to just collision_radius for cosmetic purposes
603 : [_atmosphereDrawable setRadius:collision_radius];
604 : }
605 : if ([PLAYER findNearestPlanet] == self) // ensure no problems in case of more than one planets
606 : {
607 : [UNIVERSE setAirResistanceFactor:0.0f]; // out of atmosphere - no air friction
608 : }
609 : }
610 :
611 : double time = [UNIVERSE getTime];
612 :
613 : if (_shuttlesOnGround > 0 && time > _lastLaunchTime + _shuttleLaunchInterval) [self launchShuttle];
614 : }
615 :
616 : quaternion_rotate_about_axis(&orientation, _rotationAxis, _rotationalVelocity * delta_t);
617 : // atmosphere orientation is relative to the orientation of the planet
618 : quaternion_rotate_about_axis(&_atmosphereOrientation, kBasisYVector, _atmosphereRotationalVelocity * delta_t);
619 :
620 : [self orientationChanged];
621 :
622 : // FIXME: update atmosphere rotation
623 : }
624 :
625 :
626 : - (BOOL) isFinishedLoading
627 : {
628 : OOMaterial *material = [self material];
629 : if (material != nil && ![material isFinishedLoading]) return NO;
630 : material = [self atmosphereMaterial];
631 : if (material != nil && ![material isFinishedLoading]) return NO;
632 : material = [self atmosphereShaderMaterial];
633 : if (material != nil && ![material isFinishedLoading]) return NO;
634 : return YES;
635 : }
636 :
637 :
638 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
639 : {
640 : BOOL canDrawShaderAtmosphere = _atmosphereShaderDrawable && [UNIVERSE detailLevel] >= DETAIL_LEVEL_SHADERS;
641 :
642 : if ([UNIVERSE breakPatternHide]) return; // DON'T DRAW
643 : if (_miniature && ![self isFinishedLoading]) return; // For responsiveness, don't block to draw as miniature.
644 :
645 : // too far away to be drawn
646 : if (magnitude(cameraRelativePosition) > [self radius]*3000) {
647 : return;
648 : }
649 : if (![UNIVERSE viewFrustumIntersectsSphereAt:cameraRelativePosition withRadius:([self radius] + ATMOSPHERE_DEPTH)])
650 : {
651 : // Don't draw
652 : return;
653 : }
654 :
655 : if ([UNIVERSE wireframeGraphics]) OOGLWireframeModeOn();
656 :
657 : if (!_miniature)
658 : {
659 : [_planetDrawable calculateLevelOfDetailForViewDistance:cam_zero_distance];
660 : [_atmosphereDrawable setLevelOfDetail:[_planetDrawable levelOfDetail]];
661 : if (canDrawShaderAtmosphere) [_atmosphereShaderDrawable setLevelOfDetail:[_planetDrawable levelOfDetail]];
662 : }
663 :
664 : // 500km squared
665 : // if (magnitude2(cameraRelativePosition) > 250000000000.0)
666 : /* Temporarily for 1.82 make this branch unconditional. There's an
667 : * odd change in appearance when crossing this boundary, which can
668 : * be quite noticeable. There don't appear to be close-range
669 : * problems with doing it this way all the time, though it's not
670 : * ideal. - CIM */
671 : {
672 : /* at this distance the atmosphere is too close to the planet
673 : * for a 24-bit depth buffer to reliably distinguish the two,
674 : * so cheat and draw the atmosphere on the opaque pass: it's
675 : * far enough away that painter's algorithm should do fine */
676 : [_planetDrawable renderOpaqueParts];
677 : if (_atmosphereDrawable != nil)
678 : {
679 : OOGLPushModelView();
680 : OOGLMultModelView(OOMatrixForQuaternionRotation(_atmosphereOrientation));
681 : [_atmosphereDrawable renderTranslucentPartsOnOpaquePass];
682 : if (canDrawShaderAtmosphere) [_atmosphereShaderDrawable renderTranslucentPartsOnOpaquePass];
683 : OOGLPopModelView();
684 : }
685 : }
686 : #if OOLITE_HAVE_FIXED_THE_ABOVE_DESCRIBED_BUG_WHICH_WE_HAVENT
687 : else
688 : {
689 : /* At close range we can do this properly and draw the
690 : * atmosphere on the transparent pass */
691 : if (translucent)
692 : {
693 : if (_atmosphereDrawable != nil)
694 : {
695 : OOGLPushModelView();
696 : OOGLMultModelView(OOMatrixForQuaternionRotation(_atmosphereOrientation));
697 : [_atmosphereDrawable renderTranslucentParts];
698 : if (canDrawShaderAtmosphere) [_atmosphereShaderDrawable renderTranslucentParts];
699 : OOGLPopModelView();
700 : }
701 : }
702 : else
703 : {
704 : [_planetDrawable renderOpaqueParts];
705 : }
706 : }
707 : #endif
708 :
709 : if ([UNIVERSE wireframeGraphics]) OOGLWireframeModeOff();
710 : }
711 :
712 :
713 : - (BOOL) checkCloseCollisionWith:(Entity *)other
714 : {
715 : if (!other)
716 : return NO;
717 : if (other->isShip)
718 : {
719 : ShipEntity *ship = (ShipEntity *)other;
720 : if ([ship behaviour] == BEHAVIOUR_LAND_ON_PLANET)
721 : {
722 : return NO;
723 : }
724 : }
725 :
726 : return YES;
727 : }
728 :
729 :
730 : - (BOOL) planetHasStation
731 : {
732 : // find the nearest station...
733 : ShipEntity *station = nil;
734 : station = [UNIVERSE nearestShipMatchingPredicate:IsStationPredicate
735 : parameter:nil
736 : relativeToEntity:self];
737 :
738 : if (station && HPdistance([station position], position) < 4 * collision_radius) // there is a station in range.
739 : {
740 : return YES;
741 : }
742 : return NO;
743 : }
744 :
745 :
746 : - (void) launchShuttle
747 : {
748 : if (_shuttlesOnGround == 0)
749 : {
750 : return;
751 : }
752 : if ([PLAYER status] == STATUS_START_GAME)
753 : {
754 : // don't launch if game not started
755 : return;
756 : }
757 : if (self != [UNIVERSE planet] && ![self planetHasStation])
758 : {
759 : // don't launch shuttles when no station is nearby.
760 : _shuttlesOnGround = 0;
761 : return;
762 : }
763 :
764 : Quaternion q1;
765 : quaternion_set_random(&q1);
766 : float start_distance = collision_radius + 125.0f;
767 : HPVector launch_pos = HPvector_add(position, vectorToHPVector(vector_multiply_scalar(vector_forward_from_quaternion(q1), start_distance)));
768 :
769 : ShipEntity *shuttle_ship = [UNIVERSE newShipWithRole:@"shuttle"]; // retain count = 1
770 : if (shuttle_ship)
771 : {
772 : if ([[shuttle_ship crew] count] == 0)
773 : {
774 : [shuttle_ship setSingleCrewWithRole:@"trader"];
775 : }
776 :
777 : [shuttle_ship setPosition:launch_pos];
778 : [shuttle_ship setOrientation:q1];
779 :
780 : [shuttle_ship setScanClass: CLASS_NEUTRAL];
781 : [shuttle_ship setCargoFlag:CARGO_FLAG_FULL_PLENTIFUL];
782 : [shuttle_ship switchAITo:@"oolite-shuttleAI.js"];
783 : [UNIVERSE addEntity:shuttle_ship]; // STATUS_IN_FLIGHT, AI state GLOBAL
784 : _shuttlesOnGround--;
785 : _lastLaunchTime = [UNIVERSE getTime];
786 :
787 : [shuttle_ship release];
788 : }
789 : }
790 :
791 :
792 : - (void) welcomeShuttle:(ShipEntity *)shuttle
793 : {
794 : _shuttlesOnGround++;
795 : }
796 :
797 :
798 : - (BOOL) isPlanet
799 : {
800 : return YES;
801 : }
802 :
803 :
804 : - (BOOL) isVisible
805 : {
806 : return YES;
807 : }
808 :
809 :
810 : - (double) rotationalVelocity
811 : {
812 : return _rotationalVelocity;
813 : }
814 :
815 :
816 : - (void) setRotationalVelocity:(double) v
817 : {
818 : if ([self hasAtmosphere])
819 : {
820 : // FIXME: change atmosphere rotation speed proportionally
821 : }
822 : _rotationalVelocity = v;
823 : }
824 :
825 :
826 : // this method is visible to shader bindings, hence it returns vector
827 : - (Vector) airColorAsVector
828 : {
829 : float r, g, b, a;
830 : [_airColor getRed:&r green:&g blue:&b alpha:&a];
831 : return make_vector(r, g, b); // don't care about a
832 : }
833 :
834 :
835 : - (OOColor *) airColor
836 : {
837 : return _airColor;
838 : }
839 :
840 :
841 : - (void) setAirColor:(OOColor *) newColor
842 : {
843 : if (newColor)
844 : {
845 : [_airColor release];
846 : _airColor = [newColor retain];
847 : }
848 : }
849 :
850 :
851 : // this method is visible to shader bindings, hence it returns vector
852 : - (Vector) illuminationColorAsVector
853 : {
854 : float r, g, b, a;
855 : [_illuminationColor getRed:&r green:&g blue:&b alpha:&a];
856 : return make_vector(r, g, b); // don't care about a
857 : }
858 :
859 :
860 : - (OOColor *) illuminationColor
861 : {
862 : return _illuminationColor;
863 : }
864 :
865 :
866 : - (void) setIlluminationColor:(OOColor *) newColor
867 : {
868 : if (newColor)
869 : {
870 : [_illuminationColor release];
871 : _illuminationColor = [newColor retain];
872 : }
873 : }
874 :
875 :
876 : // visible to shader bindings
877 : - (float) airColorMixRatio
878 : {
879 : return _airColorMixRatio;
880 : }
881 :
882 :
883 : - (void) setAirColorMixRatio:(float) newRatio
884 : {
885 : _airColorMixRatio = OOClamp_0_1_f(newRatio);
886 : }
887 :
888 :
889 : // visible to shader bindings
890 : - (float) airDensity
891 : {
892 : return _airDensity;
893 : }
894 :
895 :
896 : - (void) setAirDensity: (float)newDensity
897 : {
898 : _airDensity = OOClamp_0_1_f(newDensity);
899 : }
900 :
901 :
902 : - (void) setTerminatorThresholdVector:(Vector) newTerminatorThresholdVector
903 : {
904 : _terminatorThresholdVector.x = newTerminatorThresholdVector.x;
905 : _terminatorThresholdVector.y = newTerminatorThresholdVector.y;
906 : _terminatorThresholdVector.z = newTerminatorThresholdVector.z;
907 : }
908 :
909 :
910 : - (Vector) terminatorThresholdVector
911 : {
912 : return _terminatorThresholdVector;
913 : }
914 :
915 :
916 : - (BOOL) hasAtmosphere
917 : {
918 : return _atmosphereDrawable != nil;
919 : }
920 :
921 :
922 : // FIXME: need material model.
923 : - (NSString *) textureFileName
924 : {
925 : return [_planetDrawable textureName];
926 : }
927 :
928 :
929 : - (void)resetGraphicsState
930 : {
931 : // reset the texture if graphics mode changes
932 : [self setUpPlanetFromTexture:_textureName];
933 : }
934 :
935 :
936 : - (void) setTextureFileName:(NSString *)textureName
937 : {
938 : BOOL isMoon = _atmosphereDrawable == nil;
939 :
940 : OOTexture *diffuseMap = nil;
941 : OOTexture *normalMap = nil;
942 : NSDictionary *macros = nil;
943 : NSDictionary *materialDefaults = [ResourceManager materialDefaults];
944 :
945 : #if OO_SHADERS
946 : OOGraphicsDetail detailLevel = [UNIVERSE detailLevel];
947 : BOOL shadersOn = detailLevel >= DETAIL_LEVEL_SHADERS;
948 : #else
949 : const BOOL shadersOn = NO;
950 : #endif
951 :
952 : if (textureName != nil)
953 : {
954 : NSDictionary *spec = [NSDictionary dictionaryWithObjectsAndKeys:textureName, @"name", @"yes", @"repeat_s", @"linear", @"min_filter", @"yes", @"cube_map", nil];
955 : diffuseMap = [OOTexture textureWithConfiguration:spec];
956 : if (diffuseMap == nil) return; // OOTexture will have logged a file-not-found warning.
957 : if (shadersOn)
958 : {
959 : [diffuseMap ensureFinishedLoading]; // only know if it is a cube map if it's loaded
960 : if ([diffuseMap isCubeMap])
961 : {
962 : macros = [materialDefaults oo_dictionaryForKey:isMoon ? @"moon-customized-cubemap-macros" : @"planet-customized-cubemap-macros"];
963 : }
964 : else
965 : {
966 : macros = [materialDefaults oo_dictionaryForKey:isMoon ? @"moon-customized-macros" : @"planet-customized-macros"];
967 : }
968 : }
969 : else textureName = @"dynamic";
970 :
971 : // let's try giving some love to normalMap too
972 : if (_normSpecMapName)
973 : {
974 : NSDictionary *nspec = [NSDictionary dictionaryWithObjectsAndKeys:_normSpecMapName, @"name", @"yes", @"repeat_s", @"linear", @"min_filter", @"yes", @"cube_map", nil];
975 : normalMap = [OOTexture textureWithConfiguration:nspec];
976 : if (normalMap != nil) // OOTexture will have logged a file-not-found warning.
977 : {
978 : if (shadersOn)
979 : {
980 : [normalMap ensureFinishedLoading]; // only know if it is a cube map if it's loaded
981 : if ([normalMap isCubeMap])
982 : {
983 : macros = [materialDefaults oo_dictionaryForKey:isMoon ? @"moon-customized-cubemap-normspec-macros" : @"planet-customized-cubemap-normspec-macros"];
984 : }
985 : else
986 : {
987 : macros = [materialDefaults oo_dictionaryForKey:isMoon ? @"moon-customized-normspec-macros" : @"planet-customized-normspec-macros"];
988 : }
989 : }
990 : }
991 : }
992 : }
993 : else
994 : {
995 : [OOPlanetTextureGenerator generatePlanetTexture:&diffuseMap
996 : secondaryTexture:(detailLevel >= DETAIL_LEVEL_SHADERS) ? &normalMap : NULL
997 : withInfo:_materialParameters];
998 :
999 : if (shadersOn)
1000 : {
1001 : macros = [materialDefaults oo_dictionaryForKey:isMoon ? @"moon-dynamic-macros" : @"planet-dynamic-macros"];
1002 : }
1003 : textureName = @"dynamic";
1004 : }
1005 :
1006 : /* Generate atmosphere texture */
1007 : if (!isMoon)
1008 : {
1009 : if (shadersOn)
1010 : {
1011 : NSMutableDictionary *aConfig = [[[materialDefaults oo_dictionaryForKey:@"atmosphere-material"] mutableCopy] autorelease];
1012 : [aConfig setObject:[NSArray arrayWithObjects:diffuseMap, normalMap, nil] forKey:@"_oo_texture_objects"];
1013 :
1014 : NSDictionary *amacros = [materialDefaults oo_dictionaryForKey:@"atmosphere-dynamic-macros"];
1015 :
1016 : OOMaterial *dynamicShaderMaterial = [OOShaderMaterial shaderMaterialWithName:@"dynamic"
1017 : configuration:aConfig
1018 : macros:amacros
1019 : bindingTarget:self];
1020 :
1021 : if (dynamicShaderMaterial == nil)
1022 : {
1023 : DESTROY(_atmosphereShaderDrawable);
1024 : }
1025 : else
1026 : {
1027 : [_atmosphereShaderDrawable setMaterial:dynamicShaderMaterial];
1028 : }
1029 : }
1030 :
1031 : OOLog(@"texture.planet.generate",@"Preparing atmosphere for planet %@",self);
1032 : /* Generate a standalone atmosphere texture */
1033 : OOTexture *atmosphere = nil;
1034 : [OOStandaloneAtmosphereGenerator generateAtmosphereTexture:&atmosphere
1035 : withInfo:_materialParameters];
1036 :
1037 : OOLog(@"texture.planet.generate",@"Planet %@ has atmosphere %@",self,atmosphere);
1038 :
1039 : OOSingleTextureMaterial *dynamicMaterial = [[OOSingleTextureMaterial alloc] initWithName:@"dynamic" texture:atmosphere configuration:nil];
1040 : [_atmosphereDrawable setMaterial:dynamicMaterial];
1041 : [dynamicMaterial release];
1042 : }
1043 :
1044 : OOMaterial *material = nil;
1045 :
1046 : #if OO_SHADERS
1047 : if (shadersOn)
1048 : {
1049 : NSMutableDictionary *config = [[[materialDefaults oo_dictionaryForKey:@"planet-material"] mutableCopy] autorelease];
1050 : [config setObject:[NSArray arrayWithObjects:diffuseMap, normalMap, nil] forKey:@"_oo_texture_objects"];
1051 :
1052 : material = [OOShaderMaterial shaderMaterialWithName:textureName
1053 : configuration:config
1054 : macros:macros
1055 : bindingTarget:self];
1056 : }
1057 : #endif
1058 : if (material == nil)
1059 : {
1060 : material = [[OOSingleTextureMaterial alloc] initWithName:textureName texture:diffuseMap configuration:nil];
1061 : [material autorelease];
1062 : }
1063 : [_planetDrawable setMaterial:material];
1064 : }
1065 :
1066 :
1067 : - (BOOL) setUpPlanetFromTexture:(NSString *)textureName
1068 : {
1069 : [self setTextureFileName:textureName];
1070 : return YES;
1071 : }
1072 :
1073 :
1074 : - (OOMaterial *) material
1075 : {
1076 : return [_planetDrawable material];
1077 : }
1078 :
1079 :
1080 : - (OOMaterial *) atmosphereMaterial
1081 : {
1082 : return [_atmosphereDrawable material];
1083 : }
1084 :
1085 :
1086 : - (OOMaterial *) atmosphereShaderMaterial
1087 : {
1088 : if(!_atmosphereShaderDrawable) return nil;
1089 : return [_atmosphereShaderDrawable material];
1090 : }
1091 :
1092 :
1093 : - (NSString *) name
1094 : {
1095 : return _name;
1096 : }
1097 :
1098 :
1099 : - (void) setName:(NSString *)name
1100 : {
1101 : [_name release];
1102 : _name = [name retain];
1103 : }
1104 :
1105 : @end
1106 :
1107 : #endif // NEW_PLANETS
|