Readme

NOTE: This page is designed to use CSS styles - you may want to upgrade your browser.

Usage

OK, I've heard a few people saying that using ZenNode is confusing and needs a real README.  Well, I'm the first to admit that I'm not one for writing documentation, so if anyone would like to submit a 'decent README' or a tutorial, I'll be glad to include it (with credits) in future releases.

In the meantime, I'll give you the simplest tutorial I can think of.  Suppose you have a .wad file, lets call it DOOM.WAD, and you want to use ZenNode with it.  Create a command prompt window, change to the directory where you have ZenNode installed (I'll use C:\ZENNODE for this example) and, assuming your DOOM.WAD file is located the directory C:\DOOM, type:

        C:\ZENNODE>ZenNode c:\doom\doom.wad
      

Now press the [Enter] key.  It's as simple as that!  ZenNode should now process each level in the wad file, creating/updating the BLOCKMAP, BSP structures (NODES, SEGS, SSECTOR), and REJECT resources.  Once the processing is completed, ZenNode will write the output file.  By default, the output file has the same name as the input file (i.e. DOOM.WAD), but is put in the current directory (i.e. C:\ZENNODE in this example).  If you want to change the name of the output file (to say ZENDOOM.WAD) use this instead:

        C:\ZENNODE>ZenNode c:\doom\doom.wad -o zendoom.wad
      

This will create a new file called zendoom.wad in the current directory.  You can specify a full path name (i.e. -o c:\doom\zendoom.wad) if you don't want the new .wad file to end up in the current directory.  By default, the output file contains everything found in the input file.  If you don't want the entire .wad file to be copied, but only the levels that ZenNode has processed, you can extract them to a new file which will be a new PWAD file containing only those levels:

        C:\ZENNODE>ZenNode c:\doom\doom.wad -x zendoom.wad
      

Lets assume that you only want to update specific levels inside the wad file (lets use E2M3 and E3M4).  You can specify the levels on the command line after the name of the .wad file, but before any output options (i.e. -o or -x):

        C:\ZENNODE>ZenNode c:\doom\doom.wad e2m3+e3m4 -x zendoom.wad
      

This will create a PWAD file that contains only levels e2m3 and e3m4.  If you use the -o option (or no output option) the output file will be a copy of the original with only these two level modified.

You can also use ZenNode to merge individual .wad files in to a larger one.  If you had two .wad files (i.e. file1.wad and file2.wad) and wanted to merge them, you could type:

        C:\ZENNODE>ZenNode file1+file2 -o zendoom.wad
      

The resulting zendoom.wad file will contain all the levels found in both file1.wad and file2.wad.  If both files have any level(s) with the same name, the ones in the last file listed will be used.  So, if file1.wad contained E1M1 and E1M2 and file2.wad contained E1M2 and E1M3, the output file would contain the E1M2 level found in file2.wad and not file1.wad.

By default, ZenNode will process the BLOCKMAP, BSP structures and REJECT resources.  If you don't want any of these to be processed, you can tell ZenNode to turn off processing for any or all of them.  To tell ZenNode to skip the BLOCKMAP you would use:

        C:\ZENNODE>ZenNode -b- c:\doom\doom.wad
      

Similarly, you can use -n- to turn off the NODES builder and -r- to turn off the REJECT builder.  You can also specify options (just type zennode for a complete list) for each step.  For example, if you think ZenNode is taking too long to build the BSP tree and REJECT, you can tell it to use a faster algorithm (-n3) and an empty reject (-rz):

        C:\ZENNODE>ZenNode -n3 -rz c:\doom\doom.wad
      

Once ZenNode finishes processing a file (or set of files) and writing the output file, it will continue processing the command line.  If you're really adventurous, try to figure out what this does:

        C:\ZENNODE>ZenNode -n3 -rz -nq c:\doom\doom.wad e1m1 -r+ -n2 doom e1m2 -x level2
      

How does it work?

The BLOCKMAP Resource

The BLOCKMAP is the simplest structure created by ZenNode.  This resource breaks the level down into squares 128x128 pixels and, for each square, lists the LINEDEFs that are found there.  The BLOCKMAP makes it easy for the game to quickly find all of the lines in an area of the map without having to go through each and every LINEDEF and checking to see if any part of it is in the area of interest.  Like many other programs, ZenNode attempts to compress the data in the BLOCKMAP when possible to keep the size of the WAD as small as possible.

Options:

Building a BSP

