Object library creation (ASM code)

From FSDeveloper Wiki
Jump to: navigation, search

This article has been written by Christian Stock in the days of Fs2002 on the AvSim scenery design forum. It is still kept here for the purpose of knowledge gathering, but please note that in Fs2004 the library format has changed, so what is described here is only valid for FS2002.

The most serious limitation of makemdl, in my opinion, is that it doesn't generate any library objects. The odd thing about this is that the default scenery from Microsoft is built from library objects, which means that the FS team either uses a different program or a more advanced version of makemdl.

What is the advantage of library files? In a normal bgl file you would dump all the code into one file (although you could use include statements for nested files for easier file management). In library bgl files you would also put all object into one file. But you also have to 'call' the library object in a normal bgl file. So, you have two bgl files to look after. Why would you want that?

Firstly, if you call the same object (for example a tree or antenna) several times, you don't have to repeat the whole code (which could result in larger files + more typing work), but just have to do a library call. So you'd save some work there. Also, there have been done some tests and it seems that for many objects the library method is slightly better in regards of framerates.

Secondly, libraries are reusable and shareable. Two (or more) people could easily design an airport, one can do the buildings as libraries and the other could do the runways, etc, and place the buildings using library calls. This is quite similar to API macros.

One remark before I start going into source code. Be default, GMax generates objects with a scale of 2. This means that you'll have to double all your dimensions, e.g. 1 metre becomes 2.

First, you'll have to modify the wrapper ASM file. Get rid of everything above the crash code. You won't need any of it.

Then, you add the following at the beginning of the file:

RESLIST 0, 8

VERTEX 100, 250, 100 ; 0
VERTEX 100, 0, 100 ; 1
VERTEX -100, 0, 100 ; 2
VERTEX -100, 250, 100 ; 3
VERTEX 100, 250, -100 ; 4
VERTEX 100, 0, -100 ; 5
VERTEX -100, 0, -100 ; 6
VERTEX -100, 250, -100 ; 7

IFVIS8 object_0000_return, 0, 1, 2, 3, 4, 5, 6, 7

This will build an (invisable) box around your object. Make sure that the box is enclosing your object completely. FS2002 will check if the box is visible. If yes, it'll draw the object. This is used to do some fast culling (i.e. decide if to draw an object or not to save framerates). Since your object can have many vertices, culling could have a negative impact on framerates. This pre-culling should speed things up a bit.

Now add the code below.

VAR_BASE_OVERRIDE 0
IFIN1 model_0000_shadow, shadow_flag, 0, 0
BGL_CALL model_0000_crash
BGL_JUMP_32 LOD_0000_0L
object_0000_return label word
BGL_RETURN
BGL_RETURN
model_0000_shadow label word
BGL_JUMP_32 LOD_0000_0L

This will call the shadow and the object itself. More about shadows later in the LOD model section. One thing you'll need to remember is that you'll have to adjust your numbering when you want to use several objects in one library file (which you'll usually do), e.g. 0000, 0001, 0002, etc.

Change the next lines so the numbering is consistant (and change the crash type to what you want), something like this:

model_0000_crash label word
BGL_CRASH_START model_0000_crash_end, 57
BGL_CRASH_OCTTREE crash_0000_end_1
dw CRASH_FLAG_BUILDING_PLANE

Next comes the crash octree which you mostly don't want to change. If you know how octrees work (similar to bintrees and 2D quadrees, but in 3D with volumes, I won't explain that here though), check it and adjust it if you wish.

Lastly, change the very bottom of the list to something like this:

crash_0000_end_1 label word
model_0000_crash_end label word
BGL_RETURN

LOD_0000_0L label word
include filename_0.asm

This will call your object file.

After you have fixed the wrapper ASM file, you'll have to create a new library wrapper file, i.e. a file that will call all your library objects.

Now it's time to create a GUID for your object. You need one for each object. The GUID has to be unique, because this is how FS2002 will recognise it. If you own Microsoft Visual Studio you can use guidgen.exe which ships with it. Otherwise, you'll have to improvise. Once you have one GUID it is best to modify it for subsequent objects in the file (i.e. keep adding 1). It is so important to have a unique id, because otherwise another library could have the same number. FS2002 keeps track of the library objects using this number, so if two objects use the same number FS2002 wouldn't know what to do.

Then create a header like this in your new file:

data_base label word
; DATABASE HEADER
dw 1 ; world set number
dd LAT32_S41:15:00 ; North Bound
dd LAT32_S41:20:00 ; South Bound
dd LON32_E174:50:00 ; East Bound
dd LON32_E174:40:00 ; West Bound
dd 0 ; VOR data
dw 0 ; lowest VOR freq
dw 0 ; highest VOR freq
dd 0 ; seeds level 8 data
dd 0 ; seeds level 9 data
dd 0 ; seeds level 10 data
dd 0 ; seeds level 11 data
dd 0 ; seeds level 12 data
dd 0 ; traffic data
dd 0 ; minimum safe altitude data
dd 0 ; terrain mesh data
dd 0 ; object data
dd library_data - data_base ; library data
dd 0 ; facilities data
dd 0 ; anchor point data
dd 0 ; ATIS data
dd 0 ; NDB data
dd 0 ; dynamic object paths data
dd 0F2FC74A0h, 08F2B4ACAh ; minimum library id
dd 0F2FC74A0h, 08F2B4ACAh ; maximum library id
dd 0 ; miscellaneous data
dd 0 ; title and description data
dd 0 ; magnetic variation data
dd 0 ; exception and exclusion data
dd 0 ; magic number
dd 0 ; compression switch
dw 0 ; spare

