Viewing Issue Simple Details Jump to Notes ] Wiki ] View Advanced ] Issue History ] Print ]
ID Category Severity Reproducibility Date Submitted Last Update
0006184 [DCSS] Implementables feature N/A 2012-09-07 08:06 2012-12-25 06:25
Reporter brendan View Status public  
Assigned To
Priority normal Resolution open  
Status new   Product Branch 0.16 ancient branch
Summary 0006184: Abyss Procedural Level Generators
Description I'm reworking the Abyss (again)! The current Abyss is largely homogenous, monotonous and stale.

The new approach is largely inspired by constructive solid geometry (http://en.wikipedia.org/wiki/Constructive_solid_geometry [^] ) and procedural noise (ex. Perlin noise and Worley noise). Rather than constructing the dungeon by generating the whole world in one go, we can instead describe functions that when queried will produce parts of the dungeon. This allows us to have arbitrarily sized levels!

A collection of dungeon generators should produce interesting layouts.

How do dungeon generators work? Each generator should implement ProceduralLayout:
class ProceduralLayout
{
  public:
    virtual dungeon_feature_type operator()(const coord_def &p, double offset = 0);
};

`p` is a coordinate in the dungeon, `offset` is a parameter that will slowly vary as the player explores the dungeon. If the output varies in offset, then the abyss will morph slowly over time. Other generators might be static.

What should generators do?
Gosh. This one is tricky. I encourage you to look at the examples in dgn-proclayouts.h on the inception branch. Here's an example:

  class ColumnLayout : public ProceduralLayout
  {
    public:
      ColumnLayout(int cw, int cs = -1, int rw = -1, int rs = -1)
      {
        cs = (cs < 0 ? cw : cs);
        _col_width = cw;
        _col_space = cs;
        _row_width = (rw < 0 ? cw : rw);
        _row_space = (rs < 0 ? cs : rs);
      }
      
      dungeon_feature_type
      ColumnLayout::operator()(const coord_def &p, double offset)
      {
        int x = p.x % (_col_width + _col_space);
        int y = p.y % (_row_width + _row_space);
        if (x < _col_width && y < _row_width)
          return DNGN_MINWALL;
        return DNGN_FLOOR;
      }
    
    private:
      int _col_width, _col_space, _row_width, _row_space;
  };

Suppose we create a ColumnLayout(2). When the x and y coordinates each are less than 2 mod 4, you get a floor, otherwise you get a floor. This gives us a nice, regular grid pattern. Not too exciting, but it can be downright creepy! ColumnLayout(2,6) gives us a similar pattern, but with half as many columns.

Perhaps you (yes, you!) could try making a similar layout that produces diamonds instead of squares. Experiment!

More complex layouts consume other layouts and blend between them. The attached screenshots show a blended ColumnLayout / ChaoticLayout.
Additional Information
Tags No tags attached.
Attached Files png file icon Screen Shot 2012-09-06 at 10.57.31 PM.png [^] (72,267 bytes) 2012-09-07 08:06


png file icon Screen Shot 2012-09-06 at 10.56.37 PM.png [^] (77,667 bytes) 2012-09-07 08:06


zip file icon Boxes-2012-10-25.zip [^] (33,711 bytes) 2012-10-26 06:42
txt file icon example.txt [^] (1,403 bytes) 2012-12-10 07:30 [Show Content]

- Relationships

-  Notes
(0020406)
infiniplex (reporter)
2012-10-26 06:41

Here is a box/city layout generator. It is quite variable in appearance, so I included many samples. The style is controlled by a seed value and is consistent for any instance of the map generator. The important files are dgn-abyss-boxes-layouts.h and .cpp.

I currently do not have Crawl compiling on my machine, so I was unable to test it in the game. (There is a test program that queries it to build a vault map.) However, it does use the appropriate data types (I made copies) for input and output. I marked the three places that need to be changed with TODO. They are two spots for includes and one where crawl's random number generator should be used.
(0020407)
infiniplex (reporter)
2012-10-26 07:03

Note for anyone making layouts:

The return type has been changed. It now returns an instance of ProceduralSample that you create with (usually) 3 parameters:
1. The position (always p)
2. The map value (e.g. DNGN_FLOOR)
3. A lower bound on how long until this cell changes. If the layout does not change, return offset + 4096.
(0020417)
brendan (developer)
2012-10-27 05:22

infiniplex: Thanks for the layout. I'll get it merged into inception.
(0020614)
brendan (developer)
2012-12-10 07:38

Phenomenologically the layout performs rather well. There are a few peculiar cases (see example.txt).

I do have some concerns about the complexity of the layout. For example, a lot of the work done in _get_cell_for_random_box seems to replicate pieces of Worley noise. I think with some modest changes to cellular.cc (accepting a metric parameter, for example) the size of the layout could be reduced considerably.
(0020668)
infiniplex (reporter)
2012-12-21 06:58

I know about that output case. It can also occur horizontally, or as squarish blocks of both happen. There is also another one where the map is mostly empty floor. I had assumed that the Abyss was supposed to be weird, so it would be OK. You can avoid it by increasing the minimum-size-for-boxes constant in the .cpp file. It will be called something like GRID_SIZE_MIN (I don't have the code accessible here). A more targeted fix would make the file even longer.

I cannot say what affect using Worley noise would have. I my mind, you can do whatever you want with my layout, but I am not going to re-write it. I am making you another (simpler) layout, though. My bad habit of making things as separate programs and merging them does lead to code duplication at times. I will work on it.

I am not sure that basing all the sublayouts on Perlin and Worley noise is a good idea in general. It has a real danger of ending up "largely homogenous, monotonous and stale" in an undefinable (to non-developers anyway) way.
(0020672)
brendan (developer)
2012-12-25 06:25

Thanks. I'll see if I can rework the layout to do away with the pathological case.

The rationale behind using noise functions is that they're very useful primitives and our efforts to replicate their behavior are likely to fall flat and exhibit undesirable artifacts. In this case, placing the centers of rooms seems like an ideal candidate for Worley noise.

- Issue History
Date Modified Username Field Change
2012-09-07 08:06 brendan New Issue
2012-09-07 08:06 brendan File Added: Screen Shot 2012-09-06 at 10.57.31 PM.png
2012-09-07 08:06 brendan File Added: Screen Shot 2012-09-06 at 10.56.37 PM.png
2012-09-07 08:07 evilmike Category Patches => Implementables
2012-09-07 08:10 brendan Description Updated
2012-09-07 08:11 brendan Product Version inception =>
2012-09-07 08:11 brendan Additional Information Updated
2012-09-07 08:11 brendan Steps to Reproduce Updated
2012-09-07 08:11 brendan Additional Information Updated
2012-10-26 06:41 infiniplex Note Added: 0020406
2012-10-26 06:42 infiniplex File Added: Boxes-2012-10-25.zip
2012-10-26 07:03 infiniplex Note Added: 0020407
2012-10-27 05:22 brendan Note Added: 0020417
2012-12-10 07:30 brendan File Added: example.txt
2012-12-10 07:38 brendan Note Added: 0020614
2012-12-21 06:58 infiniplex Note Added: 0020668
2012-12-25 06:25 brendan Note Added: 0020672
2013-04-08 20:01 mumra Issue Monitored: mumra
2014-06-03 00:26 infiniplex Issue Monitored: infiniplex


Mantis 1.1.8[^]
Copyright © 2000 - 2009 Mantis Group
Powered by Mantis Bugtracker