New ways to have fun with CFS1

Chapter 2

How big is our playground?

If you have practiced replacing playground.scx by our last cleaned-up playgroundM.scx (if you have noted, the "M" stands for "Master"), what I'm about to ask you is child play; open MDLDisAs, go fetch playground.mdl, and save it on your desktop as playground.scx. Open it with Notepad++ (from now on, I will use EditPad and post the text rather than pictures, just adapt if you're using Notepad++).

A few things have changed. There is a "list" of individual structures containing one item and wrongly associated to Label :L000000, this is a glitch of MDLDisAs with short compilation. At the end of the file, the commands listing has also reappeared. Our "human touch" has vanished; comments we made and Labels we changed are gone. This is why we never let a freshly decompiled SCX file get into our workplace, NEVER EVER!

Let's concentrate on the only visual code subroutine (in green) and compare it with our own (in red);

:L001284
Points( 1 ; 4 points
0 0 780 ; 1
0 1560 780 ; 2
0 1560 -780 ; 3
0 0 -780 ; 4
)
SurfaceColor( 0x05 0xF0 )
Poly( m 32767 0 0 0.000000 1 2 3 4 )
SurfaceColor( 0x06 0xF0 )
Poly( m -32767 0 0 0.000000 1 2 3 4 )
Return


:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
0 0 780 ; 1
0 1560 780 ; 2
0 1560 -780 ; 3
0 0 -780 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 )
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 )
Return


Ok, Label is changed and comments are gone, but points' declaration is unchanged. SurfaceColor is still there, but the arguments (the stuff in parenthesis) are a bit different. The "0x" is a prefix telling the assembler that hex numbers are coming. But SCASM is expecting hex numbers for SurfaceColor command, so they're is no need to "identify" our numbers as hexadecimal notation. We will study colors in a future lesson.

Poly commands are still there, but the arguments are different. The "m" stands for "manual". This will be the occasion of exploring our playground and see where are the boundaries.

The arguments before the "1 2 3 4" points' calling are establishing a vector. Here is Poly command explained;

Poly( m vx vz vy len pnum1 ... pnumn )
where "m" is for "manual", "vx" for vector X, "vz" for vector Z, "vy" for vector Y, "len" for vector actual length, and "pnum1 ... pnumn" for points being called. This vector is used to calculate sun's reflection on that polygon and must be perpendicular to that polygon's surface.


Now, from a guy who almost flunked math in high-school, a few mathematical explanations are necessary, sorry...

The mathematical formula used to get the hypotenuse of a three-dimensional vector, or diagonal solid line, is;

Don't run to the medicine cabinet yet!

Imagine an arrow being attached to RefPoint at the tail and to a cord at the tip fraught tight towards X-Y-Z coordinates. The vector is the arrow while the cord-arrow assembly is the spatial 3D coordinates. The first number (in the green subroutine above) represent the displacement along the X axis (32767), the second number (0) is for Z axis, the third (0) for Y axis, and the fourth (0.000000) for the vector length from RefPoint (one of the rare case when vector lenght = zero is when that vector coincide with one of the three axis, like here). The first entry (32767) is the maximum length of that vector-arrow in SCASM unit. The value 32767 is the highest value SCASM will compile successfully. More precisely; 32767 is the highest positive value it will accept, but -32768 is the lowest value. SCASM will simply compute ±32767, I suppose by convenience. Following SCASM conventions, maximum fore limit is 32767, minimum aft is -32768, maximum right is 32767, and minimum left is -32768. This gives us a "cube" of 65535³ units.



This mean, considering that one foot = 156 units, that our playground is about 420 cubic feet. Plenty of space for now.

In our playground.scx, it is normal to find our vector align along the X axis. The "wall" is itself aligned fore-aft and up-down, the perpendicular is therefore right-left;

coinciding with the X axis. But what happens if, let's say, our wall is inclined? Type or copy/paste this in playgroundM.scx point's declaration;


