Compare commits

..

15 Commits

Author SHA1 Message Date
Jon Cundill c0abc4d2e7 test keys still work 2012-03-07 19:54:25 +00:00
Jon Cundill 99bc21499b refactored command parsing 2012-01-05 05:07:24 +00:00
Jon Cundill 60cb5a018c reversed order in changes file 2011-07-24 22:07:16 +01:00
Jon Cundill b02e89189f documented changes 2011-07-24 00:24:11 +01:00
Jon Cundill e584d81e0c added ability to control flock from scripts. 2011-07-24 00:18:03 +01:00
Jon Cundill 1c410fdb81 updated changes 2011-07-22 13:49:33 +01:00
Jon Cundill f0cb3dee00 fixed README for stable 2011-07-21 22:55:57 +01:00
Jon Cundill 9083dbf23b added all files to stable branch 2011-07-21 22:37:51 +01:00
Jon Cundill 9e84d1f957 fixed up repo 2011-07-21 22:34:15 +01:00
Jon Cundill 502b66e7bb added snapshot binary 2011-07-21 22:32:04 +01:00
Jon Cundill c2ecfbaf7c fixes for stable 2011-07-21 22:30:45 +01:00
Jon Cundill 9aa909ba74 separated flock size from flocking parameters and exposed parameters to command handlers 2011-07-17 13:43:24 +01:00
Jon Cundill bf2061a391 encapsulated parameters that control flock behaviour 2011-07-17 12:42:48 +01:00
Jon Cundill c1109c1810 fixed up in world command handling somewhat, added parser 2011-07-17 11:53:35 +01:00
Jon Cundill cdbac1c921 added tests - no sogs yet 2011-07-16 08:56:20 +01:00
26 changed files with 2014 additions and 1608 deletions

21
.gitattributes vendored
View File

