572 lines
17 KiB
C++
572 lines
17 KiB
C++
// cmdparser.cpp
|
|
//
|
|
// Parse Celestia command files and turn them into CommandSequences.
|
|
//
|
|
// Copyright (C) 2001 Chris Laurel <claurel@shatters.net>
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include <algorithm>
|
|
|
|
#include "celestia.h"
|
|
// Ugh . . . the C++ standard says that stringstream should be in
|
|
// sstream, but the GNU C++ compiler uses strstream instead.
|
|
#ifdef HAVE_SSTREAM
|
|
#include <sstream>
|
|
#else
|
|
#include <strstream>
|
|
#endif // HAVE_SSTREAM
|
|
|
|
#include <celutil/util.h>
|
|
#include <celutil/debug.h>
|
|
#include <celmath/mathlib.h>
|
|
#include "astro.h"
|
|
#include "cmdparser.h"
|
|
|
|
using namespace std;
|
|
|
|
|
|
static int parseRenderFlags(string);
|
|
static int parseLabelFlags(string);
|
|
|
|
CommandParser::CommandParser(istream& in)
|
|
{
|
|
tokenizer = new Tokenizer(&in);
|
|
parser = new Parser(tokenizer);
|
|
}
|
|
|
|
CommandParser::CommandParser(Tokenizer& tok)
|
|
{
|
|
tokenizer = &tok;
|
|
parser = new Parser(tokenizer);
|
|
}
|
|
|
|
CommandParser::~CommandParser()
|
|
{
|
|
delete parser;
|
|
delete tokenizer;
|
|
}
|
|
|
|
|
|
CommandSequence* CommandParser::parse()
|
|
{
|
|
CommandSequence* seq = new CommandSequence();
|
|
|
|
if (tokenizer->nextToken() != Tokenizer::TokenBeginGroup)
|
|
{
|
|
error("'{' expected at start of script.");
|
|
delete seq;
|
|
return NULL;
|
|
}
|
|
|
|
Tokenizer::TokenType ttype = tokenizer->nextToken();
|
|
while (ttype != Tokenizer::TokenEnd && ttype != Tokenizer::TokenEndGroup)
|
|
{
|
|
tokenizer->pushBack();
|
|
Command* cmd = parseCommand();
|
|
if (cmd == NULL)
|
|
{
|
|
for (CommandSequence::const_iterator iter = seq->begin();
|
|
iter != seq->end();
|
|
iter++)
|
|
{
|
|
delete *iter;
|
|
}
|
|
delete seq;
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
seq->insert(seq->end(), cmd);
|
|
}
|
|
|
|
ttype = tokenizer->nextToken();
|
|
}
|
|
|
|
if (ttype != Tokenizer::TokenEndGroup)
|
|
{
|
|
error("Missing '}' at end of script.");
|
|
for_each(seq->begin(), seq->end(), deleteFunc<Command*>());;
|
|
delete seq;
|
|
return NULL;
|
|
}
|
|
|
|
return seq;
|
|
}
|
|
|
|
|
|
const vector<string>* CommandParser::getErrors() const
|
|
{
|
|
return &errorList;
|
|
}
|
|
|
|
|
|
void CommandParser::error(const string errMsg)
|
|
{
|
|
errorList.insert(errorList.end(), errMsg);
|
|
}
|
|
|
|
|
|
static astro::CoordinateSystem parseCoordinateSystem(const string& name)
|
|
{
|
|
if (compareIgnoringCase(name, "observer") == 0)
|
|
return astro::ObserverLocal;
|
|
else if (compareIgnoringCase(name, "geographic") == 0)
|
|
return astro::Geographic;
|
|
else if (compareIgnoringCase(name, "equatorial") == 0)
|
|
return astro::Equatorial;
|
|
else if (compareIgnoringCase(name, "ecliptical") == 0)
|
|
return astro::Ecliptical;
|
|
else if (compareIgnoringCase(name, "universal") == 0)
|
|
return astro::Universal;
|
|
else if (compareIgnoringCase(name, "lock") == 0)
|
|
return astro::PhaseLock;
|
|
else if (compareIgnoringCase(name, "chase") == 0)
|
|
return astro::Chase;
|
|
else
|
|
return astro::ObserverLocal;
|
|
}
|
|
|
|
|
|
Command* CommandParser::parseCommand()
|
|
{
|
|
if (tokenizer->nextToken() != Tokenizer::TokenName)
|
|
{
|
|
error("Invalid command name");
|
|
return NULL;
|
|
}
|
|
|
|
string commandName = tokenizer->getStringValue();
|
|
|
|
Value* paramListValue = parser->readValue();
|
|
if (paramListValue == NULL || paramListValue->getType() != Value::HashType)
|
|
{
|
|
error("Bad parameter list");
|
|
return NULL;
|
|
}
|
|
|
|
Hash* paramList = paramListValue->getHash();
|
|
Command* cmd = NULL;
|
|
|
|
if (commandName == "wait")
|
|
{
|
|
double duration = 1.0;
|
|
paramList->getNumber("duration", duration);
|
|
cmd = new CommandWait(duration);
|
|
}
|
|
else if (commandName == "set")
|
|
{
|
|
double value = 0.0;
|
|
string name;
|
|
paramList->getString("name", name);
|
|
paramList->getNumber("value", value);
|
|
cmd = new CommandSet(name, value);
|
|
}
|
|
else if (commandName == "select")
|
|
{
|
|
string object;
|
|
paramList->getString("object", object);
|
|
cmd = new CommandSelect(object);
|
|
}
|
|
else if (commandName == "setframe")
|
|
{
|
|
string refName;
|
|
paramList->getString("ref", refName);
|
|
string targetName;
|
|
paramList->getString("target", targetName);
|
|
string coordSysName;
|
|
astro::CoordinateSystem coordSys = astro::Universal;
|
|
if (paramList->getString("coordsys", coordSysName))
|
|
coordSys = parseCoordinateSystem(coordSysName);
|
|
|
|
cmd = new CommandSetFrame(coordSys, refName, targetName);
|
|
}
|
|
else if (commandName == "goto")
|
|
{
|
|
double t = 1.0;
|
|
paramList->getNumber("time", t);
|
|
double distance = 5.0;
|
|
paramList->getNumber("distance", distance);
|
|
|
|
astro::CoordinateSystem upFrame = astro::ObserverLocal;
|
|
string frameString;
|
|
if (paramList->getString("upframe", frameString))
|
|
upFrame = parseCoordinateSystem(frameString);
|
|
|
|
Vec3d up(0, 1, 0);
|
|
paramList->getVector("up", up);
|
|
|
|
cmd = new CommandGoto(t,
|
|
distance,
|
|
Vec3f((float) up.x, (float) up.y, (float) up.z),
|
|
upFrame);
|
|
}
|
|
else if (commandName == "gotolonglat")
|
|
{
|
|
double t = 1.0;
|
|
paramList->getNumber("time", t);
|
|
double distance = 5.0;
|
|
paramList->getNumber("distance", distance);
|
|
Vec3d up(0, 1, 0);
|
|
paramList->getVector("up", up);
|
|
double longitude;
|
|
paramList->getNumber("longitude", longitude);
|
|
double latitude;
|
|
paramList->getNumber("latitude", latitude);
|
|
|
|
cmd = new CommandGotoLongLat(t,
|
|
distance,
|
|
(float) degToRad(longitude),
|
|
(float) degToRad(latitude),
|
|
Vec3f((float) up.x, (float) up.y, (float) up.z));
|
|
}
|
|
else if (commandName == "gotoloc")
|
|
{
|
|
double t = 1.0;
|
|
paramList->getNumber("time", t);
|
|
Vec3d pos(0, 1, 0);
|
|
paramList->getVector("position", pos);
|
|
pos = pos * astro::kilometersToMicroLightYears(1.0);
|
|
double xrot = 0.0;
|
|
paramList->getNumber("xrot", xrot);
|
|
double yrot = 0.0;
|
|
paramList->getNumber("yrot", yrot);
|
|
double zrot = 0.0;
|
|
paramList->getNumber("zrot", zrot);
|
|
zrot = degToRad(zrot);
|
|
Quatf rotation = Quatf::xrotation((float) degToRad(xrot)) *
|
|
Quatf::yrotation((float) degToRad(yrot)) *
|
|
Quatf::zrotation((float) degToRad(zrot));
|
|
cmd = new CommandGotoLocation(t, Point3d(0.0, 0.0, 0.0) + pos,
|
|
rotation);
|
|
}
|
|
else if (commandName == "center")
|
|
{
|
|
double t = 1.0;
|
|
paramList->getNumber("time", t);
|
|
cmd = new CommandCenter(t);
|
|
}
|
|
else if (commandName == "follow")
|
|
{
|
|
cmd = new CommandFollow();
|
|
}
|
|
else if (commandName == "synchronous")
|
|
{
|
|
cmd = new CommandSynchronous();
|
|
}
|
|
else if (commandName == "lock")
|
|
{
|
|
cmd = new CommandLock();
|
|
}
|
|
else if (commandName == "chase")
|
|
{
|
|
cmd = new CommandChase();
|
|
}
|
|
else if (commandName == "cancel")
|
|
{
|
|
cmd = new CommandCancel();
|
|
}
|
|
else if (commandName == "print")
|
|
{
|
|
string text;
|
|
string origin;
|
|
int horig = -1;
|
|
int vorig = -1;
|
|
double hoff = 0;
|
|
double voff = 0;
|
|
double duration = 1.0e9;
|
|
paramList->getString("text", text);
|
|
paramList->getString("origin", origin);
|
|
paramList->getNumber("duration", duration);
|
|
paramList->getNumber("row", voff);
|
|
paramList->getNumber("column", hoff);
|
|
if (compareIgnoringCase(origin, "left") == 0)
|
|
{
|
|
horig = -1;
|
|
vorig = 0;
|
|
}
|
|
else if (compareIgnoringCase(origin, "right") == 0)
|
|
{
|
|
horig = 1;
|
|
vorig = 0;
|
|
}
|
|
else if (compareIgnoringCase(origin, "center") == 0)
|
|
{
|
|
horig = 0;
|
|
vorig = 0;
|
|
}
|
|
else if (compareIgnoringCase(origin, "left") == 0)
|
|
{
|
|
horig = -1;
|
|
vorig = 0;
|
|
}
|
|
else if (compareIgnoringCase(origin, "top") == 0)
|
|
{
|
|
horig = 0;
|
|
vorig = 1;
|
|
}
|
|
else if (compareIgnoringCase(origin, "bottom") == 0)
|
|
{
|
|
horig = 0;
|
|
vorig = -1;
|
|
}
|
|
else if (compareIgnoringCase(origin, "topright") == 0)
|
|
{
|
|
horig = 1;
|
|
vorig = 1;
|
|
}
|
|
else if (compareIgnoringCase(origin, "topleft") == 0)
|
|
{
|
|
horig = -1;
|
|
vorig = 1;
|
|
}
|
|
else if (compareIgnoringCase(origin, "bottomleft") == 0)
|
|
{
|
|
horig = -1;
|
|
vorig = -1;
|
|
}
|
|
else if (compareIgnoringCase(origin, "bottomright") == 0)
|
|
{
|
|
horig = 1;
|
|
vorig = -1;
|
|
}
|
|
|
|
cmd = new CommandPrint(text,
|
|
horig, vorig, (int) hoff, (int) -voff,
|
|
duration);
|
|
}
|
|
else if (commandName == "cls")
|
|
{
|
|
cmd = new CommandClearScreen();
|
|
}
|
|
else if (commandName == "time")
|
|
{
|
|
double jd = 2451545;
|
|
paramList->getNumber("jd", jd);
|
|
cmd = new CommandSetTime(jd);
|
|
}
|
|
else if (commandName == "timerate")
|
|
{
|
|
double rate = 1.0;
|
|
paramList->getNumber("rate", rate);
|
|
cmd = new CommandSetTimeRate(rate);
|
|
}
|
|
else if (commandName == "changedistance")
|
|
{
|
|
double rate = 0.0;
|
|
double duration = 1.0;
|
|
paramList->getNumber("rate", rate);
|
|
paramList->getNumber("duration", duration);
|
|
cmd = new CommandChangeDistance(duration, rate);
|
|
}
|
|
else if (commandName == "orbit")
|
|
{
|
|
double rate = 0.0;
|
|
double duration = 1.0;
|
|
Vec3d axis;
|
|
paramList->getNumber("duration", duration);
|
|
paramList->getNumber("rate", rate);
|
|
paramList->getVector("axis", axis);
|
|
cmd = new CommandOrbit(duration,
|
|
Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
|
|
(float) degToRad(rate));
|
|
}
|
|
else if (commandName == "rotate")
|
|
{
|
|
double rate = 0.0;
|
|
double duration = 1.0;
|
|
Vec3d axis;
|
|
paramList->getNumber("duration", duration);
|
|
paramList->getNumber("rate", rate);
|
|
paramList->getVector("axis", axis);
|
|
cmd = new CommandRotate(duration,
|
|
Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
|
|
(float) degToRad(rate));
|
|
}
|
|
else if (commandName == "move")
|
|
{
|
|
Vec3d velocity;
|
|
double duration;
|
|
paramList->getNumber("duration", duration);
|
|
paramList->getVector("velocity", velocity);
|
|
cmd = new CommandMove(duration, velocity * astro::kilometersToLightYears(1.0));
|
|
}
|
|
else if (commandName == "setposition")
|
|
{
|
|
Vec3d base, offset;
|
|
paramList->getVector("base", base);
|
|
paramList->getVector("offset", offset);
|
|
cmd = new CommandSetPosition(astro::universalPosition(Point3d(offset.x, offset.y, offset.z),
|
|
Point3f((float) base.x, (float) base.y, (float) base.z)));
|
|
}
|
|
else if (commandName == "setorientation")
|
|
{
|
|
Vec3d axis;
|
|
double angle;
|
|
paramList->getNumber("angle", angle);
|
|
paramList->getVector("axis", axis);
|
|
cmd = new CommandSetOrientation(Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
|
|
(float) degToRad(angle));
|
|
}
|
|
else if (commandName == "renderflags")
|
|
{
|
|
int setFlags = 0;
|
|
int clearFlags = 0;
|
|
string s;
|
|
|
|
if (paramList->getString("set", s))
|
|
setFlags = parseRenderFlags(s);
|
|
if (paramList->getString("clear", s))
|
|
clearFlags = parseRenderFlags(s);
|
|
|
|
cmd = new CommandRenderFlags(setFlags, clearFlags);
|
|
}
|
|
else if (commandName == "labels")
|
|
{
|
|
int setFlags = 0;
|
|
int clearFlags = 0;
|
|
string s;
|
|
|
|
if (paramList->getString("set", s))
|
|
setFlags = parseLabelFlags(s);
|
|
if (paramList->getString("clear", s))
|
|
clearFlags = parseLabelFlags(s);
|
|
|
|
cmd = new CommandLabels(setFlags, clearFlags);
|
|
}
|
|
else if (commandName == "setvisibilitylimit")
|
|
{
|
|
double mag = 6.0;
|
|
paramList->getNumber("magnitude", mag);
|
|
cmd = new CommandSetVisibilityLimit(mag);
|
|
}
|
|
else if (commandName == "setambientlight")
|
|
{
|
|
double brightness = 0.0;
|
|
paramList->getNumber("brightness", brightness);
|
|
cmd = new CommandSetAmbientLight((float) brightness);
|
|
}
|
|
else if (commandName == "preloadtex")
|
|
{
|
|
string object;
|
|
paramList->getString("object", object);
|
|
cmd = new CommandPreloadTextures(object);
|
|
}
|
|
else
|
|
{
|
|
error("Unknown command name '" + commandName + "'");
|
|
cmd = NULL;
|
|
}
|
|
|
|
delete paramListValue;
|
|
|
|
return cmd;
|
|
}
|
|
|
|
|
|
int parseRenderFlags(string s)
|
|
{
|
|
#ifdef HAVE_SSTREAM
|
|
istringstream in(s);
|
|
#else
|
|
istrstream in(s.c_str());
|
|
#endif
|
|
Tokenizer tokenizer(&in);
|
|
int flags = 0;
|
|
|
|
Tokenizer::TokenType ttype = tokenizer.nextToken();
|
|
while (ttype != Tokenizer::TokenEnd)
|
|
{
|
|
if (ttype == Tokenizer::TokenName)
|
|
{
|
|
string name = tokenizer.getNameValue();
|
|
if (compareIgnoringCase(name, "orbits") == 0)
|
|
flags |= Renderer::ShowOrbits;
|
|
else if (compareIgnoringCase(name, "cloudmaps") == 0)
|
|
flags |= Renderer::ShowCloudMaps;
|
|
else if (compareIgnoringCase(name, "constellations") == 0)
|
|
flags |= Renderer::ShowDiagrams;
|
|
else if (compareIgnoringCase(name, "galaxies") == 0)
|
|
flags |= Renderer::ShowGalaxies;
|
|
else if (compareIgnoringCase(name, "planets") == 0)
|
|
flags |= Renderer::ShowPlanets;
|
|
else if (compareIgnoringCase(name, "stars") == 0)
|
|
flags |= Renderer::ShowStars;
|
|
else if (compareIgnoringCase(name, "nightmaps") == 0)
|
|
flags |= Renderer::ShowNightMaps;
|
|
else if (compareIgnoringCase(name, "eclipseshadows") == 0)
|
|
flags |= Renderer::ShowEclipseShadows;
|
|
else if (compareIgnoringCase(name, "ringshadows") == 0)
|
|
flags |= Renderer::ShowRingShadows;
|
|
else if (compareIgnoringCase(name, "pointstars") == 0)
|
|
flags |= Renderer::ShowStarsAsPoints;
|
|
else if (compareIgnoringCase(name, "ringshadows") == 0)
|
|
flags |= Renderer::ShowRingShadows;
|
|
else if (compareIgnoringCase(name, "comettails") == 0)
|
|
flags |= Renderer::ShowCometTails;
|
|
else if (compareIgnoringCase(name, "boundaries") == 0)
|
|
flags |= Renderer::ShowBoundaries;
|
|
|
|
ttype = tokenizer.nextToken();
|
|
if (ttype == Tokenizer::TokenBar)
|
|
ttype = tokenizer.nextToken();
|
|
}
|
|
else
|
|
{
|
|
DPRINTF(0, "Command Parser: error parsing render flags\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
|
|
int parseLabelFlags(string s)
|
|
{
|
|
#ifdef HAVE_SSTREAM
|
|
istringstream in(s);
|
|
#else
|
|
istrstream in(s.c_str());
|
|
#endif
|
|
Tokenizer tokenizer(&in);
|
|
int flags = 0;
|
|
|
|
Tokenizer::TokenType ttype = tokenizer.nextToken();
|
|
while (ttype != Tokenizer::TokenEnd)
|
|
{
|
|
if (ttype == Tokenizer::TokenName)
|
|
{
|
|
string name = tokenizer.getNameValue();
|
|
if (compareIgnoringCase(name, "planets") == 0)
|
|
flags |= Renderer::PlanetLabels;
|
|
else if (compareIgnoringCase(name, "moons") == 0)
|
|
flags |= Renderer::MoonLabels;
|
|
else if (compareIgnoringCase(name, "spacecraft") == 0)
|
|
flags |= Renderer::SpacecraftLabels;
|
|
else if (compareIgnoringCase(name, "asteroids") == 0)
|
|
flags |= Renderer::AsteroidLabels;
|
|
else if (compareIgnoringCase(name, "constellations") == 0)
|
|
flags |= Renderer::ConstellationLabels;
|
|
else if (compareIgnoringCase(name, "stars") == 0)
|
|
flags |= Renderer::StarLabels;
|
|
else if (compareIgnoringCase(name, "galaxies") == 0)
|
|
flags |= Renderer::GalaxyLabels;
|
|
|
|
ttype = tokenizer.nextToken();
|
|
if (ttype == Tokenizer::TokenBar)
|
|
ttype = tokenizer.nextToken();
|
|
}
|
|
else
|
|
{
|
|
DPRINTF(0, "Command Parser: error parsing label flags\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return flags;
|
|
}
|