pull/1/merge
kcozens 2018-04-03 14:56:24 +00:00 committed by GitHub
commit 90e2661c35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 849 additions and 847 deletions

View File

@ -31,343 +31,343 @@ using OpenMetaverse;
namespace Flocking namespace Flocking
{ {
public class Bird public class Bird
{ {
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
private string m_id; private string m_id;
private Vector3 m_loc; private Vector3 m_loc;
private Vector3 m_vel; private Vector3 m_vel;
private Vector3 m_acc; private Vector3 m_acc;
private Random m_rndnums = new Random (Environment.TickCount); private Random m_rndnums = new Random (Environment.TickCount);
private FlockingModel m_model; private FlockingModel m_model;
private FlowMap m_flowMap; private FlowMap m_flowMap;
private int m_regionX; private int m_regionX;
private int m_regionY; private int m_regionY;
private int m_regionZ; private int m_regionZ;
private float m_regionBorder; private float m_regionBorder;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Flocking.Bird"/> class. /// Initializes a new instance of the <see cref="Flocking.Bird"/> class.
/// </summary> /// </summary>
/// <param name='l'> /// <param name='l'>
/// L. the initial position of this bird /// L. the initial position of this bird
/// </param> /// </param>
/// <param name='ms'> /// <param name='ms'>
/// Ms. max speed this bird can attain /// Ms. max speed this bird can attain
/// </param> /// </param>
/// <param name='mf'> /// <param name='mf'>
/// Mf. max force / acceleration this bird can extert /// Mf. max force / acceleration this bird can extert
/// </param> /// </param>
public Bird (string id, FlockingModel model, FlowMap flowMap) public Bird (string id, FlockingModel model, FlowMap flowMap)
{ {
m_id = id; m_id = id;
m_acc = Vector3.Zero; m_acc = Vector3.Zero;
m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1)); m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1));
m_model = model; m_model = model;
m_flowMap = flowMap; m_flowMap = flowMap;
m_regionX = m_flowMap.LengthX; m_regionX = m_flowMap.LengthX;
m_regionY = m_flowMap.LengthY; m_regionY = m_flowMap.LengthY;
m_regionZ = m_flowMap.LengthZ; m_regionZ = m_flowMap.LengthZ;
m_regionBorder = m_flowMap.Border; m_regionBorder = m_flowMap.Border;
} }
public Vector3 Location {
get { return m_loc;}
set { m_loc = value; }
}
public Vector3 Velocity { public Vector3 Location {
get { return m_vel;} get { return m_loc;}
} set { m_loc = value; }
}
public String Id {
get {return m_id;}
}
/// <summary>
/// Moves our bird in the scene relative to the rest of the flock.
/// </summary>
/// <param name='birds'>
/// Birds. all the other chaps in the scene
/// </param>
public void MoveInSceneRelativeToFlock (List<Bird> birds)
{
// we would like to stay with our mates
Flock (birds);
// our first priority is to not hurt ourselves public Vector3 Velocity {
AvoidObstacles (); get { return m_vel;}
}
// then we want to avoid any threats
// this not implemented yet
// ok so we worked our where we want to go, so ...
UpdatePositionInScene ();
}
/// <summary> public String Id {
/// Move within our flock get {return m_id;}
/// }
/// We accumulate a new acceleration each time based on three rules
/// these are:
/// our separation from our closest neighbours,
/// our desire to keep travelling within the local flock,
/// our desire to move towards the flock centre
///
/// </summary>
void Flock (List<Bird> birds)
{
// calc the force vectors on this bird
Vector3 sep = Separate (birds); // Separation
Vector3 ali = Align (birds); // Alignment
Vector3 coh = Cohesion (birds); // Cohesion
// Arbitrarily weight these forces
//TODO: expose these consts
sep *= 1.5f; //.mult(1.5);
//ali.mult(1.0);
ali *= 1.0f;
//coh.mult(1.0);
coh *= 1.0f;
// Add the force vectors to the current acceleration of the bird
//acc.add(sep);
m_acc += sep;
//acc.add(ali);
m_acc += ali;
//acc.add(coh);
m_acc += coh;
}
/// <summary> /// <summary>
/// Method to update our location within the scene. /// Moves our bird in the scene relative to the rest of the flock.
/// update our location in the world based on our /// </summary>
/// current location, velocity and acceleration /// <param name='birds'>
/// taking into account our max speed /// Birds. all the other chaps in the scene
/// /// </param>
/// </summary> public void MoveInSceneRelativeToFlock (List<Bird> birds)
void UpdatePositionInScene () {
{ // we would like to stay with our mates
// Update velocity Flock (birds);
//vel.add(acc);
m_vel += m_acc;
// Limit speed
//m_vel.limit(maxspeed);
m_vel = BirdsUtil.Limit (m_vel, m_model.MaxSpeed);
m_loc += m_vel;
// Reset accelertion to 0 each cycle
m_acc *= 0.0f;
}
/// <summary>
/// Seek the specified target. Move into that flock
/// Accelerate us towards where we want to go
/// </summary>
/// <param name='target'>
/// Target. the position within the flock we would like to achieve
/// </param>
void Seek (Vector3 target)
{
m_acc += Steer (target, false);
}
/// <summary>
/// Arrive the specified target. Slow us down, as we are almost there
/// </summary>
/// <param name='target'>
/// Target. the flock we would like to think ourselves part of
/// </param>
void arrive (Vector3 target)
{
m_acc += Steer (target, true);
}
/// A method that calculates a steering vector towards a target // our first priority is to not hurt ourselves
/// Takes a second argument, if true, it slows down as it approaches the target AvoidObstacles ();
Vector3 Steer (Vector3 target, bool slowdown)
{
Vector3 steer; // The steering vector
Vector3 desired = Vector3.Subtract(target, m_loc); // A vector pointing from the location to the target
float d = desired.Length (); // Distance from the target is the magnitude of the vector
// If the distance is greater than 0, calc steering (otherwise return zero vector)
if (d > 0) {
// Normalize desired
desired.Normalize ();
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
if ((slowdown) && (d < 100.0f)) {
desired *= (m_model.MaxSpeed * (d / 100.0f)); // This damping is somewhat arbitrary
} else {
desired *= m_model.MaxSpeed;
}
// Steering = Desired minus Velocity
//steer = target.sub(desired,m_vel);
steer = Vector3.Subtract (desired, m_vel);
//steer.limit(maxforce); // Limit to maximum steering force
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
} else {
steer = Vector3.Zero;
}
return steer;
}
// then we want to avoid any threats
/// <summary> // this not implemented yet
/// Borders this instance.
/// if we get too close wrap us around
/// CHANGE THIS to navigate away from whatever it is we are too close to
/// </summary>
void AvoidObstacles ()
{
//look tolerance metres ahead
Vector3 normVel = Vector3.Normalize(m_vel);
Vector3 inFront = m_loc + Vector3.Multiply(normVel, m_model.Tolerance);
if( m_flowMap.WouldHitObstacle( m_loc, inFront ) ) {
AdjustVelocityToAvoidObstacles ();
}
}
void AdjustVelocityToAvoidObstacles ()
{ // ok so we worked our where we want to go, so ...
for( int i = 1; i < 5; i++ ) { UpdatePositionInScene ();
Vector3 normVel = Vector3.Normalize(m_vel);
int xDelta = m_rndnums.Next (-i, i); }
int yDelta = m_rndnums.Next (-i, i);
int zDelta = m_rndnums.Next (-i, i); /// <summary>
normVel.X += xDelta; /// Move within our flock
normVel.Y += yDelta; ///
normVel.Z += zDelta; /// We accumulate a new acceleration each time based on three rules
Vector3 inFront = m_loc + Vector3.Multiply(normVel, m_model.Tolerance); /// these are:
if( !m_flowMap.WouldHitObstacle( m_loc, inFront ) ) { /// our separation from our closest neighbours,
m_vel.X += xDelta; /// our desire to keep travelling within the local flock,
m_vel.Y += yDelta; /// our desire to move towards the flock centre
m_vel.Z += zDelta; ///
//m_log.Info("avoided"); /// </summary>
return; void Flock (List<Bird> birds)
} {
}
//m_log.Info("didn't avoid"); // calc the force vectors on this bird
// try increaing our acceleration Vector3 sep = Separate (birds); // Separation
// or try decreasing our acceleration Vector3 ali = Align (birds); // Alignment
// or turn around - coz where we came from was OK Vector3 coh = Cohesion (birds); // Cohesion
if (m_loc.X < m_regionBorder || m_loc.X > m_regionX - m_regionBorder)
m_vel.X = -m_vel.X; // Arbitrarily weight these forces
//TODO: expose these consts
sep *= 1.5f; //.mult(1.5);
//ali.mult(1.0);
ali *= 1.0f;
//coh.mult(1.0);
coh *= 1.0f;
// Add the force vectors to the current acceleration of the bird
//acc.add(sep);
m_acc += sep;
//acc.add(ali);
m_acc += ali;
//acc.add(coh);
m_acc += coh;
}
/// <summary>
/// Method to update our location within the scene.
/// update our location in the world based on our
/// current location, velocity and acceleration
/// taking into account our max speed
///
/// </summary>
void UpdatePositionInScene ()
{
// Update velocity
//vel.add(acc);
m_vel += m_acc;
// Limit speed
//m_vel.limit(maxspeed);
m_vel = BirdsUtil.Limit (m_vel, m_model.MaxSpeed);
m_loc += m_vel;
// Reset accelertion to 0 each cycle
m_acc *= 0.0f;
}
/// <summary>
/// Seek the specified target. Move into that flock
/// Accelerate us towards where we want to go
/// </summary>
/// <param name='target'>
/// Target. the position within the flock we would like to achieve
/// </param>
void Seek (Vector3 target)
{
m_acc += Steer (target, false);
}
/// <summary>
/// Arrive the specified target. Slow us down, as we are almost there
/// </summary>
/// <param name='target'>
/// Target. the flock we would like to think ourselves part of
/// </param>
void arrive (Vector3 target)
{
m_acc += Steer (target, true);
}
/// A method that calculates a steering vector towards a target
/// Takes a second argument, if true, it slows down as it approaches the target
Vector3 Steer (Vector3 target, bool slowdown)
{
Vector3 steer; // The steering vector
Vector3 desired = Vector3.Subtract(target, m_loc); // A vector pointing from the location to the target
float d = desired.Length (); // Distance from the target is the magnitude of the vector
// If the distance is greater than 0, calc steering (otherwise return zero vector)
if (d > 0) {
// Normalize desired
desired.Normalize ();
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
if ((slowdown) && (d < 100.0f)) {
desired *= (m_model.MaxSpeed * (d / 100.0f)); // This damping is somewhat arbitrary
} else {
desired *= m_model.MaxSpeed;
}
// Steering = Desired minus Velocity
//steer = target.sub(desired,m_vel);
steer = Vector3.Subtract (desired, m_vel);
//steer.limit(maxforce); // Limit to maximum steering force
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
} else {
steer = Vector3.Zero;
}
return steer;
}
/// <summary>
/// Borders this instance.
/// if we get too close wrap us around
/// CHANGE THIS to navigate away from whatever it is we are too close to
/// </summary>
void AvoidObstacles ()
{
//look tolerance metres ahead
Vector3 normVel = Vector3.Normalize(m_vel);
Vector3 inFront = m_loc + Vector3.Multiply(normVel, m_model.Tolerance);
if( m_flowMap.WouldHitObstacle( m_loc, inFront ) ) {
AdjustVelocityToAvoidObstacles ();
}
}
void AdjustVelocityToAvoidObstacles ()
{
for( int i = 1; i < 5; i++ ) {
Vector3 normVel = Vector3.Normalize(m_vel);
int xDelta = m_rndnums.Next (-i, i);
int yDelta = m_rndnums.Next (-i, i);
int zDelta = m_rndnums.Next (-i, i);
normVel.X += xDelta;
normVel.Y += yDelta;
normVel.Z += zDelta;
Vector3 inFront = m_loc + Vector3.Multiply(normVel, m_model.Tolerance);
if( !m_flowMap.WouldHitObstacle( m_loc, inFront ) ) {
m_vel.X += xDelta;
m_vel.Y += yDelta;
m_vel.Z += zDelta;
//m_log.Info("avoided");
return;
}
}
//m_log.Info("didn't avoid");
// try increaing our acceleration
// or try decreasing our acceleration
// or turn around - coz where we came from was OK
if (m_loc.X < m_regionBorder || m_loc.X > m_regionX - m_regionBorder)
m_vel.X = -m_vel.X;
if (m_loc.Y < m_regionBorder || m_loc.Y > m_regionY - m_regionBorder) if (m_loc.Y < m_regionBorder || m_loc.Y > m_regionY - m_regionBorder)
m_vel.Y = -m_vel.Y; m_vel.Y = -m_vel.Y;
if (m_loc.Z < 21 || m_loc.Z > m_regionZ ) if (m_loc.Z < 21 || m_loc.Z > m_regionZ )
m_vel.Z = -m_vel.Z; m_vel.Z = -m_vel.Z;
} }
/// <summary>
/// Separate ourselves from the specified birds.
/// keeps us a respectable distance from our closest neighbours whilst still
/// being part of our local flock
/// </summary>
/// <param name='birds'>
/// Birds. all the birds in the scene
/// </param>
Vector3 Separate (List<Bird> birds)
{
Vector3 steer = new Vector3 (0, 0, 0);
int count = 0;
// For every bird in the system, check if it's too close
foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < m_model.DesiredSeparation)) {
// Calculate vector pointing away from neighbor
Vector3 diff = Vector3.Subtract (m_loc, other.Location);
diff.Normalize ();
diff = Vector3.Divide (diff, d);
steer = Vector3.Add (steer, diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
steer /= (float)count;
}
// As long as the vector is greater than 0 /// <summary>
if (steer.Length () > 0) { /// Separate ourselves from the specified birds.
// Implement Reynolds: Steering = Desired - Velocity /// keeps us a respectable distance from our closest neighbours whilst still
steer.Normalize (); /// being part of our local flock
steer *= m_model.MaxSpeed; /// </summary>
steer -= m_vel; /// <param name='birds'>
//steer.limit(maxforce); /// Birds. all the birds in the scene
steer = BirdsUtil.Limit (steer, m_model.MaxForce); /// </param>
} Vector3 Separate (List<Bird> birds)
return steer; {
} Vector3 steer = new Vector3 (0, 0, 0);
int count = 0;
// For every bird in the system, check if it's too close
foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < m_model.DesiredSeparation)) {
// Calculate vector pointing away from neighbor
Vector3 diff = Vector3.Subtract (m_loc, other.Location);
diff.Normalize ();
diff = Vector3.Divide (diff, d);
steer = Vector3.Add (steer, diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
steer /= (float)count;
}
/// <summary> // As long as the vector is greater than 0
/// Align our bird within the flock. if (steer.Length () > 0) {
/// For every nearby bird in the system, calculate the average velocity // Implement Reynolds: Steering = Desired - Velocity
/// and move us towards that - this keeps us moving with the flock. steer.Normalize ();
/// </summary> steer *= m_model.MaxSpeed;
/// <param name='birds'> steer -= m_vel;
/// Birds. all the birds in the scene - we only really care about those in the neighbourdist //steer.limit(maxforce);
/// </param> steer = BirdsUtil.Limit (steer, m_model.MaxForce);
Vector3 Align (List<Bird> birds) }
{ return steer;
Vector3 steer = new Vector3 (0, 0, 0); }
int count = 0;
foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
if ((d > 0) && (d < m_model.NeighbourDistance)) {
steer += other.Velocity;
count++;
}
}
if (count > 0) {
steer /= (float)count;
}
// As long as the vector is greater than 0 /// <summary>
if (steer.Length () > 0) { /// Align our bird within the flock.
// Implement Reynolds: Steering = Desired - Velocity /// For every nearby bird in the system, calculate the average velocity
steer.Normalize (); /// and move us towards that - this keeps us moving with the flock.
steer *= m_model.MaxSpeed; /// </summary>
steer -= m_vel; /// <param name='birds'>
//steer.limit(maxforce); /// Birds. all the birds in the scene - we only really care about those in the neighbourdist
steer = BirdsUtil.Limit (steer, m_model.MaxForce); /// </param>
Vector3 Align (List<Bird> birds)
} {
return steer; Vector3 steer = new Vector3 (0, 0, 0);
} int count = 0;
foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
if ((d > 0) && (d < m_model.NeighbourDistance)) {
steer += other.Velocity;
count++;
}
}
if (count > 0) {
steer /= (float)count;
}
/// <summary> // As long as the vector is greater than 0
/// MAintain the cohesion of our local flock if (steer.Length () > 0) {
/// For the average location (i.e. center) of all nearby birds, calculate our steering vector towards that location // Implement Reynolds: Steering = Desired - Velocity
/// </summary> steer.Normalize ();
/// <param name='birds'> steer *= m_model.MaxSpeed;
/// Birds. the birds in the scene steer -= m_vel;
/// </param> //steer.limit(maxforce);
Vector3 Cohesion (List<Bird> birds) steer = BirdsUtil.Limit (steer, m_model.MaxForce);
{
}
Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations return steer;
int count = 0; }
foreach (Bird other in birds) { /// <summary>
float d = Vector3.Distance (m_loc, other.Location); /// MAintain the cohesion of our local flock
if ((d > 0) && (d < m_model.NeighbourDistance)) { /// For the average location (i.e. center) of all nearby birds, calculate our steering vector towards that location
sum += other.Location; // Add location /// </summary>
count++; /// <param name='birds'>
} /// Birds. the birds in the scene
} /// </param>
if (count > 0) { Vector3 Cohesion (List<Bird> birds)
sum /= (float)count; {
return Steer (sum, false); // Steer towards the location
} Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations
return sum; int count = 0;
}
} foreach (Bird other in birds) {
float d = Vector3.Distance (m_loc, other.Location);
if ((d > 0) && (d < m_model.NeighbourDistance)) {
sum += other.Location; // Add location
count++;
}
}
if (count > 0) {
sum /= (float)count;
return Steer (sum, false); // Steer towards the location
}
return sum;
}
}
} }