Adjust the latitude and longitude boundaries. Outside these boundaries your object won't be useable. Keep that in mind if you want to use your object anywhere in the world. Also adjust the min and max library ids. While a GUID always has four parts, you only specify the first two here. These entries are used by FS2002 to scan through your library files. If a library object called from a normal BGL file has a GUID that is not in between these numbers, FS2002 will stop searching this file and move on to search the next library file. If the GUID is in between those numbers, FS2002 will continue searching the library indices in this file.

It is a good idea to keep those two first GUID parts for min and max close together, e.g. start all your GUIDs in one file with the same numbers. This way you minimise the searching FS2002 will have to do. On a side note, just modifying GUIDs (e.g. by adding 1) does make them not being truly unique anymore. However, chances that someone else will use that GUID are so minimal that they are virtually zero. If you would use only guidgen.exe generated numbers they will unique, but most likely the min and max will have a big gap which will cause FS2002 to search through your library file much more often (which could have a negative impact on framerates).

Next, you'll have to make an index like this:

; LIBRARY DATABASE
library_data label word
LIBRARY_START
LIBRARY_INDEX object_list_0000, 0F2FC74A0h, 08F2B4ACAh, 087E1CBC5h, 0B7210B70h
LIBRARY_INDEX object_list_0001, 0F2FC74A0h, 08F2B4ACAh, 087E1CBC5h, 0B7210B71h
LIBRARY_INDEX object_list_0002, 0F2FC74A0h, 08F2B4ACAh, 087E1CBC5h, 0B7210B72h
LIBRARY_END

Here you point to the objects and give them the GUID. See how I have reused the first GUID for the following two by adding 1 and 2. This way my min and max in the header are the same, minimising searching times.

Finally, for each object you'll have to add a code block like this:

object_list_0000 label word
LIBRARY_HEADER 0F2FC74A0h, 08F2B4ACAh, 087E1CBC5h, 0B7210B70h, 0, header_end_0000, object_end_0000, 110, 20000h, 0, 0, "nzwn_bnz"

header_end_0000 label word
include nzwn_bnz.asm

EOF
object_end_0000 label word

Here you have to specify your GUID again, set the object radius (here 110, remember to double metre values), and set the scale to 2 (20000h). You can, of course, use a different scale, but 2 is what gmax uses as a default. Then you'll include the asm file of your object.

Once you have set all your objects up like this, compile your file. This will generate one library BGL file containing all your objects. You can use this file to build your own scenery, or you can share this file with other designers, so they can use your library objects.

Of course, you'll want to actually use your libraries. There are basically two different kinds of BGL files. A placement BGL file, which calls your library objects and places them, but doesn't have any other BGL code otherwise, and BGL files that contain other code. Airport BGL files would fall under the second category.

If you are designing an airport chances are that you are using a third party GUI program. Theoretically, you could place a library object using the GUI interface of the program by specifying your GUID. I don't know if there actually are any third party airport designers though that support BGL library objects (mostly they only support API macros, which are something completely different). If your favourite program has no native support for BGL library objects, you'll have to modify the source code. Most airport design programs allow you to export your scenery into the SCASM format. SCASM is a widely supported standard, whereas Microsofts own BGLC format isn't fully supported by any airport design program at this stage, as far as I know. You'll have to study the SCASM documentation to find out how to add the necessary code to call your library object. I personally design my airports using Notepad and Adobe Photoshop (using scanned diagrams to work out coordinates) and write my BGLC sources from scratch. If you're writing your own BGLC sources, you'll obviously have to add your own code. Next, I'll explain how to write a pure placement file, you can extract the necessary code bits from there.

There are two ways of writing a placement BGL file. You can use the effects placer to place library objects. The effects placer came as an SDK from Microsoft and should be straight forward to use. You can also write your own source code and I'll explain how to next.

First, you'll have to write a header (which I won't explain here though). Next, you'll have to add the latband section, e.g.:

object_data label word
LATBAND_START
LATBAND_REL LAT16_S41:20:00, LAT16_S41:15:00, object_list_0000
LATBAND_END

Then, you'll add the code section for each object, something like this:

object_list_0000 label word
NEAR_FAR_SMALL_OBJECT_HEADER LAT32_S41:17:13.69, LON32_E174:46:34.96, object_end_0000, 100
BGL_CRASH_INDIRECT 4, object_call_0000, object_scale_0000, object_instance_0000
SHADOW_CALL object_scale_0000
ADDOBJ object_scale_0000
JUMP object_end_0000

object_scale_0000 label word
SCALE_AGL object_return_0000, 0, 110, 20000h, LAT48_S41:17:13.69, LON48_E174:46:34.96, ALT48_0.0

object_instance_0000 label word
INSTANCE_CALL object_call_0000, 00000h, 00000h, ANGL16_300
object_return_0000 label word
BGL_RETURN

object_call_0000 label word
BGL_LIBRARY_CALL 0F2FC74A0h, 08F2B4ACAh, 087E1CBC5h, 0B7210B70h
BGL_RETURN
object_end_0000 label word

This code will add the crash code, shadow, and object itself.