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:
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.
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.
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.
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.
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[])
{
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;
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");
if (!antiAlias)
{
}
else
{
}
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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.
Position = irr::core::vector2d<double>(4.0,-0.5);
detailObject.
Rotation = irr::core::vector3d<double>(0.0,270.0,0.0);
bool 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);
detailObject.
Position = irr::core::vector2d<double>(4.0,0.0);
detailObject.
Position = irr::core::vector2d<double>(-4.0,0.0);
detailObject.
Rotation = irr::core::vector3d<double>(0.0,90.0,0.0);
sightBlocking);
dunGen->
AddDungeon(sceneManager->getRootSceneNode(),sceneManager);
mesh->drop();
detailObject.
Node->setVisible(
false);
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;
while(irrDevice->run() && videoDriver)
{
if (irrDevice->isWindowActive())
{
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)
{
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();
}
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.
As you see, the "Wide Hall" is a very simple L-system, so you don't need very complex systems for nice caves.