View File

@ -30,23 +30,23 @@ using OpenMetaverse;
namespace Flocking namespace Flocking
{ {
public class BirdsUtil public class BirdsUtil
{ {
public static Vector3 Limit (Vector3 initial, float maxLen) public static Vector3 Limit (Vector3 initial, float maxLen)
{ {
float currLen = initial.Length (); float currLen = initial.Length ();
float ratio = 1.0f; float ratio = 1.0f;
if (currLen > maxLen) {
ratio = currLen / maxLen;
}
return initial /= ratio;
}
} if (currLen > maxLen) {
ratio = currLen / maxLen;
}
return initial /= ratio;
}
}
} }

View File

@ -31,105 +31,105 @@ using OpenMetaverse;
namespace Flocking namespace Flocking
{ {
public class FlockingModel public class FlockingModel
{ {
private List<Bird> m_flock = new List<Bird>(); private List<Bird> m_flock = new List<Bird>();
private FlowMap m_flowMap; private FlowMap m_flowMap;
private float m_maxSpeed; private float m_maxSpeed;
private float m_maxForce; private float m_maxForce;
private float m_neighbourDistance; private float m_neighbourDistance;
private float m_desiredSeparation; private float m_desiredSeparation;
private float m_tolerance; private float m_tolerance;
private float m_border; private float m_border;
private string m_name; private string m_name;
private Random m_rnd = new Random(Environment.TickCount); private Random m_rnd = new Random(Environment.TickCount);
public int Size { public int Size {
get {return m_flock.Count;} get {return m_flock.Count;}
set { set {
//if( value < m_flock.Count ) { //if( value < m_flock.Count ) {
// m_flock.RemoveRange( 0, m_flock.Count - value ); // m_flock.RemoveRange( 0, m_flock.Count - value );
//} else //} else
m_flock = new List<Bird>(); m_flock = new List<Bird>();
while( value > m_flock.Count ) { while( value > m_flock.Count ) {
AddBird(m_name + m_flock.Count); AddBird(m_name + m_flock.Count);
} }
} }
} }
public FlockingModel(string moduleName, float maxSpeed, float maxForce, float neighbourDistance, float desiredSeparation, float tolerance, float border) { public FlockingModel(string moduleName, float maxSpeed, float maxForce, float neighbourDistance, float desiredSeparation, float tolerance, float border) {
m_name = moduleName; m_name = moduleName;
m_maxSpeed = maxSpeed; m_maxSpeed = maxSpeed;
m_maxForce = maxForce; m_maxForce = maxForce;
m_neighbourDistance = neighbourDistance; m_neighbourDistance = neighbourDistance;
m_desiredSeparation = desiredSeparation; m_desiredSeparation = desiredSeparation;
m_tolerance = tolerance; m_tolerance = tolerance;
m_border = border; m_border = border;
} }
void AddBird (string name) void AddBird (string name)
{ {
Bird Bird = new Bird (name, this, m_flowMap); Bird Bird = new Bird (name, this, m_flowMap);
// find an initial random location for this Bird // find an initial random location for this Bird
// somewhere not within an obstacle // somewhere not within an obstacle
int xInit = m_rnd.Next(m_flowMap.LengthX); int xInit = m_rnd.Next(m_flowMap.LengthX);
int yInit = m_rnd.Next(m_flowMap.LengthY); int yInit = m_rnd.Next(m_flowMap.LengthY);
int zInit = m_rnd.Next(m_flowMap.LengthZ); int zInit = m_rnd.Next(m_flowMap.LengthZ);
while( m_flowMap.IsWithinObstacle( xInit, yInit, zInit ) ){ while( m_flowMap.IsWithinObstacle( xInit, yInit, zInit ) ){
xInit = m_rnd.Next(m_flowMap.LengthX); xInit = m_rnd.Next(m_flowMap.LengthX);
yInit = m_rnd.Next(m_flowMap.LengthY); yInit = m_rnd.Next(m_flowMap.LengthY);
zInit = m_rnd.Next(m_flowMap.LengthZ); zInit = m_rnd.Next(m_flowMap.LengthZ);
} }
Bird.Location = new Vector3 (Convert.ToSingle(xInit), Convert.ToSingle(yInit), Convert.ToSingle(zInit)); Bird.Location = new Vector3 (Convert.ToSingle(xInit), Convert.ToSingle(yInit), Convert.ToSingle(zInit));
m_flock.Add (Bird); m_flock.Add (Bird);
} }
public float MaxSpeed { public float MaxSpeed {
get {return m_maxSpeed;} get {return m_maxSpeed;}
set { m_maxSpeed = value; } set { m_maxSpeed = value; }
} }
public float MaxForce { public float MaxForce {
get {return m_maxForce;} get {return m_maxForce;}
set { m_maxForce = value; } set { m_maxForce = value; }
} }
public float NeighbourDistance { public float NeighbourDistance {
get {return m_neighbourDistance;} get {return m_neighbourDistance;}
set { m_neighbourDistance = value; } set { m_neighbourDistance = value; }
} }
public float DesiredSeparation { public float DesiredSeparation {
get {return m_desiredSeparation;} get {return m_desiredSeparation;}
set { m_desiredSeparation = value; } set { m_desiredSeparation = value; }
} }
public float Tolerance { public float Tolerance {
get {return m_tolerance;} get {return m_tolerance;}
set { m_tolerance = value; } set { m_tolerance = value; }
} }
public void Initialise (int num, FlowMap flowMap)
{
m_flowMap = flowMap;
for (int i = 0; i < num; i++) {
AddBird (m_name + i );
}
}
public List<Bird> UpdateFlockPos () public void Initialise (int num, FlowMap flowMap)
{ {
foreach (Bird b in m_flock) { m_flowMap = flowMap;
b.MoveInSceneRelativeToFlock(m_flock); // Passing the entire list of Birds to each Bird individually for (int i = 0; i < num; i++) {
} AddBird (m_name + i );
}
return m_flock; }
}
} public List<Bird> UpdateFlockPos ()
{
foreach (Bird b in m_flock) {
b.MoveInSceneRelativeToFlock(m_flock); // Passing the entire list of Birds to each Bird individually
}
return m_flock;
}
}
} }