Points( 1 ; 4 points
-200 0 780 ; 1
200 1560 780 ; 2
200 1560 -780 ; 3
-200 0 -780 ; 4
)


This will incline our wall, the top being 200 units to the right, the bottom 200 units to the left. Open playground.MDL with MDLDisAs and replace with our modified playgroundM.scx. Then re-open playground.mdl and save playground.scx on the desktop. This is what you should get;

:L001284
Points( 1 ; 4 points
-200 0 780 ; 1
200 1560 780 ; 2
200 1560 -780 ; 3
-200 0 -780 ; 4
)
SurfaceColor( 0x05 0xF0 )
Poly( m 31740 -8138 0 -193.732749 1 2 3 4 )
SurfaceColor( 0x06 0xF0 )
Poly( m -31740 8138 0 193.732749 1 2 3 4 )
Return


Now, considering that distances are absolutes and that the minus (-) sign is only there to indicate direction. We have;

Close enough!

The vector length of 193.732749 is the distance between the "tip" of our arrow to RefPoint along vx-vy-vz axis.

If my explanation is too obscure, maybe this site will do it better. I also hope this drawing will help;



The formula of the preceding lesson was just an application of the Pythagorean Theorem to 3D distance. The vector length is determined by the distance between RefPoint and the surface on which our polygon stands. The vector coordinates are as long as possible to increase accuracy, the same way an architect protractor is way larger than a first grade schoolboy protractor. The vector length is capable of a precision of six digits after the decimal point, again for accuracy purpose. Remember that vector lenght and vector coordinates lenght are two different things, the latter always being longer, or at least equal, than the former, but both point exactly in the same direction.

This is why we're using the "a"; it stands for "automatic". SCASM will do all the calculations for us. For someone who almost flunked his math in high-school, this is a blessing. And I imagine that you're all exhaling a sigh of relief right now; I told you not to run to the medicine cabinet!

Now, for fun, let's push the limit and see what's happening;

:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
0 0 780 ; 1
0 32767 780 ; 2
0 32767 -780 ; 3
0 0 -780 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 )
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 )
Return


"Implant" this in your playground.mdl, you know how by now, and it should give you something like A below. Now, go beyond the 32767 and "implant" this;

:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
0 0 780 ; 1
0 32768 780 ; 2
0 32768 -780 ; 3
0 0 -780 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 )
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 )
Return


OOOPS! B is what a tiny-winy-little-too-many unit can do! Go get this with MDLDisAs and paste playground.scx on desktop. This is what I got;

Points( 1 ; 4 points
0 0 780 ; 1
0 -32768 780 ; 2
0 -32768 -780 ; 3
0 0 -780 ; 4
)


Where did these -32768 came from? Well, this is SCASM trying to make sense out of nonsense; since 32768 can only be in the negative form, it does just that. Now, we will push the nonsense a bit further;

:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
0 0 780 ; 1
0 327670 780 ; 2
0 327670 -780 ; 3
0 0 -780 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 )
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 )
Return


"Implant" that in the MDL and go see what it looks;



Again, disassemble playground.mdl and save playground.scx on desktop;

Points( 1 ; 4 points
0 0 780 ; 1
0 -10 780 ; 2
0 -10 -780 ; 3
0 0 -780 ; 4
)


SCASM has run up-and-down ten times, saving a negative unit each time, which explain your "-10". So, to conclude this first lesson, be reminded that numbers over 32767 or under -32768 will compile but return foolish entries. Go replace the visual code with this;

:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
0 0 780 ; 1
0 1560 780 ; 2
0 1560 -780 ; 3
0 0 -780 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 )
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 )
Return


and close MDLDisAs

 

Automatic calculation

By one of these mysteries fathered by the twisted minds of programmers, automation is for humans and "manual" for computers... go figure!

Now that we have saved our souls from eternal mathematical damnation, a word about our savior; the automatic calculation.

How does automatic calculation works? We need to know that, if only to use it wisely.

