Edit detail for SwarM revision 1 of 1

1
Editor: antont
Time: 2006/05/29 20:59:46 GMT+0
Note:

changed:
-
a little test with an early blender tool by macouno,
http://studio.kyperjokki.fi/data/swarm/0001_0101.avi

blend for that stupidity is <a href="swarm3.blend">swarm3.blend</a>

<PRE>
@path "classes"

@include "Stationary.tz"
@include "Control.tz"
@include "Mobile.tz"

@define SWARM_SIZE    80.

#
# This simulation is inspired by Craig Reynolds classic Boids flocking
# algorithm.  The implementation is a bit more complicated than some of
# the other breve demos.
#
# The idea behind the algorithm is that each bird is influenced by three
# main urges:
#   1) the urge to avoid collisions with neighbors
#   2) the urge to stay close to neighbors
#   3) the urge to match the velocity of neighbors
#
# These three urges are implemented in the Birds class, and by giving 
# them different weights, we produce different flocking behaviors.
# Additionally, this simulation adds a "random" urge, and an urge
# to stay close to the center of the world.  
#
# If a bird touches the ground, it will land and stay on the ground
# for a short time before rejoining the flock.
#
# Run the demo as it is and try the different behaviors from the 
# Simulation menu.  Then try modifying the urge weights in the 
# Birds class yourself to change the behavior of the flock.  
#
# This demo was O(n^2) previous to breve 1.2.  We used to make every 
# bird look at the location of every other bird in order to assess 
# whether it was close enough to have an influence on behavior.  Starting 
# in breve version 1.2 we support "neighborhoods", a very fast and 
# efficient way for objects in the simulation to find other objects 
# within a certain range.
#
# It is unfortunate that sometimes it is difficult to see all the birds at 
# once, and that if the flock breaks in two, the camera may point in between
# them such that neither is visible. 
#

Controller Swarm.

Control : Swarm {
    + variables:
        birds (list).
        item (object).
        breveTexture (int).
        cloudTexture (int).
        selection (object).

        # the menu items corresponding to different flocking behaviors

        wackyMenu (object).
        obedientMenu (object).
        normalMenu (object).

    + to click on item (object):
        if selection: selection hide-neighbor-lines.
        if item: item show-neighbor-lines.

        selection = item.

        super click on item.

    + to init:
        floor (object).

        # Set up menus to modify the type of movement allowed.

        self add-menu named "Smoosh The Birdies" for-method "squish".
        self add-menu-separator.
        obedientMenu = (self add-menu named "Flock Obediently" for-method "flock-obediently").
        normalMenu = (self add-menu named "Flock Normally" for-method "flock-normally").
        wackyMenu = (self add-menu named "Flock Wackily" for-method "flock-wackily").

        self enable-lighting.
        self enable-smooth-drawing.
        self move-light to (0, 20, 20).

        breveTexture = (self load-image from "breve.sgi").
        cloudTexture = (self load-image from "clouds.sgi").

        # Add a huge floor.

        floor = new Stationary.
        floor register with-shape (new Shape init-with-cube size (100, 2, 100)) at-location (0, -5, 0).
        floor catch-shadows.

        birds = SWARM_SIZE new Birds.

        birds set-bitmap to breveTexture.

        # we'll call this method to set up the initial bird behavior.

        self flock-normally.

        self set-background-texture to cloudTexture.

        self offset-camera by (5, 1.5, 6).

        self enable-shadows.

    + to iterate:
        location (vector).
        topDiff (double).
        highLoc (vector).
 
        self update-neighbors.

        # get the average location, and point the camera at it.

        foreach item in birds: {
            item fly.
            location += (item get-location).

            if | (item get-location) | > | highLoc |: highLoc = (item get-location).
        }

        # in the context of a number, the list represents the number of items 
        # in the list.

        location /= birds.
        # print "the highest location is $location".

        topDiff = 0.0.

        foreach item in birds: {
            if topDiff < |location - (item get-location) |:
                topDiff = | location - (item get-location) |.
        }

        # we'll try to keep the camera aimed at the center of the 
        # flock, and zoom in and out depending on how spread out the 
        # flock is.  it's doesn't work very well :).

        self aim-camera at location.
        self zoom-camera to (.5 * topDiff) + 10.

        # call the superclass iterate method to step the simulation forward.

        super iterate.

    # The following methods are the menu items we provide.

    + to squish:
        birds move to (0, 0, 0).
    
    + to flock-normally:
        birds flock-normally.
        normalMenu check.
        obedientMenu uncheck.
        wackyMenu uncheck.

    + to flock-obediently:
        birds flock-obediently.
        normalMenu uncheck.
        obedientMenu check.
        wackyMenu uncheck.

    + to flock-wackily:
        birds flock-wackily.
        normalMenu uncheck.
        obedientMenu uncheck.
        wackyMenu check.
}

