added all files to stable branch

stable0711
Jon Cundill 2011-07-21 22:37:51 +01:00
parent 9e84d1f957
commit 9083dbf23b
16 changed files with 1756 additions and 0 deletions

14
CHANGES.txt Normal file
View File

@ -0,0 +1,14 @@
08/07/2011 - Initial pre alpha release
Basic Flocking algos implemented
Border avoidance not so good
No configuration capabilities
09/07/2011
Fixed Boid rotation, boid now orients with direction of travel
added command handlers for both console and inworld chat channel
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

4
CONTRIBUTORS.txt Normal file
View File

@ -0,0 +1,4 @@
The following people have contributed to the development of the osboids module
* Jon Cundill - initial implementation

BIN
Flocking.dll Executable file

Binary file not shown.

BIN
Flocking.dll.mdb Normal file

Binary file not shown.

334
Flocking/Boid.cs Normal file
View File

@ -0,0 +1,334 @@
/*
* 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;
private FlowField m_flowField;
/// <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, FlowField flowField)
{
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;
m_flowField = flowField;
}
public Vector3 Location {
get { return m_loc;}
set { m_loc = value; }
}
public Vector3 Velocity {
get { return m_vel;}
}
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)
{
//List<Boid> neighbours = m_model.GetNeighbours(this);
// we would like to stay with our mates
Flock (neighbours);
// our first priority is to not hurt ourselves
// so adjust where we would like to go to avoid hitting things
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 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();
// 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 ()
{
//look tolerance metres ahead
m_acc += m_flowField.AdjustVelocity( this, m_behaviour.tolerance );
}
/// <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;
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;
}
}
}

174
Flocking/BoidBehaviour.cs Normal file
View File

@ -0,0 +1,174 @@
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);
}
}
}

View File

@ -0,0 +1,127 @@
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 ChatCommandParser
{
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
private string m_name;
private Scene m_scene;
private int m_chatChannel;
private Dictionary<string, BoidCmdDelegate> m_commandMap = new Dictionary<string, BoidCmdDelegate> ();
private Dictionary<string, BoidCmdDefn> m_syntaxMap = new Dictionary<string, BoidCmdDefn> ();
public ChatCommandParser (IRegionModuleBase module, Scene scene, int channel)
{
m_name = module.Name;
m_scene = scene;
m_chatChannel = channel;
}
public void AddCommand (string cmd, string args, string help, CommandDelegate fn)
{
m_commandMap.Add (cmd, new BoidCmdDelegate (fn));
m_syntaxMap.Add (cmd, new BoidCmdDefn (cmd, args, help));
}
public void SimChatSent (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 (m_name, args);
} else {
// we don't understand this command
// shouldn't happen
m_log.ErrorFormat ("Unable to invoke command {0}", args [0]);
RespondToMessage (msg, "Unable to invoke command " + args [0]);
}
} else {
// we recognise the command, but we got the call wrong
RespondToMessage (msg, "wrong syntax: " + defn.GetSyntax ());
}
} else {
// this is not a command we recognise
RespondToMessage (msg, args [0] + " is not a valid command for osboids");
}
}
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 RespondToMessage (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);
}
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
);
}
}
}

110
Flocking/FlockingModel.cs Normal file
View File

@ -0,0 +1,110 @@
/*
* 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 class FlockingModel
{
private List<Boid> m_flock = new List<Boid>();
private FlowField m_flowField;
private BoidBehaviour m_behaviour;
private Random m_rnd = new Random(Environment.TickCount);
private int m_flockSize;
private Vector3 m_boidSize;
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 FlockingModel( BoidBehaviour behaviour ) {
m_behaviour = behaviour;
}
void AddBoid (string name)
{
Boid boid = new Boid (name, m_boidSize, m_behaviour, m_flowField);
// find an initial random location for this Boid
// somewhere not within an obstacle
int xInit = m_rnd.Next(Util.SCENE_SIZE);
int yInit = m_rnd.Next(Util.SCENE_SIZE);
int zInit = m_rnd.Next(Util.SCENE_SIZE);
Vector3 location = new Vector3 (Convert.ToSingle(xInit), Convert.ToSingle(yInit), Convert.ToSingle(zInit));
boid.Location = location + m_flowField.AdjustVelocity(boid, 5f);
m_flock.Add (boid);
}
public void Initialise (int flockSize, Vector3 boidSize, FlowField flowField)
{
m_flowField = flowField;
m_flockSize = flockSize;
m_boidSize = boidSize;
for (int i = 0; i < m_flockSize; i++) {
AddBoid ("boid"+i );
}
}
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 ()
{
m_flock.ForEach( delegate(Boid boid) {
boid.MoveInSceneRelativeToFlock(GetNeighbours(boid));
} );
return m_flock;
}
public override string ToString ()
{
string retVal = "Num Boids: " + m_flockSize + Environment.NewLine
+ m_behaviour.ToString();
return retVal;
}
}
}

312
Flocking/FlockingModule.cs Normal file
View File

@ -0,0 +1,312 @@
/*
* 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;
namespace Flocking
{
public class FlockingModule : INonSharedRegionModule
{
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
static object m_sync = new object();
private Scene m_scene;
private FlockingModel m_model;
private FlockingView m_view;
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_boidPrim;
private ChatCommandParser m_chatCommandParser;
private BoidBehaviour m_behaviour;
private int m_flockSize = 100;
private UUID m_owner;
#region IRegionModule Members
public void Initialise (IConfigSource source)
{
//check if we are in the ini files
//if so get some physical constants out of them and pass into the model
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", 3f);
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)
{
//m_log.Info ("ADDING FLOCKING");
m_scene = scene;
if (m_enabled) {
//register commands
m_chatCommandParser = new ChatCommandParser(this, scene, m_chatChannel);
RegisterCommands ();
//register handlers
m_scene.EventManager.OnFrame += FlockUpdate;
m_scene.EventManager.OnChatFromClient += m_chatCommandParser.SimChatSent; //listen for commands sent from the client
// init module
m_model = new FlockingModel (m_behaviour);
m_view = new FlockingView (m_scene);
m_view.BoidPrim = m_boidPrim;
}
}
public void RegionLoaded (Scene scene)
{
if (m_enabled) {
//build a proper flow field based on the scene
FlowField field = new FlowField(scene, new Vector3(128f, 128f, 128f), 200, 200, 200);
//ask the view how big the boid is
Vector3 scale = m_view.GetBoidSize();
// m_log.Error( m_boidPrim + " = " + scale.ToString());
// Generate initial flock values
m_model.Initialise (m_flockSize, scale, field);
// who is the owner for the flock in this region
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
m_view.PostInitialize (m_owner);
// Mark Module Ready for duty
m_ready = true;
}
}
public void RemoveRegion (Scene scene)
{
if (m_enabled) {
m_scene.EventManager.OnFrame -= FlockUpdate;
m_scene.EventManager.OnChatFromClient -= m_chatCommandParser.SimChatSent;
}
}
public string Name {
get { return "FlockingModule"; }
}
public bool IsSharedModule {
get { return false; }
}
#endregion
#region EventHandlers
public void FlockUpdate ()
{
if (((m_frame++ % m_frameUpdateRate) != 0) || !m_ready || !m_enabled) {
return;
}
// work out where everyone has moved to
// and tell the scene to render the new positions
lock( m_sync ) {
List<Boid > boids = m_model.UpdateFlockPos ();
m_view.Render (boids);
}
}
#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_scene.AddCommand (this, "flock-" + cmd, "flock-" + cmd + argStr, help, fn);
m_chatCommandParser.AddCommand(cmd, args, help, fn);
}
private void RegisterCommands ()
{
AddCommand ("stop", "", "Stop all Flocking", HandleStopCmd);
AddCommand ("start", "", "Start Flocking", HandleStartCmd);
AddCommand ("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd);
AddCommand ("stats", "", "show flocking stats", HandleShowStatsCmd);
AddCommand ("prim", "name", "set the prim used for each boid to that passed in", HandleSetPrimCmd);
AddCommand ("framerate", "num", "[debugging] only update boids every <num> frames", HandleSetFrameRateCmd);
AddCommand ("set", "name, value", "change the flock dynamics", HandleSetParameterCmd);
}
private bool ShouldHandleCmd ()
{
return m_scene.ConsoleScene () == m_scene;
}
private bool IsInWorldCmd (ref string [] args)
{
bool retVal = false;
if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) {
retVal = true;
}
return retVal;
}
private void ShowResponse (string response, bool inWorld)
{
if (inWorld) {
ScenePresence owner = m_scene.GetScenePresence(m_owner);
m_chatCommandParser.SendMessage(owner, response);
} else {
MainConsole.Instance.Output (response);
}
}
public void HandleSetParameterCmd(string module, string[] args)
{
if (ShouldHandleCmd ()) {
string name = args[1];
string newVal = args[2];
if( m_behaviour.IsValidParameter( name ) ) {
m_behaviour.SetParameter(name, newVal);
} else {
bool inWorld = IsInWorldCmd( ref args);
ShowResponse( name + "is not a valid flock parameter", inWorld );
ShowResponse( "valid parameters are: " + m_behaviour.GetList(), inWorld);
}
}
}
public void HandleStopCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
m_log.Info ("stop the flocking capability");
m_enabled = false;
m_view.Clear ();
}
}
void HandleSetFrameRateCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
int frameRate = Convert.ToInt32( args[1] );
m_frameUpdateRate = frameRate;
}
}
public void HandleStartCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
m_log.Info ("start the flocking capability");
m_enabled = true;
FlockUpdate ();
}
}
public void HandleSetSizeCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
lock( m_sync ) {
int newSize = Convert.ToInt32(args[1]);
m_model.Size = newSize;
m_view.Clear();
}
}
}
public void HandleShowStatsCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
bool inWorld = IsInWorldCmd (ref args);
string str = m_model.ToString();
ShowResponse (str, inWorld);
}
}
public void HandleSetPrimCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
string primName = args[1];
lock(m_sync) {
m_view.BoidPrim = primName;
m_view.Clear();
}
}
}
#endregion
#region IRegionModuleBase Members
public void Close ()
{
}
public Type ReplaceableInterface {
get { return null; }
}
#endregion
}
}

172
Flocking/FlockingView.cs Normal file
View File

@ -0,0 +1,172 @@
/*
* 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 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 UUID m_owner;
private String m_boidPrim;
private Dictionary<string, SceneObjectGroup> m_sogMap = new Dictionary<string, SceneObjectGroup> ();
public FlockingView (Scene scene)
{
m_scene = scene;
}
public void PostInitialize (UUID owner)
{
m_owner = owner;
}
public String BoidPrim {
set{ m_boidPrim = value;}
}
public Vector3 GetBoidSize ()
{
float offsetHeight;
return findByName(m_boidPrim).GetAxisAlignedBoundingBox( out offsetHeight );
}
public void Clear ()
{
//trash everything we have
List<string> current = new List<string> (m_sogMap.Keys);
current.ForEach (delegate(string name) {
RemoveSOGFromScene(name);
});
m_sogMap.Clear();
}
public void Render (List<Boid> boids)
{
boids.ForEach(delegate( Boid boid ) {
DrawBoid (boid);
});
}
private void DrawBoid (Boid boid)
{
SceneObjectPart existing = m_scene.GetSceneObjectPart (boid.Id);
SceneObjectGroup sog;
if (existing == null) {
//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);
} else {
sog = existing.ParentGroup;
}
Quaternion rotation = CalcRotationToEndpoint (sog, boid.Location);
//sog.UpdateGroupRotationPR( boid.Location, rotation);
sog.UpdateGroupPosition( boid.Location );
sog.UpdateGroupRotationR( rotation );
}
private static Quaternion CalcRotationToEndpoint (SceneObjectGroup sog, Vector3 ev)
{
//llSetRot(llRotBetween(<1,0,0>,llVecNorm(targetPosition - llGetPos())));
// boid wil fly x forwards and Z up
Vector3 sv = sog.AbsolutePosition;
Vector3 currDirVec = Vector3.UnitX;
Vector3 desiredDirVec = Vector3.Subtract (ev, sv);
desiredDirVec.Normalize ();
return Vector3.RotationBetween (currDirVec, desiredDirVec);
}
private SceneObjectGroup CopyPrim (SceneObjectGroup prim, string name)
{
SceneObjectGroup copy = prim.Copy (true);
copy.Name = name;
copy.DetachFromBackup ();
return copy;
}
private SceneObjectGroup findByName (string name)
{
// SceneObjectGroup retVal = (SceneObjectGroup)m_scene.Entities.Find (delegate( EntityBase e ) {
// return e.Name == name;
//});
SceneObjectGroup retVal = null;
SceneObjectPart sop = m_scene.GetSceneObjectPart(name);
// can't find it so make a default one
if (sop == null) {
m_log.Error("no " + name);
retVal = MakeDefaultPrim (name);
} else {
retVal = sop.ParentGroup;
}
return retVal;
}
private SceneObjectGroup MakeDefaultPrim (string name)
{
PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere ();
shape.Scale = new Vector3 (0.5f, 0.5f, 0.5f);
SceneObjectGroup prim = new SceneObjectGroup (m_owner, new Vector3 (128f, 128f, 25f), shape);
prim.Name = name;
prim.DetachFromBackup ();
m_scene.AddNewSceneObject (prim, false);
return prim;
}
private void RemoveSOGFromScene(string sogName)
{
SceneObjectGroup sog = m_sogMap[sogName];
m_scene.DeleteSceneObject(sog, false);
//sog.SendGroupFullUpdate();
}
}
}

193
Flocking/FlowField.cs Normal file
View File

@ -0,0 +1,193 @@
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 the object 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, Vector3 centre, int width, int depth, int height)
{
m_scene = scene;
m_startX = Math.Max (BUFFER, centre.X - width / 2f);
m_startY = Math.Max (BUFFER, centre.Y - depth / 2f);
m_startZ = Math.Max (BUFFER, centre.Z - height / 2f);
m_endX = Math.Min (Util.SCENE_SIZE - BUFFER, centre.X + width / 2f);
m_endY = Math.Min (Util.SCENE_SIZE - BUFFER, centre.Y + depth / 2f);
m_endZ = Math.Min (Util.SCENE_SIZE - BUFFER, centre.Z + height / 2f);
// 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);
minX = Convert.ToInt32 (fminX);
maxX = Convert.ToInt32 (fmaxX);
minY = Convert.ToInt32 (fminY);
maxY = Convert.ToInt32 (fmaxX);
minZ = Convert.ToInt32 (fminZ);
maxZ = Convert.ToInt32 (fmaxZ);
for (int x = minX; x < maxX; x++) {
for (int y = minY; y < maxY; y++) {
for (int z = minZ; z < maxZ; z++) {
m_field [x, y, z] = sog.UUID;
}
}
}
}
}
public Vector3 AdjustVelocity (Boid boid, float lookAheadDist)
{
Vector3 normVel = Vector3.Normalize (boid.Velocity);
Vector3 loc = boid.Location;
Vector3 inFront = loc + normVel * lookAheadDist;
Vector3 adjustedDestintation = inFront + FieldStrength (loc, boid.Size, inFront);
Vector3 newVel = Vector3.Normalize (adjustedDestintation - loc) * Vector3.Mag (boid.Velocity);
return newVel;
}
public Vector3 FieldStrength (Vector3 current, Vector3 size, Vector3 inFront)
{
Vector3 retVal = Vector3.Zero;
float length = size.X/2;
float width = size.Y/2;
float height = size.Z/2;
//keep us in bounds
if (inFront.X > m_endX)
retVal.X -= inFront.X - m_endX - length;
if (inFront.Y > m_endY)
retVal.Y -= inFront.Y - m_endY - width;
if (inFront.Z > m_endZ)
retVal.Z -= inFront.Z - m_endZ - height;
if (inFront.X < m_startX)
retVal.X += m_startX - inFront.X + length;
if (inFront.Y < m_startY)
retVal.Y += m_startY - inFront.Y + width;
if (inFront.Z < m_startZ)
retVal.Z += m_startZ - inFront.Z + height;
//now get the field strength at the inbounds position
UUID collider = LookUp (inFront + retVal);
while (collider != UUID.Zero) {
if (collider == TERRAIN) {
// ground height at current and dest averaged
float h1 = m_scene.GetGroundHeight (current.X, current.Y);
float h2 = m_scene.GetGroundHeight (inFront.X, inFront.Y);
float h = (h1 + h2) / 2;
retVal.Z += h;
} else if (collider == EDGE) {
// we ain't ever going to hit these
} 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);
//keep us out of the sog
if (inFront.X > sogMinX)
retVal.X -= inFront.X - sogMinX - length;
if (inFront.Y > sogMinY)
retVal.Y -= inFront.Y - sogMinY - width;
if (inFront.Z > sogMinZ)
retVal.Z -= inFront.Z - sogMinZ - height;
if (inFront.X < sogMaxX)
retVal.X += sogMaxX - inFront.X + length;
if (inFront.Y < sogMaxY)
retVal.Y += sogMaxY - inFront.Y + width;
if (inFront.Z < sogMaxZ)
retVal.Z += sogMaxZ - inFront.Z + height;
}
}
collider = LookUp (inFront + retVal);
//inFront += retVal;
}
return retVal;
}
public UUID LookUp (Vector3 loc)
{
return m_field [(int)loc.X, (int)loc.Y, (int)loc.Z];
}
}
}

185
Flocking/FlowMap.cs Normal file
View File

@ -0,0 +1,185 @@
/*
* 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 FlowMap
{
private Scene m_scene;
private float[,,] m_flowMap = new float[256,256,256];
public FlowMap (Scene scene)
{
m_scene = scene;
}
public int LengthX {
get {return 256;}
}
public int LengthY {
get {return 256;}
}
public int LengthZ {
get {return 256;}
}
public void Initialise() {
//fill in the boundaries
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 < 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 < 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 < 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_flowMap[x,y,z] = 100f;
}
}
}
// fill in the things
foreach( EntityBase entity in m_scene.GetEntities() ) {
if( entity is SceneObjectGroup ) {
SceneObjectGroup sog = (SceneObjectGroup)entity;
//todo: ignore phantom
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
int maxX, minX, maxY, minY, maxZ, minZ;
sog.GetAxisAlignedBoundingBoxRaw( out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ );
minX = Convert.ToInt32(fminX);
maxX = Convert.ToInt32(fmaxX);
minY = Convert.ToInt32(fminY);
maxY = Convert.ToInt32(fmaxX);
minZ = Convert.ToInt32(fminZ);
maxZ = Convert.ToInt32(fmaxZ);
for( int x = minX; x < maxX; x++ ) {
for( int y = minY; y < maxY; y++ ) {
for( int z = minZ; z < maxZ; z++ ) {
m_flowMap[x,y,z] = 100f;
}
}
}
}
}
}
public bool WouldHitObstacle (Vector3 currPos, Vector3 targetPos)
{
bool retVal = false;
//fail fast
if( IsOutOfBounds(targetPos) ) {
retVal = true;
} else if( IsWithinObstacle(targetPos) ) {
retVal = true;
} else if( IntersectsObstacle (currPos, targetPos) ) {
retVal = true;
}
return retVal;
}
public bool IsOutOfBounds(Vector3 targetPos) {
bool retVal = false;
if( targetPos.X < 5f ||
targetPos.X > 250f ||
targetPos.Y < 5f ||
targetPos.Y > 250f ||
targetPos.Z < 5f ||
targetPos.Z > 250f ) {
retVal = true;
}
return retVal;
}
public bool IntersectsObstacle (Vector3 currPos, Vector3 targetPos)
{
bool retVal = false;
// Ray trace the Vector and fail as soon as we hit something
Vector3 direction = targetPos - currPos;
float length = direction.Length();
// check every metre
for( float i = 1f; i < length; i += 1f ) {
Vector3 rayPos = currPos + ( direction * i );
//give up if we go OOB on this ray
if( IsOutOfBounds( rayPos ) ){
retVal = true;
break;
}
else if( IsWithinObstacle( rayPos ) ) {
retVal = true;
break;
}
}
return retVal;
}
public bool IsWithinObstacle( Vector3 targetPos ) {
return IsWithinObstacle(Convert.ToInt32(targetPos.X), Convert.ToInt32(targetPos.Y),Convert.ToInt32(targetPos.Z));
}
public bool IsWithinObstacle( int x, int y, int z ) {
bool retVal = false;
if( x > LengthX || y > LengthY || z > LengthZ ) {
retVal = true;
} else if( x < 0 || y < 0 || z < 0 ) {
retVal = true;
} else if (m_flowMap[x,y,z] > 50f) {
retVal = true;
}
return retVal;
}
}
}

54
Flocking/Util.cs Normal file
View File

@ -0,0 +1,54 @@
/*
* 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;
namespace Flocking
{
public class Util
{
public const int SCENE_SIZE = 256;
public static Vector3 Limit (Vector3 initial, float maxLen)
{
float currLen = initial.Length ();
float ratio = 1.0f;
if (currLen > maxLen) {
ratio = currLen / maxLen;
}
return initial /= ratio;
}
}
}

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>

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

36
prebuild.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Core osboids Project -->
<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="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>