Adding Lua Hooks to Celestia

The place to discuss creating, porting and modifying Celestia's source code.
Avatar
Cham M
Posts: 4324
Joined: 14.01.2004
Age: 60
With us: 20 years 10 months
Location: Montreal

Post #21by Cham » 12.09.2006, 16:58

Fortunately, Chris is the one who has the last word on all this. If he wants to add this "hack", then be it. I'm just asking him to be very carefull.
"Well! I've often seen a cat without a grin", thought Alice; "but a grin without a cat! It's the most curious thing I ever saw in all my life!"

chris
Site Admin
Posts: 4211
Joined: 28.01.2002
With us: 22 years 9 months
Location: Seattle, Washington, USA

Post #22by chris » 12.09.2006, 17:07

Cham wrote:Fortunately, Chris is the one who has the last word on all this. If he wants to add this "hack", then be it. I'm just asking him to be very carefull.


What Hank is proposing is not a hack at all . . . It will enable a lot of very useful extensions to Celestia without having to constantly patch the main program. If this is done right, add-on developers need not learn a lot about scripting to be able to take advantage of the new facilities that will be provided. I think that a demo will quickly convince you :)

--Chris

Vincent
Developer
Posts: 1356
Joined: 07.01.2005
With us: 19 years 10 months
Location: Nancy, France

Post #23by Vincent » 12.09.2006, 17:50

Cham,

I spent a lot of time making the Info patch as easy-friendly as possible for end users. Thanks to Hank's, we, end users, might be able to enjoy quite the same features (and even more), but in a more handy and suitable way from the developper's point of view.

So we should let confirmed developpers talk about this project.

Then, I will, as an end user with little knowledge about C++ and Lua, try to build a new Infopatch function. Believe Hank : If they aren't simple to use, they simply won't be used, by me, by you, and by the majority of users. And we will still be able to use the old Infopatch, in a clandestine way, of course... :wink:
@+
Vincent

Celestia Qt4 SVN / Celestia 1.6.1 + Lua Edu Tools v1.2
GeForce 8600 GT 1024MB / AMD Athlon 64 Dual Core / 4Go DDR2 / XP SP3

Topic author
hank
Developer
Posts: 645
Joined: 03.02.2002
With us: 22 years 9 months
Location: Seattle, WA USA

Post #24by hank » 23.09.2006, 17:32

I've been experimenting with Lua-defined orbits using the hook mechanism described above. Here's a brief explanation of what I've done.

To create an object with a Lua-defined orbit, I define it in a .ssc file with a SampledOrbit specification, something like this:

Code: Select all

"Moov" "Sol"
{
   Class "planet"
   Texture "moon.*"
   Radius   1737.53
   SampledOrbit   "moov.xyz"

}

The .xyz file contains two entries to delimit the start and end times (the spatial corrdinates in the .xyz file will be ignored.)

In the lua hook init file I first create an object which determines the orbit by its computepositon method. This method receives the julian date as its argument and returns the coordinates of the position (and a success boolean) as its result. For example, here's a Lua orbit object that defines the orbit for an object which always lies midway between Earth and Mars:

Code: Select all

orbittest = {
   computeposition = function(this,jd)
      if not orbitteststarted then
         KM_PER_MLY = 9466411.842;
         sun = celestia:find("Sol");
         earth = celestia:find("Sol/Earth");
         mars = celestia:find("Sol/Mars");
         orbitteststarted = true;
      end;
      earthPosition = earth:getposition(jd);
      marsPosition  = mars:getposition(jd);
      sunPosition   = sun:getposition(jd);
      marsVector    = earthPosition:vectorto(marsPosition);
      newPos        = earthPosition:addvector( marsVector*0.5 );
      newVector = sunPosition:vectorto(newPos)*KM_PER_MLY;
      return newVector:getx(), newVector:gety(), newVector:getz(), true;
   end;
};

Of course this isn't a physically possible orbit (except perhaps for a spacecraft with advanced alien technology), but it makes a good test. It's also somewhat interesting to watch, especially for its near-misses with Mercury, Venus and Sol. (I've become curious whether any actual collisions will occur. I'll have to write a Lua script to check that out sometime...)

Once the Lua orbit object is defined, I attach it to the object with a new celx call:

Code: Select all

            moov = celestia:find("Sol/Moov");
            moov:setorbitluahook(orbittest);

Once this is done, the orbittest:computeposition method will be called by Celestia to find the position of the object at a given time. This happens because of a hook I've inserted in Celestia's SampledOrbit::computePosition method:

