Adding Lua Hooks to Celestia
-
- Site Admin
- Posts: 4211
- Joined: 28.01.2002
- With us: 22 years 9 months
- Location: Seattle, Washington, USA
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
-
- Developer
- Posts: 1356
- Joined: 07.01.2005
- With us: 19 years 10 months
- Location: Nancy, France
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...
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...
@+
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
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 authorhank
- Developer
- Posts: 645
- Joined: 03.02.2002
- With us: 22 years 9 months
- Location: Seattle, WA USA
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:
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:
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:
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:
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:
The .ssc parser could be rewritten to accept the following syntax:
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
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 authorhank
- Developer
- Posts: 645
- Joined: 03.02.2002
- With us: 22 years 9 months
- Location: Seattle, WA USA
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:
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
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
Adaptive parser
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--
-
- Site Admin
- Posts: 4211
- Joined: 28.01.2002
- With us: 22 years 9 months
- Location: Seattle, Washington, USA
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
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.
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
-
- Site Admin
- Posts: 4211
- Joined: 28.01.2002
- With us: 22 years 9 months
- Location: Seattle, Washington, USA
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
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