The Nebula Device

Code

NOH structure (named object heirarchy)

/lib = libary of nents to clone fro

/node = library of nvisnodes to be attached to the scene graph

/sys/servers = servers

Classes / structure

big classes

nThing : nRoot = my base class +

  1. message(nMessage& m) -> passes m up the tree
  2. trigger() -> super function for tick
  3. tick() -> code to be ran every frame.
  4. convenience functions
    1. up
    2. down

nEnt : nThing = game entity

  1. inheritVars ( enttype ) -> copies/links vars from /lib/entname
  2. addVar( varname ) -> attachs variable varname to this entity.
  3. getVar( varname ) -> returns a pointer / handle to my variable varname
  4. stopStalling -> signals that an aspect has finsihed post mortem processing.

nAspect : nThing = attribute / function set attached to an nEnt

  1. bool entDying() -> return whether or not to stall the ents death

nFSM : nAspect = Finite State Machine aspect

  1. getState = return link / name to my current state.
  2. setState = set link

nFSMState : nAspect = State for an FSM

  1. process( event e )

nFSM : nThing = finite state machine

  1. setState
  2. getState

Servers

nGameServer : nThing

  1. main loop

nTimeServer - Controls nThing->tick()

  1. calls nEnt->trigger( this )
  2. manages tick calls

OLD STUFF

Win dlls

Nebula Homepage

Nebula Twiki

The nebula device is a professional game engine my major projects are built upon.

Compiling

As with all things big it can be a pain to compile.

Libraries

tcl/tk 8.4.4.

occasionally python and libmng.

Environment Variables - Put this in your .bashrc

export NOMADS_HOME='/home/miles/nebula'
export NEBULADIR='/home/miles/nebula'
export PATH=$PATH:$NOMADS_HOME/bin:$NOMADS_HOME/bin/linuxd
export LD_LIBRARY_PATH=$NOMADS_HOME/bin/linuxd:$LD_LIBRARY_PATH
export OSTYPE

If you are going to work with my code in the least bit you need to understand the architecture it provides.

I will attempt to explain the most important parts of the engine in here, as well as explain much of the work I have built on top of it.

The Namespace heirarchy

Every major class that is instantiated lives somewhere in the namespace tree. Think of it as a directory tree, every file is organized into a tree. This allows you to easily and efficiently find anything you want.

/sys -> base directory for all high level engine stuff

/sys/servers -> where all the major major classes live. My gameserver holds the main loop and lives in here, as well as the all important kernelserver. Anything that is a very high level class that everything else will access with lives here.

/usr -> usr is where all the information relevant to the game state lives.

/usr/scene -> This is the base of the scenegraph, everything that exists in the world lives in here somewhere.

/lib -> This is my library of entities. Any object in the world is going to be a clone of one of these.

/node -> This is where all the scenegraph nodes live, each one will be attached to the graph multiple times through link nodes. (Explained later).

/side -> Probably should be /usr/side this holds all the information pertaining a side. These are usually lists of units belonging to each.

Classes

Nebula Provided

nKernelServer -> The big big big class, this is the central class. It mostly works with namespace issues, and creating objects.

nConsoleServer -> Renders the console on the screen.

nRoot -> comparable to an Object in java, this is a ancestor for any class that lives in the namespace.

n3DNode -> This represents a position/orientation node in the scenegraph, anything that is displayed will have one of these at its root.

nMeshNode -> This holds a 3 dimensional mesh of polygons. These are loaded from .n3d files, whih are converted via wfflatten from .obj files.

nShaderNode -> This holds all the information about a shader, which sums up to everything you need to know to take a mesh and a texture and render it on screen. This is a huge class with tons of options.

nTexArrayNode -> This holds an array of textures and alpha maps.

My

nGameServer -> The big big class, this holds the main loop.

nMessageServer -> Manages message passing between objects. This is done via a heap of messages to send sorted by time.

nThing -> my super class of nVisNode this holds the ability to have a trigger call passed down as well as messaging.

nEnt -> My entity class, an entity represents any kind of actor in the world. It functions as a place to keep variables.

nVar -> A variable attached to an entity, should be handled entirely by the nent class. Going to be phased out into nenvs eventually.

nAspect -> The root aspect class, an aspect is any kind of trait you could give to an entity.

nBody -> this aspect binds a n3dnode to the entity, allowing it to be rendered.

nPhysics -> this and its children represent varying levels of physics attached to the object, they come with a large set of variables to represent all the physical attributes of a rigid body entity.

nHp -> Manages the death and destruction of an object. (Non trivial).

npAspect -> Root aspect for np classes, p or persistent classes halt the destruction of an object until they are done. (I.e. making a dying animation, or going through some final commands).

nArmor -> Translates events recieved into damage taken.

nWeapon -> Holds the ability to fire projectiles.

nAI -> The root class for all the various ai's. An Ai is defined as something that alters the ent according to some kind of decision versus formula. Has a special trigger that only goes to its first child (for todo queueing).

The scene graph

Provided by nebula, the scenegraph is how everything is displayed. Any node that inherits from nvisnode attaches to it in someway. The position/orientation of nodes is inherited down the tree, while aspects are attached up the tree.

The root of any object displayed is a n3DNode, which holds the position / rotation.

It usually has 3 children

  1. nMeshNode which holds the mesh of polygons for that object
  2. nTexArrayNode holds all the textures mapped on that object
  3. nShaderNode contains the variables needed to know how to render the above too into the scene.

Hierarchy

n3dnodes are placed in a tree which describes the graph that is displayed. For instance a car would have a body at the base with wheels for children. The wheels need only know their position offset from the base, since they are children of it as the car moves and turns the wheels will move with them, offset by there own position and rotation. This hugely simplifies things.

Graph Aspects

Complicating things are nLinkNodes. There are many cases where it is desirable to reduce the tree to a more flexible DAG, or directed acyclic graph. Objects live in the usual tree, but links are allowed to essentially have children that live elsewhere. For example there might exist a car and a wheel, in two entirely different places. The car has 4 3dnodes with the wheel's offset, each of which has a linknode which points to the wheel. This is superior to having the 4 wheel's in that all 4 children are identical, changes to the wheel are always synchronized, and nlinknodes are much smaller in consumption of resources then an entire object tree.

My architecture

In my projects there is another level of complication the ent classes. Since these hold all the entity data there is no reason to instantiate 3dnodes at all, they all exist in there own library (/node) and each entity has links to the objects representing there display.

Channels

Since more then one car might exist, if they all point to the same tire how do you display when one of the car gets a flat? This is accomplished through channels, a channel is similar to a variable that is attached to an object. So the 3dnode would have a channel attached to it holding the state of the wheels, then the 3dnode for the wheel has a nFlipFlop which checks the channel and displays the appropriate tire.

I don't use channels, nvar's attached to ent's are a more flexible way to use the same data, and channels are damn confusing (they propogate down and up the tree funnily). The time is coming where I will want to use them however, mainly for doing rotating treads on the tanks.

Vars

Aspects all use the same variable names for the same kind of data to keep things standardized. Most are long and obvious but here are the most important

Position / Velocity / Force -> Physics variables for the objects location

Rotation -> Quaternion representing orientation

Heading -> Convenient vector representation for orientation. Great for AI's, heading.x = pitch, heading.y = yaw.

rVelocity / Torque -> Angular velocity / torque.