Comparison with L-Systems


Executing the Rules

The rules are located in the GrowthBehavior class, a subclass of Behavior. The GrowthBehavior object created by WrapTrees3D maintains an ArrayList of TReeLimb objects and an ImageComponent2D array of leaf pictures. The pictures are passed to it at construction time, while storing the first five treeLimb objects via calls to addLimb( ):

     // globals     private final static int TIME_DELAY = 100;  //ms     private WakeupCondition timeOut;     private ArrayList treeLimbs;           // of TreeLimb objects     private ImageComponent2D[] leafIms;    // a sequence of leaf images     public GrowthBehavior(ImageComponent2D[] lfIms)     { timeOut = new WakeupOnElapsedTime(TIME_DELAY);       treeLimbs = new ArrayList( );       leafIms = lfIms;     }     public void addLimb(TreeLimb limb)     {  treeLimbs.add(limb);  }

processStimulus( ) is called every TIME_DELAY milliseconds (100 ms), which calls applyRulesToLimbs( ). applyRulesToLimbs( ) cycles through the treeLimb objects in the ArrayList, calling applyRules( ) for each one:

     private void applyRulesToLimbs( )     {       TreeLimb limb;       for(int i=0; i < treeLimbs.size( ); i++) {         limb = (TreeLimb) treeLimbs.get(i);         applyRules(limb);         limb.incrAge( );   // a limb gets older after each iteration       }     }

Seven rules controlling the modification of a tree limb are placed in applyRules( ):

     private void applyRules(TreeLimb limb)     // Apply rules to the tree limb.     {       // get longer       if ((limb.getLength( ) < 1.0f) && !limb.hasLeaves( ))         limb.scaleLength(1.1f);       // get thicker       if ((limb.getRadius( ) <= (-0.05f*limb.getLevel( )+0.25f))                  && !limb.hasLeaves( ))         limb.scaleRadius(1.05f);       // get more brown       limb.stepToBrown( );       // spawn some child limbs       int axis;       if ((limb.getAge( ) == 5) && (treeLimbs.size( ) <= 256)               && !limb.hasLeaves( ) &&  (limb.getLevel( ) < 10)) {         axis = (Math.random( ) < 0.5) ? Z_AXIS : X_AXIS;         if (Math.random( ) < 0.85)           makeChild(axis, randomRange(10,30), 0.05f, 0.5f, limb);         axis = (Math.random( ) < 0.5) ? Z_AXIS : X_AXIS;         if (Math.random( ) < 0.85)           makeChild(axis, randomRange(-30,-10), 0.05f, 0.5f, limb);       }       // start some leaves       if ( (limb.getLevel( ) > 3) && (Math.random( ) < 0.08) &&            (limb.getNumChildren( ) == 0) && !limb.hasLeaves( ) )         makeLeaves(limb);       // grow the leaves       if (limb.getAge( )%10 == 0)         limb.showNextLeaf( );       // turn the base limb into a "blue bucket"       if ((limb.getAge( ) == 100) && (limb.getLevel( ) == 1)) {         limb.setRadius( 2.0f*limb.getRadius( ));         // limb.setLength( 2.0f*limb.getLength( ));         limb.setCurrColour( new Color3f(0.0f, 0.0f, 1.0f));       }     }  // end of applyRules( )

Almost all the rules have an if-then form, where the action is only carried out if the conditions evaluate to true for the current limb. Due to the repeating nature of GrowthBehavior, each rule will be applied to each tree limb in each time interval. This means that the rules express change in incremental terms.

The rule controlling a limb's thickness looks like this:

      if ((limb.getRadius( ) <= (-0.05f*limb.getLevel( )+0.25f))                  && !limb.hasLeaves( ))       limb.scaleRadius(1.05f);

The equation -0.05*limb.getLevel( )+0.25 relates the maximum radius to the limb's level. For example, a limb touching the ground (level = 1) can have a larger maximum radius than a branch higher up the tree. This means that branches will get less thick the higher up the tree they appear as in nature. The hasLeaves( ) part of the condition stops branches from growing thicker once they have leaves.

The rule for generating child limbs looks like this:

     if ((limb.getAge( ) == 5) && (treeLimbs.size( ) <= 256)               && !limb.hasLeaves( ) &&  (limb.getLevel( ) < 10)) {       axis = (Math.random( ) < 0.5) ? Z_AXIS : X_AXIS;       if (Math.random( ) < 0.85)         makeChild(axis, randomRange(10,30), 0.05f, 0.5f, limb);       axis = (Math.random( ) < 0.5) ? Z_AXIS : X_AXIS;       if (Math.random( ) < 0.85)         makeChild(axis, randomRange(-30,-10), 0.05f, 0.5f, limb);     }

The four conditions only permit a child limb to appear if the parent is at least five time intervals old, the total number of limbs in the scene is less or equal to 256, the parent has no leaves, and the branch isn't too far up the tree.

Math.random( ) is used to randomize the orientation axis and make it less certain that two children will be spawned. randomRange( ) returns a random number (in this case, an angle) in the specified range.

makeChild( )'s definition is:

     private void makeChild(int axis, double angle, float rad,                                        float len, TreeLimb par)     { TransformGroup startLimbTG = par.getEndLimbTG( );       TreeLimb child = new TreeLimb(axis, angle, rad, len, startLimbTG, par);       treeLimbs.add(child);   // add new limb to the ArrayList     } // end of makeChild( )

Leaves are spawned with the following rule:

     if ( (limb.getLevel( ) > 3) && (Math.random( ) < 0.08) &&          (limb.getNumChildren( ) == 0) && !limb.hasLeaves( ) )       makeLeaves(limb);

If a limb is far enough up the tree and has no children or existing leaves, then it will have a small chance of bursting into leaf. makeLeaves( ) packages up the creation of two ImageCsSeries objects, which are passed to the treeLimb object:

     private void makeLeaves(TreeLimb limb)     {       ImageCsSeries frontLeafShape = new ImageCsSeries(0.5f, 2.0f, leafIms);       ImageCsSeries backLeafShape = new ImageCsSeries(-0.5f, 2.0f, leafIms);       limb.addLeaves(frontLeafShape, backLeafShape);     }



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net