@ -1,21 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto
*.cs text diff=csharp eol=lf
*.java text diff=java
*.html text diff=html
*.css text
*.js text
*.sql text
*.csproj text merge=union
*.sln text merge=union eol=crlf
*.docx diff=astextplain
*.DOCX diff=astextplain
# absolute paths are ok, as are globs
/**/postinst* text eol=lf
# paths that don't start with / are treated relative to the .gitattributes folder
relative/path/*.txt text eol=lf

View File

@ -1,8 +1,19 @@
08/07/2011 - Initial pre alpha release
Basic Flocking algos implemented
Border avoidance not so good
No configuration capabilities
23/07/2011
added ability to script boid behaviour from lsl
tidied up copyright notices
further flow field work - still flying into objects, so still work to do here
flocking simulation now doesn't start until invoked by start command
flow field now recalculated on each start - so can edit scene and restart takes this into account
21/07/2011
introduced branch based on stable 0.7.1.1 opensim code. This currently ahead of master
added ability to change the flocking parameters at runtime through set commands
improved the collision algorithms somewhat
added orientation flock step that attempts to limit excessive vertical velocity vectors
13/07/2011
added more documentation
09/07/2011
Fixed Boid rotation, boid now orients with direction of travel
@ -10,30 +21,10 @@
added commands for setting size of flock, prim to use for boid
added commands to start and stop the boids flocking
13/07/2011
added more documentation
10/07/2014
forked from https://github.com/jonc/osboids
17/07/2014
refactored the code
added some more config items to set the region border size (no go zone) and the max height
added support to the module for varregions
24/12/2014
renamed the module to OpenSimBirds, changed all references to [Bb]oids to [Bb]irds in code
change the configuration to use own ini file and to setup per region configurations
changed Util class to BirdsUtil to avoid clash with OpenSim.Util
Um.... I think I missed some change log here... many things were changed. Use the git log??
16/10/2015
added a setting to determine whether the module is active on startup. This defaults to true,
meaning that even if there is no config at all the module still loads and waits for commands
from the console or in-world.
08/07/2011 - Initial pre alpha release
Basic Flocking algos implemented
Border avoidance not so good
No configuration capabilities
17/10/2015
added basic security. It's now possible to specify a list of allowed UUIDs (or ESTATE_MANAGER or
ESTATE_OWNER) of avatars that can send commands to the module from chat, or of object owners
if the commands are from a script.

View File

@ -1,5 +1,4 @@
The following people have contributed to the development of the OpenSimBirds module
The following people have contributed to the development of the osboids module
* Jon Cundill - initial implementation
* Jak Daniels - refactor for opensim 0.8.x and varregions and per region configuration

BIN
Flocking.dll Executable file

Binary file not shown.

BIN
Flocking.dll.mdb Normal file

Binary file not shown.

348
Flocking/Boid.cs Normal file
View File

@ -0,0 +1,348 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using log4net;
using OpenMetaverse;
using Utils = OpenSim.Framework.Util;
namespace Flocking
{
public class Boid
{
// private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
private string m_id;
private Vector3 m_size;
private Vector3 m_loc;
private Vector3 m_vel;
private Vector3 m_acc;
private Random m_rndnums = new Random (Environment.TickCount);
private BoidBehaviour m_behaviour;
/// <summary>
/// Initializes a new instance of the <see cref="Flocking.Boid"/> class.
/// </summary>
/// <param name='l'>
/// L. the initial position of this boid
/// </param>
/// <param name='ms'>
/// Ms. max speed this boid can attain
/// </param>
/// <param name='mf'>
/// Mf. max force / acceleration this boid can extert
/// </param>
public Boid (string id, Vector3 size, BoidBehaviour behaviour)
{
m_id = id;
m_acc = Vector3.Zero;
m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1));
m_size = size;
m_behaviour = behaviour;
}
public Vector3 Location {
get { return m_loc; }
set { m_loc = value; }
}
public Vector3 Velocity {
get { return m_vel; }
set { m_vel = value; }
}
public Vector3 Size {
get { return m_size; }
}
public String Id {
get { return m_id; }
}
/// <summary>
/// Moves our boid in the scene relative to the rest of the flock.
/// </summary>
/// <param name='boids'>
/// Boids. all the other chaps in the scene
/// </param>
public void MoveInSceneRelativeToFlock (List<Boid> neighbours, FlowField field)
{
// we would like to stay with our mates
Flock (neighbours);
// however, our first priority is to not hurt ourselves
// so adjust where we would like to go to avoid hitting things
AvoidObstacles (field);
// then we want to avoid any threats
// this not implemented yet
// ok so we worked our where we want to go, so ...
UpdatePositionInScene ();
}
/// <summary>
/// Move within our local flock
/// 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<Boid> neighbours)
{
// calc the force vectors on this boid
Vector3 sep = Separate (neighbours); // Separation
Vector3 ali = Align (neighbours); // Alignment
Vector3 coh = Cohesion (neighbours); // Cohesion
Vector3 ori = Orientation(); // its tricky to fly directly up or down
// Arbitrarily weight these forces
sep *= m_behaviour.separationWeighting;
ali *= m_behaviour.alignmentWeighting;
coh *= m_behaviour.cohesionWeighting;
// Add the force vectors to the current acceleration of the boid
m_acc += sep;
m_acc += ali;
m_acc += coh;
//m_acc += ori;
}
/// <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
m_vel += m_acc;
// Limit speed
m_vel = Util.Limit (m_vel, m_behaviour.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 = Vector3.Zero; // The steering vector
Vector3 desired = target - m_loc; // A vector pointing from the location to the target
float distance = 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 (distance > 0) {
// Normalize desired
desired.Normalize ();
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
if ((slowdown) && (distance < m_behaviour.lookaheadDistance )) {
desired *= (m_behaviour.maxSpeed * (distance / m_behaviour.lookaheadDistance)); // This damping is somewhat arbitrary
} else {
desired *= m_behaviour.maxSpeed;
}
// Steering = Desired minus Velocity
steer = desired - m_vel;
//steer.limit(maxforce); // Limit to maximum steering force
steer = Util.Limit (steer, m_behaviour.maxForce);
}
return steer;
}
/// <summary>
/// navigate away from whatever it is we are too close to
/// </summary>
void AvoidObstacles (FlowField field)
{
//look tolerance metres ahead
//m_acc += field.AdjustVelocity( this, m_behaviour.lookaheadDistance );
Vector3 normVel = Vector3.Normalize (m_vel);
Vector3 inFront = m_loc + normVel * m_behaviour.LookaheadDistance;
Vector3 adjustedDestintation = field.FieldStrength (m_loc, m_size, inFront);
Vector3 newVel = Vector3.Normalize (adjustedDestintation - m_loc) * Vector3.Mag (m_vel);
float mOrigVel = Vector3.Mag(m_vel);
float mNewVel = Vector3.Mag(newVel);
if( mNewVel != 0f && mNewVel > mOrigVel ) {
newVel *= mOrigVel / mNewVel;
}
m_vel = newVel;
}
/// <summary>
/// Separate ourselves from the specified boids.
/// keeps us a respectable distance from our closest neighbours whilst still
/// being part of our local flock
/// </summary>
/// <param name='neighbours'>
/// Boids. all the boids in the scene
/// </param>
Vector3 Separate (List<Boid> neighbours)
{
// For every boid in the system, check if it's too close
float desired = m_behaviour.desiredSeparation;
//who are we too close to at the moment
List<Boid> tooCloseNeighbours = neighbours.FindAll( delegate(Boid neighbour) {
// Is the distance is less than the desired amount
return Utils.DistanceLessThan(m_loc, neighbour.Location, desired); //GetDistanceTo (m_loc, neighbour.Location) < desired;
});
// move a bit away from them
Vector3 steer = Vector3.Zero;
tooCloseNeighbours.ForEach( delegate(Boid neighbour) {
// Calculate vector pointing away from neighbor
Vector3 diff = m_loc - neighbour.Location;
if( diff == Vector3.Zero ) {
diff = new Vector3( (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble());
}
steer += Utils.GetNormalizedVector(diff) / (float)(Utils.GetDistanceTo (m_loc, neighbour.Location));
});
if( steer.Length () > 0 ) {
// Average -- divide by how many
steer /= (float)tooCloseNeighbours.Count;
// Implement Reynolds: Steering = Desired - Velocity
steer.Normalize ();
steer *= m_behaviour.maxSpeed;
steer -= m_vel;
//don't go too fast;
steer = Util.Limit (steer, m_behaviour.maxForce);
}
return steer;
}
Vector3 Orientation() {
Vector3 retVal = Vector3.Zero;
float biggestLevel = Math.Max( m_vel.X, m_vel.Y );
if ( biggestLevel == 0f ) {
// wobble off the vertical
retVal.X += (float)(2*m_rndnums.NextDouble() - 1f);
retVal.Y += (float)(2*m_rndnums.NextDouble() - 1f);
}
return retVal;
}
/// <summary>
/// Align our boid within the flock.
/// For every nearby boid in the system, calculate the average velocity
/// and move us towards that - this keeps us moving with the flock.
/// </summary>
/// <param name='boids'>
/// Boids. all the boids in the scene - we only really care about those in the neighbourdist
/// </param>
Vector3 Align (List<Boid> boids)
{
Vector3 steer = Vector3.Zero;
boids.ForEach( delegate( Boid other ) {
steer += other.Velocity;
});
int count = boids.Count;
if (count > 0) {
steer /= (float)count;
}
// As long as the vector is greater than 0
if (steer.Length () > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.Normalize ();
steer *= m_behaviour.maxSpeed;
steer -= m_vel;
//steer.limit(maxforce);
steer = Util.Limit (steer, m_behaviour.maxForce);
}
return steer;
}
/// <summary>
/// MAintain the cohesion of our local flock
/// For the average location (i.e. center) of all nearby boids, calculate our steering vector towards that location
/// </summary>
/// <param name='neighbours'>
/// Boids. the boids in the scene
/// </param>
Vector3 Cohesion (List<Boid> neighbours)
{
Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations
neighbours.ForEach( delegate(Boid other) {
sum += other.Location; // Add location
});
int count = neighbours.Count;
if (count > 0) {
sum /= (float)count;
return Steer (sum, false); // Steer towards the location
}
return sum;
}
}
}

200
Flocking/BoidBehaviour.cs Normal file
View File

@ -0,0 +1,200 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
namespace Flocking
{
public class BoidBehaviour
{
public float maxSpeed;
public float maxForce;
public float neighbourDistance;
public float desiredSeparation;
public float tolerance;
public float separationWeighting;
public float alignmentWeighting;
public float cohesionWeighting;
public float lookaheadDistance;
private Dictionary<string, string> m_paramDescriptions = new Dictionary<string, string> ();
public float AlignmentWeighting {
get {
return this.alignmentWeighting;
}
set {
alignmentWeighting = value;
}
}
public float CohesionWeighting {
get {
return this.cohesionWeighting;
}
set {
cohesionWeighting = value;
}
}
public float DesiredSeparation {
get {
return this.desiredSeparation;
}
set {
desiredSeparation = value;
}
}
public float LookaheadDistance {
get {
return this.lookaheadDistance;
}
set {
lookaheadDistance = value;
}
}
public float MaxForce {
get {
return this.maxForce;
}
set {
maxForce = value;
}
}
public float MaxSpeed {
get {
return this.maxSpeed;
}
set {
maxSpeed = value;
}
}
public float NeighbourDistance {
get {
return this.neighbourDistance;
}
set {
neighbourDistance = value;
}
}
public float SeparationWeighting {
get {
return this.separationWeighting;
}
set {
separationWeighting = value;
}
}
public float Tolerance {
get {
return this.tolerance;
}
set {
tolerance = value;
}
}
public BoidBehaviour() {
m_paramDescriptions.Add("max-speed", "max distance boid will travel per frame");
m_paramDescriptions.Add("max-force", "max acceleration od decelleration boid can exert");
m_paramDescriptions.Add("neighbour-distance", "boid will consider other boids within this distance as part of local flock");
m_paramDescriptions.Add("desired-separation", "closest distance to other boids that our boid would like to get");
m_paramDescriptions.Add("tolerance", "how close to the edges of objects or the scene should our boid get");
m_paramDescriptions.Add("separation-weighting", "factor by which closeness to other boids should be favoured when updating position in flock");
m_paramDescriptions.Add("alignment-weighting", "factor by which alignment with other boids should be favoured when updating position in flock");
m_paramDescriptions.Add("cohesion-weighting", "factor by which keeping within the local flock should be favoured when updating position in flock");
m_paramDescriptions.Add("lookahead-distance", "how far in front should the boid look for edges and boundaries");
}
public bool IsValidParameter (string name)
{
return m_paramDescriptions.ContainsKey (name);
}
public void SetParameter (string name, string newVal)
{
switch (name) {
case "max-speed":
maxSpeed = Convert.ToSingle(newVal);
break;
case "max-force":
maxForce = Convert.ToSingle(newVal);
break;
case "neighbour-distance":
neighbourDistance = Convert.ToSingle(newVal);
break;
case "desired-separation":
desiredSeparation = Convert.ToSingle(newVal);
break;
case "tolerance":
tolerance = Convert.ToSingle(newVal);
break;
case "separation-weighting":
separationWeighting = Convert.ToSingle(newVal);
break;
case "alignment-weighting":
alignmentWeighting = Convert.ToSingle(newVal);
break;
case "cohesion-weighting":
cohesionWeighting = Convert.ToSingle(newVal);
break;
case "lookahead-distance":
lookaheadDistance = Convert.ToSingle(newVal);
break;
}
}
public string GetList ()
{
string retVal = Environment.NewLine;
foreach (string name in m_paramDescriptions.Keys) {
retVal += name + " - " + m_paramDescriptions [name] + Environment.NewLine;
}
return retVal;
}
public override string ToString ()
{
return string.Format (
"alignment-weighting = {0}, " + Environment.NewLine +
"cohesion-weighting = {1}, " + Environment.NewLine +
"desired-separation = {2}, " + Environment.NewLine +
"lookahead-distance = {3}, " + Environment.NewLine +
"max-force = {4}, " + Environment.NewLine +
"max-speed = {5}, " + Environment.NewLine +
"neighbour-distance = {6}, " + Environment.NewLine +
"separation-weighting = {7}, " + Environment.NewLine +
"tolerance = {8}", AlignmentWeighting, CohesionWeighting, DesiredSeparation, LookaheadDistance, MaxForce, MaxSpeed, NeighbourDistance, SeparationWeighting, Tolerance);
}
}
}

220
Flocking/FlockingCommand.cs Normal file
View File

@ -0,0 +1,220 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using log4net;
using OpenMetaverse;
using OpenSim.Region.Framework.Scenes;
namespace Flocking
{
public abstract class FlockingCommand
{
protected static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
protected FlockingController m_controller;
protected string m_name;
protected string m_args;
protected string m_description;
public FlockingCommand( string name, string args, string description ) {
m_name = name;
m_args = args;
m_description = description;
}
public void Handle (string module, string[] args) {
if( ShouldHandleCmd() ) {
Invoke( args );
}
}
private bool ShouldHandleCmd ()
{
return View.Scene.ConsoleScene () == View.Scene;
}
public abstract void Invoke( string[] args );
public string Name {
get{ return m_name;}
}
public string Params {
get{ return m_args; }
}
public string Description {
get{ return m_description;}
}
public FlockingController Controller {
get{ return m_controller; }
set{ m_controller = value; }
}
public FlockingModel Model {
get { return Controller.Model; }
}
public FlockingView View {
get { return Controller.View; }
}
}
public class RoostCommand : FlockingCommand {
public RoostCommand() : base( "roost", "", "return all the boids to the start position and remove them from the scene") {}
public override void Invoke (string[] args) {
if( Model.Active ) {
Model.Goal = FlockGoal.Roost;
} else {
Controller.ShowResponse ("Flock is not active, roost has no effect", args);
}
}
}
public class StopCommand : FlockingCommand {
public StopCommand() : base("stop", "", "stop all Flocking"){}
public override void Invoke (string[] args) {
m_log.Info ("stopping the flocking activity");
Model.Active = false;
View.Clear ();
}
}
public class SetPositionCommand : FlockingCommand {
public SetPositionCommand() : base("position", "x,y,z", "location that the boids will start flocking from") {}
public override void Invoke (string[] args)
{
float x = Convert.ToSingle (args [1]);
float y = Convert.ToSingle (args [2]);
float z = Convert.ToSingle (args [3]);
Vector3 startPos = new Vector3 (x, y, z);
if (Model.ContainsPoint (startPos)) {
Model.StartPosition = startPos;
} else {
Controller.ShowResponse (startPos + "is not within the flow field", args);
}
}
}
public class SetParameterCommand : FlockingCommand {
public SetParameterCommand() : base("set", "name, value", "change the flock behaviour properties"){}
public override void Invoke (string[] args)
{
string name = args [1];
string newVal = args [2];
BoidBehaviour behaviour = Model.Behaviour;
if (behaviour.IsValidParameter (name)) {
behaviour.SetParameter (name, newVal);
} else {
Controller.ShowResponse (name + "is not a valid flock parameter", args);
Controller.ShowResponse ("valid parameters are: " + behaviour.GetList (), args);
}
}
}
public class SetFrameRateCommand : FlockingCommand {
public SetFrameRateCommand() : base("framerate", "num", "[debugging] only update boids every <num> frames") {}
public override void Invoke (string[] args)
{
int frameRate = Convert.ToInt32 (args [1]);
Controller.FrameUpdateRate = frameRate;
}
}
public class SetBoundsCommand : FlockingCommand {
public SetBoundsCommand() : base("bounds", "xMin,xMax,yMin,yMax,zMin,zMax", "Bounds of the 3D space that the flock will be confined to") {}
public override void Invoke (string[] args)
{
//TODO:
}
}
public class SetSizeCommand : FlockingCommand {
public SetSizeCommand() : base("size", "num", "Adjust the size of the flock ") {}
public override void Invoke (string [] args)
{
lock (Controller.UI_SYNC) {
int newSize = Convert.ToInt32 (args [1]);
Model.Size = newSize;
View.Clear ();
}
}
}
public class SetPrimCommand : FlockingCommand {
public SetPrimCommand() : base("prim", "name", "set the prim used for each boid to that passed in") {}
public override void Invoke (string[] args)
{
string primName = args [1];
lock (Controller.UI_SYNC) {
View.BoidPrim = primName;
View.Clear ();
}
}
}
public class ShowStatsCommand : FlockingCommand {
public ShowStatsCommand() : base("stats", "", "show flocking stats") {}
public override void Invoke (string[] args)
{
string str = Model.ToString ();
Controller.ShowResponse (str, args);
}
}
public class StartCommand : FlockingCommand {
public StartCommand() : base("start", "", "Start Flocking") {}
public override void Invoke(string[] args) {
if( Model.Active ) {
Controller.ShowResponse("Already active, restarting", args);
Model.Active = false;
View.Clear();
}
m_log.Info ("start the flocking capability");
Model.Goal = FlockGoal.Flock;
Controller.Start ();
Model.Active = true;
//m_module.FlockUpdate ();
}
}
}

View File

@ -0,0 +1,324 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Framework.Console;
using OpenSim.Region.Framework.Interfaces;
namespace Flocking
{
public delegate void BoidCmdDelegate (string module,string[] args);
public class BoidCmdDefn
{
public string Help = "";
public string Args = "";
public int NumParams = 0;
string m_name;
public BoidCmdDefn (string name, string args, string help)
{
Help = help;
Args = args;
m_name = name;
if (args.Trim ().Length > 0) {
NumParams = args.Split (",".ToCharArray ()).Length;
} else {
NumParams = 0;
}
}
public string GetSyntax ()
{
return m_name + " " + Args + " (" + Help + ")";
}
}
public class FlockingController
{
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
public object UI_SYNC = new object ();
private Scene m_scene;
private FlockingModel m_model;
private FlockingView m_view;
private int m_chatChannel;
private UUID m_owner;
private Dictionary<string, BoidCmdDelegate> m_commandMap = new Dictionary<string, BoidCmdDelegate> ();
private Dictionary<string, BoidCmdDefn> m_syntaxMap = new Dictionary<string, BoidCmdDefn> ();
private uint m_frame = 0;
private int m_frameUpdateRate = 1;
private Vector3 m_startPos = new Vector3 (128f, 128f, 128f);
private int m_minX = 0;
private int m_maxX = 256;
private int m_minY = 0;
private int m_maxY = 256;
private int m_minZ = 0;
private int m_maxZ = 256;
public FlockingController (Scene scene, BoidBehaviour behaviour, int channel, string prim, int flockSize)
{
//make the view
// who is the owner for the flock in this region
UUID owner = scene.RegionInfo.EstateSettings.EstateOwner;
m_view = new FlockingView (scene);
m_view.PostInitialize (owner);
m_view.BoidPrim = prim;
//make the model
FlowField field = new FlowField( scene, m_minX, m_maxX, m_minY, m_maxY, m_minZ, m_maxZ);
FlockingModel model = new FlockingModel(field, behaviour, m_startPos);
Vector3 startPos = new Vector3(128f, 128f, 128f);//scene.GetSceneObjectPart (View.BoidPrim).ParentGroup.AbsolutePosition;
model.StartPosition = startPos; // TODO: by default start from the prim
m_model = model;
m_scene = scene;
m_chatChannel = channel;
// who do we respond to in send messages
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
// register our event handlers
m_scene.EventManager.OnFrame += FlockUpdate; // plug in to the game loop
m_scene.EventManager.OnChatFromClient += ProcessChatCommand; //listen for commands sent from the client
IScriptModuleComms commsMod = m_scene.RequestModuleInterface<IScriptModuleComms>();
commsMod.OnScriptCommand += ProcessScriptCommand; // listen to scripts
}
public void Start()
{
//ask the view how big the boid prim is
Vector3 scale = View.GetBoidSize ();
FlowField field = new FlowField( m_scene, m_minX, m_maxX, m_minY, m_maxY, m_minZ, m_maxZ);
// init model
m_log.Info ("creating model");
// Generate initial flock values
m_model.BoidSize = scale;
m_model.Initialise (field);
m_log.Info ("done");
}
public int FrameUpdateRate {
get { return m_frameUpdateRate; }
set { m_frameUpdateRate = value; }
}
public FlockingModel Model {
get { return m_model; }
}
public FlockingView View {
get { return m_view; }
}
public void Deregister ()
{
m_scene.EventManager.OnChatFromClient -= ProcessChatCommand;
IScriptModuleComms commsMod = m_scene.RequestModuleInterface<IScriptModuleComms>();
commsMod.OnScriptCommand -= ProcessScriptCommand;
m_scene.EventManager.OnFrame -= FlockUpdate;
}
public void AddCommand (IRegionModuleBase module, FlockingCommand cmd)
{
cmd.Controller = this;
string name = cmd.Name;
string args = cmd.Params;
string help = cmd.Description;
CommandDelegate fn =cmd.Handle;
string argStr = "";
if (args.Trim ().Length > 0) {
argStr = " <" + args + "> ";
}
m_commandMap.Add (name, new BoidCmdDelegate (fn));
m_syntaxMap.Add (name, new BoidCmdDefn (name, args, help));
// register this command with the console
m_scene.AddCommand (module, "flock-" + name, "flock-" + name + argStr, help, fn);
}
#region handlers
public void FlockUpdate ()
{
if (((m_frame++ % m_frameUpdateRate) != 0) || !m_model.Active) {
return;
}
// work out where everyone has moved to
// and tell the scene to render the new positions
lock (UI_SYNC) {
List<Boid > boids = m_model.UpdateFlockPos ();
m_view.Render (boids);
}
}
public void ProcessScriptCommand (UUID scriptId, string reqId, string module, string input, string key)
{
if (FlockingModule.NAME != module) {
return;
}
string[] tokens = (input+"|<script>").Split ( new char[] { '|' }, StringSplitOptions.None);
string command = tokens [0];
m_log.Debug("Input was " + input + ", command is " + command);
BoidCmdDefn defn = null;
if (m_syntaxMap.TryGetValue (command, out defn)) {
if (CorrectSignature (tokens, defn)) {
// we got the signature of the command right
BoidCmdDelegate del = null;
if (m_commandMap.TryGetValue (command, out del)) {
m_log.Info("command ok - executing");
del (module, tokens);
} else {
// we don't understand this command
// shouldn't happen
m_log.ErrorFormat ("Unable to invoke command {0}", command);
}
} else {
m_log.Error(" signature wrong for " + command);
}
} else {
m_log.Error("no command for " + command);
}
}
void RespondToScript (UUID scriptId, int msgNum, string response)
{
IScriptModuleComms commsMod = m_scene.RequestModuleInterface<IScriptModuleComms>();
if( commsMod != null ) {
commsMod.DispatchReply (scriptId, msgNum, response, "");
} else {
Console.WriteLine("No script comms");
}
}
public void ProcessChatCommand (Object x, OSChatMessage msg)
{
if (m_scene.ConsoleScene () != m_scene || msg.Channel != m_chatChannel)
return; // not for us
// try and parse a valid cmd from this msg
string cmd = msg.Message.ToLower ();
//stick ui in the args so we know to respond in world
//bit of a hack - but lets us use CommandDelegate inWorld
string[] args = (cmd + " <ui>").Split (" ".ToCharArray ());
BoidCmdDefn defn = null;
if (m_syntaxMap.TryGetValue (args [0], out defn)) {
if (CorrectSignature (args, defn)) {
// we got the signature of the command right
BoidCmdDelegate del = null;
if (m_commandMap.TryGetValue (args [0], out del)) {
del (FlockingModule.NAME, args);
} else {
// we don't understand this command
// shouldn't happen
m_log.ErrorFormat ("Unable to invoke command {0}", args [0]);
RespondToChat (msg, "Unable to invoke command " + args [0]);
}
} else {
// we recognise the command, but we got the call wrong
RespondToChat (msg, "wrong syntax: " + defn.GetSyntax ());
}
} else {
// this is not a command we recognise
RespondToChat (msg, args [0] + " is not a valid command for osboids");
}
}
public void RespondToChat (OSChatMessage msg, string message)
{
m_log.Debug ("sending response -> " + message);
IClientAPI sender = msg.Sender;
sender.SendChatMessage (message, (byte)ChatTypeEnum.Say, msg.Position, "osboids", msg.SenderUUID, (byte)ChatSourceType.Agent, (byte)ChatAudibleLevel.Fully);
}
#endregion
private bool CorrectSignature (string[] args, BoidCmdDefn defn)
{
// args contain cmd name at 0 and <ui> tagged in last pos
return args.Length - 2 == defn.NumParams;
}
public void ShowResponse (string response, string [] cmd)
{
bool inWorld = IsInWorldCmd (cmd);
if (inWorld) {
ScenePresence owner = m_scene.GetScenePresence(m_owner);
SendMessage(owner, response);
} else {
MainConsole.Instance.Output (response);
}
}
private bool IsInWorldCmd (string [] args)
{
bool retVal = false;
if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) {
retVal = true;
}
return retVal;
}
public void SendMessage (ScenePresence recipient, string message)
{
IClientAPI ownerAPI = recipient.ControllingClient;
ownerAPI.SendChatMessage (message,
(byte)ChatTypeEnum.Say,
recipient.AbsolutePosition,
"osboids",
recipient.UUID,
(byte)ChatSourceType.Agent,
(byte)ChatAudibleLevel.Fully
);
}
}
}

210
Flocking/FlockingModel.cs Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using OpenMetaverse;
using Utils = OpenSim.Framework.Util;
namespace Flocking
{
public enum FlockGoal
{
Roost = 0x01,
Perch = 0x02,
Flock = 0x04,
}
public class FlockingModel
{
private List<Boid> m_flock = new List<Boid>();
private FlowField m_flowField;
private BoidBehaviour m_behaviour;
private int m_flockSize = 100;
private Vector3 m_boidSize;
private Vector3 m_startPos;
private FlockGoal m_goal = FlockGoal.Flock;
private bool m_active = false;
private int m_minX = 0;
private int m_maxX = 256;
private int m_minY = 0;
private int m_maxY = 256;
private int m_minZ = 0;
private int m_maxZ = 256;
public int Size {
get {return m_flockSize;}
set {
if( value < m_flock.Count ) {
m_flock.RemoveRange( 0, m_flock.Count - value );
} else while( value > m_flock.Count ) {
AddBoid( "boid"+m_flock.Count);
}
}
}
public FlockGoal Goal {
get { return m_goal; }
set { m_goal = value; }
}
public int MaxX {
get {
return this.m_maxX;
}
set {
m_maxX = value;
}
}
public int MaxY {
get {
return this.m_maxY;
}
set {
m_maxY = value;
}
}
public int MaxZ {
get {
return this.m_maxZ;
}
set {
m_maxZ = value;
}
}
public int MinX {
get {
return this.m_minX;
}
set {
m_minX = value;
}
}
public int MinY {
get {
return this.m_minY;
}
set {
m_minY = value;
}
}
public int MinZ {
get {
return this.m_minZ;
}
set {
m_minZ = value;
}
}
public bool Active {
get { return m_active; }
set { m_active = value; }
}
public Vector3 StartPosition {
get { return m_startPos; }
set { m_startPos = value; }
}
public BoidBehaviour Behaviour {
get { return m_behaviour; }
set { m_behaviour = value; }
}
public Vector3 BoidSize {
set { m_boidSize = value; }
}
public FlockingModel( FlowField field, BoidBehaviour behaviour, Vector3 startPos ) {
m_flowField = field;
m_behaviour = behaviour;
m_startPos = startPos;
}
void AddBoid (string name)
{
Boid boid = new Boid (name, m_boidSize, m_behaviour);
double d1 = ( Utils.RandomClass.NextDouble() - 0.5 ) * 20;
double d2 = ( Utils.RandomClass.NextDouble() - 0.5 ) * 20;
double d3 = ( Utils.RandomClass.NextDouble() - 0.5 ) * 20;
boid.Location = m_startPos + new Vector3( (float)d1, (float)d2, (float)d3 );
boid.Velocity = Vector3.UnitX;
m_flock.Add (boid);
}
public bool ContainsPoint (Vector3 position)
{
return m_flowField.ContainsPoint( position );
}
public void Initialise (FlowField flowField)
{
m_flowField = flowField;
for (int i = 0; i < m_flockSize; i++) {
AddBoid ("boid"+i );
//UpdateFlockPos();
}
}
public List<Boid> GetNeighbours(Boid boid) {
return m_flock.FindAll(delegate(Boid other)
{
return (boid != other) && (Utils.GetDistanceTo (boid.Location, other.Location) < m_behaviour.neighbourDistance);
});
}
public List<Boid> UpdateFlockPos ()
{
if( m_active ) {
m_flock.ForEach( delegate(Boid boid) {
boid.MoveInSceneRelativeToFlock(GetNeighbours(boid), m_flowField);
} );
}
return m_flock;
}
public override string ToString ()
{
string retVal = "Num Boids: " + m_flockSize + Environment.NewLine
+ m_behaviour.ToString() + Environment.NewLine
+ m_flowField.ToString();
return retVal;
}
}
}

142
Flocking/FlockingModule.cs Normal file
View File

@ -0,0 +1,142 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Timers;
using System.Collections.Generic;
using OpenMetaverse;
using System.IO;
using Nini.Config;
using System.Threading;
using log4net;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Services.Interfaces;
namespace Flocking
{
public class FlockingModule : INonSharedRegionModule
{
public static string NAME = "FlockingModule";
private bool m_enabled = false;
private int m_chatChannel = 118;
private string m_boidPrim = "boid-prim";
private FlockingController m_controller;
private BoidBehaviour m_behaviour;
private int m_flockSize = 100;
#region IRegionModule Members
public void Initialise (IConfigSource source)
{
//check if we are in the ini files
//if so get some default values out of them and pass them onto the controller
IConfig config = source.Configs ["Boids"];
if (config != null) {
m_chatChannel = config.GetInt ("chat-channel", 118);
m_boidPrim = config.GetString ("boid-prim", "boidPrim");
m_flockSize = config.GetInt ("flock-size", 100);
m_behaviour = new BoidBehaviour ();
m_behaviour.maxSpeed = config.GetFloat ("max-speed", 1f);
m_behaviour.maxForce = config.GetFloat ("max-force", 0.25f);
m_behaviour.neighbourDistance = config.GetFloat ("neighbour-dist", 25f);
m_behaviour.desiredSeparation = config.GetFloat ("desired-separation", 20f);
m_behaviour.tolerance = config.GetFloat ("tolerance", 5f);
m_behaviour.separationWeighting = config.GetFloat ("separation-weighting", 1.5f);
m_behaviour.alignmentWeighting = config.GetFloat ("alignment-weighting", 1f);
m_behaviour.cohesionWeighting = config.GetFloat ("cohesion-weighting", 1f);
m_behaviour.lookaheadDistance = config.GetFloat ("lookahead-dist", 100f);
// we're in the config - so turn on this module
m_enabled = true;
}
}
public void AddRegion (Scene scene)
{
}
public void RegionLoaded (Scene scene)
{
if (m_enabled) {
//set up the boid module
m_controller = new FlockingController (scene, m_behaviour, m_chatChannel, m_boidPrim, m_flockSize);
RegisterCommand (new RoostCommand());
RegisterCommand (new StopCommand());
RegisterCommand (new StartCommand());
RegisterCommand (new SetSizeCommand());
RegisterCommand (new ShowStatsCommand());
RegisterCommand (new SetPrimCommand());
RegisterCommand (new SetPositionCommand());
RegisterCommand (new SetBoundsCommand());
RegisterCommand (new SetFrameRateCommand());
RegisterCommand (new SetParameterCommand());
}
}
public void RegisterCommand (FlockingCommand cmd)
{
m_controller.AddCommand( this, cmd);
}
public void RemoveRegion (Scene scene)
{
if (m_enabled) {
m_controller.Deregister ();
}
}
public string Name {
get { return NAME; }
}
public bool IsSharedModule {
get { return false; }
}
#endregion
#region IRegionModuleBase Members
public void Close ()
{
}
public Type ReplaceableInterface {
get { return null; }
}
#endregion
}
}

View File

@ -1,6 +1,5 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -27,27 +26,27 @@
*/
using System;
using System.Collections.Generic;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using log4net;
using Utils = OpenSim.Framework.Util;
namespace Flocking
{
public class FlockingView
{
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene;
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
private Scene m_scene;
private UUID m_owner;
private String m_name;
private String m_birdPrim;
private String m_boidPrim;
private Dictionary<string, SceneObjectGroup> m_sogMap = new Dictionary<string, SceneObjectGroup> ();
public FlockingView (String moduleName, Scene scene)
public FlockingView (Scene scene)
{
m_name = moduleName;
m_scene = scene;
m_scene = scene;
}
public void PostInitialize (UUID owner)
@ -55,78 +54,71 @@ namespace Flocking
m_owner = owner;
}
public String BirdPrim {
get { return m_birdPrim; }
set{ m_birdPrim = value;}
public string BoidPrim {
get{ return m_boidPrim; }
set{ m_boidPrim = value; }
}
public Scene Scene {
get {return m_scene;}
}
public Vector3 GetBoidSize ()
{
float offsetHeight;
return findByName(m_boidPrim).GetAxisAlignedBoundingBox( out offsetHeight );
}
public void Clear ()
{
//trash everything we have
foreach (string name in m_sogMap.Keys)
{
m_log.InfoFormat("[{0}]: Removing prim {1} from region {2}", m_name, name, m_scene.RegionInfo.RegionName);
SceneObjectGroup sog = m_sogMap[name];
m_scene.DeleteSceneObject(sog, false);
}
List<string> current = new List<string> (m_sogMap.Keys);
current.ForEach (delegate(string name) {
RemoveSOGFromScene(name);
});
m_sogMap.Clear();
m_scene.ForceClientUpdate();
}
public void Render(List<Bird> birds)
public void Render (List<Boid> boids)
{
foreach (Bird bird in birds) {
DrawBird (bird);
}
boids.ForEach(delegate( Boid boid ) {
DrawBoid (boid);
});
}
private void DrawBird (Bird bird)
private void DrawBoid (Boid boid)
{
SceneObjectPart existing = m_scene.GetSceneObjectPart (bird.Id);
SceneObjectPart existing = m_scene.GetSceneObjectPart (boid.Id);
SceneObjectGroup sog;
SceneObjectPart rootPart;
if (existing == null) {
m_log.InfoFormat("[{0}]: Adding prim {1} in region {2}", m_name, bird.Id, m_scene.RegionInfo.RegionName);
SceneObjectGroup group = findByName (m_birdPrim);
sog = CopyPrim (group, bird.Id);
rootPart = sog.RootPart;
rootPart.AddFlag(PrimFlags.Temporary);
rootPart.AddFlag(PrimFlags.Phantom);
//set prim to phantom
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
m_sogMap [bird.Id] = sog;
//m_log.Error( "didnt find " + boid.Id );
SceneObjectGroup group = findByName (m_boidPrim);
sog = CopyPrim (group, boid.Id);
m_sogMap [boid.Id] = sog;
m_scene.AddNewSceneObject (sog, false);
// Fire script on_rez
sog.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 1);
rootPart.ParentGroup.ResumeScripts();
rootPart.ScheduleFullUpdate();
sog.DetachFromBackup();
} else {
sog = existing.ParentGroup;
m_sogMap[bird.Id] = sog;
//rootPart = sog.RootPart;
//set prim to phantom
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
}
Quaternion rotation = CalcRotationToEndpoint (sog, sog.AbsolutePosition, bird.Location);
sog.UpdateGroupRotationPR( bird.Location, rotation);
Quaternion rotation = CalcRotationToEndpoint (sog, boid.Location);
//sog.UpdateGroupRotationPR( boid.Location, rotation);
sog.UpdateGroupPosition( boid.Location );
sog.UpdateGroupRotationR( rotation );
}
private static Quaternion CalcRotationToEndpoint (SceneObjectGroup copy, Vector3 sv, Vector3 ev)
private static Quaternion CalcRotationToEndpoint (SceneObjectGroup sog, Vector3 ev)
{
//llSetRot(llRotBetween(<1,0,0>,llVecNorm(targetPosition - llGetPos())));
// bird wil fly x forwards and Z up
// boid wil fly x forwards and Z up
Vector3 sv = sog.AbsolutePosition;
Vector3 currDirVec = Vector3.UnitX;
Vector3 desiredDirVec = Vector3.Subtract (ev, sv);
desiredDirVec.Normalize ();
Quaternion rot = Vector3.RotationBetween (currDirVec, desiredDirVec);
return rot;
return Vector3.RotationBetween (currDirVec, desiredDirVec);
}
private SceneObjectGroup CopyPrim (SceneObjectGroup prim, string name)
@ -139,18 +131,22 @@ namespace Flocking
private SceneObjectGroup findByName (string name)
{
// SceneObjectGroup retVal = (SceneObjectGroup)m_scene.Entities.Find (delegate( EntityBase e ) {
// return e.Name == name;
//});
SceneObjectGroup retVal = null;
foreach (EntityBase e in m_scene.GetEntities()) {
if (e.Name == name) {
retVal = (SceneObjectGroup)e;
break;
}
}
SceneObjectPart sop = m_scene.GetSceneObjectPart(name);
// 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);
if (sop == null) {
m_log.Error("no " + name);
retVal = MakeDefaultPrim (name);
} else {
retVal = sop.ParentGroup;
}
return retVal;
}
@ -158,9 +154,9 @@ namespace Flocking
private SceneObjectGroup MakeDefaultPrim (string name)
{
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 (128f, 128f, 25f), shape);
prim.Name = name;
prim.DetachFromBackup ();
m_scene.AddNewSceneObject (prim, false);
@ -168,6 +164,15 @@ namespace Flocking
return prim;
}
private void RemoveSOGFromScene(string sogName)
{
SceneObjectGroup sog = m_sogMap[sogName];
m_scene.DeleteSceneObject(sog, false);
//sog.SendGroupFullUpdate();
}
}
}

