World
Wide Guide | Knowledge Bank
| Kukushkin's Notebook |
Design Fundamentals
2-D landscape is composed from the
synthetic scenery and
from 2-D objects of the visual scenery, like ground polygons, roads or runways.
The first step is normally defining the synth scenery. Besides defining the visual appearance of the ground, it is also used for specifying the ground altitude. For this reason, each scenery that uses objects with absolute altitude (like runways) must define the underlying synth scenery. The ground altitude of the default scenery is very unreliable because it differs in different versions of FS5 and also depends from the way FS5 is configured.
Even if all objects in the scenery are placed at ground altitude, the underlying synth scenery should normally be defined in order to prevent surprises like buildings standing in sea or inside synth scenery mountains. The user can resolve scenery conflicts caused by the synth scenery only by adjusting layers in the scenery library in order to bring the 'correct' version of the synth scenery to the top. If a scenery does not define its 'correct' version, the user cannot help himself in case of such problems. Exclusion files do not affect the synth scenery.
The need for specifying realistic values for the synth floor altitude should not be over-estimated. Because of the way synth tiles are displayed, altitude changes between different tiles are never visible. The unrealism of the ground visibly floating up or down is much greater than the realism of having the correct ground altitude at any location. Any landscape where the pilot is supposed to react to visible ground altitude changes cannot be implemented in a realistic way by means of synth scenery. For this reason, the only realism that can be added by this method is a correct altitude of places where the aircraft actually comes in contact with the ground, like airports, and a very average altitude of the landscape above MSL.
Because of this, altitude changes between adjacent synth tiles should always be very small. In the vicinity of airports, the altitude of synth tiles should not change at all in order not to produce confusing visual effects, like runways floating above taxiways or the whole airport suddenly floating up during the final approach. Specifying unrealistic ground altitudes in some locations is the only way to prevent more unrealistic effects from appearing.
The only way of defining the ground with visible altitude changes is to implement it as a 3-D object in the visual scenery. This requires much more effort and knowledge than developing the area of the same size by means of synth scenery/2-D objects, but still can be done.
The appearance of the ground cannot be defined accurately using the synth scenery, because it allows only defining big square pieces. For this reason, the ground is often drawn in the visual scenery, especially in dense areas.
The ground in the visual scenery is normally drawn as a set of polygons. This allows defining exact shapes of different areas. These polygons should normally be textured, but appear in a non-textured form when the Textured Ground option is disabled. A typical code should look like this:
Points( ... )
...
SurfaceColor( ... ) ; Color for the non-textured version IfVarAnd
:No_texture 0340 FFFF ) ; Textured ground enabled?
Bitmap( ... ) ; Yes - load the texture
:No_texture
Poly( ... ) ; Draw the polygon
Ground polygons representing water surfaces should react to the variable
0390 [FSASM:vTexturedWater] instead of 0340. Here, FS5 offers a small help
to the programmer. If the filename specified in the Bitmap() instruction
is WATER.R8, FS5 does NOT load the texture if the textured water
is disabled. The Poly() instruction would then produce a
non-textured polygon. So the IfVarAnd( ... 0390 ... ) instruction can be
omitted if the default water texture WATER.R8 is used.
In photorealistic sceneries, textures are not just a better filling for polygons,
but are used to display details of the landscape instead of vector graphics.
For this reason, polygons displaying photorealistic parts of the scenery
should normally remain textured regardless of the Textured Ground setting,
unless an alternative drawing routine exists that displays the same piece
of landscape using vector graphics (like in Meigs). Overlapping ground polygons
should be avoided, because they would cause each pixel in the overlapped
area to be drawn twice, thus reducing the frame rate. Also, it is often useful
to define all points of all polygons around a specific RefPoint
using only one Points() instruction, instead of putting
a Points() instruction before each polygon. Because many points
are used from several polygons, this can significantly reduce the code size
and sometimes increase the performance.
Sometimes ground polygons completely cover an entire synth tile. In such cases, this tile should be defined as transparent (kind 0000) in order not to draw its surface, which is fully covered and thus never visible. It is very important to ensure that ground polygon(s) that cover it are visible from any distance the synth tile is visible from. Transparent synth tiles are discussed in How to keep frame rates high. Similarly, ground polygons and synth tiles completely covered by 3-D objects should be removed. This is especially important with large 3-D objects, like elevated ground, where underlying flat ground polygons are large enough to affect the frame rate in a negative way. It must be ensured that such 3-D objects are drawn at any scenery complexity setting.
Besides primitive drawing instructions, FS5 also has complex instructions
that can draw roads, taxiways and runways. They draw corresponding objects
and also take care of night lightning. They internally call primitive drawing
instructions, and thus have certain side effects. For example, the line color
after a road drawing instruction is set to non-fading orange at night (was
used to draw lights) and to the surface color during daytime. After a runway
drawing instruction, even the RefPoint gets changed. These side
effects must be carefully observed.
Roads and taxiways normally display lights at night. These lights can be
undesirable, in particular when the scenery provides its own night lightning.
They can be disabled either by setting the surface color to 0E F0
(water color), or by specifying negative width for road/taxiway drawing
instructions. Rivers are normally displayed by road drawing instructions
with the color 0E F0, which prevents night lights from appearing.
Road drawing instructions check the distance between the viewpoint and the
RefPoint (variable 033B) and the relative altitude
(0382) of the viewpoint above the RefPoint. If
either distance or relative altitude exceed certain values, roads are displayed
as thin lines. Both critical values do not depend from the road width or
screen resolution, but they are scaled together with the
RefPoint.
Roads collapsing into thin lines are often undesirable. By setting the variable
033B to 0 using SetVar(), it is possible to eliminate
the horizontal distance limit. However, there is no way to eliminate the
limit on altitude because manipulations on the variable 0382
confuse the graphics engine and result in objects displayed incorrectly.
One solution is to display roads using taxiway drawing instructions. Bill
Roccia has described a method known as "Roccia
Roads".
The behavior of the aircraft on the ground depends on the so-called ground
surface type. There are 3 types of surfaces: smooth, rough and water
("SPLASH!"). By default, the surface type is rough. The RunwayData()
instruction sets the surface type inside the runway to smooth.
For all other surfaces with type other than rough, the surface type must
be set explicitly using the SurfaceType() [FSASM:
SurfaceDef] instruction. This instruction takes the desired
surface type, an altitude and dimensions of a rectangle as parameters. The
altitude does not seem to have any meaning. The rectangle defines the area
with the surface type specified. It is placed north-oriented so that the
RefPoint is in its middle. The rectangle can be transformed using
RotatedCall()/ TransformCall() [FSASM:
RotCall/TransRotCall].
Sometimes the surface type in a more complex area than a rectangle should be defined. In particular, lakes normally do not have rectangular shapes, but should have the surface type set to water in order to produce splashes.
The surface type for such a complex area can be specified by preceding the
SurfaceType() instruction with a SenseBorder()
[FSASM: SurfaceBoundary] instruction. SenseBorder()
should allow the execution of SurfaceType() only if the aircraft
is within the polygon with this specific surface type. A typical code should
look like this:
SenseBorder( :Outside ... ) ; Is the aircraft within the
polygon?
SurfaceType( ... ) ; Specify a BIG
rectangle - actual dimensions of the
:Outside
; area are determined by SenseBorder()
SenseBorder() allows only convex polygons. If a more complex
surface has to be defined, it should be split into convex polygons and each
of them processed separately.
Instead of SenseBorder(), the SensePt() [FSASM:?]
instruction can be used to use pre-defined Points() as polygon
coordinates. It allows reducing the code size when used immediately after
the polygon drawing instruction:
Points( ... ) ; Used both for polygon drawing and
surface type commands
... ; Load a
texture, etc.
Poly( ... ) ; Draw a polygon
SensePt( :Outside ... ) ; Is the aircraft within the
polygon?
SurfaceType( ... ) ; Yes - set the surface type
:Outside
Multiple SurfaceType() instructions can be executed in a row.
In this case, the surface type remains as set in the last instruction executed.
At airports, it is very time and code-consuming to set the surface type for each taxiway. It is normally sufficient to set the smooth surface type in the polygon bounding the airport. Of course, this would lead to a smooth aircraft movement on the airport grass too, but this effect can normally be neglected and efforts be spent on designing more visual features instead.
At most real airports, it is possible to refuel the aircraft. In FS5, airports should have so-called fuel boxes. If the aircraft stops inside a fuel box, it is automatically refueled. This is a much more realistic way to refuel than by using the Sim menu.
A fuel box is implemented by testing aircraft coordinates to be in a certain
area and, if so, setting the refuel flag (variable 0288, FSASM:
vFuelTank) to 1. FS5 refuels the plane when the scenery
sets the fuel flag to 1 and the plane comes to a complete stop. Typical code
should look like this:
...
; Draw the fuel box
Monitor3D( :Do_not_refuel ... ) ; Allow enough Z space for big
planes
SetVar( 0288 1 )
:Do_not_refuel
The Monitor3D() [FSASM: Jump3Ranges] instruction
tests aircraft coordinates to be inside a cube-shaped area. Because FS5 would
not refuel aircraft that are not standing on the ground, a wide range for
the Z coordinate should be specified. This would ensure a correct refueling
of big aircraft.
It is important to restrict refueling to a small area, because the pilot should have the opportunity not to refuel as well. In particular, the whole airport surface should never be defined as a fuel box.
FS5 automatically resets 0288 to 0 before drawing each frame,
thus there is no need to do it inside the scenery. Besides setting the refuel
flag, the fuel box should normally also draw itself. This is normally done
by drawing an 'F' sign on the ground, but other signs and even 3-D objects
can be used as well.
Regular 2-D objects are drawn in the order they appear in scenery files. An improper order of files can lead to scenery conflicts between sceneries (which can be resolved using the scenery library), but also to conflicts between BGL files of the same scenery. For example, if ground polygons are drawn in one BGL file and roads in another, the second file must always be processed after the first one, otherwise roads would be covered by the ground. Such conflicts cannot be resolved using the scenery library, because all files of a scenery are normally installed into the same layer. It is nearly impossible to (instruct the user how to) ensure a specific order of files in the DOS directory. For this reason, the scenery must work independently from the order of its files.
There are two solutions to this problem. The best one is to link BGL files that must appear in a specific order with SCLINK. This would ensure the correct drawing order of 2-D objects.
Another solution is to use priority 2-D objects. They are drawn after all regular 2-D objects.
The LayerCall() [FSASM: PriorityCall] instruction
should be used for defining such objects. It takes an address of a subroutine
and a layer number (not to be confused with layers in the scenery library)
as parameters. LayerCall() does not call the routine, but only
notifies its address. Later, when all regular 2-D objects have been drawn,
FS5 sorts priority 2-D objects according to their layer numbers and calls
their routines. A typical code should look like this:
LayerCall( :Draw_object ) ...
:Draw_object ; Because this routine is executed later, we cannot
rely
RefPoint( ... ) ; on RefPoints defined in the main code
... ; Draw the object
Return ; Return
Scenery conflicts caused by priority 2-D objects cannot be resolved using
the scenery library. Also, the maximum number of LayerCalled
routines is limited because FS5 has to keep the addresses of all routines
before executing them. For this reasons, LayerCall() should
normally not be used for resolving scenery conflicts.
Runways should always be drawn as priority 2-D objects.
LayerCall()/RunwayCall() can often be confused with other
...Call() instructions.
This is NOT a call instruction, because it does not execute the routine. For example, a sequence
RefPoint( ... )
LayerCall( :Draw_sign 32
...
:Draw_sign
... ; Drawing instructions using the current RefPoint
Return
would not work as could be expected. The RefPoint would be at the correct
location during the execution of LayerCall(). But the routine
would not be immediately called and the rest of the scenery code would most
likely define other RefPoints. Later, the subroutine would be called with
a RefPoint at an almost random location.