The "a" is for "automatic" and "ai" for "automatic inverted". Basically, automatic calculation will texture the side exterior to RefPoint and automatic inverted the side interior to RefPoint if your polygon's vertices are called in a clockwise order. This is, almost verbatim, the usual formula "experts" are using. I must say, to my own shame, that I never explored thoroughly the automatic calculation. When things were not going my way, I would fiddle around until they did. So this lesson will be as much for me as it is for you; let's learn together, shall we?

We need to turn our "wall" into a "yard" first. This drawing should help you to follow;


We will "paint" each interior walls first, then exterior ones. From the exterior, points calling is done clockwise and, when seen from the interior, anticlockwise. Refer to the drawing above;

:BEGINNING
;Yard red and green 10x10x10
Points( 1 ; 8 points
780 0 780 ; 1
780 1560 780 ; 2
780 1560 -780 ; 3
780 0 -780 ; 4
-780 0 780 ; 5
-780 1560 780 ; 6
-780 1560 -780 ; 7
-780 0 -780 ; 8
)
SurfaceColor( 05 F0 ) ;red
Poly( ai 1 2 6 5 ) ;fore int
Poly( ai 8 7 3 4 ) ;aft int
Poly( ai 4 3 2 1 ) ;right int
Poly( ai 5 6 7 8 ) ;left int
SurfaceColor( 06 F0 ) ;green
Poly( a 1 2 6 5 ) ;fore ext
Poly( a 8 7 3 4 ) ;aft ext
Poly( a 4 3 2 1 ) ;right ext
Poly( a 5 6 7 8 ) ;left ext
Return


Interior walls are painted (spoiler alert!!!) red (05) and exterior walls green (06). At least, that is what I'm trying to do. So, is it?



Yippee! It works! You will note that, by painting the interior walls first, we have eluded bleeds.

Now, we will invert the points' calling order so that they will be called anticlockwise from the exterior and clockwise from the inside. This is to test the "clockwise-anticlockwise" theory which, if right, should gives us green interior and red exterior.

:BEGINNING
;Yard red and green 10x10x10
Points( 1 ; 8 points
780 0 780 ; 1
780 1560 780 ; 2
780 1560 -780 ; 3
780 0 -780 ; 4
-780 0 780 ; 5
-780 1560 780 ; 6
-780 1560 -780 ; 7
-780 0 -780 ; 8
)
SurfaceColor( 05 F0 ) ;red
Poly( ai 5 6 2 1 ) ;fore int
Poly( ai 4 3 7 8 ) ;aft int
Poly( ai 1 2 3 4 ) ;right int
Poly( ai 8 7 6 5 ) ;left int
SurfaceColor( 06 F0 ) ;green
Poly( a 5 6 2 1 ) ;fore ext
Poly( a 4 3 7 8 ) ;aft ext
Poly( a 1 2 3 4 ) ;right ext
Poly( a 8 7 6 5 ) ;left ext
Return


Naaaa... walls remained red inside and green outside. SCASM does recognize if a polygon's surface is away from RefPoint (exterior) or facing it (interior) during compilation, no matter if points are called clockwise or anticlockwise. To verify what happened, I have saved the visual code as found in playground.scx. The first assembly is in green, the second in red.

:L001284
Points( 1 ; 8 points
780 0 780 ; 1
780 1560 780 ; 2
780 1560 -780 ; 3
780 0 -780 ; 4
-780 0 780 ; 5
-780 1560 780 ; 6
-780 1560 -780 ; 7
-780 0 -780 ; 8
)
SurfaceColor( 0x05 0xF0 )
Poly( m 0 0 -32767 -780.000000 1 2 6 5 )
Poly( m 0 0 32767 -780.000000 8 7 3 4 )
Poly( m -32767 0 0 -780.000000 4 3 2 1 )
Poly( m 32767 0 0 -780.000000 5 6 7 8 )
SurfaceColor( 0x06 0xF0 )
Poly( m 0 0 32767 780.000000 1 2 6 5 )
Poly( m 0 0 -32767 780.000000 8 7 3 4 )
Poly( m 32767 0 0 780.000000 4 3 2 1 )
Poly( m -32767 0 0 780.000000 5 6 7 8 )
Return