Code: Select all

   if (luaHook && luaHook->callLuaHook(this,"computeposition", jd, &pos)) return pos;

This hook uses a new form of the LuaState::callLuaHook method which calls a Lua hook method with a double as input and a point3D as output. With the hook in place, I can add any kind of orbit that can be computed using Lua, and attach it to any Celestia object.

Ideally it would be possible to specify the Lua orbit in the .ssc file rather than using a hook in SampledOrbit. This would require a modification of the parser for .ssc files and a new ScriptedObject subclass of CachedOrbit. I don't plan to attempt that, but here's a suggestion as to how it might work:

A factory function for creating parameterized orbits would be defined in the Lua init file. For example, one could be defined for orbits that are midway between any two objects. It might look like this:

Code: Select all

midpointOrbit =
   function(args)
      return
         {
            this.arg = args;
            computeposition =
               function(this,jd)
                  if not this.sun then
                     this.sun = celestia:find("Sol");
                  end;
                  this.obj1 = celestia:find(this.arg.fromObject);
                  this.obj2 = celestia:find(this.arg.toObject);
                  local obj1Position = this.obj1:getposition(jd);
                  local obj2Position  = this.obj2:getposition(jd);
                  local sunPosition   = this.sun:getposition(jd);
                  local objVector    = obj1Position:vectorto(obj2Position);
                  local newPos        = obj1Position:addvector( objVector*0.5 );
                  local newVector = sunPosition:vectorto(newPos)*KM_PER_MLY;
                  return newVector:getx(), newVector:gety(), newVector:getz(), true;
               end;

         end;

The .ssc parser could be rewritten to accept the following syntax:

Code: Select all

ScriptedOrbit "midpointOrbit" {
   fromObject "Sol/Earth"
   toObject "Sol/Mars"
}

To process this construct, the specified Lua function ("midpointOrbit") would be called with the specified parameters (fromObject,toObject) passed in a Lua table as its argument. The function would return a Lua orbit object which would be stored in a ScriptedOrbit defined for the .ssc object. The ScriptedOrbit::computePosition method would call this Lua object to get the position at a specified time. This would not use a hook per se, but would be built into the ScriptedOrbit object. A built-in ScriptedOrbit will be nice once it's implemented, but for experimentation in the meantime a hook will suffice.

- Hank

Topic author
hank
Developer
Posts: 645
Joined: 03.02.2002
With us: 22 years 9 months
Location: Seattle, WA USA

Post #25by hank » 26.09.2006, 00:48

In another experiment using Lua with Celestia, I implemented a celx interface to the LoadSolarSystemObjects function. A celx:loadssc function allows specific .ssc files to be loaded from Lua. It seems to work, although I haven't thoroughly tested it. Here's the code:

Code: Select all

static int celestia_loadssc(lua_State* l)
{
    checkArgs(l, 2, 2, "One argument expected to celestia:loadssc()");
    CelestiaCore* appCore = this_celestia(l);
    const char* filename = safeGetString(l, 2, AllErrors, "Argument to celestia:loadssc must be a string");
   Universe*   universe = appCore->getSimulation()->getUniverse();
   ifstream solarSysFile(filename, ios::in);
   if (!solarSysFile.good())
   {
        doError(l, "Error opening solar system catalog.\n");
   }
   else
   {
      string fname = filename;
      string path = "";
      int pathPos = fname.rfind('/');
      if (pathPos != (int)string::npos)
      {
         path = string(fname, 0, pathPos);
      }
      LoadSolarSystemObjects(solarSysFile, *universe, path);
   }
    return 0;
}

I believe this capability could enable the use of Lua to implement a flexible add-on management facility. I think it should also be possible (although I haven't tested it) to use this in conjunction with the "Replace" and "Modify" object dispositions to update object definitions on the fly (without restarting Celestia). That might be useful in some situations.

- Hank

bidmaron
Posts: 44
Joined: 10.04.2005
With us: 19 years 7 months
Location: Portsmouth, NH

Adaptive parser

Post #26by bidmaron » 12.10.2006, 11:24

Why not add a hook such that any Celestia-uncrecognized token in an stc or ssc or dsc file is passed to the Lua hook for parsing by a Lua routine? This would permit new objects to be defined in catalog files without changing base Celestia code and to add parameters (e.g. for games, like 'Population') to existing data.
--Dale--

chris
Site Admin
Posts: 4211
Joined: 28.01.2002
With us: 22 years 9 months
Location: Seattle, Washington, USA

Post #27by chris » 15.11.2006, 11:07

hank wrote:I've been experimenting with Lua-defined orbits using the hook mechanism described above. Here's a brief explanation of what I've done.

It turns out that I need ScriptedOrbits for a project that I'm working on . . .

hank wrote:Ideally it would be possible to specify the Lua orbit in the .ssc file rather than using a hook in SampledOrbit. This would require a modification of the parser for .ssc files and a new ScriptedObject subclass of CachedOrbit. I don't plan to attempt that, but here's a suggestion as to how it might work:

A factory function for creating parameterized orbits would be defined in the Lua init file. For example, one could be defined for orbits that are midway between any two objects. It might look like this:

Code: Select all

midpointOrbit =
   function(args)
      return
         {
            this.arg = args;
            computeposition =
               function(this,jd)
                  if not this.sun then
                     this.sun = celestia:find("Sol");
                  end;
                  this.obj1 = celestia:find(this.arg.fromObject);
                  this.obj2 = celestia:find(this.arg.toObject);
                  local obj1Position = this.obj1:getposition(jd);
                  local obj2Position  = this.obj2:getposition(jd);
                  local sunPosition   = this.sun:getposition(jd);
                  local objVector    = obj1Position:vectorto(obj2Position);
                  local newPos        = obj1Position:addvector( objVector*0.5 );
                  local newVector = sunPosition:vectorto(newPos)*KM_PER_MLY;
                  return newVector:getx(), newVector:gety(), newVector:getz(), true;
               end;

         end;

The .ssc parser could be rewritten to accept the following syntax:

Code: Select all

ScriptedOrbit "midpointOrbit" {
   fromObject "Sol/Earth"
   toObject "Sol/Mars"
}


I implemented something very close to this proposal. Here's a sample usage:

Code: Select all

"Scripted" "Sol/Ida"
{
    Texture "io.*"
    Radius 500

    ScriptedOrbit
    {
        Module "orbits"
        Function "lissajous"

        PeriodX 1
        PeriodY 3.1
        PeriodZ 2.4
        PhaseX 0.5
        PhaseY 0.2
        PhaseZ 0.0
        AmplitudeX 1000
        AmplitudeY 800
        AmplitudeZ 540
    }
}


Module gives the name of a package that will be loaded via Lua's require method. Presently, it searches some standard set of paths, but with a small bit of work it the package loader could be made to search the add-on directory too. Function is the name of a factory function that produces a table with the orbit properties, including a function that returns the position at a particular time. All of the ScriptOrbit properties other than Module and Function are passed on to the Lua factory function to create the orbit object.

Here's the implementation of a Lissajous orbit:

Code: Select all

function lissajous(t)
   local orbit = {};

   orbit.params = t;

   orbit.boundingRadius =
       math.sqrt(t.AmplitudeX * t.AmplitudeX +
                 t.AmplitudeY * t.AmplitudeY +
                 t.AmplitudeZ * t.AmplitudeZ)

   function orbit:position(tjd)
       local t = tjd - 2451545.0
       local pi2 = math.pi * 2;
       local x = self.params.AmplitudeX * math.sin((t / self.params.PeriodX + self.params.PhaseX) * pi2)
       local y = self.params.AmplitudeY * math.sin((t / self.params.PeriodY + self.params.PhaseY) * pi2)
       local z = self.params.AmplitudeZ * math.sin((t / self.params.PeriodZ + self.params.PhaseZ) * pi2)

       return x, y, z
    end

    return orbit
end


period, begin, and end are additional optional number properties that aren't used in my example. I've checked in the code, but it's disabled by default; I'm inclined to leave it that way until after 1.5.0, since this release is already suffering heavily from featuritis. I'm posting mostly to see if anyone has comments on Hank's and my ideas for ScriptedOrbits.

See this link if you're wondering what Lissajous curves are:
http://mathworld.wolfram.com/LissajousCurve.html

In astrodynamics, Lissajous orbits are used for spacecraft stationed near the collinear Lagrange points--they're only semi-stable, but just minimal maneuvering is required to maintain one.

--Chris

Avatar
selden
Developer
Posts: 10192
Joined: 04.09.2002
With us: 22 years 2 months
Location: NY, USA

Post #28by selden » 15.11.2006, 12:22

How would the orbit path of a ScriptedOrbit be drawn?

Aren't time boundaries and path step sizes going to be needed, too?

Parenthetical comment:

One of the things that I've been wanting to do is to adjust orbital parameters in realtime so that I can match EllipticalOrbit parameters to xyz trajectories -- either to determine what Keplerian parameters produce an orbit similar to the trajectory or to determine what EllipticalOrbits would best match with the end points of a trajectory.

It looks like this Lua functionality could be a start toward having this ability, although it doesn't provide access to the other features that would be needed.
Specifically:
+ read and write access to the EllipticalOrbit parameters
+ orbit path locations updated on demand instead of being calculated once.

I suspect this latter may be needed for displaying the paths of Scripted Orbits in general, since there's no guarantee that they'd always return the same values for the same simulation epoch.
Selden

chris
Site Admin
Posts: 4211
Joined: 28.01.2002
With us: 22 years 9 months
Location: Seattle, Washington, USA

Post #29by chris » 15.11.2006, 21:42

selden wrote:How would the orbit path of a ScriptedOrbit be drawn?

Aren't time boundaries and path step sizes going to be needed, too?

Time boundaries are provided by the begin and end properties. But for the Lissajous example, orbit rendering doesn't work very well. This is a general problem affecting all non-periodic orbits, though. I envision a plotting tool will be useful in the future, where the number of steps, time span, line style, and reference frame can all be specified.

Parenthetical comment:

One of the things that I've been wanting to do is to adjust orbital parameters in realtime so that I can match EllipticalOrbit parameters to xyz trajectories -- either to determine what Keplerian parameters produce an orbit similar to the trajectory or to determine what EllipticalOrbits would best match with the end points of a trajectory.

It looks like this Lua functionality could be a start toward having this ability, although it doesn't provide access to the other features that would be needed.
Specifically:
+ read and write access to the EllipticalOrbit parameters
+ orbit path locations updated on demand instead of being calculated once.

You could implement a modifiable elliptical orbit with a script, but you'd have to replicate all the calculations in the C++ code. But, if you're interested in approximating a sampled orbit with an elliptical orbit, why not convert the state vector (position and velocity) to a set of orbital elements for some point on the orbit? I can give you some code to do this (it's even in Celestia.)

I suspect this latter may be needed for displaying the paths of Scripted Orbits in general, since there's no guarantee that they'd always return the same values for the same simulation epoch.


A well-behaved ScriptedOrbit should always return the same value at a particular time. I *might* allow a scripted orbit to report a property that indicates it doesn't obey this constraint, for a trajectory based on the camera position or data from a real-time feed.

--Chris

Avatar
selden
Developer
Posts: 10192
Joined: 04.09.2002
With us: 22 years 2 months
Location: NY, USA

Post #30by selden » 15.11.2006, 22:31

chris wrote:
selden wrote:How would the orbit path of a ScriptedOrbit be drawn?

Aren't time boundaries and path step sizes going to be needed, too?

Time boundaries are provided by the begin and end properties. But for the Lissajous example, orbit rendering doesn't work very well. This is a general problem affecting all non-periodic orbits, though. I envision a plotting tool will be useful in the future, where the number of steps, time span, line style, and reference frame can all be specified.

Do you mean a plotting tool that is built into Celestia (i.e. selectable orbit path parameters)
or do you envision a separate (Lua?) program that'd invoke OpenGL methods in Celestia
or something else?

Parenthetical comment:
[omitted]

You could implement a modifiable elliptical orbit with a script, but you'd have to replicate all the calculations in the C++ code. But, if you're interested in approximating a sampled orbit with an elliptical orbit, why not convert the state vector (position and velocity) to a set of orbital elements for some point on the orbit? I can give you some code to do this (it's even in Celestia.)

That probably would help, although I really, really want to be able to twiddle the Keplerian orbital parameters individually in realtime.

I would expect this to be quite educational, not only for me, but for others who do not yet really understand their interactions.

I suspect this latter may be needed for displaying the paths of Scripted Orbits in general, since there's no guarantee that they'd always return the same values for the same simulation epoch.

A well-behaved ScriptedOrbit should always return the same value at a particular time. I *might* allow a scripted orbit to report a property that indicates it doesn't obey this constraint, for a trajectory based on the camera position or data from a real-time feed.


To the extent that a Lua function can return any value it wants, I think Celestia's code has to be prepared to handle this kind of non-well-behaved orbital information. I seem to recall that the desirability of being able to display the results of realtime optimizations of orbital trajectories was one of the topics in another thread earlier this year.
Selden


Return to “Development”