264
Flocking/FlowField.cs Normal file
View File

@ -0,0 +1,264 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using OpenMetaverse;
using OpenSim.Region.Framework.Scenes;
namespace Flocking
{
public class FlowField
{
private const int BUFFER = 5;
private Scene m_scene;
private float m_startX;
private float m_startY;
private float m_startZ;
private float m_endX;
private float m_endY;
private float m_endZ;
private UUID TERRAIN = UUID.Random ();
private UUID EDGE = UUID.Random ();
private UUID[,,] m_field = new UUID[256, 256, 256]; // field of objects at this position
/// <summary>
/// Initializes a new instance of the <see cref="Flocking.FlowField"/> class.
/// </summary>
/// <param name='scene'>
/// Scene.
/// </param>
/// <param name='centre'>
/// Centre.
/// </param>
/// <param name='width'>
/// Width.
/// </param>
/// <param name='depth'>
/// Depth.
/// </param>
/// <param name='height'>
/// Height.
/// </param>
///
public FlowField (Scene scene, int minX, int maxX, int minY, int maxY, int minZ, int maxZ)
{
m_scene = scene;
m_startX = Math.Max (BUFFER, minX);
m_startY = Math.Max (BUFFER, minY);
m_startZ = Math.Max (BUFFER, minZ);
m_endX = Math.Min (Util.SCENE_SIZE - BUFFER, maxX);
m_endY = Math.Min (Util.SCENE_SIZE - BUFFER, maxY);
m_endZ = Math.Min (Util.SCENE_SIZE - BUFFER, maxZ);
// build the flow field over the given bounds
Initialize ();
}
/// <summary>
/// build a flow field on the scene at the specified centre
/// position in the scene and of extent given by width, depth and height.
/// </summary>
public void Initialize ()
{
//fill in the boundaries
for (int x = 0; x < 256; x++) {
for (int y = 0; y < 256; y++) {
m_field [x, y, 0] = EDGE;
m_field [x, y, 255] = EDGE;
}
}
for (int x = 0; x < 256; x++) {
for (int z = 0; z < 256; z++) {
m_field [x, 0, z] = EDGE;
m_field [x, 255, z] = EDGE;
}
}
for (int y = 0; y < 256; y++) {
for (int z = 0; z < 256; z++) {
m_field [0, y, z] = EDGE;
m_field [255, y, z] = EDGE;
}
}
//fill in the terrain
for (int x = 0; x < 256; x++) {
for (int y = 0; y < 256; y++) {
int zMax = Convert.ToInt32 (m_scene.GetGroundHeight (x, y));
for (int z = 1; z < zMax; z++) {
m_field [x, y, z] = TERRAIN;
}
}
}
foreach (SceneObjectGroup sog in m_scene.Entities.GetAllByType<SceneObjectGroup>()) {
//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);
Vector3 pos = sog.AbsolutePosition;
minX = Convert.ToInt32 (fminX + pos.X);
maxX = Convert.ToInt32 (fmaxX + pos.X);
minY = Convert.ToInt32 (fminY + pos.Y);
maxY = Convert.ToInt32 (fmaxX + pos.Y);
minZ = Convert.ToInt32 (fminZ + pos.Z);
maxZ = Convert.ToInt32 (fmaxZ + pos.Z);
for (int x = minX; x < maxX; x++) {
for (int y = minY; y < maxY; y++) {
for (int z = minZ; z < maxZ; z++) {
if( inBounds(x,y,z) ) {
m_field [x, y, z] = sog.UUID;
} else {
Console.WriteLine(sog.Name + " OOB at " + sog.AbsolutePosition + " -> " + x + " " + y + " " + z);
}
}
}
}
}
}
public bool ContainsPoint (Vector3 p)
{
return p.X > m_startX &&
p.X < m_endX &&
p.Y > m_startY &&
p.Y < m_endY &&
p.Z > m_startZ &&
p.Z < m_endZ;
}
private bool inBounds (int x, int y, int z)
{
return x >= 0 && x < 256 && y >= 0 && y < 256 && z >= 0;
}
#if false
public Vector3 AdjustVelocity (Boid boid, float lookAheadDist)
{
Vector3 normVel = Vector3.Normalize (boid.Velocity);
Vector3 loc = boid.Location;
Vector3 inFront = loc + normVel * lookAheadDist;
Vector3 adjustedDestintation = FieldStrength (loc, boid.Size, inFront);
Vector3 newVel = Vector3.Normalize (adjustedDestintation - loc) * Vector3.Mag (boid.Velocity);
float mOrigVel = Vector3.Mag(boid.Velocity);
float mNewVel = Vector3.Mag(newVel);
if( mNewVel != 0f && mNewVel > mOrigVel ) {
newVel *= mOrigVel / mNewVel;
}
return newVel;
}
#endif
public Vector3 FieldStrength (Vector3 currentPos, Vector3 size, Vector3 targetPos)
{
float length = size.X/2;
float width = size.Y/2;
float height = size.Z/2;
//keep us in bounds
targetPos.X = Math.Min( targetPos.X, m_endX - length );
targetPos.X = Math.Max(targetPos.X, m_startX + length);
targetPos.Y = Math.Min( targetPos.Y, m_endY - width );
targetPos.Y = Math.Max(targetPos.Y, m_startY + width);
targetPos.Z = Math.Min( targetPos.Z, m_endZ - height );
targetPos.Z = Math.Max(targetPos.Z, m_startZ + height);
int count = 0;
//now get the field strength at the inbounds position
UUID collider = LookUp (targetPos);
while (collider != UUID.Zero && count < 100) {
count++;
if (collider == TERRAIN) {
// ground height at currentPos and dest averaged
float h1 = m_scene.GetGroundHeight (currentPos.X, currentPos.Y);
float h2 = m_scene.GetGroundHeight (targetPos.X, targetPos.Y);
float h = (h1 + h2) / 2;
targetPos.Z = h + height;
} else if (collider == EDGE) {
//keep us in bounds
targetPos.X = Math.Min( targetPos.X, m_endX - length );
targetPos.X = Math.Max(targetPos.X, m_startX + length);
targetPos.Y = Math.Min( targetPos.Y, m_endY - width );
targetPos.Y = Math.Max(targetPos.Y, m_startY + width);
targetPos.Z = Math.Min( targetPos.Z, m_endZ - height );
targetPos.Z = Math.Max(targetPos.Z, m_startZ + height);
} else {
//we have hit a SOG
SceneObjectGroup sog = m_scene.GetSceneObjectPart(collider).ParentGroup;
if (sog == null) {
Console.WriteLine (collider);
} else {
float sogMinX, sogMinY, sogMinZ, sogMaxX, sogMaxY, sogMaxZ;
sog.GetAxisAlignedBoundingBoxRaw (out sogMinX, out sogMaxX, out sogMinY, out sogMaxY, out sogMinZ, out sogMaxZ);
Vector3 pos = sog.AbsolutePosition;
//keep us out of the sog
// adjust up/down first if necessary
// then turn left or right
if (targetPos.Z > sogMinZ + pos.Z)
targetPos.Z = (sogMinZ + pos.Z) - height;
if (targetPos.Z < sogMaxZ + pos.Z)
targetPos.Z = (sogMaxZ + pos.Z) + height;
if (targetPos.X > sogMinX + pos.X)
targetPos.X = (sogMinX + pos.X) - length;
if (targetPos.Y > sogMinY + pos.Y)
targetPos.Y = (sogMinY + pos.Y) - width;
if (targetPos.X < sogMaxX + pos.X)
targetPos.X = (sogMaxX + pos.X) + length;
if (targetPos.Y < sogMaxY + pos.Y)
targetPos.Y = (sogMaxY + pos.Y) + width;
}
}
// we what is at the new target position
collider = LookUp (targetPos);
}
return targetPos;
}
public UUID LookUp (Vector3 loc)
{
return m_field [(int)loc.X, (int)loc.Y, (int)loc.Z];
}
public override string ToString ()
{
return string.Format ("[FlowField]" + Environment.NewLine +
"startX = {0}" + Environment.NewLine +
"endX = {1}" + Environment.NewLine +
"startY = {2}" + Environment.NewLine +
"endY = {3}" + Environment.NewLine +
"startZ = {4}" + Environment.NewLine +
"endZ = {5}", m_startX, m_endX, m_startY, m_endY, m_startZ, m_endZ);
}
}
}