:L001284
Points( 1 ; 8 points
780 0 780 ; 1
780 1560 780 ; 2
780 1560 -780 ; 3
780 0 -780 ; 4
-780 0 780 ; 5
-780 1560 780 ; 6
-780 1560 -780 ; 7
-780 0 -780 ; 8
)
SurfaceColor( 0x05 0xF0 )
Poly( m 0 0 -32767 -780.000000 5 6 2 1 )
Poly( m 0 0 32767 -780.000000 4 3 7 8 )
Poly( m -32767 0 0 -780.000000 1 2 3 4 )
Poly( m 32767 0 0 -780.000000 8 7 6 5 )
SurfaceColor( 0x06 0xF0 )
Poly( m 0 0 32767 780.000000 5 6 2 1 )
Poly( m 0 0 -32767 780.000000 4 3 7 8 )
Poly( m 32767 0 0 780.000000 1 2 3 4 )
Poly( m -32767 0 0 780.000000 8 7 6 5 )
Return


The "manual" vectoring is exactly the same between both assemblies; vx, vz, vy, and vector length are unchanged. As long as points are being called in a sequence respecting the perimeter, SCASM will properly identify exterior-interior orientation. By "respecting the perimeter", I mean clockwise or anticlockwise. For example, the fore wall sequence "5 2 6 1" would compile but create a "bow-tie" weird look. Go and try it if you want.

These SCASM SCX are also good examples of what I've tried explaining in the preceding lesson. As all walls are perpendicular to Y or X axis, you see ±32767 as vx or vy. Vector length is always at 780.000000 (the minus sign is only there to indicate direction). For example, in the last assembly, the interior fore wall is at vy = -32767 and vector length at -780.000000 because the vector is "read" from tip to tail. The same fore wall, but from the exterior, has vy = 32767 and vector length at 780.000000 simply because now it is "read" from tail to tip. In both cases, vector has the same length. Study these two SCX carefully until our next lesson.

Automatic calculation (part two)

In the preceding lesson, we have establish that SCASM can "recognize" interior and exterior polygon's surfaces to RefPoint. But what about surfaces that have RefPoint as one of their vertices? What is "interior" and what is "exterior" when our polygons are "splitting" RefPoint?

For this lesson, we will return to our green-red wall;

:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
0 0 780 ; 1
0 1560 780 ; 2
0 1560 -780 ; 3
0 0 -780 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 )
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 )
Return


Logically, red is "interior" and green is "exterior", so right is "interior" and left is "exterior". But what happens when we change the calling order of the points from "1 2 3 4" to "4 3 2 1"? Go and try it.

Oops! Colors have switched! So now, "interior" is left and "exterior" is right. We will now try with a fore-aft wall splitting RefPoint;

:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
780 0 0 ; 1
780 1560 0 ; 2
-780 1560 0 ; 3
-780 0 0 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 ) ;or 4 3 2 1
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 ) ;or 4 3 2 1
Return




Again, colors, therefore sides, are switching depending on the order of our points (vertices) calling. We can repeat the experiment on a "floor" at Refpoint level;

:BEGINNING
;Wall red and green 10x10
Points( 1 ; 4 points
780 0 780 ; 1
780 0 -780 ; 2
-780 0 -780 ; 3
-780 0 780 ; 4
)
SurfaceColor( 05 F0 )
Poly( ai 1 2 3 4 ) ;or 4 3 2 1
SurfaceColor( 06 F0 )
Poly( a 1 2 3 4 ) ;or 4 3 2 1
Return


I only have taken screen captures of the top side, but you can take my word that the bottom follows the same pattern (or you can go and try it yourself!).

The top of our floor changes color depending on the order we called our points. Now, have a look at the three preceding pictures and tell me when green (exterior) is on the left, fore, or top side.