The BSP, ZenNode's raison d'être.  The BSP (Binary Space Partition) is a collection of resources that describe where lines are in relation to each other.  ZenNode starts with a collection of LINEDEFS, SIDEDEFS, and SECTORS.  From these, it creates the 'BSP' for the level in the form of the NODES, SEGS, and SSECTORS.  To begin with, a SEG is created for each side of a LINEDEF.  ZenNode's job is to take this collection of SEGS and break it up into SSECTORS.  If you think of a SECTOR as a room, an SSECTOR is that part of the room where no wall blocks the view of any other wall.  Each sector will end up being divided up into one or more SSECTORS as the BSP is built.

The process of breaking up the collection of SEGS is a simple, recursive opration.  First, one SEG is chosen, the partition line, from the collection to serve as a dividing line that breaks the list into two halves (hence the name binary paritition).  Once the list of SEGS is split into two (all SEGS that are on the left side of the partition line and all those to the right), each half is split again until no partition for a group of SEGS can be found.  This happens when all of the SEGS in a list form a convex region, an SSECTOR.  The job of ZenNode, or any other NODES builder, is to choose the partition line as best as possible to keep the number of SEGS/SSECTORS/NODES as small as possible.  In the perfect case, each split would create an equal number of SSECTORS on each side of the partition.  The problem is, you don't know where the SSECTORS are until you can't find a new parition line.  So, the best a NODES builder can do is use some kind of metric that, hopefully, will achieve this.  Since the only thing you have to start with is a bunch of SEGS, the metric is usually some function of the number of SEGS on each side of a partition line.  Another possible measurement for the metric is the number of SECTORS on each side of the partition line.  Most NODES builders take the SEGS approach, since it's the easiest.  One consideration to keep in mind when selecting the partition line is how many SEGS will be split if this partition is chosen.  Each split increases the total number of SEGS in the final list.  Because of this, most metric functions try to factor in the number of splits.  Once you have a metric function, you're set to go.  Just keep recursively dividing SEGS until you're finished!

Testing each SEG in the list at each step, and doing the math to figure out which side of the partion a SEG is is time consuming.  There are a few ways to speed things up.  The one used by some other NODES builders is only look at a portion of all the available SEGS at each step.  This can speed thing up considerably, at the expense of possibly missing the 'optimal' partition line.  ZenNode tries to speed things up a bit without sacrificing thouroughness.  First, most maps have several SEGS that lie along the same line.  Since a partition line is really a line extended from a SEG, testing each SEG along a line is redundant, and time consuming.  ZenNode solves this by making a list of unique lines, internally referred to as an alias, in the map before starting the BSP building process.  At each step, as a SEG is tested, it's alias is marked as checked.  If another SEG lies along an alias that has been checked, it is skipped.  At each level, any line that was chosen as a partition earlier cannot be chosen again, since all lines will be to one side of it.  ZenNode elminates each partition line from the list of SEGS to check at each successive step.  Also, if a SEG is found to be an outside wall (which also can't be a partition line), it is eliminated from the list as well.  Another step ZenNode takes it to cache the side calculation information whenever possible.  When ZenNode needs to know which side of a partition line a SEGS is on it looks in the cache first.  If it's there, all of the time consuming calculations can be skipped.  Finally, at each step, the optimal metric value is calculated up front.  If any partition line's metric matches the optimal metric, the search stops there, since we can't find a better partition line.

ZenNode provides three different ways to create the BSP.  Two are almost identical - the only difference is the metric used to select the partition.  The third takes the shortcut mentioned above and only looks at a portion of the available SEGS.

Options:

The REJECT Resource