Mobile : Birds {
    + variables: 
        landed (int).

        # These are all parameters that can change the way the flock
        # behaves.  Look at the flock-normally method for an example.

        cruiseDistance (float).
 
        maxAcceleration (float).
        maxVelocity (float).

        wanderConstant (float).
        worldCenterConstant (float).
        centerConstant (float).
        velocityConstant (float).
        spacingConstant (float).

    + to init:
        # register the object, set the initial location, velocity and color.

        self register with-shape ((new Shape) init-with-sphere radius .1). 
        self move to random[(10, 10, 10)] - (5, -5, 5).
        self set-velocity to random[(20, 20, 20)] - (10, 10, 10).
        self set-color to random[(1, 1, 1)].

        # when we hit the ground, we want to land on it using the method "land".
        # it gets called automatically when the collision occurs.

        self handle-collisions with-type "Stationary" with-method "land".

        # set the neighborhood radius that we will look in to find neighbors.

        self set-neighborhood-size to 3.0.

    + to flock-normally:
        wanderConstant = 4.0.
        worldCenterConstant = 5.0.
        centerConstant = 2.0.
        velocityConstant = 2.0.
        spacingConstant = 5.0.

        maxVelocity = 15.
        maxAcceleration = 15.
        cruiseDistance = .4.

    + to flock-normally-old:
        wanderConstant = 6.0.
        worldCenterConstant = 5.0.
        centerConstant = 2.0.
        velocityConstant = 4.0.
        spacingConstant = 5.0.

        maxVelocity = 20.
        maxAcceleration = 12.
        cruiseDistance = .4.

    + to flock-obediently:
        # The obedient flocking has a lower maxAcceleration, which means that 
        # the objects don't react as quickly.  This has a calming effect on the 
        # agents.
       
        wanderConstant = 6.0.
        worldCenterConstant = 6.0.
        centerConstant = 2.0.
        velocityConstant = 3.0.
        spacingConstant = 4.0.

        maxVelocity = 16.
        maxAcceleration = 20.
        cruiseDistance = 1.

    + to flock-wackily:
        # The wacky flocking has a high maximum acceleration so that the 
        # agents act heavily on all of their impulses every iteration.
        # This makes them swarm like insects.

        wanderConstant = 8.0.
        worldCenterConstant = 14.0.
        centerConstant = 1.0.
        velocityConstant = 3.0.
        spacingConstant = 4.0.

        maxVelocity = 20.
        maxAcceleration = 30.
        cruiseDistance = .5.

    + to land with ground (object):
        # if we hit the ground, we stop moving and set the landed flag to 1.

        # print "Landing".

        self set-acceleration to (0, 0, 0).
        self set-velocity to (0, 0, 0).

        landed = 1.

        # we don't want to keep colliding with the ground, or else we'll
        # keep on getting stuck again at every iteration--move up just a 
        # tiny bit.

        self offset by (0, 0.1, 0).

    + to check-landed:
        # Ask the object if it has landed...

        return landed.

    + to fly:
        # This is the method which does it all.

        bird (object).
        toNeighbor (vector).
        
        centerUrge (vector).
        worldCenterUrge (vector).
        velocityUrge (vector).
        spacingUrge (vector).
        wanderUrge (vector).

        acceleration (vector).

        newVelocity (vector).

        neighbors (list).

        take-off (int).

        # get a list of neighbors within our neighborhood-size 
        # (set during init), and check to see if they're visible.
        # using get neighbors, we very quickly get a list of 
        # eligible objects and then check them further.  this is
        # *much* faster than checking every bird in the simulation.

        foreach bird in (self get-neighbors):
            if (self check-visibility of bird): push bird onto neighbors.

        # if we're on the ground now, we'll pick a random number to see if we 
        # take off again.  The chances are 1 in 40, which is actually pretty
        # reasonable since this code is called every iteration.

        if landed: {
            take-off = random[40].

            if take-off == 1: {
                # if we decide to take off, pick a random direction,
                # but not towards the ground.

                landed = 0.
                self set-velocity to random[(.1, 1.1, .1)] - (.05, 0, .05).
            } else {
                return. 
            }
        }

        # get the urge towards the center, and velocity matching urge. 

        centerUrge = (self get-center-urge with neighbors).
        velocityUrge = (self get-velocity-urge with neighbors).

        # are we too close to our neighbors?  get a vector which reflects that
        # urge.

        foreach bird in neighbors: {
            toNeighbor = (self get-location) - (bird get-location).
            if |toNeighbor| < cruiseDistance: spacingUrge += toNeighbor.
        }

        # Are we wandering too far from the center of the world?

        if |(self get-location)| > 10: worldCenterUrge = -(self get-location).

        # Add a random component to the accumulation.

        wanderUrge = random[(2, 2, 2)] - (1, 1, 1).

        # normalize all of the vectors to length 1.

        if |spacingUrge|: spacingUrge /= |spacingUrge|.
        if |worldCenterUrge|: worldCenterUrge /= |worldCenterUrge|.
        if |velocityUrge|: velocityUrge /= |velocityUrge|.
        if |centerUrge|: centerUrge /= |centerUrge|.
        if |wanderUrge|: wanderUrge /= |wanderUrge|.

        wanderUrge *= wanderConstant.
        worldCenterUrge *= worldCenterConstant.
        centerUrge *= centerConstant.
        velocityUrge *= velocityConstant.
        spacingUrge *= spacingConstant.

        acceleration = (worldCenterUrge + centerUrge + velocityUrge  + spacingUrge + wanderUrge).

        if |acceleration| != 0: acceleration /= |acceleration|.
        self set-acceleration to maxAcceleration * acceleration.

        newVelocity = (self get-velocity).

        if |newVelocity| > maxVelocity: 
            newVelocity = maxVelocity * newVelocity/|newVelocity|.

        self set-velocity to newVelocity.

    + to get-velocity-urge with flock (list):
        item (object).
        count (float).
        velocity (vector).

        # get the average velocity of all the visible birds in the flock.        
        foreach item in flock: {
            count += 1.
            velocity += (item get-velocity).
        }

        if count == 0: return (0, 0, 0).

        velocity /= count.

        return velocity - (self get-velocity).

    + to get-center-urge with flock (list):
        item (object).
        count (float).
        center (vector).

        # get the average location of all the visible birds in the flock.
        
        foreach item in flock: {
            count += 1.
            center += (item get-location).
        }

        if count == 0: return (0, 0, 0).

        center /= count.

        return center - (self get-location).

    + to check-visibility of item (object):
        # An item is visible if it is within a certain angle (2.0 radians) 
        # of the direction we're facing (assumed to be a vector in the 
        # direction we're moving).

        if (item == self): return 0.
        if !(item is a "Birds"): return 0.
        if (self get-angle to item) > 2.0: return 0.
        if (item check-landed): return 0.

        return 1. 

    + to get-angle to otherMobile (object):
        tempVector (vector).
        
        tempVector = (otherMobile get-location) - (self get-location).
        return angle((self get-velocity), tempVector).
}
</PRE>


