From 9083dbf23b31943be6a5f04e15c38beb21398d67 Mon Sep 17 00:00:00 2001 From: Jon Cundill Date: Thu, 21 Jul 2011 22:37:51 +0100 Subject: [PATCH] added all files to stable branch --- CHANGES.txt | 14 ++ CONTRIBUTORS.txt | 4 + Flocking.dll | Bin 0 -> 28672 bytes Flocking.dll.mdb | Bin 0 -> 10373 bytes Flocking/Boid.cs | 334 ++++++++++++++++++++++++++ Flocking/BoidBehaviour.cs | 174 ++++++++++++++ Flocking/ChatCommandParser.cs | 127 ++++++++++ Flocking/FlockingModel.cs | 110 +++++++++ Flocking/FlockingModule.cs | 312 ++++++++++++++++++++++++ Flocking/FlockingView.cs | 172 +++++++++++++ Flocking/FlowField.cs | 193 +++++++++++++++ Flocking/FlowMap.cs | 185 ++++++++++++++ Flocking/Util.cs | 54 +++++ Flocking/resources/Flocking.addin.xml | 13 + TODO.txt | 28 +++ prebuild.xml | 36 +++ 16 files changed, 1756 insertions(+) create mode 100644 CHANGES.txt create mode 100644 CONTRIBUTORS.txt create mode 100755 Flocking.dll create mode 100644 Flocking.dll.mdb create mode 100644 Flocking/Boid.cs create mode 100644 Flocking/BoidBehaviour.cs create mode 100644 Flocking/ChatCommandParser.cs create mode 100644 Flocking/FlockingModel.cs create mode 100644 Flocking/FlockingModule.cs create mode 100644 Flocking/FlockingView.cs create mode 100644 Flocking/FlowField.cs create mode 100644 Flocking/FlowMap.cs create mode 100644 Flocking/Util.cs create mode 100644 Flocking/resources/Flocking.addin.xml create mode 100644 TODO.txt create mode 100644 prebuild.xml diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..f59752c --- /dev/null +++ b/CHANGES.txt @@ -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 diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 0000000..0ecda72 --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,4 @@ +The following people have contributed to the development of the osboids module + +* Jon Cundill - initial implementation + diff --git a/Flocking.dll b/Flocking.dll new file mode 100755 index 0000000000000000000000000000000000000000..fe1459f00dce4eb48742bb3e92b701939c411e56 GIT binary patch literal 28672 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

4w;_RPhI;0wwMcDk?aScT8G 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 literal 0 HcmV?d00001 diff --git a/Flocking.dll.mdb b/Flocking.dll.mdb new file mode 100644 index 0000000000000000000000000000000000000000..7bcec258252c6bae8d1255121d34d9ea63499efa GIT binary patch literal 10373 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 + /// Initializes a new instance of the class. + /// + /// + /// L. the initial position of this boid + /// + /// + /// Ms. max speed this boid can attain + /// + /// + /// Mf. max force / acceleration this boid can extert + /// + 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;} + } + + ///

