1 // celx_object.cpp
2 //
3 // Copyright (C) 2003-2009, the Celestia Development Team
4 //
5 // Lua script extensions for Celestia: object
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11
12 #include <cstring>
13 #include "celx.h"
14 #include "celx_internal.h"
15 #include "celx_object.h"
16 #include <celengine/body.h>
17 #include <celengine/timelinephase.h>
18 #include <celengine/axisarrow.h>
19 #include <celengine/visibleregion.h>
20 #include <celengine/planetgrid.h>
21 #include "celestiacore.h"
22
23
24 using namespace std;
25
26
parseMarkerSymbol(const string & name)27 static MarkerRepresentation::Symbol parseMarkerSymbol(const string& name)
28 {
29 if (compareIgnoringCase(name, "diamond") == 0)
30 return MarkerRepresentation::Diamond;
31 else if (compareIgnoringCase(name, "triangle") == 0)
32 return MarkerRepresentation::Triangle;
33 else if (compareIgnoringCase(name, "square") == 0)
34 return MarkerRepresentation::Square;
35 else if (compareIgnoringCase(name, "filledsquare") == 0)
36 return MarkerRepresentation::FilledSquare;
37 else if (compareIgnoringCase(name, "plus") == 0)
38 return MarkerRepresentation::Plus;
39 else if (compareIgnoringCase(name, "x") == 0)
40 return MarkerRepresentation::X;
41 else if (compareIgnoringCase(name, "leftarrow") == 0)
42 return MarkerRepresentation::LeftArrow;
43 else if (compareIgnoringCase(name, "rightarrow") == 0)
44 return MarkerRepresentation::RightArrow;
45 else if (compareIgnoringCase(name, "uparrow") == 0)
46 return MarkerRepresentation::UpArrow;
47 else if (compareIgnoringCase(name, "downarrow") == 0)
48 return MarkerRepresentation::DownArrow;
49 else if (compareIgnoringCase(name, "circle") == 0)
50 return MarkerRepresentation::Circle;
51 else if (compareIgnoringCase(name, "disk") == 0)
52 return MarkerRepresentation::Disk;
53 else
54 return MarkerRepresentation::Diamond;
55 }
56
57
58 // ==================== Object ====================
59 // star, planet, or deep-sky object
object_new(lua_State * l,const Selection & sel)60 int object_new(lua_State* l, const Selection& sel)
61 {
62 CelxLua celx(l);
63
64 Selection* ud = reinterpret_cast<Selection*>(lua_newuserdata(l, sizeof(Selection)));
65 *ud = sel;
66
67 celx.setClass(Celx_Object);
68
69 return 1;
70 }
71
to_object(lua_State * l,int index)72 Selection* to_object(lua_State* l, int index)
73 {
74 CelxLua celx(l);
75 return static_cast<Selection*>(celx.checkUserData(index, Celx_Object));
76 }
77
this_object(lua_State * l)78 static Selection* this_object(lua_State* l)
79 {
80 CelxLua celx(l);
81
82 Selection* sel = to_object(l, 1);
83 if (sel == NULL)
84 {
85 celx.doError("Bad position object!");
86 }
87
88 return sel;
89 }
90
91
object_tostring(lua_State * l)92 static int object_tostring(lua_State* l)
93 {
94 lua_pushstring(l, "[Object]");
95
96 return 1;
97 }
98
99
100 // Return true if the object is visible, false if not.
object_visible(lua_State * l)101 static int object_visible(lua_State* l)
102 {
103 CelxLua celx(l);
104 celx.checkArgs(1, 1, "No arguments expected to function object:visible");
105
106 Selection* sel = this_object(l);
107 lua_pushboolean(l, sel->isVisible());
108
109 return 1;
110 }
111
112
113 // Set the object visibility flag.
object_setvisible(lua_State * l)114 static int object_setvisible(lua_State* l)
115 {
116 CelxLua celx(l);
117 celx.checkArgs(2, 2, "One argument expected to object:setvisible()");
118
119 Selection* sel = this_object(l);
120 bool visible = celx.safeGetBoolean(2, AllErrors, "Argument to object:setvisible() must be a boolean");
121 if (sel->body() != NULL)
122 {
123 sel->body()->setVisible(visible);
124 }
125 else if (sel->deepsky() != NULL)
126 {
127 sel->deepsky()->setVisible(visible);
128 }
129
130 return 0;
131 }
132
133
object_setorbitcolor(lua_State * l)134 static int object_setorbitcolor(lua_State* l)
135 {
136 CelxLua celx(l);
137 celx.checkArgs(4, 4, "Red, green, and blue color values exepected for object:setorbitcolor()");
138
139 Selection* sel = this_object(l);
140 float r = (float) celx.safeGetNumber(2, WrongType, "Argument 1 to object:setorbitcolor() must be a number", 0.0);
141 float g = (float) celx.safeGetNumber(3, WrongType, "Argument 2 to object:setorbitcolor() must be a number", 0.0);
142 float b = (float) celx.safeGetNumber(4, WrongType, "Argument 3 to object:setorbitcolor() must be a number", 0.0);
143 Color orbitColor(r, g, b);
144
145 if (sel->body() != NULL)
146 {
147 sel->body()->setOrbitColor(orbitColor);
148 }
149
150 return 0;
151 }
152
153
object_orbitcoloroverridden(lua_State * l)154 static int object_orbitcoloroverridden(lua_State* l)
155 {
156 CelxLua celx(l);
157 celx.checkArgs(1, 1, "No arguments expected to object:orbitcoloroverridden");
158
159 bool isOverridden = false;
160 Selection* sel = this_object(l);
161 if (sel->body() != NULL)
162 {
163 isOverridden = sel->body()->isOrbitColorOverridden();
164 }
165
166 lua_pushboolean(l, isOverridden);
167
168 return 1;
169 }
170
171
object_setorbitcoloroverridden(lua_State * l)172 static int object_setorbitcoloroverridden(lua_State* l)
173 {
174 CelxLua celx(l);
175 celx.checkArgs(2, 2, "One argument expected to object:setorbitcoloroverridden");
176
177 Selection* sel = this_object(l);
178 bool override = celx.safeGetBoolean(2, AllErrors, "Argument to object:setorbitcoloroverridden() must be a boolean");
179
180 if (sel->body() != NULL)
181 {
182 sel->body()->setOrbitColorOverridden(override);
183 }
184
185 return 0;
186 }
187
188
object_orbitvisibility(lua_State * l)189 static int object_orbitvisibility(lua_State* l)
190 {
191 CelxLua celx(l);
192 celx.checkArgs(1, 1, "No arguments expected to object:orbitvisibility");
193
194 Body::VisibilityPolicy visibility = Body::UseClassVisibility;
195
196 Selection* sel = this_object(l);
197 if (sel->body() != NULL)
198 {
199 visibility = sel->body()->getOrbitVisibility();
200 }
201
202 const char* s = "normal";
203 if (visibility == Body::AlwaysVisible)
204 s = "always";
205 else if (visibility == Body::NeverVisible)
206 s = "never";
207
208 lua_pushstring(l, s);
209
210 return 1;
211 }
212
213
object_setorbitvisibility(lua_State * l)214 static int object_setorbitvisibility(lua_State* l)
215 {
216 CelxLua celx(l);
217 celx.checkArgs(2, 2, "One argument expected to object:setorbitcoloroverridden");
218
219 if (!lua_isstring(l, 2))
220 {
221 celx.doError("First argument to object:setorbitvisibility() must be a string");
222 }
223
224 Selection* sel = this_object(l);
225
226 string key;
227 key = lua_tostring(l, 2);
228
229 if (CelxLua::OrbitVisibilityMap.count(key) == 0)
230 {
231 cerr << "Unknown visibility policy: " << key << endl;
232 }
233 else
234 {
235 Body::VisibilityPolicy visibility = static_cast<Body::VisibilityPolicy>(CelxLua::OrbitVisibilityMap[key]);
236
237 if (sel->body() != NULL)
238 {
239 sel->body()->setOrbitVisibility(visibility);
240 }
241 }
242
243 return 0;
244 }
245
246
object_addreferencemark(lua_State * l)247 static int object_addreferencemark(lua_State* l)
248 {
249 CelxLua celx(l);
250 celx.checkArgs(2, 2, "Expected one table as argument to object:addreferencemark()");
251
252 if (!lua_istable(l, 2))
253 {
254 celx.doError("Argument to object:addreferencemark() must be a table");
255 }
256
257 Selection* sel = this_object(l);
258 Body* body = sel->body();
259
260 lua_pushstring(l, "type");
261 lua_gettable(l, 2);
262 const char* rmtype = celx.safeGetString(3, NoErrors, "");
263 lua_settop(l, 2);
264
265 lua_pushstring(l, "size");
266 lua_gettable(l, 2);
267 float rmsize = (float) celx.safeGetNumber(3, NoErrors, "", body->getRadius()) + body->getRadius();
268 lua_settop(l, 2);
269
270 lua_pushstring(l, "opacity");
271 lua_gettable(l, 2);
272 // -1 indicates that the opacity wasn't set and the default value
273 // should be used.
274 float rmopacity = (float) celx.safeGetNumber(3, NoErrors, "", -1.0f);
275 lua_settop(l, 2);
276
277 lua_pushstring(l, "color");
278 lua_gettable(l, 2);
279 const char* rmcolorstring = celx.safeGetString(3, NoErrors, "");
280 Color rmcolor(0.0f, 1.0f, 0.0f);
281 if (rmcolorstring != NULL)
282 Color::parse(rmcolorstring, rmcolor);
283 lua_settop(l, 2);
284
285 lua_pushstring(l, "tag");
286 lua_gettable(l, 2);
287 const char* rmtag = celx.safeGetString(3, NoErrors, "");
288 if (rmtag == NULL)
289 rmtag = rmtype;
290 lua_settop(l, 2);
291
292 lua_pushstring(l, "target");
293 lua_gettable(l, 2);
294 Selection* rmtarget = to_object(l, 3);
295 lua_settop(l, 2);
296
297 if (rmtype != NULL)
298 {
299 body->removeReferenceMark(rmtype);
300
301 if (compareIgnoringCase(rmtype, "body axes") == 0)
302 {
303 BodyAxisArrows* arrow = new BodyAxisArrows(*body);
304 arrow->setTag(rmtag);
305 arrow->setSize(rmsize);
306 if (rmopacity >= 0.0f)
307 arrow->setOpacity(rmopacity);
308 body->addReferenceMark(arrow);
309 }
310 else if (compareIgnoringCase(rmtype, "frame axes") == 0)
311 {
312 FrameAxisArrows* arrow = new FrameAxisArrows(*body);
313 arrow->setTag(rmtag);
314 arrow->setSize(rmsize);
315 if (rmopacity >= 0.0f)
316 arrow->setOpacity(rmopacity);
317 body->addReferenceMark(arrow);
318 }
319 else if (compareIgnoringCase(rmtype, "sun direction") == 0)
320 {
321 SunDirectionArrow* arrow = new SunDirectionArrow(*body);
322 arrow->setTag(rmtag);
323 arrow->setSize(rmsize);
324 if (rmcolorstring != NULL)
325 arrow->setColor(rmcolor);
326 body->addReferenceMark(arrow);
327 }
328 else if (compareIgnoringCase(rmtype, "velocity vector") == 0)
329 {
330 VelocityVectorArrow* arrow = new VelocityVectorArrow(*body);
331 arrow->setTag(rmtag);
332 arrow->setSize(rmsize);
333 if (rmcolorstring != NULL)
334 arrow->setColor(rmcolor);
335 body->addReferenceMark(arrow);
336 }
337 else if (compareIgnoringCase(rmtype, "spin vector") == 0)
338 {
339 SpinVectorArrow* arrow = new SpinVectorArrow(*body);
340 arrow->setTag(rmtag);
341 arrow->setSize(rmsize);
342 if (rmcolorstring != NULL)
343 arrow->setColor(rmcolor);
344 body->addReferenceMark(arrow);
345 }
346 else if (compareIgnoringCase(rmtype, "body to body direction") == 0 && rmtarget != NULL)
347 {
348 BodyToBodyDirectionArrow* arrow = new BodyToBodyDirectionArrow(*body, *rmtarget);
349 arrow->setTag(rmtag);
350 arrow->setSize(rmsize);
351 if (rmcolorstring != NULL)
352 arrow->setColor(rmcolor);
353 body->addReferenceMark(arrow);
354 }
355 else if (compareIgnoringCase(rmtype, "visible region") == 0 && rmtarget != NULL)
356 {
357 VisibleRegion* region = new VisibleRegion(*body, *rmtarget);
358 region->setTag(rmtag);
359 if (rmopacity >= 0.0f)
360 region->setOpacity(rmopacity);
361 if (rmcolorstring != NULL)
362 region->setColor(rmcolor);
363 body->addReferenceMark(region);
364 }
365 else if (compareIgnoringCase(rmtype, "planetographic grid") == 0)
366 {
367 PlanetographicGrid* grid = new PlanetographicGrid(*body);
368 body->addReferenceMark(grid);
369 }
370 }
371
372 return 0;
373 }
374
375
object_removereferencemark(lua_State * l)376 static int object_removereferencemark(lua_State* l)
377 {
378 CelxLua celx(l);
379 celx.checkArgs(1, 1000, "Invalid number of arguments in object:removereferencemark");
380 CelestiaCore* appCore = celx.appCore(AllErrors);
381
382 Selection* sel = this_object(l);
383 Body* body = sel->body();
384
385 int argc = lua_gettop(l);
386 for (int i = 2; i <= argc; i++)
387 {
388 string refMark = celx.safeGetString(i, AllErrors, "Arguments to object:removereferencemark() must be strings");
389
390 if (body->findReferenceMark(refMark))
391 appCore->toggleReferenceMark(refMark, *sel);
392 }
393
394 return 0;
395 }
396
397
object_radius(lua_State * l)398 static int object_radius(lua_State* l)
399 {
400 CelxLua celx(l);
401 celx.checkArgs(1, 1, "No arguments expected to function object:radius");
402
403 Selection* sel = this_object(l);
404 lua_pushnumber(l, sel->radius());
405
406 return 1;
407 }
408
object_setradius(lua_State * l)409 static int object_setradius(lua_State* l)
410 {
411 CelxLua celx(l);
412 celx.checkArgs(2, 2, "One argument expected to object:setradius()");
413
414 Selection* sel = this_object(l);
415 if (sel->body() != NULL)
416 {
417 Body* body = sel->body();
418 float iradius = body->getRadius();
419 double radius = celx.safeGetNumber(2, AllErrors, "Argument to object:setradius() must be a number");
420 if ((radius > 0))
421 {
422 body->setSemiAxes(body->getSemiAxes() * ((float) radius / iradius));
423 }
424
425 if (body->getRings() != NULL)
426 {
427 RingSystem rings(0.0f, 0.0f);
428 rings = *body->getRings();
429 float inner = rings.innerRadius;
430 float outer = rings.outerRadius;
431 rings.innerRadius = inner * (float) radius / iradius;
432 rings.outerRadius = outer * (float) radius / iradius;
433 body->setRings(rings);
434 }
435 }
436
437 return 0;
438 }
439
object_type(lua_State * l)440 static int object_type(lua_State* l)
441 {
442 CelxLua celx(l);
443 celx.checkArgs(1, 1, "No arguments expected to function object:type");
444
445 Selection* sel = this_object(l);
446 const char* tname = "unknown";
447 switch (sel->getType())
448 {
449 case Selection::Type_Body:
450 {
451 int cl = sel->body()->getClassification();
452 switch (cl)
453 {
454 case Body::Planet : tname = "planet"; break;
455 case Body::DwarfPlanet : tname = "dwarfplanet"; break;
456 case Body::Moon : tname = "moon"; break;
457 case Body::MinorMoon : tname = "minormoon"; break;
458 case Body::Asteroid : tname = "asteroid"; break;
459 case Body::Comet : tname = "comet"; break;
460 case Body::Spacecraft : tname = "spacecraft"; break;
461 case Body::Invisible : tname = "invisible"; break;
462 case Body::SurfaceFeature : tname = "surfacefeature"; break;
463 case Body::Component : tname = "component"; break;
464 case Body::Diffuse : tname = "diffuse"; break;
465 }
466 }
467 break;
468
469 case Selection::Type_Star:
470 tname = "star";
471 break;
472
473 case Selection::Type_DeepSky:
474 tname = sel->deepsky()->getObjTypeName();
475 break;
476
477 case Selection::Type_Location:
478 tname = "location";
479 break;
480
481 case Selection::Type_Nil:
482 tname = "null";
483 break;
484 }
485
486 lua_pushstring(l, tname);
487
488 return 1;
489 }
490
object_name(lua_State * l)491 static int object_name(lua_State* l)
492 {
493 CelxLua celx(l);
494 celx.checkArgs(1, 1, "No arguments expected to function object:name");
495
496 Selection* sel = this_object(l);
497 switch (sel->getType())
498 {
499 case Selection::Type_Body:
500 lua_pushstring(l, sel->body()->getName().c_str());
501 break;
502 case Selection::Type_DeepSky:
503 lua_pushstring(l, celx.appCore(AllErrors)->getSimulation()->getUniverse()
504 ->getDSOCatalog()->getDSOName(sel->deepsky()).c_str());
505 break;
506 case Selection::Type_Star:
507 lua_pushstring(l, celx.appCore(AllErrors)->getSimulation()->getUniverse()
508 ->getStarCatalog()->getStarName(*(sel->star())).c_str());
509 break;
510 case Selection::Type_Location:
511 lua_pushstring(l, sel->location()->getName().c_str());
512 break;
513 default:
514 lua_pushstring(l, "?");
515 break;
516 }
517
518 return 1;
519 }
520
object_localname(lua_State * l)521 static int object_localname(lua_State* l)
522 {
523 CelxLua celx(l);
524 celx.checkArgs(1, 1, "No arguments expected to function object:localname");
525
526 Selection* sel = this_object(l);
527 switch (sel->getType())
528 {
529 case Selection::Type_Body:
530 lua_pushstring(l, sel->body()->getName(true).c_str());
531 break;
532 case Selection::Type_DeepSky:
533 lua_pushstring(l, celx.appCore(AllErrors)->getSimulation()->getUniverse()
534 ->getDSOCatalog()->getDSOName(sel->deepsky(), true).c_str());
535 break;
536 case Selection::Type_Star:
537 lua_pushstring(l, celx.appCore(AllErrors)->getSimulation()->getUniverse()
538 ->getStarCatalog()->getStarName(*(sel->star()), true).c_str());
539 break;
540 default:
541 lua_pushstring(l, "?");
542 break;
543 }
544
545 return 1;
546 }
547
object_spectraltype(lua_State * l)548 static int object_spectraltype(lua_State* l)
549 {
550 CelxLua celx(l);
551 celx.checkArgs(1, 1, "No arguments expected to function object:spectraltype");
552
553 Selection* sel = this_object(l);
554 if (sel->star() != NULL)
555 {
556 char buf[16];
557 strncpy(buf, sel->star()->getSpectralType(), sizeof buf);
558 buf[sizeof(buf) - 1] = '\0'; // make sure it's zero terminate
559 lua_pushstring(l, buf);
560 }
561 else
562 {
563 lua_pushnil(l);
564 }
565
566 return 1;
567 }
568
object_getinfo(lua_State * l)569 static int object_getinfo(lua_State* l)
570 {
571 CelxLua celx(l);
572 celx.checkArgs(1, 1, "No arguments expected to function object:getinfo");
573
574 lua_newtable(l);
575
576 Selection* sel = this_object(l);
577 if (sel->star() != NULL)
578 {
579 Star* star = sel->star();
580 celx.setTable("type", "star");
581 celx.setTable("name", celx.appCore(AllErrors)->getSimulation()->getUniverse()
582 ->getStarCatalog()->getStarName(*(sel->star())).c_str());
583 celx.setTable("catalogNumber", star->getCatalogNumber());
584 celx.setTable("stellarClass", star->getSpectralType());
585 celx.setTable("absoluteMagnitude", (lua_Number)star->getAbsoluteMagnitude());
586 celx.setTable("luminosity", (lua_Number)star->getLuminosity());
587 celx.setTable("radius", (lua_Number)star->getRadius());
588 celx.setTable("temperature", (lua_Number)star->getTemperature());
589 celx.setTable("rotationPeriod", (lua_Number)star->getRotationModel()->getPeriod());
590 celx.setTable("bolometricMagnitude", (lua_Number)star->getBolometricMagnitude());
591
592 const Orbit* orbit = star->getOrbit();
593 if (orbit != NULL)
594 celx.setTable("orbitPeriod", orbit->getPeriod());
595
596 if (star->getOrbitBarycenter() != NULL)
597 {
598 Selection parent((Star*)(star->getOrbitBarycenter()));
599 lua_pushstring(l, "parent");
600 object_new(l, parent);
601 lua_settable(l, -3);
602 }
603 }
604 else if (sel->body() != NULL)
605 {
606 Body* body = sel->body();
607 const char* tname = "unknown";
608 switch (body->getClassification())
609 {
610 case Body::Planet : tname = "planet"; break;
611 case Body::DwarfPlanet : tname = "dwarfplanet"; break;
612 case Body::Moon : tname = "moon"; break;
613 case Body::MinorMoon : tname = "minormoon"; break;
614 case Body::Asteroid : tname = "asteroid"; break;
615 case Body::Comet : tname = "comet"; break;
616 case Body::Spacecraft : tname = "spacecraft"; break;
617 case Body::Invisible : tname = "invisible"; break;
618 case Body::SurfaceFeature : tname = "surfacefeature"; break;
619 case Body::Component : tname = "component"; break;
620 case Body::Diffuse : tname = "diffuse"; break;
621 }
622
623 celx.setTable("type", tname);
624 celx.setTable("name", body->getName().c_str());
625 celx.setTable("mass", (lua_Number)body->getMass());
626 celx.setTable("albedo", (lua_Number)body->getAlbedo());
627 celx.setTable("infoURL", body->getInfoURL().c_str());
628 celx.setTable("radius", (lua_Number)body->getRadius());
629
630 // TODO: add method to return semiaxes
631 Vec3f semiAxes = body->getSemiAxes();
632 // Note: oblateness is an obsolete field, replaced by semiaxes;
633 // it's only here for backward compatibility.
634 float polarRadius = semiAxes.y;
635 float eqRadius = max(semiAxes.x, semiAxes.z);
636 celx.setTable("oblateness", (eqRadius - polarRadius) / eqRadius);
637
638 double lifespanStart, lifespanEnd;
639 body->getLifespan(lifespanStart, lifespanEnd);
640 celx.setTable("lifespanStart", (lua_Number)lifespanStart);
641 celx.setTable("lifespanEnd", (lua_Number)lifespanEnd);
642 // TODO: atmosphere, surfaces ?
643
644 PlanetarySystem* system = body->getSystem();
645 if (system->getPrimaryBody() != NULL)
646 {
647 Selection parent(system->getPrimaryBody());
648 lua_pushstring(l, "parent");
649 object_new(l, parent);
650 lua_settable(l, -3);
651 }
652 else
653 {
654 Selection parent(system->getStar());
655 lua_pushstring(l, "parent");
656 object_new(l, parent);
657 lua_settable(l, -3);
658 }
659
660 lua_pushstring(l, "hasRings");
661 lua_pushboolean(l, body->getRings() != NULL);
662 lua_settable(l, -3);
663
664 // TIMELINE-TODO: The code to retrieve orbital and rotation periods only works
665 // if the object has a single timeline phase. This should hardly ever
666 // be a problem, but it still may be best to set the periods to zero
667 // for objects with multiple phases.
668 const RotationModel* rm = body->getRotationModel(0.0);
669 celx.setTable("rotationPeriod", (double) rm->getPeriod());
670
671 const Orbit* orbit = body->getOrbit(0.0);
672 celx.setTable("orbitPeriod", orbit->getPeriod());
673 Atmosphere* atmosphere = body->getAtmosphere();
674 if (atmosphere != NULL)
675 {
676 celx.setTable("atmosphereHeight", (double)atmosphere->height);
677 celx.setTable("atmosphereCloudHeight", (double)atmosphere->cloudHeight);
678 celx.setTable("atmosphereCloudSpeed", (double)atmosphere->cloudSpeed);
679 }
680 }
681 else if (sel->deepsky() != NULL)
682 {
683 DeepSkyObject* deepsky = sel->deepsky();
684 const char* objTypeName = deepsky->getObjTypeName();
685 celx.setTable("type", objTypeName);
686
687 celx.setTable("name", celx.appCore(AllErrors)->getSimulation()->getUniverse()
688 ->getDSOCatalog()->getDSOName(deepsky).c_str());
689 celx.setTable("catalogNumber", deepsky->getCatalogNumber());
690
691 if (!strcmp(objTypeName, "galaxy"))
692 celx.setTable("hubbleType", deepsky->getType());
693
694 celx.setTable("absoluteMagnitude", (lua_Number)deepsky->getAbsoluteMagnitude());
695 celx.setTable("radius", (lua_Number)deepsky->getRadius());
696 }
697 else if (sel->location() != NULL)
698 {
699 celx.setTable("type", "location");
700 Location* location = sel->location();
701 celx.setTable("name", location->getName().c_str());
702 celx.setTable("size", (lua_Number)location->getSize());
703 celx.setTable("importance", (lua_Number)location->getImportance());
704 celx.setTable("infoURL", location->getInfoURL().c_str());
705
706 uint32 featureType = location->getFeatureType();
707 string featureName("Unknown");
708 for (CelxLua::FlagMap::const_iterator it = CelxLua::LocationFlagMap.begin();
709 it != CelxLua::LocationFlagMap.end(); it++)
710 {
711 if (it->second == featureType)
712 {
713 featureName = it->first;
714 break;
715 }
716 }
717 celx.setTable("featureType", featureName.c_str());
718
719 Body* parent = location->getParentBody();
720 if (parent != NULL)
721 {
722 Selection selection(parent);
723 lua_pushstring(l, "parent");
724 object_new(l, selection);
725 lua_settable(l, -3);
726 }
727 }
728 else
729 {
730 celx.setTable("type", "null");
731 }
732 return 1;
733 }
734
object_absmag(lua_State * l)735 static int object_absmag(lua_State* l)
736 {
737 CelxLua celx(l);
738 celx.checkArgs(1, 1, "No arguments expected to function object:absmag");
739
740 Selection* sel = this_object(l);
741 if (sel->star() != NULL)
742 lua_pushnumber(l, sel->star()->getAbsoluteMagnitude());
743 else
744 lua_pushnil(l);
745
746 return 1;
747 }
748
object_mark(lua_State * l)749 static int object_mark(lua_State* l)
750 {
751 CelxLua celx(l);
752 celx.checkArgs(1, 7, "Need 0 to 6 arguments for object:mark");
753
754 Selection* sel = this_object(l);
755 CelestiaCore* appCore = celx.appCore(AllErrors);
756
757 Color markColor(0.0f, 1.0f, 0.0f);
758 const char* colorString = celx.safeGetString(2, WrongType, "First argument to object:mark must be a string");
759 if (colorString != NULL)
760 Color::parse(colorString, markColor);
761
762 MarkerRepresentation::Symbol markSymbol = MarkerRepresentation::Diamond;
763 const char* markerString = celx.safeGetString(3, WrongType, "Second argument to object:mark must be a string");
764 if (markerString != NULL)
765 markSymbol = parseMarkerSymbol(markerString);
766
767 float markSize = (float)celx.safeGetNumber(4, WrongType, "Third arg to object:mark must be a number", 10.0);
768 if (markSize < 1.0f)
769 markSize = 1.0f;
770 else if (markSize > 10000.0f)
771 markSize = 10000.0f;
772
773 float markAlpha = (float)celx.safeGetNumber(5, WrongType, "Fourth arg to object:mark must be a number", 0.9);
774 if (markAlpha < 0.0f)
775 markAlpha = 0.0f;
776 else if (markAlpha > 1.0f)
777 markAlpha = 1.0f;
778
779 Color markColorAlpha(0.0f, 1.0f, 0.0f, 0.9f);
780 markColorAlpha = Color(markColor, markAlpha);
781
782 const char* markLabel = celx.safeGetString(6, WrongType, "Fifth argument to object:mark must be a string");
783 if (markLabel == NULL)
784 markLabel = "";
785
786 bool occludable = celx.safeGetBoolean(7, WrongType, "Sixth argument to object:mark must be a boolean", true);
787
788 Simulation* sim = appCore->getSimulation();
789
790 MarkerRepresentation markerRep(markSymbol);
791 markerRep.setSize(markSize);
792 markerRep.setColor(markColorAlpha);
793 markerRep.setLabel(markLabel);
794 sim->getUniverse()->markObject(*sel, markerRep, 1, occludable);
795
796 return 0;
797 }
798
object_unmark(lua_State * l)799 static int object_unmark(lua_State* l)
800 {
801 CelxLua celx(l);
802 celx.checkArgs(1, 1, "No arguments expected to function object:unmark");
803
804 Selection* sel = this_object(l);
805 CelestiaCore* appCore = celx.appCore(AllErrors);
806
807 Simulation* sim = appCore->getSimulation();
808 sim->getUniverse()->unmarkObject(*sel, 1);
809
810 return 0;
811 }
812
813 // Return the object's current position. A time argument is optional;
814 // if not provided, the current master simulation time is used.
object_getposition(lua_State * l)815 static int object_getposition(lua_State* l)
816 {
817 CelxLua celx(l);
818 celx.checkArgs(1, 2, "Expected no or one argument to object:getposition");
819
820 Selection* sel = this_object(l);
821 CelestiaCore* appCore = celx.appCore(AllErrors);
822
823 double t = celx.safeGetNumber(2, WrongType, "Time expected as argument to object:getposition",
824 appCore->getSimulation()->getTime());
825 celx.newPosition(sel->getPosition(t));
826
827 return 1;
828 }
829
object_getchildren(lua_State * l)830 static int object_getchildren(lua_State* l)
831 {
832 CelxLua celx(l);
833 celx.checkArgs(1, 1, "No arguments expected for object:getchildren()");
834
835 Selection* sel = this_object(l);
836 CelestiaCore* appCore = celx.appCore(AllErrors);
837
838 Simulation* sim = appCore->getSimulation();
839
840 lua_newtable(l);
841 if (sel->star() != NULL)
842 {
843 SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog();
844 SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel->star()->getCatalogNumber());
845 if (iter != solarSystemCatalog->end())
846 {
847 SolarSystem* solarSys = iter->second;
848 for (int i = 0; i < solarSys->getPlanets()->getSystemSize(); i++)
849 {
850 Body* body = solarSys->getPlanets()->getBody(i);
851 Selection satSel(body);
852 object_new(l, satSel);
853 lua_rawseti(l, -2, i + 1);
854 }
855 }
856 }
857 else if (sel->body() != NULL)
858 {
859 const PlanetarySystem* satellites = sel->body()->getSatellites();
860 if (satellites != NULL && satellites->getSystemSize() != 0)
861 {
862 for (int i = 0; i < satellites->getSystemSize(); i++)
863 {
864 Body* body = satellites->getBody(i);
865 Selection satSel(body);
866 object_new(l, satSel);
867 lua_rawseti(l, -2, i + 1);
868 }
869 }
870 }
871
872 return 1;
873 }
874
object_preloadtexture(lua_State * l)875 static int object_preloadtexture(lua_State* l)
876 {
877 CelxLua celx(l);
878 celx.checkArgs(1, 1, "No argument expected to object:preloadtexture");
879 CelestiaCore* appCore = celx.appCore(AllErrors);
880
881 Renderer* renderer = appCore->getRenderer();
882 Selection* sel = this_object(l);
883
884 if (sel->body() != NULL && renderer != NULL)
885 {
886 LuaState* luastate = celx.getLuaStateObject();
887 // make sure we don't timeout because of texture-loading:
888 double timeToTimeout = luastate->timeout - luastate->getTime();
889
890 renderer->loadTextures(sel->body());
891
892 // no matter how long it really took, make it look like 0.1s:
893 luastate->timeout = luastate->getTime() + timeToTimeout - 0.1;
894 }
895
896 return 0;
897 }
898
899
900 /*! object:catalognumber(string: catalog_prefix)
901 *
902 * Look up the catalog number for a star in one of the supported catalogs,
903 * currently HIPPARCOS, HD, or SAO. The single argument is a string that
904 * specifies the catalog number, either "HD", "SAO", or "HIP".
905 * If the object is a star, the catalog string is valid, and the star
906 * is present in the catalog, the catalog number is returned on the stack.
907 * Otherwise, nil is returned.
908 *
909 * \verbatim
910 * -- Example: Get the SAO and HD catalog numbers for Rigel
911 * --
912 * rigel = celestia:find("Rigel")
913 * sao = rigel:catalognumber("SAO")
914 * hd = rigel:catalognumber("HD")
915 *
916 * \endverbatim
917 */
object_catalognumber(lua_State * l)918 static int object_catalognumber(lua_State* l)
919 {
920 CelxLua celx(l);
921 celx.checkArgs(2, 2, "One argument expected to object:catalognumber");
922 CelestiaCore* appCore = celx.appCore(AllErrors);
923
924 Selection* sel = this_object(l);
925 const char* catalogName = celx.safeGetString(2, WrongType, "Argument to object:catalognumber must be a string");
926
927 // The argument is a string indicating the catalog.
928 bool validCatalog = false;
929 bool useHIPPARCOS = false;
930 StarDatabase::Catalog catalog = StarDatabase::HenryDraper;
931 if (catalogName != NULL)
932 {
933 if (compareIgnoringCase(catalogName, "HD") == 0)
934 {
935 catalog = StarDatabase::HenryDraper;
936 validCatalog = true;
937 }
938 else if (compareIgnoringCase(catalogName, "SAO") == 0)
939 {
940 catalog = StarDatabase::SAO;
941 validCatalog = true;
942 }
943 else if (compareIgnoringCase(catalogName, "HIP") == 0)
944 {
945 useHIPPARCOS = true;
946 validCatalog = true;
947 }
948 }
949
950 uint32 catalogNumber = Star::InvalidCatalogNumber;
951 if (sel->star() != NULL && validCatalog)
952 {
953 uint32 internalNumber = sel->star()->getCatalogNumber();
954
955 if (useHIPPARCOS)
956 {
957 // Celestia's internal catalog numbers /are/ HIPPARCOS numbers
958 if (internalNumber < StarDatabase::MAX_HIPPARCOS_NUMBER)
959 catalogNumber = internalNumber;
960 }
961 else
962 {
963 const StarDatabase* stardb = appCore->getSimulation()->getUniverse()->getStarCatalog();
964 catalogNumber = stardb->crossIndex(catalog, internalNumber);
965 }
966 }
967
968 if (catalogNumber != Star::InvalidCatalogNumber)
969 lua_pushnumber(l, catalogNumber);
970 else
971 lua_pushnil(l);
972
973 return 1;
974 }
975
976
977 // Locations iterator function; two upvalues expected. Used by
978 // object:locations method.
object_locations_iter(lua_State * l)979 static int object_locations_iter(lua_State* l)
980 {
981 CelxLua celx(l);
982 Selection* sel = to_object(l, lua_upvalueindex(1));
983 if (sel == NULL)
984 {
985 celx.doError("Bad object!");
986 return 0;
987 }
988
989 // Get the current counter value
990 uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2));
991
992 vector<Location*>* locations = NULL;
993 if (sel->body() != NULL)
994 {
995 locations = sel->body()->getLocations();
996 }
997
998 if (locations != NULL && i < locations->size())
999 {
1000 // Increment the counter
1001 lua_pushnumber(l, i + 1);
1002 lua_replace(l, lua_upvalueindex(2));
1003
1004 Location* loc = locations->at(i);
1005 if (loc == NULL)
1006 lua_pushnil(l);
1007 else
1008 object_new(l, Selection(loc));
1009
1010 return 1;
1011 }
1012 else
1013 {
1014 // Return nil when we've enumerated all the locations (or if
1015 // there were no locations associated with the object.)
1016 return 0;
1017 }
1018 }
1019
1020
1021 /*! object:locations()
1022 *
1023 * Return an iterator over all the locations associated with an object.
1024 * Only solar system bodies have locations; for all other object types,
1025 * this method will return an empty iterator.
1026 *
1027 * \verbatim
1028 * -- Example: print locations of current selection
1029 * --
1030 * for loc in celestia:getselection():locations() do
1031 * celestia:log(loc:name())
1032 * end
1033 *
1034 * \endverbatim
1035 */
object_locations(lua_State * l)1036 static int object_locations(lua_State* l)
1037 {
1038 CelxLua celx(l);
1039 // Push a closure with two upvalues: the object and a counter
1040 lua_pushvalue(l, 1); // object
1041 lua_pushnumber(l, 0); // counter
1042 lua_pushcclosure(l, object_locations_iter, 2);
1043
1044 return 1;
1045 }
1046
1047
1048 /*! object:bodyfixedframe()
1049 *
1050 * Return the body-fixed frame for this object.
1051 *
1052 * \verbatim
1053 * -- Example: get the body-fixed frame of the Earth
1054 * --
1055 * earth = celestia:find("Sol/Earth")
1056 * ebf = earth:bodyfixedframe()
1057 *
1058 * \endverbatim
1059 */
object_bodyfixedframe(lua_State * l)1060 static int object_bodyfixedframe(lua_State* l)
1061 {
1062 CelxLua celx(l);
1063 celx.checkArgs(1, 1, "No arguments allowed for object:bodyfixedframe");
1064
1065 Selection* sel = this_object(l);
1066 celx.newFrame(ObserverFrame(ObserverFrame::BodyFixed, *sel));
1067
1068 return 1;
1069 }
1070
1071
1072 /*! object:equatorialframe()
1073 *
1074 * Return the mean equatorial frame for this object.
1075 *
1076 * \verbatim
1077 * -- Example: getthe equatorial frame of the Earth
1078 * --
1079 * earth = celestia:find("Sol/Earth")
1080 * eme = earth:equatorialframe()
1081 *
1082 * \endverbatim
1083 */
object_equatorialframe(lua_State * l)1084 static int object_equatorialframe(lua_State* l)
1085 {
1086 // TODO: allow one argument specifying a freeze time
1087 CelxLua celx(l);
1088 celx.checkArgs(1, 1, "No arguments allowed for to object:equatorialframe");
1089
1090 Selection* sel = this_object(l);
1091 celx.newFrame(ObserverFrame(ObserverFrame::Equatorial, *sel));
1092
1093 return 1;
1094 }
1095
1096
1097 /*! object:orbitframe(time: t)
1098 *
1099 * Return the frame in which the orbit for an object is defined at a particular
1100 * time. If time isn't specified, the current simulation time is assumed. The
1101 * positions of stars and deep sky objects are always defined in the universal
1102 * frame.
1103 *
1104 * \verbatim
1105 * -- Example: get the orbital frame for the Earth at the current time.
1106 * --
1107 * earth = celestia:find("Sol/Earth")
1108 * eof = earth:orbitframe()
1109 *
1110 * \endverbatim
1111 */
object_orbitframe(lua_State * l)1112 static int object_orbitframe(lua_State* l)
1113 {
1114 CelxLua celx(l);
1115 celx.checkArgs(1, 2, "One or no arguments allowed for to object:orbitframe");
1116
1117 Selection* sel = this_object(l);
1118 CelestiaCore* appCore = celx.appCore(AllErrors);
1119
1120 double t = celx.safeGetNumber(2, WrongType, "Time expected as argument to object:orbitframe",
1121 appCore->getSimulation()->getTime());
1122
1123 if (sel->body() == NULL)
1124 {
1125 // The default universal frame
1126 celx.newFrame(ObserverFrame());
1127 }
1128 else
1129 {
1130 const ReferenceFrame* f = sel->body()->getOrbitFrame(t);
1131 celx.newFrame(ObserverFrame(*f));
1132 }
1133
1134 return 1;
1135 }
1136
1137
1138 /*! object:bodyframe(time: t)
1139 *
1140 * Return the frame in which the orientation for an object is defined at a
1141 * particular time. If time isn't specified, the current simulation time is
1142 * assumed. The positions of stars and deep sky objects are always defined
1143 * in the universal frame.
1144 *
1145 * \verbatim
1146 * -- Example: get the curren body frame for the International Space Station.
1147 * --
1148 * iss = celestia:find("Sol/Earth/ISS")
1149 * f = iss:bodyframe()
1150 *
1151 * \endverbatim
1152 */
object_bodyframe(lua_State * l)1153 static int object_bodyframe(lua_State* l)
1154 {
1155 CelxLua celx(l);
1156 celx.checkArgs(1, 2, "One or no arguments allowed for to object:bodyframe");
1157
1158 Selection* sel = this_object(l);
1159 CelestiaCore* appCore = celx.appCore(AllErrors);
1160
1161 double t = celx.safeGetNumber(2, WrongType, "Time expected as argument to object:orbitframe",
1162 appCore->getSimulation()->getTime());
1163
1164 if (sel->body() == NULL)
1165 {
1166 // The default universal frame
1167 celx.newFrame(ObserverFrame());
1168 }
1169 else
1170 {
1171 const ReferenceFrame* f = sel->body()->getBodyFrame(t);
1172 celx.newFrame(ObserverFrame(*f));
1173 }
1174
1175 return 1;
1176 }
1177
1178
1179 /*! object:getphase(time: t)
1180 *
1181 * Get the active timeline phase at the specified time. If no time is
1182 * specified, the current simulation time is used. This method returns
1183 * nil if the object is not a solar system body, or if the time lies
1184 * outside the range covered by the timeline.
1185 *
1186 * \verbatim
1187 * -- Example: get the timeline phase for Cassini at midnight January 1, 2000 UTC.
1188 * --
1189 * cassini = celestia:find("Sol/Cassini")
1190 * tdb = celestia:utctotdb(2000, 1, 1)
1191 * phase = cassini:getphase(tdb)
1192 *
1193 * \endverbatim
1194 */
object_getphase(lua_State * l)1195 static int object_getphase(lua_State* l)
1196 {
1197 CelxLua celx(l);
1198 celx.checkArgs(1, 2, "One or no arguments allowed for to object:getphase");
1199
1200 Selection* sel = this_object(l);
1201 CelestiaCore* appCore = celx.appCore(AllErrors);
1202
1203 double t = celx.safeGetNumber(2, WrongType, "Time expected as argument to object:getphase",
1204 appCore->getSimulation()->getTime());
1205
1206 if (sel->body() == NULL)
1207 {
1208 lua_pushnil(l);
1209 }
1210 else
1211 {
1212 const Timeline* timeline = sel->body()->getTimeline();
1213 if (timeline->includes(t))
1214 {
1215 celx.newPhase(*timeline->findPhase(t));
1216 }
1217 else
1218 {
1219 lua_pushnil(l);
1220 }
1221 }
1222
1223 return 1;
1224 }
1225
1226
1227 // Phases iterator function; two upvalues expected. Used by
1228 // object:phases method.
object_phases_iter(lua_State * l)1229 static int object_phases_iter(lua_State* l)
1230 {
1231 CelxLua celx(l);
1232 Selection* sel = to_object(l, lua_upvalueindex(1));
1233 if (sel == NULL)
1234 {
1235 celx.doError("Bad object!");
1236 return 0;
1237 }
1238
1239 // Get the current counter value
1240 uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2));
1241
1242 const Timeline* timeline = NULL;
1243 if (sel->body() != NULL)
1244 {
1245 timeline = sel->body()->getTimeline();
1246 }
1247
1248 if (timeline != NULL && i < timeline->phaseCount())
1249 {
1250 // Increment the counter
1251 lua_pushnumber(l, i + 1);
1252 lua_replace(l, lua_upvalueindex(2));
1253
1254 const TimelinePhase* phase = timeline->getPhase(i);
1255 celx.newPhase(*phase);
1256
1257 return 1;
1258 }
1259 else
1260 {
1261 // Return nil when we've enumerated all the phases (or if
1262 // if the object wasn't a solar system body.)
1263 return 0;
1264 }
1265 }
1266
1267
1268 /*! object:phases()
1269 *
1270 * Return an iterator over all the phases in an object's timeline.
1271 * Only solar system bodies have timeline; for all other object types,
1272 * this method will return an empty iterator. The phases in a timeline
1273 * are always sorted from earliest to latest, and always coverage a
1274 * continuous span of time.
1275 *
1276 * \verbatim
1277 * -- Example: copy all of an objects phases into the array timeline
1278 * --
1279 * timeline = { }
1280 * count = 0
1281 * for phase in celestia:getselection():phases() do
1282 * count = count + 1
1283 * timeline[count] = phase
1284 * end
1285 *
1286 * \endverbatim
1287 */
object_phases(lua_State * l)1288 static int object_phases(lua_State* l)
1289 {
1290 CelxLua celx(l);
1291 // Push a closure with two upvalues: the object and a counter
1292 lua_pushvalue(l, 1); // object
1293 lua_pushnumber(l, 0); // counter
1294 lua_pushcclosure(l, object_phases_iter, 2);
1295
1296 return 1;
1297 }
1298
1299
CreateObjectMetaTable(lua_State * l)1300 void CreateObjectMetaTable(lua_State* l)
1301 {
1302 CelxLua celx(l);
1303
1304 celx.createClassMetatable(Celx_Object);
1305
1306 celx.registerMethod("__tostring", object_tostring);
1307 celx.registerMethod("visible", object_visible);
1308 celx.registerMethod("setvisible", object_setvisible);
1309 celx.registerMethod("orbitcoloroverridden", object_orbitcoloroverridden);
1310 celx.registerMethod("setorbitcoloroverridden", object_setorbitcoloroverridden);
1311 celx.registerMethod("setorbitcolor", object_setorbitcolor);
1312 celx.registerMethod("orbitvisibility", object_orbitvisibility);
1313 celx.registerMethod("setorbitvisibility", object_setorbitvisibility);
1314 celx.registerMethod("addreferencemark", object_addreferencemark);
1315 celx.registerMethod("removereferencemark", object_removereferencemark);
1316 celx.registerMethod("radius", object_radius);
1317 celx.registerMethod("setradius", object_setradius);
1318 celx.registerMethod("type", object_type);
1319 celx.registerMethod("spectraltype", object_spectraltype);
1320 celx.registerMethod("getinfo", object_getinfo);
1321 celx.registerMethod("catalognumber", object_catalognumber);
1322 celx.registerMethod("absmag", object_absmag);
1323 celx.registerMethod("name", object_name);
1324 celx.registerMethod("localname", object_localname);
1325 celx.registerMethod("mark", object_mark);
1326 celx.registerMethod("unmark", object_unmark);
1327 celx.registerMethod("getposition", object_getposition);
1328 celx.registerMethod("getchildren", object_getchildren);
1329 celx.registerMethod("locations", object_locations);
1330 celx.registerMethod("bodyfixedframe", object_bodyfixedframe);
1331 celx.registerMethod("equatorialframe", object_equatorialframe);
1332 celx.registerMethod("orbitframe", object_orbitframe);
1333 celx.registerMethod("bodyframe", object_bodyframe);
1334 celx.registerMethod("getphase", object_getphase);
1335 celx.registerMethod("phases", object_phases);
1336 celx.registerMethod("preloadtexture", object_preloadtexture);
1337
1338 lua_pop(l, 1); // pop metatable off the stack
1339 }
1340
1341
1342 // ==================== object extensions ====================
1343
1344 // TODO: This should be replaced by an actual Atmosphere object
object_setatmosphere(lua_State * l)1345 static int object_setatmosphere(lua_State* l)
1346 {
1347 CelxLua celx(l);
1348
1349 celx.checkArgs(23, 23, "22 arguments (!) expected to function object:setatmosphere");
1350
1351 Selection* sel = this_object(l);
1352 //CelestiaCore* appCore = getAppCore(l, AllErrors);
1353
1354 if (sel->body() != NULL)
1355 {
1356 Body* body = sel->body();
1357 Atmosphere* atmosphere = body->getAtmosphere();
1358 if (atmosphere != NULL)
1359 {
1360 float r = (float) celx.safeGetNumber(2, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1361 float g = (float) celx.safeGetNumber(3, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1362 float b = (float) celx.safeGetNumber(4, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1363 // Color testColor(0.0f, 1.0f, 0.0f);
1364 Color testColor(r, g, b);
1365 atmosphere->lowerColor = testColor;
1366 r = (float) celx.safeGetNumber(5, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1367 g = (float) celx.safeGetNumber(6, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1368 b = (float) celx.safeGetNumber(7, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1369 atmosphere->upperColor = Color(r, g, b);
1370 r = (float) celx.safeGetNumber(8, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1371 g = (float) celx.safeGetNumber(9, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1372 b = (float) celx.safeGetNumber(10, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1373 atmosphere->skyColor = Color(r, g, b);
1374 r = (float) celx.safeGetNumber(11, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1375 g = (float) celx.safeGetNumber(12, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1376 b = (float) celx.safeGetNumber(13, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1377 atmosphere->sunsetColor = Color(r, g, b);
1378 r = (float) celx.safeGetNumber(14, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1379 g = (float) celx.safeGetNumber(15, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1380 b = (float) celx.safeGetNumber(16, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1381 //HWR atmosphere->rayleighCoeff = Vector3(r, g, b);
1382 r = (float) celx.safeGetNumber(17, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1383 g = (float) celx.safeGetNumber(18, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1384 b = (float) celx.safeGetNumber(19, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1385 //HWR atmosphere->absorptionCoeff = Vector3(r, g, b);
1386 b = (float) celx.safeGetNumber(20, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1387 atmosphere->mieCoeff = b;
1388 b = (float) celx.safeGetNumber(21, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1389 atmosphere->mieScaleHeight = b;
1390 b = (float) celx.safeGetNumber(22, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1391 atmosphere->miePhaseAsymmetry = b;
1392 b = (float) celx.safeGetNumber(23, AllErrors, "Arguments to observer:setatmosphere() must be numbers");
1393 atmosphere->rayleighScaleHeight = b;
1394
1395 body->setAtmosphere(*atmosphere);
1396 cout << "set atmosphere\n";
1397 }
1398 }
1399
1400 return 0;
1401 }
1402
ExtendObjectMetaTable(lua_State * l)1403 void ExtendObjectMetaTable(lua_State* l)
1404 {
1405 CelxLua celx(l);
1406
1407 celx.pushClassName(Celx_Object);
1408 lua_rawget(l, LUA_REGISTRYINDEX);
1409 if (lua_type(l, -1) != LUA_TTABLE)
1410 cout << "Metatable for " << CelxLua::ClassNames[Celx_Object] << " not found!\n";
1411 celx.registerMethod("setatmosphere", object_setatmosphere);
1412 lua_pop(l, 1);
1413 }
1414