a little test with an early blender tool by macouno, http://studio.kyperjokki.fi/data/swarm/0001_0101.avi

blend for that stupidity is swarm3.blend

@path "classes"

@include "Stationary.tz" @include "Control.tz" @include "Mobile.tz"

@define SWARM_SIZE 80.

# # This simulation is inspired by Craig Reynolds classic Boids flocking # algorithm. The implementation is a bit more complicated than some of # the other breve demos. # # The idea behind the algorithm is that each bird is influenced by three # main urges: # 1) the urge to avoid collisions with neighbors # 2) the urge to stay close to neighbors # 3) the urge to match the velocity of neighbors # # These three urges are implemented in the Birds class, and by giving # them different weights, we produce different flocking behaviors. # Additionally, this simulation adds a "random" urge, and an urge # to stay close to the center of the world. # # If a bird touches the ground, it will land and stay on the ground # for a short time before rejoining the flock. # # Run the demo as it is and try the different behaviors from the # Simulation menu. Then try modifying the urge weights in the # Birds class yourself to change the behavior of the flock. # # This demo was O(n^2) previous to breve 1.2. We used to make every # bird look at the location of every other bird in order to assess # whether it was close enough to have an influence on behavior. Starting # in breve version 1.2 we support "neighborhoods", a very fast and # efficient way for objects in the simulation to find other objects # within a certain range. # # It is unfortunate that sometimes it is difficult to see all the birds at # once, and that if the flock breaks in two, the camera may point in between # them such that neither is visible. #

Controller Swarm.

Control : Swarm { + variables: birds (list). item (object). breveTexture (int). cloudTexture (int). selection (object).

# the menu items corresponding to different flocking behaviors

wackyMenu (object). obedientMenu (object). normalMenu (object).

+ to click on item (object): if selection: selection hide-neighbor-lines. if item: item show-neighbor-lines.

selection = item.

super click on item.

+ to init: floor (object).

# Set up menus to modify the type of movement allowed.

self add-menu named "Smoosh The Birdies" for-method "squish". self add-menu-separator. obedientMenu = (self add-menu named "Flock Obediently" for-method "flock-obediently"). normalMenu = (self add-menu named "Flock Normally" for-method "flock-normally"). wackyMenu = (self add-menu named "Flock Wackily" for-method "flock-wackily").

self enable-lighting. self enable-smooth-drawing. self move-light to (0, 20, 20).

breveTexture = (self load-image from "breve.sgi"). cloudTexture = (self load-image from "clouds.sgi").

# Add a huge floor.

floor = new Stationary. floor register with-shape (new Shape init-with-cube size (100, 2, 100)) at-location (0, -5, 0). floor catch-shadows.

birds = SWARM_SIZE new Birds.

birds set-bitmap to breveTexture.

# we'll call this method to set up the initial bird behavior.

self flock-normally.

self set-background-texture to cloudTexture.

self offset-camera by (5, 1.5, 6).

self enable-shadows.

+ to iterate: location (vector). topDiff (double). highLoc (vector).

self update-neighbors.

# get the average location, and point the camera at it.

foreach item in birds: { item fly. location += (item get-location).

if | (item get-location) | > | highLoc |: highLoc = (item get-location). }

# in the context of a number, the list represents the number of items # in the list.

location /= birds. # print "the highest location is $location".

topDiff = 0.0.

foreach item in birds: { if topDiff < |location - (item get-location) |: topDiff = | location - (item get-location) |. }

# we'll try to keep the camera aimed at the center of the # flock, and zoom in and out depending on how spread out the # flock is. it's doesn't work very well :).

self aim-camera at location. self zoom-camera to (.5 * topDiff) + 10.

# call the superclass iterate method to step the simulation forward.

super iterate.

# The following methods are the menu items we provide.

+ to squish: birds move to (0, 0, 0).

+ to flock-normally: birds flock-normally. normalMenu check. obedientMenu uncheck. wackyMenu uncheck.

+ to flock-obediently: birds flock-obediently. normalMenu uncheck. obedientMenu check. wackyMenu uncheck.

+ to flock-wackily: birds flock-wackily. normalMenu uncheck. obedientMenu uncheck. wackyMenu check. }