View File

@ -43,8 +43,10 @@ using OpenSim.Framework.Console;
using Mono.Addins; using Mono.Addins;
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
[assembly: Addin("OpenSimBirds", "0.2")] [assembly: Addin("OpenSimBirds", OpenSim.VersionInfo.VersionNumber + "0.3")]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] [assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
[assembly: AddinDescription("OpenSimBirds module.")]
[assembly: AddinAuthor("Jak Daniels")]
namespace Flocking namespace Flocking
{ {
@ -58,31 +60,31 @@ namespace Flocking
private string m_regionConfigDir = ""; private string m_regionConfigDir = "";
private Scene m_scene; private Scene m_scene;
private ICommandConsole m_console; private ICommandConsole m_console;
private FlockingModel m_model; private FlockingModel m_model;
private FlockingView m_view; private FlockingView m_view;
private bool m_startup = true; private bool m_enabled = false;
private bool m_enabled = false; private bool m_startup = false;
private bool m_ready = false; private bool m_ready = false;
private uint m_frame = 0; private uint m_frame = 0;
private int m_frameUpdateRate = 1; private int m_frameUpdateRate = 1;
private int m_chatChannel = 118; private int m_chatChannel = 118;
private string m_birdPrim; private string m_birdPrim;
private int m_flockSize = 50; private int m_flockSize = 50;
private int m_maxFlockSize = 100; private int m_maxFlockSize = 100;
private float m_maxSpeed; private float m_maxSpeed;
private float m_maxForce; private float m_maxForce;
private float m_neighbourDistance; private float m_neighbourDistance;
private float m_desiredSeparation; private float m_desiredSeparation;
private float m_tolerance; private float m_tolerance;
private float m_borderSize; private float m_borderSize;
private int m_maxHeight; private int m_maxHeight;
private Vector3 m_shoutPos = new Vector3(128f, 128f, 30f); private Vector3 m_shoutPos = new Vector3(128f, 128f, 30f);
static object m_sync = new object(); static object m_sync = new object();
private List<UUID> m_allowedControllers = new List<UUID>(); private List<UUID> m_allowedControllers = new List<UUID>();
public IConfigSource m_config; public IConfigSource m_config;
private UUID m_owner; private UUID m_owner;
#endregion #endregion
#region IRegionModuleBase implementation #region IRegionModuleBase implementation
@ -92,28 +94,28 @@ namespace Flocking
public bool IsSharedModule { get { return false; } } public bool IsSharedModule { get { return false; } }
public void Initialise (IConfigSource source) public void Initialise (IConfigSource source)
{ {
m_config = source; m_config = source;
} }
public void AddRegion (Scene scene) public void AddRegion (Scene scene)
{ {
IConfig cnf; IConfig cnf;
m_log.InfoFormat("[{0}]: Adding region {1} to this module", m_name, scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Adding region {1} to this module", m_name, scene.RegionInfo.RegionName);
cnf = m_config.Configs["Startup"]; cnf = m_config.Configs["Startup"];
m_regionConfigDir = cnf.GetString("regionload_regionsdir", Path.Combine(Util.configDir(), "bin/Regions/")); m_regionConfigDir = cnf.GetString("regionload_regionsdir", Path.Combine(Util.configDir(), "bin/Regions/"));
cnf = m_config.Configs[scene.RegionInfo.RegionName]; cnf = m_config.Configs[scene.RegionInfo.RegionName];
if (cnf == null) if (cnf == null)
{ {
m_log.InfoFormat("[{0}]: No region section [{1}] found in addon-modules/{2}/config/*.ini configuration files.", m_name, scene.RegionInfo.RegionName, m_name); m_log.InfoFormat("[{0}]: No region section [{1}] found in {2} configuration file.", m_name, scene.RegionInfo.RegionName, m_regionConfigDir);
//string moduleConfigFile = Path.Combine(Util.configDir(),m_name + ".ini"); //string moduleConfigFile = Path.Combine(Util.configDir(),m_name + ".ini");
string moduleConfigFile = Path.Combine(m_regionConfigDir, "Regions.ini"); string moduleConfigFile = Path.Combine(m_regionConfigDir, "Regions.ini");
try try
{ {
m_log.InfoFormat("[{0}]: Checking {1} for [{2}] section containing valid config keys", m_name, moduleConfigFile, scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Checking {1} for [{2}] section", m_name, moduleConfigFile, scene.RegionInfo.RegionName);
m_config = new IniConfigSource(moduleConfigFile); m_config = new IniConfigSource(moduleConfigFile);
cnf = m_config.Configs[scene.RegionInfo.RegionName]; cnf = m_config.Configs[scene.RegionInfo.RegionName];
} }
@ -124,18 +126,23 @@ namespace Flocking
if (cnf == null) if (cnf == null)
{ {
m_log.InfoFormat("[{0}]: No region section [{1}] found in configuration {2}. Birds in this region are set to Disabled", m_name, scene.RegionInfo.RegionName, moduleConfigFile); m_log.InfoFormat("[{0}]: No region section [{1}] found in configuration {2}.", m_name, scene.RegionInfo.RegionName, moduleConfigFile);
m_enabled = false;
return; cnf = m_config.Configs[scene.RegionInfo.RegionName];
if (cnf == null)
{
m_log.InfoFormat("[{0}]: No region section [{1}] found in main configuration. Module is disabled.", m_name, scene.RegionInfo.RegionName);
return;
}
} }
} }
m_startup = cnf.GetBoolean("BirdsModuleStartup", true); m_enabled = cnf.GetBoolean("BirdsModuleEnabled", false);
if (m_startup) if (m_enabled)
{ {
m_scene = scene; m_scene = scene;
m_enabled = cnf.GetBoolean("BirdsEnabled", false); m_startup = cnf.GetBoolean("BirdsShowOnStartup", false);
m_chatChannel = cnf.GetInt("BirdsChatChannel", 118); m_chatChannel = cnf.GetInt("BirdsChatChannel", 118);
m_birdPrim = cnf.GetString("BirdsPrim", "birdPrim"); m_birdPrim = cnf.GetString("BirdsPrim", "birdPrim");
m_flockSize = cnf.GetInt("BirdsFlockSize", 20); m_flockSize = cnf.GetInt("BirdsFlockSize", 20);
@ -185,7 +192,7 @@ namespace Flocking
m_log.InfoFormat("[{0}] No command security was defined in the config. Any user may possibly configure this module from a script!", m_name); m_log.InfoFormat("[{0}] No command security was defined in the config. Any user may possibly configure this module from a script!", m_name);
} }
m_log.InfoFormat("[{0}] Module is {1} listening for commands on channel {2} with Flock Size {3}", m_name, m_enabled ? "enabled and" : "disabled, but still", m_chatChannel, m_flockSize); m_log.InfoFormat("[{0}] Module is {1} listening for commands on channel {2} with Flock Size {3}", m_name, m_startup ? "enabled and" : "disabled, but still", m_chatChannel, m_flockSize);
m_console = MainConsole.Instance; m_console = MainConsole.Instance;
//register commands with the scene //register commands with the scene
@ -205,19 +212,18 @@ namespace Flocking
m_shoutPos = new Vector3(scene.RegionInfo.RegionSizeX / 2f, scene.RegionInfo.RegionSizeY / 2f, 30f); m_shoutPos = new Vector3(scene.RegionInfo.RegionSizeX / 2f, scene.RegionInfo.RegionSizeY / 2f, 30f);
FlockInitialise(); FlockInitialise();
} }
else m_log.InfoFormat("[{0}] Module is disabled in Region {1}", m_name, scene.RegionInfo.RegionName); else m_log.InfoFormat("[{0}] Module is disabled in region {1}", m_name, scene.RegionInfo.RegionName);
} }
public void RegionLoaded (Scene scene) public void RegionLoaded (Scene scene)
{ {
if (m_startup) if (m_enabled)
{ {
// Mark Module Ready for duty // Mark Module Ready for duty
m_ready = true; m_ready = true;
} }
} }
public void PrimsLoaded(Scene scene) public void PrimsLoaded(Scene scene)
{ {
@ -225,22 +231,22 @@ namespace Flocking
ClearPersisted(); ClearPersisted();
} }
public void RemoveRegion (Scene scene) public void RemoveRegion (Scene scene)
{ {
m_log.InfoFormat("[{0}]: Removing region {1} from this module", m_name, scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Removing region {1} from this module", m_name, scene.RegionInfo.RegionName);
if (m_startup) { if (m_enabled) {
m_view.Clear(); m_view.Clear();
scene.EventManager.OnFrame -= FlockUpdate; scene.EventManager.OnFrame -= FlockUpdate;
scene.EventManager.OnChatFromClient -= SimChatSent; scene.EventManager.OnChatFromClient -= SimChatSent;
scene.EventManager.OnChatFromWorld -= SimChatSent; scene.EventManager.OnChatFromWorld -= SimChatSent;
scene.EventManager.OnPrimsLoaded -= PrimsLoaded; scene.EventManager.OnPrimsLoaded -= PrimsLoaded;
m_ready = false; m_ready = false;
} }
} }
public void Close() public void Close()
{ {
if (m_startup) if (m_enabled)
{ {
m_scene.EventManager.OnFrame -= FlockUpdate; m_scene.EventManager.OnFrame -= FlockUpdate;
m_scene.EventManager.OnChatFromClient -= SimChatSent; m_scene.EventManager.OnChatFromClient -= SimChatSent;
@ -250,7 +256,7 @@ namespace Flocking
} }
} }
#endregion #endregion
#region Helpers #region Helpers
@ -292,25 +298,25 @@ namespace Flocking
#region EventHandlers #region EventHandlers
public void FlockUpdate () public void FlockUpdate ()
{ {
if (!m_ready || !m_enabled || ((m_frame++ % m_frameUpdateRate) != 0)) if (!m_ready || !m_startup || ((m_frame++ % m_frameUpdateRate) != 0))
{ {
return; return;
} }
//m_log.InfoFormat("update my birds"); //m_log.InfoFormat("update my birds");
// work out where everyone has moved to // work out where everyone has moved to
// and tell the scene to render the new positions // and tell the scene to render the new positions
lock( m_sync ) { lock( m_sync ) {
List<Bird > birds = m_model.UpdateFlockPos (); List<Bird > birds = m_model.UpdateFlockPos ();
m_view.Render (birds); m_view.Render (birds);
} }
} }
protected void SimChatSent (Object x, OSChatMessage msg) protected void SimChatSent (Object x, OSChatMessage msg)
{ {
if (msg.Channel != m_chatChannel) return; // not for us if (msg.Channel != m_chatChannel) return; // not for us
if (m_allowedControllers.Count > 0) if (m_allowedControllers.Count > 0)
{ {
bool reject = true; bool reject = true;
@ -325,27 +331,27 @@ namespace Flocking
if (reject) return; //not for us if (reject) return; //not for us
} }
// try and parse a valid cmd from this msg // try and parse a valid cmd from this msg
string cmd = msg.Message; //.ToLower (); string cmd = msg.Message; //.ToLower ();
//stick ui in the args so we know to respond in world //stick ui in the args so we know to respond in world
//bit of a hack - but lets us use CommandDelegate inWorld //bit of a hack - but lets us use CommandDelegate inWorld
string[] args = (cmd + " <ui>").Split (" ".ToCharArray ()); string[] args = (cmd + " <ui>").Split (" ".ToCharArray ());
if (cmd.StartsWith ("stop")) { if (cmd.StartsWith ("stop")) {
HandleStopCmd (m_name, args); HandleStopCmd (m_name, args);
} else if (cmd.StartsWith ("start")) { } else if (cmd.StartsWith ("start")) {
HandleStartCmd (m_name, args); HandleStartCmd (m_name, args);
} else if (cmd.StartsWith("enable")) { } else if (cmd.StartsWith("enable")) {
HandleEnableCmd(m_name, args); HandleEnableCmd(m_name, args);
} else if (cmd.StartsWith("disable")) { } else if (cmd.StartsWith("disable")) {
HandleDisableCmd(m_name, args); HandleDisableCmd(m_name, args);
} else if (cmd.StartsWith ("size")) { } else if (cmd.StartsWith ("size")) {
HandleSetSizeCmd (m_name, args); HandleSetSizeCmd (m_name, args);
} else if (cmd.StartsWith ("stats")) { } else if (cmd.StartsWith ("stats")) {
HandleShowStatsCmd (m_name, args); HandleShowStatsCmd (m_name, args);
} else if (cmd.StartsWith ("prim")) { } else if (cmd.StartsWith ("prim")) {
HandleSetPrimCmd (m_name, args); HandleSetPrimCmd (m_name, args);
} else if (cmd.StartsWith("speed")) { } else if (cmd.StartsWith("speed")) {
HandleSetMaxSpeedCmd(m_name, args); HandleSetMaxSpeedCmd(m_name, args);
} else if (cmd.StartsWith("force")) { } else if (cmd.StartsWith("force")) {
@ -356,36 +362,35 @@ namespace Flocking
HandleSetDesiredSeparationCmd(m_name, args); HandleSetDesiredSeparationCmd(m_name, args);
} else if (cmd.StartsWith("tolerance")) { } else if (cmd.StartsWith("tolerance")) {
HandleSetToleranceCmd(m_name, args); HandleSetToleranceCmd(m_name, args);
} else if (cmd.StartsWith ("framerate")) { } else if (cmd.StartsWith ("framerate")) {
HandleSetFrameRateCmd (m_name, args); HandleSetFrameRateCmd (m_name, args);
} }
}
}
#endregion #endregion
#region Command Handling #region Command Handling
private void AddCommand (string cmd, string args, string help, CommandDelegate fn) private void AddCommand (string cmd, string args, string help, CommandDelegate fn)
{ {
string argStr = ""; string argStr = "";
if (args.Trim ().Length > 0) { if (args.Trim ().Length > 0) {
argStr = " <" + args + "> "; argStr = " <" + args + "> ";
} }
m_log.InfoFormat("[{0}]: Adding console command {1} - {2} to region {3}", m_name, "birds-" + cmd + argStr, help, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Adding console command {1} - {2} to region {3}", m_name, "birds-" + cmd + argStr, help, m_scene.RegionInfo.RegionName);
//m_scene.AddCommand (this, "birds-" + cmd, "birds-" + cmd + argStr, help, fn); //m_scene.AddCommand (this, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
m_console.Commands.AddCommand(m_name, false, "birds-" + cmd, "birds-" + cmd + argStr, help, fn); m_console.Commands.AddCommand(m_name, false, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
} }
private void RegisterCommands () private void RegisterCommands ()
{ {
AddCommand ("stop", "", "Stop Birds Flocking", HandleStopCmd); AddCommand("stop", "", "Stop Birds Flocking", HandleStopCmd);
AddCommand ("start", "", "Start Birds Flocking", HandleStartCmd); AddCommand("start", "", "Start Birds Flocking", HandleStartCmd);
AddCommand ("enable", "", "Enable Birds Flocking", HandleEnableCmd); AddCommand("enable", "", "Enable Birds Flocking", HandleEnableCmd);
AddCommand ("disable", "", "Disable Birds Flocking", HandleDisableCmd); AddCommand("disable", "", "Disable Birds Flocking", HandleDisableCmd);
AddCommand ("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd); AddCommand("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd);
AddCommand ("prim", "name", "Set the prim used for each bird to that passed in", HandleSetPrimCmd); AddCommand("prim", "name", "Set the prim used for each bird to that passed in", HandleSetPrimCmd);
AddCommand ("speed", "num", "Set the maximum velocity each bird may achieve", HandleSetMaxSpeedCmd); AddCommand("speed", "num", "Set the maximum velocity each bird may achieve", HandleSetMaxSpeedCmd);
AddCommand("force", "num", "Set the maximum force each bird may accelerate", HandleSetMaxForceCmd); AddCommand("force", "num", "Set the maximum force each bird may accelerate", HandleSetMaxForceCmd);
AddCommand("distance", "num", "Set the maximum distance that other birds are to be considered in the same flock as us", HandleSetNeighbourDistanceCmd); AddCommand("distance", "num", "Set the maximum distance that other birds are to be considered in the same flock as us", HandleSetNeighbourDistanceCmd);
AddCommand("separation", "num", "How far away from other birds we would like to stay", HandleSetDesiredSeparationCmd); AddCommand("separation", "num", "How far away from other birds we would like to stay", HandleSetDesiredSeparationCmd);
@ -400,7 +405,6 @@ namespace Flocking
if (comms != null) if (comms != null)
{ {
comms.RegisterScriptInvocation(this, "birdsGetStats"); comms.RegisterScriptInvocation(this, "birdsGetStats");
} }
} }
@ -408,36 +412,37 @@ namespace Flocking
{ {
return ""; //currently a placeholder return ""; //currently a placeholder
} }
private bool ShouldHandleCmd () private bool ShouldHandleCmd ()
{ {
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) { if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) {
return false; return false;
} }
return true; return true;
} }
private bool IsInWorldCmd (ref string [] args) private bool IsInWorldCmd (ref string [] args)
{ {
if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) { if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) {
m_log.InfoFormat("[{0}]: Inworld command detected in region {1}", m_name, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Inworld command detected in region {1}", m_name, m_scene.RegionInfo.RegionName);
return true; return true;
} }
return false; return false;
} }
private void ShowResponse (string response, bool inWorld) private void ShowResponse (string response, bool inWorld)
{ {
if (inWorld) { if (inWorld) {
//IClientAPI ownerAPI = null; //IClientAPI ownerAPI = null;
//if (m_scene.TryGetClient (m_owner, out ownerAPI)) { //if (m_scene.TryGetClient (m_owner, out ownerAPI)) {
// ownerAPI.SendBlueBoxMessage (m_owner, "Birds", response); // ownerAPI.SendBlueBoxMessage (m_owner, "Birds", response);
//} //}
SendSimChat(response, m_chatChannel); SendSimChat(response, m_chatChannel);
} else { } else {
MainConsole.Instance.Output (response); if (m_ready)
} MainConsole.Instance.Output (response);
} }
}
private void SendSimChat(string msg, int channel) private void SendSimChat(string msg, int channel)
{ {
@ -448,7 +453,7 @@ namespace Flocking
{ {
if (m_ready && ShouldHandleCmd ()) { if (m_ready && ShouldHandleCmd ()) {
m_log.InfoFormat("[{0}]: Bird flocking is disabled in region {1}.", m_name, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Bird flocking is disabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = false; m_startup = false;
//m_ready = false; //m_ready = false;
m_view.Clear(); m_view.Clear();
} }
@ -459,60 +464,59 @@ namespace Flocking
if (m_ready && ShouldHandleCmd()) if (m_ready && ShouldHandleCmd())
{ {
m_log.InfoFormat("[{0}]: Bird flocking is enabled in region {1}.", m_name, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Bird flocking is enabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = true; m_startup = true;
//m_ready = true; //m_ready = true;
} }
} }
public void HandleStopCmd (string module, string[] args) public void HandleStopCmd (string module, string[] args)
{ {
if (m_enabled && m_ready && ShouldHandleCmd()) if (m_startup && m_ready && ShouldHandleCmd())
{ {
m_log.InfoFormat("[{0}]: Bird flocking is stopped in region {1}.", m_name, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Bird flocking is stopped in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = false; m_startup = false;
} }
} }
public void HandleStartCmd(string module, string[] args) public void HandleStartCmd(string module, string[] args)
{ {
if (!m_enabled && m_ready && ShouldHandleCmd()) if (!m_startup && m_ready && ShouldHandleCmd())
{ {
m_log.InfoFormat("[{0}]: Bird flocking is started in region {1}.", m_name, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Bird flocking is started in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = true; m_startup = true;
FlockUpdate(); FlockUpdate();
} }
} }
void HandleSetFrameRateCmd (string module, string[] args) void HandleSetFrameRateCmd (string module, string[] args)
{ {
if (m_ready && ShouldHandleCmd()) if (m_ready && ShouldHandleCmd())
{ {
int frameRate = Convert.ToInt32( args[1] ); int frameRate = Convert.ToInt32( args[1] );
m_frameUpdateRate = frameRate; m_frameUpdateRate = frameRate;
m_log.InfoFormat("[{0}]: Bird updates set to every {1} frames in region {2}.", m_name, frameRate, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Bird updates set to every {1} frames in region {2}.", m_name, frameRate, m_scene.RegionInfo.RegionName);
} }
} }
public void HandleSetSizeCmd (string module, string[] args) public void HandleSetSizeCmd (string module, string[] args)
{ {
if (m_ready && ShouldHandleCmd()) if (m_ready && ShouldHandleCmd())
{ {
lock( m_sync ) { lock( m_sync ) {
int newSize = Convert.ToInt32(args[1]); int newSize = Convert.ToInt32(args[1]);
if (newSize > m_maxFlockSize) newSize = m_maxFlockSize; if (newSize > m_maxFlockSize) newSize = m_maxFlockSize;
m_view.Clear(); m_view.Clear();
m_model.Size = newSize; m_model.Size = newSize;
m_log.InfoFormat("[{0}]: Bird flock size is set to {1} in region {2}.", m_name, newSize, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Bird flock size is set to {1} in region {2}.", m_name, newSize, m_scene.RegionInfo.RegionName);
}
} }
} }
}
public void HandleShowStatsCmd (string module, string[] args)
public void HandleShowStatsCmd (string module, string[] args) {
{
if (m_ready && ShouldHandleCmd()) if (m_ready && ShouldHandleCmd())
{ {
bool inWorld = IsInWorldCmd (ref args); bool inWorld = IsInWorldCmd (ref args);
int i; int i;
int s=m_model.Size; int s=m_model.Size;
UUID primUuid; UUID primUuid;
@ -523,8 +527,8 @@ namespace Flocking
m_log.InfoFormat("[{0}]: Sending bird statistics to region {1}.", m_name, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Sending bird statistics to region {1}.", m_name, m_scene.RegionInfo.RegionName);
} }
ShowResponse("birds-started = " + (m_enabled ? "True" : "False"), inWorld); ShowResponse("birds-enabled = " + (m_enabled ? "True" : "False"), inWorld);
ShowResponse("birds-enabled = " + (m_ready ? "True" : "False"), inWorld); ShowResponse("birds-startup = " + (m_ready ? "True" : "False"), inWorld);
ShowResponse("birds-prim = " + m_view.BirdPrim, inWorld); ShowResponse("birds-prim = " + m_view.BirdPrim, inWorld);
ShowResponse("birds-framerate = " + m_frameUpdateRate, inWorld); ShowResponse("birds-framerate = " + m_frameUpdateRate, inWorld);
ShowResponse("birds-maxsize = " + m_maxFlockSize, inWorld); ShowResponse("birds-maxsize = " + m_maxFlockSize, inWorld);
@ -560,21 +564,21 @@ namespace Flocking
ShowResponse("birds-prim" + i + " = " + m_name + i + " : " + primUuid.ToString() + " : " + avUuids.Trim(), inWorld); ShowResponse("birds-prim" + i + " = " + m_name + i + " : " + primUuid.ToString() + " : " + avUuids.Trim(), inWorld);
} }
} }
} }
public void HandleSetPrimCmd (string module, string[] args) public void HandleSetPrimCmd (string module, string[] args)
{ {
if (m_ready && ShouldHandleCmd()) if (m_ready && ShouldHandleCmd())
{ {
string primName = args[1]; string primName = args[1];
lock(m_sync) { lock(m_sync) {
m_view.BirdPrim = primName; m_view.BirdPrim = primName;
m_log.InfoFormat("[{0}]: Bird prim is set to {1} in region {2}.", m_name, primName, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Bird prim is set to {1} in region {2}.", m_name, primName, m_scene.RegionInfo.RegionName);
m_view.Clear(); m_view.Clear();
} }
} }
} }
public void HandleSetMaxSpeedCmd(string module, string[] args) public void HandleSetMaxSpeedCmd(string module, string[] args)
{ {
@ -641,8 +645,6 @@ namespace Flocking
} }
} }
#endregion #endregion
}
}
} }

View File

@ -34,34 +34,34 @@ using log4net;
namespace Flocking namespace Flocking
{ {
public class FlockingView public class FlockingView
{ {
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene; private Scene m_scene;
private UUID m_owner; private UUID m_owner;
private String m_name; private String m_name;
private String m_birdPrim; private String m_birdPrim;
private Dictionary<string, SceneObjectGroup> m_sogMap = new Dictionary<string, SceneObjectGroup> ();
public FlockingView (String moduleName, Scene scene)
{
m_name = moduleName;
m_scene = scene;
}
public void PostInitialize (UUID owner)
{
m_owner = owner;
}
public String BirdPrim {
get { return m_birdPrim; }
set{ m_birdPrim = value;}
}
public void Clear () private Dictionary<string, SceneObjectGroup> m_sogMap = new Dictionary<string, SceneObjectGroup> ();
{
public FlockingView (String moduleName, Scene scene)
{
m_name = moduleName;
m_scene = scene;
}
public void PostInitialize (UUID owner)
{
m_owner = owner;
}
public String BirdPrim {
get { return m_birdPrim; }
set{ m_birdPrim = value;}
}
public void Clear ()
{
//trash everything we have //trash everything we have
foreach (string name in m_sogMap.Keys) foreach (string name in m_sogMap.Keys)
{ {
@ -71,103 +71,103 @@ namespace Flocking
} }
m_sogMap.Clear(); m_sogMap.Clear();
m_scene.ForceClientUpdate(); m_scene.ForceClientUpdate();
} }
public void Render(List<Bird> birds) public void Render(List<Bird> birds)
{ {
foreach (Bird bird in birds) { foreach (Bird bird in birds) {
DrawBird (bird); DrawBird (bird);
} }
} }
private void DrawBird (Bird bird) private void DrawBird (Bird bird)
{ {
SceneObjectPart existing = m_scene.GetSceneObjectPart (bird.Id); SceneObjectPart existing = m_scene.GetSceneObjectPart (bird.Id);
SceneObjectGroup sog; SceneObjectGroup sog;
SceneObjectPart rootPart; SceneObjectPart rootPart;
if (existing == null) { if (existing == null) {
m_log.InfoFormat("[{0}]: Adding prim {1} in region {2}", m_name, bird.Id, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Adding prim {1} in region {2}", m_name, bird.Id, m_scene.RegionInfo.RegionName);
SceneObjectGroup group = findByName (m_birdPrim); SceneObjectGroup group = findByName (m_birdPrim);
sog = CopyPrim (group, bird.Id); sog = CopyPrim (group, bird.Id);
rootPart = sog.RootPart; rootPart = sog.RootPart;
rootPart.AddFlag(PrimFlags.Temporary); rootPart.AddFlag(PrimFlags.Temporary);
rootPart.AddFlag(PrimFlags.Phantom); rootPart.AddFlag(PrimFlags.Phantom);
//set prim to phantom //set prim to phantom
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false); //sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
m_sogMap [bird.Id] = sog; m_sogMap [bird.Id] = sog;
m_scene.AddNewSceneObject (sog, false); m_scene.AddNewSceneObject (sog, false);
// Fire script on_rez // Fire script on_rez
sog.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 1); sog.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 1);
rootPart.ParentGroup.ResumeScripts(); rootPart.ParentGroup.ResumeScripts();
rootPart.ScheduleFullUpdate(); rootPart.ScheduleFullUpdate();
sog.DetachFromBackup(); sog.DetachFromBackup();
} else { } else {
sog = existing.ParentGroup; sog = existing.ParentGroup;
m_sogMap[bird.Id] = sog; m_sogMap[bird.Id] = sog;
//rootPart = sog.RootPart; //rootPart = sog.RootPart;
//set prim to phantom //set prim to phantom
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false); //sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
} }
Quaternion rotation = CalcRotationToEndpoint (sog, sog.AbsolutePosition, bird.Location);
sog.UpdateGroupRotationPR( bird.Location, rotation);
}
private static Quaternion CalcRotationToEndpoint (SceneObjectGroup copy, Vector3 sv, Vector3 ev)
{
//llSetRot(llRotBetween(<1,0,0>,llVecNorm(targetPosition - llGetPos())));
// bird wil fly x forwards and Z up
Vector3 currDirVec = Vector3.UnitX;
Vector3 desiredDirVec = Vector3.Subtract (ev, sv);
desiredDirVec.Normalize ();
Quaternion rot = Vector3.RotationBetween (currDirVec, desiredDirVec); Quaternion rotation = CalcRotationToEndpoint (sog, sog.AbsolutePosition, bird.Location);
return rot; sog.UpdateGroupRotationPR( bird.Location, rotation);
} }
private SceneObjectGroup CopyPrim (SceneObjectGroup prim, string name) private static Quaternion CalcRotationToEndpoint (SceneObjectGroup copy, Vector3 sv, Vector3 ev)
{ {
SceneObjectGroup copy = prim.Copy (true); //llSetRot(llRotBetween(<1,0,0>,llVecNorm(targetPosition - llGetPos())));
copy.Name = name; // bird wil fly x forwards and Z up
copy.DetachFromBackup ();
return copy; Vector3 currDirVec = Vector3.UnitX;
} Vector3 desiredDirVec = Vector3.Subtract (ev, sv);
desiredDirVec.Normalize ();
private SceneObjectGroup findByName (string name)
{ Quaternion rot = Vector3.RotationBetween (currDirVec, desiredDirVec);
SceneObjectGroup retVal = null; return rot;
foreach (EntityBase e in m_scene.GetEntities()) { }
if (e.Name == name) {
retVal = (SceneObjectGroup)e; private SceneObjectGroup CopyPrim (SceneObjectGroup prim, string name)
break; {
} SceneObjectGroup copy = prim.Copy (true);
} copy.Name = name;
// can't find it so make a default one copy.DetachFromBackup ();
if (retVal == null) { return copy;
}
private SceneObjectGroup findByName (string name)
{
SceneObjectGroup retVal = null;
foreach (EntityBase e in m_scene.GetEntities()) {
if (e.Name == name) {
retVal = (SceneObjectGroup)e;
break;
}
}
// can't find it so make a default one
if (retVal == null) {
m_log.InfoFormat("[{0}]: Prim named {1} was not found in region {2}. Making default wooden sphere.", m_name, name, m_scene.RegionInfo.RegionName); m_log.InfoFormat("[{0}]: Prim named {1} was not found in region {2}. Making default wooden sphere.", m_name, name, m_scene.RegionInfo.RegionName);
retVal = MakeDefaultPrim (name); retVal = MakeDefaultPrim (name);
} }
return retVal; return retVal;
} }
private SceneObjectGroup MakeDefaultPrim (string name) private SceneObjectGroup MakeDefaultPrim (string name)
{ {
PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere (); PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere ();
shape.Scale = new Vector3 (0.5f, 0.5f, 0.5f); shape.Scale = new Vector3 (0.5f, 0.5f, 0.5f);
SceneObjectGroup prim = new SceneObjectGroup(m_owner, new Vector3((float)m_scene.RegionInfo.RegionSizeX / 2, (float)m_scene.RegionInfo.RegionSizeY / 2, 25f), shape); SceneObjectGroup prim = new SceneObjectGroup(m_owner, new Vector3((float)m_scene.RegionInfo.RegionSizeX / 2, (float)m_scene.RegionInfo.RegionSizeY / 2, 25f), shape);
prim.Name = name; prim.Name = name;
prim.DetachFromBackup (); prim.DetachFromBackup ();
m_scene.AddNewSceneObject (prim, false); m_scene.AddNewSceneObject (prim, false);
return prim; return prim;
} }
} }
} }

