DunGen  1.0
Dungeongenerator library for Irrlicht
DunGen 1.0 API documentation

Introduction

Welcome to the DunGen API documentation. Here you'll find any information you'll need to develop applications with the DunGen library.
You can get the newest versions of DunGen at dungen.squarefox.net

DunGen allows you to procedurally generate dungeons for your Irrlicht application.

A dungeon is created in several steps:

  1. create a L-system
  2. create a voxel cave from it
  3. erode the voxel cave, if wished
  4. place rooms
  5. create corridors
  6. filter the voxel cave, if wished
  7. create a mesh cave from the voxel cave
  8. assemble the dungeon and add it to your scene

L-Systems

A L-system (Lindenmayer system) is a grammar based mechanism, to describe fractal structures. It was originally developed by Lindenmayer to generate plants, but many L-systems drawn with a radius are suitable for some nice caves.

A L-system generates turtle graphics drawing instructions and has some parameters.
In DunGen these parameters are: angleYaw, anglePitch, angleRoll, radiusStart, radiusFactor, radiusDecrementor.
There are several instructions, which the turtle can perform. In DunGen, these are:

'F' - draw forward
'!' - decrease drawing radius, the formula is: newRadius = radiusFactor * oldRadius - radiusDecrement
'[' - push actual position, direction and radius on stack
']' - pop position, direction and radius from stack
'+' - rotate around the Up axis by angleYaw
'-' - rotate around the Up axis by -angleYaw
'u' - rotate around the Left axis by anglePitch
'o' - rotate around the Left axis by -anglePitch
'z' - rotate around the Front axis by angleRoll
'g' - rotate around the Front axis by -angleRoll
'|' - rotate around the Up axis by 180 degrees
'$' - orient turtle in the XZ-plane

Generating Caves

