World
Wide Guide | Knowledge Bank
| Kukushkin's Notebook |
Design Fundamentals
This section discusses the use of texture mapping
operations.
Textures for texture mapping operations are stored in texture files. There are a number of "default" textures that came with FS5. However, it is sometimes desirable to create a "custom" texture file.
There are two approaches to creating custom textures.
The first one is to create a bitmap file using a paint program and convert it into the R8 format. Its main advantage is the ability to use the whole palette of tools offered by paint programs. Also, working in a familiar environment of a paint program is very comfortable and thus increases the productivity.
The main disadvantage is a limited control over the contents of a resulting texture file. For example, it is difficult to specify which pixels should be converted into non-fading colors, etc. Also, remapping colors to the palette of FS5 can cause problems. Here, using the palette of FS5 in the drawing program can reduce the risk of unpleasant surprises.
Bitmaps can be converted into texture files using the Airphoto program. It can also create custom palette/haze files in order to achieve much more accurate colors than possible with the default palette. Airphoto takes care of remapping colors to the palette of FS5.
Another program for converting bitmaps into the R8 format is BMP2R8. BMP2R8 does not remap colors to the palette of FS5, but simply retains pixel values from the bitmap file. This makes it much more difficult to convert bitmaps with its help. The palette used for the bitmap file must normally be identical to the palette of FS5. Also, care has to be taken in order not to use some pixel values, like those reserved for the instrument panel. However, the lack of remapping in BMP2R8 also allows much greater control over resulting pixel values, because they are exactly the same as values in the source bitmap file. Many paint programs allow specifying pixel values explicitly. This makes it possible to set some texture pixels to values that do not fade at night, or use some "special" palette entries, like sky colors etc. Such effects are not possible with Airphoto.
Another approach is to use a special program for painting texture files. There are quite a number of them: R8WORX, R8Paint, SVGA EDIT, etc. These programs were specially written for designing texture files, so they know about the color palette of FS5, properties of individual colors etc. They also offer special functions especially useful for designing textures. However, the choice of general-purpose drawing tools is very limited as compared to universal drawing programs. Also, most of these programs use non-standard user interfaces, which can require some effort to learn. But these disadvantages are often compensated by additional functions these programs offer. There is no general rule, which of these two approaches is better.
During a texture mapping operation, a texture, which is basically an array of pixels, has to be projected onto the screen also divided into pixels. This often causes some of texture pixels to be projected onto several screen pixels. Other pixels often do not make it onto the screen and are simply skipped over. FS5 does not implement any algorithms that would compensate the loss or duplication of pixels. For this reason, only textures that look OK even when some pixels are duplicated or skipped over are suitable for texture mapping operations in FS5.
A good(?) example of a texture not suitable for texture mapping are urban textures of FS5.1 containing pixels that do not fade at night. As the aircraft moves, different pixels from distant textures are skipped over. This causes the flickering lights effect that many find very unrealistic.
Before doing a texture mapping operation, a texture file should be loaded using a Bitmap() [FSASM:TextureFile] instruction. If the texture file requires a custom palette, the palette must be loaded too.
FS5 allows loading only a limited number of texture files per frame. This loading process can be observed by closing all view windows and opening a view window again. Before a specific texture file is loaded, all textured surfaces it should be applied to are drawn in a solid color. By setting the surface color to a value somehow close to the average color of the texture, it is possible to minimize this disturbing effect. The color can be set using a regular SurfaceColor() instruction, which is normally used for selecting colors for non-textured surfaces.
There are basically 2 families of instructions capable of texture mapping, representing 2 approaches to displaying textures in FS5. Textured polygons can be treated either as "windows" to a texture painted on the ground (or on the sky, depending from the viewing angle). I call these instructions (such as Poly() in FS5.1, TexWindow(), roads ,etc) "flat", because the only reasonable use for them is to display flat ground textures.
Another approach is to specify explicitly, which points of the texture should be 'tied' to corners of the polygon. These instructions (TexPoly(), ShadedTexPoly(), etc(?) ) are often referred to simply as "TexPolys". The texture mapping here is performed independently from the orientation of the polygon in 3D.
The choice between these approaches is difficult, In my opinion, "flat" polygons are preferable for horizontal ground textures, while TexPolys are preferable for other uses.
The biggest advantages of "flat" textured polygons are their speed and more accurate haze effect. They are displayed almost twice as fast as TexPolys. Also, sometimes it is more convenient not to care about texture coordinates. Synth scenery tiles are (internally) displayed using 'flat' polygons.
In haze modes of FS5.1, the intensity of the haze effect varies inside "flat" textured polygons according to the distance from the viewpoint. In all other drawing instructions, including TexPolys, this intensity does not vary within the graphic primitive and only depends from the distance between the RefPoint and the viewpoint.
This makes "flat" textured polygons the best way of displaying the ground, where irregularities with the haze effect are especially disturbing.
The main problem when using these instruction is to specify the location for the texture on the ground.
Also, you can normally use them only to display horizontal polygons. While you can indeed use these instructions for non-horizontal polygons, the texture will still appear as painted on the ground, we will call this a 'window effect'. Maybe it is possible to display non-horizontal surfaces using RotatedCall()s, but it is very difficult and not worth the effort, because such surfaces often require complex stretching of the texture, which can only be done by TexPolys.
When using "flat" polygons, you specify the location, orientation and the scale factor for a texture _before_ drawing the polygon.
The scale factor is determined by the scale factor of the RefPoint and is always 1 pixel/RefPoint unit.
The placement is relative to the RefPoint and is determine by dx and dy parameters of the Bitmap()/RepeatBitmap() [FSASM:MoveTexture!] instruction. The dz parameter can be used to change the altitude of the texture. Nonzero values can be used for displaying horizontal surfaces above or below the RefPoint level.
dz should always be equal to the relative altitude of the polygon over the RefPoint. Otherwise, the "window effect" will be observed.
Also, it is possible to rotate the texture by loading _and_ using it from a RotatedCalled/TransformCalled subroutine. The Points() for the polygon can be defined outside of this subroutine. They will neither be transformed nor rotated.
The instructions that support "flat" textures include:
StartSurface()..EndSurface()
Poly()
TexWindow()
Roads (only when not collapsed to lines)
Taxiways
Rivers (when not collapsed)
The TexWindow() instruction always displays a textured surface. Other instructions are capable of displaying both textured and textureless surfaces. A textured surface is displayed if the texture is "activated". The texture is activated when it is loaded with Bitmap() or reactivated with RepeatBitmap(). It is deactivated after a textured surface has been drawn using a polygon drawing instruction, like TexPoly(), TexWindow() or StartSurface()..EndSurface(). "Deactivated" does not mean it is unloaded from memory, so it can still be used by "pure" texture mapping instructions, like TexPolys and TexWindow().
After road drawing instructions, like Road...() and Taxi...(), the texture is NOT deactivated automatically. This can lead to problems if a non-textured polygon is to be drawn next, because the SurfaceColor() instruction does not deactivate the texture either, so the polygon would be textured instead of appearing in a solid color (as planned). For this reason, it is a good practice to deactivate the texture always after road drawing instructions. This can be done either using surface drawing instructions:
Bitmap( ... ) ; Load a texture file
... ; Draw roads, taxiways etc
StartSurface ; Deactivate the texture. Because there are no
EndSurface ; MoveTo()/LineTo()s, nothing will be drawn.
or by actually drawing a textured polygon.
Roads drawn using RoadLineTo[2]() [FSASM:DrawRoad/River] are always displayed in two steps. First, a thin solid line is drawn using the current surface color. Then, if the road is not collapsed to thin line, the road surface is drawn over this line, either using a texture or the surface color too. Due to rounding errors, the thin line is sometimes visible "under" the road surface even if the road is not collapsed. For this reason, it is important to set a proper surface color even when drawing a textured road.
The main advantage of TexPolys is the ability to display non-horizontal surfaces. Also, the bitmap can be stretched in a complex way (a good example is the Earth globe in the map window) and the image quality is slightly higher than with flat polygons. All these advantages come at the cost of drawing speed and reliability - in some cases TexPolys do not work as expected.
The placement for the texture within the polygon is determined by texture coordinates specified explicitly in the [Shaded]TexPoly() instruction.
In the TexPoly() instruction, the dx and dy parameters of the Bitmap() resp. RepeatBitmap() instruction are added to the texture coordinates. This is used for shading building sides. The TexPolyShading() instruction (executed internally for Building()s) increments the dx parameter by a factor of 32, according to the orientation of the polygon and the position of the virtual sun.
Thus, a proper texture file for shading should have 8 columns of 32x256 pixels each, containing 8 different intensity versions of a same image. This is very well illustrated by default building textures.
Sometimes an image reacting to direct sunlight that is bigger than 32x256 pixels has to be displayed. This can be done either by displaying multiple adjacent TexPoly()s or by evaluating the dx parameter and loading different texture files, as described in the section about colors. TexPolyShading() takes a vector as parameter. This vector is supposed to be an external normal to the polygon to be shaded. The best way to learn how to use this instruction is to display a custom textured object near a default building of FS5 and then try to make this object look exactly like the building - including the shading.
The ShadedTexPoly() instruction applies Gouraud shading to the texture. It is basically a textured analog of ShadedPoly(). ShadedTexPoly() can be used to make a textured object look perfectly round. For example, the textured fuselage of the default Cessna is implemented using ShadedTexPoly().
This instruction requires defining points with the VecPoints() instruction, as opposed to Points() in case of TexPoly(). Also, ShadedTexPoly() displays pixels with values greater than 127 incorrectly. The reason for that is, the shading algorithm requires a special structure of the palette, and only the first half of the default palette is guaranteed to be structured that way.
Unlike TexPoly(), ShadedTexPoly() ignores the dx and dy parameters of the bitmap, thus TexPolyShading does not affect this instruction.
Both forms of TexPoly() must always be prefixed by the Inst_7D [FSASM: ResetTexture] instruction. Otherwise, the polygon will appear 'bent' from a close distance.
Image smoothing can be used with both families of texture mapping instructions. It needs to be enabled with the Smoothing( 1 ) instruction [FSASM:ImageSmoothing] before actually drawing a polygon. Also, it _must_ be disabled afterwards with Smoothing( 0 ), otherwise the synth scenery would not look nice and default buildings would look car-bombed.
Image smoothing does not work with "flat" textured surfaces in the SVGA haze mode of FS5.1.
In the VGA 320x400 haze mode, it normally does not work with TexPoly()s (but works with ShadedTexPoly()s). However, it can be re-enabled by setting the variable 033E to 1 before drawing the polygon and resetting it to 0 afterwards. This does not eliminate the need for Smoothing().
In some other modes, image smoothing works only with image quality set to high or medium.
The choice whether to enable image smoothing has to be made by the scenery designer for each texture separately. Many bitmaps, mostly lower-resolution bitmaps representing natural landscapes, look much better smoothed. Some other bitmaps, mostly high-resolution bitmaps or man-made landscapes like cities, look much sharper and better without image smoothing. In case of uncertainty, image smoothing should be enabled. The user can always disable it from the Preferences dialog box, but he has no possibility to enable image smoothing if it is left disabled in the scenery.
Normally, the texture fills the entire texture-mapped polygon, which means each pixel inside the polygon is drawn. However, FS5 can be instructed not to draw certain pixel values. This allows using textures to define very complex shapes that can hardly be drawn using sets of polygons, like trees or smoke. Pixels in the texture file that do not belong to the shape are simply set to the transparent value and thus not drawn. Transparent pixels can be only handled by the TexPoly() instruction. The BitmapMode() [FSASM:PaletteMask] instruction is used to set the minimum pixel value to be drawn. All pixels below this value are transparent. BitmapMode() must be reset to 0 after textured polygon(s) have been drawn. A typical code should look like this:
BitmapMode( 1 )
TexPoly( ... )
... ; further TexPolys
BitmapMode( 0 )
BitmapMode has no effect on ShadedTexPoly. its effect on "flat" textured surfaces strongly depends from the active display driver, so nonzero values should not be used with texture mapping instructions other than TexPoly().
With BitmapMode set to nonzero value, the image smoothing does not work. Also, shadows are never textured, so the shadow from a textured polygon always resembles the shape of the polygon regardless of any transparent pixels inside. For this reason, the shape of such a TexPoly() should somehow resemble the shape of the object.
Textures displayed by "flat" texture mapping instructions are tiled. This means the 256x256-pixel square is repeated over and over in order to cover the whole polygon, if necessary. This is a very useful feature if the texture is used just as a better filling for primitives. However, the texture is normally not tiled when it is used to display details of the landscape, like parts of a photorealistic scenery. In theory, specifying proper polygon coordinates should prevent any tiling:
Bitmap( ... ) ; Load a 256x256-pixel bitmap
Points( 0 0 0 0 256 0 0 0 0 256 256 0 256 ) ; A 256x256-pixel
square
Smoothing( 1 )
TexWindow( au 0 1 3 2 ) ; Display a part of a photorealistic
scenery
Smoothing( 0 )
In practice, two problems occur. First, rounding errors cause minimal tiling on edges of the square, where some pixels from the opposite side of the bitmap appear. Second, pixels from the last row/column of the bitmap are interpolated with pixels from the first row/column from the same bitmap when image smoothing is enabled, thus often producing 0.75-pixel wide lines on far edges of the bitmap. Both effects often disturb edges of the bitmap, which is especially visible when multiple adjacent bitmaps representing adjacent pieces of the landscape are displayed, or when opposite edges of the bitmap have very different colors.
These problems can be resolved either by not displaying pixels from bitmap edges, thus using only a 254x254-pixel square, or by using the MoveTexture() [FSASM:MapTexture!] instruction.
MoveTexture() accepts 4 floating-point values as parameters, which should specify delta coordinates (which are also pixel coordinates) of a rectangle restricting the normal texture mapping operation. Outside of it, pixels from the nearest edge of the rectangle are repeated. The best way of seeing how this (and others) instruction works is trying it out. The two mentioned problems can be solved using
MoveTexture( 0.25 0.25 255.24 255.24 )
0.25 eliminates the problem on the near edge of the texture. 255.24 also restricts the output on the far edge to the first stage of image smoothing (no interpolation), thus eliminating both problems there. The coordinates in MoveTexture() here should be increased/decreased by multiples of 256 if another "tile" should be used instead of (0,0)-(256,256).
Similar problems can happen when the bitmap contains multiple images displayed on different earth locations. In this case, the rectangle in MoveTexture() should be reduced accordingly in order to prevent pixels from neighboring images from appearing.
The effect of MoveTexture() can be overridden by another MoveTexture(). It is disabled when a new bitmap is loaded with Bitmap() or the current bitmap reactivated with RepeatBitmap().
Textures displayed using TexPolys can be tiled by using texture coordinates above 255. However, having many pixels in a TexPoly has a very negative effect on its drawing speed. Also, tiled textures in TexPolys are displayed incorrectly in very rare occasions.