View File

@ -32,170 +32,170 @@ using OpenSim.Region.Framework.Scenes;
namespace Flocking namespace Flocking
{ {
public class FlowMap public class FlowMap
{ {
private Scene m_scene; private Scene m_scene;
private float[, ,] m_flowMap; private float[, ,] m_flowMap;
private uint regionX; private uint regionX;
private uint regionY; private uint regionY;
private uint regionZ; private uint regionZ;
private float regionBorder; private float regionBorder;
public FlowMap (Scene scene, int maxHeight, float borderSize) public FlowMap (Scene scene, int maxHeight, float borderSize)
{ {
m_scene = scene; m_scene = scene;
regionX = m_scene.RegionInfo.RegionSizeX; regionX = m_scene.RegionInfo.RegionSizeX;
regionY = m_scene.RegionInfo.RegionSizeY; regionY = m_scene.RegionInfo.RegionSizeY;
regionZ = (uint)maxHeight; regionZ = (uint)maxHeight;
regionBorder = borderSize; regionBorder = borderSize;
m_flowMap = new float[regionX, regionY, regionZ]; m_flowMap = new float[regionX, regionY, regionZ];
} }
public int LengthX { public int LengthX {
get {return (int)regionX;} get {return (int)regionX;}
} }
public int LengthY { public int LengthY {
get {return (int)regionY;} get {return (int)regionY;}
} }
public int LengthZ { public int LengthZ {
get {return (int)regionZ;} get {return (int)regionZ;}
} }
public int Border { public int Border {
get {return (int)regionBorder;} get {return (int)regionBorder;}
} }
public void Initialise() {
//fill in the boundaries
for( int x = 0; x < regionX; x++ ) {
for( int y = 0; y < regionY; y++ ) {
m_flowMap[x,y,0] = 100f;
m_flowMap[x,y, regionZ-1] = 100f;
}
}
for( int x = 0; x < regionX; x++ ) {
for( int z = 0; z < regionZ; z++ ) {
m_flowMap[x,0,z] = 100f;
m_flowMap[x,regionY-1,z] = 100f;
}
}
for( int y = 0; y < regionY; y++ ) {
for( int z = 0; z < regionZ; z++ ) {
m_flowMap[0,y,z] = 100f;
m_flowMap[regionX-1,y,z] = 100f;
}
}
//fill in the terrain
for( int x = 0; x < regionX; x++ ) {
for( int y = 0; y < regionY; y++ ) {
int zMax = Convert.ToInt32(m_scene.GetGroundHeight( x, y ));
for( int z = 1; z < zMax; z++ ) {
m_flowMap[x,y,z] = 100f;
}
}
}
// fill in the things
foreach( EntityBase entity in m_scene.GetEntities() ) {
if( entity is SceneObjectGroup ) {
SceneObjectGroup sog = (SceneObjectGroup)entity;
//todo: ignore phantom
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
int maxX, minX, maxY, minY, maxZ, minZ;
sog.GetAxisAlignedBoundingBoxRaw( out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ );
minX = Convert.ToInt32(fminX);
maxX = Convert.ToInt32(fmaxX);
minY = Convert.ToInt32(fminY);
maxY = Convert.ToInt32(fmaxX);
minZ = Convert.ToInt32(fminZ);
maxZ = Convert.ToInt32(fmaxZ);
for( int x = minX; x < maxX; x++ ) {
for( int y = minY; y < maxY; y++ ) {
for( int z = minZ; z < maxZ; z++ ) {
m_flowMap[x,y,z] = 100f;
}
}
}
}
}
}
public bool WouldHitObstacle (Vector3 currPos, Vector3 targetPos) public void Initialise() {
{ //fill in the boundaries
bool retVal = false; for( int x = 0; x < regionX; x++ ) {
//fail fast for( int y = 0; y < regionY; y++ ) {
if( IsOutOfBounds(targetPos) ) { m_flowMap[x,y,0] = 100f;
retVal = true; m_flowMap[x,y, regionZ-1] = 100f;
} else if( IsWithinObstacle(targetPos) ) { }
retVal = true; }
} else if( IntersectsObstacle (currPos, targetPos) ) { for( int x = 0; x < regionX; x++ ) {
retVal = true; for( int z = 0; z < regionZ; z++ ) {
} m_flowMap[x,0,z] = 100f;
m_flowMap[x,regionY-1,z] = 100f;
return retVal; }
} }
for( int y = 0; y < regionY; y++ ) {
public bool IsOutOfBounds(Vector3 targetPos) { for( int z = 0; z < regionZ; z++ ) {
bool retVal = false; m_flowMap[0,y,z] = 100f;
if( targetPos.X < regionBorder || m_flowMap[regionX-1,y,z] = 100f;
targetPos.X > regionX - regionBorder || }
}
//fill in the terrain
for( int x = 0; x < regionX; x++ ) {
for( int y = 0; y < regionY; y++ ) {
int zMax = Convert.ToInt32(m_scene.GetGroundHeight( x, y ));
for( int z = 1; z < zMax; z++ ) {
m_flowMap[x,y,z] = 100f;
}
}
}
// fill in the things
foreach( EntityBase entity in m_scene.GetEntities() ) {
if( entity is SceneObjectGroup ) {
SceneObjectGroup sog = (SceneObjectGroup)entity;
//todo: ignore phantom
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
int maxX, minX, maxY, minY, maxZ, minZ;
sog.GetAxisAlignedBoundingBoxRaw( out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ );
minX = Convert.ToInt32(fminX);
maxX = Convert.ToInt32(fmaxX);
minY = Convert.ToInt32(fminY);
maxY = Convert.ToInt32(fmaxX);
minZ = Convert.ToInt32(fminZ);
maxZ = Convert.ToInt32(fmaxZ);
for( int x = minX; x < maxX; x++ ) {
for( int y = minY; y < maxY; y++ ) {
for( int z = minZ; z < maxZ; z++ ) {
m_flowMap[x,y,z] = 100f;
}
}
}
}
}
}
public bool WouldHitObstacle (Vector3 currPos, Vector3 targetPos)
{
bool retVal = false;
//fail fast
if( IsOutOfBounds(targetPos) ) {
retVal = true;
} else if( IsWithinObstacle(targetPos) ) {
retVal = true;
} else if( IntersectsObstacle (currPos, targetPos) ) {
retVal = true;
}
return retVal;
}
public bool IsOutOfBounds(Vector3 targetPos) {
bool retVal = false;
if( targetPos.X < regionBorder ||
targetPos.X > regionX - regionBorder ||
targetPos.Y < regionBorder || targetPos.Y < regionBorder ||
targetPos.Y > regionY - regionBorder || targetPos.Y > regionY - regionBorder ||
targetPos.Z < regionBorder || targetPos.Z < regionBorder ||
targetPos.Z > regionZ - regionBorder ) { targetPos.Z > regionZ - regionBorder ) {
retVal = true;
}
return retVal;
}
public bool IntersectsObstacle (Vector3 currPos, Vector3 targetPos) retVal = true;
{ }
bool retVal = false;
// Ray trace the Vector and fail as soon as we hit something return retVal;
Vector3 direction = targetPos - currPos; }
float length = direction.Length();
// check every metre public bool IntersectsObstacle (Vector3 currPos, Vector3 targetPos)
for( float i = 1f; i < length; i += 1f ) { {
Vector3 rayPos = currPos + ( direction * i ); bool retVal = false;
//give up if we go OOB on this ray // Ray trace the Vector and fail as soon as we hit something
if( IsOutOfBounds( rayPos ) ){ Vector3 direction = targetPos - currPos;
retVal = true; float length = direction.Length();
break; // check every metre
} for( float i = 1f; i < length; i += 1f ) {
else if( IsWithinObstacle( rayPos ) ) { Vector3 rayPos = currPos + ( direction * i );
retVal = true; //give up if we go OOB on this ray
break; if( IsOutOfBounds( rayPos ) ){
} retVal = true;
} break;
}
return retVal; else if( IsWithinObstacle( rayPos ) ) {
} retVal = true;
break;
public bool IsWithinObstacle( Vector3 targetPos ) { }
return IsWithinObstacle(Convert.ToInt32(targetPos.X), Convert.ToInt32(targetPos.Y),Convert.ToInt32(targetPos.Z)); }
}
return retVal;
public bool IsWithinObstacle( int x, int y, int z ) { }
bool retVal = false;
public bool IsWithinObstacle( Vector3 targetPos ) {
return IsWithinObstacle(Convert.ToInt32(targetPos.X), Convert.ToInt32(targetPos.Y),Convert.ToInt32(targetPos.Z));
}
public bool IsWithinObstacle( int x, int y, int z ) {
bool retVal = false;
if (x >= LengthX || y >= LengthY || z >= LengthZ) if (x >= LengthX || y >= LengthY || z >= LengthZ)
{ {
retVal = true; retVal = true;
} }
else if (x < 0 || y < 0 || z < 0) else if (x < 0 || y < 0 || z < 0)
{ {
retVal = true; retVal = true;
} else if (m_flowMap[x,y,z] > 50f) { } else if (m_flowMap[x,y,z] > 50f) {
retVal = true; retVal = true;
} }
return retVal; return retVal;
} }
} }
} }

