Procedural Map Format
Procedural Map Format
The procedural map generation is divided into two parts, a geometry generation part and a rasterization part.
In the geometry generation phase simple geometrical input is converted to more complex shapes for the requested area.
E.g. a road, represented by a straight line, may be defined to connect two cities, represented by dots. The dots can be expanded to a set of neighbourhoods and further to building lots, with mai roads running across the city. The road between the cities may have fractal noise added to it along the way, making it undulate, bridges can be added to it where it crosses streams and rivers, and it could be made to follow the terrain shape, or even cut through hills, depending on the parameters used for it and the geometry generation functions.
Geometrical objects are:
- Point - A single point in 2D (or 3D) space
- Edge - A single line segment, connecting two Points.
- Network - Several line segments, that may be connected to each other. Could be e.g. a river including all tributaries, or the road network of a city.
- Sector - A single convex polygon along a plane (typically ground plane), with no holes.
- Area - Several sectors, which may share edges, but don't have to. Can cover disjoint space, and can have holes. Can even overlap itself, if several sectors cover the same space.
Geometrical objects can have properties associated with them, with string identifiers (start with letter, may contain letters, numbers, and underscores), and floating point or integer or string values. CHECK: Should we allow other values? Perhaps arbitrary Atlas values (list, map, boolean, etc)?
A Geometry can contains some number of each of these types of Geometrical Objects.
The generation is component based.
Component Based Generation
The same component based generation can be used for geometries as well as for the rasterization functions, and many other applications also (e.g. texture generation, sound generation, machine systems, spells, perhaps AI behaviours, and so on).
The idea is simple, we have Components, that have Ports that can be either input or output ports. The input ports of components are then either assigned fixed values, or the value of an output port of one component is used as the value for an input port of another component. A number of Components connected like this form a Component System.
The application domain can specify a set of basic Component types that can be used in a component system, for example in the domain of geometry we could have Component Types such as AddPointsAlongEdges (to produce an ouput geometry with point objects added along line objects in the input geometry, e.g. to add trees along a road), FillSectorsWithPoints (to e.g. populate a forest area with trees), AddNoiseToNetworks (to e.g. make rivers or roads undulate), AddBranchesToNetworks (to e.g. add smaller tributory roads or rivers to larger ones), and so on.
Component Modules are cutsom user defined Components, where the input and output ports are specified by the user, and connected to output and input ports of a Component System created by the user.
A Component Interface is a defined set of required input and output ports that accept some specified types of values.
The values produced or accepted by ports can be primitives (integer, boolean, floating point number, string), or various system types (colors, geometries, rasters, sounds, etc). It is even possible to have a Component Interface type as type for an input or output port, allowing a Component System to operate on Component Systems.
Component Interfaces may also be defined by different parts of the game system, for example the terrain generation specifies one Component Interface that has an output port of type Geometry. The world editors can then create procedural maps by creating component systems that implement this interface, generating a Geometry.
Component systems can be instantiated by creating instances of the Components, and then calling the set methods of each input port, using the get method of the output ports providing the value for that input port (or a fixed value if a fixed value is used as input).
An output port may have a requirement that all the input ports of the component should be initialized first before the output port is called. If all the output ports have this requirement, it leads to an acyclic component graph (which is ok, the editor just has to enforce these requirements, and the instantiation must initialize the components in the right order). NOTE: Allowing this requirement makes both the editor and instantiation a bit more complex, while allowing systems that e.g. calculate some primitive values, passing them from output to input ports. It is always possible to get around this, by instead passing iterators, visitors, or accessors as the output and input values of ports, in this case the calculation is then done lazily when a value is requested from the fully instantiated system..
A component system could import libraries of other component systems, using a java like classpath concept. Specific domain implementations (e.g. terrain generation, machine systems, etc) can make their basic components available in some directory in the classpath.
Component System XML Example
TODO: The XML format is to be completed, but perhaps something a bit like the example below.
NOTE: The view_x and view_y attributes are the locations of the component UI views themselves in a component graph editor, so they have nothing directly to do with the generated geometry.
NOTE: This format still has a lot of loose ends, so it will change.
<system> <component id = "ABC123456" type = "geometry.PrimitiveGeometry" name="Road to Hell" view_x = "10", view_y = "20" > <primitive_geometry> <point index = 1 > <p id="x">10</p> <p id="y">20</p> <p id="z">0</p> <p id="width">5</p> <p id="surface">dirt</p> </point> <point index = 2 > <p id="x">50</p> <p id="y">300</p> <p id="z">10</p> <p id="width">3</p> <p id="surface">dirt</p> </point> <edge> <start>1</start> <end>2</end> </edge> </primitive_geometry> </component> <component id = "DEF2000" type = "geometry.AddUndulationToLines" name="Twist roads" view_x = "50", view_y = "50" > <input id = "InputGeometry" source_component_id = "ABC123456" source_component_output_port = "OutputGeometry"> </input> <p id = "undulationWavelength" >100</p> <p id = "undulationAmplitude" >10</p> <p id = "randomSeed" >42</p> </component> <component id = "FGH3000" type = "geometry.AddPointsAlongLine" name="Good Intentions" view_x = "100", view_y = "20" > <input id = "InputGeometry" source_component_id = "DEF2000" source_component_output_port = "OutputGeometry"> </input> <p id = "pointDensity" >0.04</p> <p id = "pointProperties" > <p id = "modelIdentifier" >"environment/stones/models/goodIntentionStone.3ds"</p> <p id = "textureIdentifier" >"environment/stones/textures/hellishTexture.jpg"</p> </p> </component> <component id = "SystemOutput" type = "geometry.Geometry" view_x = "200", view_y = "20" > <system_output id = "OutputGeometry" source_component_id = "FGH3000" source_component_output_port = "OutputGeometry"> </system_output> </component> </system>