+ /// Moves our boid in the scene relative to the rest of the flock. + /// + /// + /// Boids. all the other chaps in the scene + /// + public void MoveInSceneRelativeToFlock (List neighbours) + { + //List 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 (); + + } + + /// + /// 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 + /// + /// + void Flock (List 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; + + } + + + /// + /// 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 + /// + /// + 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; + } + + /// + /// Seek the specified target. Move into that flock + /// Accelerate us towards where we want to go + /// + /// + /// Target. the position within the flock we would like to achieve + /// + void Seek (Vector3 target) + { + m_acc += Steer (target, false); + } + + /// + /// Arrive the specified target. Slow us down, as we are almost there + /// + /// + /// Target. the flock we would like to think ourselves part of + /// + 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; + } + + + /// + /// navigate away from whatever it is we are too close to + /// + void AvoidObstacles () + { + //look tolerance metres ahead + m_acc += m_flowField.AdjustVelocity( this, m_behaviour.tolerance ); + } + + + /// + /// Separate ourselves from the specified boids. + /// keeps us a respectable distance from our closest neighbours whilst still + /// being part of our local flock + /// + /// + /// Boids. all the boids in the scene + /// + Vector3 Separate (List 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 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; + } + + /// + /// 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. + /// + /// + /// Boids. all the boids in the scene - we only really care about those in the neighbourdist + /// + Vector3 Align (List 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; + } + + /// + /// 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 + /// + /// + /// Boids. the boids in the scene + /// + Vector3 Cohesion (List 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; + } + } +} + diff --git a/Flocking/BoidBehaviour.cs b/Flocking/BoidBehaviour.cs new file mode 100644 index 0000000..d907957 --- /dev/null +++ b/Flocking/BoidBehaviour.cs @@ -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 m_paramDescriptions = new Dictionary (); + + 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); + } + } +} + diff --git a/Flocking/ChatCommandParser.cs b/Flocking/ChatCommandParser.cs new file mode 100644 index 0000000..7ec8123 --- /dev/null +++ b/Flocking/ChatCommandParser.cs @@ -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 m_commandMap = new Dictionary (); + private Dictionary m_syntaxMap = new Dictionary (); + + 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 + " ").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 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 + ); + } + } +} + diff --git a/Flocking/FlockingModel.cs b/Flocking/FlockingModel.cs new file mode 100644 index 0000000..c36e501 --- /dev/null +++ b/Flocking/FlockingModel.cs @@ -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 m_flock = new List(); + 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 GetNeighbours(Boid boid) { + return m_flock.FindAll(delegate(Boid other) + { + return (boid != other) && (Utils.GetDistanceTo (boid.Location, other.Location) < m_behaviour.neighbourDistance); + }); + } + + + public List 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; + } + } +} + diff --git a/Flocking/FlockingModule.cs b/Flocking/FlockingModule.cs new file mode 100644 index 0000000..e4a97e7 --- /dev/null +++ b/Flocking/FlockingModule.cs @@ -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 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 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 ("")) { + 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 + } + +} diff --git a/Flocking/FlockingView.cs b/Flocking/FlockingView.cs new file mode 100644 index 0000000..36ca548 --- /dev/null +++ b/Flocking/FlockingView.cs @@ -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 m_sogMap = new Dictionary (); + + 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 current = new List (m_sogMap.Keys); + current.ForEach (delegate(string name) { + RemoveSOGFromScene(name); + }); + m_sogMap.Clear(); + } + + public void Render (List 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(); + + } + + + } +} + diff --git a/Flocking/FlowField.cs b/Flocking/FlowField.cs new file mode 100644 index 0000000..ab9d1e9 --- /dev/null +++ b/Flocking/FlowField.cs @@ -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 + + /// + /// Initializes a new instance of the class. + /// + /// + /// Scene. + /// + /// + /// Centre. + /// + /// + /// Width. + /// + /// + /// Depth. + /// + /// + /// Height. + /// + /// + 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 (); + } + + /// + /// build a flow field on the scene at the specified centre + /// position in the scene and of extent given by width, depth and height. + /// + 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()) { + //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]; + } + } +} + diff --git a/Flocking/FlowMap.cs b/Flocking/FlowMap.cs new file mode 100644 index 0000000..b4df130 --- /dev/null +++ b/Flocking/FlowMap.cs @@ -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; + } + } + + +} + diff --git a/Flocking/Util.cs b/Flocking/Util.cs new file mode 100644 index 0000000..50d46e4 --- /dev/null +++ b/Flocking/Util.cs @@ -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; + + } + + + + } +} + diff --git a/Flocking/resources/Flocking.addin.xml b/Flocking/resources/Flocking.addin.xml new file mode 100644 index 0000000..5e1cfcd --- /dev/null +++ b/Flocking/resources/Flocking.addin.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..b1e88ff --- /dev/null +++ b/TODO.txt @@ -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 + + diff --git a/prebuild.xml b/prebuild.xml new file mode 100644 index 0000000..839b00c --- /dev/null +++ b/prebuild.xml @@ -0,0 +1,36 @@ + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + +