added ability to control flock from scripts.

stable0711
Jon Cundill 2011-07-24 00:18:03 +01:00
parent 1c410fdb81
commit e584d81e0c
8 changed files with 457 additions and 273 deletions

View File

@ -44,7 +44,6 @@ namespace Flocking
private Random m_rndnums = new Random (Environment.TickCount); private Random m_rndnums = new Random (Environment.TickCount);
private BoidBehaviour m_behaviour; private BoidBehaviour m_behaviour;
private FlowField m_flowField;
/// <summary> /// <summary>
@ -59,31 +58,31 @@ namespace Flocking
/// <param name='mf'> /// <param name='mf'>
/// Mf. max force / acceleration this boid can extert /// Mf. max force / acceleration this boid can extert
/// </param> /// </param>
public Boid (string id, Vector3 size, BoidBehaviour behaviour, FlowField flowField) public Boid (string id, Vector3 size, BoidBehaviour behaviour)
{ {
m_id = id; m_id = id;
m_acc = Vector3.Zero; m_acc = Vector3.Zero;
m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1)); m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1));
m_size = size; m_size = size;
m_behaviour = behaviour; m_behaviour = behaviour;
m_flowField = flowField;
} }
public Vector3 Location { public Vector3 Location {
get { return m_loc;} get { return m_loc; }
set { m_loc = value; } set { m_loc = value; }
} }
public Vector3 Velocity { public Vector3 Velocity {
get { return m_vel;} get { return m_vel; }
set { m_vel = value; }
} }
public Vector3 Size { public Vector3 Size {
get { return m_size;} get { return m_size; }
} }
public String Id { public String Id {
get {return m_id;} get { return m_id; }
} }
/// <summary> /// <summary>
@ -92,15 +91,14 @@ namespace Flocking
/// <param name='boids'> /// <param name='boids'>
/// Boids. all the other chaps in the scene /// Boids. all the other chaps in the scene
/// </param> /// </param>
public void MoveInSceneRelativeToFlock (List<Boid> neighbours) public void MoveInSceneRelativeToFlock (List<Boid> neighbours, FlowField field)
{ {
//List<Boid> neighbours = m_model.GetNeighbours(this);
// we would like to stay with our mates // we would like to stay with our mates
Flock (neighbours); Flock (neighbours);
// our first priority is to not hurt ourselves // however, our first priority is to not hurt ourselves
// so adjust where we would like to go to avoid hitting things // so adjust where we would like to go to avoid hitting things
AvoidObstacles (); AvoidObstacles (field);
// then we want to avoid any threats // then we want to avoid any threats
// this not implemented yet // this not implemented yet
@ -127,7 +125,7 @@ namespace Flocking
Vector3 sep = Separate (neighbours); // Separation Vector3 sep = Separate (neighbours); // Separation
Vector3 ali = Align (neighbours); // Alignment Vector3 ali = Align (neighbours); // Alignment
Vector3 coh = Cohesion (neighbours); // Cohesion Vector3 coh = Cohesion (neighbours); // Cohesion
Vector3 ori = Orientation(); Vector3 ori = Orientation(); // its tricky to fly directly up or down
// Arbitrarily weight these forces // Arbitrarily weight these forces
sep *= m_behaviour.separationWeighting; sep *= m_behaviour.separationWeighting;
@ -213,10 +211,23 @@ namespace Flocking
/// <summary> /// <summary>
/// navigate away from whatever it is we are too close to /// navigate away from whatever it is we are too close to
/// </summary> /// </summary>
void AvoidObstacles () void AvoidObstacles (FlowField field)
{ {
//look tolerance metres ahead //look tolerance metres ahead
m_acc += m_flowField.AdjustVelocity( this, m_behaviour.tolerance ); //m_acc += field.AdjustVelocity( this, m_behaviour.lookaheadDistance );
Vector3 normVel = Vector3.Normalize (m_vel);
Vector3 inFront = m_loc + normVel * m_behaviour.LookaheadDistance;
Vector3 adjustedDestintation = field.FieldStrength (m_loc, m_size, inFront);
Vector3 newVel = Vector3.Normalize (adjustedDestintation - m_loc) * Vector3.Mag (m_vel);
float mOrigVel = Vector3.Mag(m_vel);
float mNewVel = Vector3.Mag(newVel);
if( mNewVel != 0f && mNewVel > mOrigVel ) {
newVel *= mOrigVel / mNewVel;
}
m_vel = newVel;
} }
@ -243,6 +254,9 @@ namespace Flocking
tooCloseNeighbours.ForEach( delegate(Boid neighbour) { tooCloseNeighbours.ForEach( delegate(Boid neighbour) {
// Calculate vector pointing away from neighbor // Calculate vector pointing away from neighbor
Vector3 diff = m_loc - neighbour.Location; Vector3 diff = m_loc - neighbour.Location;
if( diff == Vector3.Zero ) {
diff = new Vector3( (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble());
}
steer += Utils.GetNormalizedVector(diff) / (float)(Utils.GetDistanceTo (m_loc, neighbour.Location)); steer += Utils.GetNormalizedVector(diff) / (float)(Utils.GetDistanceTo (m_loc, neighbour.Location));
}); });

View File

@ -1,3 +1,29 @@
/*
* 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;
using System.Collections.Generic; using System.Collections.Generic;

View File

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

View File

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

View File

@ -39,6 +39,7 @@ namespace Flocking
private Random m_rnd = new Random(Environment.TickCount); private Random m_rnd = new Random(Environment.TickCount);
private int m_flockSize; private int m_flockSize;
private Vector3 m_boidSize; private Vector3 m_boidSize;
private Vector3 m_startPos;
public int Size { public int Size {
get {return m_flockSize;} get {return m_flockSize;}
@ -51,21 +52,17 @@ namespace Flocking
} }
} }
public FlockingModel( BoidBehaviour behaviour ) { public FlockingModel( BoidBehaviour behaviour, Vector3 startPos ) {
m_behaviour = behaviour; m_behaviour = behaviour;
m_startPos = startPos;
} }
void AddBoid (string name) void AddBoid (string name)
{ {
Boid boid = new Boid (name, m_boidSize, m_behaviour, m_flowField); Boid boid = new Boid (name, m_boidSize, m_behaviour);
// find an initial random location for this Boid boid.Location = m_startPos;
// somewhere not within an obstacle boid.Velocity = Vector3.UnitX;
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); m_flock.Add (boid);
} }
@ -77,6 +74,7 @@ namespace Flocking
m_boidSize = boidSize; m_boidSize = boidSize;
for (int i = 0; i < m_flockSize; i++) { for (int i = 0; i < m_flockSize; i++) {
AddBoid ("boid"+i ); AddBoid ("boid"+i );
UpdateFlockPos();
} }
} }
@ -91,7 +89,7 @@ namespace Flocking
public List<Boid> UpdateFlockPos () public List<Boid> UpdateFlockPos ()
{ {
m_flock.ForEach( delegate(Boid boid) { m_flock.ForEach( delegate(Boid boid) {
boid.MoveInSceneRelativeToFlock(GetNeighbours(boid)); boid.MoveInSceneRelativeToFlock(GetNeighbours(boid), m_flowField);
} ); } );
return m_flock; return m_flock;

View File

@ -36,6 +36,8 @@ using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Console; using OpenSim.Framework.Console;
using OpenSim.Services.Interfaces;
namespace Flocking namespace Flocking
{ {
@ -49,12 +51,12 @@ namespace Flocking
private FlockingModel m_model; private FlockingModel m_model;
private FlockingView m_view; private FlockingView m_view;
private bool m_enabled = false; private bool m_enabled = false;
private bool m_ready = false; private bool m_active = false;
private uint m_frame = 0; private uint m_frame = 0;
private int m_frameUpdateRate = 1; private int m_frameUpdateRate = 1;
private int m_chatChannel = 118; private int m_chatChannel = 118;
private string m_boidPrim; private string m_boidPrim = "boid-prim";
private ChatCommandParser m_chatCommandParser; private FlockingCommandParser m_commandParser;
private BoidBehaviour m_behaviour; private BoidBehaviour m_behaviour;
private int m_flockSize = 100; private int m_flockSize = 100;
@ -95,42 +97,26 @@ namespace Flocking
//m_log.Info ("ADDING FLOCKING"); //m_log.Info ("ADDING FLOCKING");
m_scene = scene; m_scene = scene;
if (m_enabled) { if (m_enabled) {
//register commands
m_chatCommandParser = new ChatCommandParser(this, scene, m_chatChannel);
RegisterCommands ();
//register handlers //register handlers
m_scene.EventManager.OnFrame += FlockUpdate; 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) public void RegionLoaded (Scene scene)
{ {
if (m_enabled) { 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 // who is the owner for the flock in this region
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner; m_owner = scene.RegionInfo.EstateSettings.EstateOwner;
m_view.PostInitialize (m_owner);
// Mark Module Ready for duty //register command handler
m_ready = true; m_commandParser = new FlockingCommandParser(this, scene, m_chatChannel);
RegisterCommands ();
// init view
m_view = new FlockingView (scene);
m_view.PostInitialize (m_owner);
m_view.BoidPrim = m_boidPrim;
} }
} }
@ -138,7 +124,7 @@ namespace Flocking
{ {
if (m_enabled) { if (m_enabled) {
m_scene.EventManager.OnFrame -= FlockUpdate; m_scene.EventManager.OnFrame -= FlockUpdate;
m_scene.EventManager.OnChatFromClient -= m_chatCommandParser.SimChatSent; m_commandParser.Deregister();
} }
} }
@ -156,7 +142,7 @@ namespace Flocking
public void FlockUpdate () public void FlockUpdate ()
{ {
if (((m_frame++ % m_frameUpdateRate) != 0) || !m_ready || !m_enabled) { if (((m_frame++ % m_frameUpdateRate) != 0) || !m_active || !m_enabled) {
return; return;
} }
// work out where everyone has moved to // work out where everyone has moved to
@ -169,27 +155,37 @@ namespace Flocking
#endregion #endregion
private void BuildFlowField(Vector3 centre, int width, int depth, int height) {
m_log.Info("building flow field");
//build a flow field based on the scene
FlowField field = new FlowField(m_scene, centre, width, depth, height);
m_log.Info("built");
//ask the view how big the boid prim is
Vector3 scale = m_view.GetBoidSize();
Vector3 startPos = m_scene.GetSceneObjectPart(m_view.BoidPrim).ParentGroup.AbsolutePosition;
// init model
m_log.Info("creating model");
m_model = new FlockingModel (m_behaviour, startPos );
// Generate initial flock values
m_model.Initialise (m_flockSize, scale, field);
m_log.Info("done");
}
#region Command Handling #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 () private void RegisterCommands ()
{ {
AddCommand ("stop", "", "Stop all Flocking", HandleStopCmd); m_commandParser.AddCommand ("stop", "", "Stop all Flocking", HandleStopCmd);
AddCommand ("start", "", "Start Flocking", HandleStartCmd); m_commandParser.AddCommand ("start", "", "Start Flocking", HandleStartCmd);
AddCommand ("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd); m_commandParser.AddCommand ("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd);
AddCommand ("stats", "", "show flocking stats", HandleShowStatsCmd); m_commandParser.AddCommand ("stats", "", "show flocking stats", HandleShowStatsCmd);
AddCommand ("prim", "name", "set the prim used for each boid to that passed in", HandleSetPrimCmd); m_commandParser.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); m_commandParser.AddCommand ("framerate", "num", "[debugging] only update boids every <num> frames", HandleSetFrameRateCmd);
AddCommand ("set", "name, value", "change the flock dynamics", HandleSetParameterCmd); m_commandParser.AddCommand ("set", "name, value", "change the flock behaviour properties", HandleSetParameterCmd);
} }
private bool ShouldHandleCmd () private bool ShouldHandleCmd ()
@ -197,26 +193,6 @@ namespace Flocking
return m_scene.ConsoleScene () == m_scene; 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) public void HandleSetParameterCmd(string module, string[] args)
{ {
if (ShouldHandleCmd ()) { if (ShouldHandleCmd ()) {
@ -226,9 +202,8 @@ namespace Flocking
if( m_behaviour.IsValidParameter( name ) ) { if( m_behaviour.IsValidParameter( name ) ) {
m_behaviour.SetParameter(name, newVal); m_behaviour.SetParameter(name, newVal);
} else { } else {
bool inWorld = IsInWorldCmd( ref args); m_commandParser.ShowResponse( name + "is not a valid flock parameter", args );
ShowResponse( name + "is not a valid flock parameter", inWorld ); m_commandParser.ShowResponse( "valid parameters are: " + m_behaviour.GetList(), args);
ShowResponse( "valid parameters are: " + m_behaviour.GetList(), inWorld);
} }
} }
} }
@ -237,7 +212,7 @@ namespace Flocking
{ {
if (ShouldHandleCmd ()) { if (ShouldHandleCmd ()) {
m_log.Info ("stop the flocking capability"); m_log.Info ("stop the flocking capability");
m_enabled = false; m_active = false;
m_view.Clear (); m_view.Clear ();
} }
} }
@ -254,7 +229,8 @@ namespace Flocking
{ {
if (ShouldHandleCmd ()) { if (ShouldHandleCmd ()) {
m_log.Info ("start the flocking capability"); m_log.Info ("start the flocking capability");
m_enabled = true; BuildFlowField(new Vector3(128f, 128f, 128f), 200, 200, 200);
m_active = true;
FlockUpdate (); FlockUpdate ();
} }
} }
@ -273,9 +249,8 @@ namespace Flocking
public void HandleShowStatsCmd (string module, string[] args) public void HandleShowStatsCmd (string module, string[] args)
{ {
if (ShouldHandleCmd ()) { if (ShouldHandleCmd ()) {
bool inWorld = IsInWorldCmd (ref args);
string str = m_model.ToString(); string str = m_model.ToString();
ShowResponse (str, inWorld); m_commandParser.ShowResponse (str, args);
} }
} }

View File

@ -54,8 +54,9 @@ namespace Flocking
m_owner = owner; m_owner = owner;
} }
public String BoidPrim { public string BoidPrim {
set{ m_boidPrim = value;} get{ return m_boidPrim; }
set{ m_boidPrim = value; }
} }
public Vector3 GetBoidSize () public Vector3 GetBoidSize ()

View File

@ -1,3 +1,29 @@
/*
* 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;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
@ -16,7 +42,7 @@ namespace Flocking
private float m_endZ; private float m_endZ;
private UUID TERRAIN = UUID.Random (); private UUID TERRAIN = UUID.Random ();
private UUID EDGE = UUID.Random (); private UUID EDGE = UUID.Random ();
private UUID[,,] m_field = new UUID[256, 256, 256]; // field of the object at this position private UUID[,,] m_field = new UUID[256, 256, 256]; // field of objects at this position
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Flocking.FlowField"/> class. /// Initializes a new instance of the <see cref="Flocking.FlowField"/> class.
@ -93,95 +119,119 @@ namespace Flocking
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ; float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
int maxX, minX, maxY, minY, maxZ, minZ; int maxX, minX, maxY, minY, maxZ, minZ;
sog.GetAxisAlignedBoundingBoxRaw (out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ); sog.GetAxisAlignedBoundingBoxRaw (out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ);
Vector3 pos = sog.AbsolutePosition;
minX = Convert.ToInt32 (fminX); minX = Convert.ToInt32 (fminX + pos.X);
maxX = Convert.ToInt32 (fmaxX); maxX = Convert.ToInt32 (fmaxX + pos.X);
minY = Convert.ToInt32 (fminY); minY = Convert.ToInt32 (fminY + pos.Y);
maxY = Convert.ToInt32 (fmaxX); maxY = Convert.ToInt32 (fmaxX + pos.Y);
minZ = Convert.ToInt32 (fminZ); minZ = Convert.ToInt32 (fminZ + pos.Z);
maxZ = Convert.ToInt32 (fmaxZ); maxZ = Convert.ToInt32 (fmaxZ + pos.Z);
for (int x = minX; x < maxX; x++) { for (int x = minX; x < maxX; x++) {
for (int y = minY; y < maxY; y++) { for (int y = minY; y < maxY; y++) {
for (int z = minZ; z < maxZ; z++) { for (int z = minZ; z < maxZ; z++) {
m_field [x, y, z] = sog.UUID; if( inBounds(x,y,z) ) {
m_field [x, y, z] = sog.UUID;
} else {
Console.WriteLine(sog.Name + " OOB at " + sog.AbsolutePosition + " -> " + x + " " + y + " " + z);
}
} }
} }
} }
} }
} }
private bool inBounds (int x, int y, int z)
{
return x >= 0 && x < 256 && y >= 0 && y < 256 && z >= 0;
}
#if false
public Vector3 AdjustVelocity (Boid boid, float lookAheadDist) public Vector3 AdjustVelocity (Boid boid, float lookAheadDist)
{ {
Vector3 normVel = Vector3.Normalize (boid.Velocity); Vector3 normVel = Vector3.Normalize (boid.Velocity);
Vector3 loc = boid.Location; Vector3 loc = boid.Location;
Vector3 inFront = loc + normVel * lookAheadDist; Vector3 inFront = loc + normVel * lookAheadDist;
Vector3 adjustedDestintation = inFront + FieldStrength (loc, boid.Size, inFront); Vector3 adjustedDestintation = FieldStrength (loc, boid.Size, inFront);
Vector3 newVel = Vector3.Normalize (adjustedDestintation - loc) * Vector3.Mag (boid.Velocity); Vector3 newVel = Vector3.Normalize (adjustedDestintation - loc) * Vector3.Mag (boid.Velocity);
float mOrigVel = Vector3.Mag(boid.Velocity);
float mNewVel = Vector3.Mag(newVel);
if( mNewVel != 0f && mNewVel > mOrigVel ) {
newVel *= mOrigVel / mNewVel;
}
return newVel; return newVel;
} }
#endif
public Vector3 FieldStrength (Vector3 current, Vector3 size, Vector3 inFront) public Vector3 FieldStrength (Vector3 currentPos, Vector3 size, Vector3 targetPos)
{ {
Vector3 retVal = Vector3.Zero;
float length = size.X/2; float length = size.X/2;
float width = size.Y/2; float width = size.Y/2;
float height = size.Z/2; float height = size.Z/2;
//keep us in bounds //keep us in bounds
if (inFront.X > m_endX) targetPos.X = Math.Min( targetPos.X, m_endX - length );
retVal.X -= inFront.X - m_endX - length; targetPos.X = Math.Max(targetPos.X, m_startX + length);
if (inFront.Y > m_endY) targetPos.Y = Math.Min( targetPos.Y, m_endY - width );
retVal.Y -= inFront.Y - m_endY - width; targetPos.Y = Math.Max(targetPos.Y, m_startY + width);
if (inFront.Z > m_endZ) targetPos.Z = Math.Min( targetPos.Z, m_endZ - height );
retVal.Z -= inFront.Z - m_endZ - height; targetPos.Z = Math.Max(targetPos.Z, m_startZ + height);
if (inFront.X < m_startX)
retVal.X += m_startX - inFront.X + length; int count = 0;
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 //now get the field strength at the inbounds position
UUID collider = LookUp (inFront + retVal); UUID collider = LookUp (targetPos);
while (collider != UUID.Zero) { while (collider != UUID.Zero && count < 100) {
count++;
if (collider == TERRAIN) { if (collider == TERRAIN) {
// ground height at current and dest averaged // ground height at currentPos and dest averaged
float h1 = m_scene.GetGroundHeight (current.X, current.Y); float h1 = m_scene.GetGroundHeight (currentPos.X, currentPos.Y);
float h2 = m_scene.GetGroundHeight (inFront.X, inFront.Y); float h2 = m_scene.GetGroundHeight (targetPos.X, targetPos.Y);
float h = (h1 + h2) / 2; float h = (h1 + h2) / 2;
retVal.Z += h; targetPos.Z = h + height;
} else if (collider == EDGE) { } else if (collider == EDGE) {
// we ain't ever going to hit these //keep us in bounds
targetPos.X = Math.Min( targetPos.X, m_endX - length );
targetPos.X = Math.Max(targetPos.X, m_startX + length);
targetPos.Y = Math.Min( targetPos.Y, m_endY - width );
targetPos.Y = Math.Max(targetPos.Y, m_startY + width);
targetPos.Z = Math.Min( targetPos.Z, m_endZ - height );
targetPos.Z = Math.Max(targetPos.Z, m_startZ + height);
} else { } else {
//we have hit a SOG //we have hit a SOG
SceneObjectGroup sog = m_scene.GetSceneObjectPart (collider).ParentGroup; SceneObjectGroup sog = m_scene.GetSceneObjectPart(collider).ParentGroup;
if (sog == null) { if (sog == null) {
Console.WriteLine (collider); Console.WriteLine (collider);
} else { } else {
float sogMinX, sogMinY, sogMinZ, sogMaxX, sogMaxY, sogMaxZ; float sogMinX, sogMinY, sogMinZ, sogMaxX, sogMaxY, sogMaxZ;
sog.GetAxisAlignedBoundingBoxRaw (out sogMinX, out sogMaxX, out sogMinY, out sogMaxY, out sogMinZ, out sogMaxZ); sog.GetAxisAlignedBoundingBoxRaw (out sogMinX, out sogMaxX, out sogMinY, out sogMaxY, out sogMinZ, out sogMaxZ);
Vector3 pos = sog.AbsolutePosition;
//keep us out of the sog //keep us out of the sog
if (inFront.X > sogMinX) // adjust up/down first if necessary
retVal.X -= inFront.X - sogMinX - length; // then turn left or right
if (inFront.Y > sogMinY) if (targetPos.Z > sogMinZ + pos.Z)
retVal.Y -= inFront.Y - sogMinY - width; targetPos.Z = (sogMinZ + pos.Z) - height;
if (inFront.Z > sogMinZ) if (targetPos.Z < sogMaxZ + pos.Z)
retVal.Z -= inFront.Z - sogMinZ - height; targetPos.Z = (sogMaxZ + pos.Z) + height;
if (inFront.X < sogMaxX) if (targetPos.X > sogMinX + pos.X)
retVal.X += sogMaxX - inFront.X + length; targetPos.X = (sogMinX + pos.X) - length;
if (inFront.Y < sogMaxY) if (targetPos.Y > sogMinY + pos.Y)
retVal.Y += sogMaxY - inFront.Y + width; targetPos.Y = (sogMinY + pos.Y) - width;
if (inFront.Z < sogMaxZ) if (targetPos.X < sogMaxX + pos.X)
retVal.Z += sogMaxZ - inFront.Z + height; targetPos.X = (sogMaxX + pos.X) + length;
if (targetPos.Y < sogMaxY + pos.Y)
targetPos.Y = (sogMaxY + pos.Y) + width;
} }
} }
collider = LookUp (inFront + retVal);
//inFront += retVal; // we what is at the new target position
collider = LookUp (targetPos);
} }
return retVal; return targetPos;
} }
public UUID LookUp (Vector3 loc) public UUID LookUp (Vector3 loc)