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