View File

@ -25,13 +25,13 @@ Configuration:
If you have the module added to or compiled with your OpenSim build then it will run a instance of itself once per region. If you have the module added to or compiled with your OpenSim build then it will run a instance of itself once per region.
By default it will configure itself to some sensible defaults and will sit quietly in the background waiting for commands By default it will configure itself to some sensible defaults and will sit quietly in the background waiting for commands
from the console, or from in-world. It will not enable itself (i.e. rez some birds) until commanded to do so, or from the console, or from in-world. It will not enable itself (i.e. rez some birds) until commanded to do so, or
configured to do so in the .ini file. It is possible to completely stop the module from doing anything (including configured to do so in the .ini file. It is possible to completely stop the module from doing anything (including
listening for commands), but you must have a particular setting in the .ini file for that region (see below) - in other listening for commands), but you must have a particular setting in the .ini file for that region (see below) - in other
words, you must have a config for it! words, you must have a config for it!
To become active, the module needs to be enabled in the ini file or commanded to do so from inworld or the console. To become active, the module needs to be enabled in the ini file or commanded to do so from inworld or the console.
Otherwise it does nothing on startup except listen for commands. If you are running multiple regions on one simulator you Otherwise it does nothing on startup except listen for commands. If you are running multiple regions on one simulator you
can have different Birds settings per region in the configuration file, in the exact same way you can customize per Region can have different Birds settings per region in the configuration file, in the exact same way you can customize per Region
setting in Regions.ini setting in Regions.ini
@ -59,9 +59,9 @@ Here is an example config:
;; Set the Birds settings per named region ;; Set the Birds settings per named region
[Test Region 1] [Test Region 1]
BirdsModuleStartup = True ;this is the default and determines whether the module does anything BirdsModuleEnabled = True ;this is the default and determines whether the module does anything
BirdsEnabled = True ;set to false to disable the birds from appearing in this region BirdsShowOnStartup = True ;set to false to disable the birds from appearing in this region
BirdsFlockSize = 50 ;the number of birds to flock BirdsFlockSize = 50 ;the number of birds to flock
BirdsMaxFlockSize = 100 ;the maximum flock size that can be created (keeps things sane) BirdsMaxFlockSize = 100 ;the maximum flock size that can be created (keeps things sane)
BirdsMaxSpeed = 3 ;how far each bird can travel per update BirdsMaxSpeed = 3 ;how far each bird can travel per update
@ -72,9 +72,9 @@ Here is an example config:
BirdsBorderSize = 5 ;how close to the edge of a region can we get? BirdsBorderSize = 5 ;how close to the edge of a region can we get?
BirdsMaxHeight = 256 ;how high are we allowed to flock BirdsMaxHeight = 256 ;how high are we allowed to flock
BirdsUpdateEveryNFrames = 1 ;update bird positions every N simulator frames BirdsUpdateEveryNFrames = 1 ;update bird positions every N simulator frames
BirdsPrim = SeaGull1 ;By default the module will create a flock of plain wooden spheres, BirdsPrim = SeaGull1 ;By default the module will create a flock of plain wooden spheres,
;however this can be overridden to the name of an existing prim that ;however this can be overridden to the name of an existing prim that
;needs to already exist in the scene - i.e. be rezzed in the region. ;needs to already exist in the scene - i.e. be rezzed in the region.
;who is allowed to send commands via chat or script: list of UUIDs or ESTATE_OWNER or ESTATE_MANAGER ;who is allowed to send commands via chat or script: list of UUIDs or ESTATE_OWNER or ESTATE_MANAGER
;or everyone if not specified ;or everyone if not specified
@ -94,7 +94,7 @@ Runtime Commands:
The following commands, which can be issued on the Console or via in-world chat or scripted chat on the BirdsChatChannel The following commands, which can be issued on the Console or via in-world chat or scripted chat on the BirdsChatChannel
to control the birds at runtime: to control the birds at runtime:
birds-stop or /118 stop ;stop all birds flocking birds-stop or /118 stop ;stop all birds flocking
birds-start or /118 start ;start all birds flocking birds-start or /118 start ;start all birds flocking
birds-enable or /118 enable ;enable the flocking simulation if disabled and rez new birds birds-enable or /118 enable ;enable the flocking simulation if disabled and rez new birds
birds-disable or /118 disable ;stop all birds and remove them from the scene birds-disable or /118 disable ;stop all birds and remove them from the scene
@ -122,8 +122,8 @@ Security:
By default anyone can send commands to the module from within a script or via the in-world chat on the 'BirdsChatChannel' channel. By default anyone can send commands to the module from within a script or via the in-world chat on the 'BirdsChatChannel' channel.
You should use a high negative value for channel if you want to allow script access, but not in-world chat. Further you can restrict You should use a high negative value for channel if you want to allow script access, but not in-world chat. Further you can restrict
which users are allowed to control the module using the 'BirdsAllowedControllers' setting. This is a comma separated list of user UUIDs, which users are allowed to control the module using the 'BirdsAllowedControllers' setting. This is a comma separated list of user UUIDs,
but it may also contain one of the pre-defined constants ESTATE_OWNER (evaluates to the UUID of the estate owner) and ESTATE_MANAGER but it may also contain one of the pre-defined constants ESTATE_OWNER (evaluates to the UUID of the estate owner) and ESTATE_MANAGER
(evaluates to a list of estate manager UUIDS). (evaluates to a list of estate manager UUIDS).
* For commands sent from in-world chat, it is the UUID of the avatar sending the command that is checked against the list. * For commands sent from in-world chat, it is the UUID of the avatar sending the command that is checked against the list.
* For commands sent from a script it is the UUID of the owner of the prim in which the script resides that is checked against the list. * For commands sent from a script it is the UUID of the owner of the prim in which the script resides that is checked against the list.
@ -134,7 +134,7 @@ Any currently rezzed in-scene-object can be used as the bird prim. However fps i
complexity of the entity to use. It is easier to throw a single prim (or sculpty) around the scene than it is to complexity of the entity to use. It is easier to throw a single prim (or sculpty) around the scene than it is to
throw the constituent parts of a 200 linked prim dragon. throw the constituent parts of a 200 linked prim dragon.
Tests show that <= 500 single prims can be flocked effectively - depending on system and network Tests show that <= 500 single prims can be flocked effectively - depending on system and network
However maybe <= 300 simple linksets can perform as well. However maybe <= 300 simple linksets can perform as well.
Network Traffic: Network Traffic:
@ -186,9 +186,9 @@ is an example output:
In the above example, there is one avatar sitting on bird-prim18. For more than one avatar the UUID list will be separated by spaces. In the above example, there is one avatar sitting on bird-prim18. For more than one avatar the UUID list will be separated by spaces.
Please Note: Please Note:
This module is currently only tested against opensim master. This module is currently only tested against opensim master.
Licence: all files released under a BSD licence Licence: all files released under a BSD licence
If you have any question please contact Jak Daniels, jak@ateb.co.uk If you have any question please contact Jak Daniels, jak@ateb.co.uk