When it is on "clockwise" order! And when going counterclockwise, green (exterior) is on the right - aft - bottom side. As a convention, left, top, and front sides are usually the ones we're referring to;

this is how AF99 is rigged, and this is how airplanes are flying (unless you want to fly tail first...).

Unless I specifically say otherwise, these perspectives are to be used from now on.

So, to resume, using "a" (automatic) or "ai" (automatic inverted) attributes, we can choose which side (interior or exterior) we want to paint. One exception is when RefPoint is a vertex among others vertices of our polygon's plane surface. In this particular case, exterior is clockwise and interior is counterclockwise.

Scales

SCASM unit is "dimensionless", but it gets a dimension with a scaling command. Scenery designers used to make their macros (subroutines for buildings and stuff integrated in a BGL scenery file) using the SetScale or ReScale commands. This had the advantage of permitting decimal with floating point value. But it was "computer unfriendly" and forced the Flight Simulator EXE (COMBATFS.EXE for CFS1) to go through long calculations, slowing down framerate eventually. SetScaleX, originally created for aircraft (moving objects) was adopted by scenery-builders as well. This is probably due to its more "computer friendly" approach by "bit shifting".

Incidentally; why 156 units equal one feet at SetScaleX 7?

According to Manfred Moldenhauer, when the scale is 1:1, each unit represent 1 meter. The following insert is a table for SetScaleX I have done a while ago (copy and paste if you want);
----------------
scaling with SetScaleX instruction

Mathematical formulas:
scale = ( 2^SX ) / 65536
scale = 1 / 2^(16-SX)
or or 1 meter= ? units
5- 0.00048828125 1/2048 2048
6- 0.0009765625 1/1024 1024
7- 0.001953125 1/512 512
8- 0.00390625 1/256 256
9- 0.078125 1/128 128
10- 0.015625 1/64 64
11- 0.03125 1/32 32
12- 0.0625 1/16 16
13- 0.125 1/8 8
14- 0.25 1/4 4
15- 0.5 1/2 2
16- 1 1 1
17- 2 2 0.5
18- 4 4 0.25

From SetScaleX 7 to;
5- x 0.25
6- x 0.5
8- x 2
9- x 4
10- x 8
11- x 16
12- x 32
13- x 64
14- x 128
15- x 256
16- x 512
17- x 1024
18- x 2048

----------------

Since 1 foot = .3048 meter and that, at SetScaleX 7, a meter = 512 units, it should be .3048 x 512 = 156.0576. AF99 rounds-up at 156.13. We will round-down at 156, which is even more precise than AF99.

As you can see, progression is done by multiple of two (2^SX), the same "language" PC (or Mac and Linux for that matter) is familiar with. The advantage is a gain in speed calculation, the disadvantage is a certain rigidity in scaling. For example, you can double the size of an object, but not triple it. Some forethought is in order.

Scale is to be determined on basically two opposing considerations: 1) How detailed do I want my object to be? ; 2) How big do I want my object to be? If I want to make a very detailed object, for example a violin, scales under 7 are to be considered. If I want to make a very large building, or a mountain, scales over 7 come to mind. But for now, we will use 7 as a good compromise.

Incidentally, if you were curious enough, you may have seen that CFS1 stock, flyable and non-flyable, aircraft are at 180. According to Manfred Moldenhauer, any values above 31 "are interpreted as a local variable which carry the scaling information in the normal fractional format". Said differently, that CFS1 value of 180 refers not to a SetScaleX value, but to a SetScale "sorta" command somewhere in the non-BGL part of the MDL. FS98 stock aircraft are all using a SetscaleX of 6, which is a "real" scale.

Taken from the same distance under the same angle, here is "our" wall at different scales;



12 was the maximum possible under the circumstances, anything bigger simply wouldn't fit CFS1 window!

Next chapter(s) will dig into colors. Can't say for sure how many of them they're will be.