Mobile : Birds { + variables: landed (int).

# These are all parameters that can change the way the flock # behaves. Look at the flock-normally method for an example.

cruiseDistance (float).

maxAcceleration (float). maxVelocity (float).

wanderConstant (float). worldCenterConstant (float). centerConstant (float). velocityConstant (float). spacingConstant (float).

+ to init: # register the object, set the initial location, velocity and color.

self register with-shape ((new Shape) init-with-sphere radius .1). self move to random[(10, 10, 10)] - (5, -5, 5). self set-velocity to random[(20, 20, 20)] - (10, 10, 10). self set-color to random[(1, 1, 1)].

# when we hit the ground, we want to land on it using the method "land". # it gets called automatically when the collision occurs.

self handle-collisions with-type "Stationary" with-method "land".

# set the neighborhood radius that we will look in to find neighbors.

self set-neighborhood-size to 3.0.

+ to flock-normally: wanderConstant = 4.0. worldCenterConstant = 5.0. centerConstant = 2.0. velocityConstant = 2.0. spacingConstant = 5.0.

maxVelocity = 15. maxAcceleration = 15. cruiseDistance = .4.

+ to flock-normally-old: wanderConstant = 6.0. worldCenterConstant = 5.0. centerConstant = 2.0. velocityConstant = 4.0. spacingConstant = 5.0.

maxVelocity = 20. maxAcceleration = 12. cruiseDistance = .4.

+ to flock-obediently: # The obedient flocking has a lower maxAcceleration, which means that # the objects don't react as quickly. This has a calming effect on the # agents.

wanderConstant = 6.0. worldCenterConstant = 6.0. centerConstant = 2.0. velocityConstant = 3.0. spacingConstant = 4.0.

maxVelocity = 16. maxAcceleration = 20. cruiseDistance = 1.

+ to flock-wackily: # The wacky flocking has a high maximum acceleration so that the # agents act heavily on all of their impulses every iteration. # This makes them swarm like insects.

wanderConstant = 8.0. worldCenterConstant = 14.0. centerConstant = 1.0. velocityConstant = 3.0. spacingConstant = 4.0.

maxVelocity = 20. maxAcceleration = 30. cruiseDistance = .5.

+ to land with ground (object): # if we hit the ground, we stop moving and set the landed flag to 1.

# print "Landing".

self set-acceleration to (0, 0, 0). self set-velocity to (0, 0, 0).

landed = 1.

# we don't want to keep colliding with the ground, or else we'll # keep on getting stuck again at every iteration--move up just a # tiny bit.

self offset by (0, 0.1, 0).

+ to check-landed: # Ask the object if it has landed...

return landed.

+ to fly: # This is the method which does it all.

bird (object). toNeighbor (vector).

centerUrge (vector). worldCenterUrge (vector). velocityUrge (vector). spacingUrge (vector). wanderUrge (vector).

acceleration (vector).

newVelocity (vector).

neighbors (list).

take-off (int).

# get a list of neighbors within our neighborhood-size # (set during init), and check to see if they're visible. # using get neighbors, we very quickly get a list of # eligible objects and then check them further. this is # much faster than checking every bird in the simulation.

foreach bird in (self get-neighbors): if (self check-visibility of bird): push bird onto neighbors.

# if we're on the ground now, we'll pick a random number to see if we # take off again. The chances are 1 in 40, which is actually pretty # reasonable since this code is called every iteration.

if landed: { take-off = random[40].

if take-off == 1: { # if we decide to take off, pick a random direction, # but not towards the ground.

landed = 0. self set-velocity to random[(.1, 1.1, .1)] - (.05, 0, .05). } else { return. } }

# get the urge towards the center, and velocity matching urge.

centerUrge = (self get-center-urge with neighbors). velocityUrge = (self get-velocity-urge with neighbors).

# are we too close to our neighbors? get a vector which reflects that # urge.

foreach bird in neighbors: { toNeighbor = (self get-location) - (bird get-location). if |toNeighbor| < cruiseDistance: spacingUrge += toNeighbor. }

# Are we wandering too far from the center of the world?

if |(self get-location)| > 10: worldCenterUrge = -(self get-location).

# Add a random component to the accumulation.

wanderUrge = random[(2, 2, 2)] - (1, 1, 1).

# normalize all of the vectors to length 1.

if |spacingUrge|: spacingUrge /= |spacingUrge|. if |worldCenterUrge|: worldCenterUrge /= |worldCenterUrge|. if |velocityUrge|: velocityUrge /= |velocityUrge|. if |centerUrge|: centerUrge /= |centerUrge|. if |wanderUrge|: wanderUrge /= |wanderUrge|.

wanderUrge = wanderConstant. worldCenterUrge = worldCenterConstant. centerUrge = centerConstant. velocityUrge = velocityConstant. spacingUrge *= spacingConstant.

acceleration = (worldCenterUrge + centerUrge + velocityUrge + spacingUrge + wanderUrge).

if |acceleration| != 0: acceleration /= |acceleration|. self set-acceleration to maxAcceleration * acceleration.

newVelocity = (self get-velocity).

if |newVelocity| > maxVelocity: newVelocity = maxVelocity * newVelocity/|newVelocity|.

self set-velocity to newVelocity.

+ to get-velocity-urge with flock (list): item (object). count (float). velocity (vector).

# get the average velocity of all the visible birds in the flock. foreach item in flock: { count += 1. velocity += (item get-velocity). }

if count == 0: return (0, 0, 0).

velocity /= count.

return velocity - (self get-velocity).

+ to get-center-urge with flock (list): item (object). count (float). center (vector).

# get the average location of all the visible birds in the flock.

foreach item in flock: { count += 1. center += (item get-location). }

if count == 0: return (0, 0, 0).

center /= count.

return center - (self get-location).

+ to check-visibility of item (object): # An item is visible if it is within a certain angle (2.0 radians) # of the direction we're facing (assumed to be a vector in the # direction we're moving).

if (item == self): return 0. if !(item is a "Birds"): return 0. if (self get-angle to item) > 2.0: return 0. if (item check-landed): return 0.

return 1.

+ to get-angle to otherMobile (object): tempVector (vector).

tempVector = (otherMobile get-location) - (self get-location). return angle((self get-velocity), tempVector). }