|
|
|
Appendix E. Inside "Lunar Lander" The implementation of the lunar lander simulation consists of three classes: LmSimulatorShell, LmSimulator and LmLem. The lunar landscape and lunar excursion module (a.k.a., "LEM") were created using the Geometry and Part Editors (see part files lunarsurf2.stb and lander.stb). The landscape is composed of a large disk and several mountainous shapes positioned on the disks surface (See Figure 18). Texturing was accomplished by first applying a texture image to the face of the large disk and then using the "Sync texture" tool to project the same texture onto the mountains.
Figure 18. Lunar surface composed of large disk and mountains LmSimulatorShellLmSimulatorShell implements the shell presenter. The shell view contains an instance of CcOpenglView in which a view from the LEM cockpit is displayed. A container view holding several static text fields is positioned directly below the OpenGL view to display telemetry information LmSimulatorShell is responsible for initializing the LmSimulator, responding to user input and driving the animation process. A process dedicated to running the animation loop is created in LmSimulatorShell>>onViewOpened. The animation loop, implemented in LmSimulatorShell>>refreshViewLoop, executes until the lander lands or the application window is closed. Each pass through the #whileTrue: loop in #refreshViewLoop causes #advanceStateTo: to be invoked and the scene redrawn. #advanceStateTo: calculates the elapsed time since the last refresh and instructs the LmSimulation instance to update the simulation state based on that interval. (This approach is somewhat different from that used in "CoolPool" where an instance of CcTimer is used to drive the animation process). The simulator shell makes use of the DirectInput subsystem to read the keyboard. The keyboard state is checked during each pass through the animation loop.
LmSimulatorLmSimulator is responsible for loading the lunar terrain and LEM models from disk files and adding the models to the scene graph. An instance of CcNodeFollower is associated with the scenes camera to allow the viewpoint to follow the movement of the LEM (either "in cockpit" or "third person"). LmSimulator>>advanceStateBy: coordinates state changes of the LEM and the scenes camera by forwarding the #advanceStateBy: message to the LEM and the cameras node follower. The simulator is also responsible for calculating the altitude of the LEM over the terrain. To determine the LEMs altitude, the simulator first checks to see whether the LEM is over one of the mountains (See Figure 18). If it isnt, the altitude is simply the LEM z coordinate minus the z coordinate of the large disk. If it is over a mountain, then the specific face the LEM is over must be identified. Once that face is known, the altitude is determined by calculating the length of a vertical line segment starting at the LEM and intersecting with the face (See LmSimulator>>calculateLemAltitudeOver:in:). The simulator also manages a change that must occur in the lunar landscape as the camera is moved. When flying over the landscape, it is necessary to keep the center of the disk directly below the camera to maintain the appearance of a slightly curved horizon (and, in the extreme case, to avoid flying off the edge of a flat moon). So, the disk is actually moving along with the camera and LEM while the mountains remain stationary. Unfortunately, any movement of the disk relative to the mountains results in a misalignment between the mountain and disk textures (Comment out the second to last line in LmSimulator>>onCameraMoved and start the simulation to see a "graphic" demonstration of this problem). To compensate for this, LmSimulator adjusts the disks texture coordinates each time the camera is moved.
LmLemLmLem manages state and behavior related to the LEM. This includes the CcPart node instantiated from file "lander.stb" and a collection of node controllers that effect the movement of the node. The node controllers include a CcFlightController, used to control the LEMs attitude, an instance of CcAcceleration to account for the effect of gravity on the LEM, and a second instance of CcAcceleration to account for the force generated by the LEMs main thruster. LmLem receives the message #advanceStateBy: each frame. This message is forwarded to each of the node controllers to allow them to update the velocity and orientation of the part node. LmLem also maintains a set of ValueHolders to hold telemetry data (e.g., altitude, airspeed and descent rate). The use of ValueHolders allows state changes to be reflected in the user interface as they occur (see LmSimulatorShell>>createSchematicWiring). |