Caves are first created as voxel caves from L-systems. The L-system is adapted in size, so it fits exactly the voxelspace. The drawing radius of the L-system remains unchanged. The voxel cave can be eroded an arbitrary amount of times. Eroding removes random cave surface voxels with a specified likelihood (maybe you'll reach the border of the voxelspace on some parts of the cave - there nothing is eroded anymore). Sometimes some voxels in the cave are floating around (e.g. through erosion, or the nature of the L-system). You can remove them by filtering the cave.

Roompatterns

You can provide your own room patterns for DunGen. These patterns have to be .irr (Irrlicht scene) files and can be used as templates for rooms in the dungeon.

Each pattern has to fulfil some requirements, for being suitable:
If corridors shall be to connectable to the rooms, they have to have docking sites. Each docking site is a 2-dimensional mesh, with exact one boundary curve. Docking site mesh nodes in the .irr-file have to be named "dockingsite_xxx", where xxx is a increasing number, beginning with 000, 001, ... . You can have up to 1000 docking sites for each roompattern.

Docking Corridors

Corridors can be connected to rooms and the cave. If you connect them to rooms, you specify the room and docking site to connect to. If you connect them to caves, you specify a vector of minVoxels and maxVoxels. Those voxels define, in which region the cave is connected. The connection is done after a search for a possible cave section. There are 4 different search directions: X-positive, X-negative, Z-positive, Z-negative. Assume you search in positive X-direction. Then minVox would specify the start of the search. The Y and Z coordinates of minVoxels and maxVoxels specify the profile (a rectangle) of the created cave docking site (the docking site for the cave is created automatically). The difference between the X coordinates is additionally carved into the cave.

Detailobjects

It's possibly to place detailobjects (e.g. torches) within corridors. They can be placed in random distances, if wished. Please refer to SDetailobjectParameters for possible options.

Tutorial Dungeon

Here you find the code for a tutorial dungeon with a cave, some rooms, corridors and detailobjects.

#include <iostream>
#include <irrlicht.h>
#include <DunGen.h>
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(lib, "DunGen.lib")
#endif
int main(int argc, char* argv[])
{
// check for additional parameters: resolution, anti aliasing
irr::core::dimension2du resolution = irr::core::dimension2du(1200, 900);
if (argc>=3)
{
resolution.Width = atoi(argv[1]);
resolution.Height = atoi(argv[2]);
}
int antiAlias = 0;
if (argc>=4)
antiAlias = atoi(argv[3]);
std::cout << "Dungeongenerator Tutorial is started." << std::endl;
std::cout << "Resolution is: " << resolution.Width << " * " << resolution.Height << " , AntiAliasing: " << antiAlias << std::endl;
// create Irrlicht device
irr::SIrrlichtCreationParameters irrlichtParameter;
irrlichtParameter.DriverType = irr::video::EDT_DIRECT3D9;
irrlichtParameter.WindowSize = resolution;
irrlichtParameter.Bits = 32;
irrlichtParameter.Fullscreen = false;
irrlichtParameter.Stencilbuffer = false;
irrlichtParameter.Vsync = false;
irrlichtParameter.AntiAlias = antiAlias;
irrlichtParameter.EventReceiver = 0;
irr::IrrlichtDevice* irrDevice = irr::createDeviceEx(irrlichtParameter);
if(irrDevice == 0)
{
std::cout << "Irrlicht device could not be created, program is terminated." << std::endl;
return 1;
}
irr::scene::ISceneManager* sceneManager = irrDevice->getSceneManager();
irr::video::IVideoDriver* videoDriver = irrDevice->getVideoDriver();
irrDevice->setWindowCaption(L"Dungeongenerator Tutorial");
//************************************************************************
// Start of dungeon creation
//************************************************************************
// create Dungeongenerator instance
DunGen::CDunGen *dunGen = new DunGen::CDunGen(irrDevice);
dunGen->SetPrintToConsole(true);
// load room patterns and set materials
if (!antiAlias)
{
dunGen->RoomPatternLoad("data/roompattern_sphere.irr");
dunGen->RoomPatternLoad("data/roompattern_square1.irr");
dunGen->RoomPatternLoad("data/roompattern_square2.irr");
dunGen->RoomPatternLoad("data/roompattern_square4.irr");
dunGen->MaterialSetCorridor("data/corridor_texture.jpg", false, false);
dunGen->MaterialSetCaveMultiColor(false, false);
}
else
{
dunGen->RoomPatternLoad("data/roompattern_sphere_aa.irr");
dunGen->RoomPatternLoad("data/roompattern_square1_aa.irr");
dunGen->RoomPatternLoad("data/roompattern_square2_aa.irr");
dunGen->RoomPatternLoad("data/roompattern_square4_aa.irr");
dunGen->MaterialSetCorridor("data/corridor_texture.jpg", false, true);
dunGen->MaterialSetCaveMultiColor(false, true);
}
// create L-system
dunGen->LSystemSetStart("YYFYF");
dunGen->LSystemAddRule('F',"F-YX-X---");
dunGen->LSystemAddRule('X',"F$F++F-X");
dunGen->LSystemAddRule('Y',"oYX--XX++");
// create voxel cave
dunGen->CreateVoxelCave();
dunGen->ErodeVoxelCave(0.5);
// place rooms
dunGen->CreateRoom(0,irr::core::vector3d<double>(379.0,239.0,497.0),irr::core::vector3d<double>(0.0,180.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //0
dunGen->CreateRoom(0,irr::core::vector3d<double>(14.0,260.0,121.0),irr::core::vector3d<double>(0.0,0.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //1
dunGen->CreateRoom(3,irr::core::vector3d<double>(539.0,281.0,378.0),irr::core::vector3d<double>(0.0,180.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //2
dunGen->CreateRoom(3,irr::core::vector3d<double>(312.0,272.0,-80.0),irr::core::vector3d<double>(0.0,270.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //3
dunGen->CreateRoom(3,irr::core::vector3d<double>(72.0,202.0,557.0),irr::core::vector3d<double>(0.0,80.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //4
dunGen->CreateRoom(2,irr::core::vector3d<double>(404.0,259.0,651.0),irr::core::vector3d<double>(0.0,90.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //5
dunGen->CreateRoom(2,irr::core::vector3d<double>(145.0,260.0,0.0),irr::core::vector3d<double>(0.0,200.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //6
dunGen->CreateRoom(2,irr::core::vector3d<double>(700.0,200,250.0),irr::core::vector3d<double>(0.0,305.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //7
dunGen->CreateRoom(1,irr::core::vector3d<double>(440.0,277.0,803.0),irr::core::vector3d<double>(0.0,90.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //8
dunGen->CreateRoom(1,irr::core::vector3d<double>(287.0,270.0,-245.0),irr::core::vector3d<double>(0.0,270.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //9
dunGen->CreateRoom(1,irr::core::vector3d<double>(-129.0,211.0,584.0),irr::core::vector3d<double>(0.0,20.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //10
dunGen->CreateRoom(1,irr::core::vector3d<double>(-100.0,300.0,-70.0),irr::core::vector3d<double>(0.0,300.0,0.0),irr::core::vector3d<double>(1.0,1.0,1.0)); //11
// set corridor parameters
dunGen->CorrdidorAddPoint(-4.0,-4.0, 1.4);
dunGen->CorrdidorAddPoint(-4.0, 0.0, 0.9);
dunGen->CorrdidorAddPoint(-3.0, 2.0, 0.6);
dunGen->CorrdidorAddPoint(-2.0, 3.0, 0.3);
dunGen->CorrdidorAddPoint( 0.0, 4.0, 0.0);
dunGen->CorrdidorAddPoint( 4.0, 0.0, 0.9);
dunGen->CorrdidorAddPoint( 4.0,-4.0, 1.4);
dunGen->CorrdidorAddPoint( 0.0,-4.0, 2.0);
// specify detailobjects
irr::scene::IMesh* mesh = sceneManager->getMesh("data/detailobject.3ds");
detailObject.Node = sceneManager->addMeshSceneNode(mesh);
detailObject.Node->setName("Torch");
detailObject.Node->setMaterialType(irr::video::EMT_SOLID);
detailObject.Node->setMaterialFlag(irr::video::EMF_LIGHTING,false);
detailObject.Node->setMaterialFlag(irr::video::EMF_ANTI_ALIASING, (0!=antiAlias) );
detailObject.Position = irr::core::vector2d<double>(-4.0,-0.5);
detailObject.Scale = irr::core::vector3d<double>(1.0,1.0,1.0);
detailObject.Rotation = irr::core::vector3d<double>(0.0,90.0,0.0);
detailObject.DistanceSampling = 0.6;
detailObject.DistanceNumFactor = 4;
detailObject.DistanceNumMin = 3;
detailObject.DistanceNumMax = 3;
detailObject.DistanceNumMinFirstElement = 0;
detailObject.DistanceNumMaxFirstElement = 0;
detailObject.ObjectAtT1 = false;
dunGen->CorrdidorAddDetailObject(detailObject);
detailObject.Position = irr::core::vector2d<double>(4.0,-0.5);
detailObject.Rotation = irr::core::vector3d<double>(0.0,270.0,0.0);
dunGen->CorrdidorAddDetailObject(detailObject);
// create corridors
bool sightBlocking; // tells us, if the corridor is definitely sight blocking, but we don't use this information here
dunGen->CreateCorridorRoomRoom(0,2,4.0,200.0,2,1,4.0,200.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(0,1,4.0,100.0,5,0,4.0,100.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(5,1,4.0,100.0,8,0,4.0,100.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(2,2,4.0,150.0,7,0,4.0,250.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(2,3,4.0,150.0,7,1,4.0,250.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(1,1,4.0,200.0,6,0,4.0,200.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(1,2,4.0,200.0,11,0,4.0,200.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(6,1,4.0,200.0,3,3,4.0,200.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(3,2,4.0,100.0,9,0,4.0,100.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(4,1,4.0,100.0,10,0,4.0,100.0,sightBlocking);
dunGen->CreateCorridorRoomRoom(4,2,4.0,400.0,4,3,4.0,400.0,sightBlocking);
dunGen->CreateCorridorRoomCave(4,0,4.0,100.0, irr::core::vector3d<unsigned int>(46,172,470),irr::core::vector3d<unsigned int>(54,180,470),DunGen::EDirection::Z_NEGATIVE,4.0,100.0,sightBlocking);
dunGen->CreateCorridorRoomCave(0,0,4.0,200.0, irr::core::vector3d<unsigned int>(136,189,450),irr::core::vector3d<unsigned int>(144,197,450),DunGen::EDirection::Z_NEGATIVE,4.0,200.0,sightBlocking);
dunGen->CreateCorridorRoomCave(0,3,4.0,100.0, irr::core::vector3d<unsigned int>(389,231,450),irr::core::vector3d<unsigned int>(397,239,450),DunGen::EDirection::Z_NEGATIVE,4.0,100.0,sightBlocking);
dunGen->CreateCorridorRoomCave(2,0,4.0,400.0, irr::core::vector3d<unsigned int>(478,257,260),irr::core::vector3d<unsigned int>(486,265,260),DunGen::EDirection::Z_NEGATIVE,4.0,200.0,sightBlocking);
dunGen->CreateCorridorRoomCave(3,1,4.0,400.0, irr::core::vector3d<unsigned int>(460,264,80),irr::core::vector3d<unsigned int>(468,272,80),DunGen::EDirection::Z_POSITIVE,4.0,400.0,sightBlocking);
dunGen->CreateCorridorRoomCave(3,0,4.0,50.0, irr::core::vector3d<unsigned int>(340,287,40),irr::core::vector3d<unsigned int>(348,295,40),DunGen::EDirection::Z_POSITIVE,4.0,50.0,sightBlocking);
dunGen->CreateCorridorRoomCave(1,0,4.0,400.0, irr::core::vector3d<unsigned int>(50,318,262),irr::core::vector3d<unsigned int>(50,326,270),DunGen::EDirection::X_POSITIVE,4.0,300.0,sightBlocking);
dunGen->CreateCorridorRoomCave(1,3,4.0,100.0, irr::core::vector3d<unsigned int>(80,181,240),irr::core::vector3d<unsigned int>(88,189,240),DunGen::EDirection::Z_POSITIVE,4.0,100.0,sightBlocking);
// modify corridor parameters
dunGen->CorrdidorAddPoint(-4.0,-4.0, 0.0);
dunGen->CorrdidorAddPoint(-4.0, 4.0, 1.0);
dunGen->CorrdidorAddPoint(4.0, 4.0, 2.0);
dunGen->CorrdidorAddPoint(4.0, -4.0, 1.0);
detailObject.Position = irr::core::vector2d<double>(4.0,0.0);
dunGen->CorrdidorAddDetailObject(detailObject);
detailObject.Position = irr::core::vector2d<double>(-4.0,0.0);
detailObject.Rotation = irr::core::vector3d<double>(0.0,90.0,0.0);
dunGen->CorrdidorAddDetailObject(detailObject);
// cave to cave corridor with 2 voxel additional digging depth
irr::core::vector3d<unsigned int>(300,306,205),irr::core::vector3d<unsigned int>(302,314,213),DunGen::EDirection::X_NEGATIVE,4.0,200.0,
irr::core::vector3d<unsigned int>(350,244,259),irr::core::vector3d<unsigned int>(352,252,267),DunGen::EDirection::X_POSITIVE,4.0,200.0,
sightBlocking);
// create mesh cave (including filter step)
dunGen->CreateMeshCave();
// assemble dungeon
dunGen->AddDungeon(sceneManager->getRootSceneNode(),sceneManager);
// drop the detailobject-mesh and hide the original node (it is duplicated in the dungeon already)
// note: you cannot drop the original node itself, this would lead to some strange behavior
// it seems, that the clones depend on the original node on some way
mesh->drop();
detailObject.Node->setVisible(false);
//************************************************************************
// Dungeon creation finished
//************************************************************************
// create camera
irr::scene::ICameraSceneNode* camera = sceneManager->addCameraSceneNodeFPS(0,100.0f,0.05f);
camera->setFarValue(20000.0f);
camera->setPosition(irr::core::vector3df(0,0,0));
camera->setTarget(irr::core::vector3df(99999.f, 99999.f, 99999.f));
irr::s32 lastFPS(0), actFPS(0);
irr::core::vector3d<int> lastCamPos(-1,-1,-1);
irr::core::vector3d<int> actCamPos(0,0,0);
irr::core::vector3df tmp;
// as long as the engine is active:
while(irrDevice->run() && videoDriver)
{
if (irrDevice->isWindowActive())
{
// draw everything
videoDriver->beginScene(true, true, irr::video::SColor(255,100,101,140));
sceneManager->drawAll();
videoDriver->endScene();
actFPS = videoDriver->getFPS();
tmp = camera->getAbsolutePosition();
actCamPos.X = static_cast<int>(tmp.X + 0.5f);
actCamPos.Y = static_cast<int>(tmp.Y + 0.5f);
actCamPos.Z = static_cast<int>(tmp.Z + 0.5f);
if (actFPS != lastFPS || lastCamPos != actCamPos)
{
// update FPS and camera position
// and show them as window name
lastFPS = actFPS;
lastCamPos = actCamPos;
irr::core::stringw str = L"Dungeongenerator Tutorial - FPS: ";
str += actFPS;
str += " - Position: (";
str += actCamPos.X;
str += ",";
str += actCamPos.Y;
str += ",";
str += actCamPos.Z;
str += ")";
irrDevice->setWindowCaption(str.c_str());
}
}
else
irrDevice->yield();
}
// destroy the engine
irrDevice->drop();
return 0;
}

Try also some other L-system (of course, then you have to replace the rooms and corridors). Some nice caves are:

"Bizzare": start = "uFgF", rule1 = 'F'->"Y[FF]", rule2 = 'X'->"YFF+FF]+F", rule3 = 'Y'->"F[YXFFF[Y",
angleYaw = 340, anglePitch = 12, angleRoll = -20.8334, startRadius, = 20, radiusFactor = 1, radiusDecrement = 0
iterationDepth = 7

"Mayan Sunsphere": start = "W", rule1 = 'W'->"W+[X]", rule2 = 'X'->"YYYYYYYYY", rule3 = 'Y'->"FoFoFoFo",
angleYaw = 10, anglePitch = 10, angleRoll = 0, startRadius, = 32, radiusFactor = 1, radiusDecrement = 0
iterationDepth = 20
For this L-system, you need the filter step, to remove the inner voxels.

"Wide Hall": start = "F", rule1 = 'F'->"F+FFF"
angleYaw = 68, anglePitch = 0, angleRoll = 0, startRadius, = 16, radiusFactor = 1, radiusDecrement = 0
iterationDepth = 7

As you see, the "Wide Hall" is a very simple L-system, so you don't need very complex systems for nice caves.