View File

@ -1,6 +1,5 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -27,7 +26,6 @@
*/
using System;
using System.Collections.Generic;
using OpenMetaverse;
using OpenSim.Region.Framework.Scenes;
@ -36,63 +34,50 @@ namespace Flocking
public class FlowMap
{
private Scene m_scene;
private uint regionX;
private uint regionY;
private uint regionZ;
private float regionBorder;
private List<FlowEntry> m_flowMap = new List<FlowEntry>();
public FlowMap (Scene scene, int maxHeight, float borderSize)
private float[,,] m_flowMap = new float[256,256,256];
public FlowMap (Scene scene)
{
m_scene = scene;
regionX = m_scene.RegionInfo.RegionSizeX;
regionY = m_scene.RegionInfo.RegionSizeY;
regionZ = (uint)maxHeight;
regionBorder = borderSize;
}
public int LengthX {
get {return (int)regionX;}
get {return 256;}
}
public int LengthY {
get {return (int)regionY;}
get {return 256;}
}
public int LengthZ {
get {return (int)regionZ;}
get {return 256;}
}
public int Border {
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.Add(new FlowEntry(x, y, 0, 100));
m_flowMap.Add(new FlowEntry(x, y, regionZ - 1, 100));
for( int x = 0; x < 256; x++ ) {
for( int y = 0; y < 256; y++ ) {
m_flowMap[x,y,0] = 100f;
m_flowMap[x,y,255] = 100f;
}
}
for( int x = 0; x < regionX; x++ ) {
for( int z = 0; z < regionZ; z++ ) {
m_flowMap.Add(new FlowEntry(x, 0, z, 100));
m_flowMap.Add(new FlowEntry(x, regionY - 1, z, 100));
for( int x = 0; x < 256; x++ ) {
for( int z = 0; z < 256; z++ ) {
m_flowMap[x,0,z] = 100f;
m_flowMap[x,255,z] = 100f;
}
}
for( int y = 0; y < regionY; y++ ) {
for( int z = 0; z < regionZ; z++ ) {
m_flowMap.Add(new FlowEntry(0, y, z, 100));
m_flowMap.Add(new FlowEntry(regionX - 1, y, z, 100));
for( int y = 0; y < 256; y++ ) {
for( int z = 0; z < 256; z++ ) {
m_flowMap[0,y,z] = 100f;
m_flowMap[255,y,z] = 100f;
}
}
//fill in the terrain
for( int x = 0; x < regionX; x++ ) {
for( int y = 0; y < regionY; y++ ) {
for( int x = 0; x < 256; x++ ) {
for( int y = 0; y < 256; y++ ) {
int zMax = Convert.ToInt32(m_scene.GetGroundHeight( x, y ));
for( int z = 1; z < zMax; z++ ) {
if (x < regionX && y < regionY && z < zMax)
m_flowMap.Add(new FlowEntry(x, y, z, 100));
m_flowMap[x,y,z] = 100f;
}
}
}
@ -101,10 +86,8 @@ namespace Flocking
foreach( EntityBase entity in m_scene.GetEntities() ) {
if( entity is SceneObjectGroup ) {
SceneObjectGroup sog = (SceneObjectGroup)entity;
if (sog.IsPhantom)
continue;
//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 );
@ -119,8 +102,7 @@ namespace Flocking
for( int x = minX; x < maxX; x++ ) {
for( int y = minY; y < maxY; y++ ) {
for( int z = minZ; z < maxZ; z++ ) {
if(x < maxX && y < maxY && z < maxX)
m_flowMap.Add(new FlowEntry(x, y, z, 100));
m_flowMap[x,y,z] = 100f;
}
}
}
@ -145,12 +127,12 @@ namespace Flocking
public bool IsOutOfBounds(Vector3 targetPos) {
bool retVal = false;
if( targetPos.X < regionBorder ||
targetPos.X > regionX - regionBorder ||
targetPos.Y < regionBorder ||
targetPos.Y > regionY - regionBorder ||
targetPos.Z < regionBorder ||
targetPos.Z > regionZ - regionBorder ) {
if( targetPos.X < 5f ||
targetPos.X > 250f ||
targetPos.Y < 5f ||
targetPos.Y > 250f ||
targetPos.Z < 5f ||
targetPos.Z > 250f ) {
retVal = true;
}
@ -187,20 +169,11 @@ namespace Flocking
public bool IsWithinObstacle( int x, int y, int z ) {
bool retVal = false;
FlowEntry flow = m_flowMap.Find(X => X.X == x && X.Y == y && X.Z == z);
if (flow == null)
return retVal;
if (x >= LengthX || y >= LengthY || z >= LengthZ)
{
if( x > LengthX || y > LengthY || z > LengthZ ) {
retVal = true;
}
else if (x < 0 || y < 0 || z < 0)
{
} else if( x < 0 || y < 0 || z < 0 ) {
retVal = true;
} else if (flow.Value > 50) {
} else if (m_flowMap[x,y,z] > 50f) {
retVal = true;
}
return retVal;

View File

@ -1,6 +1,5 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
@ -30,8 +29,11 @@ using OpenMetaverse;
namespace Flocking
{
public class BirdsUtil
public class Util
{
public const int SCENE_SIZE = 256;
public static Vector3 Limit (Vector3 initial, float maxLen)
{
float currLen = initial.Length ();

View File

@ -0,0 +1,13 @@
<Addin id="Flocking" version="0.1">
<Runtime>
<Import assembly="Flocking.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/RegionModules">
<RegionModule id="Flocking" type="Flocking.FlockingModule" />
</Extension>
</Addin>

View File

@ -1,373 +0,0 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osbirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using log4net;
using OpenMetaverse;
namespace Flocking
{
public class Bird
{
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
private string m_id;
private Vector3 m_loc;
private Vector3 m_vel;
private Vector3 m_acc;
private Random m_rndnums = new Random (Environment.TickCount);
private FlockingModel m_model;
private FlowMap m_flowMap;
private int m_regionX;
private int m_regionY;
private int m_regionZ;
private float m_regionBorder;
/// <summary>
/// Initializes a new instance of the <see cref="Flocking.Bird"/> class.
/// </summary>
/// <param name='l'>
/// L. the initial position of this bird
/// </param>
/// <param name='ms'>
/// Ms. max speed this bird can attain
/// </param>
/// <param name='mf'>
/// Mf. max force / acceleration this bird can extert
/// </param>
public Bird (string id, FlockingModel model, FlowMap flowMap)
{
m_id = id;
m_acc = Vector3.Zero;
m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1));
m_model = model;
m_flowMap = flowMap;
m_regionX = m_flowMap.LengthX;
m_regionY = m_flowMap.LengthY;
m_regionZ = m_flowMap.LengthZ;
m_regionBorder = m_flowMap.Border;
}
public Vector3 Location {
get { return m_loc;}
set { m_loc = value; }
}
public Vector3 Velocity {
get { return m_vel;}
}
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
AvoidObstacles ();
// then we want to avoid any threats
// this not implemented yet
// ok so we worked our where we want to go, so ...
UpdatePositionInScene ();
}
/// <summary>
/// Move within our flock
///
/// 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>
/// 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)
m_vel.Y = -m_vel.Y;
if (m_loc.Z < 21 || m_loc.Z > m_regionZ )
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
if (steer.Length () > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.Normalize ();
steer *= m_model.MaxSpeed;
steer -= m_vel;
//steer.limit(maxforce);
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
}
return steer;
}
/// <summary>
/// Align our bird within the flock.
/// For every nearby bird in the system, calculate the average velocity
/// and move us towards that - this keeps us moving with the flock.
/// </summary>
/// <param name='birds'>
/// Birds. all the birds in the scene - we only really care about those in the neighbourdist
/// </param>
Vector3 Align (List<Bird> birds)
{
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
if (steer.Length () > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.Normalize ();
steer *= m_model.MaxSpeed;
steer -= m_vel;
//steer.limit(maxforce);
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
}
return steer;
}
/// <summary>
/// MAintain the cohesion of our local flock
/// For the average location (i.e. center) of all nearby birds, calculate our steering vector towards that location
/// </summary>
/// <param name='birds'>
/// Birds. the birds in the scene
/// </param>
Vector3 Cohesion (List<Bird> birds)
{
Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations
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

@ -1,135 +0,0 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using OpenMetaverse;
namespace Flocking
{
public class FlockingModel
{
private List<Bird> m_flock = new List<Bird>();
private FlowMap m_flowMap;
private float m_maxSpeed;
private float m_maxForce;
private float m_neighbourDistance;
private float m_desiredSeparation;
private float m_tolerance;
private float m_border;
private string m_name;
private Random m_rnd = new Random(Environment.TickCount);
public int Size {
get {return m_flock.Count;}
set {
//if( value < m_flock.Count ) {
// m_flock.RemoveRange( 0, m_flock.Count - value );
//} else
m_flock = new List<Bird>();
while( value > 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) {
m_name = moduleName;
m_maxSpeed = maxSpeed;
m_maxForce = maxForce;
m_neighbourDistance = neighbourDistance;
m_desiredSeparation = desiredSeparation;
m_tolerance = tolerance;
m_border = border;
}
void AddBird (string name)
{
Bird Bird = new Bird (name, this, m_flowMap);
// find an initial random location for this Bird
// somewhere not within an obstacle
int xInit = m_rnd.Next(m_flowMap.LengthX);
int yInit = m_rnd.Next(m_flowMap.LengthY);
int zInit = m_rnd.Next(m_flowMap.LengthZ);
while( m_flowMap.IsWithinObstacle( xInit, yInit, zInit ) ){
xInit = m_rnd.Next(m_flowMap.LengthX);
yInit = m_rnd.Next(m_flowMap.LengthY);
zInit = m_rnd.Next(m_flowMap.LengthZ);
}
Bird.Location = new Vector3 (Convert.ToSingle(xInit), Convert.ToSingle(yInit), Convert.ToSingle(zInit));
m_flock.Add (Bird);
}
public float MaxSpeed {
get {return m_maxSpeed;}
set { m_maxSpeed = value; }
}
public float MaxForce {
get {return m_maxForce;}
set { m_maxForce = value; }
}
public float NeighbourDistance {
get {return m_neighbourDistance;}
set { m_neighbourDistance = value; }
}
public float DesiredSeparation {
get {return m_desiredSeparation;}
set { m_desiredSeparation = value; }
}
public float Tolerance {
get {return m_tolerance;}
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 ()
{
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

@ -1,648 +0,0 @@
/*
* Copyright (c) Contributors, https://github.com/jonc/osboids
* https://github.com/JakDaniels/OpenSimBirds
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Timers;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using OpenMetaverse;
using Nini.Config;
using System.Threading;
using log4net;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using Mono.Addins;
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
[assembly: Addin("OpenSimBirds", "0.2")]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
namespace Flocking
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OpenSimBirds")]
public class FlockingModule : INonSharedRegionModule
{
#region Fields
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
public string m_name = "OpenSimBirds";
private string m_regionConfigDir = "";
private Scene m_scene;
private ICommandConsole m_console;
private FlockingModel m_model;
private FlockingView m_view;
private bool m_startup = true;
private bool m_enabled = false;
private bool m_ready = false;
private uint m_frame = 0;
private int m_frameUpdateRate = 1;
private int m_chatChannel = 118;
private string m_birdPrim;
private int m_flockSize = 50;
private int m_maxFlockSize = 100;
private float m_maxSpeed;
private float m_maxForce;
private float m_neighbourDistance;
private float m_desiredSeparation;
private float m_tolerance;
private float m_borderSize;
private int m_maxHeight;
private Vector3 m_shoutPos = new Vector3(128f, 128f, 30f);
static object m_sync = new object();
private List<UUID> m_allowedControllers = new List<UUID>();
public IConfigSource m_config;
private UUID m_owner;
#endregion
#region IRegionModuleBase implementation
public string Name { get { return m_name; } }
public Type ReplaceableInterface { get { return null; } }
public bool IsSharedModule { get { return false; } }
public void Initialise (IConfigSource source)
{
m_config = source;
}
public void AddRegion (Scene scene)
{
IConfig cnf;
m_log.InfoFormat("[{0}]: Adding region {1} to this module", m_name, scene.RegionInfo.RegionName);
cnf = m_config.Configs["Startup"];
m_regionConfigDir = cnf.GetString("regionload_regionsdir", Path.Combine(Util.configDir(), "bin/Regions/"));
cnf = m_config.Configs[scene.RegionInfo.RegionName];
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);
//string moduleConfigFile = Path.Combine(Util.configDir(),m_name + ".ini");
string moduleConfigFile = Path.Combine(m_regionConfigDir, "Regions.ini");
try
{
m_log.InfoFormat("[{0}]: Checking {1} for [{2}] section containing valid config keys", m_name, moduleConfigFile, scene.RegionInfo.RegionName);
m_config = new IniConfigSource(moduleConfigFile);
cnf = m_config.Configs[scene.RegionInfo.RegionName];
}
catch (Exception)
{
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_enabled = false;
return;
}
}
m_startup = cnf.GetBoolean("BirdsModuleStartup", true);
if (m_startup)
{
m_scene = scene;
m_enabled = cnf.GetBoolean("BirdsEnabled", false);
m_chatChannel = cnf.GetInt("BirdsChatChannel", 118);
m_birdPrim = cnf.GetString("BirdsPrim", "birdPrim");
m_flockSize = cnf.GetInt("BirdsFlockSize", 20);
m_maxFlockSize = cnf.GetInt("BirdsMaxFlockSize", 100);
m_maxSpeed = cnf.GetFloat("BirdsMaxSpeed", 1.5f);
m_maxForce = cnf.GetFloat("BirdsMaxForce", 0.2f);
m_neighbourDistance = cnf.GetFloat("BirdsNeighbourDistance", 25f);
m_desiredSeparation = cnf.GetFloat("BirdsDesiredSeparation", 10f);
m_tolerance = cnf.GetFloat("BirdsTolerance", 5f);
m_borderSize = cnf.GetFloat("BirdsRegionBorderSize", 5f);
m_maxHeight = cnf.GetInt("BirdsMaxHeight", 75);
m_frameUpdateRate = cnf.GetInt("BirdsUpdateEveryNFrames", 1);
string allowedControllers = cnf.GetString("BirdsAllowedControllers", UUID.Zero.ToString());
if (allowedControllers != UUID.Zero.ToString())
{
string[] ac = allowedControllers.Split(new char[] { ',' });
UUID acUUID;
for (int i = 0; i < ac.Length; i++)
{
string value = ac[i].Trim();
if (value == "ESTATE_OWNER")
{
UUID eoUUID = m_scene.RegionInfo.EstateSettings.EstateOwner;
m_allowedControllers.Add(eoUUID);
m_log.InfoFormat("[{0}] Added Estate Owner UUID: {1} to list of allowed users", m_name, eoUUID.ToString());
continue;
}
if (value == "ESTATE_MANAGER")
{
foreach (UUID emUUID in m_scene.RegionInfo.EstateSettings.EstateManagers)
{
m_allowedControllers.Add(emUUID);
m_log.InfoFormat("[{0}] Added Estate Manager UUID: {1} to list of allowed users", m_name, emUUID.ToString());
}
continue;
}
if (UUID.TryParse(ac[i].Trim(), out acUUID))
{
m_allowedControllers.Add(acUUID);
m_log.InfoFormat("[{0}] Added UUID: {1} to list of allowed users", m_name, acUUID.ToString());
}
}
}
else
{
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_console = MainConsole.Instance;
//register commands with the scene
RegisterCommands();
//register handlers
m_scene.EventManager.OnFrame += FlockUpdate;
m_scene.EventManager.OnChatFromClient += SimChatSent; //listen for commands sent from the client
m_scene.EventManager.OnChatFromWorld += SimChatSent;
m_scene.EventManager.OnPrimsLoaded += PrimsLoaded;
// init module
m_model = new FlockingModel(m_name, m_maxSpeed, m_maxForce, m_neighbourDistance, m_desiredSeparation, m_tolerance, m_borderSize);
m_view = new FlockingView(m_name, m_scene);
m_view.BirdPrim = m_birdPrim;
m_frame = 0;
m_shoutPos = new Vector3(scene.RegionInfo.RegionSizeX / 2f, scene.RegionInfo.RegionSizeY / 2f, 30f);
FlockInitialise();
}
else m_log.InfoFormat("[{0}] Module is disabled in Region {1}", m_name, scene.RegionInfo.RegionName);
}
public void RegionLoaded (Scene scene)
{
if (m_startup)
{
// Mark Module Ready for duty
m_ready = true;
}
}
public void PrimsLoaded(Scene scene)
{
m_scene = scene;
ClearPersisted();
}
public void RemoveRegion (Scene scene)
{
m_log.InfoFormat("[{0}]: Removing region {1} from this module", m_name, scene.RegionInfo.RegionName);
if (m_startup) {
m_view.Clear();
scene.EventManager.OnFrame -= FlockUpdate;
scene.EventManager.OnChatFromClient -= SimChatSent;
scene.EventManager.OnChatFromWorld -= SimChatSent;
scene.EventManager.OnPrimsLoaded -= PrimsLoaded;
m_ready = false;
}
}
public void Close()
{
if (m_startup)
{
m_scene.EventManager.OnFrame -= FlockUpdate;
m_scene.EventManager.OnChatFromClient -= SimChatSent;
m_scene.EventManager.OnChatFromWorld -= SimChatSent;
m_scene.EventManager.OnPrimsLoaded -= PrimsLoaded;
m_ready = false;
}
}
#endregion
#region Helpers
public void FlockInitialise()
{
//make a flow map for this scene
FlowMap flowMap = new FlowMap(m_scene, m_maxHeight, m_borderSize);
flowMap.Initialise();
// Generate initial flock values
m_model.Initialise(m_flockSize, flowMap);
// who is the owner for the flock in this region
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
m_view.PostInitialize(m_owner);
}
public void ClearPersisted()
{
//really trash all possible birds that may have been persisted at last shutdown
int i;
for (i = m_flockSize; i < m_maxFlockSize; i++)
{
foreach (EntityBase e in m_scene.GetEntities())
{
if (e.Name == (string)(m_name + i))
{
m_log.InfoFormat("[{0}]: Removing old persisted prim {1} from region {2}", m_name, m_name + i, m_scene.RegionInfo.RegionName);
m_scene.DeleteSceneObject((SceneObjectGroup)e, false);
break;
}
}
}
m_scene.ForceClientUpdate();
}
#endregion
#region EventHandlers
public void FlockUpdate ()
{
if (!m_ready || !m_enabled || ((m_frame++ % m_frameUpdateRate) != 0))
{
return;
}
//m_log.InfoFormat("update my birds");
// work out where everyone has moved to
// and tell the scene to render the new positions
lock( m_sync ) {
List<Bird > birds = m_model.UpdateFlockPos ();
m_view.Render (birds);
}
}
protected void SimChatSent (Object x, OSChatMessage msg)
{
if (msg.Channel != m_chatChannel) return; // not for us
if (m_allowedControllers.Count > 0)
{
bool reject = true;
if (msg.SenderObject != null)
{
UUID ooUUID = ((SceneObjectPart)msg.SenderObject).OwnerID;
//m_log.InfoFormat("[{0}]: Message from object {1} with OwnerID: {2}", m_name, msg.SenderUUID, ooUUID);
if (m_allowedControllers.Contains(ooUUID)) reject = false;
}
if (m_allowedControllers.Contains(msg.SenderUUID)) reject = false;
if (reject) return; //not for us
}
// try and parse a valid cmd from this msg
string cmd = msg.Message; //.ToLower ();
//stick ui in the args so we know to respond in world
//bit of a hack - but lets us use CommandDelegate inWorld
string[] args = (cmd + " <ui>").Split (" ".ToCharArray ());
if (cmd.StartsWith ("stop")) {
HandleStopCmd (m_name, args);
} else if (cmd.StartsWith ("start")) {
HandleStartCmd (m_name, args);
} else if (cmd.StartsWith("enable")) {
HandleEnableCmd(m_name, args);
} else if (cmd.StartsWith("disable")) {
HandleDisableCmd(m_name, args);
} else if (cmd.StartsWith ("size")) {
HandleSetSizeCmd (m_name, args);
} else if (cmd.StartsWith ("stats")) {
HandleShowStatsCmd (m_name, args);
} else if (cmd.StartsWith ("prim")) {
HandleSetPrimCmd (m_name, args);
} else if (cmd.StartsWith("speed")) {
HandleSetMaxSpeedCmd(m_name, args);
} else if (cmd.StartsWith("force")) {
HandleSetMaxForceCmd(m_name, args);
} else if (cmd.StartsWith("distance")) {
HandleSetNeighbourDistanceCmd(m_name, args);
} else if (cmd.StartsWith("separation")) {
HandleSetDesiredSeparationCmd(m_name, args);
} else if (cmd.StartsWith("tolerance")) {
HandleSetToleranceCmd(m_name, args);
} else if (cmd.StartsWith ("framerate")) {
HandleSetFrameRateCmd (m_name, args);
}
}
#endregion
#region Command Handling
private void AddCommand (string cmd, string args, string help, CommandDelegate fn)
{
string argStr = "";
if (args.Trim ().Length > 0) {
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_scene.AddCommand (this, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
m_console.Commands.AddCommand(m_name, false, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
}
private void RegisterCommands ()
{
AddCommand ("stop", "", "Stop Birds Flocking", HandleStopCmd);
AddCommand ("start", "", "Start Birds Flocking", HandleStartCmd);
AddCommand ("enable", "", "Enable Birds Flocking", HandleEnableCmd);
AddCommand ("disable", "", "Disable Birds Flocking", HandleDisableCmd);
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 ("speed", "num", "Set the maximum velocity each bird may achieve", HandleSetMaxSpeedCmd);
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("separation", "num", "How far away from other birds we would like to stay", HandleSetDesiredSeparationCmd);
AddCommand("tolerance", "num", "How close to the edges of things can we get without being worried", HandleSetToleranceCmd);
AddCommand("stats", "", "Show birds stats", HandleShowStatsCmd);
AddCommand("framerate", "num", "[debugging] only update birds every <num> frames", HandleSetFrameRateCmd);
}
private void RegisterScriptFunctions()
{
IScriptModuleComms comms = m_scene.RequestModuleInterface<IScriptModuleComms>();
if (comms != null)
{
comms.RegisterScriptInvocation(this, "birdsGetStats");
}
}
private string birdsGetStats(UUID host, UUID script, string stat)
{
return ""; //currently a placeholder
}
private bool ShouldHandleCmd ()
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) {
return false;
}
return true;
}
private bool IsInWorldCmd (ref string [] args)
{
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);
return true;
}
return false;
}
private void ShowResponse (string response, bool inWorld)
{
if (inWorld) {
//IClientAPI ownerAPI = null;
//if (m_scene.TryGetClient (m_owner, out ownerAPI)) {
// ownerAPI.SendBlueBoxMessage (m_owner, "Birds", response);
//}
SendSimChat(response, m_chatChannel);
} else {
MainConsole.Instance.Output (response);
}
}
private void SendSimChat(string msg, int channel)
{
m_scene.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Region, channel, m_shoutPos, m_name, UUID.Zero, false);
}
public void HandleDisableCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd ()) {
m_log.InfoFormat("[{0}]: Bird flocking is disabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = false;
//m_ready = false;
m_view.Clear();
}
}
public void HandleEnableCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
m_log.InfoFormat("[{0}]: Bird flocking is enabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = true;
//m_ready = true;
}
}
public void HandleStopCmd (string module, string[] args)
{
if (m_enabled && m_ready && ShouldHandleCmd())
{
m_log.InfoFormat("[{0}]: Bird flocking is stopped in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = false;
}
}
public void HandleStartCmd(string module, string[] args)
{
if (!m_enabled && m_ready && ShouldHandleCmd())
{
m_log.InfoFormat("[{0}]: Bird flocking is started in region {1}.", m_name, m_scene.RegionInfo.RegionName);
m_enabled = true;
FlockUpdate();
}
}
void HandleSetFrameRateCmd (string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
int frameRate = Convert.ToInt32( args[1] );
m_frameUpdateRate = frameRate;
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)
{
if (m_ready && ShouldHandleCmd())
{
lock( m_sync ) {
int newSize = Convert.ToInt32(args[1]);
if (newSize > m_maxFlockSize) newSize = m_maxFlockSize;
m_view.Clear();
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);
}
}
}
public void HandleShowStatsCmd (string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
bool inWorld = IsInWorldCmd (ref args);
int i;
int s=m_model.Size;
UUID primUuid;
List<ScenePresence> avatarsSitList = new List<ScenePresence>();
string avUuids;
if (inWorld)
{
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_ready ? "True" : "False"), inWorld);
ShowResponse("birds-prim = " + m_view.BirdPrim, inWorld);
ShowResponse("birds-framerate = " + m_frameUpdateRate, inWorld);
ShowResponse("birds-maxsize = " + m_maxFlockSize, inWorld);
ShowResponse("birds-size = " + s, inWorld);
ShowResponse("birds-speed = " + m_model.MaxSpeed, inWorld);
ShowResponse("birds-force = " + m_model.MaxForce, inWorld);
ShowResponse("birds-distance = " + m_model.NeighbourDistance, inWorld);
ShowResponse("birds-separation = " + m_model.DesiredSeparation, inWorld);
ShowResponse("birds-tolerance = " + m_model.Tolerance, inWorld);
ShowResponse("birds-border = " + m_borderSize, inWorld);
for (i = 0; i < s; i++)
{
primUuid = UUID.Zero;
avatarsSitList.Clear();
avUuids = "";
foreach (EntityBase e in m_scene.GetEntities())
{
if (e.Name == m_name + i)
{
SceneObjectGroup sog = (SceneObjectGroup)e;
SceneObjectPart rootPart = sog.RootPart;
primUuid = rootPart.UUID;
avatarsSitList = sog.GetSittingAvatars();
foreach (ScenePresence av in avatarsSitList)
{
avUuids += av.UUID.ToString() + " ";
}
break;
}
}
ShowResponse("birds-prim" + i + " = " + m_name + i + " : " + primUuid.ToString() + " : " + avUuids.Trim(), inWorld);
}
}
}
public void HandleSetPrimCmd (string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
string primName = args[1];
lock(m_sync) {
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_view.Clear();
}
}
}
public void HandleSetMaxSpeedCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float maxSpeed = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.MaxSpeed = maxSpeed;
m_log.InfoFormat("[{0}]: Birds maximum speed is set to {1} in region {2}.", m_name, maxSpeed, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetMaxForceCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float maxForce = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.MaxForce = maxForce;
m_log.InfoFormat("[{0}]: Birds maximum force is set to {1} in region {2}.", m_name, maxForce, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetNeighbourDistanceCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float neighbourDistance = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.NeighbourDistance = neighbourDistance;
m_log.InfoFormat("[{0}]: Birds neighbour distance is set to {1} in region {2}.", m_name, neighbourDistance, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetDesiredSeparationCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float desiredSeparation = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.DesiredSeparation = desiredSeparation;
m_log.InfoFormat("[{0}]: Birds desired separation is set to {1} in region {2}.", m_name, desiredSeparation, m_scene.RegionInfo.RegionName);
}
}
}
public void HandleSetToleranceCmd(string module, string[] args)
{
if (m_ready && ShouldHandleCmd())
{
float tolerance = (float)Convert.ToDecimal(args[1]);
lock (m_sync)
{
m_model.Tolerance = tolerance;
m_log.InfoFormat("[{0}]: Birds tolerance is set to {1} in region {2}.", m_name, tolerance, m_scene.RegionInfo.RegionName);
}
}
}
#endregion
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flocking
{
class FlowEntry
{
public float X = 0;
public float Y = 0;
public float Z = 0;
public float Value = 0;
public FlowEntry(float x, float y, float z, float value)
{
X = x;
Y = y;
Z = z;
Value = value;
}
}
}

103
README Normal file
View File

@ -0,0 +1,103 @@
Region Module - ability to control flocks within an OpenSim scene
This branch tracks the OpenSim 0.7.1.1 release
To build from source
Add osboids source tree under opensim/addon-modules
./runprebuild.sh against opensim root to build this module into the solution
then xbuild, or build within Visual Studio / Monodevelop to produce the binaries
osboids has no external dependencies other than the dlls currently included in opensim.
The project generates a single dll - Flocking.dll which is copied into opensim/bin as part of the build step
Configuration
To become active, the module needs to be both referenced and enabled in the ini files. Otherwise it does nothing on startup
Entry is as follows:
[Boids]
enabled = true
removing the Boids group or setting enabled = false will switch off the module
In addition various config parameters are available to control the flock dynamics
flock-size = 100 the number of Boids to flock
By default the module will create a flock of plain wooden spheres, however this can be overridden
boid-prim = fish01 names the prim to use to represent each boid in the flock
currently this prim needs to already exist in the scene - i.e. be rezzed in the region.
Eventually this will be an inventory lookup
Various runtime commands control the flocking module behaviour - described below. These can either be invoked
from the Console or in world by directing them to a chat channel. To specify which channel to use:
chat-channel = 118 the chat channel to listen for boid commands on
Runtime Commands
The following commands, which can either be issued on the Console, or via a chat channel in-world, control the behaviour
of the flock at runtime
flock-stop or /118 stop in chat - stop all flocking and remove boids from the region
flock-start or /118 start - start the flocking simulation
flock-size <num> or /118 size <num> - change the size of the flock
flock-prim <name> or /118 prim <name> - change the boid prim to that passed in - must be rezzed in the scene
flock-framerate <num> or /118 framerate - only update the flock positions every <num> frames - only really useful
- for photography and debugging boid behaviour
Additionally various parameters of the flocking algorithms can be set at runtime, these are configured using the
flock-set <name> <value> or /118 set <name> <value> commands
The following parameters can be set - current defaults are shown below
max-speed = 3 how far each boid can travel per update
max-force = 0.25 the maximum acceleration allowed to the current velocity of the boid
neighbour-distance = 25 max distance for other boids to be considered in the same flock as us
desired-separation = 20 how far away from other boids we would like to stay
tolerance = 5 how close to the edges of objects or the scene should our boid get
separation-weighting = 1.5 factor by which closeness to other boids should be favoured when updating position in flock
alignment-weighting = 1 factor by which alignment with other boids should be favoured when updating position in flock
cohesion-weighting = 1 factor by which keeping within the local flock should be favoured when updating position in flock
lookahead-distance = 10 how far in front should the boid look for edges and boundaries
Boidprims
Any, currently rezzed in scene, object can be used as the boid prim. However fps is very much affected by the
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.
Tests show that a few hundred single prims can be flocked effectively - depending on system and network
I intend to allow inventory items and UUIDs to represent the boids - this is not written yet however.
Prebuilt binaries etc.. proved in project root
Status
probably made it to alpha by now ...
Next Steps
I want to improve the ability of the boids to avoid obstacles within the scene. Current avoidance is pretty basic, and
only functions correctly about fifty percent of the time. Need to improve this without increasing computational cost.
Licence: all files released under a BSD licence

196
README.md
View File

@ -1,196 +0,0 @@
INonSharedRegion Module - ability to control flocks of prims within an OpenSim scene.
To build from source:
Add OpenSimBirds source tree under opensim/addon-modules
e.g.
cd ~/opensim/addon-modules
git clone https://github.com/JakDaniels/OpenSimBirds.git
./runprebuild.sh against opensim root to build this module into the solution
then xbuild, or build within Visual Studio / Monodevelop to produce the binaries.
OpenSimBirds does not need a config in order to run, but without one it will not persist your bird flock settings from one
region restart to another!
The settings go in an .ini file in bin/addon-modules/OpenSimBirds/config/ *or* in your existing Regions.ini
OpenSimBirds has no external dependencies other than the dlls currently included in opensim.
The project generates a single dll - OpenSimBirds.Module.dll which is copied into opensim/bin as part of the build step
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.
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
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
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.
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
setting in Regions.ini
The configuration for this module can be placed in one of two places:
Option 1:
bin/addon-modules/OpenSimBirds/config/OpenSimBirds.ini
This follows the similar format as a Regions.ini file, where you specify setting for
each region using the [Region Name] section heading. There is an example .ini file
provided which should be edited and copied to the correct place above.
Option 2:
bin/Regions/Regions.ini
Add config parameters to the existing Regions.ini file under the appropriate region name heading.
The file is usually in the above location, but this could have been changed in OpenSim.ini via the
'regionload_regionsdir' parameter in the [Startup] section of OpenSim.ini
Here is an example config:
;; Set the Birds settings per named region
[Test Region 1]
BirdsModuleStartup = 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
BirdsFlockSize = 50 ;the number of birds to flock
BirdsMaxFlockSize = 100 ;the maximum flock size that can be created (keeps things sane)
BirdsMaxSpeed = 3 ;how far each bird can travel per update
BirdsMaxForce = 0.25 ;the maximum acceleration allowed to the current velocity of the bird
BirdsNeighbourDistance = 25 ;max distance for other birds to be considered in the same flock as us
BirdsDesiredSeparation = 20 ;how far away from other birds we would like to stay
BirdsTolerance = 5 ;how close to the edges of things can we get without being worried
BirdsBorderSize = 5 ;how close to the edge of a region can we get?
BirdsMaxHeight = 256 ;how high are we allowed to flock
BirdsUpdateEveryNFrames = 1 ;update bird positions every N simulator frames
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
;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
;or everyone if not specified
BirdsAllowedControllers = ESTATE_OWNER, ESTATE_MANAGER, 12345678-1234-1234-1234-123456789abc
Various runtime commands control the flocking module behaviour - described below. These can either be invoked
from the Console or in world by directing them to a chat channel either from the client's Local Chat or via a script.
You can specify which channel to use in the .ini:
BirdsChatChannel = 118 ;the chat channel to listen for Bird commands on
Runtime Commands:
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:
birds-stop or /118 stop ;stop 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-disable or /118 disable ;stop all birds and remove them from the scene
birds-prim <name> or /118 prim <name> ;change the bird prim to a prim already rezzed in the scene
birds-stats or /118 stats ;show all the parameters and list the bird prim names and uuids
birds-framerate <num> or /118 framerate <num> ;only update the flock positions every <num> frames
;only really useful for photography and debugging bird
;behaviour
These commands are great for playing with the flock dynamics in real time:
birds-size <num> or /118 size <num> ;change the size of the flock
birds-speed <num> or /118 speed <num> ;change the maximum velocity each bird may achieve
birds-force <num> or /118 force <num> ;change the maximum force each bird may accelerate
birds-distance <num> or /118 distance <num> ;change the maximum distance that other birds are to be considered in the same flock as us
birds-separation <num> or /118 separation <num> ;sets how far away from other birds we would like to stay
birds-tolerance <num> or /118 tolerance <num> ;sets how close to the edges of things can we get without being worried
Of course if distance is less than separation then the birds will never flock. The other way around and they will always
eventually form one or more flocks.
Security:
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
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
(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 a script it is the UUID of the owner of the prim in which the script resides that is checked against the list.
Bird prims:
Any currently rezzed in-scene-object can be used as the bird prim. However fps is very much affected by the
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.
Tests show that <= 500 single prims can be flocked effectively - depending on system and network
However maybe <= 300 simple linksets can perform as well.
Network Traffic:
I tested the amount of network traffic generated by bird updates. 20 birds (each with 4 linked prims) takes up about 300kbps
in network position updates. 50 of the same birds generates about 750kbps traffic.
Each bird uses roughly 15kbps of network traffic. This is all measured using an update framerate of 1, i.e. birds' position
is updated every simulator frame.
Statistics:
The stats command in-world or via script returns data to BirdsChatChannel. The console command returns stats to the console.
All the the modules parameters are returned including a list of the active bird prims currently rezzed in the region,
and the UUIDs of those prims' root prim. Also included is a list of any avatar UUIDs that may be sitting on those prims. Here
is an example output:
birds-started = False
birds-enabled = True
birds-prim = SeaGull1
birds-framerate = 1
birds-maxsize = 100
birds-size = 20
birds-speed = 1.5
birds-force = 0.2
birds-distance = 25
birds-separation = 10
birds-tolerance = 5
birds-border = 5
birds-prim0 = OpenSimBirds0 : 01abef79-7fb2-4c8d-831e-62ce1ce878f1 :
birds-prim1 = OpenSimBirds1 : af85996d-af4d-4dda-bc89-721c51e09d0c :
birds-prim2 = OpenSimBirds2 : ca766390-1877-4b19-a29e-4590cf40aece :
birds-prim3 = OpenSimBirds3 : 6694bfa9-8e7f-4ac5-b336-ad13e5cfced2 :
birds-prim4 = OpenSimBirds4 : 1c6b152d-dcca-4fef-8979-b7ccc8139e1e :
birds-prim5 = OpenSimBirds5 : 08bba2cc-d427-4855-a7f0-57aa55109707 :
birds-prim6 = OpenSimBirds6 : bbeb8b6d-28d8-41a9-b8ce-dab3173bd454 :
birds-prim7 = OpenSimBirds7 : 45c73475-1f0f-487f-ac9f-87d30d0315e8 :
birds-prim8 = OpenSimBirds8 : d5891cc8-c196-4b05-82ef-3c7d0f703963 :
birds-prim9 = OpenSimBirds9 : 557b61e1-5fd6-4878-980e-e93cabcc078f :
birds-prim10 = OpenSimBirds10 : 7ff2c02d-d73c-4e49-a4e9-84b652dc70a9 :
birds-prim11 = OpenSimBirds11 : c2b0820c-ba20-4318-a0e8-ec6ad521f524 :
birds-prim12 = OpenSimBirds12 : e8e87309-7a47-4983-89a1-4bb11d05a40c :
birds-prim13 = OpenSimBirds13 : a351e0e3-ae99-48b8-877d-65156f437b33 :
birds-prim14 = OpenSimBirds14 : 150f1c3b-e9d9-4cda-9e03-69fb5286e436 :
birds-prim15 = OpenSimBirds15 : ebf63de1-d419-45d0-8eee-3db14295e401 :
birds-prim16 = OpenSimBirds16 : faad97af-4ee6-425c-b221-99ef53650e93 :
birds-prim17 = OpenSimBirds17 : d75ba544-bbc2-4f5a-9d7e-00e21ed6f191 :
birds-prim18 = OpenSimBirds18 : b91e42cb-ae5b-4f03-bf6e-dc03d52858b7 : 19cc284d-11c6-4ee7-a8de-f69788d08434
birds-prim19 = OpenSimBirds19 : 44aa3e14-56bc-43dd-afbd-7348c5dfe3a5 :
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:
This module is currently only tested against opensim master.
Licence: all files released under a BSD licence
If you have any question please contact Jak Daniels, jak@ateb.co.uk
If you do fork or modify this project for the better, please let me know!! I would be interested to incorporate any enhancements you may make.

28
TODO.txt Normal file
View File

@ -0,0 +1,28 @@
TODO:
[done] Expose Settings to ini Files
[done] Write in world Chat controller to allow maintenance of flocking in world.
[done - but basic] Handle Borders Correctly
[done - but basic] Handle Collision Avoidance
Replace above with a proper flow field implementation
Handle Wander
Handle Perching
Handle Predator Avoidance
Optimise it all...
Look at maintaining flocks across sim boundaries.
Look at exposing flocking as a service to scripts

Binary file not shown.

View File

@ -1,22 +0,0 @@
;; Set the Birds settings per named region
[Test Region 2]
BirdsModuleStartup = 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
BirdsChatChannel = 118 ;which channel do we listen on for in world commands
BirdsFlockSize = 20 ;the number of birds to flock
BirdsMaxSpeed = 1.5 ;how far each bird can travel per update
BirdsMaxForce = 0.20 ;the maximum acceleration allowed to the current velocity of the bird
BirdsNeighbourDistance = 25 ;max distance for other birds to be considered in the same flock as us
BirdsDesiredSeparation = 10 ;how far away from other birds we would like to stay
BirdsTolerance = 5 ;how close to the edges of things can we get without being worried
BirdsBorderSize = 5 ;how close to the edge of a region can we get?
BirdsMaxHeight = 75 ;how high are we allowed to flock
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
;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
;or everyone if not specified
BirdsAllowedControllers = ESTATE_OWNER, ESTATE_MANAGER, 12345678-1234-1234-1234-123456789abc

View File

@ -1,33 +1,37 @@
<?xml version="1.0" ?>
<Project frameworkVersion="v4_0" name="OpenSimBirds.Module" path="addon-modules/OpenSimBirds/Module" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Core osboids Project -->
<ReferencePath>../../../bin/</ReferencePath>
<Reference name="System"/>
<Reference name="System.Net"/>
<Reference name="OpenMetaverse"/>
<Reference name="OpenMetaverseTypes"/>
<Reference name="OpenMetaverse.StructuredData"/>
<Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Framework.Communications"/>
<Reference name="OpenSim.Framework.Console"/>
<Reference name="OpenSim.Region.Framework"/>
<Reference name="OpenSim.Region.ScriptEngine.Shared"/>
<Reference name="Nini"/>
<Reference name="log4net"/>
<Reference name="Mono.Addins"/>
<Project frameworkVersion="v3_5" name="Flocking" path="addon-modules/osboids/Flocking/" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<ReferencePath>../../../bin/</ReferencePath>
<Reference name="log4net" path="../../../bin/" />
<Reference name="System"/>
<Reference name="System.Drawing" />
<Reference name="System.Xml"/>
<Reference name="Nini" path="../../../bin/" />
<Reference name="nunit.framework" path="../../../bin/" />
<Reference name="OpenSim.Framework" path="../../../bin/" />
<Reference name="OpenSim.Tests.Common" path="../../../bin/" />
<Reference name="OpenSim.Framework.Communications" path="../../../bin/" />
<Reference name="OpenSim.Region.Framework" path="../../../bin/" />
<Reference name="OpenSim.Framework.Console" path="../../../bin/" />
<Reference name="OpenSim.Services.Interfaces" path="../../../bin/" />
<Reference name="OpenMetaverseTypes" path="../../../bin/" />
<Reference name="OpenMetaverse" path="../../../bin/" />
<Files>
<Match pattern="*.cs" recurse="true"/>
<Match pattern="*.addin.xml" path="Resources" buildAction="EmbeddedResource" recurse="true"/>
</Files>
</Project>
<Files>
<Match pattern="*.cs" recurse="true">
</Match>
</Files>
</Project>