The REJECT map is an array, indexed by sector, that indicates who can see whom in the map.  For each pair of sectors, a single bit is stored in the REJECT map.  If this bit is a '1', then there is no line of sight (LOS) between the two.  A '0' means there is at least one position somewhere within the sectors that are visible to each other.  If a sector pair is marked with a '0' and there is, in fact, no real LOS, nothing bad will happen - the game won't crash but it will slow things down as it tries to find that LOS.  The more sectors that are marked with a '1', the faster the game will play.  If there is a LOS, but the REJECT is contains a '1', the monsters won't react as you might expect - they won't be able to see the player.  Some programs (like the Reject Map Builder a.k.a. RMB) allow you to apply special effects that mark sectors with a '1' or '0' without taking into account the actual LOS.  This allows you to create regions where you are safe from monsters, monsters that won't attack you, or increase the efficiency of the map (the number of 1's in the map) by telling the REJECT builder that two sectors really can't be seen (i.e. they're too far apart, or are blocked by differences in the heights of intervening celings and floors).  Before rebuilding a REJECT map, ZenNode will attempt to detect the presence of special effects (which appear as asymetric 0's and 1's in the REJECT) and will refuse to rebuild a new one unless forced to (if forced, the resulting REJECT will destroy all the effects present).

ZenNode currently creates a REJECT map based solely on LOS calculations.  The first thing ZenNode does is create a table of distances (in sectors) between each sector and every other one.  If no path is found between two sectors (i.e. there is no way to walk from one to the other) they are marked as hidden from each other up front, before the time-consuming LOS calculations start.  This allows ZenNode to speed up LOS calculations, since line combinations that are impossible won't even be attempted.  Note: Some special effects (like deep-water) will confuse ZenNodes calculations of distance between sectors.  If you have a map with these effects and monsters aren't behaving as expected, turn this option off (using -rc-).  Next, it analyzes the map, creates a list of lines to test, and runs through each combinition until all sectors are marked as either visible or hidden. .

To do the actual LOS calculations, ZenNode creates two lines that connect the two lines under test.  These are internally referred to as polyLines, since they are lines that may contain multiple segments.  They can be thought of as an elastic band wrapped around the two lines being tested.  As each solid line is tested, the rubber bands may be pushed inward.  Processing continues until either none of the intervening solid lines touch the polyLines or the two sides of the polyLines touch.  In the first case, a LOS is possible and the LOS is blocked in the second.  Note:If there are still lines within the enclosing polyLines, ZenNode considers the two lines under test as visible.  These remaining lines, internally mentioned as obstacles, are usually things like columns or small walls.  If there are more than one of them, there may in fact be no true LOS, but ZenNode will mark them as visible.  This is actually OK as we saw earlier.  This is somewhat of a simplification, but more or less depicts how it works.  This method allows ZenNode to definatively find a LOS if one exists.  Other REJECT builders simply try plotting lines between several points along the two lines being tested and looking for a blocking line.  If fewer lines are plotted, the time to process the REJECT can be decreased, but it is more likely to miss a valid LOS.  Likewise, to get the accuracy that ZenNode can achieve would require a large number of line to be plotted and would take an extremely long time.

ZenNode attempts to speed thing up by creating a list of which sectors are entirely contained within other sectors.  If an outer sector is determined to be hidden from another sector that is not contained within it, then it and all of it's children can be marked as hidden as well.  This is due to the fact that any possible LOS from a child sector would have to pass through the parent sector, and we've just determined that there are no valid LOSs to the parent.  By ordering the lines to test based on the sector's child count, ZenNode can elminate many line pair tests if the parent sectors are determined to be hidden early in the build process.  If a parent is found to visible, nothing is done to the children, they are still processed normally.  This is similar to, but more accurate than, RMB's PREPROCESS or GROUP options.

Options:

There are several things lined up for ZenNode's REJECT builder.  Some will be included soon, others are just ideas for things to speed things up a bit more.  These include:

Notes

  1. The REJECT algorithm may ignore some 'obstacles' (i.e.: pillars in a sector).  This means that a sector pair may be reported as visible, when the actual LOS is blocked by an obstacle.  This does not adversely affect the game (it's similar to a 'default' reject of all zeros), but does decrease the reject efficiency slightly (usually by less than 0.01%).
  2. I still haven't completely verified that ZenNode works properly with PolyObjects (the moving sectors in HEXEN).
  3. This program only does a cursory validity check of the level.  It does not check to see if you have a completely valid .WAD file to begin with.  If you don't, ZenNode will dutifully build it's data structures for the .WAD file, but DOOM may behave unpredictably.
  4. If you don't specify an output filename, the results will be written to a .WAD with the orignal filename in the current directory.  If the .WAD you specified as the source is in the current directory, YOUR ORIGINAL .WAD WILL BE OVERWRITTEN.
  5. If ZenNode detects that the new resources (NODES, SEGS, BLOCKMAP, etc.) are the same as those in the original file, the file is not rewritten.  That is, if you run the program twice on the same .WAD file, the second time, after generating the resources, it will not update the file.  (No big deal, just thought you might like to know.)
  6. Since ZenNode only builds resources (i.e.: NODES and related structures), it WILL rebuild levels E1M1 - E1M9.  By id Software's request, you are asked not to distribute custom levels below E2M1 for DOOM/Heretic.
  7. At any time while building a .WAD, file pressing <ESC> will stop ZenNode from rebuilding any further levels in the .WAD file.  However, it will continue rebuilding the level it is currently on.  Any levels that have been rebuilt will be written to the .WAD file before exiting.
  8. ZenNode stores any new data for a level in memory until the .WAD file is rewritten.  This means that if you rebuild a large .WAD file with lots of levels (id's IWADs for example), ZenNode may need a lot of memory.  If you run into memory problems (usually indicated by an 'Abnormal program termination' message), use the level selection option and do a few at a time.  For example:
              C:\>ZenNode DOOM2 map01+map02+map03+map+04+map05
              C:\>ZenNode DOOM2 map06+map07+map08+map+09+map10
              ...