# smlrecorder.sml
# Script for TNTsim3D that records and plays flight paths using controls
# in a dialog created in and opened by the script. Position information for
# a flight path is recorded in a set of numeric arrays for replay. Recording
# a new flight path replaces the last one in memory. The current flight path
# can be saved to a text file (Save button) and reloaded for replay (Open button).
#####################################################################################
### For a script to run under TNTsim3D, it must include an instance of
### class TNTSIM3D named "TNTsim3D". The script must also be saved as an RVC
### object in the TNTsim3D Landscape File you want to run it with.
#####################################################################################
# declare class instance for TNTsim3D interface class
class TNTSIM3D TNTsim3D;
# declare arrays to hold viewer position coordinates and orientation angles in memory
numeric maxindices = 10000;
array numeric viewerx[maxindices];
array numeric viewery[maxindices];
array numeric viewerz[maxindices];
array numeric orientationx[maxindices];
array numeric orientationy[maxindices];
array numeric orientationz[maxindices];
numeric numindices = 0; # number of indices in array set
numeric index = 0; # current index during record or play
numeric loop = 0;
# State of recorder: 0 = idle, 1 = record, 2 = play, set by toggles on dialog
numeric state = 0;
# class declarations for dialog controls
class GUI_CTRL_PUSHBUTTON save;
class GUI_CTRL_TOGGLEBUTTON record, play, pause, stop;
class GUI_CTRL_LABEL numberframes, frame;
######################################################################
### callback procedures for control dialog
######################################################################
proc Enable () { # procedure to enable dialog controls when recording
save.SetEnabled(1);
play.SetEnabled(1);
pause.SetEnabled(1);
stop.SetEnabled(1);
}
proc OnLoop () { # reset loop status when checkbox is changed
loop = !loop;
}
proc OnRecord () { # called when Record button is pressed;
Enable(); # enable controls, set state to record,
state = 1; # initialize counters, and set status of controls
index = 0;
numindices = 0;
record.SetValue(1, 0);
play.SetValue(0, 0);
pause.SetValue(0, 0);
stop.SetValue(0, 0);
}
proc OnPlay () { # called when Play button is pressed;
state = 2; # set state to play and set status of controls
play.SetValue(1, 0);
record.SetValue(0, 0);
pause.SetValue(0, 0);
stop.SetValue(0, 0);
}
proc OnPause () { # called when Pause button is pressed;
state = 0; # set state to pause and set status of controls
pause.SetValue(1, 0);
stop.SetValue(0, 0);
}
proc OnStop () {
state = 0;
index = 0;
stop.SetValue(1, 0);
record.SetValue(0, 0);
play.SetValue(0, 0);
pause.SetValue(0, 0);
frame.SetLabel("");
}
### procedure to save the recorded flight path in a text file; called when Save
### button on the dialog is pressed
proc OnSave (
) {
local class FILE file = GetOutputTextFile("", "Select Output File", "txt");
if (file == 0) return;
local numeric idx;
for (idx = 0; idx < numindices; idx++)
fwritestring(file, sprintf("%f,%f,%f,%f,%f,%f\n", viewerx[idx], viewery[idx], viewerz[idx], orientationx[idx], orientationy[idx], orientationz[idx]));
OnStop();
}
### procedure to open a flight path from a text file; called when the Open
### button on the dialog is pressed
proc OnOpen (
) {
local class FILE file = GetInputTextFile("", "Select Input File", "txt");
if (file == 0) return;
local string string$ = "something";
numindices = 0;
while (string$ != "") {
string$ = fgetline$(file);
if (NumberTokens(string$, ",") != 6) continue;
viewerx[numindices] = StrToNum(GetToken(string$, ",", 1));
viewery[numindices] = StrToNum(GetToken(string$, ",", 2));
viewerz[numindices] = StrToNum(GetToken(string$, ",", 3));
orientationx[numindices] = StrToNum(GetToken(string$, ",", 4));
orientationy[numindices] = StrToNum(GetToken(string$, ",", 5));
orientationz[numindices] = StrToNum(GetToken(string$, ",", 6));
numindices++;
}
numberframes.SetLabel(NumToStr(numindices));
OnStop();
Enable();
}
########################################################################
### SML function name predefined in TNTsim3D, called for each frame.
### Record or play frame in flight as designated by recorder state.
########################################################################
proc OnFrame () {
# if recording, get current viewer position and orientation and store in arrays
if (state == 1) {
local class POINT3D viewer, orientation;
TNTsim3D.GetSceneByOrientation(viewer, orientation);
viewerx[index] = viewer.x; # viewer position
viewery[index] = viewer.y;
viewerz[index] = viewer.z;
orientationx[index] = orientation.x; # viewer orientation (x=pitch,
orientationy[index] = orientation.y; # y = roll, z = heading azimuth
orientationz[index] = orientation.z;
numberframes.SetLabel(NumToStr(numindices)); # update count labels on dialog
frame.SetLabel(NumToStr(index));
index++; # increment counts
numindices++;
if (numindices >= maxindices)
OnStop();
}
# if playback, read stored viewer position and orientation from
# arrays and use these to set current frame
else if (state == 2) {
local class POINT3D viewer, orientation;
viewer.x = viewerx[index]; # viewer position
viewer.y = viewery[index];
viewer.z = viewerz[index];
orientation.x = orientationx[index]; # viewer orientation (x = pitch,
orientation.y = orientationy[index]; # y = roll, z = heading azimuth
orientation.z = orientationz[index];
TNTsim3D.SetSceneByOrientation(viewer, orientation); # set frame
frame.SetLabel(NumToStr(index)); # update frame count label on dialog
index++;
if (index >= numindices) { # increment frame count
if (loop == 1)
index = 0; # if Loop checkbox is on, restart playback
else OnStop(); # otherwise stop
}
}
}
###########################################################
### Dialog specification in XML
###########################################################
string xml$ = '
';
class XMLDOC doc; # class instance for the XML document
numeric err = doc.Parse(xml$); # read the XML and parse into memory
if (err < 0) { # if no XML document found, exit
PopupError(err);
Exit();
}
class XMLNODE dlgnode; # class instance for the dialog element in the XML structure
dlgnode = doc.GetElementByID("recorder"); # get the dialog element from the XML structure
if (dlgnode == 0) {
PopupMessage("Could not find dialog node in XML document");
Exit();
}
class GUI_DLG dlg; # class instance for the dialog window
dlg.SetXMLNode(dlgnode); # set the XML dialog element as the source for the dialog window
numeric ret;
dlg.CreateModeless();
ret = dlg.Open(); # open as a modeless dialog
if ( ret == -1 ) { # exit script if Cancel button on dialog is pressed
Exit();
}
# get the handle for each dialog control (by its ID) and assign to the previously-declared
# class instance
save = dlg.GetCtrlByID("Save");
record = dlg.GetCtrlByID("Record");
play = dlg.GetCtrlByID("Play");
pause = dlg.GetCtrlByID("Pause");
stop = dlg.GetCtrlByID("Stop");
numberframes = dlg.GetCtrlByID("NumberFrames");
frame = dlg.GetCtrlByID("Frame");